svrnty-mcp-gateway/tests/Svrnty.MCP.Gateway.Infrastructure.Tests/Connection/ServerConnectionTests.cs
Svrnty a4a1dd2e38 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

176 lines
6.3 KiB
C#

using Xunit;
using Moq;
using OpenHarbor.MCP.Gateway.Infrastructure.Connection;
using OpenHarbor.MCP.Gateway.Core.Interfaces;
using OpenHarbor.MCP.Gateway.Core.Models;
namespace OpenHarbor.MCP.Gateway.Infrastructure.Tests.Connection;
/// <summary>
/// Unit tests for ServerConnection following TDD approach.
/// Tests connection lifecycle and request handling.
/// </summary>
public class ServerConnectionTests
{
[Fact]
public async Task ConnectAsync_OpensTransport()
{
// Arrange
var mockTransport = new Mock<IServerTransport>();
mockTransport.Setup(t => t.ConnectAsync(It.IsAny<CancellationToken>()))
.Returns(Task.CompletedTask)
.Callback(() => mockTransport.Setup(t => t.IsConnected).Returns(true));
mockTransport.Setup(t => t.IsConnected).Returns(false); // Initially not connected
var serverConfig = new ServerConfig { Id = "test", Name = "Test Server" };
var connection = new ServerConnection(serverConfig, mockTransport.Object);
// Act
await connection.ConnectAsync();
// Assert
mockTransport.Verify(t => t.ConnectAsync(It.IsAny<CancellationToken>()), Times.Once);
Assert.True(connection.IsConnected);
}
[Fact]
public async Task DisconnectAsync_ClosesTransport()
{
// Arrange
var mockTransport = new Mock<IServerTransport>();
mockTransport.Setup(t => t.DisconnectAsync(It.IsAny<CancellationToken>())).Returns(Task.CompletedTask);
mockTransport.Setup(t => t.IsConnected).Returns(false);
var serverConfig = new ServerConfig { Id = "test", Name = "Test Server" };
var connection = new ServerConnection(serverConfig, mockTransport.Object);
// Act
await connection.DisconnectAsync();
// Assert
mockTransport.Verify(t => t.DisconnectAsync(It.IsAny<CancellationToken>()), Times.Once);
Assert.False(connection.IsConnected);
}
[Fact]
public async Task SendRequestAsync_ForwardsToTransport()
{
// Arrange
var mockTransport = new Mock<IServerTransport>();
var request = new GatewayRequest { ToolName = "test_tool" };
var expectedResponse = new GatewayResponse { Success = true };
mockTransport
.Setup(t => t.SendRequestAsync(It.IsAny<GatewayRequest>(), It.IsAny<CancellationToken>()))
.ReturnsAsync(expectedResponse);
mockTransport.Setup(t => t.IsConnected).Returns(true);
var serverConfig = new ServerConfig { Id = "test", Name = "Test Server" };
var connection = new ServerConnection(serverConfig, mockTransport.Object);
// Act
var response = await connection.SendRequestAsync(request);
// Assert
Assert.NotNull(response);
Assert.True(response.Success);
mockTransport.Verify(t => t.SendRequestAsync(request, It.IsAny<CancellationToken>()), Times.Once);
}
[Fact]
public async Task SendRequestAsync_WithTimeout_ThrowsOperationCanceledException()
{
// Arrange
var mockTransport = new Mock<IServerTransport>();
mockTransport
.Setup(t => t.SendRequestAsync(It.IsAny<GatewayRequest>(), It.IsAny<CancellationToken>()))
.Returns(async (GatewayRequest req, CancellationToken ct) =>
{
// Respect cancellation token
await Task.Delay(5000, ct);
return new GatewayResponse { Success = true };
});
mockTransport.Setup(t => t.IsConnected).Returns(true);
var serverConfig = new ServerConfig { Id = "test", Name = "Test Server" };
var connection = new ServerConnection(serverConfig, mockTransport.Object) { RequestTimeout = TimeSpan.FromMilliseconds(100) };
var request = new GatewayRequest { ToolName = "test_tool" };
// Act & Assert
await Assert.ThrowsAnyAsync<OperationCanceledException>(() => connection.SendRequestAsync(request));
}
[Fact]
public void ServerInfo_ReturnsCorrectInfo()
{
// Arrange
var mockTransport = new Mock<IServerTransport>();
mockTransport.Setup(t => t.IsConnected).Returns(true);
var serverConfig = new ServerConfig { Id = "test-123", Name = "Test Server" };
var connection = new ServerConnection(serverConfig, mockTransport.Object);
// Act
var info = connection.ServerInfo;
// Assert
Assert.NotNull(info);
Assert.Equal("test-123", info.Id);
Assert.Equal("Test Server", info.Name);
Assert.True(info.IsHealthy);
}
[Fact]
public void IsConnected_ReflectsTransportState()
{
// Arrange
var mockTransport = new Mock<IServerTransport>();
mockTransport.Setup(t => t.IsConnected).Returns(false);
var serverConfig = new ServerConfig { Id = "test", Name = "Test Server" };
var connection = new ServerConnection(serverConfig, mockTransport.Object);
// Act & Assert
Assert.False(connection.IsConnected);
// Change transport state
mockTransport.Setup(t => t.IsConnected).Returns(true);
Assert.True(connection.IsConnected);
}
[Fact]
public async Task ConnectAsync_CalledMultipleTimes_CallsTransportEachTime()
{
// Arrange
var mockTransport = new Mock<IServerTransport>();
mockTransport.Setup(t => t.ConnectAsync(It.IsAny<CancellationToken>())).Returns(Task.CompletedTask);
mockTransport.Setup(t => t.IsConnected).Returns(true);
var serverConfig = new ServerConfig { Id = "test", Name = "Test Server" };
var connection = new ServerConnection(serverConfig, mockTransport.Object);
// Act
await connection.ConnectAsync();
await connection.ConnectAsync(); // Second call
// Assert - ConnectAsync delegates to transport, which handles idempotency
mockTransport.Verify(t => t.ConnectAsync(It.IsAny<CancellationToken>()), Times.Exactly(2));
}
[Fact]
public void Dispose_DisposesTransport()
{
// Arrange
var mockTransport = new Mock<IServerTransport>();
var serverConfig = new ServerConfig { Id = "test", Name = "Test Server" };
var connection = new ServerConnection(serverConfig, mockTransport.Object);
// Act
connection.Dispose();
// Assert
mockTransport.Verify(t => t.Dispose(), Times.Once);
}
}