# VAL-04 – Pruefprotokoll: Audit-Log-Kontinuitaetspruefung ## Auftrag Pruefung, dass aeltester und neuester Audit-Log-Eintrag je Tenant nach der Migration (COR-05, `internal/migrationaudit`) mit der Quelle (`legacy_audit_log_staging`) uebereinstimmt — keine Luecke, keine Zeitverschiebung. ## Umsetzung Neues Paket `internal/migrationauditcontinuity`: - `continuity.go`: `AuditStats` (Anzahl, aeltester/neuester Zeitstempel je Seite), `StatsProvider`-Interface, `ContinuityReport` mit `Luecke()`, `Zeitverschiebung()` und `OffenerBefund()` — jede der beiden Abweichungen gilt als blockierender Befund, es gibt (anders als bei VAL-01) keinen Erklaerungs-Mechanismus, da das Produkt-DNA-Prinzip fuer Audit-Logs keine Ausnahme vorsieht. `Run()` vergleicht Quelle gegen Ziel je Tenant. - `postgres.go`: `PostgresSourceStatsProvider` liest Anzahl/min/max aus `legacy_audit_log_staging`, ueber `tenant_migration_map` auf den Tenant-Slug aufgeloest (beide Altsysteme in einer Abfrage, je nach belegter `archivmail_tenant_id`/`archivdms_tenant_id`-Spalte). `PostgresTargetStatsProvider` liest dieselben Werte direkt aus `audit_events` (COR-05-Ziel). - `render.go`: `RenderBericht()` (deutschsprachige Markdown-Tabelle je Tenant, Berichtsmuster aus VAL-01 `internal/migrationverify`) und `OffeneBefunde()`. - `continuity_test.go`: zwei Integrationstests gegen einen echten Postgres-Testcluster (Host 204). Kein Eingriff in COR-05 (`internal/migrationaudit`) oder VAL-01 (`internal/migrationverify`) — reines, zusaetzliches Lese-/Pruefwerkzeug nach demselben Muster. ## Pruefungen vor Abnahme — real durchgefuehrt Testcluster: Host 204, `TEST_ADMIN_DSN` aus `/etc/nexarch-dev/core-dev.env` (Werte nie im Klartext ausgegeben, nur per `set -a && source ... && set +a` im selben Befehl eingespeist). Eigenes Test-Schema je Lauf (`internal/testdb`), automatisch aufgeraeumt. ### Pruefung 1: Aeltester/neuester Eintrag fuer 3 Test-Tenants exakt mit Quelle abgeglichen `TestKontinuitaetspruefung_DreiTenantsMitEntferntemEintrag`: - 3 synthetische Test-Tenants (`mandant-a`, `mandant-b`, `mandant-c`), je mit mehreren historischen `legacy_audit_log_staging`-Eintraegen (Zeitstempel 2018–2020, synthetische IPs/Metadaten). - Migration via `migrationaudit.Migrator.MigrateAll` (COR-05, unveraendert). - `migrationauditcontinuity.Run` gegen Quelle und Ziel. - Ergebnis: fuer `mandant-a` und `mandant-b` (ohne Manipulation) stimmen Anzahl, aeltester und neuester Zeitstempel exakt ueberein, `OffenerBefund()` ist `false`. **Zielwert erreicht: PASS.** ### Pruefung 2: Absichtlich entfernter Testeintrag wird als Luecke erkannt Selber Test, Tenant `mandant-c`: nach der Migration wird ein mittlerer Audit-Eintrag im Ziel (`audit_events`) real geloescht (Append-only-Trigger dafuer temporaer deaktiviert, wie es ein tatsaechlicher Datenverlust im Betrieb taete), die zugehoerige Staging-Referenz zuvor geloest, um die Fremdschluessel-Bindung sauber aufzuheben. - Ergebnis: Quelle=3, Ziel=2 Eintraege fuer `mandant-c`. `ContinuityReport.Luecke()` ist `true`, `OffenerBefund()` ist `true`. Der gerenderte Bericht enthaelt fuer diesen Tenant "OFFENER BEFUND", `OffeneBefunde()` liefert genau diesen einen Tenant. **Zielwert erreicht: PASS.** ### Zusatzpruefung: Zeitverschiebung ohne Lueckenbildung `TestKontinuitaetspruefung_Zeitverschiebung` (ueber die geforderten Pruefungen hinaus, deckt den zweiten in Akzeptanzkriterium 2 genannten Fall explizit ab): Zeitstempel eines migrierten Eintrags wird im Ziel um eine Stunde verschoben (Append-only-Update-Trigger temporaer deaktiviert). Anzahl bleibt identisch (`Luecke()` = `false`), aber `Zeitverschiebung()` = `true`, somit `OffenerBefund()` = `true`. **Zielwert erreicht: PASS.** ### Testlauf ``` go test ./internal/migrationauditcontinuity/... -v === RUN TestKontinuitaetspruefung_DreiTenantsMitEntferntemEintrag --- PASS: TestKontinuitaetspruefung_DreiTenantsMitEntferntemEintrag (0.20s) === RUN TestKontinuitaetspruefung_Zeitverschiebung --- PASS: TestKontinuitaetspruefung_Zeitverschiebung (0.09s) PASS ok gitea.perlbach24.de/scripte/nexarch/internal/migrationauditcontinuity 0.297s ``` `go build ./...` und `go vet ./...` fuer das gesamte Repository: fehlerfrei. ## Akzeptanzkriterien — Abgleich 1. Fuer jeden Tenant liegt ein Kontinuitaetsbericht vor — `Run()` erzeugt je Tenant genau einen `ContinuityReport`, `RenderBericht()` stellt sie als Markdown-Tabelle dar. Erfuellt. 2. Jede erkannte Luecke oder Zeitverschiebung ist ein blockierender Befund — `OffenerBefund()` kennt keine Erklaerungs-Ausnahme, beide Testfaelle (Luecke, Zeitverschiebung) zeigen den Befund. Erfuellt. ## DSGVO Alle Testdaten synthetisch (fiktive Tenant-/Nutzernamen, erfundene IPs/Metadaten), keine archivmail-/archivdms-Exporte verwendet. Keine Zugangsdaten im Code, Testverbindung ausschliesslich ueber `TEST_ADMIN_DSN` aus der Umgebung.