using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Mvc.Testing;
using Microsoft.Data.Sqlite;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;
namespace GerbilManager.Tests;
///
/// FEAT-13: In-Memory-Test-Host für Endpoint-Round-Trips. Ersetzt die
/// Aspire/Npgsql-Registrierung durch SQLite in-memory (eine offene Verbindung
/// hält die DB am Leben), Umgebung "Testing" überspringt Database.Migrate()
/// (Npgsql-Migrationen laufen nicht auf SQLite) — Schema via EnsureCreated,
/// inklusive der HasData-Seeds (73 Farbschläge + Zuchtprofil-Singleton).
/// Vertrags-Dateien landen in einem Temp-Ordner, der mit dem Host stirbt.
///
public sealed class ApiFactory : WebApplicationFactory
{
private readonly SqliteConnection _connection = new("DataSource=:memory:");
///
/// INBOX-2: Test-spezifische DI-Überschreibungen (z. B. AI-Stub-Handler).
/// Init-Property statt Konstruktor — xUnit-Klassen-Fixtures erlauben nur
/// EINEN öffentlichen (parameterlosen) Konstruktor.
///
public Action? ConfigureTestServices { get; init; }
public string ContractRoot { get; } =
Path.Combine(Path.GetTempPath(), $"gerbil-contract-tests-{Guid.NewGuid():N}");
/// Optionaler Isolations-Override für den Import-Quellordner (Import:SourcePath),
/// damit der Upload-Ingest-Test in einem Temp-Verzeichnis arbeitet statt im echten Repo.
public string? ImportSourcePath { get; init; }
/// Optionaler Isolations-Override für den Foto-Ordner (Photos:RootPath).
public string? PhotosRootPath { get; init; }
protected override void ConfigureWebHost(IWebHostBuilder builder)
{
builder.UseEnvironment("Testing");
// Dummy, damit Aspires AddNpgsqlDbContext beim Host-Aufbau zufrieden ist;
// die eigentliche Registrierung wird unten durch SQLite ersetzt.
builder.UseSetting("ConnectionStrings:gerbilmanager",
"Host=localhost;Database=test;Username=test;Password=test");
builder.UseSetting("Contracts:RootPath", ContractRoot);
if (ImportSourcePath is not null) builder.UseSetting("Import:SourcePath", ImportSourcePath);
if (PhotosRootPath is not null) builder.UseSetting("Photos:RootPath", PhotosRootPath);
builder.ConfigureServices(services =>
{
// EF 9+: AddDbContext registriert die Provider-Konfiguration als
// IDbContextOptionsConfiguration — ohne deren Entfernung blieben
// Npgsql UND SQLite registriert (ein Provider pro ServiceProvider).
services.RemoveAll>();
services.RemoveAll>();
services.RemoveAll();
_connection.Open();
services.AddDbContext(o => o.UseSqlite(_connection));
using var provider = services.BuildServiceProvider();
using var scope = provider.CreateScope();
scope.ServiceProvider.GetRequiredService().Database.EnsureCreated();
ConfigureTestServices?.Invoke(services);
});
}
protected override void Dispose(bool disposing)
{
base.Dispose(disposing);
if (disposing)
{
_connection.Dispose();
if (Directory.Exists(ContractRoot)) Directory.Delete(ContractRoot, recursive: true);
}
}
}