"""Dependency-aware health integration coverage.""" from __future__ import annotations import asyncio import time from fastapi.testclient import TestClient from ragcore.infrastructure.health import ReadinessChecker from ragcore.main import app, create_app async def _ok() -> None: return None async def _fail() -> None: raise OSError("database password should never escape readiness details") async def _slow() -> None: await asyncio.sleep(0.2) def _client(checker: ReadinessChecker) -> TestClient: return TestClient(create_app(readiness_checker=checker), raise_server_exceptions=False) def test_liveness_does_not_call_downstreams() -> None: async def must_not_run() -> None: raise AssertionError("liveness must not probe dependencies") with _client( ReadinessChecker( {"postgres": must_not_run, "qdrant": must_not_run, "storage": must_not_run} ) ) as client: response = client.get("/health/live") assert response.status_code == 200 assert response.json() == {"status": "ok", "service": "ragcore"} def test_readiness_returns_all_ok_components() -> None: checker = ReadinessChecker({"postgres": _ok, "qdrant": _ok, "storage": _ok}) with _client(checker) as client: response = client.get("/health/ready") assert response.status_code == 200 body = response.json() assert body["status"] == "ok" assert set(body["components"]) == {"postgres", "qdrant", "storage"} assert all(component["status"] == "ok" for component in body["components"].values()) assert all(component["code"] is None for component in body["components"].values()) def test_readiness_returns_stable_problem_details_for_dependency_failure() -> None: checker = ReadinessChecker({"postgres": _fail, "qdrant": _ok, "storage": _ok}) with _client(checker) as client: response = client.get("/health/ready", headers={"X-Request-ID": "health-failure-123"}) assert response.status_code == 503 assert response.headers["content-type"].startswith("application/problem+json") body = response.json() assert body["status"] == 503 assert body["code"] == "DEPENDENCY_UNAVAILABLE" assert body["trace_id"] == "health-failure-123" assert body["errors"] == [{"component": "postgres", "code": "POSTGRES_UNAVAILABLE"}] assert "database password" not in response.text def test_readiness_applies_strict_probe_timeout() -> None: checker = ReadinessChecker( {"postgres": _slow, "qdrant": _ok, "storage": _ok}, timeout_seconds=0.02 ) started = time.perf_counter() with _client(checker) as client: response = client.get("/health/ready") elapsed = time.perf_counter() - started assert response.status_code == 503 # The timeout actually firing (not the probe merely finishing fast on # its own) is what the error code below proves -- it can only appear if # the checker's own 20ms deadline cut the 200ms `_slow()` probe off # early. `elapsed` therefore only needs to rule out an indefinite hang, # not stand in for a precise timing proof: a tight millisecond-scale # margin here (this used to assert `elapsed < 0.15`) flakes under real # system load from TestClient/ASGI scheduling jitter alone, with the # timeout logic itself never having failed at all. assert response.json()["errors"] == [{"component": "postgres", "code": "POSTGRES_TIMEOUT"}] assert elapsed < 2.0 def test_production_readiness_checks_compose_dependencies() -> None: with TestClient(app, raise_server_exceptions=False) as client: response = client.get("/health/ready") assert response.status_code == 200 body = response.json() assert body["status"] == "ok" assert set(body["components"]) == {"postgres", "qdrant", "storage"} assert all(component["status"] == "ok" for component in body["components"].values())