using GerbilManagerWebAPI.Import; using GerbilManagerWebAPI.Models; using Microsoft.Data.Sqlite; using Microsoft.EntityFrameworkCore; namespace GerbilManager.Tests { /// /// FEAT-8d-CREATE: docx importer CREATE path tests. /// Uses SQLite (not InMemory) so FK constraints are enforced. /// public class ImportDocxServiceTests : IDisposable { private readonly string _dir; public ImportDocxServiceTests() { _dir = Path.Combine(Path.GetTempPath(), "feat8d-" + Guid.NewGuid().ToString("N")); Directory.CreateDirectory(_dir); } public void Dispose() { try { Directory.Delete(_dir, recursive: true); } catch { } } // One open connection keeps the :memory: SQLite DB alive across calls. private (ApplicationContext db, SqliteConnection conn) NewSqliteDb() { var conn = new SqliteConnection("DataSource=:memory:"); conn.Open(); var opts = new DbContextOptionsBuilder().UseSqlite(conn).Options; var db = new ApplicationContext(opts); db.Database.EnsureCreated(); return (db, conn); } private void WriteLitters(object litters) => File.WriteAllText(Path.Combine(_dir, "docx_litters.json"), System.Text.Json.JsonSerializer.Serialize(litters)); private void WriteAnimals(object animals) => File.WriteAllText(Path.Combine(_dir, "docx_animals.json"), System.Text.Json.JsonSerializer.Serialize(animals)); // ── Test 1: dry-run shows correct counts without writing ───────────────── [Fact] public async Task DryRun_counts_new_animal_without_writing() { var (db, conn) = NewSqliteDb(); await using (conn) await using (db) { // Litter in DB with PairingCode matching the docx WS-code var litter = new Litter { Id = Guid.NewGuid(), Name = "Testwurf WS1", Date = new DateOnly(2023, 5, 1), PairingCode = "WS1" }; db.Litters.Add(litter); await db.SaveChangesAsync(); WriteLitters(Array.Empty()); WriteAnimals(new[] { new { wsCode = "WS1", litterDob = "01.05.2023", name = "Pepper", gender = "female", owner = "Max Mustermann", abgabeDate = "01.07.2023", deathDate = "", deathCause = "", farbschlag = "" } }); var report = await new ImportDocxService(db, _dir).RunAsync(execute: false); Assert.False(report.Executed); Assert.Equal(1, report.Created); Assert.Equal(1, report.LitterLinked); Assert.Equal(1, report.GoHomeFilled); // new animal will have GoHomeDate set Assert.Equal(0, await db.Gerbils.CountAsync()); // nothing written Assert.Equal(0, await db.Contacts.CountAsync()); // nothing written } } // ── Test 2: execute creates animal with all fields + FK constraints ─────── [Fact] public async Task Execute_creates_animal_with_litter_contact_and_goHomeDate() { var (db, conn) = NewSqliteDb(); await using (conn) await using (db) { var litter = new Litter { Id = Guid.NewGuid(), Name = "Testwurf WS1", Date = new DateOnly(2023, 5, 1), PairingCode = "WS1" }; db.Litters.Add(litter); await db.SaveChangesAsync(); WriteLitters(Array.Empty()); WriteAnimals(new[] { new { wsCode = "WS1", litterDob = "01.05.2023", name = "Pepper", gender = "female", owner = "Max Mustermann", abgabeDate = "01.07.2023", deathDate = "", deathCause = "", farbschlag = "" } }); var report = await new ImportDocxService(db, _dir).RunAsync(execute: true); Assert.True(report.Executed); Assert.Equal(1, report.Created); Assert.Equal(1, report.ContactsCreated); var gerbil = await db.Gerbils.SingleAsync(); Assert.Equal("Pepper", gerbil.Name); Assert.Equal(new DateOnly(2023, 5, 1), gerbil.DateOfBirth); Assert.Equal(Gender.female, gerbil.Gender); Assert.Equal(GerbilStatus.GivenAway, gerbil.Status); Assert.Equal(litter.Id, gerbil.LitterId); Assert.Equal(new DateOnly(2023, 7, 1), gerbil.GoHomeDate); Assert.Equal("Zucht der Kleinen Chaoten", gerbil.OriginBreeder); Assert.False(gerbil.IsResident); Assert.Equal("docx", gerbil.ImportSource); Assert.StartsWith("docx-", gerbil.ExternalRef); var contact = await db.Contacts.SingleAsync(); Assert.Equal("Max Mustermann", contact.Name); Assert.Equal(contact.Id, gerbil.ReceiverContactId); } } // ── Test 3: idempotency — second run creates zero ──────────────────────── [Fact] public async Task Execute_is_idempotent_second_run_creates_zero() { var (db, conn) = NewSqliteDb(); await using (conn) await using (db) { WriteLitters(Array.Empty()); WriteAnimals(new[] { new { wsCode = "WS2", litterDob = "15.03.2023", name = "Flash", gender = "male", owner = "", abgabeDate = "01.05.2023", deathDate = "", deathCause = "", farbschlag = "" } }); var first = await new ImportDocxService(db, _dir).RunAsync(execute: true); Assert.Equal(1, first.Created); var second = await new ImportDocxService(db, _dir).RunAsync(execute: true); Assert.Equal(0, second.Created); // ExternalRef path, no dupe Assert.Equal(1, await db.Gerbils.CountAsync()); // exactly one row } } // ── Test 4: existing main-import animal → enrich only, no duplicate ────── [Fact] public async Task Execute_enriches_existing_animal_does_not_duplicate() { var (db, conn) = NewSqliteDb(); await using (conn) await using (db) { // Animal already in DB (from main import, ExternalRef ≠ "docx-…") var existing = new Gerbil { Id = Guid.NewGuid(), Name = "Eddie", DateOfBirth = new DateOnly(2022, 8, 10), Gender = Gender.male, ExternalRef = "main-eddie-xyz", // NOT a "docx-" ref IsResident = false, }; db.Gerbils.Add(existing); await db.SaveChangesAsync(); WriteLitters(Array.Empty()); WriteAnimals(new[] { // Same name+dob → enrich, not create new { wsCode = "", litterDob = "10.08.2022", name = "Eddie", gender = "male", owner = "Anna Beispiel", abgabeDate = "01.10.2022", deathDate = "", deathCause = "", farbschlag = "" } }); var report = await new ImportDocxService(db, _dir).RunAsync(execute: true); Assert.Equal(0, report.Created); Assert.Equal(1, await db.Gerbils.CountAsync()); // still exactly one // GoHomeDate was enriched var updated = await db.Gerbils.SingleAsync(); Assert.Equal(new DateOnly(2022, 10, 1), updated.GoHomeDate); } } // ── Test 5: Deceased status when deathDate set ─────────────────────────── [Fact] public async Task Execute_sets_Deceased_status_when_deathDate_provided() { var (db, conn) = NewSqliteDb(); await using (conn) await using (db) { WriteLitters(Array.Empty()); WriteAnimals(new[] { new { wsCode = "WS3", litterDob = "01.01.2022", name = "Ghost", gender = "male", owner = "", abgabeDate = "", deathDate = "15.06.2022", deathCause = "Tumor", farbschlag = "" } }); await new ImportDocxService(db, _dir).RunAsync(execute: true); var gerbil = await db.Gerbils.SingleAsync(); Assert.Equal(GerbilStatus.Deceased, gerbil.Status); Assert.Equal(new DateOnly(2022, 6, 15), gerbil.DateOfDeath); Assert.Equal("Tumor", gerbil.CauseOfDeath); } } } }