fix(backend): Fix Integration Test database provider conflict with environment-aware DI

Implement environment-aware dependency injection to resolve EF Core provider conflict
in Integration Tests. The issue was caused by both PostgreSQL and InMemory providers
being registered in the same service provider.

Changes:
- Modified Identity Module DependencyInjection to skip PostgreSQL DbContext registration in Testing environment
- Modified ProjectManagement Module ModuleExtensions with same environment check
- Updated Program.cs to pass IHostEnvironment to both module registration methods
- Added Microsoft.Extensions.Hosting.Abstractions package to Identity.Infrastructure project
- Updated ColaFlowWebApplicationFactory to set Testing environment and register InMemory databases
- Simplified WebApplicationFactory by removing complex RemoveAll logic

Results:
- All 31 Integration Tests now run (previously only 1 ran)
- No EF Core provider conflict errors
- 23 tests pass, 8 tests fail (failures are business logic issues, not infrastructure)
- Production environment still uses PostgreSQL as expected

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
Yaojia Wang
2025-11-03 17:16:31 +01:00
parent 69e23d9d2a
commit ebdd4ee0d7
5 changed files with 164 additions and 13 deletions

View File

@@ -0,0 +1,132 @@
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Mvc.Testing;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;
using Microsoft.Extensions.Hosting;
using ColaFlow.Modules.Identity.Infrastructure.Persistence;
using ColaFlow.Modules.ProjectManagement.Infrastructure.Persistence;
namespace ColaFlow.Modules.Identity.IntegrationTests.Infrastructure;
/// <summary>
/// Custom WebApplicationFactory for ColaFlow Integration Tests
/// Supports both In-Memory and Real PostgreSQL databases
/// </summary>
public class ColaFlowWebApplicationFactory : WebApplicationFactory<Program>
{
private readonly bool _useInMemoryDatabase;
private readonly string? _testDatabaseName;
public ColaFlowWebApplicationFactory(bool useInMemoryDatabase = true, string? testDatabaseName = null)
{
_useInMemoryDatabase = useInMemoryDatabase;
_testDatabaseName = testDatabaseName ?? $"TestDb_{Guid.NewGuid()}";
}
protected override void ConfigureWebHost(IWebHostBuilder builder)
{
// Set environment to Testing - this prevents PostgreSQL DbContext registration in modules
builder.UseEnvironment("Testing");
// Configure test-specific settings
builder.ConfigureAppConfiguration((context, config) =>
{
// Clear existing connection strings to prevent PostgreSQL registration
config.Sources.Clear();
// Add minimal config for testing
config.AddInMemoryCollection(new Dictionary<string, string?>
{
["ConnectionStrings:DefaultConnection"] = "",
["ConnectionStrings:PMDatabase"] = "",
["Jwt:SecretKey"] = "test-secret-key-for-integration-tests-minimum-32-characters",
["Jwt:Issuer"] = "ColaFlow.Test",
["Jwt:Audience"] = "ColaFlow.Test",
["Jwt:AccessTokenExpirationMinutes"] = "15",
["Jwt:RefreshTokenExpirationDays"] = "7"
});
});
builder.ConfigureServices(services =>
{
// Register test databases (modules won't register PostgreSQL due to Testing environment)
if (_useInMemoryDatabase)
{
// Use In-Memory Database for fast, isolated tests
// IMPORTANT: Share the same database name for cross-context data consistency
services.AddDbContext<IdentityDbContext>(options =>
{
options.UseInMemoryDatabase(_testDatabaseName!);
options.EnableSensitiveDataLogging();
});
services.AddDbContext<PMDbContext>(options =>
{
options.UseInMemoryDatabase(_testDatabaseName!);
options.EnableSensitiveDataLogging();
});
}
else
{
// Use Real PostgreSQL for integration tests
var connectionString = $"Host=localhost;Port=5432;Database=colaflow_test_{_testDatabaseName};Username=postgres;Password=postgres";
services.AddDbContext<IdentityDbContext>(options =>
{
options.UseNpgsql(connectionString);
options.EnableSensitiveDataLogging();
});
services.AddDbContext<PMDbContext>(options =>
{
options.UseNpgsql(connectionString);
options.EnableSensitiveDataLogging();
});
}
});
}
// Override CreateHost to initialize databases after the host is created
protected override IHost CreateHost(IHostBuilder builder)
{
var host = base.CreateHost(builder);
// Initialize databases after host is created
using var scope = host.Services.CreateScope();
var services = scope.ServiceProvider;
try
{
// Initialize Identity database
var identityDb = services.GetRequiredService<IdentityDbContext>();
if (_useInMemoryDatabase)
{
identityDb.Database.EnsureCreated();
}
else
{
identityDb.Database.Migrate();
}
// Initialize ProjectManagement database
var pmDb = services.GetRequiredService<PMDbContext>();
if (_useInMemoryDatabase)
{
pmDb.Database.EnsureCreated();
}
else
{
pmDb.Database.Migrate();
}
}
catch (Exception ex)
{
Console.WriteLine($"Error initializing test database: {ex.Message}");
throw;
}
return host;
}
}