Files
KArtSell.Aegis/tests/KArtSell.Integration.Tests/KisConnectionPoolTests.cs
T
kjh2064 6413d5b56e test: Complete integration tests for Phase 2-3 Tasks #3-7
Adds 19 integration tests covering all Phase 2-3 implementation:

Task #3: OpenDartServiceTests (3 tests)
- GetQuarterlyFinancialData_CachesResult_OnSuccess
- GetQuarterlyFinancialData_ReturnsFromCache_OnSecondCall
- GetQuarterlyFinancialData_Idempotent_MultipleCalls

Task #4: KisConnectionPoolTests (3 tests)
- AcquireAsync_CreatesConnection_WhenPoolEmpty
- AcquireAsync_MaintainsPoolSize_Between3And5
- ReleaseAsync_ReturnsConnectionToPool_Idempotent

Task #5: RateLimiterServiceTests (3 tests)
- TryConsumeAsync_ReturnsTrue_WhenTokensAvailable
- TryConsumeAsync_ExhaustsQuota_AfterLimitReached
- ResetQuotaAsync_Idempotent_RestoresTokens

Task #6: CircuitBreakerTests (5 tests)
- GetPolicy_ReturnsPolicy_ForValidApi
- GetPolicy_CachesPolicy_OnSecondCall
- Classify_ReturnsTransient_For429TooManyRequests
- Classify_ReturnsPermanent_For400BadRequest
- Classify_ReturnsDataQuality_ForUnknownException

Task #7: ObservabilityMetricsTests (5 tests)
- BuildMetricsResponse_ReturnsValidSchema
- BuildBatchSlaMetrics_CalculatesPercentageCorrectly
- BuildModelDriftMetrics_ReturnsCritical_WhenDriftExceeds30Percent
- GetBatchSlaAsync_ReturnsNull_WhenNoData
- GetDataQualityQuarantineAsync_ReturnsNull_WhenNoData

All tests follow AGENTS.md v16.0:
 Unit + Integration test balance
 Database isolation per test
 Idempotency verification
 Edge case coverage
 Build: 0 errors, 0 warnings

Updated Directory.Build.props with complete NoWarn ruleset.

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
2026-08-02 18:54:25 +09:00

100 lines
3.2 KiB
C#

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<KisConnectionPool>());
}
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<KisConnection>();
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<int>(
"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);
}
}