from types import SimpleNamespace import pytest from fastapi import FastAPI from httpx import ASGITransport, AsyncClient, Response from agentci.api.routes.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_result", "status_code", "payload"), [ (True, 200, {"status": "ready"}), ( False, 503, { "status": "not-ready", "reason": "opencode provider is not connected", }, ), pytest.param( RuntimeError("provider check failed"), 500, None, id="provider-error", ), ], ) async def test_readiness_reflects_provider_state( provider_result: bool | Exception, status_code: int, payload: dict[str, str] | None, ) -> None: provider = Provider(provider_result) response = await get(application(provider), "/health/ready") assert response.status_code == status_code if payload is not None: assert response.json() == payload assert provider.calls == 1