using Baya.Infrastructure.Identity.Identity.SeedDatabaseService; using Baya.Infrastructure.Persistence; using Baya.Infrastructure.Persistence.Interceptors; using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Mvc.Testing; using Microsoft.Data.Sqlite; using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Infrastructure; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection.Extensions; using Microsoft.Extensions.Hosting; namespace Baya.Test.Api; /// /// Boots the whole API (Program.cs wiring: envelope filters, JWE auth, rate limiter, Mediator) in the /// "Testing" environment over an isolated in-memory SQLite database. Program skips SQL Server /// migrations/seeding for this environment; the factory does EnsureCreated + the role/admin seed. /// Use one factory per test class — the rate limiter is per-host, so a fresh host keeps each class /// inside the OTP/auth per-IP budgets. /// public sealed class BayaApiFactory : WebApplicationFactory { // A named shared-cache in-memory database (kept alive by this connection) instead of a single // shared SqliteConnection instance: request scopes and hosted services open their own // connections, so nothing initializes one connection concurrently. private readonly string _connectionString = $"Data Source={Guid.NewGuid():N};Mode=Memory;Cache=Shared"; private readonly SqliteConnection _keepAlive; public BayaApiFactory() { _keepAlive = new SqliteConnection(_connectionString); _keepAlive.Open(); } protected override void ConfigureWebHost(IWebHostBuilder builder) { builder.UseEnvironment("Testing"); builder.ConfigureServices(services => { // Swap the SQL Server DbContext for in-memory SQLite. EF 8+ keeps AddDbContext's // option lambda in IDbContextOptionsConfiguration — it must go too, or both providers // end up configured on the same options. services.RemoveAll(typeof(IDbContextOptionsConfiguration)); services.RemoveAll(typeof(DbContextOptions)); services.AddDbContext((serviceProvider, options) => { options .UseSqlite(_connectionString) .AddInterceptors(serviceProvider.GetRequiredService()); }); }); } protected override IHost CreateHost(IHostBuilder builder) { var host = base.CreateHost(builder); using var scope = host.Services.CreateScope(); scope.ServiceProvider.GetRequiredService().Database.EnsureCreated(); scope.ServiceProvider.GetRequiredService().Seed().GetAwaiter().GetResult(); return host; } protected override void Dispose(bool disposing) { base.Dispose(disposing); _keepAlive.Dispose(); } }