From 7e20f910fe538fa641989e5d424fc66411ede59e Mon Sep 17 00:00:00 2001 From: Dotty Dotter Date: Tue, 28 Apr 2026 02:04:24 +0200 Subject: [PATCH] =?UTF-8?q?docs(#134):=20ADR=200007=20=E2=80=94=20Test-Tax?= =?UTF-8?q?onomie?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Phase 3 von #134: Klassifizierung Unit / Integration / E2E / Property / Smoke mit Markern, Latenz-Budgets, Verzeichnis-Konventionen und Lauf-Befehlen. Index aktualisiert (0007 zwischen 0006 und 0008 eingefuegt — ADRs sind chronologisch, nicht numerisch sortiert). --- docs/adr/0007-test-taxonomy.md | 120 +++++++++++++++++++++++++++++++++ docs/adr/index.md | 1 + 2 files changed, 121 insertions(+) create mode 100644 docs/adr/0007-test-taxonomy.md diff --git a/docs/adr/0007-test-taxonomy.md b/docs/adr/0007-test-taxonomy.md new file mode 100644 index 0000000..a2aff8d --- /dev/null +++ b/docs/adr/0007-test-taxonomy.md @@ -0,0 +1,120 @@ +# 0007 — Test-Taxonomie (Unit / Integration / E2E / Property / Smoke) + +| | | +|---|---| +| **Status** | accepted | +| **Datum** | 2026-04-28 | +| **Refs** | #50 (Umbrella E2E Functional Acceptance), #51-54 (Sub-A-D), #134 (Phase 3 Audit), ADR 0003 | + +## Kontext + +Die Test-Suite ist organisch ueber drei Epochen gewachsen: + +1. **Original Unit-Suite** (#46, #91) — `tests/conftest.py` stubbt + `fitz`/`bs4`/`openai`/`pydantic_settings`. Lokal in Sekunden lauffaehig, + keine externen Calls, keine Live-Daten. +2. **E2E Functional Acceptance** (#50 Umbrella) — `tests/integration/` + mit eigenem `conftest.py`, das die Stubs *nicht* setzt. HTTP gegen + echte Landtags-Portale, PDF-Parsing mit echtem `fitz`, DB-Lookups + gegen `embeddings.db`. Marker `integration`. +3. **Playwright UI-Tests** (#120) — `tests/e2e/test_ui.py`, headless + Chromium gegen die laufende App. Marker `e2e`. + +Mit dem Backfill aus #134 kamen zusaetzlich: + +- **Property-/Substring-Tests** (ADR 0003) fuer LLM-Zitate gegen PDF-Seiten +- **Smoke-Tests** (`test_endpoints_smoke.py`) — Endpoints nur auf + Antwortcode + Format pruefen, kein Geschaeftslogik-Detail + +Ohne klare Taxonomie weiss niemand, wo ein neuer Test hingehoert. Folge: +ad-hoc Tests werden in `tests/` abgelegt, Marker werden vergessen, und +beim CI-Lauf brennen langsame Tests die schnellen mit ab. + +## Optionen + +### Option A — Flacher Test-Ordner ohne formale Kategorien + +Status quo bis zu #50: alle Tests unter `tests/`, Marker frei waehlbar. +**Vorteile:** keine Migrationskosten, niedrige kognitive Last. +**Nachteile:** Kategorien implizit, Stub-Setup kollidiert mit echten +Imports, Lauf-Dauer schwankt unvorhersehbar. + +### Option B — Drei harte Verzeichnisse (`tests/unit/`, `tests/integration/`, `tests/e2e/`) + +Strenge raeumliche Trennung mit jeweils eigenem `conftest.py`. +**Vorteile:** Stub-Konflikte ausgeschlossen, einfaches `pytest tests/unit/`. +**Nachteile:** grosse Migration; viele bestehende Test-Files muessten +in `unit/` umziehen; verschachtelte Pfade werden vom Test-Runner und +von Reports etwas sperriger. + +### Option C — Flacher Ordner + verbindliche Marker (gewaehlt) + +`tests/` flach, aber **jede** Datei traegt einen klaren Kategorie-Marker +(`integration`, `e2e`, `slow`) oder ist Default-Unit. Neue +Sub-Verzeichnisse nur wenn sie strukturell notwendig sind (z.B. +`tests/integration/` weil dort ein anderer `conftest.py` lebt — keine +Stubs). + +**Vorteile:** wenig Migrationsschmerz, Default-Run laeuft schnell, aber +opt-in zu langsamen Suiten ist explizit (`pytest -m integration`). +**Nachteile:** Disziplin noetig, Marker mssen gepflegt werden. + +## Entscheidung + +**Option C** mit folgender expliziter Taxonomie: + +| Typ | Marker | Verzeichnis | Latenz | Was ist erlaubt | +|---|---|---|---|---| +| **Unit** | (none, default) | `tests/*.py` | < 100 ms / Test | Reines Python, alle externen Dependencies gestubbed in `tests/conftest.py`. Domain-Logik, Validatoren, Pure Functions, Datenstrukturen. | +| **Smoke** | (none, default) | `tests/test_*_smoke.py` | < 200 ms / Test | TestClient gegen `app.main`, nur Status-Code + Pflicht-Felder pruefen. Skipped wenn `app.main` nicht importierbar. | +| **Property** | (none, default) | `tests/test_citations_substring.py` u.a. | < 500 ms / Test | Invarianten-Checks gegen Fixture-Corpus. Substrings, Strukturmuster. PDF-Parsing erlaubt, aber nur gegen Fixtures im Repo. | +| **Integration** | `integration` | `tests/integration/` | < 5 s / Test, gesamt < 5 min | Echtes HTTP gegen Landtags-Portale, echtes `fitz` gegen reale PDFs, DB-Lookups gegen `embeddings.db`. Eigenes `conftest.py` ohne Stubs. Opt-in via `pytest -m integration`. | +| **E2E** | `e2e` | `tests/e2e/` | < 30 s / Test | Headless-Chromium gegen lokal laufende App oder Prod-URL. Tests koennen flaky sein — werden NICHT von Default-Run getriggert. | +| **Slow** | `slow` | (queruerend) | beliebig | Marker-Suffix zu jedem Typ. Ausschliessbar via `pytest -m "not slow"`. Beispiel: ein Integration-Test, der pro BL einen Wahlprogramm-PDF herunterlaedt. | + +**Lauf-Konvention** (verbindlich, in `pytest.ini` definiert): + +```bash +pytest # Default — Unit + Smoke + Property, ~1s +pytest -m integration # nur E2E-Functional-Acceptance, ~5 min +pytest -m "integration and not slow" # E2E ohne PDF-Downloads +pytest -m e2e # nur Playwright-UI-Tests +pytest -m "" tests/ # ALLES (auch lokal selten gebraucht) +``` + +**Naming-Konvention:** +- `test_.py` — Unit-Tests fuer ein Modul +- `test__smoke.py` — Smoke-Tests +- `test__substring.py` / `_substring_*` — Property-Tests +- Integration- und E2E-Tests heissen wie das Feature, das sie testen + (z.B. `test_adapters_live.py`, `test_ui.py`). + +## Konsequenzen + +### Positiv + +- Default-Run bleibt schnell (< 2s) — niemand wartet bei jedem Save. +- Klar, wo neue Tests landen: jeder neue Test im Default-Ordner ist + ein **Unit-Test** mit Stubs; alles, was Live-HTTP/PDF/LLM braucht, + geht zwingend nach `tests/integration/`. +- CI kann Default-Suite als Pre-Commit-Gate nutzen, Integration-Suite + nightly oder pre-deploy. + +### Negativ + +- Disziplin noetig: Marker vergessen → langsame Tests im Default-Run + oder unbemerkte Lueckentest. Code-Review muss darauf achten. +- Smoke-Tests sind technisch keine Unit-Tests (importieren `app.main`), + aber wir behandeln sie wegen geringer Latenz als Default. Ausnahme + bewusst akzeptiert. + +### Folgen fuer andere ADRs + +- **ADR 0003** (Sub-D Citation-Property-Tests) bleibt gueltig; Property-Tests + sind hier explizit als eigene Kategorie verortet. +- Folge-Issue: Coverage-Baseline (`.coveragerc` mit `fail_under` pro + Modul) — nicht im Skopus dieses ADRs, sondern eigenstaendig in + Phase 3 von #134. +- Folge-Arbeit: einzelne bestehende Test-Files umtaggen, falls sie + faktisch Integration sind aber als Unit liefen (Audit ergab: keine + bekannten Faelle, alle Live-Calls liegen in `tests/integration/`). diff --git a/docs/adr/index.md b/docs/adr/index.md index 02603a9..a43a4d4 100644 --- a/docs/adr/index.md +++ b/docs/adr/index.md @@ -23,6 +23,7 @@ und Konsequenzen. Format inspiriert von [Michael Nygard](https://cognitect.com/b | [0004](0004-deployment-workflow.md) | Docker Compose Deploy mit DB-/Reports-Volume und SN-XML-Sonderpfad | accepted | 2026-04-10 | | [0005](0005-keycloak-sso-with-dev-bypass.md) | Keycloak SSO mit Dev-Bypass-Fallback | accepted | 2026-04-10 | | [0006](0006-embedding-model-migration-v3-to-v4.md) | Embedding-Modell-Migration text-embedding-v3 → v4 | accepted | 2026-04-11 | +| [0007](0007-test-taxonomy.md) | Test-Taxonomie (Unit / Integration / E2E / Property / Smoke) | accepted | 2026-04-28 | | [0008](0008-ddd-lightweight-migration.md) | DDD-Lightweight-Migration (Repository, LLM-Port, Domain-Verhalten) | accepted | 2026-04-20 | ## Wann ADR, wann nicht