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

258 lines
7.4 KiB
C#

using Xunit;
using Moq;
using OpenHarbor.MCP.Gateway.Infrastructure.Connection;
using OpenHarbor.MCP.Gateway.Core.Models;
namespace OpenHarbor.MCP.Gateway.Infrastructure.Tests.Connection;
/// <summary>
/// Unit tests for ServerConnectionPool following TDD approach.
/// Tests connection pooling, eviction, and limits.
/// </summary>
public class ServerConnectionPoolTests
{
[Fact]
public async Task GetConnectionAsync_CreatesNewConnection()
{
// Arrange
var serverConfig = new ServerConfig
{
Id = "test",
Name = "Test Server",
TransportType = "Http",
BaseUrl = "http://localhost:5000"
};
var pool = new ServerConnectionPool();
// Act
var connection = await pool.GetConnectionAsync(serverConfig);
// Assert
Assert.NotNull(connection);
Assert.Equal(serverConfig.Id, connection.ServerInfo.Id);
}
[Fact]
public async Task GetConnectionAsync_ReusesSameConnection()
{
// Arrange
var serverConfig = new ServerConfig
{
Id = "test",
Name = "Test Server",
TransportType = "Http",
BaseUrl = "http://localhost:5000"
};
var pool = new ServerConnectionPool();
// Act
var connection1 = await pool.GetConnectionAsync(serverConfig);
var connection2 = await pool.GetConnectionAsync(serverConfig);
// Assert
Assert.Same(connection1, connection2); // Should be the exact same instance
}
[Fact]
public async Task ReleaseConnectionAsync_MarksConnectionAsAvailable()
{
// Arrange
var serverConfig = new ServerConfig
{
Id = "test",
Name = "Test Server",
TransportType = "Http",
BaseUrl = "http://localhost:5000"
};
var pool = new ServerConnectionPool();
var connection = await pool.GetConnectionAsync(serverConfig);
// Act
await pool.ReleaseConnectionAsync(connection);
// Assert - should be able to get it again
var connection2 = await pool.GetConnectionAsync(serverConfig);
Assert.Same(connection, connection2);
}
[Fact]
public async Task GetConnectionAsync_WithMaxConnections_WaitsForAvailable()
{
// Arrange
var serverConfig = new ServerConfig
{
Id = "test",
Name = "Test Server",
TransportType = "Http",
BaseUrl = "http://localhost:5000"
};
var pool = new ServerConnectionPool { MaxConnectionsPerServer = 1 };
var connection1 = await pool.GetConnectionAsync(serverConfig);
// Act - try to get second connection (should wait or create new based on pool strategy)
var getTask = pool.GetConnectionAsync(serverConfig);
// Release first connection
await pool.ReleaseConnectionAsync(connection1);
var connection2 = await getTask;
// Assert
Assert.NotNull(connection2);
}
[Fact]
public async Task EvictIdleConnectionsAsync_RemovesIdleConnections()
{
// Arrange
var serverConfig = new ServerConfig
{
Id = "test",
Name = "Test Server",
TransportType = "Http",
BaseUrl = "http://localhost:5000"
};
var pool = new ServerConnectionPool { IdleTimeout = TimeSpan.FromMilliseconds(50) };
var connection = await pool.GetConnectionAsync(serverConfig);
await pool.ReleaseConnectionAsync(connection);
// Wait for idle timeout
await Task.Delay(100);
// Act
await pool.EvictIdleConnectionsAsync();
// Assert - getting connection again should create a new one
var connection2 = await pool.GetConnectionAsync(serverConfig);
Assert.NotNull(connection2);
// Note: Without access to internal state, we can't directly verify it's a new instance
// This is tested indirectly through behavior
}
[Fact]
public void GetPoolStats_ReturnsCorrectStats()
{
// Arrange
var pool = new ServerConnectionPool();
// Act
var stats = pool.GetPoolStats();
// Assert
Assert.NotNull(stats);
Assert.Equal(0, stats.TotalConnections);
Assert.Equal(0, stats.ActiveConnections);
Assert.Equal(0, stats.IdleConnections);
}
[Fact]
public async Task GetPoolStats_ReflectsActiveConnections()
{
// Arrange
var serverConfig = new ServerConfig
{
Id = "test",
Name = "Test Server",
TransportType = "Http",
BaseUrl = "http://localhost:5000"
};
var pool = new ServerConnectionPool();
// Act
await pool.GetConnectionAsync(serverConfig);
var stats = pool.GetPoolStats();
// Assert
Assert.Equal(1, stats.TotalConnections);
Assert.Equal(1, stats.ActiveConnections);
}
[Fact]
public async Task GetPoolStats_ReflectsIdleConnections()
{
// Arrange
var serverConfig = new ServerConfig
{
Id = "test",
Name = "Test Server",
TransportType = "Http",
BaseUrl = "http://localhost:5000"
};
var pool = new ServerConnectionPool();
var connection = await pool.GetConnectionAsync(serverConfig);
await pool.ReleaseConnectionAsync(connection);
// Act
var stats = pool.GetPoolStats();
// Assert
Assert.Equal(1, stats.TotalConnections);
Assert.Equal(0, stats.ActiveConnections);
Assert.Equal(1, stats.IdleConnections);
}
[Fact]
public async Task Dispose_DisposesAllConnections()
{
// Arrange
var serverConfig1 = new ServerConfig
{
Id = "test1",
Name = "Test Server 1",
TransportType = "Http",
BaseUrl = "http://localhost:5001"
};
var serverConfig2 = new ServerConfig
{
Id = "test2",
Name = "Test Server 2",
TransportType = "Http",
BaseUrl = "http://localhost:5002"
};
var pool = new ServerConnectionPool();
await pool.GetConnectionAsync(serverConfig1);
await pool.GetConnectionAsync(serverConfig2);
// Act
pool.Dispose();
// Assert - getting stats after dispose should show zero connections
var stats = pool.GetPoolStats();
Assert.Equal(0, stats.TotalConnections);
}
[Fact]
public async Task GetConnectionAsync_DifferentServers_CreatesSeparateConnections()
{
// Arrange
var serverConfig1 = new ServerConfig
{
Id = "test1",
Name = "Test Server 1",
TransportType = "Http",
BaseUrl = "http://localhost:5001"
};
var serverConfig2 = new ServerConfig
{
Id = "test2",
Name = "Test Server 2",
TransportType = "Http",
BaseUrl = "http://localhost:5002"
};
var pool = new ServerConnectionPool();
// Act
var connection1 = await pool.GetConnectionAsync(serverConfig1);
var connection2 = await pool.GetConnectionAsync(serverConfig2);
// Assert
Assert.NotSame(connection1, connection2);
Assert.Equal("test1", connection1.ServerInfo.Id);
Assert.Equal("test2", connection2.ServerInfo.Id);
}
}