From a173182158c6904fb8381004a3c85a64cecfa870 Mon Sep 17 00:00:00 2001 From: timmy Date: Thu, 6 Aug 2026 01:18:41 +0000 Subject: [PATCH] fix: bound event stream refresh latency (#58) --- src/main.py | 23 +++++++++++++++++++++-- tests/test_event_stream.py | 25 +++++++++++++++++++++++++ 2 files changed, 46 insertions(+), 2 deletions(-) diff --git a/src/main.py b/src/main.py index b9851c3..6082fda 100644 --- a/src/main.py +++ b/src/main.py @@ -14,6 +14,7 @@ from src.views import router as frontend_router app = FastAPI(title="Stackchain Dashboard") CONTEXT_TIMEOUT_SECONDS = 5.0 +EVENT_STREAM_TIMEOUT_SECONDS = 5.0 FRONTEND_DIR = Path(__file__).resolve().parent.parent / "frontend" app.add_middleware( @@ -103,5 +104,23 @@ async def context() -> JSONResponse: @app.get("/api/v1/events") -async def event_stream() -> list[dict]: - return await activity_events() +async def event_stream(): + try: + return await asyncio.wait_for( + activity_events(), timeout=EVENT_STREAM_TIMEOUT_SECONDS + ) + except TimeoutError: + return JSONResponse( + { + "error": ( + "Gitea event stream request timed out after " + f"{EVENT_STREAM_TIMEOUT_SECONDS:g}s" + ) + }, + status_code=503, + headers={ + "Retry-After": str( + max(1, math.ceil(EVENT_STREAM_TIMEOUT_SECONDS)) + ) + }, + ) diff --git a/tests/test_event_stream.py b/tests/test_event_stream.py index 9c2a8c8..a369485 100644 --- a/tests/test_event_stream.py +++ b/tests/test_event_stream.py @@ -1,3 +1,5 @@ +import asyncio +import json from pathlib import Path import pytest @@ -49,6 +51,29 @@ async def test_event_stream_returns_recent_authenticated_gitea_activity(monkeypa assert response == expected +@pytest.mark.anyio +async def test_event_stream_returns_retryable_response_when_gitea_exceeds_deadline(monkeypatch): + cancelled = asyncio.Event() + + async def hanging_activity_events(): + try: + await asyncio.Event().wait() + finally: + cancelled.set() + + monkeypatch.setattr(main, "EVENT_STREAM_TIMEOUT_SECONDS", 0.01) + monkeypatch.setattr(main, "activity_events", hanging_activity_events) + + response = await main.event_stream() + + assert response.status_code == 503 + assert response.headers["retry-after"] == "1" + assert json.loads(response.body) == { + "error": "Gitea event stream request timed out after 0.01s" + } + assert cancelled.is_set() + + @pytest.mark.anyio async def test_activity_events_fetches_feed_for_authenticated_user(monkeypatch): paths = []