gwoe-antragspruefer/tests/test_protokoll_parsers_bb.py
Dotty Dotter 33bb564ed1 feat(#149): BB-Parser produktiv — Brandenburger Plenarprotokolle (Status-Only)
URL-Pattern verifiziert WP8 Sitzung 22:
https://www.parlamentsdokumentation.brandenburg.de/starweb/LBB/ELVIS/parladoku/w8/plpr/{n}.pdf

**Wichtig:** parladoku-PDF-URL liefert 403 ohne Cookie-Session. Erst
GET auf portal/browse.tt.html?wp=8 zur Cookie-Akquise, dann mit
gesetztem Cookie die PDF-URL aufrufen. Ingest-Cron implementiert
diesen Flow per http.cookiejar.CookieJar in Python.

Anchor-Pattern (NRW-aehnlich):
- "Damit ist [Subj] (mehrheitlich|einstimmig)? (angenommen|abgelehnt|ueberwiesen)"
- Drucksachen-Lookup: Drucksache 8/N rueckwaerts vom Anchor

Vote-Style: Handzeichen-only (kein Fraktionen-Listing). Daher
Vote-Listen leer; einstimmig=True setzt JA=alle WP8-Fraktionen
(SPD, AfD, CDU, BSW, GRÜNE).

Tests: 14 BB-Tests, Verifikation S22 → 26 Vote-Anchors extrahiert.
Stand: 10 produktive Parser
(NRW, BUND, BE, HH, TH, HE, SH, HB, SL, BB).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-29 02:04:21 +02:00

84 lines
2.7 KiB
Python

"""Tests fuer app/protokoll_parsers/bb.py — BB Plenarprotokoll-Parser (#149).
Stichprobe-getestet gegen WP8 Sitzung 22 (Brandenburg).
"""
from __future__ import annotations
import pytest
from app.protokoll_parsers.bb import (
_normalize_text,
_resolve_drucksache_bb,
RESULT_ANCHOR_RE,
DS_RE_BB,
ALLE_FRAKTIONEN_BB,
)
class TestNormalizeText:
def test_collapses_whitespace(self):
assert _normalize_text("a b\n\tc") == "a b c"
def test_repairs_soft_hyphenation(self):
assert _normalize_text("zustim- mt") == "zustimmt"
class TestResultAnchorRegex:
def test_matches_mehrheitlich_abgelehnt(self):
m = RESULT_ANCHOR_RE.search("Damit ist der Antrag mehrheitlich abgelehnt.")
assert m
assert m.group("modus") == "mehrheitlich"
assert m.group("ergebnis") == "abgelehnt"
def test_matches_einstimmig_angenommen(self):
m = RESULT_ANCHOR_RE.search("Damit ist der Antrag einstimmig angenommen.")
assert m
assert m.group("modus") == "einstimmig"
def test_matches_entschliessungsantrag(self):
m = RESULT_ANCHOR_RE.search(
"Damit ist der Entschließungsantrag mehrheitlich abgelehnt."
)
assert m and m.group("subject") == "Entschließungsantrag"
def test_matches_beschlussempfehlung(self):
m = RESULT_ANCHOR_RE.search(
"Damit sind die Beschlussempfehlung mit Enthaltungen angenommen worden."
)
assert m and m.group("subject") == "Beschlussempfehlung"
def test_no_match_random(self):
m = RESULT_ANCHOR_RE.search("Der Antrag wurde abgelehnt.")
assert m is None
class TestDrucksacheRegex:
def test_matches_drucksache_8(self):
m = DS_RE_BB.search("Drucksache 8/2054")
assert m and m.group(1) == "2054"
def test_only_wp8_matches(self):
# Drucksache 7/123 sollte nicht matchen (anderer WP)
assert DS_RE_BB.search("Drucksache 7/123") is None
class TestResolveDrucksacheBb:
def test_finds_drucksache_before_anchor(self):
text = "Drucksache 8/2054 ... Damit ist der Antrag abgelehnt."
anchor = text.index("Damit")
assert _resolve_drucksache_bb(text, anchor) == "8/2054"
def test_picks_most_recent_drucksache(self):
text = "Drucksache 8/1000 ... Drucksache 8/2000 ... Damit ist abgelehnt."
anchor = text.index("Damit")
assert _resolve_drucksache_bb(text, anchor) == "8/2000"
def test_returns_none_when_no_ds(self):
assert _resolve_drucksache_bb("Damit ist abgelehnt.", 0) is None
class TestConstants:
def test_all_fraktionen_set(self):
# WP8-BB Konstellation: SPD-BSW Koalition + AfD/CDU/GRÜNE Opposition
assert set(ALLE_FRAKTIONEN_BB) == {"SPD", "AfD", "CDU", "BSW", "GRÜNE"}