diff --git a/tests/test_wahlprogramm_fetch.py b/tests/test_wahlprogramm_fetch.py index b809126..269d654 100644 --- a/tests/test_wahlprogramm_fetch.py +++ b/tests/test_wahlprogramm_fetch.py @@ -300,6 +300,88 @@ class TestShaLock: assert lock["spd-mv.pdf"] == _sha(content) +# --------------------------------------------------------------------------- +# Test 5: Lock-File und YAML-Robustheit (#134 Coverage-Backfill) +# --------------------------------------------------------------------------- + +class TestLockFileRobustness: + def test_corrupt_lock_file_returns_empty_dict(self, tmp_path): + """Kaputtes JSON darf den Caller nicht crashen — leeren Lock liefern.""" + from app.wahlprogramm_fetch import _load_lock + bad = tmp_path / "broken-lock.json" + bad.write_text("{ this is not json ;)") + with patch("app.wahlprogramm_fetch._LOCK_FILE", bad): + result = _load_lock() + assert result == {} + + def test_missing_lock_file_returns_empty_dict(self, tmp_path): + from app.wahlprogramm_fetch import _load_lock + missing = tmp_path / "no-such-file.json" + with patch("app.wahlprogramm_fetch._LOCK_FILE", missing): + assert _load_lock() == {} + + def test_save_lock_writes_valid_json(self, tmp_path): + from app.wahlprogramm_fetch import _save_lock + target = tmp_path / "lock.json" + with patch("app.wahlprogramm_fetch._LOCK_FILE", target): + _save_lock({"x.pdf": "abc123", "y.pdf": "def456"}) + import json + loaded = json.loads(target.read_text()) + assert loaded == {"x.pdf": "abc123", "y.pdf": "def456"} + + +class TestLoadLinks: + def test_missing_yaml_returns_empty(self, tmp_path): + from app.wahlprogramm_fetch import _load_links + with patch("app.wahlprogramm_fetch._LINKS_FILE", tmp_path / "missing.yaml"): + assert _load_links() == {} + + def test_empty_yaml_returns_empty(self, tmp_path): + from app.wahlprogramm_fetch import _load_links + target = tmp_path / "empty.yaml" + target.write_text("") + with patch("app.wahlprogramm_fetch._LINKS_FILE", target): + assert _load_links() == {} + + # Hinweis: yaml ist im Unit-Setup gestubbed (siehe Top-of-File), deshalb + # testen wir _load_links nur mit existing-vs-missing-File. Die echte + # YAML-Parsing-Logik wird in der integration-Suite gegen die echte + # links.yaml validiert. + + +class TestGetMissingProgrammes: + """Tests fuer get_missing_programmes — listet BL/Partei-Kombinationen mit + Kandidaten-URL aber fehlender lokaler Datei. yaml ist gestubbed; Tests + patchen daher _load_links direkt.""" + + def test_no_yaml_returns_empty(self): + from app.wahlprogramm_fetch import get_missing_programmes + with patch("app.wahlprogramm_fetch._load_links", return_value={}): + assert get_missing_programmes() == [] + + def test_lists_entries_when_file_missing(self, tmp_path): + """Eintrag in YAML, registriertes WAHLPROGRAMME-File fehlt → listed.""" + from app.wahlprogramm_fetch import get_missing_programmes + fake_links = {"BX": {"XYZ": [{"url": "https://example.com/x.pdf"}]}} + with patch("app.wahlprogramm_fetch._load_links", return_value=fake_links): + with patch("app.wahlprogramm_fetch._REFERENZEN_DIR", tmp_path / "ref"): + missing = get_missing_programmes() + codes = [m["bl"] for m in missing] + assert "BX" in codes + + def test_bundesland_filter(self, tmp_path): + from app.wahlprogramm_fetch import get_missing_programmes + fake_links = { + "BX": {"XYZ": [{"url": "https://example.com/x.pdf"}]}, + "BY": {"ABC": [{"url": "https://example.com/y.pdf"}]}, + } + with patch("app.wahlprogramm_fetch._load_links", return_value=fake_links): + with patch("app.wahlprogramm_fetch._REFERENZEN_DIR", tmp_path / "ref"): + missing = get_missing_programmes(bundesland="BX") + codes = {m["bl"] for m in missing} + assert codes == {"BX"} + + # --------------------------------------------------------------------------- # Test 4: og_card — cache_key Determinismus und Cache-Miss/Hit # ---------------------------------------------------------------------------