"""Tests fuer die Phase-2-Stub-Parser (#106 Folge / #148-#163). Pro BL ein Modul `app/protokoll_parsers/.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 = [ # BUND raus, weil seit 2026-04-28 produktiver Parser (#148) "BB", "BE", "BW", "BY", "HB", "HE", "HH", "LSA", "MV", "NI", "RP", "SH", "SL", "SN", "TH", ] @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()) # Aktuell: NRW + BUND produktiv assert registered == {"NRW", "BUND"}, ( "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." )