REVIEW-FIXES-2: DB-4 (Deutsche Collation ä/ö/ü) + DB-5 (Litter.ExternalRef)

DB-4 (medium): ICU German collation de-x-icu auf deutschen Textspalten
  Gerbil.Name, Gerbil.NameSearch, Gerbil.OriginBreeder, ColorVariety.Name,
  Contact.Name. Korrekte ä/ö/ü-Sortierung + locale-aware lower() in Postgres.
  Nur bei Npgsql (Database.ProviderName check) — SQLite-Testhost kennt keine
  eigenen Collation-Namen und wuerde EnsureCreated fail lassen.
  Migration AddGermanCollation (ALTER COLUMN ... TYPE text COLLATE de-x-icu).

DB-5 (low): Litter.ExternalRef + filtered unique index + Importer-Anbindung
  Litter.ExternalRef (string?, nullable) = stable import source id (sl.Id).
  Filtered unique index WHERE ExternalRef IS NOT NULL (wie DB-1 fuer Gerbil).
  Migration AddLitterExternalRef.
  Importer (ImportService.cs): setzt ExternalRef = sl.Id beim Litter-Insert;
  prueft existingLitterExtRefSet als PRIMAEREN Idempotenz-Key (ExternalRef),
  Name+Date-Key als Fallback fuer Wuerfe ohne ExternalRef aus frueheren Laeufen.
  +Test DB5_Litter_ExternalRef_set_and_used_for_idempotency (mutiert den Namen
  nach erstem Import, prueft dass Re-Import via ExternalRef erkennt).

GATE: 140/140 C#, has-pending=No.
This commit is contained in:
2026-06-06 19:49:50 +02:00
parent e6b513ed5d
commit ea3596ca0d
9 changed files with 3047 additions and 14 deletions

View File

@@ -103,6 +103,31 @@ namespace GerbilManager.Tests
Assert.Equal(2, await db.Litters.CountAsync());
}
[Fact]
public async Task DB5_Litter_ExternalRef_set_and_used_for_idempotency()
{
// DB-5: litters must have ExternalRef set to the source litter id on first import,
// and subsequent runs must detect them via ExternalRef (not just Name+Date).
using var db = NewDb();
await new ImportService(db, _dir, _dir).RunAsync(execute: true);
// ExternalRef is set on created litters
var litters = await db.Litters.ToListAsync();
Assert.All(litters, l => Assert.NotNull(l.ExternalRef));
Assert.Contains(litters, l => l.ExternalRef == "L1");
Assert.Contains(litters, l => l.ExternalRef == "L2");
// Simulate the "Name+Date lookup would still work, but ExternalRef is now primary":
// mutate Name to something different — Name+Date fallback would fail, ExternalRef must catch it.
foreach (var l in litters) l.Name = "Geänderter Name";
await db.SaveChangesAsync();
// Re-import: litters detected as existing via ExternalRef even though Name changed
var second = await new ImportService(db, _dir, _dir).RunAsync(execute: true);
Assert.Equal(0, second.Litters.Created);
Assert.Equal(2, await db.Litters.CountAsync());
}
[Fact]
public async Task Execute_persists_deaf_flag_and_preserves_sls_and_tags()
{