diff --git a/docs/QA-14-PRUEFPROTOKOLL.md b/docs/QA-14-PRUEFPROTOKOLL.md new file mode 100644 index 00000000..410bcd8f --- /dev/null +++ b/docs/QA-14-PRUEFPROTOKOLL.md @@ -0,0 +1,54 @@ +# QA-14 – Prüfprotokoll: Testisolation Core und Mail + +Branch `feature/qa-14-testisolation-core-mail`, Basis `integration/gesamtprojekt` (inklusive RBAC-10 #108 und API-16 #109). Tests liefen auf dem Entwicklungshost 204 gegen den Wegwerf-Cluster `17/coredev` (Port 5433), seriell über die gemeinsame Sperre; der Cluster des `archivdms` blieb unberührt. + +## Ausgangslage (Messung vor der Umstellung, Stand `integration/gesamtprojekt` vor RBAC-10/API-16) + +| Modul | Lauf `go test ./...` ohne `-p 1` | Ergebnis | +|---|---|---| +| Core | 2 Läufe | jeweils Ausfälle in `migrate` (4 Tests: „slug bereits vergeben“, Ergebnis fremder Mandanten), `notify`, `pentest`, `kek` (nur Lauf 1); alle einzeln grün | +| Mail | 2 Läufe | `indexworker` (2 Tests, Spalte `tenant_slug` fehlt), `ingestiontestgate`, `pop3`/`smtp` Lasttests (p95 111 bis 145 ms bei Ziel 100 ms) | + +Ursachen: Core-Tests legten Registry- und Fachtabellen im gemeinsamen `public`-Schema an, die `migrate`-Tests rollten über alle Mandanten der gemeinsamen Registry aus. Mail: `healthcheck/checks_test.go` legte `mail_index_jobs` in veralteter Form ohne `tenant_slug` an, `CREATE TABLE IF NOT EXISTS` in `indexworker` ergänzte die Spalte nicht. Lasttests maßen im parallelen Paketlauf. + +## Umsetzung + +- **Neu:** `internal/testdb` (Core) und `mail/internal/testdb` (Mail) mit `Pool`, `PoolWithSchema`, `Schema`, `Open`, `Sibling`, `Suffix`, `Slug`; Mail zusätzlich `RequirePerf`. Muster wie `dms/internal/testdb` (FDN-16), Namen `qatest_s`. +- **Core:** 59 Testdateien. Pools für Registry- und Fachtabellen laufen über `testdb.Pool`. Admin-Pools (`CREATE`/`DROP DATABASE`) bleiben unverändert. Datenbanknamen in den Helfern `user`, `rbac` (2×), `auth` tragen einen eindeutigen Suffix. +- **Mehrinstanz-Tests** (`ratelimit`, `lockout`, `channels`, `saml`, `loadtest`, `pentest`) öffnen ihre weiteren Pools mit `Sibling` bzw. `Open` auf dasselbe Schema. `audit/append_only_test.go`: der Pool der Login-Rolle bekommt `search_path` und `USAGE` auf das Testschema. +- **Mail:** 16 Testdateien mit Pool-Aufruf umgestellt, Latenz-/Lasttests (`pop3`, `smtp`, `imap`) und `ingestiontestgate/TestRun_RealGate…` nur mit `TEST_PERF=1`. Die Zielwerte (100 ms p95) bleiben unverändert. +- **Wettläufe in Tests an der Ursache behoben:** `smtp/protolog_test.go` (server loggt `session_end` erst nach dem Schließen: Test wartet jetzt darauf und liest den Log-Puffer synchronisiert), `flag` Stale-Fallback und `license` (unerreichbare Datenbank über `127.0.0.1:1` statt eines DNS-Namens, Gnadenfrist 80 → 300 ms), `webhook` Backoff (30 → 250 ms Basiswert). +- **Dokumentation:** `docs/TESTSTRATEGIE-CORE.md` Abschnitt 4 (Schema je Test statt `-p 1`, Lastmessungen seriell). +- **Nachweis der Hilfspakete:** `internal/testdb/testdb_test.go` und `mail/internal/testdb/testdb_test.go` (zwei Pools, gleicher Tabellenname, kein gegenseitiges Sehen, `Sibling` und `Open` teilen das Schema, Aufräumen entfernt die Schemas; Slug passt zum Mandanten-Slug-Muster und ist eindeutig). + +## Prüfungen + +| Prüfung | Zielwert | Ergebnis | +|---|---|---| +| 1. Core `go test -count=1 ./...` ohne `-p 1`, frische Test-DB, 3 Läufe | kein Ausfall | vor dem Merge von #108/#109: 3 von 3 Läufen ohne Ausfall (153 bis 167 s) und ein weiterer Lauf ohne Ausfall; nach dem Merge: siehe Nachtrag | +| 1. Mail `go test -count=1 ./...` ohne `-p 1`, 3 Läufe | kein Ausfall | 3 von 3 Läufen ohne Ausfall (Erstlauf hatte den `protolog`-Wettlauf, danach behoben) | +| 2. `-shuffle=on`, Core | kein Ausfall | 1. Lauf: 2 Fehler (`flag` Stale-Fallback, `webhook` Backoff, beide zeitabhängig), behoben; danach 3 Läufe und ein weiterer Lauf ohne Ausfall | +| 2. `-shuffle=on`, Mail | kein Ausfall | ohne Ausfall | +| 3. Serieller Lastlauf `TEST_PERF=1 -p 1` (`pop3`, `smtp`, `imap`, `ingestiontestgate`) | PASS, Ziel 100 ms unverändert | PASS | +| 3. Isolationstest der Hilfspakete | PASS | PASS (Core und Mail) | +| `go vet ./...`, `gofmt -l` der geänderten Dateien | sauber | sauber (beide Module) | + +## QA und Entscheidungen + +Iterationen: 8 Fix-Runden, keine mehr als zwei je Problem (importierte `pgxpool`, ungenutzte `ctx`-Variable, Mehrinstanz-Tests, Mail-Lasttests, `protolog`-Wettlauf, Shuffle-Wettläufe, Merge mit #108/#109). + +Entscheidungen (Alternative, Grund): +1. **Schema je Test statt Datenbank je Test.** Datenbank je Test wäre stärker isoliert, aber deutlich langsamer; Schemas genügen, weil die Tests Tabellen und Registry-Zeilen isolieren müssen, keine Datenbankeigenschaften. +2. **Slugs nicht global eindeutig gemacht.** Die Registry liegt jetzt pro Test im eigenen Schema; die physischen Datenbanknamen `tenant_` tragen paketeigene Präfixe (`mig_`, `router_`, `lc_`, …). Eindeutige Slugs überall hätten Testliterale in Assertions gebrochen. Ein Kollisionsfall (gleicher Slug in zwei Paketen) bleibt möglich und wird über die Präfixkonvention verhindert; `testdb.Slug` steht bereit. +3. **Admin-Pools bleiben ohne Schema**, sie führen nur `CREATE`/`DROP DATABASE` aus. +4. **Zielwerte der Lasttests nicht gelockert**, die Messung läuft nur seriell (wie im DMS, `TEST_PERF=1 -p 1`). Der Gate-Test `TestRun_RealGate…` führt die Lasttests als Teil der Suite aus und ist deshalb ebenfalls nur im seriellen Lauf aktiv. +5. **Unerreichbare Datenbank in `flag`/`license` über `127.0.0.1:1`** statt eines nicht auflösbaren Namens: die Fehlerdauer hing an der DNS-Auflösung (kalter Resolver), das machte die 80-ms-Gnadenfrist unbestimmt. Zusätzlich wurden Gnadenfrist und Wartezeit vergrößert. +6. **CI-Pipeline nicht angefasst.** `-p 1` bleibt dort bestehen; die Umstellung auf paralleles `go test ./...` ist ein eigener Schritt. +7. **Archive nicht umgestellt** (gleiches Muster, eigenes Ticket). + +## Grenzen und offene Punkte + +- Das Core-Paket `audit` braucht allein rund 110 s (vor der Umstellung 108 s, danach 114 s): die Laufzeit liegt an den Tests selbst, nicht an der Isolation. Es bestimmt die Gesamtdauer des Core-Laufs (rund 160 s). +- Cluster-weite Objekte (Rollen wie `nexarch_app`, Testrollen `test_aud07_app_`) sind nicht schemagebunden; ihre Namen sind eindeutig, aufgeräumt wird per Cleanup, verwaiste Reste entfernt `core-cluster.sh reset`. +- Archive-Tests folgen demselben alten Muster (20 Dateien mit `TEST_*_DSN`), ungeprüft. +- Die CI-Pipeline nutzt weiterhin `-p 1`.