using Dapper; using KArtSell.Host.Infrastructure; using Npgsql; using Xunit; namespace KArtSell.Integration.Tests; [Collection("Database")] public class KisConnectionPoolTests : IAsyncLifetime { private readonly NpgsqlDataSource _dataSource; private readonly KisConnectionPool _pool; public KisConnectionPoolTests(DatabaseFixture fixture) { _dataSource = fixture.DataSource; _pool = new KisConnectionPool(_dataSource, new HttpClient(), fixture.Logger()); } public async Task InitializeAsync() { await using var conn = await _dataSource.OpenConnectionAsync(); await conn.ExecuteAsync(""" CREATE SCHEMA IF NOT EXISTS kis; CREATE TABLE IF NOT EXISTS kis.connection_pool_state ( id BIGSERIAL PRIMARY KEY, connection_id UUID NOT NULL UNIQUE, state VARCHAR(50) NOT NULL, priority INT NOT NULL, token_hash VARCHAR(256), expires_at TIMESTAMP WITH TIME ZONE, created_at TIMESTAMP WITH TIME ZONE NOT NULL, released_at TIMESTAMP WITH TIME ZONE, published_at TIMESTAMP WITH TIME ZONE NOT NULL ); CREATE TABLE IF NOT EXISTS kis.token_refresh_log ( id BIGSERIAL PRIMARY KEY, connection_id UUID NOT NULL, refresh_at TIMESTAMP WITH TIME ZONE NOT NULL, status VARCHAR(50) NOT NULL, error_message TEXT, new_token_hash VARCHAR(256), executed_at TIMESTAMP WITH TIME ZONE NOT NULL, published_at TIMESTAMP WITH TIME ZONE NOT NULL ); TRUNCATE kis.connection_pool_state CASCADE; TRUNCATE kis.token_refresh_log CASCADE; """); } public async Task DisposeAsync() { await _pool.DisposeAsync(); } [Fact] public async Task AcquireAsync_CreatesConnection_WhenPoolEmpty() { // Act var conn = await _pool.AcquireAsync(); // Assert Assert.NotEqual(Guid.Empty, conn.ConnectionId); Assert.Equal("active", conn.State); } [Fact] public async Task AcquireAsync_MaintainsPoolSize_Between3And5() { // Act - Create 5 connections var connections = new List(); for (int i = 0; i < 5; i++) { connections.Add(await _pool.AcquireAsync()); } // Assert - Verify count in database await using var dbConn = await _dataSource.OpenConnectionAsync(); var count = await dbConn.QueryFirstOrDefaultAsync( "SELECT COUNT(*) FROM kis.connection_pool_state WHERE state IN ('active', 'idle')"); Assert.True(count <= 5, $"Pool size exceeded: {count}"); } [Fact] public async Task ReleaseAsync_ReturnsConnectionToPool_Idempotent() { // Arrange var conn = await _pool.AcquireAsync(); var connId = conn.ConnectionId; // Act - Release and re-acquire await _pool.ReleaseAsync(connId); var reused = await _pool.AcquireAsync(); // Assert - Should reuse released connection Assert.Equal(connId, reused.ConnectionId); } }