using Microsoft.AspNetCore.Authentication; using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Mvc.Testing; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using StackExchange.Redis; public class ApiFixture : WebApplicationFactory, IAsyncLifetime { // Prefer CI/env overrides; fall back to local docker-compose.yml host ports. public static string PgConnection { get; } = Environment.GetEnvironmentVariable("ConnectionStrings__DefaultConnection") ?? "Host=localhost;Port=5436;Database=vigilcare_test;Username=postgres;Password=password"; public static string RedisConnection { get; } = Environment.GetEnvironmentVariable("Redis__ConnectionString") ?? "localhost:6382,defaultDatabase=1,allowAdmin=true"; public static string RabbitHost { get; } = Environment.GetEnvironmentVariable("RabbitMq__Host") ?? "localhost"; public static int RabbitPort { get; } = int.TryParse(Environment.GetEnvironmentVariable("RabbitMq__Port"), out var p) ? p : 5674; // Override configuration to point at a test database — never run tests against // the development database; a botched rollback could corrupt seed data. protected override void ConfigureWebHost(IWebHostBuilder builder) { builder.UseEnvironment("Testing"); builder.ConfigureAppConfiguration((_, config) => { config.AddInMemoryCollection(new Dictionary { ["ConnectionStrings:DefaultConnection"] = PgConnection, ["Redis:ConnectionString"] = RedisConnection, ["RabbitMq:Host"] = RabbitHost, ["RabbitMq:Port"] = RabbitPort.ToString(), ["RabbitMq:Username"] = Environment.GetEnvironmentVariable("RabbitMq__Username") ?? "guest", ["RabbitMq:Password"] = Environment.GetEnvironmentVariable("RabbitMq__Password") ?? "guest", ["RabbitMq:PagingAckTimeoutMs"] = "5000", ["RabbitMq:VirtualHost"] = "vigilcare_test", ["Kafka:BootstrapServers"] = Environment.GetEnvironmentVariable("Kafka__BootstrapServers") ?? "localhost:9092", ["Kafka:ReplicationFactor"] = Environment.GetEnvironmentVariable("Kafka__ReplicationFactor") ?? "1", ["Kafka:NotificationPublisherGroupId"] = "notification-publisher-integration-test", ["Kafka:NotificationPublisherAutoOffsetReset"] = "Latest", ["Elasticsearch:Uri"] = Environment.GetEnvironmentVariable("Elasticsearch__Uri") ?? "http://localhost:9200", ["Minio:Endpoint"] = Environment.GetEnvironmentVariable("Minio__Endpoint") ?? "localhost:9005", ["Fhir:ApiKey"] = "dev-integration-key-change-in-production", ["ApiKey:Gateway"] = GatewayAuthHelper.DevGatewayKey, }); config.AddJsonFile("appsettings.Testing.json", optional: true, reloadOnChange: false); }); builder.ConfigureServices(services => { services.AddAuthentication(options => { options.DefaultAuthenticateScheme = TestingAuthHandler.SchemeName; options.DefaultChallengeScheme = TestingAuthHandler.SchemeName; }) .AddScheme( TestingAuthHandler.SchemeName, _ => { }); }); } public async Task InitializeAsync() { // Apply migrations and clean stale data before the host starts — background // services such as ThresholdCacheLoader query the database during StartAsync, // so the reset must happen while no hosted service holds a lock. var options = new DbContextOptionsBuilder() .UseNpgsql(PgConnection) .Options; await using (var migrateDb = new AppDbContext(options)) { await migrateDb.Database.MigrateAsync(); await DbResetHelper.ResetAsync(migrateDb); } await RabbitMqTestHelper.EnsureVirtualHostAsync(new RabbitMqOptions { Host = RabbitHost, Port = RabbitPort, Username = Environment.GetEnvironmentVariable("RabbitMq__Username") ?? "guest", Password = Environment.GetEnvironmentVariable("RabbitMq__Password") ?? "guest", VirtualHost = "vigilcare_test", }); using var scope = Services.CreateScope(); var redis = scope.ServiceProvider.GetRequiredService(); var server = redis.GetServer(redis.GetEndPoints().First()); await server.FlushDatabaseAsync(1); } protected override void ConfigureClient(HttpClient client) { base.ConfigureClient(client); client.DefaultRequestHeaders.Add("X-Test-Role", "ADMIN"); } public new async Task DisposeAsync() { await base.DisposeAsync(); } }