svrnty-mcp-gateway/tests/Svrnty.MCP.Gateway.Infrastructure.Tests/Transport/HttpServerTransportTests.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

177 lines
5.6 KiB
C#

using Xunit;
using Moq;
using Moq.Protected;
using System.Net;
using OpenHarbor.MCP.Gateway.Infrastructure.Transport;
using OpenHarbor.MCP.Gateway.Core.Models;
namespace OpenHarbor.MCP.Gateway.Infrastructure.Tests.Transport;
/// <summary>
/// Unit tests for HttpServerTransport following TDD approach.
/// Tests HTTP transport implementation.
/// </summary>
public class HttpServerTransportTests
{
[Fact]
public void Constructor_WithValidUrl_CreatesSuccessfully()
{
// Arrange & Act
var transport = new HttpServerTransport("http://localhost:5000");
// Assert
Assert.NotNull(transport);
Assert.False(transport.IsConnected);
}
[Fact]
public void Constructor_WithNullUrl_ThrowsException()
{
// Arrange, Act & Assert
Assert.Throws<ArgumentNullException>(() =>
new HttpServerTransport(null!));
}
[Fact]
public async Task ConnectAsync_WithHealthyServer_SetsConnected()
{
// Arrange
var mockHttpMessageHandler = new Mock<HttpMessageHandler>();
mockHttpMessageHandler.Protected()
.Setup<Task<HttpResponseMessage>>(
"SendAsync",
ItExpr.IsAny<HttpRequestMessage>(),
ItExpr.IsAny<CancellationToken>())
.ReturnsAsync(new HttpResponseMessage
{
StatusCode = HttpStatusCode.OK
});
var httpClient = new HttpClient(mockHttpMessageHandler.Object);
var transport = new HttpServerTransport("http://localhost:5000", httpClient);
// Act
await transport.ConnectAsync();
// Assert
Assert.True(transport.IsConnected);
}
[Fact]
public async Task SendRequestAsync_WithSuccessfulResponse_ReturnsResponse()
{
// Arrange
var expectedResponse = new GatewayResponse
{
Success = true,
Result = new Dictionary<string, object> { { "data", "test" } }
};
var mockHttpMessageHandler = new Mock<HttpMessageHandler>();
mockHttpMessageHandler.Protected()
.SetupSequence<Task<HttpResponseMessage>>(
"SendAsync",
ItExpr.IsAny<HttpRequestMessage>(),
ItExpr.IsAny<CancellationToken>())
.ReturnsAsync(new HttpResponseMessage // Health check
{
StatusCode = HttpStatusCode.OK
})
.ReturnsAsync(new HttpResponseMessage // Actual request
{
StatusCode = HttpStatusCode.OK,
Content = new StringContent(System.Text.Json.JsonSerializer.Serialize(expectedResponse))
});
var httpClient = new HttpClient(mockHttpMessageHandler.Object);
var transport = new HttpServerTransport("http://localhost:5000", httpClient);
await transport.ConnectAsync();
var request = new GatewayRequest { ToolName = "test_tool" };
// Act
var response = await transport.SendRequestAsync(request);
// Assert
Assert.NotNull(response);
Assert.True(response.Success);
}
[Fact]
public async Task SendRequestAsync_WithHttpError_ReturnsErrorResponse()
{
// Arrange
var mockHttpMessageHandler = new Mock<HttpMessageHandler>();
mockHttpMessageHandler.Protected()
.SetupSequence<Task<HttpResponseMessage>>(
"SendAsync",
ItExpr.IsAny<HttpRequestMessage>(),
ItExpr.IsAny<CancellationToken>())
.ReturnsAsync(new HttpResponseMessage // Health check
{
StatusCode = HttpStatusCode.OK
})
.ReturnsAsync(new HttpResponseMessage // Actual request
{
StatusCode = HttpStatusCode.InternalServerError,
ReasonPhrase = "Internal Server Error"
});
var httpClient = new HttpClient(mockHttpMessageHandler.Object);
var transport = new HttpServerTransport("http://localhost:5000", httpClient);
await transport.ConnectAsync();
var request = new GatewayRequest { ToolName = "test_tool" };
// Act
var response = await transport.SendRequestAsync(request);
// Assert
Assert.NotNull(response);
Assert.False(response.Success);
Assert.Contains("InternalServerError", response.Error);
}
[Fact]
public async Task SendRequestAsync_WithoutConnect_ThrowsException()
{
// Arrange
var transport = new HttpServerTransport("http://localhost:5000");
var request = new GatewayRequest { ToolName = "test" };
// Act & Assert
await Assert.ThrowsAsync<InvalidOperationException>(() =>
transport.SendRequestAsync(request));
}
[Fact]
public async Task DisconnectAsync_SetsNotConnected()
{
// Arrange
var mockHttpMessageHandler = new Mock<HttpMessageHandler>();
mockHttpMessageHandler.Protected()
.Setup<Task<HttpResponseMessage>>(
"SendAsync",
ItExpr.IsAny<HttpRequestMessage>(),
ItExpr.IsAny<CancellationToken>())
.ReturnsAsync(new HttpResponseMessage
{
StatusCode = HttpStatusCode.OK
});
var httpClient = new HttpClient(mockHttpMessageHandler.Object);
var transport = new HttpServerTransport("http://localhost:5000", httpClient);
await transport.ConnectAsync();
Assert.True(transport.IsConnected);
// Act
await transport.DisconnectAsync();
// Assert
Assert.False(transport.IsConnected);
}
}