From 09c29cac69fe00e398108dbf0784a5e3ab673bed Mon Sep 17 00:00:00 2001 From: Dotty Dotter Date: Tue, 28 Apr 2026 01:46:35 +0200 Subject: [PATCH] fix(#142): SL HTTP 5xx als Fehler raisen statt return [] MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Symptom: Monitoring-Scan zeigte bei SL seen=0 errors=OK, obwohl der Umbraco-Backend HTTP 500 zurueckgab. Im _post_search wurde 5xx via 'logger.error + return []' geschluckt, sodass der Monitoring-Layer die Fehlerursache nicht in monitoring_daily_summary persistierte. Fix: bei resp.status_code != 200 httpx.HTTPStatusError raisen — das propagiert durch search() ueber _search_adapter ins outer except in daily_scan, das den Fehlertext in summary.errors schreibt. Regression-Test test_search_propagates_http_500. Closes #142 --- app/parlamente.py | 5 ++++- tests/test_parlamente.py | 24 ++++++++++++++++++++++++ 2 files changed, 28 insertions(+), 1 deletion(-) diff --git a/app/parlamente.py b/app/parlamente.py index 974a514..2f92544 100644 --- a/app/parlamente.py +++ b/app/parlamente.py @@ -3206,7 +3206,10 @@ class SaarlandAdapter(ParlamentAdapter): ) if resp.status_code != 200: logger.error("SL HTTP %s: %s", resp.status_code, resp.text[:200]) - return [] + raise httpx.HTTPStatusError( + f"SL HTTP {resp.status_code}", + request=resp.request, response=resp, + ) data = resp.json() return data.get("FilteredResult", []) or [] except Exception: diff --git a/tests/test_parlamente.py b/tests/test_parlamente.py index 1b181cb..a9d54c3 100644 --- a/tests/test_parlamente.py +++ b/tests/test_parlamente.py @@ -576,3 +576,27 @@ class TestSaarlandSearchPropagatesErrors: with pytest.raises(httpx.ConnectError): asyncio.run(_run()) + + def test_search_propagates_http_500(self): + """HTTP 5xx response must NOT be silently turned into empty results + (regression #142): a 500 from the Umbraco backend used to log+return + [], hiding it from the monitoring summary.""" + import httpx + from app.parlamente import SaarlandAdapter + + adapter = SaarlandAdapter() + + async def _run(): + mock_client = AsyncMock() + mock_client.__aenter__ = AsyncMock(return_value=mock_client) + mock_client.__aexit__ = AsyncMock(return_value=False) + mock_resp = MagicMock() + mock_resp.status_code = 500 + mock_resp.text = "Server Error" + mock_resp.request = MagicMock() + mock_client.post = AsyncMock(return_value=mock_resp) + with patch.object(adapter, "_make_client", return_value=mock_client): + await adapter.search("Schule") + + with pytest.raises(httpx.HTTPStatusError): + asyncio.run(_run())