From 722b073bbd37a010114159229fe6dbdd49a26610 Mon Sep 17 00:00:00 2001 From: Dotty Dotter Date: Tue, 28 Apr 2026 08:42:29 +0200 Subject: [PATCH] =?UTF-8?q?test(#134):=20wahlprogramm=5Ffetch=20Coverage?= =?UTF-8?q?=2042.8%=20=E2=86=92=2054.4%?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 8 zusaetzliche Tests: - TestLockFileRobustness: kaputtes JSON, fehlende Datei, _save_lock-Roundtrip - TestLoadLinks: missing yaml + empty yaml (gestubbed) - TestGetMissingProgrammes: leere/gefuellte Eintraege, Bundesland-Filter yaml ist im Unit-Setup gestubbed; Tests patchen _load_links direkt statt echte YAML-Parsing zu erzwingen — die echte Datei-Validierung gehoert in die integration-Suite gegen die produktive links.yaml. --- tests/test_wahlprogramm_fetch.py | 82 ++++++++++++++++++++++++++++++++ 1 file changed, 82 insertions(+) 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 # ---------------------------------------------------------------------------