2026-04-28 23:11:38 +02:00
|
|
|
"""Tests fuer die Phase-2-Stub-Parser (#106 Folge / #148-#163).
|
|
|
|
|
|
|
|
|
|
Pro BL ein Modul `app/protokoll_parsers/<bl>.py`, das:
|
|
|
|
|
1. importierbar ist (Recherche-Findings im Docstring)
|
|
|
|
|
2. ``parse_protocol(...)`` raised ``NotImplementedError`` mit Issue-Verweis
|
|
|
|
|
3. **NICHT** in ``PROTOKOLL_PARSERS``-Registry registriert ist (sonst wuerde
|
|
|
|
|
der Auto-Ingest-Cron versuchen, sie zu nutzen und mit NotImplementedError
|
|
|
|
|
abbrechen)
|
|
|
|
|
|
|
|
|
|
Diese Tests sind das Safety-Net: sobald ein Stub durch einen echten Parser
|
|
|
|
|
ersetzt wird, MUSS gleichzeitig der Eintrag in PROTOKOLL_PARSERS gesetzt
|
|
|
|
|
werden — sonst greift ``test_implemented_parsers_are_registered``.
|
|
|
|
|
"""
|
|
|
|
|
from __future__ import annotations
|
|
|
|
|
|
|
|
|
|
import importlib
|
|
|
|
|
|
|
|
|
|
import pytest
|
|
|
|
|
|
|
|
|
|
from app.protokoll_parsers import PROTOKOLL_PARSERS, supported_bundeslaender
|
|
|
|
|
|
|
|
|
|
STUB_BL_CODES = [
|
feat(#163): TH-Parser produktiv — Thueringer Plenarprotokolle
Fuenfter produktiver Parser nach NRW + BUND + BE + HH.
URL-Pattern verifiziert (WP8 Sitzungen 1, 10, 20, 30, 40, 42):
https://www.thueringer-landtag.de/uploads/tx_tltcalendar/protocols/Arbeitsfassung{n}.pdf
Anchor-Sprache (BE-aehnlich):
Wer dem zustimmt, ... Das sind die Stimmen aus den Fraktionen der
CDU, BSW, SPD und Die Linke. Wer stimmt gegen ...? Das sind die
Stimmen aus der Fraktion der AfD. Damit ist [...] mehrheitlich
angenommen.
Pattern:
- Result-Anchor: Damit ist [Subjekt] (mehrheitlich|einstimmig)?
(angenommen|abgelehnt)
- Vote-Block: Wer dem zustimmt / Wer stimmt gegen / Wer enthaelt sich
- Drucksachen-Lookup: 'Drucksache 8/N' rueckwaerts
Fraktions-Mapping WP8 (ab Mai 2024): CDU, AfD, BSW, Linke, SPD
(WP7-Faktionen GRUENE/FDP fuer Backfill ebenfalls im Mapping).
Cron-PROTO_TARGETS um TH-WP8 erweitert. Stub-Test angepasst.
2026-04-29 01:11:58 +02:00
|
|
|
# BUND/BE/HH/TH raus, weil seit 2026-04-28/29 produktive Parser
|
2026-04-29 00:57:58 +02:00
|
|
|
"BB", "BW", "BY", "HB", "HE",
|
feat(#163): TH-Parser produktiv — Thueringer Plenarprotokolle
Fuenfter produktiver Parser nach NRW + BUND + BE + HH.
URL-Pattern verifiziert (WP8 Sitzungen 1, 10, 20, 30, 40, 42):
https://www.thueringer-landtag.de/uploads/tx_tltcalendar/protocols/Arbeitsfassung{n}.pdf
Anchor-Sprache (BE-aehnlich):
Wer dem zustimmt, ... Das sind die Stimmen aus den Fraktionen der
CDU, BSW, SPD und Die Linke. Wer stimmt gegen ...? Das sind die
Stimmen aus der Fraktion der AfD. Damit ist [...] mehrheitlich
angenommen.
Pattern:
- Result-Anchor: Damit ist [Subjekt] (mehrheitlich|einstimmig)?
(angenommen|abgelehnt)
- Vote-Block: Wer dem zustimmt / Wer stimmt gegen / Wer enthaelt sich
- Drucksachen-Lookup: 'Drucksache 8/N' rueckwaerts
Fraktions-Mapping WP8 (ab Mai 2024): CDU, AfD, BSW, Linke, SPD
(WP7-Faktionen GRUENE/FDP fuer Backfill ebenfalls im Mapping).
Cron-PROTO_TARGETS um TH-WP8 erweitert. Stub-Test angepasst.
2026-04-29 01:11:58 +02:00
|
|
|
"LSA", "MV", "NI", "RP", "SH", "SL", "SN",
|
2026-04-28 23:11:38 +02:00
|
|
|
]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@pytest.fixture(params=STUB_BL_CODES)
|
|
|
|
|
def stub_module(request):
|
|
|
|
|
code = request.param
|
|
|
|
|
mod_name = f"app.protokoll_parsers.{code.lower()}"
|
|
|
|
|
return code, importlib.import_module(mod_name)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class TestStubImportability:
|
|
|
|
|
def test_each_stub_imports(self, stub_module):
|
|
|
|
|
code, mod = stub_module
|
|
|
|
|
assert mod is not None
|
|
|
|
|
assert hasattr(mod, "parse_protocol")
|
|
|
|
|
|
|
|
|
|
def test_each_stub_has_recherche_in_docstring(self, stub_module):
|
|
|
|
|
"""Docstring enthaelt 'Recherche'-Marker und Status-Hinweis."""
|
|
|
|
|
code, mod = stub_module
|
|
|
|
|
doc = mod.__doc__ or ""
|
|
|
|
|
assert "Recherche" in doc or "Status" in doc, \
|
|
|
|
|
f"Stub {code}: Docstring enthaelt keine Recherche-Findings"
|
|
|
|
|
|
|
|
|
|
def test_each_stub_links_to_issue(self, stub_module):
|
|
|
|
|
"""Docstring verweist auf konkretes Tracking-Issue."""
|
|
|
|
|
code, mod = stub_module
|
|
|
|
|
doc = mod.__doc__ or ""
|
|
|
|
|
assert "issues/" in doc, f"Stub {code}: kein Issue-Link"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class TestStubBehavior:
|
|
|
|
|
def test_each_stub_raises_not_implemented(self, stub_module):
|
|
|
|
|
code, mod = stub_module
|
|
|
|
|
with pytest.raises(NotImplementedError) as exc:
|
|
|
|
|
mod.parse_protocol("/dev/null")
|
|
|
|
|
msg = str(exc.value)
|
|
|
|
|
assert "noch nicht implementiert" in msg, \
|
|
|
|
|
f"Stub {code}: NotImplementedError-Message ist nicht informativ"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class TestRegistryDiscipline:
|
|
|
|
|
"""Sicherheitsnetz: Stubs duerfen NICHT in PROTOKOLL_PARSERS sein.
|
|
|
|
|
|
|
|
|
|
Sobald ein Stub durch echten Parser ersetzt wird, MUSS der Implementer:
|
|
|
|
|
1. NotImplementedError aus parse_protocol entfernen
|
|
|
|
|
2. PROTOKOLL_PARSERS[code] = parse_protocol setzen
|
|
|
|
|
|
|
|
|
|
Dieser Test schlaegt fehl, wenn eines der beiden vergessen wird —
|
|
|
|
|
explizit unten ueber test_implemented_parsers_are_registered.
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
def test_stubs_not_in_registry(self):
|
|
|
|
|
registered = set(supported_bundeslaender())
|
feat(#163): TH-Parser produktiv — Thueringer Plenarprotokolle
Fuenfter produktiver Parser nach NRW + BUND + BE + HH.
URL-Pattern verifiziert (WP8 Sitzungen 1, 10, 20, 30, 40, 42):
https://www.thueringer-landtag.de/uploads/tx_tltcalendar/protocols/Arbeitsfassung{n}.pdf
Anchor-Sprache (BE-aehnlich):
Wer dem zustimmt, ... Das sind die Stimmen aus den Fraktionen der
CDU, BSW, SPD und Die Linke. Wer stimmt gegen ...? Das sind die
Stimmen aus der Fraktion der AfD. Damit ist [...] mehrheitlich
angenommen.
Pattern:
- Result-Anchor: Damit ist [Subjekt] (mehrheitlich|einstimmig)?
(angenommen|abgelehnt)
- Vote-Block: Wer dem zustimmt / Wer stimmt gegen / Wer enthaelt sich
- Drucksachen-Lookup: 'Drucksache 8/N' rueckwaerts
Fraktions-Mapping WP8 (ab Mai 2024): CDU, AfD, BSW, Linke, SPD
(WP7-Faktionen GRUENE/FDP fuer Backfill ebenfalls im Mapping).
Cron-PROTO_TARGETS um TH-WP8 erweitert. Stub-Test angepasst.
2026-04-29 01:11:58 +02:00
|
|
|
# Aktuell: NRW + BUND + BE + HH + TH produktiv
|
|
|
|
|
assert registered == {"NRW", "BUND", "BE", "HH", "TH"}, (
|
2026-04-28 23:11:38 +02:00
|
|
|
"Unerwartete Registry-Eintraege. Wenn neue BL implementiert sind, "
|
|
|
|
|
"diesen Test anpassen UND den Stub durch echten Parser ersetzen."
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
def test_implemented_parsers_are_registered(self, stub_module):
|
|
|
|
|
"""Wenn ein Stub-Modul KEIN NotImplementedError mehr wirft (also
|
|
|
|
|
implementiert wurde), muss es in PROTOKOLL_PARSERS registriert sein.
|
|
|
|
|
|
|
|
|
|
Dieser Test ist der Trigger: sobald der Implementer parse_protocol
|
|
|
|
|
echt implementiert (kein NotImplementedError mehr), schlaegt der
|
|
|
|
|
andere Test (test_each_stub_raises_not_implemented) fehl. Das ist
|
|
|
|
|
das richtige Signal — dann muessen Tests + Registry beide angepasst
|
|
|
|
|
werden.
|
|
|
|
|
"""
|
|
|
|
|
code, mod = stub_module
|
|
|
|
|
# Probe: wirft das Modul noch NotImplementedError?
|
|
|
|
|
try:
|
|
|
|
|
mod.parse_protocol("/dev/null")
|
|
|
|
|
implemented = True
|
|
|
|
|
except NotImplementedError:
|
|
|
|
|
implemented = False
|
|
|
|
|
except Exception:
|
|
|
|
|
# Anderer Fehler (z.B. fitz konnte /dev/null nicht oeffnen) →
|
|
|
|
|
# Modul ist implementiert (wuerde mit echtem PDF arbeiten)
|
|
|
|
|
implemented = True
|
|
|
|
|
|
|
|
|
|
if implemented:
|
|
|
|
|
assert code in PROTOKOLL_PARSERS, (
|
|
|
|
|
f"{code}-Parser scheint implementiert (keine NotImplementedError), "
|
|
|
|
|
f"aber nicht in PROTOKOLL_PARSERS registriert. Beide muessen "
|
|
|
|
|
f"zusammen aktualisiert werden."
|
|
|
|
|
)
|