test: 11 weitere Smoketests fuer Stimmverhalten-Endpoints

Smoketests fuer alle 7 Stimmverhalten-Aggregat-Endpoints (stimm-index,
heuchelei, empfehlungs-konsistenz, pro-wert, cross-bl, zeitreihe) plus
zwei CSV-Tests (Header-Spalten + konsistente Datenzeilen-Spaltenzahl).

Refs: ADR 0010

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Dotty Dotter 2026-05-06 15:45:59 +02:00
parent 9498ca4b97
commit a8f85bf3ee

View File

@ -234,3 +234,94 @@ class TestAdminStandAuth:
def test_api_unauthenticated_rejected(self): def test_api_unauthenticated_rejected(self):
resp = client.get("/api/admin/stand") resp = client.get("/api/admin/stand")
assert resp.status_code in (401, 403, 307, 302) assert resp.status_code in (401, 403, 307, 302)
class TestStimmverhaltenAggregateEndpoints:
"""Die 7 Stimmverhalten-Aggregat-Endpoints liefern strukturierte JSONs (siehe ADR 0010)."""
def test_stimm_index_returns_fraktionen(self):
resp = client.get("/api/auswertungen/stimm-index")
assert resp.status_code == 200
data = resp.json()
assert "fraktionen" in data
assert isinstance(data["fraktionen"], list)
assert "n_assessments_matched" in data
def test_stimm_index_min_n_filter(self):
resp = client.get("/api/auswertungen/stimm-index?min_n=1")
assert resp.status_code == 200
data = resp.json()
assert data["filter"]["min_n"] == 1
def test_stimm_index_exclude_antragsteller_default(self):
"""Default `exclude_antragsteller=True`."""
resp = client.get("/api/auswertungen/stimm-index")
data = resp.json()
assert data["filter"]["exclude_antragsteller"] in (True, "true", 1)
def test_heuchelei_returns_fraktionen(self):
resp = client.get("/api/auswertungen/heuchelei")
assert resp.status_code == 200
data = resp.json()
assert "fraktionen" in data
def test_empfehlungs_konsistenz(self):
resp = client.get("/api/auswertungen/empfehlungs-konsistenz")
assert resp.status_code == 200
data = resp.json()
assert "fraktionen" in data or "items" in data
def test_stimm_index_pro_wert_5_werte(self):
resp = client.get("/api/auswertungen/stimm-index-pro-wert")
assert resp.status_code == 200
data = resp.json()
assert "werte" in data
# 5 GWÖ-Werte: Würde, Solidarität, Nachhaltigkeit, Gerechtigkeit, Demokratie
assert len(data["werte"]) == 5
def test_stimm_index_cross_bl(self):
resp = client.get("/api/auswertungen/stimm-index-cross-bl")
assert resp.status_code == 200
data = resp.json()
assert "fraktionen" in data
assert "bundeslaender" in data
assert "cells" in data
def test_stimm_index_zeitreihe(self):
resp = client.get("/api/auswertungen/stimm-index-zeitreihe")
assert resp.status_code == 200
data = resp.json()
# Erwartete Felder: buckets (Zeit-Achse), fraktionen, series
assert "buckets" in data
assert "fraktionen" in data
assert "series" in data
class TestStimmverhaltenCsvExport:
"""CSV-Export Long-Format aus #168."""
def test_csv_returns_text(self):
resp = client.get("/api/auswertungen/stimmverhalten.csv")
assert resp.status_code == 200
ctype = resp.headers.get("content-type", "")
assert "text/csv" in ctype or "text/plain" in ctype
def test_csv_has_header_row(self):
resp = client.get("/api/auswertungen/stimmverhalten.csv")
body = resp.text
# Header-Zeile mit den erwarteten Long-Format-Spalten
first_line = body.split("\n", 1)[0]
# Mindestens drei Pflicht-Spalten erwartet — Partei statt Fraktion in CSV
for required in ("drucksache", "bundesland", "partei", "vote"):
assert required in first_line, f"Spalte {required!r} fehlt im Header: {first_line!r}"
def test_csv_data_rows_have_consistent_columns(self):
"""Datenzeilen haben gleiche Spalten-Anzahl wie der Header."""
resp = client.get("/api/auswertungen/stimmverhalten.csv")
body = resp.text.strip()
if not body:
pytest.skip("kein CSV-Body — DB enthält evtl. keine Vote-Matches")
lines = body.split("\n")
n_cols = lines[0].count(",") + 1
for line in lines[1:6]: # Samples
assert line.count(",") + 1 == n_cols, f"Spaltenzahl-Mismatch: {line!r}"