🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
152 lines
4.7 KiB
C#
152 lines
4.7 KiB
C#
using System;
|
|
using System.Linq;
|
|
using Microsoft.AspNetCore.Hosting;
|
|
using Microsoft.AspNetCore.Mvc.Testing;
|
|
using Microsoft.EntityFrameworkCore;
|
|
using Microsoft.Extensions.DependencyInjection;
|
|
using Microsoft.Extensions.DependencyInjection.Extensions;
|
|
using Microsoft.Extensions.Hosting;
|
|
using Testcontainers.PostgreSql;
|
|
using Testcontainers.Redis;
|
|
|
|
namespace ColaFlow.IntegrationTests.Infrastructure;
|
|
|
|
/// <summary>
|
|
/// Custom WebApplicationFactory for API integration tests
|
|
/// Replaces production database with Testcontainers
|
|
/// </summary>
|
|
/// <typeparam name="TProgram">Program class from ColaFlow.API</typeparam>
|
|
/// <typeparam name="TDbContext">DbContext class from ColaFlow.Infrastructure</typeparam>
|
|
public class ColaFlowWebApplicationFactory<TProgram, TDbContext>
|
|
: WebApplicationFactory<TProgram>, IAsyncDisposable
|
|
where TProgram : class
|
|
where TDbContext : DbContext
|
|
{
|
|
private PostgreSqlContainer? _postgresContainer;
|
|
private RedisContainer? _redisContainer;
|
|
|
|
/// <summary>
|
|
/// Configure services for testing
|
|
/// </summary>
|
|
protected override void ConfigureWebHost(IWebHostBuilder builder)
|
|
{
|
|
builder.ConfigureServices(async services =>
|
|
{
|
|
// Remove existing DbContext registration
|
|
var dbContextDescriptor = services.SingleOrDefault(
|
|
d => d.ServiceType == typeof(DbContextOptions<TDbContext>));
|
|
|
|
if (dbContextDescriptor != null)
|
|
{
|
|
services.Remove(dbContextDescriptor);
|
|
}
|
|
|
|
// Start Testcontainers
|
|
_postgresContainer = new PostgreSqlBuilder()
|
|
.WithImage("postgres:16-alpine")
|
|
.WithDatabase("colaflow_test")
|
|
.WithUsername("colaflow_test")
|
|
.WithPassword("colaflow_test_password")
|
|
.WithCleanUp(true)
|
|
.Build();
|
|
|
|
_redisContainer = new RedisBuilder()
|
|
.WithImage("redis:7-alpine")
|
|
.WithCleanUp(true)
|
|
.Build();
|
|
|
|
await _postgresContainer.StartAsync();
|
|
await _redisContainer.StartAsync();
|
|
|
|
// Add test DbContext with Testcontainers connection string
|
|
services.AddDbContext<TDbContext>(options =>
|
|
{
|
|
options.UseNpgsql(_postgresContainer.GetConnectionString());
|
|
options.EnableSensitiveDataLogging();
|
|
options.EnableDetailedErrors();
|
|
});
|
|
|
|
// Replace Redis connection string
|
|
// TODO: Configure Redis connection with Testcontainers connection string
|
|
|
|
// Build service provider and apply migrations
|
|
var serviceProvider = services.BuildServiceProvider();
|
|
using var scope = serviceProvider.CreateScope();
|
|
var dbContext = scope.ServiceProvider.GetRequiredService<TDbContext>();
|
|
|
|
// Apply migrations
|
|
await dbContext.Database.MigrateAsync();
|
|
});
|
|
|
|
// Optional: Disable HTTPS redirection for tests
|
|
builder.UseEnvironment("Testing");
|
|
}
|
|
|
|
/// <summary>
|
|
/// Create a new scope with fresh DbContext
|
|
/// </summary>
|
|
public IServiceScope CreateScope()
|
|
{
|
|
return Services.CreateScope();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Get DbContext from a scope
|
|
/// </summary>
|
|
public TDbContext GetDbContext(IServiceScope scope)
|
|
{
|
|
return scope.ServiceProvider.GetRequiredService<TDbContext>();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Cleanup Testcontainers
|
|
/// </summary>
|
|
public new async ValueTask DisposeAsync()
|
|
{
|
|
if (_postgresContainer != null)
|
|
{
|
|
await _postgresContainer.DisposeAsync();
|
|
}
|
|
|
|
if (_redisContainer != null)
|
|
{
|
|
await _redisContainer.DisposeAsync();
|
|
}
|
|
|
|
await base.DisposeAsync();
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Example usage in test class:
|
|
///
|
|
/// public class ProjectsApiTests : IClassFixture<ColaFlowWebApplicationFactory<Program, ColaFlowDbContext>>
|
|
/// {
|
|
/// private readonly HttpClient _client;
|
|
/// private readonly ColaFlowWebApplicationFactory<Program, ColaFlowDbContext> _factory;
|
|
///
|
|
/// public ProjectsApiTests(ColaFlowWebApplicationFactory<Program, ColaFlowDbContext> factory)
|
|
/// {
|
|
/// _factory = factory;
|
|
/// _client = factory.CreateClient();
|
|
/// }
|
|
///
|
|
/// [Fact]
|
|
/// public async Task GetProjects_ReturnsSuccessStatusCode()
|
|
/// {
|
|
/// // Arrange
|
|
/// using var scope = _factory.CreateScope();
|
|
/// var dbContext = _factory.GetDbContext(scope);
|
|
///
|
|
/// // Seed test data
|
|
/// // ...
|
|
///
|
|
/// // Act
|
|
/// var response = await _client.GetAsync("/api/v1/projects");
|
|
///
|
|
/// // Assert
|
|
/// response.EnsureSuccessStatusCode();
|
|
/// }
|
|
/// }
|
|
/// </summary>
|