Schließt #56 (Bundespolitik überprüfbar machen). Neuer ``BundestagAdapter`` in ``app/parlamente.py``, neuer ``BUND``-Eintrag in ``app/bundeslaender.py`` als 17. Parlament-Slot. API: - DIP-Search-API auf ``search.dip.bundestag.de/api/v1/drucksache`` - API-Key aus ``dip-config.js`` gescraped (öffentlich, klartext) - Auth via URL-Param ``?apikey=...`` plus ``Origin: https://dip.bundestag.de``- Header (Origin-Locking, server-to-server-tauglich) - Pagination via ``cursor``-Parameter, 100 Hits pro Page - ``f.drucksachetyp=Antrag`` und ``f.wahlperiode=21`` als Server-Filter Mapping: - ``dokumentnummer`` → ``Drucksache.drucksache`` - ``titel`` → ``title`` - ``urheber[*].titel`` → durch ``parteien.extract_fraktionen`` zu ``["AfD"]``/``["GRÜNE"]``/etc. — die ``"Fraktion der AfD"``- Schreibweise wird vom zentralen Mapper aus #55 bereits korrekt geparst, kein Adapter-spezifisches Pattern nötig - ``fundstelle.pdf_url`` → ``link`` - ``datum`` → bereits ISO ``YYYY-MM-DD`` ``get_document(drucksache)`` nutzt ``f.dokumentnummer`` als direkter Server-Filter, kein linearer Pagination-Scan. BUND-Eintrag in ``bundeslaender.py``: - ``code="BUND"``, ``parlament_name="Deutscher Bundestag"``, ``wahlperiode=21``, ``wahlperiode_start="2025-03-25"`` (Konstituierung 21. WP nach BTW 2025), ``regierungsfraktionen=["CDU", "CSU", "SPD"]`` (Kabinett Merz) - ``aktiv=True`` — taucht automatisch in ``alle_bundeslaender()`` und ``aktive_bundeslaender()`` auf, damit die UI- und Auswertungs-Pipelines BUND ohne zusätzliche Sonderpfade kennen - 17 Einträge in ``BUNDESLAENDER`` statt 16 — Tests entsprechend aktualisiert (``test_sixteen_bundeslaender_plus_bund``, ``test_alle_bundeslaender_returns_all``, ``test_all_wahlperioden_lists_each_bl_twice``) Live-Probe direkt im Repo: ``` adapter: Deutscher Bundestag (DIP), wahlperiode=21 search returned 5 docs 21/5136 2026-03-31 | ['AfD'] | Transparenz, Wirtschaftlichkeit ... 21/5064 2026-03-27 | ['GRÜNE'] | Ausverkauf der Energieinfrastruktur ... 21/5059 2026-03-27 | ['AfD'] | Berufsfreiheit für Selbstständige ... get_document('21/5136') -> drucksache=21/5136 ``` 176 Unit-Tests grün, Live-Verifikation Sub-A im Container nach Deploy. Refs: #56, #59 (Phase G) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
94 lines
3.5 KiB
Python
94 lines
3.5 KiB
Python
"""Tests for bundeslaender.py — sanity over the parliament registry.
|
|
|
|
Stand: 16 Bundesländer + Sondereintrag ``BUND`` für den Deutschen
|
|
Bundestag (siehe #56). Der ``Bundesland``-Dataclass-Name ist historisch
|
|
und wird inzwischen als generischer Parlament-Slot verwendet — eine
|
|
Umbenennung ist als separates Refactoring-Item geplant.
|
|
|
|
Includes the #48 classification regression: TH must be ParlDok, HB must
|
|
be StarWeb, SN must be Eigensystem (not ParlDok).
|
|
"""
|
|
from app.bundeslaender import BUNDESLAENDER, get, aktive_bundeslaender, alle_bundeslaender
|
|
|
|
|
|
class TestRegistryStructure:
|
|
def test_sixteen_bundeslaender_plus_bund(self):
|
|
# 16 echte Bundesländer + BUND-Sondereintrag (#56)
|
|
assert len(BUNDESLAENDER) == 17
|
|
assert "BUND" in BUNDESLAENDER
|
|
|
|
def test_codes_are_uppercase(self):
|
|
for code in BUNDESLAENDER:
|
|
assert code.isupper(), f"{code} is not uppercase"
|
|
|
|
def test_each_entry_has_naechste_wahl_or_none(self):
|
|
for code, bl in BUNDESLAENDER.items():
|
|
assert bl.naechste_wahl is None or len(bl.naechste_wahl) == 10
|
|
|
|
def test_wahlperiode_is_positive_integer(self):
|
|
for bl in BUNDESLAENDER.values():
|
|
assert isinstance(bl.wahlperiode, int) and bl.wahlperiode > 0
|
|
|
|
|
|
class TestActiveBundeslaender:
|
|
def test_active_bundeslaender_include_phase_1_set(self):
|
|
"""At least the original four (NRW, LSA, MV, BE) plus any
|
|
Phase-1 additions (BW after #29) must be active. The test
|
|
avoids hardcoding the exact count so adding a new active
|
|
Bundesland in a follow-up doesn't break this case."""
|
|
active_codes = {bl.code for bl in aktive_bundeslaender()}
|
|
original = {"NRW", "LSA", "MV", "BE"}
|
|
assert original <= active_codes
|
|
|
|
def test_alle_bundeslaender_returns_all(self):
|
|
# 16 BL + BUND
|
|
assert len(alle_bundeslaender()) == 17
|
|
|
|
def test_alle_bundeslaender_active_first(self):
|
|
out = alle_bundeslaender()
|
|
active_codes = {bl.code for bl in aktive_bundeslaender()}
|
|
# The first len(active) entries must all be active
|
|
for bl in out[: len(active_codes)]:
|
|
assert bl.code in active_codes
|
|
|
|
|
|
class TestGetHelper:
|
|
def test_returns_bundesland_for_known_code(self):
|
|
bl = get("NRW")
|
|
assert bl is not None
|
|
assert bl.name == "Nordrhein-Westfalen"
|
|
|
|
def test_returns_none_for_unknown_code(self):
|
|
assert get("XX") is None
|
|
|
|
|
|
class TestClassificationFix48:
|
|
"""Regression: #48 corrected three doku_system entries that the
|
|
follow-up adapter issues depend on."""
|
|
|
|
def test_th_is_parldok_not_starweb(self):
|
|
assert BUNDESLAENDER["TH"].doku_system == "ParlDok"
|
|
|
|
def test_hb_is_starweb_not_paris(self):
|
|
"""PARiS is just a StarWeb skin — must be classified as StarWeb."""
|
|
assert BUNDESLAENDER["HB"].doku_system == "StarWeb"
|
|
|
|
def test_sn_is_eigensystem_not_parldok(self):
|
|
"""EDAS is ASP.NET-Webforms, NOT ParlDok-compatible with MV."""
|
|
assert BUNDESLAENDER["SN"].doku_system == "Eigensystem"
|
|
|
|
|
|
class TestWahltermineSane:
|
|
"""All upcoming elections must be in chronological order and in the
|
|
near future (sanity check that someone has not pasted a 1990 date)."""
|
|
|
|
def test_no_election_before_2026(self):
|
|
for bl in BUNDESLAENDER.values():
|
|
if bl.naechste_wahl:
|
|
assert bl.naechste_wahl >= "2026-01-01"
|
|
|
|
def test_no_election_after_2035(self):
|
|
for bl in BUNDESLAENDER.values():
|
|
if bl.naechste_wahl:
|
|
assert bl.naechste_wahl < "2035-01-01"
|