using Xunit; using Moq; using Svrnty.MCP.Gateway.Infrastructure.Connection; using Svrnty.MCP.Gateway.Core.Interfaces; using Svrnty.MCP.Gateway.Core.Models; namespace Svrnty.MCP.Gateway.Infrastructure.Tests.Connection; /// /// Unit tests for ServerConnection following TDD approach. /// Tests connection lifecycle and request handling. /// public class ServerConnectionTests { [Fact] public async Task ConnectAsync_OpensTransport() { // Arrange var mockTransport = new Mock(); mockTransport.Setup(t => t.ConnectAsync(It.IsAny())) .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()), Times.Once); Assert.True(connection.IsConnected); } [Fact] public async Task DisconnectAsync_ClosesTransport() { // Arrange var mockTransport = new Mock(); mockTransport.Setup(t => t.DisconnectAsync(It.IsAny())).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()), Times.Once); Assert.False(connection.IsConnected); } [Fact] public async Task SendRequestAsync_ForwardsToTransport() { // Arrange var mockTransport = new Mock(); var request = new GatewayRequest { ToolName = "test_tool" }; var expectedResponse = new GatewayResponse { Success = true }; mockTransport .Setup(t => t.SendRequestAsync(It.IsAny(), It.IsAny())) .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()), Times.Once); } [Fact] public async Task SendRequestAsync_WithTimeout_ThrowsOperationCanceledException() { // Arrange var mockTransport = new Mock(); mockTransport .Setup(t => t.SendRequestAsync(It.IsAny(), It.IsAny())) .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(() => connection.SendRequestAsync(request)); } [Fact] public void ServerInfo_ReturnsCorrectInfo() { // Arrange var mockTransport = new Mock(); 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(); 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(); mockTransport.Setup(t => t.ConnectAsync(It.IsAny())).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()), Times.Exactly(2)); } [Fact] public void Dispose_DisposesTransport() { // Arrange var mockTransport = new Mock(); 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); } }