Daten-Fixes (conflict-decisions.json, re-ingest-stabil) für ~30 Tickets:
Merges (Jamie/Hiro/Mino/Jana/Blacky/Sakura/Malou/Socke→Marty), Eltern-Korrekturen
(Jacky/Idefix/Ichika/Roni/Ethan), Kruke→Kuke (+ Todesdatum), Targa-Wurf R14 + Druna,
Stacy/Merle/Domi/Eliza; Joghurt-Phantomwurf entfernt.
Code-Fixes:
- Gaida & alle Verstorbenen: Status wird aus Todesdatum/Abgabe abgeleitet
(Program.cs Startup-Sweep heilt Altfälle; IngestResolved re-derived nach Freeze).
- CoCo: Scheckungsart wird bei jeder Schecke angezeigt (Platzhalter wenn leer).
- M-Wurf/Gale: über-gemergte Fremdtiere via neuem litterChildren-Override entfernt.
- renameTo eltern-verknüpfungssicher (Quell-Name im Index); dateOfDeath als Override.
Prod-fähige Triage (API):
- GET /feedback/{id} + GET /feedback?status= (kein 2-MB-Dump).
- POST /import/ingest-resolved/upload (multipart) → Ingest gegen Prod ohne SSH.
Tests: 280 Backend, 149 Frontend, alle Python, betroffene Playwright grün.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
80 lines
3.6 KiB
C#
80 lines
3.6 KiB
C#
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;
|
|
|
|
/// <summary>
|
|
/// 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.
|
|
/// </summary>
|
|
public sealed class ApiFactory : WebApplicationFactory<Program>
|
|
{
|
|
private readonly SqliteConnection _connection = new("DataSource=:memory:");
|
|
|
|
/// <summary>
|
|
/// INBOX-2: Test-spezifische DI-Überschreibungen (z. B. AI-Stub-Handler).
|
|
/// Init-Property statt Konstruktor — xUnit-Klassen-Fixtures erlauben nur
|
|
/// EINEN öffentlichen (parameterlosen) Konstruktor.
|
|
/// </summary>
|
|
public Action<IServiceCollection>? ConfigureTestServices { get; init; }
|
|
|
|
public string ContractRoot { get; } =
|
|
Path.Combine(Path.GetTempPath(), $"gerbil-contract-tests-{Guid.NewGuid():N}");
|
|
|
|
/// <summary>Optionaler Isolations-Override für den Import-Quellordner (Import:SourcePath),
|
|
/// damit der Upload-Ingest-Test in einem Temp-Verzeichnis arbeitet statt im echten Repo.</summary>
|
|
public string? ImportSourcePath { get; init; }
|
|
|
|
/// <summary>Optionaler Isolations-Override für den Foto-Ordner (Photos:RootPath).</summary>
|
|
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<T> — ohne deren Entfernung blieben
|
|
// Npgsql UND SQLite registriert (ein Provider pro ServiceProvider).
|
|
services.RemoveAll<Microsoft.EntityFrameworkCore.Infrastructure.IDbContextOptionsConfiguration<ApplicationContext>>();
|
|
services.RemoveAll<DbContextOptions<ApplicationContext>>();
|
|
services.RemoveAll<ApplicationContext>();
|
|
|
|
_connection.Open();
|
|
services.AddDbContext<ApplicationContext>(o => o.UseSqlite(_connection));
|
|
|
|
using var provider = services.BuildServiceProvider();
|
|
using var scope = provider.CreateScope();
|
|
scope.ServiceProvider.GetRequiredService<ApplicationContext>().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);
|
|
}
|
|
}
|
|
}
|