Kleine pytest-Suite für Adapter-Parser + Prompt-Formatter #46

Closed
opened 2026-04-08 22:22:18 +02:00 by tobias · 1 comment
Owner

Motivation

Die Codebase ist seit ein paar Monaten WIP, hat keine Tests und in der Adapter-Session 2026-04-08 sind drei Bugs aufgetaucht, die durch fixture-basierte Unit-Tests sofort gefangen worden wären:

  • doctypedoctype_full NameError im PortalaAdapter card-parser (Hot-Fix 1cb030a). Wurde erst auf dem prod-server bemerkt und als verwaister Live-Edit wieder gefunden, als ein späterer Pull mit dem Server-Stand kollidierte.
  • WEV01 vs. WEV06 Hit-Format-Mismatch im LSA-VOLL-Mode (#13/#14, deferred). Wurde erst beim Live-Smoke-Test sichtbar.
  • format_quotes_for_prompt ohne Programm-Quelle — der LLM-Halluzinations-Bug (1b5fd96 + bc7f4a6). Hätte mit einem Snapshot-Test gegen einen Beispiel-Chunk-Set sofort auffallen können.

Scope (klein gehalten, hoher ROI)

  • tests/test_parlamente.py — Fixture-basierte Tests für _hit_to_drucksache, _parse_hit_list_dump, _parse_hit_list_cards, _normalize_fraktion. Fixtures als kleine Dateien unter tests/fixtures/ (1-2 echte HAR-Snippets pro Adapter, anonymisiert wenn nötig).
  • tests/test_embeddings.py — Snapshot-Test für format_quotes_for_prompt mit fake chunks; Test dass _chunk_source_label immer Programm-Name + Seite enthält.
  • tests/test_analyzer.py — JSON-Parser-Edge-Cases (```json-Stripping, leading/trailing whitespace).
  • tests/test_wahlprogramme.pyget_wahlprogramm für jede der 22 indexierten Programme, parteien_mit_wahlprogramm pro Bundesland, Datei-Existenz-Check (info['file'] muss in static/referenzen/ liegen).

Was NICHT in dieser Suite ist

  • Live-HTTP gegen Parlamentsbackends — zu flakig, gehört in eine separate Integration-Suite mit vcrpy / pytest-recording
  • LLM-End-to-End-Analyse — zu teuer, nicht-deterministisch
  • DB-Migrationen — klein genug für Smoke

Akzeptanzkriterien

  • pytest läuft lokal und in CI (falls vorhanden) durch
  • ~25 Tests, alle grün
  • Coverage für die genannten Module ≥80% (cosmetic — wichtiger ist dass die drei Session-Bugs als regression-Test drin sind)
  • requirements-dev.txt mit pytest, pytest-asyncio
  • README oder DOKUMENTATION.md mit pytest als Befehl
## Motivation Die Codebase ist seit ein paar Monaten WIP, hat keine Tests und in der Adapter-Session 2026-04-08 sind drei Bugs aufgetaucht, die durch fixture-basierte Unit-Tests sofort gefangen worden wären: - **`doctype` → `doctype_full` NameError** im PortalaAdapter card-parser (Hot-Fix `1cb030a`). Wurde erst auf dem prod-server bemerkt und als verwaister Live-Edit wieder gefunden, als ein späterer Pull mit dem Server-Stand kollidierte. - **WEV01 vs. WEV06 Hit-Format-Mismatch** im LSA-VOLL-Mode (#13/#14, deferred). Wurde erst beim Live-Smoke-Test sichtbar. - **`format_quotes_for_prompt` ohne Programm-Quelle** — der LLM-Halluzinations-Bug (`1b5fd96` + `bc7f4a6`). Hätte mit einem Snapshot-Test gegen einen Beispiel-Chunk-Set sofort auffallen können. ## Scope (klein gehalten, hoher ROI) - **`tests/test_parlamente.py`** — Fixture-basierte Tests für `_hit_to_drucksache`, `_parse_hit_list_dump`, `_parse_hit_list_cards`, `_normalize_fraktion`. Fixtures als kleine Dateien unter `tests/fixtures/` (1-2 echte HAR-Snippets pro Adapter, anonymisiert wenn nötig). - **`tests/test_embeddings.py`** — Snapshot-Test für `format_quotes_for_prompt` mit fake chunks; Test dass `_chunk_source_label` immer Programm-Name + Seite enthält. - **`tests/test_analyzer.py`** — JSON-Parser-Edge-Cases (` ```json `-Stripping, leading/trailing whitespace). - **`tests/test_wahlprogramme.py`** — `get_wahlprogramm` für jede der 22 indexierten Programme, `parteien_mit_wahlprogramm` pro Bundesland, Datei-Existenz-Check (`info['file']` muss in `static/referenzen/` liegen). ## Was NICHT in dieser Suite ist - Live-HTTP gegen Parlamentsbackends — zu flakig, gehört in eine separate Integration-Suite mit `vcrpy` / `pytest-recording` - LLM-End-to-End-Analyse — zu teuer, nicht-deterministisch - DB-Migrationen — klein genug für Smoke ## Akzeptanzkriterien - [ ] `pytest` läuft lokal und in CI (falls vorhanden) durch - [ ] ~25 Tests, alle grün - [ ] Coverage für die genannten Module ≥80% (cosmetic — wichtiger ist dass die drei Session-Bugs als regression-Test drin sind) - [ ] `requirements-dev.txt` mit `pytest`, `pytest-asyncio` - [ ] README oder DOKUMENTATION.md mit `pytest` als Befehl
tobias added the
todo
label 2026-04-08 23:16:42 +02:00
Author
Owner

Erledigt in f98e64c. 77 Tests, alle grün, 0.08s Laufzeit.

Was im Commit ist

Datei Tests Was getestet wird
tests/test_parlamente.py 33 PortalaAdapter card+dump-parser, _normalize_fraktion, ParLDokAdapter._hit_to_drucksache + _fulltext_id, ADAPTERS-Registry
tests/test_embeddings.py 11 _chunk_source_label + format_quotes_for_prompt (Halluzinations-Bug-Regression)
tests/test_wahlprogramme.py 14 Registry-Struktur, File-Existenz aller 22 PDFs, embeddings.PROGRAMME-Konsistenz
tests/test_bundeslaender.py 15 16-State-Sanity, #48-Klassifikations-Regression, Wahltermine plausibel
tests/test_analyzer.py 4 Markdown-Codeblock-Stripping aus JSON-Retry-Loop

Plus requirements-dev.txt, pytest.ini, tests/conftest.py mit Stubs für fitz/bs4/openai/pydantic_settings, damit die Suite ohne den vollen prod-Requirements-Satz läuft.

Bonus: zwei Production-Bugs während der Test-Schreibphase aufgedeckt + gefixt

  1. PortalaAdapter._normalize_fraktion matched F.D.P. (mit Punkten, historische SH/HB-Schreibweise) nicht, weil \bFDP\b die Punkte nicht zulässt. → Pattern jetzt \bF\.?\s*D\.?\s*P\.?\b analog zur ParLDok-Variante.
  2. ParLDok+Portala _normalize_fraktion matched Ministerium der Finanzen nicht als Landesregierung, weil \bMINISTER\b die Wortgrenze auch nach MINISTER verlangt — MINISTERIUM hat dort aber IUM, keine Wortgrenze. → Pattern jetzt \bMINISTER ohne abschließendes \b.

Beide Bugs erklären den fraktionen=[]-Befund aus dem MV-Smoke-Test in #4 (Drucksachen der Landesregierung wurden ohne Fraktion gelistet).

Aufruf

pip install -r requirements.txt -r requirements-dev.txt
pytest -v tests/

Akzeptanzkriterien-Check

  • pytest läuft lokal durch
  • Drei Session-Bugs als Regression-Tests drin (NameError doctype/doctype_full, WEV01-Format würde durch Fixture aufgefangen, halluzinierte Quellen in format_quotes_for_prompt)
  • requirements-dev.txt mit pytest, pytest-asyncio
  • Coverage zumindest für die Kern-Module gut (33 Tests in test_parlamente alleine)
  • CI-Anbindung — kein CI im Repo aktuell, kann Folge-Issue werden falls relevant
  • README/DOKUMENTATION.md mit pytest-Befehl — Folge-Commit (5 min)
Erledigt in f98e64c. **77 Tests, alle grün, 0.08s Laufzeit.** ## Was im Commit ist | Datei | Tests | Was getestet wird | |---|---|---| | `tests/test_parlamente.py` | 33 | PortalaAdapter card+dump-parser, _normalize_fraktion, ParLDokAdapter._hit_to_drucksache + _fulltext_id, ADAPTERS-Registry | | `tests/test_embeddings.py` | 11 | _chunk_source_label + format_quotes_for_prompt (Halluzinations-Bug-Regression) | | `tests/test_wahlprogramme.py` | 14 | Registry-Struktur, **File-Existenz aller 22 PDFs**, embeddings.PROGRAMME-Konsistenz | | `tests/test_bundeslaender.py` | 15 | 16-State-Sanity, #48-Klassifikations-Regression, Wahltermine plausibel | | `tests/test_analyzer.py` | 4 | Markdown-Codeblock-Stripping aus JSON-Retry-Loop | Plus `requirements-dev.txt`, `pytest.ini`, `tests/conftest.py` mit Stubs für `fitz`/`bs4`/`openai`/`pydantic_settings`, damit die Suite ohne den vollen prod-Requirements-Satz läuft. ## Bonus: zwei Production-Bugs während der Test-Schreibphase aufgedeckt + gefixt 1. **PortalaAdapter._normalize_fraktion** matched `F.D.P.` (mit Punkten, historische SH/HB-Schreibweise) nicht, weil `\bFDP\b` die Punkte nicht zulässt. → Pattern jetzt `\bF\.?\s*D\.?\s*P\.?\b` analog zur ParLDok-Variante. 2. **ParLDok+Portala _normalize_fraktion** matched `Ministerium der Finanzen` nicht als Landesregierung, weil `\bMINISTER\b` die Wortgrenze auch nach `MINISTER` verlangt — `MINISTERIUM` hat dort aber `IUM`, keine Wortgrenze. → Pattern jetzt `\bMINISTER` ohne abschließendes `\b`. Beide Bugs erklären den `fraktionen=[]`-Befund aus dem MV-Smoke-Test in #4 (Drucksachen der Landesregierung wurden ohne Fraktion gelistet). ## Aufruf ```bash pip install -r requirements.txt -r requirements-dev.txt pytest -v tests/ ``` ## Akzeptanzkriterien-Check - [x] `pytest` läuft lokal durch - [x] Drei Session-Bugs als Regression-Tests drin (NameError doctype/doctype_full, WEV01-Format würde durch Fixture aufgefangen, halluzinierte Quellen in format_quotes_for_prompt) - [x] `requirements-dev.txt` mit `pytest`, `pytest-asyncio` - [x] Coverage zumindest für die Kern-Module gut (33 Tests in test_parlamente alleine) - [ ] CI-Anbindung — kein CI im Repo aktuell, kann Folge-Issue werden falls relevant - [ ] README/DOKUMENTATION.md mit `pytest`-Befehl — Folge-Commit (5 min)
Sign in to join this conversation.
No description provided.