2026-05-08 01:07:59 +02:00
|
|
|
"""Tests for app.programme — zentrale Programm-Registry mit Geltungsdaten.
|
|
|
|
|
|
|
|
|
|
Diese Tests prüfen die Helper-API und die Migrationspfade aus
|
|
|
|
|
``WAHLPROGRAMME`` und ``embeddings.PROGRAMME``. Architektur-Doku in
|
|
|
|
|
docs/adr/0013-programme-legislaturen-zeitpunktige-bewertung.md.
|
|
|
|
|
"""
|
|
|
|
|
import sys
|
|
|
|
|
import types
|
|
|
|
|
|
|
|
|
|
import pytest
|
|
|
|
|
|
|
|
|
|
# Stub openai für embeddings.py-Import (programme._migrate_from_legacy()
|
|
|
|
|
# triggert beim ersten API-Call den embeddings-Import).
|
|
|
|
|
if "openai" not in sys.modules:
|
|
|
|
|
o = types.ModuleType("openai")
|
|
|
|
|
o.OpenAI = lambda **kw: None
|
|
|
|
|
sys.modules["openai"] = o
|
|
|
|
|
|
|
|
|
|
try:
|
|
|
|
|
import fitz as _fitz
|
|
|
|
|
if not hasattr(_fitz, "open"):
|
|
|
|
|
import pymupdf
|
|
|
|
|
sys.modules["fitz"] = pymupdf
|
|
|
|
|
except ImportError:
|
|
|
|
|
try:
|
|
|
|
|
import pymupdf
|
|
|
|
|
sys.modules["fitz"] = pymupdf
|
|
|
|
|
except ImportError:
|
|
|
|
|
pass
|
|
|
|
|
|
|
|
|
|
from app.programme import (
|
|
|
|
|
PROGRAMME,
|
|
|
|
|
aktuelles_wahlprogramm,
|
|
|
|
|
alle_versionen,
|
|
|
|
|
all_programme,
|
|
|
|
|
get_programm,
|
|
|
|
|
grundsatzprogramm_zum_zeitpunkt,
|
|
|
|
|
parteien_mit_wahlprogramm,
|
|
|
|
|
wahlprogramm_zum_zeitpunkt,
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# ─────────────────────────────────────────────────────────────────────────────
|
|
|
|
|
# Migration: Daten landen aus den Legacy-Quellen in PROGRAMME
|
|
|
|
|
# ─────────────────────────────────────────────────────────────────────────────
|
|
|
|
|
|
|
|
|
|
class TestMigration:
|
|
|
|
|
def test_migration_populates_programme(self):
|
|
|
|
|
progs = all_programme()
|
|
|
|
|
assert len(progs) > 80, \
|
|
|
|
|
f"Expected >80 programme nach migration; got {len(progs)}"
|
|
|
|
|
|
|
|
|
|
def test_alle_typen_vertreten(self):
|
|
|
|
|
progs = all_programme()
|
|
|
|
|
typs = {p["typ"] for p in progs}
|
|
|
|
|
assert "wahlprogramm" in typs
|
|
|
|
|
assert "grundsatzprogramm-bund" in typs
|
|
|
|
|
assert "grundsatzprogramm-land" in typs
|
|
|
|
|
|
|
|
|
|
def test_jeder_eintrag_hat_pflichtfelder(self):
|
|
|
|
|
for prog in all_programme():
|
|
|
|
|
for f in ("id", "titel", "name", "typ", "partei",
|
|
|
|
|
"gueltig_ab", "pdf"):
|
|
|
|
|
assert f in prog, f"{prog.get('id')}: feld {f} fehlt"
|
|
|
|
|
|
|
|
|
|
def test_ids_sind_eindeutig(self):
|
|
|
|
|
ids = [p["id"] for p in all_programme()]
|
|
|
|
|
assert len(ids) == len(set(ids)), "Duplicate Programm-IDs"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# ─────────────────────────────────────────────────────────────────────────────
|
|
|
|
|
# aktuelles_wahlprogramm
|
|
|
|
|
# ─────────────────────────────────────────────────────────────────────────────
|
|
|
|
|
|
|
|
|
|
class TestAktuellesWahlprogramm:
|
|
|
|
|
def test_nrw_cdu_returns_2022(self):
|
|
|
|
|
p = aktuelles_wahlprogramm("NRW", "CDU")
|
|
|
|
|
assert p is not None
|
|
|
|
|
assert p["id"] == "cdu-nrw-2022"
|
|
|
|
|
assert p["bundesland"] == "NRW"
|
|
|
|
|
assert p["partei"] == "CDU"
|
|
|
|
|
assert p["typ"] == "wahlprogramm"
|
|
|
|
|
|
|
|
|
|
def test_bund_cdu_returns_btw_2025(self):
|
|
|
|
|
p = aktuelles_wahlprogramm("BUND", "CDU")
|
|
|
|
|
assert p is not None
|
|
|
|
|
assert p["id"] == "cdu-bund-2025"
|
2026-05-08 09:17:08 +02:00
|
|
|
# Wahltag, nicht Regierungsbildung (B1+B2: Programme gelten ab Wahl)
|
|
|
|
|
assert p["gueltig_ab"] == "2025-02-23"
|
2026-05-08 01:07:59 +02:00
|
|
|
|
|
|
|
|
def test_unknown_bl_returns_none(self):
|
|
|
|
|
assert aktuelles_wahlprogramm("XX", "CDU") is None
|
|
|
|
|
|
|
|
|
|
def test_unknown_partei_returns_none(self):
|
|
|
|
|
assert aktuelles_wahlprogramm("NRW", "BSW") is None # nicht im Landtag NRW
|
|
|
|
|
|
|
|
|
|
def test_aktuelles_hat_gueltig_bis_none(self):
|
|
|
|
|
for bl in ["NRW", "BUND", "BB", "TH"]:
|
|
|
|
|
for partei in parteien_mit_wahlprogramm(bl):
|
|
|
|
|
p = aktuelles_wahlprogramm(bl, partei)
|
|
|
|
|
if p is not None:
|
|
|
|
|
assert p["gueltig_bis"] is None, \
|
|
|
|
|
f"{bl}/{partei} aktuell aber gueltig_bis gesetzt"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# ─────────────────────────────────────────────────────────────────────────────
|
|
|
|
|
# wahlprogramm_zum_zeitpunkt — historisch korrekte Einordnung
|
|
|
|
|
# ─────────────────────────────────────────────────────────────────────────────
|
|
|
|
|
|
|
|
|
|
class TestWahlprogrammZumZeitpunkt:
|
|
|
|
|
def test_nrw_cdu_aktuelles_datum(self):
|
|
|
|
|
# 2024-01-01 liegt im Geltungszeitraum von cdu-nrw-2022
|
|
|
|
|
p = wahlprogramm_zum_zeitpunkt("NRW", "CDU", "2024-01-01")
|
|
|
|
|
assert p is not None
|
|
|
|
|
assert p["id"] == "cdu-nrw-2022"
|
|
|
|
|
|
2026-05-08 09:17:08 +02:00
|
|
|
def test_bund_btw_2025_nach_wahl(self):
|
|
|
|
|
# Nach Wahltag (2025-02-23) gilt das BTW-2025-Programm.
|
2026-05-08 01:07:59 +02:00
|
|
|
p = wahlprogramm_zum_zeitpunkt("BUND", "SPD", "2025-09-01")
|
|
|
|
|
assert p is not None
|
|
|
|
|
assert p["id"] == "spd-bund-2025"
|
|
|
|
|
|
2026-05-08 09:17:08 +02:00
|
|
|
def test_bund_btw_2025_in_uebergangsphase(self):
|
|
|
|
|
# Zwischen Wahl (2025-02-23) und Vereidigung Merz I (2025-05-06)
|
|
|
|
|
# gilt das BTW-2025-Programm bereits — B1+B2 (Programme gelten ab
|
|
|
|
|
# Wahltag).
|
|
|
|
|
p = wahlprogramm_zum_zeitpunkt("BUND", "SPD", "2025-04-01")
|
|
|
|
|
assert p is not None
|
|
|
|
|
assert p["id"] == "spd-bund-2025"
|
|
|
|
|
|
feat: Block 2.2 — BUND WP20 (BTW 2021, Scholz-Ampel) historisch indiziert
7 Wahlprogramme zur BTW 26.09.2021 — die Programme der Scholz-Ampel-Periode
(SPD+GRÜNE+FDP, vereidigt 08.12.2021, vorgezogenes Ende 25.03.2025):
- cdu-bund-2021 (gemeinsam CDU/CSU "Stabilitaet und Erneuerung", 140 S., 232 chunks)
- csu-bund-2021 (eigenstaendige CSU-Bayern-Fokus-Variante, 18 S., 24 chunks)
- spd-bund-2021 (Zukunftsprogramm "Aus Respekt vor Deiner Zukunft", 66 S., 105 chunks)
- gruene-bund-2021 (272 S. barrierefreie Fassung, 269 chunks)
- fdp-bund-2021 (Beschluss 14.-16.05.2021 Berlin, 68 S., 136 chunks)
- afd-bund-2021 ("Deutschland. Aber normal.", 210 S., 160 chunks)
- linke-bund-2021 ("Zeit zu handeln!", 168 S., 324 chunks)
Total: 1.250 Chunks.
Geltungszeitraum 2021-09-26 (Wahltag) bis 2025-02-23 (Wahltag BTW 2025,
exklusiv). Antraege aus dieser Periode bekommen jetzt automatisch das
korrekte Programm zurueckgeliefert via wahlprogramm_zum_zeitpunkt():
- 2024-01-01 BUND/SPD -> spd-bund-2021 (Scholz-Ampel)
- 2025-02-22 BUND/SPD -> spd-bund-2021 (noch alt)
- 2025-02-23 BUND/SPD -> spd-bund-2025 (BTW-Wahltag, Wechsel)
Tests: 117 gruen, plus neue test_bund_2024_returns_btw_2021 und
test_bund_grenze_btw_2021_btw_2025.
Block 2.2 abgeschlossen — Block 2 Roadmap (16 BL × 3 WPs) ist 2/16 BL.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-08 10:25:51 +02:00
|
|
|
def test_bund_2024_returns_btw_2021(self):
|
|
|
|
|
# Antrag aus 2024-01-01 liegt im Geltungszeitraum von BTW 2021
|
|
|
|
|
# (Scholz-Ampel-Programm, gueltig 2021-09-26 bis 2025-02-23).
|
2026-05-08 01:07:59 +02:00
|
|
|
p = wahlprogramm_zum_zeitpunkt("BUND", "SPD", "2024-01-01")
|
feat: Block 2.2 — BUND WP20 (BTW 2021, Scholz-Ampel) historisch indiziert
7 Wahlprogramme zur BTW 26.09.2021 — die Programme der Scholz-Ampel-Periode
(SPD+GRÜNE+FDP, vereidigt 08.12.2021, vorgezogenes Ende 25.03.2025):
- cdu-bund-2021 (gemeinsam CDU/CSU "Stabilitaet und Erneuerung", 140 S., 232 chunks)
- csu-bund-2021 (eigenstaendige CSU-Bayern-Fokus-Variante, 18 S., 24 chunks)
- spd-bund-2021 (Zukunftsprogramm "Aus Respekt vor Deiner Zukunft", 66 S., 105 chunks)
- gruene-bund-2021 (272 S. barrierefreie Fassung, 269 chunks)
- fdp-bund-2021 (Beschluss 14.-16.05.2021 Berlin, 68 S., 136 chunks)
- afd-bund-2021 ("Deutschland. Aber normal.", 210 S., 160 chunks)
- linke-bund-2021 ("Zeit zu handeln!", 168 S., 324 chunks)
Total: 1.250 Chunks.
Geltungszeitraum 2021-09-26 (Wahltag) bis 2025-02-23 (Wahltag BTW 2025,
exklusiv). Antraege aus dieser Periode bekommen jetzt automatisch das
korrekte Programm zurueckgeliefert via wahlprogramm_zum_zeitpunkt():
- 2024-01-01 BUND/SPD -> spd-bund-2021 (Scholz-Ampel)
- 2025-02-22 BUND/SPD -> spd-bund-2021 (noch alt)
- 2025-02-23 BUND/SPD -> spd-bund-2025 (BTW-Wahltag, Wechsel)
Tests: 117 gruen, plus neue test_bund_2024_returns_btw_2021 und
test_bund_grenze_btw_2021_btw_2025.
Block 2.2 abgeschlossen — Block 2 Roadmap (16 BL × 3 WPs) ist 2/16 BL.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-08 10:25:51 +02:00
|
|
|
assert p is not None
|
|
|
|
|
assert p["id"] == "spd-bund-2021"
|
|
|
|
|
assert p["wp"] == 20
|
|
|
|
|
|
|
|
|
|
def test_bund_grenze_btw_2021_btw_2025(self):
|
|
|
|
|
# Tag vor BTW 2025: Scholz-Ampel-Programm gilt noch.
|
|
|
|
|
p_alt = wahlprogramm_zum_zeitpunkt("BUND", "SPD", "2025-02-22")
|
|
|
|
|
assert p_alt["id"] == "spd-bund-2021"
|
|
|
|
|
# Wahltag BTW 2025: neues Programm gilt.
|
|
|
|
|
p_neu = wahlprogramm_zum_zeitpunkt("BUND", "SPD", "2025-02-23")
|
|
|
|
|
assert p_neu["id"] == "spd-bund-2025"
|
2026-05-08 01:07:59 +02:00
|
|
|
|
feat: Block 2.1 — NRW WP17 historische Wahlprogramme indiziert (Pilot)
5 Programme zur LTW NRW 14.05.2017 als historische Wahlprogramme im
Embeddings-Index — erster Datensatz für die zeitpunktige Bewertung
historischer Antraege:
- cdu-nrw-2017 (Laschet, 120 S., 172 chunks)
- spd-nrw-2017 (Kraft, 116 S., 169 chunks)
- gruene-nrw-2017 (131 S., 322 chunks)
- fdp-nrw-2017 (Lindner, 56 S., 92 chunks)
- afd-nrw-2017 (84 S., 78 chunks)
Geltungszeitraum 2017-05-14 (Wahltag WP17) bis 2022-05-15 (Wahltag
WP18, exklusiv). Eintraege liegen NUR in embeddings.PROGRAMME — die
WAHLPROGRAMME[NRW]-Struktur bleibt single-current (cdu-nrw-2022).
programme._migrate_from_legacy hat einen neuen Schritt 2b, der
typ=wahlprogramm-Eintraege aus embeddings.PROGRAMME mit explizitem
gueltig_ab/_bis als historische Wahlprogramme registriert. Damit
liefert wahlprogramm_zum_zeitpunkt() jetzt fuer NRW-Antraege aus dem
Zeitraum 2017-2022 das passende Programm.
Live-Verifikation auf gwoe-antragspruefer-dev:
- 2018-09-01 -> cdu-nrw-2017 (WP17)
- 2024-01-01 -> cdu-nrw-2022 (WP18)
- Grenze: 14.05.2022 -> WP17, 15.05.2022 -> WP18
Tests: 116 gruen, plus neue test_grenze_zwischen_wp17_und_wp18 und
angepasstes test_datum_vor_aktueller_wp_nrw_wp17.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-08 09:44:26 +02:00
|
|
|
def test_datum_vor_aktueller_wp_nrw_wp17(self):
|
|
|
|
|
# Antrag aus 2018 in NRW: WP17-Programm (cdu-nrw-2017) gilt
|
|
|
|
|
# ab 2017-05-14 bis 2022-05-15.
|
2026-05-08 01:07:59 +02:00
|
|
|
p = wahlprogramm_zum_zeitpunkt("NRW", "CDU", "2018-09-01")
|
feat: Block 2.1 — NRW WP17 historische Wahlprogramme indiziert (Pilot)
5 Programme zur LTW NRW 14.05.2017 als historische Wahlprogramme im
Embeddings-Index — erster Datensatz für die zeitpunktige Bewertung
historischer Antraege:
- cdu-nrw-2017 (Laschet, 120 S., 172 chunks)
- spd-nrw-2017 (Kraft, 116 S., 169 chunks)
- gruene-nrw-2017 (131 S., 322 chunks)
- fdp-nrw-2017 (Lindner, 56 S., 92 chunks)
- afd-nrw-2017 (84 S., 78 chunks)
Geltungszeitraum 2017-05-14 (Wahltag WP17) bis 2022-05-15 (Wahltag
WP18, exklusiv). Eintraege liegen NUR in embeddings.PROGRAMME — die
WAHLPROGRAMME[NRW]-Struktur bleibt single-current (cdu-nrw-2022).
programme._migrate_from_legacy hat einen neuen Schritt 2b, der
typ=wahlprogramm-Eintraege aus embeddings.PROGRAMME mit explizitem
gueltig_ab/_bis als historische Wahlprogramme registriert. Damit
liefert wahlprogramm_zum_zeitpunkt() jetzt fuer NRW-Antraege aus dem
Zeitraum 2017-2022 das passende Programm.
Live-Verifikation auf gwoe-antragspruefer-dev:
- 2018-09-01 -> cdu-nrw-2017 (WP17)
- 2024-01-01 -> cdu-nrw-2022 (WP18)
- Grenze: 14.05.2022 -> WP17, 15.05.2022 -> WP18
Tests: 116 gruen, plus neue test_grenze_zwischen_wp17_und_wp18 und
angepasstes test_datum_vor_aktueller_wp_nrw_wp17.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-08 09:44:26 +02:00
|
|
|
assert p is not None
|
|
|
|
|
assert p["id"] == "cdu-nrw-2017"
|
|
|
|
|
assert p["wp"] == 17
|
|
|
|
|
assert p["gueltig_ab"] == "2017-05-14"
|
|
|
|
|
assert p["gueltig_bis"] == "2022-05-15"
|
|
|
|
|
|
|
|
|
|
def test_grenze_zwischen_wp17_und_wp18(self):
|
|
|
|
|
# Genau am Wahltag der nächsten WP (2022-05-15) gilt das neue
|
|
|
|
|
# Programm. range = [gueltig_ab, gueltig_bis), also gueltig_bis
|
|
|
|
|
# selbst ist exklusiv.
|
|
|
|
|
p_alt = wahlprogramm_zum_zeitpunkt("NRW", "CDU", "2022-05-14")
|
|
|
|
|
assert p_alt["id"] == "cdu-nrw-2017"
|
|
|
|
|
p_neu = wahlprogramm_zum_zeitpunkt("NRW", "CDU", "2022-05-15")
|
|
|
|
|
assert p_neu["id"] == "cdu-nrw-2022"
|
2026-05-08 01:07:59 +02:00
|
|
|
|
|
|
|
|
|
|
|
|
|
# ─────────────────────────────────────────────────────────────────────────────
|
|
|
|
|
# grundsatzprogramm_zum_zeitpunkt — Bund + Land mit Vorgänger-Logik
|
|
|
|
|
# ─────────────────────────────────────────────────────────────────────────────
|
|
|
|
|
|
|
|
|
|
class TestGrundsatzprogrammZumZeitpunkt:
|
|
|
|
|
def test_cdu_bund_2025(self):
|
|
|
|
|
p = grundsatzprogramm_zum_zeitpunkt("CDU", "2025-01-01")
|
|
|
|
|
assert p is not None
|
|
|
|
|
assert p["id"] == "cdu-grundsatz" # 2024er Programm
|
|
|
|
|
assert p["typ"] == "grundsatzprogramm-bund"
|
|
|
|
|
|
|
|
|
|
def test_cdu_bund_vor_2024_keiner(self):
|
|
|
|
|
# Hannoveraner Programm 2007 ist nicht im Schema indiziert.
|
|
|
|
|
p = grundsatzprogramm_zum_zeitpunkt("CDU", "2010-01-01")
|
|
|
|
|
assert p is None
|
|
|
|
|
|
|
|
|
|
def test_cdu_nrw_landesgrundsatz_bevorzugt(self):
|
|
|
|
|
# Mit bundesland=NRW wird das Landesgrundsatzprogramm zurückgegeben,
|
|
|
|
|
# nicht das Bundes.
|
|
|
|
|
p = grundsatzprogramm_zum_zeitpunkt("CDU", "2024-01-01", bundesland="NRW")
|
|
|
|
|
assert p is not None
|
|
|
|
|
assert p["id"] == "cdu-grundsatz-nrw"
|
|
|
|
|
assert p["typ"] == "grundsatzprogramm-land"
|
|
|
|
|
|
|
|
|
|
def test_cdu_he_kein_landesgrundsatz_fallback_auf_bund(self):
|
|
|
|
|
# Hessen hat kein CDU-Landesgrundsatzprogramm → Fallback auf Bund.
|
|
|
|
|
p = grundsatzprogramm_zum_zeitpunkt("CDU", "2025-01-01", bundesland="HE")
|
|
|
|
|
assert p is not None
|
|
|
|
|
assert p["id"] == "cdu-grundsatz" # Bund
|
|
|
|
|
|
|
|
|
|
def test_ssw_sh_landesgrundsatz(self):
|
|
|
|
|
# SSW existiert nur in SH — Rahmenprogramm 2016.
|
|
|
|
|
p = grundsatzprogramm_zum_zeitpunkt("SSW", "2024-01-01", bundesland="SH")
|
|
|
|
|
assert p is not None
|
|
|
|
|
assert p["id"] == "ssw-grundsatz"
|
|
|
|
|
|
|
|
|
|
def test_csu_2023_aktuell(self):
|
|
|
|
|
p = grundsatzprogramm_zum_zeitpunkt("CSU", "2024-01-01", bundesland="BY")
|
|
|
|
|
assert p is not None
|
|
|
|
|
assert p["id"] == "csu-grundsatz"
|
|
|
|
|
assert p["gueltig_ab"] == "2023-05-06"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# ─────────────────────────────────────────────────────────────────────────────
|
|
|
|
|
# parteien_mit_wahlprogramm
|
|
|
|
|
# ─────────────────────────────────────────────────────────────────────────────
|
|
|
|
|
|
|
|
|
|
class TestParteienMitWahlprogramm:
|
|
|
|
|
def test_nrw_5_parteien(self):
|
|
|
|
|
parteien = set(parteien_mit_wahlprogramm("NRW"))
|
|
|
|
|
assert parteien == {"CDU", "SPD", "GRÜNE", "FDP", "AfD"}
|
|
|
|
|
|
|
|
|
|
def test_bund_8_parteien(self):
|
|
|
|
|
parteien = set(parteien_mit_wahlprogramm("BUND"))
|
|
|
|
|
assert parteien == {"CDU", "CSU", "SPD", "GRÜNE", "FDP",
|
|
|
|
|
"AfD", "LINKE", "BSW"}
|
|
|
|
|
|
|
|
|
|
def test_unknown_bl_empty(self):
|
|
|
|
|
assert parteien_mit_wahlprogramm("XX") == []
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# ─────────────────────────────────────────────────────────────────────────────
|
|
|
|
|
# alle_versionen — Lieferung sortiert nach gueltig_ab
|
|
|
|
|
# ─────────────────────────────────────────────────────────────────────────────
|
|
|
|
|
|
|
|
|
|
class TestAlleVersionen:
|
feat: Block 2.4 + 2.5 — historische Wahlprogramme zurück bis 2011
92 neue PDFs + Geltungsdaten in PROGRAMME-Registry:
Block 2.4 — direkte 2019er-Lücken (21 Dokumente):
- BIW Bremen: Wahlprogramme 2015 + 2019 + 2023
- BUND WP19 (BTW 2017): CDU, CSU, SPD, GRÜNE, FDP, AfD, LINKE
- HH WP21 (BS 2015): SPD, CDU, GRÜNE, LINKE, AfD, FDP
- TH WP6 (LTW 2014): LINKE, CDU, SPD, GRÜNE, AfD, FDP
Block 2.5 — Vor-Vorperioden 2011-2014 (~71 Dokumente) für 15 BL:
BB WP6, BE WP17, BUND WP18, BW WP15, BY WP17, HB WP19, HE WP19,
LSA WP6, MV WP6, NI WP17, NRW WP16, RP WP16, SH WP18, SL WP15, SN WP6.
Inkl. PIRATEN, BVB/FREIE WÄHLER, FREIE WÄHLER, SSW.
Coverage: 287 Programme (286 indiziert auf dev — linke-sl-2012 ist
ein 2-Seiten-Kurzwahlprogramm und liefert keine Chunks).
Source-of-Truth-Pflege:
- wahlprogramm-links.yaml ergänzt (Quellen-URLs für alle PDFs)
- wahlprogramm-shas.lock.json ergänzt (SHA-256 für Integritätsprüfung)
- test_programme.py: drei Versionen NRW CDU statt zwei
Schließt #233 + #234.
2026-05-08 14:10:52 +02:00
|
|
|
def test_nrw_cdu_drei_versionen(self):
|
|
|
|
|
# WP16 (2012) + WP17 (2017) + WP18 (2022) — historisch komplett bis 2012.
|
2026-05-08 01:07:59 +02:00
|
|
|
versions = alle_versionen("NRW", "CDU")
|
feat: Block 2.4 + 2.5 — historische Wahlprogramme zurück bis 2011
92 neue PDFs + Geltungsdaten in PROGRAMME-Registry:
Block 2.4 — direkte 2019er-Lücken (21 Dokumente):
- BIW Bremen: Wahlprogramme 2015 + 2019 + 2023
- BUND WP19 (BTW 2017): CDU, CSU, SPD, GRÜNE, FDP, AfD, LINKE
- HH WP21 (BS 2015): SPD, CDU, GRÜNE, LINKE, AfD, FDP
- TH WP6 (LTW 2014): LINKE, CDU, SPD, GRÜNE, AfD, FDP
Block 2.5 — Vor-Vorperioden 2011-2014 (~71 Dokumente) für 15 BL:
BB WP6, BE WP17, BUND WP18, BW WP15, BY WP17, HB WP19, HE WP19,
LSA WP6, MV WP6, NI WP17, NRW WP16, RP WP16, SH WP18, SL WP15, SN WP6.
Inkl. PIRATEN, BVB/FREIE WÄHLER, FREIE WÄHLER, SSW.
Coverage: 287 Programme (286 indiziert auf dev — linke-sl-2012 ist
ein 2-Seiten-Kurzwahlprogramm und liefert keine Chunks).
Source-of-Truth-Pflege:
- wahlprogramm-links.yaml ergänzt (Quellen-URLs für alle PDFs)
- wahlprogramm-shas.lock.json ergänzt (SHA-256 für Integritätsprüfung)
- test_programme.py: drei Versionen NRW CDU statt zwei
Schließt #233 + #234.
2026-05-08 14:10:52 +02:00
|
|
|
assert len(versions) >= 3
|
feat: Block 2.1 — NRW WP17 historische Wahlprogramme indiziert (Pilot)
5 Programme zur LTW NRW 14.05.2017 als historische Wahlprogramme im
Embeddings-Index — erster Datensatz für die zeitpunktige Bewertung
historischer Antraege:
- cdu-nrw-2017 (Laschet, 120 S., 172 chunks)
- spd-nrw-2017 (Kraft, 116 S., 169 chunks)
- gruene-nrw-2017 (131 S., 322 chunks)
- fdp-nrw-2017 (Lindner, 56 S., 92 chunks)
- afd-nrw-2017 (84 S., 78 chunks)
Geltungszeitraum 2017-05-14 (Wahltag WP17) bis 2022-05-15 (Wahltag
WP18, exklusiv). Eintraege liegen NUR in embeddings.PROGRAMME — die
WAHLPROGRAMME[NRW]-Struktur bleibt single-current (cdu-nrw-2022).
programme._migrate_from_legacy hat einen neuen Schritt 2b, der
typ=wahlprogramm-Eintraege aus embeddings.PROGRAMME mit explizitem
gueltig_ab/_bis als historische Wahlprogramme registriert. Damit
liefert wahlprogramm_zum_zeitpunkt() jetzt fuer NRW-Antraege aus dem
Zeitraum 2017-2022 das passende Programm.
Live-Verifikation auf gwoe-antragspruefer-dev:
- 2018-09-01 -> cdu-nrw-2017 (WP17)
- 2024-01-01 -> cdu-nrw-2022 (WP18)
- Grenze: 14.05.2022 -> WP17, 15.05.2022 -> WP18
Tests: 116 gruen, plus neue test_grenze_zwischen_wp17_und_wp18 und
angepasstes test_datum_vor_aktueller_wp_nrw_wp17.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-08 09:44:26 +02:00
|
|
|
# sortiert nach gueltig_ab aufsteigend
|
feat: Block 2.4 + 2.5 — historische Wahlprogramme zurück bis 2011
92 neue PDFs + Geltungsdaten in PROGRAMME-Registry:
Block 2.4 — direkte 2019er-Lücken (21 Dokumente):
- BIW Bremen: Wahlprogramme 2015 + 2019 + 2023
- BUND WP19 (BTW 2017): CDU, CSU, SPD, GRÜNE, FDP, AfD, LINKE
- HH WP21 (BS 2015): SPD, CDU, GRÜNE, LINKE, AfD, FDP
- TH WP6 (LTW 2014): LINKE, CDU, SPD, GRÜNE, AfD, FDP
Block 2.5 — Vor-Vorperioden 2011-2014 (~71 Dokumente) für 15 BL:
BB WP6, BE WP17, BUND WP18, BW WP15, BY WP17, HB WP19, HE WP19,
LSA WP6, MV WP6, NI WP17, NRW WP16, RP WP16, SH WP18, SL WP15, SN WP6.
Inkl. PIRATEN, BVB/FREIE WÄHLER, FREIE WÄHLER, SSW.
Coverage: 287 Programme (286 indiziert auf dev — linke-sl-2012 ist
ein 2-Seiten-Kurzwahlprogramm und liefert keine Chunks).
Source-of-Truth-Pflege:
- wahlprogramm-links.yaml ergänzt (Quellen-URLs für alle PDFs)
- wahlprogramm-shas.lock.json ergänzt (SHA-256 für Integritätsprüfung)
- test_programme.py: drei Versionen NRW CDU statt zwei
Schließt #233 + #234.
2026-05-08 14:10:52 +02:00
|
|
|
ids = [v["id"] for v in versions]
|
|
|
|
|
assert "cdu-nrw-2012" in ids
|
|
|
|
|
assert "cdu-nrw-2017" in ids
|
|
|
|
|
assert "cdu-nrw-2022" in ids
|
|
|
|
|
# die erste Version muss älter sein als die zweite
|
|
|
|
|
assert versions[0]["gueltig_ab"] < versions[1]["gueltig_ab"]
|
2026-05-08 01:07:59 +02:00
|
|
|
|
|
|
|
|
def test_unknown_bl_leer(self):
|
|
|
|
|
assert alle_versionen("XX", "CDU") == []
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# ─────────────────────────────────────────────────────────────────────────────
|
|
|
|
|
# get_programm — direct ID-Lookup
|
|
|
|
|
# ─────────────────────────────────────────────────────────────────────────────
|
|
|
|
|
|
|
|
|
|
class TestGetProgramm:
|
|
|
|
|
def test_known_id(self):
|
|
|
|
|
p = get_programm("cdu-nrw-2022")
|
|
|
|
|
assert p is not None
|
|
|
|
|
assert p["partei"] == "CDU"
|
|
|
|
|
|
|
|
|
|
def test_unknown_id_returns_none(self):
|
|
|
|
|
assert get_programm("does-not-exist") is None
|