gwoe-antragspruefer/tests/test_wahlprogramme.py

178 lines
8.9 KiB
Python
Raw Normal View History

refactor(programme): WAHLPROGRAMME → programme.PROGRAMME konsolidiert (#222) Schließt #222. Entfernt die Doppelung zwischen ``wahlprogramme.WAHLPROGRAMME`` und ``programme.PROGRAMME``. Single source of truth ist jetzt ``programme.PROGRAMME`` als Literal mit allen 287 Programmen (Wahlprogramme + Bundes- + Landes-Grundsatzprogramme, historisch + aktuell). Schema schmaler — Felder ohne Konsumenten entfallen: - ``regierungsbildung`` / ``regierungsende`` → gehören zu ``legislaturen.REGIERUNGEN``. Verbindung Programm→Regierung läuft jetzt über ``legislaturen.regierung_zum_zeitpunkt(bl, datum)``. - ``partei`` (Langform "CDU NRW") → ableitbar aus partei + bundesland. - ``jahr`` → ableitbar aus ``gueltig_ab[:4]``. - ``beschluss`` / ``wahl`` / ``hinweis`` → keine App-Konsumenten. Felder im neuen Schema: id, typ, partei, bundesland, wp, gueltig_ab, gueltig_bis, name, titel (Slogan, optional), pdf, seiten. Daten-Migration einmalig via ``tools/build_programme_literal.py``: - Basis: bisherige embeddings.PROGRAMME (alle 287 IDs + gueltig_ab/bis) - titel aus WAHLPROGRAMME für die ~80 aktuellen Wahlprogramme + Land-Grundsatzprogramm-Slogans (ehem. _ARCHIVED_SKELETONS) - seiten via ``fitz.open(p).page_count`` für alle 287 PDFs Aufrufer migriert: - app/main.py:4055 — ``aktuelles_wahlprogramm(bl, partei).pdf`` - app/wahlprogramm_check.py — ``parteien_mit_wahlprogramm(bl)`` - app/redline_utils.py — Reverse-Lookup über ``all_programme()`` - app/wahlprogramm_fetch.py (3 Stellen) — ``aktuelles_wahlprogramm()`` - tests/test_redline_parser.py — Programm-Lookup statt WAHLPROGRAMME ``wahlprogramme.py`` schrumpft auf den Such-Code: Keyword-Fallback + PDF-Text-Loader + ein dünner ``get_wahlprogramm``-Compat-Adapter zu ``programme.aktuelles_wahlprogramm``. Drei Helper gelöscht (keine App-Konsumenten): ``regierungsbildung_for``, ``regierungsende_for``, ``regierung_aktuell``. Wer das Datum der Regierungsbildung will, fragt ``legislaturen.aktuelle_regierung(bl).get('von')``. Test-Suite: 1217 grün (vorher 1244, Differenz 27 = entfernte regierungs-Helper-Tests + obsolete WAHLPROGRAMME-Strukturtests).
2026-05-09 00:37:35 +02:00
"""Tests for wahlprogramme.py.
Nach dem #222-Refactor ist dieses Modul nur noch ein dünner Wrapper:
keyword-basierte Suche + PDF-Text-Loader + ein Compat-Adapter
``get_wahlprogramm`` der zu ``programme.aktuelles_wahlprogramm``
delegiert. Die Stamm-Daten (``WAHLPROGRAMME``-Literal) sind nach
``programme.PROGRAMME`` gewandert. Strukturelle Daten-Tests leben
deshalb in ``test_programme.py``.
"""
import pytest
Add pytest suite + fix two regex bugs uncovered by it (#46) Erste Tests für die Codebase. 77 Tests, 0.08s Laufzeit, decken die drei Bug-Klassen aus der April-2026-Adapter-Session ab plus haben schon zwei weitere Bugs in Production-Code aufgedeckt. ## Setup - requirements-dev.txt mit pytest + pytest-asyncio - pytest.ini mit asyncio_mode=auto - tests/conftest.py stubbt fitz/bs4/openai/pydantic_settings, damit die Suite ohne den vollen prod-requirements-Satz läuft (pure unit tests, kein PDF-Parsing, kein HTTP) ## Tests - tests/test_parlamente.py (33 Tests) * PortalaAdapter._parse_hit_list_cards: doctype/doctype_full NameError-Regression aus 1cb030a, plus Title/Drucksache/Fraktion- /Datum/PDF-Extraktion gegen ein BE-Card-Fixture * PortalaAdapter._parse_hit_list_dump: gegen ein LSA-Perl-Dump- Fixture inkl. Hex-Escape-Decoding (\x{fc} → ü) * PortalaAdapter._parse_hit_list_html: Auto-Detection zwischen Card- und Dump-Format * PortalaAdapter._normalize_fraktion: kanonische Fraktion-Codes inkl. F.D.P.-mit-Punkten, BÜNDNIS 90, DIE LINKE, BSW * ParLDokAdapter._hit_to_drucksache: JSON-Hit → Drucksache Mapping inkl. /navpanes-Stripping, MdL-mit-Partei-in-Klammern, Landesregierung-Detection * ParLDokAdapter._fulltext_id: bundle.js-mirroring (deferred, aber dokumentiert) * ADAPTERS-Registry-Sanity - tests/test_embeddings.py (11 Tests) * _chunk_source_label: Programm-Name + Seite (Halluzinations- Bug-Regression aus 1b5fd96) * format_quotes_for_prompt: jeder Chunk muss Programm-Name enthalten, strict-citation-Hinweis muss im Output sein, keine NRW-Halluzinationen für MV/BE-Chunk-Sets - tests/test_wahlprogramme.py (14 Tests) * Registry-Struktur (jahr int, seiten int, .pdf-Endung) * File-Existenz: jede registrierte PDF muss in static/referenzen/ liegen — würde Tippfehler in den 22 indexierten Programmen sofort fangen * embeddings.PROGRAMME-Konsistenz-Cross-Check - tests/test_bundeslaender.py (15 Tests) * Sanity über 16-State-Registry * #48-Klassifikations-Regression: TH=ParlDok, HB=StarWeb, SN=Eigensystem * Wahltermine plausibel (zwischen 2026 und 2035) - tests/test_analyzer.py (4 Tests) * Markdown-Codeblock-Stripping aus dem JSON-Retry-Loop ## Bug-Funde während der Test-Schreibphase Zwei Production-Bugs in den _normalize_fraktion-Helfern wurden durch die neuen Tests sofort aufgedeckt und im selben Commit gefixt: 1. PortalaAdapter._normalize_fraktion matched "F.D.P." (mit Punkten, wie historische SH/HB-Drucksachen) nicht — Regex \bFDP\b ist zu strikt. Fix: \bF\.?\s*D\.?\s*P\.?\b analog zu ParLDokAdapter. 2. ParLDokAdapter._normalize_fraktion (auch PortalaAdapter) matched "Ministerium der Finanzen" nicht als Landesregierung, weil \bMINISTER\b die Wortgrenze auch nach MINISTER verlangt — bei MINISTERIUM steht aber IUM danach, keine Wortgrenze. Fix: \bMINISTER ohne abschließendes \b. Beide Bugs hätten Fraktion-Felder bei Drucksachen der Bremischen Bürgerschaft (FDP-Listen) und bei Landesregierungs-Drucksachen in MV/LSA fälschlich leer gelassen — exakt der "fraktionen=[]"- Befund aus dem MV-Smoke-Test in #4. Phase 0 aus Roadmap-Issue #49. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-08 23:26:06 +02:00
from app.wahlprogramme import (
REFERENZEN_PATH,
get_wahlprogramm,
refactor(programme): WAHLPROGRAMME → programme.PROGRAMME konsolidiert (#222) Schließt #222. Entfernt die Doppelung zwischen ``wahlprogramme.WAHLPROGRAMME`` und ``programme.PROGRAMME``. Single source of truth ist jetzt ``programme.PROGRAMME`` als Literal mit allen 287 Programmen (Wahlprogramme + Bundes- + Landes-Grundsatzprogramme, historisch + aktuell). Schema schmaler — Felder ohne Konsumenten entfallen: - ``regierungsbildung`` / ``regierungsende`` → gehören zu ``legislaturen.REGIERUNGEN``. Verbindung Programm→Regierung läuft jetzt über ``legislaturen.regierung_zum_zeitpunkt(bl, datum)``. - ``partei`` (Langform "CDU NRW") → ableitbar aus partei + bundesland. - ``jahr`` → ableitbar aus ``gueltig_ab[:4]``. - ``beschluss`` / ``wahl`` / ``hinweis`` → keine App-Konsumenten. Felder im neuen Schema: id, typ, partei, bundesland, wp, gueltig_ab, gueltig_bis, name, titel (Slogan, optional), pdf, seiten. Daten-Migration einmalig via ``tools/build_programme_literal.py``: - Basis: bisherige embeddings.PROGRAMME (alle 287 IDs + gueltig_ab/bis) - titel aus WAHLPROGRAMME für die ~80 aktuellen Wahlprogramme + Land-Grundsatzprogramm-Slogans (ehem. _ARCHIVED_SKELETONS) - seiten via ``fitz.open(p).page_count`` für alle 287 PDFs Aufrufer migriert: - app/main.py:4055 — ``aktuelles_wahlprogramm(bl, partei).pdf`` - app/wahlprogramm_check.py — ``parteien_mit_wahlprogramm(bl)`` - app/redline_utils.py — Reverse-Lookup über ``all_programme()`` - app/wahlprogramm_fetch.py (3 Stellen) — ``aktuelles_wahlprogramm()`` - tests/test_redline_parser.py — Programm-Lookup statt WAHLPROGRAMME ``wahlprogramme.py`` schrumpft auf den Such-Code: Keyword-Fallback + PDF-Text-Loader + ein dünner ``get_wahlprogramm``-Compat-Adapter zu ``programme.aktuelles_wahlprogramm``. Drei Helper gelöscht (keine App-Konsumenten): ``regierungsbildung_for``, ``regierungsende_for``, ``regierung_aktuell``. Wer das Datum der Regierungsbildung will, fragt ``legislaturen.aktuelle_regierung(bl).get('von')``. Test-Suite: 1217 grün (vorher 1244, Differenz 27 = entfernte regierungs-Helper-Tests + obsolete WAHLPROGRAMME-Strukturtests).
2026-05-09 00:37:35 +02:00
load_wahlprogramm_text,
search_wahlprogramm,
find_relevant_quotes,
format_quote_for_prompt,
Add pytest suite + fix two regex bugs uncovered by it (#46) Erste Tests für die Codebase. 77 Tests, 0.08s Laufzeit, decken die drei Bug-Klassen aus der April-2026-Adapter-Session ab plus haben schon zwei weitere Bugs in Production-Code aufgedeckt. ## Setup - requirements-dev.txt mit pytest + pytest-asyncio - pytest.ini mit asyncio_mode=auto - tests/conftest.py stubbt fitz/bs4/openai/pydantic_settings, damit die Suite ohne den vollen prod-requirements-Satz läuft (pure unit tests, kein PDF-Parsing, kein HTTP) ## Tests - tests/test_parlamente.py (33 Tests) * PortalaAdapter._parse_hit_list_cards: doctype/doctype_full NameError-Regression aus 1cb030a, plus Title/Drucksache/Fraktion- /Datum/PDF-Extraktion gegen ein BE-Card-Fixture * PortalaAdapter._parse_hit_list_dump: gegen ein LSA-Perl-Dump- Fixture inkl. Hex-Escape-Decoding (\x{fc} → ü) * PortalaAdapter._parse_hit_list_html: Auto-Detection zwischen Card- und Dump-Format * PortalaAdapter._normalize_fraktion: kanonische Fraktion-Codes inkl. F.D.P.-mit-Punkten, BÜNDNIS 90, DIE LINKE, BSW * ParLDokAdapter._hit_to_drucksache: JSON-Hit → Drucksache Mapping inkl. /navpanes-Stripping, MdL-mit-Partei-in-Klammern, Landesregierung-Detection * ParLDokAdapter._fulltext_id: bundle.js-mirroring (deferred, aber dokumentiert) * ADAPTERS-Registry-Sanity - tests/test_embeddings.py (11 Tests) * _chunk_source_label: Programm-Name + Seite (Halluzinations- Bug-Regression aus 1b5fd96) * format_quotes_for_prompt: jeder Chunk muss Programm-Name enthalten, strict-citation-Hinweis muss im Output sein, keine NRW-Halluzinationen für MV/BE-Chunk-Sets - tests/test_wahlprogramme.py (14 Tests) * Registry-Struktur (jahr int, seiten int, .pdf-Endung) * File-Existenz: jede registrierte PDF muss in static/referenzen/ liegen — würde Tippfehler in den 22 indexierten Programmen sofort fangen * embeddings.PROGRAMME-Konsistenz-Cross-Check - tests/test_bundeslaender.py (15 Tests) * Sanity über 16-State-Registry * #48-Klassifikations-Regression: TH=ParlDok, HB=StarWeb, SN=Eigensystem * Wahltermine plausibel (zwischen 2026 und 2035) - tests/test_analyzer.py (4 Tests) * Markdown-Codeblock-Stripping aus dem JSON-Retry-Loop ## Bug-Funde während der Test-Schreibphase Zwei Production-Bugs in den _normalize_fraktion-Helfern wurden durch die neuen Tests sofort aufgedeckt und im selben Commit gefixt: 1. PortalaAdapter._normalize_fraktion matched "F.D.P." (mit Punkten, wie historische SH/HB-Drucksachen) nicht — Regex \bFDP\b ist zu strikt. Fix: \bF\.?\s*D\.?\s*P\.?\b analog zu ParLDokAdapter. 2. ParLDokAdapter._normalize_fraktion (auch PortalaAdapter) matched "Ministerium der Finanzen" nicht als Landesregierung, weil \bMINISTER\b die Wortgrenze auch nach MINISTER verlangt — bei MINISTERIUM steht aber IUM danach, keine Wortgrenze. Fix: \bMINISTER ohne abschließendes \b. Beide Bugs hätten Fraktion-Felder bei Drucksachen der Bremischen Bürgerschaft (FDP-Listen) und bei Landesregierungs-Drucksachen in MV/LSA fälschlich leer gelassen — exakt der "fraktionen=[]"- Befund aus dem MV-Smoke-Test in #4. Phase 0 aus Roadmap-Issue #49. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-08 23:26:06 +02:00
)
refactor(programme): WAHLPROGRAMME → programme.PROGRAMME konsolidiert (#222) Schließt #222. Entfernt die Doppelung zwischen ``wahlprogramme.WAHLPROGRAMME`` und ``programme.PROGRAMME``. Single source of truth ist jetzt ``programme.PROGRAMME`` als Literal mit allen 287 Programmen (Wahlprogramme + Bundes- + Landes-Grundsatzprogramme, historisch + aktuell). Schema schmaler — Felder ohne Konsumenten entfallen: - ``regierungsbildung`` / ``regierungsende`` → gehören zu ``legislaturen.REGIERUNGEN``. Verbindung Programm→Regierung läuft jetzt über ``legislaturen.regierung_zum_zeitpunkt(bl, datum)``. - ``partei`` (Langform "CDU NRW") → ableitbar aus partei + bundesland. - ``jahr`` → ableitbar aus ``gueltig_ab[:4]``. - ``beschluss`` / ``wahl`` / ``hinweis`` → keine App-Konsumenten. Felder im neuen Schema: id, typ, partei, bundesland, wp, gueltig_ab, gueltig_bis, name, titel (Slogan, optional), pdf, seiten. Daten-Migration einmalig via ``tools/build_programme_literal.py``: - Basis: bisherige embeddings.PROGRAMME (alle 287 IDs + gueltig_ab/bis) - titel aus WAHLPROGRAMME für die ~80 aktuellen Wahlprogramme + Land-Grundsatzprogramm-Slogans (ehem. _ARCHIVED_SKELETONS) - seiten via ``fitz.open(p).page_count`` für alle 287 PDFs Aufrufer migriert: - app/main.py:4055 — ``aktuelles_wahlprogramm(bl, partei).pdf`` - app/wahlprogramm_check.py — ``parteien_mit_wahlprogramm(bl)`` - app/redline_utils.py — Reverse-Lookup über ``all_programme()`` - app/wahlprogramm_fetch.py (3 Stellen) — ``aktuelles_wahlprogramm()`` - tests/test_redline_parser.py — Programm-Lookup statt WAHLPROGRAMME ``wahlprogramme.py`` schrumpft auf den Such-Code: Keyword-Fallback + PDF-Text-Loader + ein dünner ``get_wahlprogramm``-Compat-Adapter zu ``programme.aktuelles_wahlprogramm``. Drei Helper gelöscht (keine App-Konsumenten): ``regierungsbildung_for``, ``regierungsende_for``, ``regierung_aktuell``. Wer das Datum der Regierungsbildung will, fragt ``legislaturen.aktuelle_regierung(bl).get('von')``. Test-Suite: 1217 grün (vorher 1244, Differenz 27 = entfernte regierungs-Helper-Tests + obsolete WAHLPROGRAMME-Strukturtests).
2026-05-09 00:37:35 +02:00
from app.programme import parteien_mit_wahlprogramm
Add pytest suite + fix two regex bugs uncovered by it (#46) Erste Tests für die Codebase. 77 Tests, 0.08s Laufzeit, decken die drei Bug-Klassen aus der April-2026-Adapter-Session ab plus haben schon zwei weitere Bugs in Production-Code aufgedeckt. ## Setup - requirements-dev.txt mit pytest + pytest-asyncio - pytest.ini mit asyncio_mode=auto - tests/conftest.py stubbt fitz/bs4/openai/pydantic_settings, damit die Suite ohne den vollen prod-requirements-Satz läuft (pure unit tests, kein PDF-Parsing, kein HTTP) ## Tests - tests/test_parlamente.py (33 Tests) * PortalaAdapter._parse_hit_list_cards: doctype/doctype_full NameError-Regression aus 1cb030a, plus Title/Drucksache/Fraktion- /Datum/PDF-Extraktion gegen ein BE-Card-Fixture * PortalaAdapter._parse_hit_list_dump: gegen ein LSA-Perl-Dump- Fixture inkl. Hex-Escape-Decoding (\x{fc} → ü) * PortalaAdapter._parse_hit_list_html: Auto-Detection zwischen Card- und Dump-Format * PortalaAdapter._normalize_fraktion: kanonische Fraktion-Codes inkl. F.D.P.-mit-Punkten, BÜNDNIS 90, DIE LINKE, BSW * ParLDokAdapter._hit_to_drucksache: JSON-Hit → Drucksache Mapping inkl. /navpanes-Stripping, MdL-mit-Partei-in-Klammern, Landesregierung-Detection * ParLDokAdapter._fulltext_id: bundle.js-mirroring (deferred, aber dokumentiert) * ADAPTERS-Registry-Sanity - tests/test_embeddings.py (11 Tests) * _chunk_source_label: Programm-Name + Seite (Halluzinations- Bug-Regression aus 1b5fd96) * format_quotes_for_prompt: jeder Chunk muss Programm-Name enthalten, strict-citation-Hinweis muss im Output sein, keine NRW-Halluzinationen für MV/BE-Chunk-Sets - tests/test_wahlprogramme.py (14 Tests) * Registry-Struktur (jahr int, seiten int, .pdf-Endung) * File-Existenz: jede registrierte PDF muss in static/referenzen/ liegen — würde Tippfehler in den 22 indexierten Programmen sofort fangen * embeddings.PROGRAMME-Konsistenz-Cross-Check - tests/test_bundeslaender.py (15 Tests) * Sanity über 16-State-Registry * #48-Klassifikations-Regression: TH=ParlDok, HB=StarWeb, SN=Eigensystem * Wahltermine plausibel (zwischen 2026 und 2035) - tests/test_analyzer.py (4 Tests) * Markdown-Codeblock-Stripping aus dem JSON-Retry-Loop ## Bug-Funde während der Test-Schreibphase Zwei Production-Bugs in den _normalize_fraktion-Helfern wurden durch die neuen Tests sofort aufgedeckt und im selben Commit gefixt: 1. PortalaAdapter._normalize_fraktion matched "F.D.P." (mit Punkten, wie historische SH/HB-Drucksachen) nicht — Regex \bFDP\b ist zu strikt. Fix: \bF\.?\s*D\.?\s*P\.?\b analog zu ParLDokAdapter. 2. ParLDokAdapter._normalize_fraktion (auch PortalaAdapter) matched "Ministerium der Finanzen" nicht als Landesregierung, weil \bMINISTER\b die Wortgrenze auch nach MINISTER verlangt — bei MINISTERIUM steht aber IUM danach, keine Wortgrenze. Fix: \bMINISTER ohne abschließendes \b. Beide Bugs hätten Fraktion-Felder bei Drucksachen der Bremischen Bürgerschaft (FDP-Listen) und bei Landesregierungs-Drucksachen in MV/LSA fälschlich leer gelassen — exakt der "fraktionen=[]"- Befund aus dem MV-Smoke-Test in #4. Phase 0 aus Roadmap-Issue #49. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-08 23:26:06 +02:00
# ─────────────────────────────────────────────────────────────────────────────
refactor(programme): WAHLPROGRAMME → programme.PROGRAMME konsolidiert (#222) Schließt #222. Entfernt die Doppelung zwischen ``wahlprogramme.WAHLPROGRAMME`` und ``programme.PROGRAMME``. Single source of truth ist jetzt ``programme.PROGRAMME`` als Literal mit allen 287 Programmen (Wahlprogramme + Bundes- + Landes-Grundsatzprogramme, historisch + aktuell). Schema schmaler — Felder ohne Konsumenten entfallen: - ``regierungsbildung`` / ``regierungsende`` → gehören zu ``legislaturen.REGIERUNGEN``. Verbindung Programm→Regierung läuft jetzt über ``legislaturen.regierung_zum_zeitpunkt(bl, datum)``. - ``partei`` (Langform "CDU NRW") → ableitbar aus partei + bundesland. - ``jahr`` → ableitbar aus ``gueltig_ab[:4]``. - ``beschluss`` / ``wahl`` / ``hinweis`` → keine App-Konsumenten. Felder im neuen Schema: id, typ, partei, bundesland, wp, gueltig_ab, gueltig_bis, name, titel (Slogan, optional), pdf, seiten. Daten-Migration einmalig via ``tools/build_programme_literal.py``: - Basis: bisherige embeddings.PROGRAMME (alle 287 IDs + gueltig_ab/bis) - titel aus WAHLPROGRAMME für die ~80 aktuellen Wahlprogramme + Land-Grundsatzprogramm-Slogans (ehem. _ARCHIVED_SKELETONS) - seiten via ``fitz.open(p).page_count`` für alle 287 PDFs Aufrufer migriert: - app/main.py:4055 — ``aktuelles_wahlprogramm(bl, partei).pdf`` - app/wahlprogramm_check.py — ``parteien_mit_wahlprogramm(bl)`` - app/redline_utils.py — Reverse-Lookup über ``all_programme()`` - app/wahlprogramm_fetch.py (3 Stellen) — ``aktuelles_wahlprogramm()`` - tests/test_redline_parser.py — Programm-Lookup statt WAHLPROGRAMME ``wahlprogramme.py`` schrumpft auf den Such-Code: Keyword-Fallback + PDF-Text-Loader + ein dünner ``get_wahlprogramm``-Compat-Adapter zu ``programme.aktuelles_wahlprogramm``. Drei Helper gelöscht (keine App-Konsumenten): ``regierungsbildung_for``, ``regierungsende_for``, ``regierung_aktuell``. Wer das Datum der Regierungsbildung will, fragt ``legislaturen.aktuelle_regierung(bl).get('von')``. Test-Suite: 1217 grün (vorher 1244, Differenz 27 = entfernte regierungs-Helper-Tests + obsolete WAHLPROGRAMME-Strukturtests).
2026-05-09 00:37:35 +02:00
# Stichproben aktiver Bundesländer + zugeordnete Parteien
Add pytest suite + fix two regex bugs uncovered by it (#46) Erste Tests für die Codebase. 77 Tests, 0.08s Laufzeit, decken die drei Bug-Klassen aus der April-2026-Adapter-Session ab plus haben schon zwei weitere Bugs in Production-Code aufgedeckt. ## Setup - requirements-dev.txt mit pytest + pytest-asyncio - pytest.ini mit asyncio_mode=auto - tests/conftest.py stubbt fitz/bs4/openai/pydantic_settings, damit die Suite ohne den vollen prod-requirements-Satz läuft (pure unit tests, kein PDF-Parsing, kein HTTP) ## Tests - tests/test_parlamente.py (33 Tests) * PortalaAdapter._parse_hit_list_cards: doctype/doctype_full NameError-Regression aus 1cb030a, plus Title/Drucksache/Fraktion- /Datum/PDF-Extraktion gegen ein BE-Card-Fixture * PortalaAdapter._parse_hit_list_dump: gegen ein LSA-Perl-Dump- Fixture inkl. Hex-Escape-Decoding (\x{fc} → ü) * PortalaAdapter._parse_hit_list_html: Auto-Detection zwischen Card- und Dump-Format * PortalaAdapter._normalize_fraktion: kanonische Fraktion-Codes inkl. F.D.P.-mit-Punkten, BÜNDNIS 90, DIE LINKE, BSW * ParLDokAdapter._hit_to_drucksache: JSON-Hit → Drucksache Mapping inkl. /navpanes-Stripping, MdL-mit-Partei-in-Klammern, Landesregierung-Detection * ParLDokAdapter._fulltext_id: bundle.js-mirroring (deferred, aber dokumentiert) * ADAPTERS-Registry-Sanity - tests/test_embeddings.py (11 Tests) * _chunk_source_label: Programm-Name + Seite (Halluzinations- Bug-Regression aus 1b5fd96) * format_quotes_for_prompt: jeder Chunk muss Programm-Name enthalten, strict-citation-Hinweis muss im Output sein, keine NRW-Halluzinationen für MV/BE-Chunk-Sets - tests/test_wahlprogramme.py (14 Tests) * Registry-Struktur (jahr int, seiten int, .pdf-Endung) * File-Existenz: jede registrierte PDF muss in static/referenzen/ liegen — würde Tippfehler in den 22 indexierten Programmen sofort fangen * embeddings.PROGRAMME-Konsistenz-Cross-Check - tests/test_bundeslaender.py (15 Tests) * Sanity über 16-State-Registry * #48-Klassifikations-Regression: TH=ParlDok, HB=StarWeb, SN=Eigensystem * Wahltermine plausibel (zwischen 2026 und 2035) - tests/test_analyzer.py (4 Tests) * Markdown-Codeblock-Stripping aus dem JSON-Retry-Loop ## Bug-Funde während der Test-Schreibphase Zwei Production-Bugs in den _normalize_fraktion-Helfern wurden durch die neuen Tests sofort aufgedeckt und im selben Commit gefixt: 1. PortalaAdapter._normalize_fraktion matched "F.D.P." (mit Punkten, wie historische SH/HB-Drucksachen) nicht — Regex \bFDP\b ist zu strikt. Fix: \bF\.?\s*D\.?\s*P\.?\b analog zu ParLDokAdapter. 2. ParLDokAdapter._normalize_fraktion (auch PortalaAdapter) matched "Ministerium der Finanzen" nicht als Landesregierung, weil \bMINISTER\b die Wortgrenze auch nach MINISTER verlangt — bei MINISTERIUM steht aber IUM danach, keine Wortgrenze. Fix: \bMINISTER ohne abschließendes \b. Beide Bugs hätten Fraktion-Felder bei Drucksachen der Bremischen Bürgerschaft (FDP-Listen) und bei Landesregierungs-Drucksachen in MV/LSA fälschlich leer gelassen — exakt der "fraktionen=[]"- Befund aus dem MV-Smoke-Test in #4. Phase 0 aus Roadmap-Issue #49. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-08 23:26:06 +02:00
# ─────────────────────────────────────────────────────────────────────────────
class TestParteienMitWahlprogramm:
refactor(programme): WAHLPROGRAMME → programme.PROGRAMME konsolidiert (#222) Schließt #222. Entfernt die Doppelung zwischen ``wahlprogramme.WAHLPROGRAMME`` und ``programme.PROGRAMME``. Single source of truth ist jetzt ``programme.PROGRAMME`` als Literal mit allen 287 Programmen (Wahlprogramme + Bundes- + Landes-Grundsatzprogramme, historisch + aktuell). Schema schmaler — Felder ohne Konsumenten entfallen: - ``regierungsbildung`` / ``regierungsende`` → gehören zu ``legislaturen.REGIERUNGEN``. Verbindung Programm→Regierung läuft jetzt über ``legislaturen.regierung_zum_zeitpunkt(bl, datum)``. - ``partei`` (Langform "CDU NRW") → ableitbar aus partei + bundesland. - ``jahr`` → ableitbar aus ``gueltig_ab[:4]``. - ``beschluss`` / ``wahl`` / ``hinweis`` → keine App-Konsumenten. Felder im neuen Schema: id, typ, partei, bundesland, wp, gueltig_ab, gueltig_bis, name, titel (Slogan, optional), pdf, seiten. Daten-Migration einmalig via ``tools/build_programme_literal.py``: - Basis: bisherige embeddings.PROGRAMME (alle 287 IDs + gueltig_ab/bis) - titel aus WAHLPROGRAMME für die ~80 aktuellen Wahlprogramme + Land-Grundsatzprogramm-Slogans (ehem. _ARCHIVED_SKELETONS) - seiten via ``fitz.open(p).page_count`` für alle 287 PDFs Aufrufer migriert: - app/main.py:4055 — ``aktuelles_wahlprogramm(bl, partei).pdf`` - app/wahlprogramm_check.py — ``parteien_mit_wahlprogramm(bl)`` - app/redline_utils.py — Reverse-Lookup über ``all_programme()`` - app/wahlprogramm_fetch.py (3 Stellen) — ``aktuelles_wahlprogramm()`` - tests/test_redline_parser.py — Programm-Lookup statt WAHLPROGRAMME ``wahlprogramme.py`` schrumpft auf den Such-Code: Keyword-Fallback + PDF-Text-Loader + ein dünner ``get_wahlprogramm``-Compat-Adapter zu ``programme.aktuelles_wahlprogramm``. Drei Helper gelöscht (keine App-Konsumenten): ``regierungsbildung_for``, ``regierungsende_for``, ``regierung_aktuell``. Wer das Datum der Regierungsbildung will, fragt ``legislaturen.aktuelle_regierung(bl).get('von')``. Test-Suite: 1217 grün (vorher 1244, Differenz 27 = entfernte regierungs-Helper-Tests + obsolete WAHLPROGRAMME-Strukturtests).
2026-05-09 00:37:35 +02:00
"""Smoke-Test: in den jeweiligen BL liegen die erwarteten Fraktionen.
Strikte Schema-Tests gegen ``programme.PROGRAMME`` in test_programme.py."""
Add pytest suite + fix two regex bugs uncovered by it (#46) Erste Tests für die Codebase. 77 Tests, 0.08s Laufzeit, decken die drei Bug-Klassen aus der April-2026-Adapter-Session ab plus haben schon zwei weitere Bugs in Production-Code aufgedeckt. ## Setup - requirements-dev.txt mit pytest + pytest-asyncio - pytest.ini mit asyncio_mode=auto - tests/conftest.py stubbt fitz/bs4/openai/pydantic_settings, damit die Suite ohne den vollen prod-requirements-Satz läuft (pure unit tests, kein PDF-Parsing, kein HTTP) ## Tests - tests/test_parlamente.py (33 Tests) * PortalaAdapter._parse_hit_list_cards: doctype/doctype_full NameError-Regression aus 1cb030a, plus Title/Drucksache/Fraktion- /Datum/PDF-Extraktion gegen ein BE-Card-Fixture * PortalaAdapter._parse_hit_list_dump: gegen ein LSA-Perl-Dump- Fixture inkl. Hex-Escape-Decoding (\x{fc} → ü) * PortalaAdapter._parse_hit_list_html: Auto-Detection zwischen Card- und Dump-Format * PortalaAdapter._normalize_fraktion: kanonische Fraktion-Codes inkl. F.D.P.-mit-Punkten, BÜNDNIS 90, DIE LINKE, BSW * ParLDokAdapter._hit_to_drucksache: JSON-Hit → Drucksache Mapping inkl. /navpanes-Stripping, MdL-mit-Partei-in-Klammern, Landesregierung-Detection * ParLDokAdapter._fulltext_id: bundle.js-mirroring (deferred, aber dokumentiert) * ADAPTERS-Registry-Sanity - tests/test_embeddings.py (11 Tests) * _chunk_source_label: Programm-Name + Seite (Halluzinations- Bug-Regression aus 1b5fd96) * format_quotes_for_prompt: jeder Chunk muss Programm-Name enthalten, strict-citation-Hinweis muss im Output sein, keine NRW-Halluzinationen für MV/BE-Chunk-Sets - tests/test_wahlprogramme.py (14 Tests) * Registry-Struktur (jahr int, seiten int, .pdf-Endung) * File-Existenz: jede registrierte PDF muss in static/referenzen/ liegen — würde Tippfehler in den 22 indexierten Programmen sofort fangen * embeddings.PROGRAMME-Konsistenz-Cross-Check - tests/test_bundeslaender.py (15 Tests) * Sanity über 16-State-Registry * #48-Klassifikations-Regression: TH=ParlDok, HB=StarWeb, SN=Eigensystem * Wahltermine plausibel (zwischen 2026 und 2035) - tests/test_analyzer.py (4 Tests) * Markdown-Codeblock-Stripping aus dem JSON-Retry-Loop ## Bug-Funde während der Test-Schreibphase Zwei Production-Bugs in den _normalize_fraktion-Helfern wurden durch die neuen Tests sofort aufgedeckt und im selben Commit gefixt: 1. PortalaAdapter._normalize_fraktion matched "F.D.P." (mit Punkten, wie historische SH/HB-Drucksachen) nicht — Regex \bFDP\b ist zu strikt. Fix: \bF\.?\s*D\.?\s*P\.?\b analog zu ParLDokAdapter. 2. ParLDokAdapter._normalize_fraktion (auch PortalaAdapter) matched "Ministerium der Finanzen" nicht als Landesregierung, weil \bMINISTER\b die Wortgrenze auch nach MINISTER verlangt — bei MINISTERIUM steht aber IUM danach, keine Wortgrenze. Fix: \bMINISTER ohne abschließendes \b. Beide Bugs hätten Fraktion-Felder bei Drucksachen der Bremischen Bürgerschaft (FDP-Listen) und bei Landesregierungs-Drucksachen in MV/LSA fälschlich leer gelassen — exakt der "fraktionen=[]"- Befund aus dem MV-Smoke-Test in #4. Phase 0 aus Roadmap-Issue #49. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-08 23:26:06 +02:00
def test_nrw_has_five_parteien(self):
parteien = parteien_mit_wahlprogramm("NRW")
assert set(parteien) == {"CDU", "SPD", "GRÜNE", "FDP", "AfD"}
chore: konsolidiere Working-Tree mit dev-Stand nach Nextcloud-Sync-Resolution Mehrtaegiger Sync-Stillstand hatte ueber 50 conflicted-copy-Dateien im Working-Tree erzeugt. Die jeweils neuere Version wurde basierend auf md5-Hash-Vergleich zum laufenden gwoe-antragspruefer-dev-Container eingespielt. Konsolidiert (38 modifiziert): - analyzer.py, auswertungen.py, auth.py, config.py, database.py, drucksache_typen.py, embeddings.py, main.py, models.py, parlamente.py, ports/llm_bewerter.py, presse_generator.py, redline_utils.py, report.py, validators.py, wahlprogramm_fetch.py, wahlprogramm-links.yaml, wahlprogramm-shas.lock.json - v2-Templates: base, components/{icon, matrix_mini, queue_widget, result_row}, screens/{admin_queue, admin_stand, aktuelle-themen, antrag_detail, auswertungen, cluster, landtag_suche, merkliste, methodik, tags}, static/v2/v2.css - Tests: test_embeddings (Strict-Mode-Drop in reconstruct_zitate), test_endpoints_smoke, test_presse_generator, test_report, test_wahlprogramme (mit TestRegierungsbildung-Block, +120 LOC) - docker-compose.dev.yml, docs/adr/index.md, docs/reference/api.md, mkdocs.yml Neuzugaenge: - app/marker.py, app/pm_render.py — Konsistenz-Marker, PM-Render-Adapter - app/templates/v2/screens/scorecard{,_portrait,_werkstatt}.html — Cloud-Design-Scorecard - app/static/v3/, app/templates/v3/ — v3-Layout-Hierarchie - docs/adr/0010-stimmverhalten-gwoe-aggregat.md - docs/adr/0011-aktuelle-themen-pm-generator.md - docs/adr/0012-debug-auth-token-bypass.md - scripts/{auto-rate-orphans, pm-quality-audit, pm-sample-bundle, rotate-debug-token}.sh - tests/e2e/test_smoke_browser.py, tests/test_{auto_rate_runs, icons, marker, pm_render, presse_generator_style, thread_splitter, v2_pdf_consistency}.py Plus inhaltlich uebernommen aus dem Conflict-Stand: - embeddings.py: fw-by-2023.partei korrigiert von "FW" zu "FREIE WAEHLER" (war Mismatch zu wahlprogramme.py) - embeddings.py: detailliertere Naming der BTW-2025-Wahlprogramme Test-Suite-Stand: 1209 passed, 73 skipped (4 pre-existing failures in test_presse_generator_style.py + 1 collection error in integration/test_citations_substring.py — beide nicht durch dieses Konsolidierungs-Commit verursacht). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-08 00:04:21 +02:00
def test_bund_has_eight_parteien(self):
parteien = parteien_mit_wahlprogramm("BUND")
assert set(parteien) == {"CDU", "CSU", "SPD", "GRÜNE", "FDP", "AfD", "LINKE", "BSW"}
def test_hb_has_five_parteien(self):
chore: konsolidiere Working-Tree mit dev-Stand nach Nextcloud-Sync-Resolution Mehrtaegiger Sync-Stillstand hatte ueber 50 conflicted-copy-Dateien im Working-Tree erzeugt. Die jeweils neuere Version wurde basierend auf md5-Hash-Vergleich zum laufenden gwoe-antragspruefer-dev-Container eingespielt. Konsolidiert (38 modifiziert): - analyzer.py, auswertungen.py, auth.py, config.py, database.py, drucksache_typen.py, embeddings.py, main.py, models.py, parlamente.py, ports/llm_bewerter.py, presse_generator.py, redline_utils.py, report.py, validators.py, wahlprogramm_fetch.py, wahlprogramm-links.yaml, wahlprogramm-shas.lock.json - v2-Templates: base, components/{icon, matrix_mini, queue_widget, result_row}, screens/{admin_queue, admin_stand, aktuelle-themen, antrag_detail, auswertungen, cluster, landtag_suche, merkliste, methodik, tags}, static/v2/v2.css - Tests: test_embeddings (Strict-Mode-Drop in reconstruct_zitate), test_endpoints_smoke, test_presse_generator, test_report, test_wahlprogramme (mit TestRegierungsbildung-Block, +120 LOC) - docker-compose.dev.yml, docs/adr/index.md, docs/reference/api.md, mkdocs.yml Neuzugaenge: - app/marker.py, app/pm_render.py — Konsistenz-Marker, PM-Render-Adapter - app/templates/v2/screens/scorecard{,_portrait,_werkstatt}.html — Cloud-Design-Scorecard - app/static/v3/, app/templates/v3/ — v3-Layout-Hierarchie - docs/adr/0010-stimmverhalten-gwoe-aggregat.md - docs/adr/0011-aktuelle-themen-pm-generator.md - docs/adr/0012-debug-auth-token-bypass.md - scripts/{auto-rate-orphans, pm-quality-audit, pm-sample-bundle, rotate-debug-token}.sh - tests/e2e/test_smoke_browser.py, tests/test_{auto_rate_runs, icons, marker, pm_render, presse_generator_style, thread_splitter, v2_pdf_consistency}.py Plus inhaltlich uebernommen aus dem Conflict-Stand: - embeddings.py: fw-by-2023.partei korrigiert von "FW" zu "FREIE WAEHLER" (war Mismatch zu wahlprogramme.py) - embeddings.py: detailliertere Naming der BTW-2025-Wahlprogramme Test-Suite-Stand: 1209 passed, 73 skipped (4 pre-existing failures in test_presse_generator_style.py + 1 collection error in integration/test_citations_substring.py — beide nicht durch dieses Konsolidierungs-Commit verursacht). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-08 00:04:21 +02:00
# AfD war wegen Listenstreit nicht zur Bürgerschaftswahl 2023 zugelassen.
# Stattdessen ist BiW (Bürger in Wut) als 6. Fraktion in der 21. WP.
chore: konsolidiere Working-Tree mit dev-Stand nach Nextcloud-Sync-Resolution Mehrtaegiger Sync-Stillstand hatte ueber 50 conflicted-copy-Dateien im Working-Tree erzeugt. Die jeweils neuere Version wurde basierend auf md5-Hash-Vergleich zum laufenden gwoe-antragspruefer-dev-Container eingespielt. Konsolidiert (38 modifiziert): - analyzer.py, auswertungen.py, auth.py, config.py, database.py, drucksache_typen.py, embeddings.py, main.py, models.py, parlamente.py, ports/llm_bewerter.py, presse_generator.py, redline_utils.py, report.py, validators.py, wahlprogramm_fetch.py, wahlprogramm-links.yaml, wahlprogramm-shas.lock.json - v2-Templates: base, components/{icon, matrix_mini, queue_widget, result_row}, screens/{admin_queue, admin_stand, aktuelle-themen, antrag_detail, auswertungen, cluster, landtag_suche, merkliste, methodik, tags}, static/v2/v2.css - Tests: test_embeddings (Strict-Mode-Drop in reconstruct_zitate), test_endpoints_smoke, test_presse_generator, test_report, test_wahlprogramme (mit TestRegierungsbildung-Block, +120 LOC) - docker-compose.dev.yml, docs/adr/index.md, docs/reference/api.md, mkdocs.yml Neuzugaenge: - app/marker.py, app/pm_render.py — Konsistenz-Marker, PM-Render-Adapter - app/templates/v2/screens/scorecard{,_portrait,_werkstatt}.html — Cloud-Design-Scorecard - app/static/v3/, app/templates/v3/ — v3-Layout-Hierarchie - docs/adr/0010-stimmverhalten-gwoe-aggregat.md - docs/adr/0011-aktuelle-themen-pm-generator.md - docs/adr/0012-debug-auth-token-bypass.md - scripts/{auto-rate-orphans, pm-quality-audit, pm-sample-bundle, rotate-debug-token}.sh - tests/e2e/test_smoke_browser.py, tests/test_{auto_rate_runs, icons, marker, pm_render, presse_generator_style, thread_splitter, v2_pdf_consistency}.py Plus inhaltlich uebernommen aus dem Conflict-Stand: - embeddings.py: fw-by-2023.partei korrigiert von "FW" zu "FREIE WAEHLER" (war Mismatch zu wahlprogramme.py) - embeddings.py: detailliertere Naming der BTW-2025-Wahlprogramme Test-Suite-Stand: 1209 passed, 73 skipped (4 pre-existing failures in test_presse_generator_style.py + 1 collection error in integration/test_citations_substring.py — beide nicht durch dieses Konsolidierungs-Commit verursacht). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-08 00:04:21 +02:00
parteien = parteien_mit_wahlprogramm("HB")
assert set(parteien) == {"SPD", "CDU", "GRÜNE", "LINKE", "BiW"}
chore: konsolidiere Working-Tree mit dev-Stand nach Nextcloud-Sync-Resolution Mehrtaegiger Sync-Stillstand hatte ueber 50 conflicted-copy-Dateien im Working-Tree erzeugt. Die jeweils neuere Version wurde basierend auf md5-Hash-Vergleich zum laufenden gwoe-antragspruefer-dev-Container eingespielt. Konsolidiert (38 modifiziert): - analyzer.py, auswertungen.py, auth.py, config.py, database.py, drucksache_typen.py, embeddings.py, main.py, models.py, parlamente.py, ports/llm_bewerter.py, presse_generator.py, redline_utils.py, report.py, validators.py, wahlprogramm_fetch.py, wahlprogramm-links.yaml, wahlprogramm-shas.lock.json - v2-Templates: base, components/{icon, matrix_mini, queue_widget, result_row}, screens/{admin_queue, admin_stand, aktuelle-themen, antrag_detail, auswertungen, cluster, landtag_suche, merkliste, methodik, tags}, static/v2/v2.css - Tests: test_embeddings (Strict-Mode-Drop in reconstruct_zitate), test_endpoints_smoke, test_presse_generator, test_report, test_wahlprogramme (mit TestRegierungsbildung-Block, +120 LOC) - docker-compose.dev.yml, docs/adr/index.md, docs/reference/api.md, mkdocs.yml Neuzugaenge: - app/marker.py, app/pm_render.py — Konsistenz-Marker, PM-Render-Adapter - app/templates/v2/screens/scorecard{,_portrait,_werkstatt}.html — Cloud-Design-Scorecard - app/static/v3/, app/templates/v3/ — v3-Layout-Hierarchie - docs/adr/0010-stimmverhalten-gwoe-aggregat.md - docs/adr/0011-aktuelle-themen-pm-generator.md - docs/adr/0012-debug-auth-token-bypass.md - scripts/{auto-rate-orphans, pm-quality-audit, pm-sample-bundle, rotate-debug-token}.sh - tests/e2e/test_smoke_browser.py, tests/test_{auto_rate_runs, icons, marker, pm_render, presse_generator_style, thread_splitter, v2_pdf_consistency}.py Plus inhaltlich uebernommen aus dem Conflict-Stand: - embeddings.py: fw-by-2023.partei korrigiert von "FW" zu "FREIE WAEHLER" (war Mismatch zu wahlprogramme.py) - embeddings.py: detailliertere Naming der BTW-2025-Wahlprogramme Test-Suite-Stand: 1209 passed, 73 skipped (4 pre-existing failures in test_presse_generator_style.py + 1 collection error in integration/test_citations_substring.py — beide nicht durch dieses Konsolidierungs-Commit verursacht). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-08 00:04:21 +02:00
Add pytest suite + fix two regex bugs uncovered by it (#46) Erste Tests für die Codebase. 77 Tests, 0.08s Laufzeit, decken die drei Bug-Klassen aus der April-2026-Adapter-Session ab plus haben schon zwei weitere Bugs in Production-Code aufgedeckt. ## Setup - requirements-dev.txt mit pytest + pytest-asyncio - pytest.ini mit asyncio_mode=auto - tests/conftest.py stubbt fitz/bs4/openai/pydantic_settings, damit die Suite ohne den vollen prod-requirements-Satz läuft (pure unit tests, kein PDF-Parsing, kein HTTP) ## Tests - tests/test_parlamente.py (33 Tests) * PortalaAdapter._parse_hit_list_cards: doctype/doctype_full NameError-Regression aus 1cb030a, plus Title/Drucksache/Fraktion- /Datum/PDF-Extraktion gegen ein BE-Card-Fixture * PortalaAdapter._parse_hit_list_dump: gegen ein LSA-Perl-Dump- Fixture inkl. Hex-Escape-Decoding (\x{fc} → ü) * PortalaAdapter._parse_hit_list_html: Auto-Detection zwischen Card- und Dump-Format * PortalaAdapter._normalize_fraktion: kanonische Fraktion-Codes inkl. F.D.P.-mit-Punkten, BÜNDNIS 90, DIE LINKE, BSW * ParLDokAdapter._hit_to_drucksache: JSON-Hit → Drucksache Mapping inkl. /navpanes-Stripping, MdL-mit-Partei-in-Klammern, Landesregierung-Detection * ParLDokAdapter._fulltext_id: bundle.js-mirroring (deferred, aber dokumentiert) * ADAPTERS-Registry-Sanity - tests/test_embeddings.py (11 Tests) * _chunk_source_label: Programm-Name + Seite (Halluzinations- Bug-Regression aus 1b5fd96) * format_quotes_for_prompt: jeder Chunk muss Programm-Name enthalten, strict-citation-Hinweis muss im Output sein, keine NRW-Halluzinationen für MV/BE-Chunk-Sets - tests/test_wahlprogramme.py (14 Tests) * Registry-Struktur (jahr int, seiten int, .pdf-Endung) * File-Existenz: jede registrierte PDF muss in static/referenzen/ liegen — würde Tippfehler in den 22 indexierten Programmen sofort fangen * embeddings.PROGRAMME-Konsistenz-Cross-Check - tests/test_bundeslaender.py (15 Tests) * Sanity über 16-State-Registry * #48-Klassifikations-Regression: TH=ParlDok, HB=StarWeb, SN=Eigensystem * Wahltermine plausibel (zwischen 2026 und 2035) - tests/test_analyzer.py (4 Tests) * Markdown-Codeblock-Stripping aus dem JSON-Retry-Loop ## Bug-Funde während der Test-Schreibphase Zwei Production-Bugs in den _normalize_fraktion-Helfern wurden durch die neuen Tests sofort aufgedeckt und im selben Commit gefixt: 1. PortalaAdapter._normalize_fraktion matched "F.D.P." (mit Punkten, wie historische SH/HB-Drucksachen) nicht — Regex \bFDP\b ist zu strikt. Fix: \bF\.?\s*D\.?\s*P\.?\b analog zu ParLDokAdapter. 2. ParLDokAdapter._normalize_fraktion (auch PortalaAdapter) matched "Ministerium der Finanzen" nicht als Landesregierung, weil \bMINISTER\b die Wortgrenze auch nach MINISTER verlangt — bei MINISTERIUM steht aber IUM danach, keine Wortgrenze. Fix: \bMINISTER ohne abschließendes \b. Beide Bugs hätten Fraktion-Felder bei Drucksachen der Bremischen Bürgerschaft (FDP-Listen) und bei Landesregierungs-Drucksachen in MV/LSA fälschlich leer gelassen — exakt der "fraktionen=[]"- Befund aus dem MV-Smoke-Test in #4. Phase 0 aus Roadmap-Issue #49. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-08 23:26:06 +02:00
def test_unknown_bundesland_empty_list(self):
assert parteien_mit_wahlprogramm("XX") == []
# ─────────────────────────────────────────────────────────────────────────────
refactor(programme): WAHLPROGRAMME → programme.PROGRAMME konsolidiert (#222) Schließt #222. Entfernt die Doppelung zwischen ``wahlprogramme.WAHLPROGRAMME`` und ``programme.PROGRAMME``. Single source of truth ist jetzt ``programme.PROGRAMME`` als Literal mit allen 287 Programmen (Wahlprogramme + Bundes- + Landes-Grundsatzprogramme, historisch + aktuell). Schema schmaler — Felder ohne Konsumenten entfallen: - ``regierungsbildung`` / ``regierungsende`` → gehören zu ``legislaturen.REGIERUNGEN``. Verbindung Programm→Regierung läuft jetzt über ``legislaturen.regierung_zum_zeitpunkt(bl, datum)``. - ``partei`` (Langform "CDU NRW") → ableitbar aus partei + bundesland. - ``jahr`` → ableitbar aus ``gueltig_ab[:4]``. - ``beschluss`` / ``wahl`` / ``hinweis`` → keine App-Konsumenten. Felder im neuen Schema: id, typ, partei, bundesland, wp, gueltig_ab, gueltig_bis, name, titel (Slogan, optional), pdf, seiten. Daten-Migration einmalig via ``tools/build_programme_literal.py``: - Basis: bisherige embeddings.PROGRAMME (alle 287 IDs + gueltig_ab/bis) - titel aus WAHLPROGRAMME für die ~80 aktuellen Wahlprogramme + Land-Grundsatzprogramm-Slogans (ehem. _ARCHIVED_SKELETONS) - seiten via ``fitz.open(p).page_count`` für alle 287 PDFs Aufrufer migriert: - app/main.py:4055 — ``aktuelles_wahlprogramm(bl, partei).pdf`` - app/wahlprogramm_check.py — ``parteien_mit_wahlprogramm(bl)`` - app/redline_utils.py — Reverse-Lookup über ``all_programme()`` - app/wahlprogramm_fetch.py (3 Stellen) — ``aktuelles_wahlprogramm()`` - tests/test_redline_parser.py — Programm-Lookup statt WAHLPROGRAMME ``wahlprogramme.py`` schrumpft auf den Such-Code: Keyword-Fallback + PDF-Text-Loader + ein dünner ``get_wahlprogramm``-Compat-Adapter zu ``programme.aktuelles_wahlprogramm``. Drei Helper gelöscht (keine App-Konsumenten): ``regierungsbildung_for``, ``regierungsende_for``, ``regierung_aktuell``. Wer das Datum der Regierungsbildung will, fragt ``legislaturen.aktuelle_regierung(bl).get('von')``. Test-Suite: 1217 grün (vorher 1244, Differenz 27 = entfernte regierungs-Helper-Tests + obsolete WAHLPROGRAMME-Strukturtests).
2026-05-09 00:37:35 +02:00
# get_wahlprogramm — Compat-Adapter
Add pytest suite + fix two regex bugs uncovered by it (#46) Erste Tests für die Codebase. 77 Tests, 0.08s Laufzeit, decken die drei Bug-Klassen aus der April-2026-Adapter-Session ab plus haben schon zwei weitere Bugs in Production-Code aufgedeckt. ## Setup - requirements-dev.txt mit pytest + pytest-asyncio - pytest.ini mit asyncio_mode=auto - tests/conftest.py stubbt fitz/bs4/openai/pydantic_settings, damit die Suite ohne den vollen prod-requirements-Satz läuft (pure unit tests, kein PDF-Parsing, kein HTTP) ## Tests - tests/test_parlamente.py (33 Tests) * PortalaAdapter._parse_hit_list_cards: doctype/doctype_full NameError-Regression aus 1cb030a, plus Title/Drucksache/Fraktion- /Datum/PDF-Extraktion gegen ein BE-Card-Fixture * PortalaAdapter._parse_hit_list_dump: gegen ein LSA-Perl-Dump- Fixture inkl. Hex-Escape-Decoding (\x{fc} → ü) * PortalaAdapter._parse_hit_list_html: Auto-Detection zwischen Card- und Dump-Format * PortalaAdapter._normalize_fraktion: kanonische Fraktion-Codes inkl. F.D.P.-mit-Punkten, BÜNDNIS 90, DIE LINKE, BSW * ParLDokAdapter._hit_to_drucksache: JSON-Hit → Drucksache Mapping inkl. /navpanes-Stripping, MdL-mit-Partei-in-Klammern, Landesregierung-Detection * ParLDokAdapter._fulltext_id: bundle.js-mirroring (deferred, aber dokumentiert) * ADAPTERS-Registry-Sanity - tests/test_embeddings.py (11 Tests) * _chunk_source_label: Programm-Name + Seite (Halluzinations- Bug-Regression aus 1b5fd96) * format_quotes_for_prompt: jeder Chunk muss Programm-Name enthalten, strict-citation-Hinweis muss im Output sein, keine NRW-Halluzinationen für MV/BE-Chunk-Sets - tests/test_wahlprogramme.py (14 Tests) * Registry-Struktur (jahr int, seiten int, .pdf-Endung) * File-Existenz: jede registrierte PDF muss in static/referenzen/ liegen — würde Tippfehler in den 22 indexierten Programmen sofort fangen * embeddings.PROGRAMME-Konsistenz-Cross-Check - tests/test_bundeslaender.py (15 Tests) * Sanity über 16-State-Registry * #48-Klassifikations-Regression: TH=ParlDok, HB=StarWeb, SN=Eigensystem * Wahltermine plausibel (zwischen 2026 und 2035) - tests/test_analyzer.py (4 Tests) * Markdown-Codeblock-Stripping aus dem JSON-Retry-Loop ## Bug-Funde während der Test-Schreibphase Zwei Production-Bugs in den _normalize_fraktion-Helfern wurden durch die neuen Tests sofort aufgedeckt und im selben Commit gefixt: 1. PortalaAdapter._normalize_fraktion matched "F.D.P." (mit Punkten, wie historische SH/HB-Drucksachen) nicht — Regex \bFDP\b ist zu strikt. Fix: \bF\.?\s*D\.?\s*P\.?\b analog zu ParLDokAdapter. 2. ParLDokAdapter._normalize_fraktion (auch PortalaAdapter) matched "Ministerium der Finanzen" nicht als Landesregierung, weil \bMINISTER\b die Wortgrenze auch nach MINISTER verlangt — bei MINISTERIUM steht aber IUM danach, keine Wortgrenze. Fix: \bMINISTER ohne abschließendes \b. Beide Bugs hätten Fraktion-Felder bei Drucksachen der Bremischen Bürgerschaft (FDP-Listen) und bei Landesregierungs-Drucksachen in MV/LSA fälschlich leer gelassen — exakt der "fraktionen=[]"- Befund aus dem MV-Smoke-Test in #4. Phase 0 aus Roadmap-Issue #49. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-08 23:26:06 +02:00
# ─────────────────────────────────────────────────────────────────────────────
refactor(programme): WAHLPROGRAMME → programme.PROGRAMME konsolidiert (#222) Schließt #222. Entfernt die Doppelung zwischen ``wahlprogramme.WAHLPROGRAMME`` und ``programme.PROGRAMME``. Single source of truth ist jetzt ``programme.PROGRAMME`` als Literal mit allen 287 Programmen (Wahlprogramme + Bundes- + Landes-Grundsatzprogramme, historisch + aktuell). Schema schmaler — Felder ohne Konsumenten entfallen: - ``regierungsbildung`` / ``regierungsende`` → gehören zu ``legislaturen.REGIERUNGEN``. Verbindung Programm→Regierung läuft jetzt über ``legislaturen.regierung_zum_zeitpunkt(bl, datum)``. - ``partei`` (Langform "CDU NRW") → ableitbar aus partei + bundesland. - ``jahr`` → ableitbar aus ``gueltig_ab[:4]``. - ``beschluss`` / ``wahl`` / ``hinweis`` → keine App-Konsumenten. Felder im neuen Schema: id, typ, partei, bundesland, wp, gueltig_ab, gueltig_bis, name, titel (Slogan, optional), pdf, seiten. Daten-Migration einmalig via ``tools/build_programme_literal.py``: - Basis: bisherige embeddings.PROGRAMME (alle 287 IDs + gueltig_ab/bis) - titel aus WAHLPROGRAMME für die ~80 aktuellen Wahlprogramme + Land-Grundsatzprogramm-Slogans (ehem. _ARCHIVED_SKELETONS) - seiten via ``fitz.open(p).page_count`` für alle 287 PDFs Aufrufer migriert: - app/main.py:4055 — ``aktuelles_wahlprogramm(bl, partei).pdf`` - app/wahlprogramm_check.py — ``parteien_mit_wahlprogramm(bl)`` - app/redline_utils.py — Reverse-Lookup über ``all_programme()`` - app/wahlprogramm_fetch.py (3 Stellen) — ``aktuelles_wahlprogramm()`` - tests/test_redline_parser.py — Programm-Lookup statt WAHLPROGRAMME ``wahlprogramme.py`` schrumpft auf den Such-Code: Keyword-Fallback + PDF-Text-Loader + ein dünner ``get_wahlprogramm``-Compat-Adapter zu ``programme.aktuelles_wahlprogramm``. Drei Helper gelöscht (keine App-Konsumenten): ``regierungsbildung_for``, ``regierungsende_for``, ``regierung_aktuell``. Wer das Datum der Regierungsbildung will, fragt ``legislaturen.aktuelle_regierung(bl).get('von')``. Test-Suite: 1217 grün (vorher 1244, Differenz 27 = entfernte regierungs-Helper-Tests + obsolete WAHLPROGRAMME-Strukturtests).
2026-05-09 00:37:35 +02:00
class TestGetWahlprogramm:
def test_returns_programm_for_known_combination(self):
prog = get_wahlprogramm("NRW", "CDU")
assert prog is not None
assert prog["pdf"] == "cdu-nrw-2022.pdf"
assert prog["partei"] == "CDU"
assert prog["bundesland"] == "NRW"
Add pytest suite + fix two regex bugs uncovered by it (#46) Erste Tests für die Codebase. 77 Tests, 0.08s Laufzeit, decken die drei Bug-Klassen aus der April-2026-Adapter-Session ab plus haben schon zwei weitere Bugs in Production-Code aufgedeckt. ## Setup - requirements-dev.txt mit pytest + pytest-asyncio - pytest.ini mit asyncio_mode=auto - tests/conftest.py stubbt fitz/bs4/openai/pydantic_settings, damit die Suite ohne den vollen prod-requirements-Satz läuft (pure unit tests, kein PDF-Parsing, kein HTTP) ## Tests - tests/test_parlamente.py (33 Tests) * PortalaAdapter._parse_hit_list_cards: doctype/doctype_full NameError-Regression aus 1cb030a, plus Title/Drucksache/Fraktion- /Datum/PDF-Extraktion gegen ein BE-Card-Fixture * PortalaAdapter._parse_hit_list_dump: gegen ein LSA-Perl-Dump- Fixture inkl. Hex-Escape-Decoding (\x{fc} → ü) * PortalaAdapter._parse_hit_list_html: Auto-Detection zwischen Card- und Dump-Format * PortalaAdapter._normalize_fraktion: kanonische Fraktion-Codes inkl. F.D.P.-mit-Punkten, BÜNDNIS 90, DIE LINKE, BSW * ParLDokAdapter._hit_to_drucksache: JSON-Hit → Drucksache Mapping inkl. /navpanes-Stripping, MdL-mit-Partei-in-Klammern, Landesregierung-Detection * ParLDokAdapter._fulltext_id: bundle.js-mirroring (deferred, aber dokumentiert) * ADAPTERS-Registry-Sanity - tests/test_embeddings.py (11 Tests) * _chunk_source_label: Programm-Name + Seite (Halluzinations- Bug-Regression aus 1b5fd96) * format_quotes_for_prompt: jeder Chunk muss Programm-Name enthalten, strict-citation-Hinweis muss im Output sein, keine NRW-Halluzinationen für MV/BE-Chunk-Sets - tests/test_wahlprogramme.py (14 Tests) * Registry-Struktur (jahr int, seiten int, .pdf-Endung) * File-Existenz: jede registrierte PDF muss in static/referenzen/ liegen — würde Tippfehler in den 22 indexierten Programmen sofort fangen * embeddings.PROGRAMME-Konsistenz-Cross-Check - tests/test_bundeslaender.py (15 Tests) * Sanity über 16-State-Registry * #48-Klassifikations-Regression: TH=ParlDok, HB=StarWeb, SN=Eigensystem * Wahltermine plausibel (zwischen 2026 und 2035) - tests/test_analyzer.py (4 Tests) * Markdown-Codeblock-Stripping aus dem JSON-Retry-Loop ## Bug-Funde während der Test-Schreibphase Zwei Production-Bugs in den _normalize_fraktion-Helfern wurden durch die neuen Tests sofort aufgedeckt und im selben Commit gefixt: 1. PortalaAdapter._normalize_fraktion matched "F.D.P." (mit Punkten, wie historische SH/HB-Drucksachen) nicht — Regex \bFDP\b ist zu strikt. Fix: \bF\.?\s*D\.?\s*P\.?\b analog zu ParLDokAdapter. 2. ParLDokAdapter._normalize_fraktion (auch PortalaAdapter) matched "Ministerium der Finanzen" nicht als Landesregierung, weil \bMINISTER\b die Wortgrenze auch nach MINISTER verlangt — bei MINISTERIUM steht aber IUM danach, keine Wortgrenze. Fix: \bMINISTER ohne abschließendes \b. Beide Bugs hätten Fraktion-Felder bei Drucksachen der Bremischen Bürgerschaft (FDP-Listen) und bei Landesregierungs-Drucksachen in MV/LSA fälschlich leer gelassen — exakt der "fraktionen=[]"- Befund aus dem MV-Smoke-Test in #4. Phase 0 aus Roadmap-Issue #49. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-08 23:26:06 +02:00
refactor(programme): WAHLPROGRAMME → programme.PROGRAMME konsolidiert (#222) Schließt #222. Entfernt die Doppelung zwischen ``wahlprogramme.WAHLPROGRAMME`` und ``programme.PROGRAMME``. Single source of truth ist jetzt ``programme.PROGRAMME`` als Literal mit allen 287 Programmen (Wahlprogramme + Bundes- + Landes-Grundsatzprogramme, historisch + aktuell). Schema schmaler — Felder ohne Konsumenten entfallen: - ``regierungsbildung`` / ``regierungsende`` → gehören zu ``legislaturen.REGIERUNGEN``. Verbindung Programm→Regierung läuft jetzt über ``legislaturen.regierung_zum_zeitpunkt(bl, datum)``. - ``partei`` (Langform "CDU NRW") → ableitbar aus partei + bundesland. - ``jahr`` → ableitbar aus ``gueltig_ab[:4]``. - ``beschluss`` / ``wahl`` / ``hinweis`` → keine App-Konsumenten. Felder im neuen Schema: id, typ, partei, bundesland, wp, gueltig_ab, gueltig_bis, name, titel (Slogan, optional), pdf, seiten. Daten-Migration einmalig via ``tools/build_programme_literal.py``: - Basis: bisherige embeddings.PROGRAMME (alle 287 IDs + gueltig_ab/bis) - titel aus WAHLPROGRAMME für die ~80 aktuellen Wahlprogramme + Land-Grundsatzprogramm-Slogans (ehem. _ARCHIVED_SKELETONS) - seiten via ``fitz.open(p).page_count`` für alle 287 PDFs Aufrufer migriert: - app/main.py:4055 — ``aktuelles_wahlprogramm(bl, partei).pdf`` - app/wahlprogramm_check.py — ``parteien_mit_wahlprogramm(bl)`` - app/redline_utils.py — Reverse-Lookup über ``all_programme()`` - app/wahlprogramm_fetch.py (3 Stellen) — ``aktuelles_wahlprogramm()`` - tests/test_redline_parser.py — Programm-Lookup statt WAHLPROGRAMME ``wahlprogramme.py`` schrumpft auf den Such-Code: Keyword-Fallback + PDF-Text-Loader + ein dünner ``get_wahlprogramm``-Compat-Adapter zu ``programme.aktuelles_wahlprogramm``. Drei Helper gelöscht (keine App-Konsumenten): ``regierungsbildung_for``, ``regierungsende_for``, ``regierung_aktuell``. Wer das Datum der Regierungsbildung will, fragt ``legislaturen.aktuelle_regierung(bl).get('von')``. Test-Suite: 1217 grün (vorher 1244, Differenz 27 = entfernte regierungs-Helper-Tests + obsolete WAHLPROGRAMME-Strukturtests).
2026-05-09 00:37:35 +02:00
def test_returns_none_for_unknown_bundesland(self):
assert get_wahlprogramm("XX", "CDU") is None
def test_returns_none_for_unknown_partei(self):
assert get_wahlprogramm("NRW", "BSW") is None # BSW nicht im NRW-Landtag
# ─────────────────────────────────────────────────────────────────────────────
refactor(programme): WAHLPROGRAMME → programme.PROGRAMME konsolidiert (#222) Schließt #222. Entfernt die Doppelung zwischen ``wahlprogramme.WAHLPROGRAMME`` und ``programme.PROGRAMME``. Single source of truth ist jetzt ``programme.PROGRAMME`` als Literal mit allen 287 Programmen (Wahlprogramme + Bundes- + Landes-Grundsatzprogramme, historisch + aktuell). Schema schmaler — Felder ohne Konsumenten entfallen: - ``regierungsbildung`` / ``regierungsende`` → gehören zu ``legislaturen.REGIERUNGEN``. Verbindung Programm→Regierung läuft jetzt über ``legislaturen.regierung_zum_zeitpunkt(bl, datum)``. - ``partei`` (Langform "CDU NRW") → ableitbar aus partei + bundesland. - ``jahr`` → ableitbar aus ``gueltig_ab[:4]``. - ``beschluss`` / ``wahl`` / ``hinweis`` → keine App-Konsumenten. Felder im neuen Schema: id, typ, partei, bundesland, wp, gueltig_ab, gueltig_bis, name, titel (Slogan, optional), pdf, seiten. Daten-Migration einmalig via ``tools/build_programme_literal.py``: - Basis: bisherige embeddings.PROGRAMME (alle 287 IDs + gueltig_ab/bis) - titel aus WAHLPROGRAMME für die ~80 aktuellen Wahlprogramme + Land-Grundsatzprogramm-Slogans (ehem. _ARCHIVED_SKELETONS) - seiten via ``fitz.open(p).page_count`` für alle 287 PDFs Aufrufer migriert: - app/main.py:4055 — ``aktuelles_wahlprogramm(bl, partei).pdf`` - app/wahlprogramm_check.py — ``parteien_mit_wahlprogramm(bl)`` - app/redline_utils.py — Reverse-Lookup über ``all_programme()`` - app/wahlprogramm_fetch.py (3 Stellen) — ``aktuelles_wahlprogramm()`` - tests/test_redline_parser.py — Programm-Lookup statt WAHLPROGRAMME ``wahlprogramme.py`` schrumpft auf den Such-Code: Keyword-Fallback + PDF-Text-Loader + ein dünner ``get_wahlprogramm``-Compat-Adapter zu ``programme.aktuelles_wahlprogramm``. Drei Helper gelöscht (keine App-Konsumenten): ``regierungsbildung_for``, ``regierungsende_for``, ``regierung_aktuell``. Wer das Datum der Regierungsbildung will, fragt ``legislaturen.aktuelle_regierung(bl).get('von')``. Test-Suite: 1217 grün (vorher 1244, Differenz 27 = entfernte regierungs-Helper-Tests + obsolete WAHLPROGRAMME-Strukturtests).
2026-05-09 00:37:35 +02:00
# File existence — every registered pdf must exist on disk
# ─────────────────────────────────────────────────────────────────────────────
refactor(programme): WAHLPROGRAMME → programme.PROGRAMME konsolidiert (#222) Schließt #222. Entfernt die Doppelung zwischen ``wahlprogramme.WAHLPROGRAMME`` und ``programme.PROGRAMME``. Single source of truth ist jetzt ``programme.PROGRAMME`` als Literal mit allen 287 Programmen (Wahlprogramme + Bundes- + Landes-Grundsatzprogramme, historisch + aktuell). Schema schmaler — Felder ohne Konsumenten entfallen: - ``regierungsbildung`` / ``regierungsende`` → gehören zu ``legislaturen.REGIERUNGEN``. Verbindung Programm→Regierung läuft jetzt über ``legislaturen.regierung_zum_zeitpunkt(bl, datum)``. - ``partei`` (Langform "CDU NRW") → ableitbar aus partei + bundesland. - ``jahr`` → ableitbar aus ``gueltig_ab[:4]``. - ``beschluss`` / ``wahl`` / ``hinweis`` → keine App-Konsumenten. Felder im neuen Schema: id, typ, partei, bundesland, wp, gueltig_ab, gueltig_bis, name, titel (Slogan, optional), pdf, seiten. Daten-Migration einmalig via ``tools/build_programme_literal.py``: - Basis: bisherige embeddings.PROGRAMME (alle 287 IDs + gueltig_ab/bis) - titel aus WAHLPROGRAMME für die ~80 aktuellen Wahlprogramme + Land-Grundsatzprogramm-Slogans (ehem. _ARCHIVED_SKELETONS) - seiten via ``fitz.open(p).page_count`` für alle 287 PDFs Aufrufer migriert: - app/main.py:4055 — ``aktuelles_wahlprogramm(bl, partei).pdf`` - app/wahlprogramm_check.py — ``parteien_mit_wahlprogramm(bl)`` - app/redline_utils.py — Reverse-Lookup über ``all_programme()`` - app/wahlprogramm_fetch.py (3 Stellen) — ``aktuelles_wahlprogramm()`` - tests/test_redline_parser.py — Programm-Lookup statt WAHLPROGRAMME ``wahlprogramme.py`` schrumpft auf den Such-Code: Keyword-Fallback + PDF-Text-Loader + ein dünner ``get_wahlprogramm``-Compat-Adapter zu ``programme.aktuelles_wahlprogramm``. Drei Helper gelöscht (keine App-Konsumenten): ``regierungsbildung_for``, ``regierungsende_for``, ``regierung_aktuell``. Wer das Datum der Regierungsbildung will, fragt ``legislaturen.aktuelle_regierung(bl).get('von')``. Test-Suite: 1217 grün (vorher 1244, Differenz 27 = entfernte regierungs-Helper-Tests + obsolete WAHLPROGRAMME-Strukturtests).
2026-05-09 00:37:35 +02:00
class TestFileExistence:
"""Catches typos im pdf-Feld der Programm-Registry, die das
Indexing oder PDF-Download silently brechen würden."""
def test_every_registered_pdf_exists(self):
from app.programme import all_programme
refactor(programme): WAHLPROGRAMME → programme.PROGRAMME konsolidiert (#222) Schließt #222. Entfernt die Doppelung zwischen ``wahlprogramme.WAHLPROGRAMME`` und ``programme.PROGRAMME``. Single source of truth ist jetzt ``programme.PROGRAMME`` als Literal mit allen 287 Programmen (Wahlprogramme + Bundes- + Landes-Grundsatzprogramme, historisch + aktuell). Schema schmaler — Felder ohne Konsumenten entfallen: - ``regierungsbildung`` / ``regierungsende`` → gehören zu ``legislaturen.REGIERUNGEN``. Verbindung Programm→Regierung läuft jetzt über ``legislaturen.regierung_zum_zeitpunkt(bl, datum)``. - ``partei`` (Langform "CDU NRW") → ableitbar aus partei + bundesland. - ``jahr`` → ableitbar aus ``gueltig_ab[:4]``. - ``beschluss`` / ``wahl`` / ``hinweis`` → keine App-Konsumenten. Felder im neuen Schema: id, typ, partei, bundesland, wp, gueltig_ab, gueltig_bis, name, titel (Slogan, optional), pdf, seiten. Daten-Migration einmalig via ``tools/build_programme_literal.py``: - Basis: bisherige embeddings.PROGRAMME (alle 287 IDs + gueltig_ab/bis) - titel aus WAHLPROGRAMME für die ~80 aktuellen Wahlprogramme + Land-Grundsatzprogramm-Slogans (ehem. _ARCHIVED_SKELETONS) - seiten via ``fitz.open(p).page_count`` für alle 287 PDFs Aufrufer migriert: - app/main.py:4055 — ``aktuelles_wahlprogramm(bl, partei).pdf`` - app/wahlprogramm_check.py — ``parteien_mit_wahlprogramm(bl)`` - app/redline_utils.py — Reverse-Lookup über ``all_programme()`` - app/wahlprogramm_fetch.py (3 Stellen) — ``aktuelles_wahlprogramm()`` - tests/test_redline_parser.py — Programm-Lookup statt WAHLPROGRAMME ``wahlprogramme.py`` schrumpft auf den Such-Code: Keyword-Fallback + PDF-Text-Loader + ein dünner ``get_wahlprogramm``-Compat-Adapter zu ``programme.aktuelles_wahlprogramm``. Drei Helper gelöscht (keine App-Konsumenten): ``regierungsbildung_for``, ``regierungsende_for``, ``regierung_aktuell``. Wer das Datum der Regierungsbildung will, fragt ``legislaturen.aktuelle_regierung(bl).get('von')``. Test-Suite: 1217 grün (vorher 1244, Differenz 27 = entfernte regierungs-Helper-Tests + obsolete WAHLPROGRAMME-Strukturtests).
2026-05-09 00:37:35 +02:00
missing = []
for prog in all_programme():
refactor(programme): WAHLPROGRAMME → programme.PROGRAMME konsolidiert (#222) Schließt #222. Entfernt die Doppelung zwischen ``wahlprogramme.WAHLPROGRAMME`` und ``programme.PROGRAMME``. Single source of truth ist jetzt ``programme.PROGRAMME`` als Literal mit allen 287 Programmen (Wahlprogramme + Bundes- + Landes-Grundsatzprogramme, historisch + aktuell). Schema schmaler — Felder ohne Konsumenten entfallen: - ``regierungsbildung`` / ``regierungsende`` → gehören zu ``legislaturen.REGIERUNGEN``. Verbindung Programm→Regierung läuft jetzt über ``legislaturen.regierung_zum_zeitpunkt(bl, datum)``. - ``partei`` (Langform "CDU NRW") → ableitbar aus partei + bundesland. - ``jahr`` → ableitbar aus ``gueltig_ab[:4]``. - ``beschluss`` / ``wahl`` / ``hinweis`` → keine App-Konsumenten. Felder im neuen Schema: id, typ, partei, bundesland, wp, gueltig_ab, gueltig_bis, name, titel (Slogan, optional), pdf, seiten. Daten-Migration einmalig via ``tools/build_programme_literal.py``: - Basis: bisherige embeddings.PROGRAMME (alle 287 IDs + gueltig_ab/bis) - titel aus WAHLPROGRAMME für die ~80 aktuellen Wahlprogramme + Land-Grundsatzprogramm-Slogans (ehem. _ARCHIVED_SKELETONS) - seiten via ``fitz.open(p).page_count`` für alle 287 PDFs Aufrufer migriert: - app/main.py:4055 — ``aktuelles_wahlprogramm(bl, partei).pdf`` - app/wahlprogramm_check.py — ``parteien_mit_wahlprogramm(bl)`` - app/redline_utils.py — Reverse-Lookup über ``all_programme()`` - app/wahlprogramm_fetch.py (3 Stellen) — ``aktuelles_wahlprogramm()`` - tests/test_redline_parser.py — Programm-Lookup statt WAHLPROGRAMME ``wahlprogramme.py`` schrumpft auf den Such-Code: Keyword-Fallback + PDF-Text-Loader + ein dünner ``get_wahlprogramm``-Compat-Adapter zu ``programme.aktuelles_wahlprogramm``. Drei Helper gelöscht (keine App-Konsumenten): ``regierungsbildung_for``, ``regierungsende_for``, ``regierung_aktuell``. Wer das Datum der Regierungsbildung will, fragt ``legislaturen.aktuelle_regierung(bl).get('von')``. Test-Suite: 1217 grün (vorher 1244, Differenz 27 = entfernte regierungs-Helper-Tests + obsolete WAHLPROGRAMME-Strukturtests).
2026-05-09 00:37:35 +02:00
pdf = prog.get("pdf")
if not pdf:
continue
refactor(programme): WAHLPROGRAMME → programme.PROGRAMME konsolidiert (#222) Schließt #222. Entfernt die Doppelung zwischen ``wahlprogramme.WAHLPROGRAMME`` und ``programme.PROGRAMME``. Single source of truth ist jetzt ``programme.PROGRAMME`` als Literal mit allen 287 Programmen (Wahlprogramme + Bundes- + Landes-Grundsatzprogramme, historisch + aktuell). Schema schmaler — Felder ohne Konsumenten entfallen: - ``regierungsbildung`` / ``regierungsende`` → gehören zu ``legislaturen.REGIERUNGEN``. Verbindung Programm→Regierung läuft jetzt über ``legislaturen.regierung_zum_zeitpunkt(bl, datum)``. - ``partei`` (Langform "CDU NRW") → ableitbar aus partei + bundesland. - ``jahr`` → ableitbar aus ``gueltig_ab[:4]``. - ``beschluss`` / ``wahl`` / ``hinweis`` → keine App-Konsumenten. Felder im neuen Schema: id, typ, partei, bundesland, wp, gueltig_ab, gueltig_bis, name, titel (Slogan, optional), pdf, seiten. Daten-Migration einmalig via ``tools/build_programme_literal.py``: - Basis: bisherige embeddings.PROGRAMME (alle 287 IDs + gueltig_ab/bis) - titel aus WAHLPROGRAMME für die ~80 aktuellen Wahlprogramme + Land-Grundsatzprogramm-Slogans (ehem. _ARCHIVED_SKELETONS) - seiten via ``fitz.open(p).page_count`` für alle 287 PDFs Aufrufer migriert: - app/main.py:4055 — ``aktuelles_wahlprogramm(bl, partei).pdf`` - app/wahlprogramm_check.py — ``parteien_mit_wahlprogramm(bl)`` - app/redline_utils.py — Reverse-Lookup über ``all_programme()`` - app/wahlprogramm_fetch.py (3 Stellen) — ``aktuelles_wahlprogramm()`` - tests/test_redline_parser.py — Programm-Lookup statt WAHLPROGRAMME ``wahlprogramme.py`` schrumpft auf den Such-Code: Keyword-Fallback + PDF-Text-Loader + ein dünner ``get_wahlprogramm``-Compat-Adapter zu ``programme.aktuelles_wahlprogramm``. Drei Helper gelöscht (keine App-Konsumenten): ``regierungsbildung_for``, ``regierungsende_for``, ``regierung_aktuell``. Wer das Datum der Regierungsbildung will, fragt ``legislaturen.aktuelle_regierung(bl).get('von')``. Test-Suite: 1217 grün (vorher 1244, Differenz 27 = entfernte regierungs-Helper-Tests + obsolete WAHLPROGRAMME-Strukturtests).
2026-05-09 00:37:35 +02:00
path = REFERENZEN_PATH / pdf
if not path.exists():
missing.append(f"{prog['id']}: {pdf}")
assert not missing, "missing PDFs:\n " + "\n ".join(missing)
# ─────────────────────────────────────────────────────────────────────────────
# load_wahlprogramm_text — Fallback-Pfade (#134 Coverage-Backfill)
# ─────────────────────────────────────────────────────────────────────────────
refactor(programme): WAHLPROGRAMME → programme.PROGRAMME konsolidiert (#222) Schließt #222. Entfernt die Doppelung zwischen ``wahlprogramme.WAHLPROGRAMME`` und ``programme.PROGRAMME``. Single source of truth ist jetzt ``programme.PROGRAMME`` als Literal mit allen 287 Programmen (Wahlprogramme + Bundes- + Landes-Grundsatzprogramme, historisch + aktuell). Schema schmaler — Felder ohne Konsumenten entfallen: - ``regierungsbildung`` / ``regierungsende`` → gehören zu ``legislaturen.REGIERUNGEN``. Verbindung Programm→Regierung läuft jetzt über ``legislaturen.regierung_zum_zeitpunkt(bl, datum)``. - ``partei`` (Langform "CDU NRW") → ableitbar aus partei + bundesland. - ``jahr`` → ableitbar aus ``gueltig_ab[:4]``. - ``beschluss`` / ``wahl`` / ``hinweis`` → keine App-Konsumenten. Felder im neuen Schema: id, typ, partei, bundesland, wp, gueltig_ab, gueltig_bis, name, titel (Slogan, optional), pdf, seiten. Daten-Migration einmalig via ``tools/build_programme_literal.py``: - Basis: bisherige embeddings.PROGRAMME (alle 287 IDs + gueltig_ab/bis) - titel aus WAHLPROGRAMME für die ~80 aktuellen Wahlprogramme + Land-Grundsatzprogramm-Slogans (ehem. _ARCHIVED_SKELETONS) - seiten via ``fitz.open(p).page_count`` für alle 287 PDFs Aufrufer migriert: - app/main.py:4055 — ``aktuelles_wahlprogramm(bl, partei).pdf`` - app/wahlprogramm_check.py — ``parteien_mit_wahlprogramm(bl)`` - app/redline_utils.py — Reverse-Lookup über ``all_programme()`` - app/wahlprogramm_fetch.py (3 Stellen) — ``aktuelles_wahlprogramm()`` - tests/test_redline_parser.py — Programm-Lookup statt WAHLPROGRAMME ``wahlprogramme.py`` schrumpft auf den Such-Code: Keyword-Fallback + PDF-Text-Loader + ein dünner ``get_wahlprogramm``-Compat-Adapter zu ``programme.aktuelles_wahlprogramm``. Drei Helper gelöscht (keine App-Konsumenten): ``regierungsbildung_for``, ``regierungsende_for``, ``regierung_aktuell``. Wer das Datum der Regierungsbildung will, fragt ``legislaturen.aktuelle_regierung(bl).get('von')``. Test-Suite: 1217 grün (vorher 1244, Differenz 27 = entfernte regierungs-Helper-Tests + obsolete WAHLPROGRAMME-Strukturtests).
2026-05-09 00:37:35 +02:00
class TestLoadWahlprogrammText:
def test_returns_empty_for_unknown_combination(self):
assert load_wahlprogramm_text("XX", "XYZ") == {}
def test_paged_textfile_used_when_present(self, tmp_path, monkeypatch):
"""Wenn die paged-Textdatei existiert, wird sie genutzt.
Format: '--- PAGE N ---'-Marker pro Seitenanfang."""
from app import wahlprogramme as wp_mod
# Mock get_wahlprogramm -> bekannte Datei
monkeypatch.setattr(wp_mod, "get_wahlprogramm",
refactor(programme): WAHLPROGRAMME → programme.PROGRAMME konsolidiert (#222) Schließt #222. Entfernt die Doppelung zwischen ``wahlprogramme.WAHLPROGRAMME`` und ``programme.PROGRAMME``. Single source of truth ist jetzt ``programme.PROGRAMME`` als Literal mit allen 287 Programmen (Wahlprogramme + Bundes- + Landes-Grundsatzprogramme, historisch + aktuell). Schema schmaler — Felder ohne Konsumenten entfallen: - ``regierungsbildung`` / ``regierungsende`` → gehören zu ``legislaturen.REGIERUNGEN``. Verbindung Programm→Regierung läuft jetzt über ``legislaturen.regierung_zum_zeitpunkt(bl, datum)``. - ``partei`` (Langform "CDU NRW") → ableitbar aus partei + bundesland. - ``jahr`` → ableitbar aus ``gueltig_ab[:4]``. - ``beschluss`` / ``wahl`` / ``hinweis`` → keine App-Konsumenten. Felder im neuen Schema: id, typ, partei, bundesland, wp, gueltig_ab, gueltig_bis, name, titel (Slogan, optional), pdf, seiten. Daten-Migration einmalig via ``tools/build_programme_literal.py``: - Basis: bisherige embeddings.PROGRAMME (alle 287 IDs + gueltig_ab/bis) - titel aus WAHLPROGRAMME für die ~80 aktuellen Wahlprogramme + Land-Grundsatzprogramm-Slogans (ehem. _ARCHIVED_SKELETONS) - seiten via ``fitz.open(p).page_count`` für alle 287 PDFs Aufrufer migriert: - app/main.py:4055 — ``aktuelles_wahlprogramm(bl, partei).pdf`` - app/wahlprogramm_check.py — ``parteien_mit_wahlprogramm(bl)`` - app/redline_utils.py — Reverse-Lookup über ``all_programme()`` - app/wahlprogramm_fetch.py (3 Stellen) — ``aktuelles_wahlprogramm()`` - tests/test_redline_parser.py — Programm-Lookup statt WAHLPROGRAMME ``wahlprogramme.py`` schrumpft auf den Such-Code: Keyword-Fallback + PDF-Text-Loader + ein dünner ``get_wahlprogramm``-Compat-Adapter zu ``programme.aktuelles_wahlprogramm``. Drei Helper gelöscht (keine App-Konsumenten): ``regierungsbildung_for``, ``regierungsende_for``, ``regierung_aktuell``. Wer das Datum der Regierungsbildung will, fragt ``legislaturen.aktuelle_regierung(bl).get('von')``. Test-Suite: 1217 grün (vorher 1244, Differenz 27 = entfernte regierungs-Helper-Tests + obsolete WAHLPROGRAMME-Strukturtests).
2026-05-09 00:37:35 +02:00
lambda bl, p: {"pdf": "test.pdf"})
paged = tmp_path / "test-paged.txt"
paged.write_text("--- PAGE 1 ---\nseite eins\n--- PAGE 2 ---\nseite zwei")
monkeypatch.setattr(wp_mod, "KONTEXT_PATH", tmp_path)
result = wp_mod.load_wahlprogramm_text("X", "Y")
assert 2 in result
assert "seite zwei" in result[2]
def test_falls_back_to_normal_textfile(self, tmp_path, monkeypatch):
refactor(programme): WAHLPROGRAMME → programme.PROGRAMME konsolidiert (#222) Schließt #222. Entfernt die Doppelung zwischen ``wahlprogramme.WAHLPROGRAMME`` und ``programme.PROGRAMME``. Single source of truth ist jetzt ``programme.PROGRAMME`` als Literal mit allen 287 Programmen (Wahlprogramme + Bundes- + Landes-Grundsatzprogramme, historisch + aktuell). Schema schmaler — Felder ohne Konsumenten entfallen: - ``regierungsbildung`` / ``regierungsende`` → gehören zu ``legislaturen.REGIERUNGEN``. Verbindung Programm→Regierung läuft jetzt über ``legislaturen.regierung_zum_zeitpunkt(bl, datum)``. - ``partei`` (Langform "CDU NRW") → ableitbar aus partei + bundesland. - ``jahr`` → ableitbar aus ``gueltig_ab[:4]``. - ``beschluss`` / ``wahl`` / ``hinweis`` → keine App-Konsumenten. Felder im neuen Schema: id, typ, partei, bundesland, wp, gueltig_ab, gueltig_bis, name, titel (Slogan, optional), pdf, seiten. Daten-Migration einmalig via ``tools/build_programme_literal.py``: - Basis: bisherige embeddings.PROGRAMME (alle 287 IDs + gueltig_ab/bis) - titel aus WAHLPROGRAMME für die ~80 aktuellen Wahlprogramme + Land-Grundsatzprogramm-Slogans (ehem. _ARCHIVED_SKELETONS) - seiten via ``fitz.open(p).page_count`` für alle 287 PDFs Aufrufer migriert: - app/main.py:4055 — ``aktuelles_wahlprogramm(bl, partei).pdf`` - app/wahlprogramm_check.py — ``parteien_mit_wahlprogramm(bl)`` - app/redline_utils.py — Reverse-Lookup über ``all_programme()`` - app/wahlprogramm_fetch.py (3 Stellen) — ``aktuelles_wahlprogramm()`` - tests/test_redline_parser.py — Programm-Lookup statt WAHLPROGRAMME ``wahlprogramme.py`` schrumpft auf den Such-Code: Keyword-Fallback + PDF-Text-Loader + ein dünner ``get_wahlprogramm``-Compat-Adapter zu ``programme.aktuelles_wahlprogramm``. Drei Helper gelöscht (keine App-Konsumenten): ``regierungsbildung_for``, ``regierungsende_for``, ``regierung_aktuell``. Wer das Datum der Regierungsbildung will, fragt ``legislaturen.aktuelle_regierung(bl).get('von')``. Test-Suite: 1217 grün (vorher 1244, Differenz 27 = entfernte regierungs-Helper-Tests + obsolete WAHLPROGRAMME-Strukturtests).
2026-05-09 00:37:35 +02:00
"""Ohne paged-Datei wird auf normale .txt-Datei zurückgefallen,
komplett unter Seite 1."""
from app import wahlprogramme as wp_mod
monkeypatch.setattr(wp_mod, "get_wahlprogramm",
refactor(programme): WAHLPROGRAMME → programme.PROGRAMME konsolidiert (#222) Schließt #222. Entfernt die Doppelung zwischen ``wahlprogramme.WAHLPROGRAMME`` und ``programme.PROGRAMME``. Single source of truth ist jetzt ``programme.PROGRAMME`` als Literal mit allen 287 Programmen (Wahlprogramme + Bundes- + Landes-Grundsatzprogramme, historisch + aktuell). Schema schmaler — Felder ohne Konsumenten entfallen: - ``regierungsbildung`` / ``regierungsende`` → gehören zu ``legislaturen.REGIERUNGEN``. Verbindung Programm→Regierung läuft jetzt über ``legislaturen.regierung_zum_zeitpunkt(bl, datum)``. - ``partei`` (Langform "CDU NRW") → ableitbar aus partei + bundesland. - ``jahr`` → ableitbar aus ``gueltig_ab[:4]``. - ``beschluss`` / ``wahl`` / ``hinweis`` → keine App-Konsumenten. Felder im neuen Schema: id, typ, partei, bundesland, wp, gueltig_ab, gueltig_bis, name, titel (Slogan, optional), pdf, seiten. Daten-Migration einmalig via ``tools/build_programme_literal.py``: - Basis: bisherige embeddings.PROGRAMME (alle 287 IDs + gueltig_ab/bis) - titel aus WAHLPROGRAMME für die ~80 aktuellen Wahlprogramme + Land-Grundsatzprogramm-Slogans (ehem. _ARCHIVED_SKELETONS) - seiten via ``fitz.open(p).page_count`` für alle 287 PDFs Aufrufer migriert: - app/main.py:4055 — ``aktuelles_wahlprogramm(bl, partei).pdf`` - app/wahlprogramm_check.py — ``parteien_mit_wahlprogramm(bl)`` - app/redline_utils.py — Reverse-Lookup über ``all_programme()`` - app/wahlprogramm_fetch.py (3 Stellen) — ``aktuelles_wahlprogramm()`` - tests/test_redline_parser.py — Programm-Lookup statt WAHLPROGRAMME ``wahlprogramme.py`` schrumpft auf den Such-Code: Keyword-Fallback + PDF-Text-Loader + ein dünner ``get_wahlprogramm``-Compat-Adapter zu ``programme.aktuelles_wahlprogramm``. Drei Helper gelöscht (keine App-Konsumenten): ``regierungsbildung_for``, ``regierungsende_for``, ``regierung_aktuell``. Wer das Datum der Regierungsbildung will, fragt ``legislaturen.aktuelle_regierung(bl).get('von')``. Test-Suite: 1217 grün (vorher 1244, Differenz 27 = entfernte regierungs-Helper-Tests + obsolete WAHLPROGRAMME-Strukturtests).
2026-05-09 00:37:35 +02:00
lambda bl, p: {"pdf": "test.pdf"})
normal = tmp_path / "test.txt"
normal.write_text("flacher text ohne seitenmarker")
monkeypatch.setattr(wp_mod, "KONTEXT_PATH", tmp_path)
result = wp_mod.load_wahlprogramm_text("X", "Y")
assert result == {1: "flacher text ohne seitenmarker"}
def test_returns_empty_when_no_textfile(self, tmp_path, monkeypatch):
refactor(programme): WAHLPROGRAMME → programme.PROGRAMME konsolidiert (#222) Schließt #222. Entfernt die Doppelung zwischen ``wahlprogramme.WAHLPROGRAMME`` und ``programme.PROGRAMME``. Single source of truth ist jetzt ``programme.PROGRAMME`` als Literal mit allen 287 Programmen (Wahlprogramme + Bundes- + Landes-Grundsatzprogramme, historisch + aktuell). Schema schmaler — Felder ohne Konsumenten entfallen: - ``regierungsbildung`` / ``regierungsende`` → gehören zu ``legislaturen.REGIERUNGEN``. Verbindung Programm→Regierung läuft jetzt über ``legislaturen.regierung_zum_zeitpunkt(bl, datum)``. - ``partei`` (Langform "CDU NRW") → ableitbar aus partei + bundesland. - ``jahr`` → ableitbar aus ``gueltig_ab[:4]``. - ``beschluss`` / ``wahl`` / ``hinweis`` → keine App-Konsumenten. Felder im neuen Schema: id, typ, partei, bundesland, wp, gueltig_ab, gueltig_bis, name, titel (Slogan, optional), pdf, seiten. Daten-Migration einmalig via ``tools/build_programme_literal.py``: - Basis: bisherige embeddings.PROGRAMME (alle 287 IDs + gueltig_ab/bis) - titel aus WAHLPROGRAMME für die ~80 aktuellen Wahlprogramme + Land-Grundsatzprogramm-Slogans (ehem. _ARCHIVED_SKELETONS) - seiten via ``fitz.open(p).page_count`` für alle 287 PDFs Aufrufer migriert: - app/main.py:4055 — ``aktuelles_wahlprogramm(bl, partei).pdf`` - app/wahlprogramm_check.py — ``parteien_mit_wahlprogramm(bl)`` - app/redline_utils.py — Reverse-Lookup über ``all_programme()`` - app/wahlprogramm_fetch.py (3 Stellen) — ``aktuelles_wahlprogramm()`` - tests/test_redline_parser.py — Programm-Lookup statt WAHLPROGRAMME ``wahlprogramme.py`` schrumpft auf den Such-Code: Keyword-Fallback + PDF-Text-Loader + ein dünner ``get_wahlprogramm``-Compat-Adapter zu ``programme.aktuelles_wahlprogramm``. Drei Helper gelöscht (keine App-Konsumenten): ``regierungsbildung_for``, ``regierungsende_for``, ``regierung_aktuell``. Wer das Datum der Regierungsbildung will, fragt ``legislaturen.aktuelle_regierung(bl).get('von')``. Test-Suite: 1217 grün (vorher 1244, Differenz 27 = entfernte regierungs-Helper-Tests + obsolete WAHLPROGRAMME-Strukturtests).
2026-05-09 00:37:35 +02:00
"""Weder paged- noch flat-Textdatei vorhanden → leeres Dict."""
from app import wahlprogramme as wp_mod
monkeypatch.setattr(wp_mod, "get_wahlprogramm",
refactor(programme): WAHLPROGRAMME → programme.PROGRAMME konsolidiert (#222) Schließt #222. Entfernt die Doppelung zwischen ``wahlprogramme.WAHLPROGRAMME`` und ``programme.PROGRAMME``. Single source of truth ist jetzt ``programme.PROGRAMME`` als Literal mit allen 287 Programmen (Wahlprogramme + Bundes- + Landes-Grundsatzprogramme, historisch + aktuell). Schema schmaler — Felder ohne Konsumenten entfallen: - ``regierungsbildung`` / ``regierungsende`` → gehören zu ``legislaturen.REGIERUNGEN``. Verbindung Programm→Regierung läuft jetzt über ``legislaturen.regierung_zum_zeitpunkt(bl, datum)``. - ``partei`` (Langform "CDU NRW") → ableitbar aus partei + bundesland. - ``jahr`` → ableitbar aus ``gueltig_ab[:4]``. - ``beschluss`` / ``wahl`` / ``hinweis`` → keine App-Konsumenten. Felder im neuen Schema: id, typ, partei, bundesland, wp, gueltig_ab, gueltig_bis, name, titel (Slogan, optional), pdf, seiten. Daten-Migration einmalig via ``tools/build_programme_literal.py``: - Basis: bisherige embeddings.PROGRAMME (alle 287 IDs + gueltig_ab/bis) - titel aus WAHLPROGRAMME für die ~80 aktuellen Wahlprogramme + Land-Grundsatzprogramm-Slogans (ehem. _ARCHIVED_SKELETONS) - seiten via ``fitz.open(p).page_count`` für alle 287 PDFs Aufrufer migriert: - app/main.py:4055 — ``aktuelles_wahlprogramm(bl, partei).pdf`` - app/wahlprogramm_check.py — ``parteien_mit_wahlprogramm(bl)`` - app/redline_utils.py — Reverse-Lookup über ``all_programme()`` - app/wahlprogramm_fetch.py (3 Stellen) — ``aktuelles_wahlprogramm()`` - tests/test_redline_parser.py — Programm-Lookup statt WAHLPROGRAMME ``wahlprogramme.py`` schrumpft auf den Such-Code: Keyword-Fallback + PDF-Text-Loader + ein dünner ``get_wahlprogramm``-Compat-Adapter zu ``programme.aktuelles_wahlprogramm``. Drei Helper gelöscht (keine App-Konsumenten): ``regierungsbildung_for``, ``regierungsende_for``, ``regierung_aktuell``. Wer das Datum der Regierungsbildung will, fragt ``legislaturen.aktuelle_regierung(bl).get('von')``. Test-Suite: 1217 grün (vorher 1244, Differenz 27 = entfernte regierungs-Helper-Tests + obsolete WAHLPROGRAMME-Strukturtests).
2026-05-09 00:37:35 +02:00
lambda bl, p: {"pdf": "test.pdf"})
monkeypatch.setattr(wp_mod, "KONTEXT_PATH", tmp_path)
refactor(programme): WAHLPROGRAMME → programme.PROGRAMME konsolidiert (#222) Schließt #222. Entfernt die Doppelung zwischen ``wahlprogramme.WAHLPROGRAMME`` und ``programme.PROGRAMME``. Single source of truth ist jetzt ``programme.PROGRAMME`` als Literal mit allen 287 Programmen (Wahlprogramme + Bundes- + Landes-Grundsatzprogramme, historisch + aktuell). Schema schmaler — Felder ohne Konsumenten entfallen: - ``regierungsbildung`` / ``regierungsende`` → gehören zu ``legislaturen.REGIERUNGEN``. Verbindung Programm→Regierung läuft jetzt über ``legislaturen.regierung_zum_zeitpunkt(bl, datum)``. - ``partei`` (Langform "CDU NRW") → ableitbar aus partei + bundesland. - ``jahr`` → ableitbar aus ``gueltig_ab[:4]``. - ``beschluss`` / ``wahl`` / ``hinweis`` → keine App-Konsumenten. Felder im neuen Schema: id, typ, partei, bundesland, wp, gueltig_ab, gueltig_bis, name, titel (Slogan, optional), pdf, seiten. Daten-Migration einmalig via ``tools/build_programme_literal.py``: - Basis: bisherige embeddings.PROGRAMME (alle 287 IDs + gueltig_ab/bis) - titel aus WAHLPROGRAMME für die ~80 aktuellen Wahlprogramme + Land-Grundsatzprogramm-Slogans (ehem. _ARCHIVED_SKELETONS) - seiten via ``fitz.open(p).page_count`` für alle 287 PDFs Aufrufer migriert: - app/main.py:4055 — ``aktuelles_wahlprogramm(bl, partei).pdf`` - app/wahlprogramm_check.py — ``parteien_mit_wahlprogramm(bl)`` - app/redline_utils.py — Reverse-Lookup über ``all_programme()`` - app/wahlprogramm_fetch.py (3 Stellen) — ``aktuelles_wahlprogramm()`` - tests/test_redline_parser.py — Programm-Lookup statt WAHLPROGRAMME ``wahlprogramme.py`` schrumpft auf den Such-Code: Keyword-Fallback + PDF-Text-Loader + ein dünner ``get_wahlprogramm``-Compat-Adapter zu ``programme.aktuelles_wahlprogramm``. Drei Helper gelöscht (keine App-Konsumenten): ``regierungsbildung_for``, ``regierungsende_for``, ``regierung_aktuell``. Wer das Datum der Regierungsbildung will, fragt ``legislaturen.aktuelle_regierung(bl).get('von')``. Test-Suite: 1217 grün (vorher 1244, Differenz 27 = entfernte regierungs-Helper-Tests + obsolete WAHLPROGRAMME-Strukturtests).
2026-05-09 00:37:35 +02:00
result = wp_mod.load_wahlprogramm_text("X", "Y")
assert result == {}
# ─────────────────────────────────────────────────────────────────────────────
# search_wahlprogramm — Edge cases
# ─────────────────────────────────────────────────────────────────────────────
class TestSearchWahlprogramm:
def test_returns_empty_for_unknown_combination(self):
refactor(programme): WAHLPROGRAMME → programme.PROGRAMME konsolidiert (#222) Schließt #222. Entfernt die Doppelung zwischen ``wahlprogramme.WAHLPROGRAMME`` und ``programme.PROGRAMME``. Single source of truth ist jetzt ``programme.PROGRAMME`` als Literal mit allen 287 Programmen (Wahlprogramme + Bundes- + Landes-Grundsatzprogramme, historisch + aktuell). Schema schmaler — Felder ohne Konsumenten entfallen: - ``regierungsbildung`` / ``regierungsende`` → gehören zu ``legislaturen.REGIERUNGEN``. Verbindung Programm→Regierung läuft jetzt über ``legislaturen.regierung_zum_zeitpunkt(bl, datum)``. - ``partei`` (Langform "CDU NRW") → ableitbar aus partei + bundesland. - ``jahr`` → ableitbar aus ``gueltig_ab[:4]``. - ``beschluss`` / ``wahl`` / ``hinweis`` → keine App-Konsumenten. Felder im neuen Schema: id, typ, partei, bundesland, wp, gueltig_ab, gueltig_bis, name, titel (Slogan, optional), pdf, seiten. Daten-Migration einmalig via ``tools/build_programme_literal.py``: - Basis: bisherige embeddings.PROGRAMME (alle 287 IDs + gueltig_ab/bis) - titel aus WAHLPROGRAMME für die ~80 aktuellen Wahlprogramme + Land-Grundsatzprogramm-Slogans (ehem. _ARCHIVED_SKELETONS) - seiten via ``fitz.open(p).page_count`` für alle 287 PDFs Aufrufer migriert: - app/main.py:4055 — ``aktuelles_wahlprogramm(bl, partei).pdf`` - app/wahlprogramm_check.py — ``parteien_mit_wahlprogramm(bl)`` - app/redline_utils.py — Reverse-Lookup über ``all_programme()`` - app/wahlprogramm_fetch.py (3 Stellen) — ``aktuelles_wahlprogramm()`` - tests/test_redline_parser.py — Programm-Lookup statt WAHLPROGRAMME ``wahlprogramme.py`` schrumpft auf den Such-Code: Keyword-Fallback + PDF-Text-Loader + ein dünner ``get_wahlprogramm``-Compat-Adapter zu ``programme.aktuelles_wahlprogramm``. Drei Helper gelöscht (keine App-Konsumenten): ``regierungsbildung_for``, ``regierungsende_for``, ``regierung_aktuell``. Wer das Datum der Regierungsbildung will, fragt ``legislaturen.aktuelle_regierung(bl).get('von')``. Test-Suite: 1217 grün (vorher 1244, Differenz 27 = entfernte regierungs-Helper-Tests + obsolete WAHLPROGRAMME-Strukturtests).
2026-05-09 00:37:35 +02:00
result = search_wahlprogramm("XX", "XYZ", ["foo"])
assert result == []
def test_returns_empty_when_text_missing(self, monkeypatch):
refactor(programme): WAHLPROGRAMME → programme.PROGRAMME konsolidiert (#222) Schließt #222. Entfernt die Doppelung zwischen ``wahlprogramme.WAHLPROGRAMME`` und ``programme.PROGRAMME``. Single source of truth ist jetzt ``programme.PROGRAMME`` als Literal mit allen 287 Programmen (Wahlprogramme + Bundes- + Landes-Grundsatzprogramme, historisch + aktuell). Schema schmaler — Felder ohne Konsumenten entfallen: - ``regierungsbildung`` / ``regierungsende`` → gehören zu ``legislaturen.REGIERUNGEN``. Verbindung Programm→Regierung läuft jetzt über ``legislaturen.regierung_zum_zeitpunkt(bl, datum)``. - ``partei`` (Langform "CDU NRW") → ableitbar aus partei + bundesland. - ``jahr`` → ableitbar aus ``gueltig_ab[:4]``. - ``beschluss`` / ``wahl`` / ``hinweis`` → keine App-Konsumenten. Felder im neuen Schema: id, typ, partei, bundesland, wp, gueltig_ab, gueltig_bis, name, titel (Slogan, optional), pdf, seiten. Daten-Migration einmalig via ``tools/build_programme_literal.py``: - Basis: bisherige embeddings.PROGRAMME (alle 287 IDs + gueltig_ab/bis) - titel aus WAHLPROGRAMME für die ~80 aktuellen Wahlprogramme + Land-Grundsatzprogramm-Slogans (ehem. _ARCHIVED_SKELETONS) - seiten via ``fitz.open(p).page_count`` für alle 287 PDFs Aufrufer migriert: - app/main.py:4055 — ``aktuelles_wahlprogramm(bl, partei).pdf`` - app/wahlprogramm_check.py — ``parteien_mit_wahlprogramm(bl)`` - app/redline_utils.py — Reverse-Lookup über ``all_programme()`` - app/wahlprogramm_fetch.py (3 Stellen) — ``aktuelles_wahlprogramm()`` - tests/test_redline_parser.py — Programm-Lookup statt WAHLPROGRAMME ``wahlprogramme.py`` schrumpft auf den Such-Code: Keyword-Fallback + PDF-Text-Loader + ein dünner ``get_wahlprogramm``-Compat-Adapter zu ``programme.aktuelles_wahlprogramm``. Drei Helper gelöscht (keine App-Konsumenten): ``regierungsbildung_for``, ``regierungsende_for``, ``regierung_aktuell``. Wer das Datum der Regierungsbildung will, fragt ``legislaturen.aktuelle_regierung(bl).get('von')``. Test-Suite: 1217 grün (vorher 1244, Differenz 27 = entfernte regierungs-Helper-Tests + obsolete WAHLPROGRAMME-Strukturtests).
2026-05-09 00:37:35 +02:00
"""get_wahlprogramm liefert ein Programm, aber kein paged-Text:
search_wahlprogramm muss [] liefern, nicht crashen."""
from app import wahlprogramme as wp_mod
monkeypatch.setattr(wp_mod, "get_wahlprogramm",
refactor(programme): WAHLPROGRAMME → programme.PROGRAMME konsolidiert (#222) Schließt #222. Entfernt die Doppelung zwischen ``wahlprogramme.WAHLPROGRAMME`` und ``programme.PROGRAMME``. Single source of truth ist jetzt ``programme.PROGRAMME`` als Literal mit allen 287 Programmen (Wahlprogramme + Bundes- + Landes-Grundsatzprogramme, historisch + aktuell). Schema schmaler — Felder ohne Konsumenten entfallen: - ``regierungsbildung`` / ``regierungsende`` → gehören zu ``legislaturen.REGIERUNGEN``. Verbindung Programm→Regierung läuft jetzt über ``legislaturen.regierung_zum_zeitpunkt(bl, datum)``. - ``partei`` (Langform "CDU NRW") → ableitbar aus partei + bundesland. - ``jahr`` → ableitbar aus ``gueltig_ab[:4]``. - ``beschluss`` / ``wahl`` / ``hinweis`` → keine App-Konsumenten. Felder im neuen Schema: id, typ, partei, bundesland, wp, gueltig_ab, gueltig_bis, name, titel (Slogan, optional), pdf, seiten. Daten-Migration einmalig via ``tools/build_programme_literal.py``: - Basis: bisherige embeddings.PROGRAMME (alle 287 IDs + gueltig_ab/bis) - titel aus WAHLPROGRAMME für die ~80 aktuellen Wahlprogramme + Land-Grundsatzprogramm-Slogans (ehem. _ARCHIVED_SKELETONS) - seiten via ``fitz.open(p).page_count`` für alle 287 PDFs Aufrufer migriert: - app/main.py:4055 — ``aktuelles_wahlprogramm(bl, partei).pdf`` - app/wahlprogramm_check.py — ``parteien_mit_wahlprogramm(bl)`` - app/redline_utils.py — Reverse-Lookup über ``all_programme()`` - app/wahlprogramm_fetch.py (3 Stellen) — ``aktuelles_wahlprogramm()`` - tests/test_redline_parser.py — Programm-Lookup statt WAHLPROGRAMME ``wahlprogramme.py`` schrumpft auf den Such-Code: Keyword-Fallback + PDF-Text-Loader + ein dünner ``get_wahlprogramm``-Compat-Adapter zu ``programme.aktuelles_wahlprogramm``. Drei Helper gelöscht (keine App-Konsumenten): ``regierungsbildung_for``, ``regierungsende_for``, ``regierung_aktuell``. Wer das Datum der Regierungsbildung will, fragt ``legislaturen.aktuelle_regierung(bl).get('von')``. Test-Suite: 1217 grün (vorher 1244, Differenz 27 = entfernte regierungs-Helper-Tests + obsolete WAHLPROGRAMME-Strukturtests).
2026-05-09 00:37:35 +02:00
lambda bl, p: {"pdf": "missing.pdf",
"name": "X Wahlprogramm 2024",
"gueltig_ab": "2024-01-01"})
monkeypatch.setattr(wp_mod, "load_wahlprogramm_text",
lambda bl, p: {})
refactor(programme): WAHLPROGRAMME → programme.PROGRAMME konsolidiert (#222) Schließt #222. Entfernt die Doppelung zwischen ``wahlprogramme.WAHLPROGRAMME`` und ``programme.PROGRAMME``. Single source of truth ist jetzt ``programme.PROGRAMME`` als Literal mit allen 287 Programmen (Wahlprogramme + Bundes- + Landes-Grundsatzprogramme, historisch + aktuell). Schema schmaler — Felder ohne Konsumenten entfallen: - ``regierungsbildung`` / ``regierungsende`` → gehören zu ``legislaturen.REGIERUNGEN``. Verbindung Programm→Regierung läuft jetzt über ``legislaturen.regierung_zum_zeitpunkt(bl, datum)``. - ``partei`` (Langform "CDU NRW") → ableitbar aus partei + bundesland. - ``jahr`` → ableitbar aus ``gueltig_ab[:4]``. - ``beschluss`` / ``wahl`` / ``hinweis`` → keine App-Konsumenten. Felder im neuen Schema: id, typ, partei, bundesland, wp, gueltig_ab, gueltig_bis, name, titel (Slogan, optional), pdf, seiten. Daten-Migration einmalig via ``tools/build_programme_literal.py``: - Basis: bisherige embeddings.PROGRAMME (alle 287 IDs + gueltig_ab/bis) - titel aus WAHLPROGRAMME für die ~80 aktuellen Wahlprogramme + Land-Grundsatzprogramm-Slogans (ehem. _ARCHIVED_SKELETONS) - seiten via ``fitz.open(p).page_count`` für alle 287 PDFs Aufrufer migriert: - app/main.py:4055 — ``aktuelles_wahlprogramm(bl, partei).pdf`` - app/wahlprogramm_check.py — ``parteien_mit_wahlprogramm(bl)`` - app/redline_utils.py — Reverse-Lookup über ``all_programme()`` - app/wahlprogramm_fetch.py (3 Stellen) — ``aktuelles_wahlprogramm()`` - tests/test_redline_parser.py — Programm-Lookup statt WAHLPROGRAMME ``wahlprogramme.py`` schrumpft auf den Such-Code: Keyword-Fallback + PDF-Text-Loader + ein dünner ``get_wahlprogramm``-Compat-Adapter zu ``programme.aktuelles_wahlprogramm``. Drei Helper gelöscht (keine App-Konsumenten): ``regierungsbildung_for``, ``regierungsende_for``, ``regierung_aktuell``. Wer das Datum der Regierungsbildung will, fragt ``legislaturen.aktuelle_regierung(bl).get('von')``. Test-Suite: 1217 grün (vorher 1244, Differenz 27 = entfernte regierungs-Helper-Tests + obsolete WAHLPROGRAMME-Strukturtests).
2026-05-09 00:37:35 +02:00
assert search_wahlprogramm("X", "Y", ["foo"]) == []
# ─────────────────────────────────────────────────────────────────────────────
# find_relevant_quotes — Bundesland-Validierung
# ─────────────────────────────────────────────────────────────────────────────
class TestFindRelevantQuotes:
def test_unknown_bundesland_raises(self):
with pytest.raises(ValueError, match="Unbekanntes Bundesland"):
refactor(programme): WAHLPROGRAMME → programme.PROGRAMME konsolidiert (#222) Schließt #222. Entfernt die Doppelung zwischen ``wahlprogramme.WAHLPROGRAMME`` und ``programme.PROGRAMME``. Single source of truth ist jetzt ``programme.PROGRAMME`` als Literal mit allen 287 Programmen (Wahlprogramme + Bundes- + Landes-Grundsatzprogramme, historisch + aktuell). Schema schmaler — Felder ohne Konsumenten entfallen: - ``regierungsbildung`` / ``regierungsende`` → gehören zu ``legislaturen.REGIERUNGEN``. Verbindung Programm→Regierung läuft jetzt über ``legislaturen.regierung_zum_zeitpunkt(bl, datum)``. - ``partei`` (Langform "CDU NRW") → ableitbar aus partei + bundesland. - ``jahr`` → ableitbar aus ``gueltig_ab[:4]``. - ``beschluss`` / ``wahl`` / ``hinweis`` → keine App-Konsumenten. Felder im neuen Schema: id, typ, partei, bundesland, wp, gueltig_ab, gueltig_bis, name, titel (Slogan, optional), pdf, seiten. Daten-Migration einmalig via ``tools/build_programme_literal.py``: - Basis: bisherige embeddings.PROGRAMME (alle 287 IDs + gueltig_ab/bis) - titel aus WAHLPROGRAMME für die ~80 aktuellen Wahlprogramme + Land-Grundsatzprogramm-Slogans (ehem. _ARCHIVED_SKELETONS) - seiten via ``fitz.open(p).page_count`` für alle 287 PDFs Aufrufer migriert: - app/main.py:4055 — ``aktuelles_wahlprogramm(bl, partei).pdf`` - app/wahlprogramm_check.py — ``parteien_mit_wahlprogramm(bl)`` - app/redline_utils.py — Reverse-Lookup über ``all_programme()`` - app/wahlprogramm_fetch.py (3 Stellen) — ``aktuelles_wahlprogramm()`` - tests/test_redline_parser.py — Programm-Lookup statt WAHLPROGRAMME ``wahlprogramme.py`` schrumpft auf den Such-Code: Keyword-Fallback + PDF-Text-Loader + ein dünner ``get_wahlprogramm``-Compat-Adapter zu ``programme.aktuelles_wahlprogramm``. Drei Helper gelöscht (keine App-Konsumenten): ``regierungsbildung_for``, ``regierungsende_for``, ``regierung_aktuell``. Wer das Datum der Regierungsbildung will, fragt ``legislaturen.aktuelle_regierung(bl).get('von')``. Test-Suite: 1217 grün (vorher 1244, Differenz 27 = entfernte regierungs-Helper-Tests + obsolete WAHLPROGRAMME-Strukturtests).
2026-05-09 00:37:35 +02:00
find_relevant_quotes("text", ["CDU"], "XX")
class TestFormatQuoteForPrompt:
def test_empty_quotes_returns_empty_string(self):
assert format_quote_for_prompt({}) == ""