136 lines
4.5 KiB
Python
136 lines
4.5 KiB
Python
|
|
"""Tests fuer app/protokoll_parsers/hh.py — Hamburger Beschlussprotokoll-Parser (#155).
|
|||
|
|
|
|||
|
|
Stichprobe-getestet gegen WP23 Sitzung 22 (Hamburg).
|
|||
|
|
"""
|
|||
|
|
from __future__ import annotations
|
|||
|
|
|
|||
|
|
import pytest
|
|||
|
|
|
|||
|
|
from app.protokoll_parsers.hh import (
|
|||
|
|
_normalize_fraktionen_hh,
|
|||
|
|
_parse_vote_block_hh,
|
|||
|
|
_resolve_drucksache_hh,
|
|||
|
|
RESULT_ANCHOR_RE,
|
|||
|
|
ALLE_FRAKTIONEN_HH,
|
|||
|
|
FRAKTIONEN_MAP_HH,
|
|||
|
|
)
|
|||
|
|
|
|||
|
|
|
|||
|
|
class TestNormalizeFraktionenHh:
|
|||
|
|
def test_simple_spd(self):
|
|||
|
|
assert _normalize_fraktionen_hh("der SPD") == ["SPD"]
|
|||
|
|
|
|||
|
|
def test_gruene_n_form(self):
|
|||
|
|
assert _normalize_fraktionen_hh("der GRÜNEN") == ["GRÜNE"]
|
|||
|
|
|
|||
|
|
def test_die_linke(self):
|
|||
|
|
assert _normalize_fraktionen_hh("der Linken") == ["LINKE"]
|
|||
|
|
|
|||
|
|
def test_combined_phrase(self):
|
|||
|
|
result = _normalize_fraktionen_hh(
|
|||
|
|
"der SPD und GRÜNEN gegen die Stimmen der CDU und AfD"
|
|||
|
|
)
|
|||
|
|
assert set(result) == {"SPD", "GRÜNE", "CDU", "AfD"}
|
|||
|
|
|
|||
|
|
def test_empty_returns_empty(self):
|
|||
|
|
assert _normalize_fraktionen_hh("") == []
|
|||
|
|
|
|||
|
|
def test_no_double_count(self):
|
|||
|
|
# 'GRÜNEN' und 'GRÜNE' beide → nur 1× GRÜNE
|
|||
|
|
result = _normalize_fraktionen_hh("der GRÜNE und der GRÜNEN")
|
|||
|
|
assert result.count("GRÜNE") == 1
|
|||
|
|
|
|||
|
|
|
|||
|
|
class TestParseVoteBlockHh:
|
|||
|
|
def test_full_block(self):
|
|||
|
|
block = (
|
|||
|
|
" mit den Stimmen der SPD und GRÜNEN "
|
|||
|
|
"gegen die Stimmen der CDU und AfD "
|
|||
|
|
"bei Enthaltung der Linken"
|
|||
|
|
)
|
|||
|
|
votes = _parse_vote_block_hh(block)
|
|||
|
|
assert set(votes["ja"]) == {"SPD", "GRÜNE"}
|
|||
|
|
assert set(votes["nein"]) == {"CDU", "AfD"}
|
|||
|
|
assert votes["enthaltung"] == ["LINKE"]
|
|||
|
|
|
|||
|
|
def test_only_ja_and_nein(self):
|
|||
|
|
block = (
|
|||
|
|
" mit den Stimmen der SPD, GRÜNEN, CDU und Linken "
|
|||
|
|
"gegen die Stimmen der AfD"
|
|||
|
|
)
|
|||
|
|
votes = _parse_vote_block_hh(block)
|
|||
|
|
assert set(votes["ja"]) == {"SPD", "GRÜNE", "CDU", "LINKE"}
|
|||
|
|
assert votes["nein"] == ["AfD"]
|
|||
|
|
assert votes["enthaltung"] == []
|
|||
|
|
|
|||
|
|
def test_empty_block_returns_empty_lists(self):
|
|||
|
|
votes = _parse_vote_block_hh("")
|
|||
|
|
assert votes == {"ja": [], "nein": [], "enthaltung": []}
|
|||
|
|
|
|||
|
|
def test_only_ja(self):
|
|||
|
|
block = " mit den Stimmen der SPD und GRÜNEN"
|
|||
|
|
votes = _parse_vote_block_hh(block)
|
|||
|
|
assert set(votes["ja"]) == {"SPD", "GRÜNE"}
|
|||
|
|
|
|||
|
|
|
|||
|
|
class TestResolveDrucksacheHh:
|
|||
|
|
def test_finds_ds_with_full_prefix(self):
|
|||
|
|
text = "Drucksache 23/1234 ... mehrheitlich angenommen"
|
|||
|
|
anchor = text.index("mehrheitlich")
|
|||
|
|
assert _resolve_drucksache_hh(text, anchor) == "23/1234"
|
|||
|
|
|
|||
|
|
def test_finds_bare_ds_format(self):
|
|||
|
|
text = "Bericht 23/3666 ... einstimmig angenommen"
|
|||
|
|
anchor = text.index("einstimmig")
|
|||
|
|
assert _resolve_drucksache_hh(text, anchor) == "23/3666"
|
|||
|
|
|
|||
|
|
def test_picks_closest(self):
|
|||
|
|
text = "Drucksache 23/100 ... Drucksache 23/200 ... einstimmig angenommen"
|
|||
|
|
anchor = text.index("einstimmig")
|
|||
|
|
assert _resolve_drucksache_hh(text, anchor) == "23/200"
|
|||
|
|
|
|||
|
|
def test_returns_none_when_no_ds(self):
|
|||
|
|
text = "irgendwas ... einstimmig angenommen"
|
|||
|
|
anchor = text.index("einstimmig")
|
|||
|
|
assert _resolve_drucksache_hh(text, anchor) is None
|
|||
|
|
|
|||
|
|
|
|||
|
|
class TestResultAnchorRegex:
|
|||
|
|
def test_einstimmig_angenommen(self):
|
|||
|
|
text = "alle Empfehlungen einstimmig angenommen"
|
|||
|
|
m = RESULT_ANCHOR_RE.search(text)
|
|||
|
|
assert m
|
|||
|
|
assert m.group("modus") == "einstimmig"
|
|||
|
|
assert m.group("ergebnis") == "angenommen"
|
|||
|
|
|
|||
|
|
def test_mehrheitlich_with_block(self):
|
|||
|
|
text = (
|
|||
|
|
"Antrag mehrheitlich mit den Stimmen der SPD und GRÜNEN "
|
|||
|
|
"gegen die Stimmen der CDU und AfD angenommen"
|
|||
|
|
)
|
|||
|
|
m = RESULT_ANCHOR_RE.search(text)
|
|||
|
|
assert m
|
|||
|
|
assert m.group("modus") == "mehrheitlich"
|
|||
|
|
assert m.group("ergebnis") == "angenommen"
|
|||
|
|
|
|||
|
|
def test_mehrheitlich_abgelehnt(self):
|
|||
|
|
text = (
|
|||
|
|
"Antrag mehrheitlich mit den Stimmen der SPD, GRÜNEN gegen die "
|
|||
|
|
"Stimmen der AfD abgelehnt"
|
|||
|
|
)
|
|||
|
|
m = RESULT_ANCHOR_RE.search(text)
|
|||
|
|
assert m
|
|||
|
|
assert m.group("ergebnis") == "abgelehnt"
|
|||
|
|
|
|||
|
|
|
|||
|
|
class TestConstants:
|
|||
|
|
def test_all_fraktionen_complete(self):
|
|||
|
|
assert set(ALLE_FRAKTIONEN_HH) == {"SPD", "GRÜNE", "CDU", "AfD", "LINKE"}
|
|||
|
|
|
|||
|
|
def test_mapping_covers_all_fraktionen(self):
|
|||
|
|
all_codes = set()
|
|||
|
|
for _phrase, codes in FRAKTIONEN_MAP_HH:
|
|||
|
|
all_codes.update(codes)
|
|||
|
|
for f in ALLE_FRAKTIONEN_HH:
|
|||
|
|
assert f in all_codes
|