534 lines
15 KiB
C#
534 lines
15 KiB
C#
using System.Reflection;
|
|
|
|
namespace AnthropicClient.Tests.Unit.Models;
|
|
|
|
public class ToolTests : SerializationTest
|
|
{
|
|
[Theory]
|
|
[InlineData(null)]
|
|
[InlineData("")]
|
|
[InlineData(" ")]
|
|
public void Constructor_WhenGivenInvalidNameValue_ItShouldThrowArgumentException(string? name)
|
|
{
|
|
var method = () => true;
|
|
var function = new AnthropicFunction(method.Method);
|
|
var action = () => new Tool(name!, "description", function);
|
|
|
|
action.Should().Throw<ArgumentException>();
|
|
}
|
|
|
|
[Theory]
|
|
[InlineData(null)]
|
|
[InlineData("")]
|
|
[InlineData(" ")]
|
|
public void Constructor_WhenGivenInvalidDescriptionValue_ItShouldThrowArgumentException(string? description)
|
|
{
|
|
var method = () => true;
|
|
var function = new AnthropicFunction(method.Method);
|
|
var action = () => new Tool("name", description!, function);
|
|
|
|
action.Should().Throw<ArgumentException>();
|
|
}
|
|
|
|
[Fact]
|
|
public void Constructor_WhenGivenNullFunctionValue_ItShouldThrowArgumentNullException()
|
|
{
|
|
var action = () => new Tool("name", "description", null!);
|
|
|
|
action.Should().Throw<ArgumentNullException>();
|
|
}
|
|
|
|
[Fact]
|
|
public void Constructor_WhenCalled_ItShouldInitializeProperties()
|
|
{
|
|
var method = () => true;
|
|
var function = new AnthropicFunction(method.Method);
|
|
var tool = new Tool("test name", "description", function);
|
|
|
|
var expectedSchema = new JsonObject()
|
|
{
|
|
["type"] = "object",
|
|
};
|
|
|
|
tool.Name.Should().Be("test_name");
|
|
tool.DisplayName.Should().Be("test name");
|
|
tool.Description.Should().Be("description");
|
|
tool.Function.Should().Be(function);
|
|
tool.InputSchema.Should().BeEquivalentTo(
|
|
expectedSchema,
|
|
t => t.IgnoringCyclicReferences()
|
|
);
|
|
}
|
|
|
|
[Fact]
|
|
public void Constructor_WhenCalledAndGivenNameIsMoreThanMaxLength_ItShouldTruncateName()
|
|
{
|
|
var method = () => true;
|
|
var function = new AnthropicFunction(method.Method);
|
|
var tool = new Tool(new string('a', 65), "description", function);
|
|
|
|
tool.Name.Should().HaveLength(64);
|
|
}
|
|
|
|
[Fact]
|
|
public void Constructor_WhenCalledAndGivenCacheControl_ItShouldInitializeCacheControl()
|
|
{
|
|
var method = () => true;
|
|
var function = new AnthropicFunction(method.Method);
|
|
var cacheControl = new EphemeralCacheControl();
|
|
var tool = new Tool("name", "description", function, cacheControl);
|
|
|
|
tool.CacheControl.Should().BeSameAs(cacheControl);
|
|
}
|
|
|
|
[Theory]
|
|
[InlineData(typeof(TestClass), null)]
|
|
[InlineData(typeof(TestClass), "")]
|
|
[InlineData(typeof(TestClass), " ")]
|
|
[InlineData(typeof(TestClass), "MadeUpMethod")]
|
|
public void CreateFromStaticMethod_WhenGivenInvalidMethodName_ItShouldThrowArgumentException(Type type, string? methodName)
|
|
{
|
|
var action = () => Tool.CreateFromStaticMethod("name", "description", type, methodName!);
|
|
|
|
action.Should().Throw<ArgumentException>();
|
|
}
|
|
|
|
[Fact]
|
|
public void CreateFromStaticMethod_WhenGivenTypeIsNull_ItShouldThrowArgumentNullException()
|
|
{
|
|
var action = () => Tool.CreateFromStaticMethod("name", "description", null!, "TestStaticMethod");
|
|
|
|
action.Should().Throw<ArgumentNullException>();
|
|
}
|
|
|
|
[Fact]
|
|
public void CreateFromStaticMethod_WhenGivenTypeThatDoesNotContainMethod_ItShouldThrowArgumentException()
|
|
{
|
|
var action = () => Tool.CreateFromStaticMethod("name", "description", typeof(ToolTests), nameof(TestClass.TestStaticMethod));
|
|
|
|
action.Should().Throw<ArgumentException>();
|
|
}
|
|
|
|
[Fact]
|
|
public void CreateFromStaticMethod_WhenCalled_ItShouldReturnTool()
|
|
{
|
|
var tool = Tool.CreateFromStaticMethod("test name", "description", typeof(TestClass), nameof(TestClass.TestStaticMethod));
|
|
|
|
var expectedSchema = new JsonObject()
|
|
{
|
|
["type"] = "object",
|
|
};
|
|
|
|
tool.Name.Should().Be("test_name");
|
|
tool.DisplayName.Should().Be("test name");
|
|
tool.Description.Should().Be("description");
|
|
tool.Function.Method.Name.Should().Be(nameof(TestClass.TestStaticMethod));
|
|
tool.Function.Instance.Should().BeNull();
|
|
tool.InputSchema.Should().BeEquivalentTo(
|
|
expectedSchema,
|
|
t => t.IgnoringCyclicReferences()
|
|
);
|
|
}
|
|
|
|
[Fact]
|
|
public void CreateFromStaticMethod_WhenCalledWithCacheControl_ItShouldReturnTool()
|
|
{
|
|
var cacheControl = new EphemeralCacheControl();
|
|
var tool = Tool.CreateFromStaticMethod("test name", "description", typeof(TestClass), nameof(TestClass.TestStaticMethod), cacheControl);
|
|
|
|
var expectedSchema = new JsonObject()
|
|
{
|
|
["type"] = "object",
|
|
};
|
|
|
|
tool.Name.Should().Be("test_name");
|
|
tool.DisplayName.Should().Be("test name");
|
|
tool.Description.Should().Be("description");
|
|
tool.Function.Method.Name.Should().Be(nameof(TestClass.TestStaticMethod));
|
|
tool.Function.Instance.Should().BeNull();
|
|
tool.InputSchema.Should().BeEquivalentTo(
|
|
expectedSchema,
|
|
t => t.IgnoringCyclicReferences()
|
|
);
|
|
tool.CacheControl.Should().BeSameAs(cacheControl);
|
|
}
|
|
|
|
[Theory]
|
|
[InlineData(null)]
|
|
[InlineData("")]
|
|
[InlineData(" ")]
|
|
[InlineData("MadeUpMethod")]
|
|
public void CreateFromInstanceMethod_WhenGivenInvalidMethodName_ItShouldThrowArgumentException(string? methodName)
|
|
{
|
|
var action = () => Tool.CreateFromInstanceMethod("name", "description", new TestClass(), methodName!);
|
|
|
|
action.Should().Throw<ArgumentException>();
|
|
}
|
|
|
|
[Fact]
|
|
public void CreateFromInstanceMethod_WhenGivenInstanceIsNull_ItShouldThrowArgumentNullException()
|
|
{
|
|
var action = () => Tool.CreateFromInstanceMethod("name", "description", null!, "TestMethod");
|
|
|
|
action.Should().Throw<ArgumentNullException>();
|
|
}
|
|
|
|
[Fact]
|
|
public void CreateFromInstanceMethod_WhenGivenInstanceThatDoesNotContainMethod_ItShouldThrowArgumentException()
|
|
{
|
|
var action = () => Tool.CreateFromInstanceMethod("name", "description", new ToolTests(), nameof(TestClass.TestStaticMethod));
|
|
|
|
action.Should().Throw<ArgumentException>();
|
|
}
|
|
|
|
[Fact]
|
|
public void CreateFromInstanceMethod_WhenMethodIsNotAnInstanceMethod_ItShouldThrowArgumentException()
|
|
{
|
|
var action = () => Tool.CreateFromInstanceMethod("name", "description", new TestClass(), nameof(TestClass.TestStaticMethod));
|
|
|
|
action.Should().Throw<ArgumentException>();
|
|
}
|
|
|
|
[Fact]
|
|
public void CreateFromInstanceMethod_WhenCalled_ItShouldReturnTool()
|
|
{
|
|
var instance = new TestClass();
|
|
var tool = Tool.CreateFromInstanceMethod("test name", "description", instance, nameof(instance.TestInstanceMethod));
|
|
|
|
var expectedSchema = new JsonObject()
|
|
{
|
|
["type"] = "object",
|
|
};
|
|
|
|
tool.Name.Should().Be("test_name");
|
|
tool.DisplayName.Should().Be("test name");
|
|
tool.Description.Should().Be("description");
|
|
tool.Function.Method.Name.Should().Be(nameof(TestClass.TestInstanceMethod));
|
|
tool.Function.Instance.Should().Be(instance);
|
|
tool.InputSchema.Should().BeEquivalentTo(
|
|
expectedSchema,
|
|
t => t.IgnoringCyclicReferences()
|
|
);
|
|
}
|
|
|
|
[Fact]
|
|
public void CreateFromInstanceMethod_WhenCalledWithCacheControl_ItShouldReturnTool()
|
|
{
|
|
var instance = new TestClass();
|
|
var cacheControl = new EphemeralCacheControl();
|
|
var tool = Tool.CreateFromInstanceMethod("test name", "description", instance, nameof(instance.TestInstanceMethod), cacheControl);
|
|
|
|
var expectedSchema = new JsonObject()
|
|
{
|
|
["type"] = "object",
|
|
};
|
|
|
|
tool.Name.Should().Be("test_name");
|
|
tool.DisplayName.Should().Be("test name");
|
|
tool.Description.Should().Be("description");
|
|
tool.Function.Method.Name.Should().Be(nameof(TestClass.TestInstanceMethod));
|
|
tool.Function.Instance.Should().Be(instance);
|
|
tool.InputSchema.Should().BeEquivalentTo(
|
|
expectedSchema,
|
|
t => t.IgnoringCyclicReferences()
|
|
);
|
|
tool.CacheControl.Should().BeSameAs(cacheControl);
|
|
}
|
|
|
|
[Fact]
|
|
public void CreateFromParameterlessFunction_WhenGivenNullFunction_ItShouldThrowArgumentNullException()
|
|
{
|
|
var action = () => Tool.CreateFromFunction<string>("name", "description", null!);
|
|
|
|
action.Should().Throw<ArgumentNullException>();
|
|
}
|
|
|
|
[Fact]
|
|
public void CreateFromParameterlessFunction_WhenCalled_ItShouldReturnTool()
|
|
{
|
|
var func = () => true;
|
|
var tool = Tool.CreateFromFunction("test name", "description", func);
|
|
|
|
var expectedSchema = new JsonObject()
|
|
{
|
|
["type"] = "object",
|
|
};
|
|
|
|
tool.Name.Should().Be("test_name");
|
|
tool.DisplayName.Should().Be("test name");
|
|
tool.Description.Should().Be("description");
|
|
tool.Function.Method.Should().BeSameAs(func.Method);
|
|
tool.InputSchema.Should().BeEquivalentTo(
|
|
expectedSchema,
|
|
t => t.IgnoringCyclicReferences()
|
|
);
|
|
}
|
|
|
|
[Fact]
|
|
public void CreateFromParameterlessFunction_WhenCalledWithCacheControl_ItShouldReturnTool()
|
|
{
|
|
var func = () => true;
|
|
var cacheControl = new EphemeralCacheControl();
|
|
var tool = Tool.CreateFromFunction("test name", "description", func, cacheControl);
|
|
|
|
var expectedSchema = new JsonObject()
|
|
{
|
|
["type"] = "object",
|
|
};
|
|
|
|
tool.Name.Should().Be("test_name");
|
|
tool.DisplayName.Should().Be("test name");
|
|
tool.Description.Should().Be("description");
|
|
tool.Function.Method.Should().BeSameAs(func.Method);
|
|
tool.InputSchema.Should().BeEquivalentTo(
|
|
expectedSchema,
|
|
t => t.IgnoringCyclicReferences()
|
|
);
|
|
tool.CacheControl.Should().BeSameAs(cacheControl);
|
|
}
|
|
|
|
[Fact]
|
|
public void CreateFromFunctionWithParameter_WhenGivenNullFunction_ItShouldThrowArgumentNullException()
|
|
{
|
|
var action = () => Tool.CreateFromFunction<string, string>("name", "description", null!);
|
|
|
|
action.Should().Throw<ArgumentNullException>();
|
|
}
|
|
|
|
[Fact]
|
|
public void CreateFromFunctionWithParameter_WhenCalled_ItShouldReturnTool()
|
|
{
|
|
var func = (string s) => true;
|
|
var tool = Tool.CreateFromFunction("test name", "description", func);
|
|
|
|
var expectedSchema = new JsonObject()
|
|
{
|
|
["type"] = "object",
|
|
["properties"] = new JsonObject()
|
|
{
|
|
["s"] = new JsonObject()
|
|
{
|
|
["type"] = "string",
|
|
},
|
|
},
|
|
["required"] = new JsonArray()
|
|
{
|
|
"s",
|
|
},
|
|
};
|
|
|
|
tool.Name.Should().Be("test_name");
|
|
tool.DisplayName.Should().Be("test name");
|
|
tool.Description.Should().Be("description");
|
|
tool.Function.Method.Should().BeSameAs(func.Method);
|
|
tool.InputSchema.Should().BeEquivalentTo(
|
|
expectedSchema,
|
|
t => t.IgnoringCyclicReferences()
|
|
);
|
|
}
|
|
|
|
[Fact]
|
|
public void CreateFromFunctionWithParameter_WhenCalledWithCacheControl_ItShouldReturnTool()
|
|
{
|
|
var func = (string s) => true;
|
|
var cacheControl = new EphemeralCacheControl();
|
|
var tool = Tool.CreateFromFunction("test name", "description", func, cacheControl);
|
|
|
|
var expectedSchema = new JsonObject()
|
|
{
|
|
["type"] = "object",
|
|
["properties"] = new JsonObject()
|
|
{
|
|
["s"] = new JsonObject()
|
|
{
|
|
["type"] = "string",
|
|
},
|
|
},
|
|
["required"] = new JsonArray()
|
|
{
|
|
"s",
|
|
},
|
|
};
|
|
|
|
tool.Name.Should().Be("test_name");
|
|
tool.DisplayName.Should().Be("test name");
|
|
tool.Description.Should().Be("description");
|
|
tool.Function.Method.Should().BeSameAs(func.Method);
|
|
tool.InputSchema.Should().BeEquivalentTo(
|
|
expectedSchema,
|
|
t => t.IgnoringCyclicReferences()
|
|
);
|
|
tool.CacheControl.Should().BeSameAs(cacheControl);
|
|
}
|
|
|
|
[Fact]
|
|
public void CreateFromClass_WhenCalledWithToolWhoseNameIsNull_ItShouldThrowException()
|
|
{
|
|
var action = () => Tool.CreateFromClass<ToolWithNullName>();
|
|
|
|
action.Should().Throw<ArgumentException>();
|
|
}
|
|
|
|
[Fact]
|
|
public void CreateFromClass_WhenCalledWithToolWhoseNameIsEmpty_ItShouldThrowException()
|
|
{
|
|
var action = () => Tool.CreateFromClass<ToolWithEmptyName>();
|
|
|
|
action.Should().Throw<ArgumentException>();
|
|
}
|
|
|
|
[Fact]
|
|
public void CreateFromClass_WhenCalledWithToolWhoseDescriptionIsNull_ItShouldThrowException()
|
|
{
|
|
var action = () => Tool.CreateFromClass<ToolWithNullDescription>();
|
|
|
|
action.Should().Throw<ArgumentException>();
|
|
}
|
|
|
|
[Fact]
|
|
public void CreateFromClass_WhenCalledWithToolWhoseDescriptionIsEmpty_ItShouldThrowException()
|
|
{
|
|
var action = () => Tool.CreateFromClass<ToolWithEmptyDescription>();
|
|
|
|
action.Should().Throw<ArgumentException>();
|
|
}
|
|
|
|
[Fact]
|
|
public void CreateFromClass_WhenCalledWithToolWhoseFunctionIsNull_ItShouldThrowException()
|
|
{
|
|
var action = () => Tool.CreateFromClass<ToolWithNullFunction>();
|
|
|
|
action.Should().Throw<ArgumentNullException>();
|
|
}
|
|
|
|
[Fact]
|
|
public void CreateFromClass_WhenCalledWithProperTool_ItShouldReturnTool()
|
|
{
|
|
var tool = Tool.CreateFromClass<ProperTool>();
|
|
|
|
var expectedSchema = new JsonObject()
|
|
{
|
|
["type"] = "object",
|
|
};
|
|
|
|
tool.Name.Should().Be("Name");
|
|
tool.DisplayName.Should().Be("Name");
|
|
tool.Description.Should().Be("Description");
|
|
tool.Function.Method.Name.Should().Be(nameof(ProperTool.GetWeather));
|
|
tool.Function.Instance.Should().BeOfType<ProperTool>();
|
|
tool.InputSchema.Should().BeEquivalentTo(
|
|
expectedSchema,
|
|
t => t.IgnoringCyclicReferences()
|
|
);
|
|
}
|
|
|
|
[Fact]
|
|
public void CreateFromClass_WhenCalledWithProperToolAndCacheControl_ItShouldReturnTool()
|
|
{
|
|
var cacheControl = new EphemeralCacheControl();
|
|
var tool = Tool.CreateFromClass<ProperTool>(cacheControl);
|
|
|
|
var expectedSchema = new JsonObject()
|
|
{
|
|
["type"] = "object",
|
|
};
|
|
|
|
tool.Name.Should().Be("Name");
|
|
tool.DisplayName.Should().Be("Name");
|
|
tool.Description.Should().Be("Description");
|
|
tool.Function.Method.Name.Should().Be(nameof(ProperTool.GetWeather));
|
|
tool.Function.Instance.Should().BeOfType<ProperTool>();
|
|
tool.InputSchema.Should().BeEquivalentTo(
|
|
expectedSchema,
|
|
t => t.IgnoringCyclicReferences()
|
|
);
|
|
tool.CacheControl.Should().BeSameAs(cacheControl);
|
|
}
|
|
}
|
|
|
|
class TestClass
|
|
{
|
|
private readonly bool _result = true;
|
|
|
|
public static bool TestStaticMethod() => true;
|
|
public bool TestInstanceMethod() => _result;
|
|
}
|
|
|
|
class ProperTool : ITool
|
|
{
|
|
public string Name => "Name";
|
|
|
|
public string Description { get; } = "Description";
|
|
|
|
public MethodInfo Function => typeof(ProperTool).GetMethod(nameof(GetWeather))!;
|
|
|
|
public static string GetWeather()
|
|
{
|
|
return "Sunny";
|
|
}
|
|
}
|
|
|
|
class ToolWithNullName : ITool
|
|
{
|
|
public string Name => null!;
|
|
|
|
public string Description { get; } = "Description";
|
|
|
|
public MethodInfo Function => typeof(ToolWithNullName).GetMethod(nameof(Tool))!;
|
|
|
|
public static string GetWeather()
|
|
{
|
|
return "Sunny";
|
|
}
|
|
}
|
|
|
|
class ToolWithEmptyName : ITool
|
|
{
|
|
public string Name => string.Empty;
|
|
|
|
public string Description { get; } = "Description";
|
|
|
|
public MethodInfo Function => typeof(ToolWithEmptyName).GetMethod(nameof(Tool))!;
|
|
|
|
public static string GetWeather()
|
|
{
|
|
return "Sunny";
|
|
}
|
|
}
|
|
|
|
class ToolWithNullDescription : ITool
|
|
{
|
|
public string Name => "Name";
|
|
|
|
public string Description => null!;
|
|
|
|
public MethodInfo Function => typeof(ToolWithNullDescription).GetMethod(nameof(Tool))!;
|
|
|
|
public static string GetWeather()
|
|
{
|
|
return "Sunny";
|
|
}
|
|
}
|
|
|
|
class ToolWithEmptyDescription : ITool
|
|
{
|
|
public string Name => "Name";
|
|
|
|
public string Description => string.Empty;
|
|
|
|
public MethodInfo Function => typeof(ToolWithEmptyDescription).GetMethod(nameof(Tool))!;
|
|
|
|
public static string GetWeather()
|
|
{
|
|
return "Sunny";
|
|
}
|
|
}
|
|
|
|
class ToolWithNullFunction : ITool
|
|
{
|
|
public string Name => "Name";
|
|
|
|
public string Description => "Description";
|
|
|
|
public MethodInfo Function => null!;
|
|
} |