- Renamed all directories: OpenHarbor.MCP.* → Svrnty.MCP.* - Updated all namespaces in 179 C# files - Renamed 20 .csproj files and 3 .sln files - Updated 193 documentation references - Updated 33 references in main CODEX codebase - Updated Codex.sln with new paths - Build verified: 0 errors Preparing for extraction to standalone repositories. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
278 lines
8.4 KiB
C#
278 lines
8.4 KiB
C#
using Xunit;
|
|
using Moq;
|
|
using System.Text.Json;
|
|
using System.Threading.Tasks;
|
|
|
|
namespace Svrnty.MCP.Core.Tests;
|
|
|
|
/// <summary>
|
|
/// Unit tests for McpServer following TDD approach.
|
|
/// Tests MCP protocol method routing and execution.
|
|
/// </summary>
|
|
public class McpServerTests
|
|
{
|
|
[Fact]
|
|
public void McpServer_ShouldInitializeWithToolRegistry()
|
|
{
|
|
// Arrange
|
|
var registry = new ToolRegistry();
|
|
|
|
// Act
|
|
var server = new McpServer(registry);
|
|
|
|
// Assert
|
|
Assert.NotNull(server);
|
|
}
|
|
|
|
[Fact]
|
|
public void McpServer_ShouldThrowOnNullRegistry()
|
|
{
|
|
// Act & Assert
|
|
Assert.Throws<ArgumentNullException>(() =>
|
|
new McpServer(null!)
|
|
);
|
|
}
|
|
|
|
[Fact]
|
|
public async Task McpServer_HandleRequest_WithToolsList_ShouldReturnToolDescriptions()
|
|
{
|
|
// Arrange
|
|
var registry = new ToolRegistry();
|
|
var mockTool = CreateMockTool("test_tool", "Test tool description");
|
|
registry.AddTool(mockTool.Object);
|
|
|
|
var server = new McpServer(registry);
|
|
var request = new McpRequest
|
|
{
|
|
Id = "1",
|
|
Method = "tools/list"
|
|
};
|
|
|
|
// Act
|
|
var response = await server.HandleRequestAsync(request);
|
|
|
|
// Assert
|
|
Assert.NotNull(response);
|
|
Assert.Equal("1", response.Id);
|
|
Assert.Null(response.Error);
|
|
Assert.NotNull(response.Result);
|
|
|
|
var result = response.Result.RootElement;
|
|
Assert.True(result.TryGetProperty("tools", out var tools));
|
|
Assert.Equal(JsonValueKind.Array, tools.ValueKind);
|
|
Assert.Equal(1, tools.GetArrayLength());
|
|
|
|
var tool = tools[0];
|
|
Assert.True(tool.TryGetProperty("name", out var name));
|
|
Assert.Equal("test_tool", name.GetString());
|
|
Assert.True(tool.TryGetProperty("description", out var desc));
|
|
Assert.Equal("Test tool description", desc.GetString());
|
|
}
|
|
|
|
[Fact]
|
|
public async Task McpServer_HandleRequest_WithToolsList_EmptyRegistry_ShouldReturnEmptyArray()
|
|
{
|
|
// Arrange
|
|
var registry = new ToolRegistry();
|
|
var server = new McpServer(registry);
|
|
var request = new McpRequest
|
|
{
|
|
Id = "1",
|
|
Method = "tools/list"
|
|
};
|
|
|
|
// Act
|
|
var response = await server.HandleRequestAsync(request);
|
|
|
|
// Assert
|
|
Assert.NotNull(response);
|
|
Assert.Null(response.Error);
|
|
Assert.NotNull(response.Result);
|
|
|
|
var result = response.Result.RootElement;
|
|
Assert.True(result.TryGetProperty("tools", out var tools));
|
|
Assert.Equal(0, tools.GetArrayLength());
|
|
}
|
|
|
|
[Fact]
|
|
public async Task McpServer_HandleRequest_WithToolsCall_ShouldExecuteTool()
|
|
{
|
|
// Arrange
|
|
var registry = new ToolRegistry();
|
|
var mockTool = CreateMockTool("search_codex", "Search tool");
|
|
mockTool.Setup(t => t.ExecuteAsync(It.IsAny<JsonDocument>()))
|
|
.ReturnsAsync(JsonDocument.Parse("""{"results": ["doc1", "doc2"]}"""));
|
|
registry.AddTool(mockTool.Object);
|
|
|
|
var server = new McpServer(registry);
|
|
var request = new McpRequest
|
|
{
|
|
Id = "2",
|
|
Method = "tools/call",
|
|
Params = JsonDocument.Parse("""{"name":"search_codex","arguments":{"query":"test"}}""")
|
|
};
|
|
|
|
// Act
|
|
var response = await server.HandleRequestAsync(request);
|
|
|
|
// Assert
|
|
Assert.NotNull(response);
|
|
Assert.Equal("2", response.Id);
|
|
Assert.Null(response.Error);
|
|
Assert.NotNull(response.Result);
|
|
|
|
var result = response.Result.RootElement;
|
|
Assert.True(result.TryGetProperty("results", out var results));
|
|
mockTool.Verify(t => t.ExecuteAsync(It.IsAny<JsonDocument>()), Times.Once);
|
|
}
|
|
|
|
[Fact]
|
|
public async Task McpServer_HandleRequest_WithToolsCall_NonexistentTool_ShouldReturnError()
|
|
{
|
|
// Arrange
|
|
var registry = new ToolRegistry();
|
|
var server = new McpServer(registry);
|
|
var request = new McpRequest
|
|
{
|
|
Id = "3",
|
|
Method = "tools/call",
|
|
Params = JsonDocument.Parse("""{"name":"nonexistent_tool","arguments":{}}""")
|
|
};
|
|
|
|
// Act
|
|
var response = await server.HandleRequestAsync(request);
|
|
|
|
// Assert
|
|
Assert.NotNull(response);
|
|
Assert.Equal("3", response.Id);
|
|
Assert.Null(response.Result);
|
|
Assert.NotNull(response.Error);
|
|
Assert.Equal(-32601, response.Error.Code); // Method not found
|
|
Assert.Contains("nonexistent_tool", response.Error.Message);
|
|
}
|
|
|
|
[Fact]
|
|
public async Task McpServer_HandleRequest_WithUnknownMethod_ShouldReturnError()
|
|
{
|
|
// Arrange
|
|
var registry = new ToolRegistry();
|
|
var server = new McpServer(registry);
|
|
var request = new McpRequest
|
|
{
|
|
Id = "4",
|
|
Method = "unknown/method"
|
|
};
|
|
|
|
// Act
|
|
var response = await server.HandleRequestAsync(request);
|
|
|
|
// Assert
|
|
Assert.NotNull(response);
|
|
Assert.Equal("4", response.Id);
|
|
Assert.Null(response.Result);
|
|
Assert.NotNull(response.Error);
|
|
Assert.Equal(-32601, response.Error.Code); // Method not found
|
|
Assert.Contains("unknown/method", response.Error.Message);
|
|
}
|
|
|
|
[Fact]
|
|
public async Task McpServer_HandleRequest_WithToolsCall_MissingParams_ShouldReturnError()
|
|
{
|
|
// Arrange
|
|
var registry = new ToolRegistry();
|
|
var server = new McpServer(registry);
|
|
var request = new McpRequest
|
|
{
|
|
Id = "5",
|
|
Method = "tools/call"
|
|
// No Params
|
|
};
|
|
|
|
// Act
|
|
var response = await server.HandleRequestAsync(request);
|
|
|
|
// Assert
|
|
Assert.NotNull(response);
|
|
Assert.Equal("5", response.Id);
|
|
Assert.Null(response.Result);
|
|
Assert.NotNull(response.Error);
|
|
Assert.Equal(-32602, response.Error.Code); // Invalid params
|
|
}
|
|
|
|
[Fact]
|
|
public async Task McpServer_HandleRequest_WithToolsCall_MissingToolName_ShouldReturnError()
|
|
{
|
|
// Arrange
|
|
var registry = new ToolRegistry();
|
|
var server = new McpServer(registry);
|
|
var request = new McpRequest
|
|
{
|
|
Id = "6",
|
|
Method = "tools/call",
|
|
Params = JsonDocument.Parse("""{"arguments":{}}""") // Missing "name"
|
|
};
|
|
|
|
// Act
|
|
var response = await server.HandleRequestAsync(request);
|
|
|
|
// Assert
|
|
Assert.NotNull(response);
|
|
Assert.Equal("6", response.Id);
|
|
Assert.Null(response.Result);
|
|
Assert.NotNull(response.Error);
|
|
Assert.Equal(-32602, response.Error.Code); // Invalid params
|
|
}
|
|
|
|
[Fact]
|
|
public async Task McpServer_HandleRequest_WithNullRequest_ShouldThrow()
|
|
{
|
|
// Arrange
|
|
var registry = new ToolRegistry();
|
|
var server = new McpServer(registry);
|
|
|
|
// Act & Assert
|
|
await Assert.ThrowsAsync<ArgumentNullException>(async () =>
|
|
await server.HandleRequestAsync(null!)
|
|
);
|
|
}
|
|
|
|
[Fact]
|
|
public async Task McpServer_HandleRequest_WithToolsList_MultipleTools_ShouldReturnAll()
|
|
{
|
|
// Arrange
|
|
var registry = new ToolRegistry();
|
|
registry.AddTool(CreateMockTool("tool1", "First tool").Object);
|
|
registry.AddTool(CreateMockTool("tool2", "Second tool").Object);
|
|
registry.AddTool(CreateMockTool("tool3", "Third tool").Object);
|
|
|
|
var server = new McpServer(registry);
|
|
var request = new McpRequest
|
|
{
|
|
Id = "7",
|
|
Method = "tools/list"
|
|
};
|
|
|
|
// Act
|
|
var response = await server.HandleRequestAsync(request);
|
|
|
|
// Assert
|
|
Assert.NotNull(response);
|
|
Assert.NotNull(response.Result);
|
|
var result = response.Result.RootElement;
|
|
Assert.True(result.TryGetProperty("tools", out var tools));
|
|
Assert.Equal(3, tools.GetArrayLength());
|
|
}
|
|
|
|
// Helper method to create mock tools
|
|
private Mock<IMcpTool> CreateMockTool(string name, string description)
|
|
{
|
|
var mockTool = new Mock<IMcpTool>();
|
|
mockTool.Setup(t => t.Name).Returns(name);
|
|
mockTool.Setup(t => t.Description).Returns(description);
|
|
mockTool.Setup(t => t.Schema).Returns(JsonDocument.Parse("""{"type": "object"}"""));
|
|
mockTool.Setup(t => t.ExecuteAsync(It.IsAny<JsonDocument>()))
|
|
.ReturnsAsync(JsonDocument.Parse("""{"status": "ok"}"""));
|
|
return mockTool;
|
|
}
|
|
}
|