gwoe-antragspruefer/tests/test_endpoints_smoke.py
Dotty Dotter b6b0ce752a feat(#170): sparsame **fett**-Hervorhebungen + Smoke-Tests fuer Histogram/Stand
PM-Prompt erlaubt nun max. eine Markdown-Bold-Markierung pro Absatz
(Schluessel-Zahl/Effekt). Force-Regen-Test bestaetigt: qwen-max liefert
**30 %** wie im Beispiel; renderPmBody im Frontend rendert das als
<strong>. Smoketests gegen die neuen Endpoints (score-histogram x4,
admin/stand x2 Auth-Walls) absichern Regressionen.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-06 09:32:54 +02:00

237 lines
7.5 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"""Smoke-Tests für die neuen API-Endpoints.
Diese Tests prüfen nur ob die Endpoints antworten und das richtige
Format zurückgeben — keine echten LLM-Calls oder DB-Writes.
"""
import pytest
# Skip wenn die App nicht importierbar ist (missing dependencies lokal)
try:
from fastapi.testclient import TestClient
from app.main import app
client = TestClient(app)
_HAS_APP = True
except ImportError:
_HAS_APP = False
client = None
pytestmark = pytest.mark.skipif(not _HAS_APP, reason="app.main not importable")
class TestQueueStatus:
def test_returns_json(self):
resp = client.get("/api/queue/status")
assert resp.status_code == 200
data = resp.json()
assert "pending" in data
assert "worker_running" in data
def test_pending_starts_at_zero(self):
data = client.get("/api/queue/status").json()
assert data["pending"] == 0
class TestAuthMe:
def test_unauthenticated_returns_false(self):
resp = client.get("/api/auth/me")
assert resp.status_code == 200
data = resp.json()
assert data["authenticated"] is False
class TestAuthLoginUrl:
def test_returns_enabled_flag(self):
resp = client.get("/api/auth/login-url")
assert resp.status_code == 200
data = resp.json()
assert "enabled" in data
class TestBundeslaender:
def test_returns_list(self):
resp = client.get("/api/bundeslaender")
assert resp.status_code == 200
data = resp.json()
assert isinstance(data, list)
assert len(data) > 10 # mindestens 10 BLs
class TestProgramme:
def test_returns_list(self):
resp = client.get("/api/programme")
assert resp.status_code == 200
data = resp.json()
assert isinstance(data, list)
assert len(data) > 50 # mindestens 50 Programme
def test_status_returns_indexed_count(self):
resp = client.get("/api/programme/status")
assert resp.status_code == 200
data = resp.json()
assert "indexed" in data
assert "total" in data
class TestHealth:
def test_health(self):
resp = client.get("/health")
assert resp.status_code == 200
class TestVoteOrphansEndpoint:
"""GET /api/auswertungen/vote-orphans (öffentlich)."""
def test_returns_json_structure(self):
resp = client.get("/api/auswertungen/vote-orphans?limit=5")
assert resp.status_code == 200
data = resp.json()
assert "count" in data
assert "items" in data
assert "by_bundesland" in data
assert isinstance(data["items"], list)
def test_filter_bundesland_param(self):
resp = client.get("/api/auswertungen/vote-orphans?bundesland=NRW&limit=3")
assert resp.status_code == 200
data = resp.json()
# Wenn items vorhanden, alle aus NRW
for it in data["items"]:
assert it["bundesland"] == "NRW"
class TestVoteOrphansAutoRateAuth:
"""POST /api/auswertungen/vote-orphans/auto-rate erfordert Admin."""
def test_unauthenticated_rejected(self):
resp = client.post(
"/api/auswertungen/vote-orphans/auto-rate",
data={"limit": 5},
)
# Auth-Wall greift entweder direkt 401, 403 oder Redirect (307/302)
assert resp.status_code in (401, 403, 307, 302)
class TestBatchAnalyzeAuth:
"""POST /api/batch-analyze erfordert Admin."""
def test_unauthenticated_rejected(self):
resp = client.post(
"/api/batch-analyze",
data={"bundesland": "NRW", "limit": 5},
)
assert resp.status_code in (401, 403, 307, 302)
def test_all_bl_unauthenticated_also_rejected(self):
resp = client.post(
"/api/batch-analyze",
data={"bundesland": "ALL", "limit": 10},
)
assert resp.status_code in (401, 403, 307, 302)
class TestAktuelleThemenEndpoints:
"""GET /api/aktuelle-themen/* sind oeffentlich."""
def test_top_returns_buckets(self):
resp = client.get("/api/aktuelle-themen/top?days=7&top_k=3")
assert resp.status_code == 200
data = resp.json()
assert "buckets" in data
assert "n_total_news" in data
assert "filter" in data
def test_top_with_single_date(self):
resp = client.get("/api/aktuelle-themen/top?date=2026-05-01")
assert resp.status_code == 200
data = resp.json()
assert data["filter"]["single_date"] == "2026-05-01"
def test_top_with_only_relevant(self):
resp = client.get("/api/aktuelle-themen/top?only_relevant=true&top_k=5")
assert resp.status_code == 200
data = resp.json()
assert data["filter"]["only_relevant"] is True
def test_zeitreihe(self):
resp = client.get("/api/aktuelle-themen/zeitreihe?days=14")
assert resp.status_code == 200
data = resp.json()
assert "buckets" in data
assert "sources" in data
assert "series" in data
def test_top_antraege(self):
resp = client.get("/api/aktuelle-themen/top-antraege?min_gwoe_score=8.0")
assert resp.status_code == 200
data = resp.json()
assert "antraege" in data
def test_cluster(self):
resp = client.get("/api/aktuelle-themen/cluster?days=7")
assert resp.status_code == 200
data = resp.json()
assert "clusters" in data
def test_drafts_list(self):
resp = client.get("/api/aktuelle-themen/drafts?limit=5")
assert resp.status_code == 200
data = resp.json()
assert "drafts" in data
def test_drafts_versions(self):
resp = client.get(
"/api/aktuelle-themen/drafts-versions"
"?drucksache=missing&news_url=https://example.com/x"
)
assert resp.status_code == 200
data = resp.json()
assert "versions" in data
assert isinstance(data["versions"], list)
class TestScoreHistogramEndpoint:
"""GET /api/auswertungen/score-histogram (öffentlich)."""
def test_returns_11_buckets(self):
resp = client.get("/api/auswertungen/score-histogram")
assert resp.status_code == 200
data = resp.json()
# 11 Buckets: 01, 12, ..., 1011
assert len(data["buckets"]) == 11
for b in data["buckets"]:
assert "score_min" in b
assert "score_max" in b
assert "count" in b
assert b["count"] >= 0
def test_total_matches_sum_of_buckets(self):
resp = client.get("/api/auswertungen/score-histogram")
data = resp.json()
total_sum = sum(b["count"] for b in data["buckets"])
assert data["total"] == total_sum
def test_filter_bundesland(self):
resp = client.get("/api/auswertungen/score-histogram?bundesland=NRW")
assert resp.status_code == 200
data = resp.json()
assert data["filter"]["bundesland"] == "NRW"
def test_filter_wahlperiode(self):
resp = client.get("/api/auswertungen/score-histogram?wahlperiode=NRW-WP18")
assert resp.status_code == 200
data = resp.json()
assert data["filter"]["wahlperiode"] == "NRW-WP18"
class TestAdminStandAuth:
"""/v2/admin/stand + /api/admin/stand erfordern Admin."""
def test_page_unauthenticated_rejected(self):
resp = client.get("/v2/admin/stand")
assert resp.status_code in (401, 403, 307, 302)
def test_api_unauthenticated_rejected(self):
resp = client.get("/api/admin/stand")
assert resp.status_code in (401, 403, 307, 302)