svrnty-mcp-gateway/tests/Svrnty.MCP.Gateway.Infrastructure.Tests/Routing/GatewayRouterTests.cs
Svrnty 19ef79362e refactor: rename OpenHarbor.MCP to Svrnty.MCP across all libraries
- 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>
2025-10-22 21:04:17 -04:00

323 lines
12 KiB
C#

using Xunit;
using Moq;
using Svrnty.MCP.Gateway.Infrastructure.Routing;
using Svrnty.MCP.Gateway.Infrastructure.Connection;
using Svrnty.MCP.Gateway.Core.Interfaces;
using Svrnty.MCP.Gateway.Core.Models;
namespace Svrnty.MCP.Gateway.Infrastructure.Tests.Routing;
/// <summary>
/// Unit tests for GatewayRouter following TDD approach.
/// Tests routing, server registration, and health tracking.
/// </summary>
public class GatewayRouterTests
{
[Fact]
public async Task RegisterServerAsync_AddsServerToPool()
{
// Arrange
var mockStrategy = new Mock<IRoutingStrategy>();
var mockPool = new Mock<IServerConnectionPool>();
var router = new GatewayRouter(mockStrategy.Object, mockPool.Object);
var serverConfig = new ServerConfig
{
Id = "server-1",
Name = "Test Server",
TransportType = "Http",
BaseUrl = "http://localhost:5000"
};
// Act
await router.RegisterServerAsync(serverConfig);
// Assert
var health = await router.GetServerHealthAsync();
Assert.Contains(health, s => s.ServerId == "server-1");
}
[Fact]
public async Task UnregisterServerAsync_RemovesServerFromPool()
{
// Arrange
var mockStrategy = new Mock<IRoutingStrategy>();
var mockPool = new Mock<IServerConnectionPool>();
var router = new GatewayRouter(mockStrategy.Object, mockPool.Object);
var serverConfig = new ServerConfig
{
Id = "server-1",
Name = "Test Server",
TransportType = "Http",
BaseUrl = "http://localhost:5000"
};
await router.RegisterServerAsync(serverConfig);
// Act
var result = await router.UnregisterServerAsync("server-1");
// Assert
Assert.True(result);
var health = await router.GetServerHealthAsync();
Assert.DoesNotContain(health, s => s.ServerId == "server-1");
}
[Fact]
public async Task UnregisterServerAsync_WithNonExistentServer_ReturnsFalse()
{
// Arrange
var mockStrategy = new Mock<IRoutingStrategy>();
var mockPool = new Mock<IServerConnectionPool>();
var router = new GatewayRouter(mockStrategy.Object, mockPool.Object);
// Act
var result = await router.UnregisterServerAsync("non-existent");
// Assert
Assert.False(result);
}
[Fact]
public async Task RouteAsync_WithValidRequest_RoutesToSelectedServer()
{
// Arrange
var serverConfig = new ServerConfig
{
Id = "server-1",
Name = "Test Server",
TransportType = "Http",
BaseUrl = "http://localhost:5000"
};
var mockConnection = new Mock<IServerConnection>();
mockConnection.Setup(c => c.IsConnected).Returns(true);
mockConnection.Setup(c => c.ServerInfo).Returns(new ServerInfo { Id = "server-1", Name = "Test Server", IsHealthy = true });
mockConnection.Setup(c => c.SendRequestAsync(It.IsAny<GatewayRequest>(), It.IsAny<CancellationToken>()))
.ReturnsAsync(new GatewayResponse { Success = true });
var mockPool = new Mock<IServerConnectionPool>();
mockPool.Setup(p => p.GetConnectionAsync(It.IsAny<ServerConfig>(), It.IsAny<CancellationToken>()))
.ReturnsAsync(mockConnection.Object);
var mockStrategy = new Mock<IRoutingStrategy>();
mockStrategy.Setup(s => s.SelectServerAsync(It.IsAny<IEnumerable<ServerInfo>>(), It.IsAny<RoutingContext>(), It.IsAny<CancellationToken>()))
.ReturnsAsync(new ServerInfo { Id = "server-1", Name = "Test Server", IsHealthy = true });
var router = new GatewayRouter(mockStrategy.Object, mockPool.Object);
await router.RegisterServerAsync(serverConfig);
var request = new GatewayRequest { ToolName = "test_tool" };
// Act
var response = await router.RouteAsync(request);
// Assert
Assert.NotNull(response);
Assert.True(response.Success);
mockConnection.Verify(c => c.SendRequestAsync(request, It.IsAny<CancellationToken>()), Times.Once);
}
[Fact]
public async Task RouteAsync_WithNoHealthyServers_ReturnsErrorResponse()
{
// Arrange
var mockStrategy = new Mock<IRoutingStrategy>();
mockStrategy.Setup(s => s.SelectServerAsync(It.IsAny<IEnumerable<ServerInfo>>(), It.IsAny<RoutingContext>(), It.IsAny<CancellationToken>()))
.ReturnsAsync((ServerInfo?)null);
var mockPool = new Mock<ServerConnectionPool>();
var router = new GatewayRouter(mockStrategy.Object, mockPool.Object);
var request = new GatewayRequest { ToolName = "test_tool" };
// Act
var response = await router.RouteAsync(request);
// Assert
Assert.NotNull(response);
Assert.False(response.Success);
Assert.Contains("No healthy servers", response.Error);
}
[Fact]
public async Task GetServerHealthAsync_ReturnsAllRegisteredServers()
{
// Arrange
var mockStrategy = new Mock<IRoutingStrategy>();
var mockPool = new Mock<IServerConnectionPool>();
var router = new GatewayRouter(mockStrategy.Object, mockPool.Object);
var server1 = new ServerConfig { Id = "server-1", Name = "Server 1", TransportType = "Http", BaseUrl = "http://localhost:5000" };
var server2 = new ServerConfig { Id = "server-2", Name = "Server 2", TransportType = "Http", BaseUrl = "http://localhost:5001" };
await router.RegisterServerAsync(server1);
await router.RegisterServerAsync(server2);
// Act
var health = await router.GetServerHealthAsync();
// Assert
Assert.Equal(2, health.Count());
Assert.Contains(health, s => s.ServerId == "server-1");
Assert.Contains(health, s => s.ServerId == "server-2");
}
[Fact]
public async Task RouteAsync_UsesRoutingStrategy()
{
// Arrange
var serverConfig = new ServerConfig
{
Id = "selected-server",
Name = "Selected Server",
TransportType = "Http",
BaseUrl = "http://localhost:5000"
};
var mockConnection = new Mock<IServerConnection>();
mockConnection.Setup(c => c.IsConnected).Returns(true);
mockConnection.Setup(c => c.ServerInfo).Returns(new ServerInfo { Id = "selected-server", IsHealthy = true });
mockConnection.Setup(c => c.SendRequestAsync(It.IsAny<GatewayRequest>(), It.IsAny<CancellationToken>()))
.ReturnsAsync(new GatewayResponse { Success = true });
var mockPool = new Mock<IServerConnectionPool>();
mockPool.Setup(p => p.GetConnectionAsync(It.Is<ServerConfig>(sc => sc.Id == "selected-server"), It.IsAny<CancellationToken>()))
.ReturnsAsync(mockConnection.Object);
var mockStrategy = new Mock<IRoutingStrategy>();
mockStrategy.Setup(s => s.SelectServerAsync(It.IsAny<IEnumerable<ServerInfo>>(), It.IsAny<RoutingContext>(), It.IsAny<CancellationToken>()))
.ReturnsAsync(new ServerInfo { Id = "selected-server", IsHealthy = true });
var router = new GatewayRouter(mockStrategy.Object, mockPool.Object);
await router.RegisterServerAsync(serverConfig);
var request = new GatewayRequest { ToolName = "test_tool", ClientId = "test-client" };
// Act
await router.RouteAsync(request);
// Assert - verify strategy was called
mockStrategy.Verify(s => s.SelectServerAsync(
It.IsAny<IEnumerable<ServerInfo>>(),
It.Is<RoutingContext>(rc => rc.ToolName == "test_tool" && rc.ClientId == "test-client"),
It.IsAny<CancellationToken>()), Times.Once);
}
[Fact]
public async Task RouteAsync_SetsServerIdInResponse()
{
// Arrange
var serverConfig = new ServerConfig
{
Id = "server-1",
Name = "Test Server",
TransportType = "Http",
BaseUrl = "http://localhost:5000"
};
var mockConnection = new Mock<IServerConnection>();
mockConnection.Setup(c => c.IsConnected).Returns(true);
mockConnection.Setup(c => c.ServerInfo).Returns(new ServerInfo { Id = "server-1", IsHealthy = true });
mockConnection.Setup(c => c.SendRequestAsync(It.IsAny<GatewayRequest>(), It.IsAny<CancellationToken>()))
.ReturnsAsync(new GatewayResponse { Success = true });
var mockPool = new Mock<IServerConnectionPool>();
mockPool.Setup(p => p.GetConnectionAsync(It.IsAny<ServerConfig>(), It.IsAny<CancellationToken>()))
.ReturnsAsync(mockConnection.Object);
var mockStrategy = new Mock<IRoutingStrategy>();
mockStrategy.Setup(s => s.SelectServerAsync(It.IsAny<IEnumerable<ServerInfo>>(), It.IsAny<RoutingContext>(), It.IsAny<CancellationToken>()))
.ReturnsAsync(new ServerInfo { Id = "server-1", IsHealthy = true });
var router = new GatewayRouter(mockStrategy.Object, mockPool.Object);
await router.RegisterServerAsync(serverConfig);
var request = new GatewayRequest { ToolName = "test_tool" };
// Act
var response = await router.RouteAsync(request);
// Assert
Assert.Equal("server-1", response.ServerId);
}
[Fact]
public async Task RouteAsync_TracksPassiveHealth_OnSuccess()
{
// Arrange
var serverConfig = new ServerConfig
{
Id = "server-1",
Name = "Test Server",
TransportType = "Http",
BaseUrl = "http://localhost:5000"
};
var mockConnection = new Mock<IServerConnection>();
mockConnection.Setup(c => c.IsConnected).Returns(true);
mockConnection.Setup(c => c.ServerInfo).Returns(new ServerInfo { Id = "server-1", IsHealthy = true, ResponseTime = TimeSpan.FromMilliseconds(50) });
mockConnection.Setup(c => c.SendRequestAsync(It.IsAny<GatewayRequest>(), It.IsAny<CancellationToken>()))
.ReturnsAsync(new GatewayResponse { Success = true });
var mockPool = new Mock<IServerConnectionPool>();
mockPool.Setup(p => p.GetConnectionAsync(It.IsAny<ServerConfig>(), It.IsAny<CancellationToken>()))
.ReturnsAsync(mockConnection.Object);
var mockStrategy = new Mock<IRoutingStrategy>();
mockStrategy.Setup(s => s.SelectServerAsync(It.IsAny<IEnumerable<ServerInfo>>(), It.IsAny<RoutingContext>(), It.IsAny<CancellationToken>()))
.ReturnsAsync(new ServerInfo { Id = "server-1", IsHealthy = true });
var router = new GatewayRouter(mockStrategy.Object, mockPool.Object);
await router.RegisterServerAsync(serverConfig);
var request = new GatewayRequest { ToolName = "test_tool" };
// Act
await router.RouteAsync(request);
// Assert - passive health should be tracked (implementation will verify this)
mockConnection.Verify(c => c.SendRequestAsync(request, It.IsAny<CancellationToken>()), Times.Once);
}
[Fact]
public async Task RouteAsync_TracksPassiveHealth_OnFailure()
{
// Arrange
var serverConfig = new ServerConfig
{
Id = "server-1",
Name = "Test Server",
TransportType = "Http",
BaseUrl = "http://localhost:5000"
};
var mockConnection = new Mock<IServerConnection>();
mockConnection.Setup(c => c.IsConnected).Returns(true);
mockConnection.Setup(c => c.ServerInfo).Returns(new ServerInfo { Id = "server-1", IsHealthy = true });
mockConnection.Setup(c => c.SendRequestAsync(It.IsAny<GatewayRequest>(), It.IsAny<CancellationToken>()))
.ThrowsAsync(new Exception("Request failed"));
var mockPool = new Mock<IServerConnectionPool>();
mockPool.Setup(p => p.GetConnectionAsync(It.IsAny<ServerConfig>(), It.IsAny<CancellationToken>()))
.ReturnsAsync(mockConnection.Object);
var mockStrategy = new Mock<IRoutingStrategy>();
mockStrategy.Setup(s => s.SelectServerAsync(It.IsAny<IEnumerable<ServerInfo>>(), It.IsAny<RoutingContext>(), It.IsAny<CancellationToken>()))
.ReturnsAsync(new ServerInfo { Id = "server-1", IsHealthy = true });
var router = new GatewayRouter(mockStrategy.Object, mockPool.Object);
await router.RegisterServerAsync(serverConfig);
var request = new GatewayRequest { ToolName = "test_tool" };
// Act
var response = await router.RouteAsync(request);
// Assert
Assert.False(response.Success);
Assert.Contains("Request failed", response.Error);
}
}