From 286680c05342edf5a1a7207535d113d4f678449c Mon Sep 17 00:00:00 2001 From: timmy Date: Wed, 5 Aug 2026 08:02:39 +0000 Subject: [PATCH] fix: bound context fallback latency (#30) --- src/main.py | 18 +++++++++++++----- tests/test_context_timeout.py | 32 ++++++++++++++++++++++++++++++++ 2 files changed, 45 insertions(+), 5 deletions(-) create mode 100644 tests/test_context_timeout.py diff --git a/src/main.py b/src/main.py index c07ffb2..ec3a4b6 100644 --- a/src/main.py +++ b/src/main.py @@ -1,3 +1,5 @@ +import asyncio + from fastapi import FastAPI from fastapi.middleware.cors import CORSMiddleware from fastapi.responses import JSONResponse @@ -9,6 +11,7 @@ from src.suggestion_engine import compute from src.views import router as frontend_router app = FastAPI(title="Stackchain Dashboard") +CONTEXT_TIMEOUT_SECONDS = 5.0 app.add_middleware( CORSMiddleware, @@ -30,11 +33,16 @@ def health() -> dict[str, str]: @app.get("/api/v1/context") async def context() -> JSONResponse: try: - user_data = await current_user() - repo_data = await repos() - issues_data = await issues() - prs_data = await pull_requests() + user_data, repo_data, issues_data, prs_data = await asyncio.wait_for( + asyncio.gather(current_user(), repos(), issues(), pull_requests()), + timeout=CONTEXT_TIMEOUT_SECONDS, + ) except Exception as e: + error_message = ( + f"Gitea context request timed out after {CONTEXT_TIMEOUT_SECONDS:g}s" + if isinstance(e, TimeoutError) + else str(e) + ) return JSONResponse({ "user": {"id": None, "login": "timmy", "full_name": "Timmy Jr", "email": ""}, "repos": [], @@ -45,7 +53,7 @@ async def context() -> JSONResponse: {"panel": "auth", "action": "connect", "target": "stackchain-dashboard backend", "priority": "high"}, {"panel": "gitea", "action": "set GITEA_URL + GITEA_TOKEN", "target": "/root/stackchain-dashboard/.env or systemd env", "priority": "high"}, ], - "error": str(e), + "error": error_message, }) user_model = User(id=user_data["id"], login=user_data["login"], full_name=user_data.get("full_name", ""), email=user_data.get("email", "")) repo_models = [ diff --git a/tests/test_context_timeout.py b/tests/test_context_timeout.py new file mode 100644 index 0000000..0319f38 --- /dev/null +++ b/tests/test_context_timeout.py @@ -0,0 +1,32 @@ +import asyncio + +import pytest + +from src import main + + +@pytest.mark.anyio +async def test_context_returns_fallback_when_gitea_exceeds_deadline(monkeypatch): + cancelled = asyncio.Event() + + async def hanging_current_user(): + try: + await asyncio.Event().wait() + finally: + cancelled.set() + + async def empty_collection(): + return [] + + monkeypatch.setattr(main, "CONTEXT_TIMEOUT_SECONDS", 0.01) + monkeypatch.setattr(main, "current_user", hanging_current_user) + monkeypatch.setattr(main, "repos", empty_collection) + monkeypatch.setattr(main, "issues", empty_collection) + monkeypatch.setattr(main, "pull_requests", empty_collection) + + response = await main.context() + + assert response.status_code == 200 + assert b'"repos":[]' in response.body + assert b'"error":"Gitea context request timed out after 0.01s"' in response.body + assert cancelled.is_set() \ No newline at end of file -- 2.43.0