feat: 初始化NewLife Studio项目,完成基础框架与数据管理模块
何炳宏 authored at 2026-05-26 12:09:09
4.72 KiB
NewLife.Studio
using NewLife.Studio.AI.Models;
using NewLife.Studio.AI.ToolCalling;
using Xunit;

namespace NewLife.Studio.AI.Tests;

public class ToolRegistryTests
{
    private readonly ToolRegistry _registry;

    public ToolRegistryTests()
    {
        _registry = new ToolRegistry();
    }

    [Fact]
    public void GetAllDefinitions_BeforeAnyRegistration_ReturnsEmptyList()
    {
        var definitions = _registry.GetAllDefinitions();

        Assert.NotNull(definitions);
        Assert.Empty(definitions);
    }

    [Fact]
    public void Register_ThenGetAllDefinitions_ReturnsRegisteredTool()
    {
        _registry.Register("test.tool", "A test tool", null, _ => Task.FromResult("done"));

        var definitions = _registry.GetAllDefinitions();

        Assert.Single(definitions);
        Assert.Equal("test.tool", definitions[0].Function.Name);
        Assert.Equal("A test tool", definitions[0].Function.Description);
        Assert.Equal("function", definitions[0].Type);
    }

    [Fact]
    public void Register_MultipleTools_ReturnsAllDefinitions()
    {
        _registry.Register("tool1", "Tool 1", null, _ => Task.FromResult("1"));
        _registry.Register("tool2", "Tool 2", null, _ => Task.FromResult("2"));
        _registry.Register("tool3", "Tool 3", null, _ => Task.FromResult("3"));

        var definitions = _registry.GetAllDefinitions();

        Assert.Equal(3, definitions.Count);
        Assert.Contains(definitions, d => d.Function.Name == "tool1");
        Assert.Contains(definitions, d => d.Function.Name == "tool2");
        Assert.Contains(definitions, d => d.Function.Name == "tool3");
    }

    [Fact]
    public void Register_DuplicateName_OverwritesPrevious()
    {
        _registry.Register("dup.tool", "First registration", null, _ => Task.FromResult("first"));
        _registry.Register("dup.tool", "Second registration", null, _ => Task.FromResult("second"));

        var definitions = _registry.GetAllDefinitions();

        Assert.Single(definitions);
        Assert.Equal("Second registration", definitions[0].Function.Description);
    }

    [Fact]
    public async Task ExecuteAsync_RegisteredTool_CallsHandlerAndReturnsOutput()
    {
        _registry.Register("echo", "Echo tool", new { type = "object", properties = new { } },
            args => Task.FromResult($"Echo: {args}"));

        var toolCall = new ToolCall
        {
            Id = "call_001",
            Function = new FunctionCall
            {
                Name = "echo",
                Arguments = "{\"message\":\"hello\"}"
            }
        };

        var result = await _registry.ExecuteAsync(toolCall);

        Assert.NotNull(result);
        Assert.Equal("call_001", result.ToolCallId);
        Assert.Null(result.Error);
        Assert.Equal("Echo: {\"message\":\"hello\"}", result.Output);
    }

    [Fact]
    public async Task ExecuteAsync_UnknownTool_ReturnsError()
    {
        var toolCall = new ToolCall
        {
            Id = "call_002",
            Function = new FunctionCall
            {
                Name = "nonexistent",
                Arguments = "{}"
            }
        };

        var result = await _registry.ExecuteAsync(toolCall);

        Assert.NotNull(result);
        Assert.Equal("call_002", result.ToolCallId);
        Assert.NotNull(result.Error);
        Assert.Contains("Unknown tool", result.Error);
        Assert.Contains("nonexistent", result.Error);
        Assert.Null(result.Output);
    }

    [Fact]
    public async Task ExecuteAsync_HandlerThrowsException_ReturnsError()
    {
        _registry.Register("failing.tool", "Always fails", null, args =>
        {
            throw new InvalidOperationException("Simulated handler failure");
        });

        var toolCall = new ToolCall
        {
            Id = "call_003",
            Function = new FunctionCall
            {
                Name = "failing.tool",
                Arguments = "{}"
            }
        };

        var result = await _registry.ExecuteAsync(toolCall);

        Assert.NotNull(result);
        Assert.Equal("call_003", result.ToolCallId);
        Assert.NotNull(result.Error);
        Assert.Contains("Simulated handler failure", result.Error);
        Assert.Null(result.Output);
    }

    [Fact]
    public void Register_WithParametersSchema_StoresParametersCorrectly()
    {
        var parameters = new
        {
            type = "object",
            properties = new
            {
                sql = new { type = "string", description = "SQL 语句" }
            },
            required = new[] { "sql" }
        };

        _registry.Register("query.select", "Execute SELECT", parameters, _ => Task.FromResult("ok"));

        var definitions = _registry.GetAllDefinitions();
        Assert.NotNull(definitions[0].Function.Parameters);
    }
}