154 lines
7.1 KiB
C#
154 lines
7.1 KiB
C#
using Microsoft.AspNetCore.Authentication;
|
|
using Microsoft.AspNetCore.Hosting;
|
|
using Microsoft.AspNetCore.Mvc.Testing;
|
|
using Microsoft.AspNetCore.TestHost;
|
|
using Microsoft.EntityFrameworkCore;
|
|
using Microsoft.Extensions.Configuration;
|
|
using Microsoft.Extensions.DependencyInjection;
|
|
using Microsoft.Extensions.DependencyInjection.Extensions;
|
|
using Microsoft.Extensions.Hosting;
|
|
using StackExchange.Redis;
|
|
|
|
public class ApiFixture : WebApplicationFactory<Program>, 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;
|
|
|
|
public static string SimulationScenarioDirectory { get; } = Path.GetFullPath(Path.Combine(
|
|
AppContext.BaseDirectory, "Fixtures", "Scenarios"));
|
|
|
|
public TestSimulationClientFactory SimulationClientFactory =>
|
|
Services.GetRequiredService<TestSimulationClientFactory>();
|
|
|
|
// 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<string, string?>
|
|
{
|
|
["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",
|
|
// Neutralize appsettings.Production.json if the process env was Production.
|
|
["RabbitMq:UseSsl"] = "false",
|
|
["Kafka:BootstrapServers"] =
|
|
Environment.GetEnvironmentVariable("Kafka__BootstrapServers") ?? "localhost:9092",
|
|
["Kafka:ReplicationFactor"] =
|
|
Environment.GetEnvironmentVariable("Kafka__ReplicationFactor") ?? "1",
|
|
["Kafka:SecurityProtocol"] =
|
|
Environment.GetEnvironmentVariable("Kafka__SecurityProtocol") ?? "Plaintext",
|
|
["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",
|
|
["Minio:UseSSL"] =
|
|
Environment.GetEnvironmentVariable("Minio__UseSSL") ?? "false",
|
|
["Fhir:ApiKey"] = "dev-integration-key-change-in-production",
|
|
["ApiKey:Gateway"] = GatewayAuthHelper.DevGatewayKey,
|
|
});
|
|
|
|
config.AddJsonFile("appsettings.Testing.json", optional: true, reloadOnChange: false);
|
|
|
|
// Win over appsettings.Testing.json catalogue path / concurrency defaults.
|
|
config.AddInMemoryCollection(new Dictionary<string, string?>
|
|
{
|
|
["Simulation:Enabled"] = "true",
|
|
["Simulation:ScenarioDirectory"] = SimulationScenarioDirectory,
|
|
["Simulation:MaxConcurrentRuns"] = "2",
|
|
["Simulation:MaxSpeed"] = "600",
|
|
["Simulation:RunHistoryLimit"] = "50",
|
|
});
|
|
});
|
|
|
|
builder.ConfigureServices(services =>
|
|
{
|
|
services.Configure<HostOptions>(o =>
|
|
o.BackgroundServiceExceptionBehavior = BackgroundServiceExceptionBehavior.Ignore);
|
|
|
|
services.AddAuthentication(options =>
|
|
{
|
|
options.DefaultAuthenticateScheme = TestingAuthHandler.SchemeName;
|
|
options.DefaultChallengeScheme = TestingAuthHandler.SchemeName;
|
|
})
|
|
.AddScheme<AuthenticationSchemeOptions, TestingAuthHandler>(
|
|
TestingAuthHandler.SchemeName, _ => { });
|
|
});
|
|
|
|
builder.ConfigureTestServices(services =>
|
|
{
|
|
services.RemoveAll<ISimulationClientFactory>();
|
|
services.AddSingleton<TestSimulationClientFactory>(_ =>
|
|
new TestSimulationClientFactory(this));
|
|
services.AddSingleton<ISimulationClientFactory>(sp =>
|
|
sp.GetRequiredService<TestSimulationClientFactory>());
|
|
});
|
|
}
|
|
|
|
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<AppDbContext>()
|
|
.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",
|
|
});
|
|
|
|
// Flush Redis before starting the host. Accessing Services boots many hosted
|
|
// services that saturate the thread pool; FLUSHDB on the shared multiplexer
|
|
// then times out waiting for a reply that already arrived (see SE.Redis
|
|
// TimeoutException: last-in/cur-in stuck, QueuedItems > 0).
|
|
await using (var redis = await ConnectionMultiplexer.ConnectAsync(RedisConnection))
|
|
{
|
|
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();
|
|
}
|
|
}
|