svrnty-mcp-server/tests/Svrnty.MCP.Core.Tests/ToolRegistryTests.cs
Svrnty 516e1479c6 docs: comprehensive AI coding assistant research and MCP-first implementation plan
Research conducted on modern AI coding assistants (Cursor, GitHub Copilot, Cline,
Aider, Windsurf, Replit Agent) to understand architecture patterns, context management,
code editing workflows, and tool use protocols.

Key Decision: Pivoted from building full CLI (40-50h) to validation-driven MCP-first
approach (10-15h). Build 5 core CODEX MCP tools that work with ANY coding assistant,
validate adoption over 2-4 weeks, then decide on full CLI if demand proven.

Files:
- research/ai-systems/modern-coding-assistants-architecture.md (comprehensive research)
- research/ai-systems/codex-coding-assistant-implementation-plan.md (original CLI plan, preserved)
- research/ai-systems/codex-mcp-tools-implementation-plan.md (approved MCP-first plan)
- ideas/registry.json (updated with approved MCP tools proposal)

Architech Validation: APPROVED with pivot to MCP-first approach
Human Decision: Approved (pragmatic validation-driven development)

Next: Begin Phase 1 implementation (10-15 hours, 5 core MCP tools)

🤖 Generated with CODEX Research System

Co-Authored-By: The Archivist <archivist@codex.svrnty.io>
Co-Authored-By: The Architech <architech@codex.svrnty.io>
Co-Authored-By: Mathias Beaulieu-Duncan <mat@svrnty.io>
2025-10-22 21:00:34 -04:00

213 lines
5.8 KiB
C#

using Xunit;
using Moq;
using System.Text.Json;
using System.Threading.Tasks;
using System.Linq;
namespace OpenHarbor.MCP.Core.Tests;
/// <summary>
/// Unit tests for ToolRegistry following TDD approach.
/// Tests dynamic tool registration and retrieval.
/// </summary>
public class ToolRegistryTests
{
[Fact]
public void ToolRegistry_ShouldInitializeEmpty()
{
// Arrange & Act
var registry = new ToolRegistry();
// Assert
Assert.NotNull(registry);
Assert.Empty(registry.GetAllTools());
}
[Fact]
public void ToolRegistry_ShouldAddTool()
{
// Arrange
var registry = new ToolRegistry();
var mockTool = CreateMockTool("test_tool", "A test tool");
// Act
registry.AddTool(mockTool.Object);
// Assert
Assert.Single(registry.GetAllTools());
}
[Fact]
public void ToolRegistry_ShouldGetToolByName()
{
// Arrange
var registry = new ToolRegistry();
var mockTool = CreateMockTool("search_codex", "Search CODEX documents");
registry.AddTool(mockTool.Object);
// Act
var retrieved = registry.GetTool("search_codex");
// Assert
Assert.NotNull(retrieved);
Assert.Equal("search_codex", retrieved.Name);
}
[Fact]
public void ToolRegistry_GetTool_WithInvalidName_ReturnsNull()
{
// Arrange
var registry = new ToolRegistry();
var mockTool = CreateMockTool("existing_tool", "Exists");
registry.AddTool(mockTool.Object);
// Act
var result = registry.GetTool("nonexistent_tool");
// Assert
Assert.Null(result);
}
[Fact]
public void ToolRegistry_ShouldAddMultipleTools()
{
// Arrange
var registry = new ToolRegistry();
var tool1 = CreateMockTool("tool1", "First tool");
var tool2 = CreateMockTool("tool2", "Second tool");
var tool3 = CreateMockTool("tool3", "Third tool");
// Act
registry.AddTool(tool1.Object);
registry.AddTool(tool2.Object);
registry.AddTool(tool3.Object);
// Assert
var allTools = registry.GetAllTools();
Assert.Equal(3, allTools.Count());
}
[Fact]
public void ToolRegistry_ShouldPreventDuplicateToolNames()
{
// Arrange
var registry = new ToolRegistry();
var tool1 = CreateMockTool("duplicate", "First");
var tool2 = CreateMockTool("duplicate", "Second");
registry.AddTool(tool1.Object);
// Act & Assert
var exception = Assert.Throws<ArgumentException>(() =>
registry.AddTool(tool2.Object)
);
Assert.Contains("already registered", exception.Message);
Assert.Single(registry.GetAllTools());
}
[Fact]
public void ToolRegistry_ShouldRemoveTool()
{
// Arrange
var registry = new ToolRegistry();
var mockTool = CreateMockTool("temp_tool", "Temporary");
registry.AddTool(mockTool.Object);
// Act
var removed = registry.RemoveTool("temp_tool");
// Assert
Assert.True(removed);
Assert.Empty(registry.GetAllTools());
Assert.Null(registry.GetTool("temp_tool"));
}
[Fact]
public void ToolRegistry_RemoveTool_WithInvalidName_ReturnsFalse()
{
// Arrange
var registry = new ToolRegistry();
// Act
var removed = registry.RemoveTool("nonexistent");
// Assert
Assert.False(removed);
}
[Fact]
public void ToolRegistry_ShouldListAllToolNames()
{
// Arrange
var registry = new ToolRegistry();
registry.AddTool(CreateMockTool("tool_a", "A").Object);
registry.AddTool(CreateMockTool("tool_b", "B").Object);
registry.AddTool(CreateMockTool("tool_c", "C").Object);
// Act
var names = registry.GetToolNames();
// Assert
Assert.Equal(3, names.Count());
Assert.Contains("tool_a", names);
Assert.Contains("tool_b", names);
Assert.Contains("tool_c", names);
}
[Fact]
public void ToolRegistry_ShouldHaveTool()
{
// Arrange
var registry = new ToolRegistry();
registry.AddTool(CreateMockTool("exists", "Tool").Object);
// Act & Assert
Assert.True(registry.HasTool("exists"));
Assert.False(registry.HasTool("not_exists"));
}
[Fact]
public void ToolRegistry_ShouldClearAllTools()
{
// Arrange
var registry = new ToolRegistry();
registry.AddTool(CreateMockTool("tool1", "1").Object);
registry.AddTool(CreateMockTool("tool2", "2").Object);
// Act
registry.Clear();
// Assert
Assert.Empty(registry.GetAllTools());
Assert.Empty(registry.GetToolNames());
}
[Fact]
public void ToolRegistry_GetAllTools_ShouldReturnReadOnlyCollection()
{
// Arrange
var registry = new ToolRegistry();
registry.AddTool(CreateMockTool("tool", "Tool").Object);
// Act
var tools = registry.GetAllTools();
// Assert
Assert.NotNull(tools);
// Ensure modifying returned collection doesn't affect registry
Assert.IsAssignableFrom<System.Collections.Generic.IEnumerable<IMcpTool>>(tools);
}
// 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;
}
}