feat: isolate readiness probes from Gitea traffic (Closes #687)
CI / lint (pull_request) Successful in 2m1s
CI / build-release (pull_request) Successful in 9s
CI / release-candidate (pull_request) Has been skipped

This commit is contained in:
timmy committed 2026-08-12 23:10:29 +00:00
1 parent 9ba03613d4
commit d2b2c7bada
4 files changed
+159 -24

No files matched your search

+1
View File
@@ -2046,6 +2046,7 @@ async def test_public_routes_remain_available_and_readiness_hides_identity(acces
return {"id": 1, "login": "timmy"}
monkeypatch.setattr(main, "current_user", user)
await main._check_readiness()
transport = httpx.ASGITransport(app=main.app)
async with httpx.AsyncClient(transport=transport, base_url="https://test") as client:
health = await client.get("/healthz")
+6
View File
@@ -1,4 +1,5 @@
import asyncio
from unittest.mock import AsyncMock
import httpx
import pytest
@@ -245,6 +246,7 @@ async def test_application_lifespan_opens_and_closes_gitea_transport(monkeypatch
calls = []
monkeypatch.setattr(gitea_proxy, "start_client", lambda: calls.append("start"))
monkeypatch.setattr(main, "_check_readiness", AsyncMock())
async def stop_client():
calls.append("stop")
@@ -270,6 +272,7 @@ async def test_application_shutdown_finishes_snapshot_before_closing_transport(m
calls.append("snapshot cancelled")
monkeypatch.setattr(gitea_proxy, "start_client", lambda: calls.append("start"))
monkeypatch.setattr(main, "_check_readiness", AsyncMock())
async def stop_client():
assert main._live_snapshot_task is not None
@@ -306,6 +309,7 @@ async def test_application_shutdown_cancels_available_work_scan_before_transport
calls.append("available scan cancelled")
monkeypatch.setattr(gitea_proxy, "start_client", lambda: calls.append("start"))
monkeypatch.setattr(main, "_check_readiness", AsyncMock())
async def stop_client():
task = main._available_issue_snapshot_task
@@ -341,6 +345,7 @@ async def test_application_shutdown_drains_authored_mutation_before_transport(mo
return {"id": 461}
monkeypatch.setattr(gitea_proxy, "start_client", lambda: calls.append("start"))
monkeypatch.setattr(main, "_check_readiness", AsyncMock())
async def stop_client():
calls.append("stop")
@@ -382,6 +387,7 @@ async def test_application_shutdown_bounds_and_settles_stalled_authored_mutation
monkeypatch.setattr(main, "AUTHORED_ACTION_SHUTDOWN_GRACE_SECONDS", 0.01)
monkeypatch.setattr(gitea_proxy, "start_client", lambda: calls.append("start"))
monkeypatch.setattr(main, "_check_readiness", AsyncMock())
async def stop_client():
calls.append("stop")
+66
View File
@@ -11,6 +11,49 @@ def test_health_endpoint_reports_service_liveness_without_gitea_access():
assert main.health() == {"status": "ok", "service": "stackchain-dashboard"}
@pytest.mark.anyio
async def test_readiness_endpoint_never_contacts_gitea_for_a_request(monkeypatch):
calls = 0
async def unexpected_user_lookup():
nonlocal calls
calls += 1
raise AssertionError("request-time readiness must not contact Gitea")
monkeypatch.setattr(main, "_readiness_state", None, raising=False)
monkeypatch.setattr(main, "current_user", unexpected_user_lookup)
first = await main.readiness()
second = await main.readiness()
assert first.status_code == 503
assert second.status_code == 503
assert calls == 0
@pytest.mark.anyio
async def test_readiness_monitor_refreshes_on_an_interval_and_cancels(monkeypatch):
calls = 0
refreshed_twice = asyncio.Event()
async def check():
nonlocal calls
calls += 1
if calls == 2:
refreshed_twice.set()
monkeypatch.setattr(main, "_check_readiness", check)
monkeypatch.setattr(main, "READINESS_INTERVAL_SECONDS", 0.01, raising=False)
task = asyncio.create_task(main._readiness_monitor())
await asyncio.wait_for(refreshed_twice.wait(), timeout=0.2)
task.cancel()
with pytest.raises(asyncio.CancelledError):
await task
assert calls == 2
@pytest.mark.anyio
async def test_readiness_endpoint_reports_connected_gitea_user(monkeypatch):
async def connected_user():
@@ -18,6 +61,8 @@ async def test_readiness_endpoint_reports_connected_gitea_user(monkeypatch):
monkeypatch.setattr(main, "current_user", connected_user)
await main._check_readiness()
response = await main.readiness()
assert any(getattr(route, "path", None) == "/readyz" for route in main.app.routes)
@@ -35,6 +80,8 @@ async def test_readiness_endpoint_hides_unexpected_upstream_error_details(monkey
monkeypatch.setattr(main, "current_user", unavailable_user)
await main._check_readiness()
response = await main.readiness()
assert response.status_code == 503
@@ -53,6 +100,8 @@ async def test_readiness_endpoint_returns_503_for_null_current_user_payload(monk
monkeypatch.setattr(main, "current_user", null_user)
await main._check_readiness()
response = await main.readiness()
assert response.status_code == 503
@@ -77,6 +126,8 @@ async def test_readiness_endpoint_times_out_and_cancels_stalled_gitea_check(monk
monkeypatch.setattr(main, "READINESS_TIMEOUT_SECONDS", 0.01, raising=False)
monkeypatch.setattr(main, "current_user", hanging_user)
await main._check_readiness()
response = await main.readiness()
assert response.status_code == 503
@@ -87,3 +138,18 @@ async def test_readiness_endpoint_times_out_and_cancels_stalled_gitea_check(monk
"error": "Gitea readiness check timed out after 0.01s",
}
assert cancelled.is_set()
@pytest.mark.anyio
async def test_readiness_endpoint_expires_a_stale_healthy_snapshot(monkeypatch):
monkeypatch.setattr(
main,
"_readiness_state",
main.ReadinessState(checked_at=10.0, ready=True, login="timmy"),
)
monkeypatch.setattr(main.time, "monotonic", lambda: 76.0)
response = await main.readiness()
assert response.status_code == 503
assert json.loads(response.body)["error"] == "Gitea readiness status is not available"