import asyncio import httpx import pytest from src import main @pytest.mark.anyio async def test_notification_detail_api_is_bounded_and_never_cacheable(monkeypatch): calls = [] async def detail(thread_id): calls.append(thread_id) return { "id": thread_id, "repository": "stackchain/api", "title": "Retry failed deploy", "subject_type": "Issue", "state": "open", "url": "https://forge.example/stackchain/api/issues/7#issuecomment-9", "subject_body": "Deploy fails after three retries.", "latest_comment": { "author": "alexander", "body": "Logs point to the worker timeout.", "created_at": "2026-08-06T12:30:00Z", "url": "https://forge.example/stackchain/api/issues/7#issuecomment-9", }, } monkeypatch.setattr(main, "notification_detail", detail, raising=False) transport = httpx.ASGITransport(app=main.app) async with httpx.AsyncClient(transport=transport, base_url="http://test") as client: response = await client.get("/api/v1/notifications/42") invalid = await client.get("/api/v1/notifications/0") assert response.status_code == 200 assert response.json()["latest_comment"]["author"] == "alexander" assert response.headers["cache-control"] == "no-store" assert invalid.status_code == 422 assert calls == [42] @pytest.mark.anyio async def test_notification_detail_timeout_is_sanitized_and_retryable(monkeypatch): async def detail(_thread_id): await asyncio.sleep(0.05) monkeypatch.setattr(main, "notification_detail", detail) monkeypatch.setattr(main, "NOTIFICATION_DETAIL_TIMEOUT_SECONDS", 0.01) transport = httpx.ASGITransport(app=main.app, raise_app_exceptions=False) async with httpx.AsyncClient(transport=transport, base_url="http://test") as client: response = await client.get("/api/v1/notifications/42") assert response.status_code == 503 assert response.json() == {"error": "Loading the update timed out. Please retry."} assert response.headers["retry-after"] == "1" assert response.headers["cache-control"] == "no-store" @pytest.mark.anyio async def test_notification_detail_upstream_failure_does_not_leak_exception(monkeypatch): async def detail(_thread_id): raise httpx.HTTPError("token=secret upstream exploded") monkeypatch.setattr(main, "notification_detail", detail) transport = httpx.ASGITransport(app=main.app, raise_app_exceptions=False) async with httpx.AsyncClient(transport=transport, base_url="http://test") as client: response = await client.get("/api/v1/notifications/42") assert response.status_code == 503 assert response.json() == { "error": "The update is temporarily unavailable. Please retry." } assert "secret" not in response.text assert response.headers["retry-after"] == "1"