diff --git a/src/main.py b/src/main.py index 13f74da..9919996 100644 --- a/src/main.py +++ b/src/main.py @@ -22,6 +22,10 @@ FRONTEND_DIR = Path(__file__).resolve().parent.parent / "frontend" class ContextPayloadError(ValueError): """Raised when Gitea returns a structurally invalid context payload.""" + +class ReadinessPayloadError(ValueError): + """Raised when Gitea returns a structurally invalid readiness payload.""" + app.add_middleware( CORSMiddleware, allow_origins=["*"], @@ -56,13 +60,19 @@ async def readiness(): current_user(), timeout=READINESS_TIMEOUT_SECONDS ) if not isinstance(user, dict) or not user.get("login"): - raise ValueError("Gitea current-user response did not include a login") + raise ReadinessPayloadError( + "Gitea current-user response did not include a login" + ) except Exception as exc: timed_out = isinstance(exc, TimeoutError) error_message = ( f"Gitea readiness check timed out after {READINESS_TIMEOUT_SECONDS:g}s" if timed_out - else str(exc) + else ( + str(exc) + if isinstance(exc, ReadinessPayloadError) + else "Gitea readiness check is temporarily unavailable" + ) ) return JSONResponse( { diff --git a/tests/test_health.py b/tests/test_health.py index db465d8..d8c5a97 100644 --- a/tests/test_health.py +++ b/tests/test_health.py @@ -29,17 +29,21 @@ async def test_readiness_endpoint_reports_connected_gitea_user(monkeypatch): @pytest.mark.anyio -async def test_readiness_endpoint_returns_503_when_gitea_is_unavailable(monkeypatch): +async def test_readiness_endpoint_hides_unexpected_upstream_error_details(monkeypatch): async def unavailable_user(): - raise RuntimeError("connection refused") + raise RuntimeError("secret internal upstream detail") monkeypatch.setattr(main, "current_user", unavailable_user) response = await main.readiness() assert response.status_code == 503 - assert b'"status":"not_ready"' in response.body - assert b'"error":"connection refused"' in response.body + assert json.loads(response.body) == { + "status": "not_ready", + "service": "stackchain-dashboard", + "error": "Gitea readiness check is temporarily unavailable", + } + assert b"secret internal upstream detail" not in response.body @pytest.mark.anyio