diff --git a/tests/Clean.Architecture.FunctionalTests/CustomWebApplicationFactory.cs b/tests/Clean.Architecture.FunctionalTests/CustomWebApplicationFactory.cs index 04c25f397..0a040d0df 100644 --- a/tests/Clean.Architecture.FunctionalTests/CustomWebApplicationFactory.cs +++ b/tests/Clean.Architecture.FunctionalTests/CustomWebApplicationFactory.cs @@ -1,127 +1,127 @@ -using Clean.Architecture.Infrastructure.Data; -using Microsoft.EntityFrameworkCore; -using Microsoft.Extensions.Configuration; -using Testcontainers.MsSql; - -namespace Clean.Architecture.FunctionalTests; - -public class CustomWebApplicationFactory : WebApplicationFactory, IAsyncLifetime where TProgram : class -{ - private MsSqlContainer? _dbContainer; - - public async ValueTask InitializeAsync() - { - try - { - _dbContainer = new MsSqlBuilder("mcr.microsoft.com/mssql/server:2022-latest") - .WithPassword("Your_password123!") - .Build(); - await _dbContainer.StartAsync(); - } - catch (ArgumentException) - { - // Docker is not available; fall back to SQLite (configured via appsettings.Testing.json) - _dbContainer = null; - } - } - - public new async ValueTask DisposeAsync() - { - // Clean up environment variable - Environment.SetEnvironmentVariable("USE_SQL_SERVER", null); - if (_dbContainer != null) - { - await _dbContainer.DisposeAsync(); - } - } - - /// - /// Overriding CreateHost to avoid creating a separate ServiceProvider per this thread: - /// https://github.com/dotnet-architecture/eShopOnWeb/issues/465 - /// - /// - /// - protected override IHost CreateHost(IHostBuilder builder) - { - builder.UseEnvironment("Testing"); // will not send real emails - var host = builder.Build(); - host.Start(); - - // Get service provider. - var serviceProvider = host.Services; - - // Create a scope to obtain a reference to the database - // context (AppDbContext). - using (var scope = serviceProvider.CreateScope()) - { - var scopedServices = scope.ServiceProvider; - var db = scopedServices.GetRequiredService(); - - var logger = scopedServices - .GetRequiredService>>(); - - try - { - // Functional tests use EnsureCreated to avoid migration-script coupling. - db.Database.EnsureCreated(); - - // Seed the database with test data only if it has not been seeded yet. - // This is safe for container reuse across test runs and multiple fixture instances. - SeedData.InitializeAsync(db).GetAwaiter().GetResult(); - } - catch (Exception ex) - { - logger.LogError(ex, "An error occurred seeding the database with test messages. Error: {exceptionMessage}", ex.Message); - throw; - } - } - - return host; - } - - protected override void ConfigureWebHost(IWebHostBuilder builder) - { - if (_dbContainer != null) - { - // Force SQL Server mode even on non-Windows platforms for functional tests - Environment.SetEnvironmentVariable("USE_SQL_SERVER", "true"); - } - - builder - .ConfigureAppConfiguration((context, config) => - { - if (_dbContainer != null) - { - // Set the connection string to use the Testcontainer - config.AddInMemoryCollection(new Dictionary - { - ["ConnectionStrings:DefaultConnection"] = _dbContainer.GetConnectionString() - }); - } - }) - .ConfigureServices(services => - { - if (_dbContainer != null) - { - // Remove the app's ApplicationDbContext registration - var descriptors = services.Where( - d => d.ServiceType == typeof(AppDbContext) || - d.ServiceType == typeof(DbContextOptions)) - .ToList(); - - foreach (var descriptor in descriptors) - { - services.Remove(descriptor); - } - - // Add ApplicationDbContext using the Testcontainers SQL Server instance - services.AddDbContext((provider, options) => - { - options.UseSqlServer(_dbContainer.GetConnectionString()); - var interceptor = provider.GetRequiredService(); - options.AddInterceptors(interceptor); - }); - } - }); - } -} +using Clean.Architecture.Infrastructure.Data; +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.Configuration; +using Testcontainers.MsSql; + +namespace Clean.Architecture.FunctionalTests; + +public class CustomWebApplicationFactory : WebApplicationFactory, IAsyncLifetime where TProgram : class +{ + private MsSqlContainer? _dbContainer; + + public async ValueTask InitializeAsync() + { + try + { + _dbContainer = new MsSqlBuilder("mcr.microsoft.com/mssql/server:2025-latest") + .WithPassword("Your_password123!") + .Build(); + await _dbContainer.StartAsync(); + } + catch (ArgumentException) + { + // Docker is not available; fall back to SQLite (configured via appsettings.Testing.json) + _dbContainer = null; + } + } + + public new async ValueTask DisposeAsync() + { + // Clean up environment variable + Environment.SetEnvironmentVariable("USE_SQL_SERVER", null); + if (_dbContainer != null) + { + await _dbContainer.DisposeAsync(); + } + } + + /// + /// Overriding CreateHost to avoid creating a separate ServiceProvider per this thread: + /// https://github.com/dotnet-architecture/eShopOnWeb/issues/465 + /// + /// + /// + protected override IHost CreateHost(IHostBuilder builder) + { + builder.UseEnvironment("Testing"); // will not send real emails + var host = builder.Build(); + host.Start(); + + // Get service provider. + var serviceProvider = host.Services; + + // Create a scope to obtain a reference to the database + // context (AppDbContext). + using (var scope = serviceProvider.CreateScope()) + { + var scopedServices = scope.ServiceProvider; + var db = scopedServices.GetRequiredService(); + + var logger = scopedServices + .GetRequiredService>>(); + + try + { + // Functional tests use EnsureCreated to avoid migration-script coupling. + db.Database.EnsureCreated(); + + // Seed the database with test data only if it has not been seeded yet. + // This is safe for container reuse across test runs and multiple fixture instances. + SeedData.InitializeAsync(db).GetAwaiter().GetResult(); + } + catch (Exception ex) + { + logger.LogError(ex, "An error occurred seeding the database with test messages. Error: {exceptionMessage}", ex.Message); + throw; + } + } + + return host; + } + + protected override void ConfigureWebHost(IWebHostBuilder builder) + { + if (_dbContainer != null) + { + // Force SQL Server mode even on non-Windows platforms for functional tests + Environment.SetEnvironmentVariable("USE_SQL_SERVER", "true"); + } + + builder + .ConfigureAppConfiguration((context, config) => + { + if (_dbContainer != null) + { + // Set the connection string to use the Testcontainer + config.AddInMemoryCollection(new Dictionary + { + ["ConnectionStrings:DefaultConnection"] = _dbContainer.GetConnectionString() + }); + } + }) + .ConfigureServices(services => + { + if (_dbContainer != null) + { + // Remove the app's ApplicationDbContext registration + var descriptors = services.Where( + d => d.ServiceType == typeof(AppDbContext) || + d.ServiceType == typeof(DbContextOptions)) + .ToList(); + + foreach (var descriptor in descriptors) + { + services.Remove(descriptor); + } + + // Add ApplicationDbContext using the Testcontainers SQL Server instance + services.AddDbContext((provider, options) => + { + options.UseSqlServer(_dbContainer.GetConnectionString()); + var interceptor = provider.GetRequiredService(); + options.AddInterceptors(interceptor); + }); + } + }); + } +}