from types import SimpleNamespace import pytest from fastapi import FastAPI from httpx import ASGITransport, AsyncClient, Response from agentci.health import router class Provider: def __init__(self, result: bool | Exception) -> None: self.result = result self.calls = 0 async def ready(self) -> bool: self.calls += 1 if isinstance(self.result, Exception): raise self.result return self.result def application(provider: Provider | None = None) -> FastAPI: app = FastAPI() app.include_router(router) if provider is not None: app.state.runtime = SimpleNamespace(opencode=provider) return app async def get(app: FastAPI, path: str) -> Response: async with AsyncClient( transport=ASGITransport(app=app, raise_app_exceptions=False), base_url="http://test", ) as client: return await client.get(path) async def test_liveness_does_not_depend_on_runtime_providers() -> None: response = await get(application(), "/health/live") assert response.status_code == 200 assert response.json() == {"status": "live"} @pytest.mark.parametrize( ("provider_ready", "status_code", "payload"), [ (True, 200, {"status": "ready"}), ( False, 503, { "status": "not-ready", "reason": "opencode provider is not connected", }, ), ], ) async def test_readiness_reflects_provider_state( provider_ready: bool, status_code: int, payload: dict[str, str] ) -> None: provider = Provider(provider_ready) response = await get(application(provider), "/health/ready") assert response.status_code == status_code assert response.json() == payload assert provider.calls == 1 async def test_readiness_provider_error_is_server_failure() -> None: provider = Provider(RuntimeError("provider check failed")) response = await get(application(provider), "/health/ready") assert response.status_code == 500 assert provider.calls == 1