Files
LegacyHUB/tests/test_api_health.py
Vadim Malanov 2e76554188
Some checks failed
CI / Backend (lint + tests + compose) (push) Has been cancelled
CI / Frontend (lint + type-check + build) (push) Has been cancelled
ci: align backend lint checks
2026-06-14 20:14:49 +03:00

104 lines
3.2 KiB
Python

"""Contract test for /health.
Patches the individual probe functions so the test does not depend on a live
Postgres / MinIO / OpenSearch / Qdrant / Redis. Verifies:
- the route is mounted under the configured API prefix;
- the response shape conforms to ``HealthResponse``;
- the overall status follows the worst-component-wins rule
(``ok`` -> ``ok``, any ``error`` -> ``error``, ``degraded`` otherwise);
- the CORS preflight responds with the configured allowed origin.
"""
from __future__ import annotations
import pytest
from fastapi.testclient import TestClient
from app.api.schemas import ComponentHealth
from app.config import settings
from app.main import app
@pytest.fixture
def client() -> TestClient:
return TestClient(app)
def _ok(name: str) -> ComponentHealth:
return ComponentHealth(name=name, status="ok", detail={})
def _err(name: str) -> ComponentHealth:
return ComponentHealth(name=name, status="error", detail={"error": "down"})
def _degraded(name: str) -> ComponentHealth:
return ComponentHealth(name=name, status="degraded", detail={"cluster_status": "red"})
def _patch_probes(monkeypatch, **overrides):
from app.api import routes_health
defaults = {
"_check_postgres": lambda: _ok("postgres"),
"_check_minio": lambda: _ok("minio"),
"_check_opensearch": lambda: _ok("opensearch"),
"_check_qdrant": lambda: _ok("qdrant"),
"_check_redis": lambda: _ok("redis"),
}
defaults.update(overrides)
for name, fn in defaults.items():
monkeypatch.setattr(routes_health, name, fn)
def test_health_all_ok(client: TestClient, monkeypatch):
_patch_probes(monkeypatch)
res = client.get(f"{settings.app_api_prefix}/health")
assert res.status_code == 200
body = res.json()
assert body["status"] == "ok"
assert {c["name"] for c in body["components"]} == {
"postgres",
"minio",
"opensearch",
"qdrant",
"redis",
}
def test_health_error_when_any_component_down(client: TestClient, monkeypatch):
_patch_probes(monkeypatch, _check_qdrant=lambda: _err("qdrant"))
res = client.get(f"{settings.app_api_prefix}/health")
assert res.status_code == 200
body = res.json()
assert body["status"] == "error"
qdrant = next(c for c in body["components"] if c["name"] == "qdrant")
assert qdrant["status"] == "error"
def test_health_degraded_when_any_component_degraded(client: TestClient, monkeypatch):
_patch_probes(monkeypatch, _check_opensearch=lambda: _degraded("opensearch"))
res = client.get(f"{settings.app_api_prefix}/health")
body = res.json()
assert body["status"] == "degraded"
def test_root_includes_api_prefix(client: TestClient):
res = client.get("/")
assert res.status_code == 200
assert res.json()["api"] == settings.app_api_prefix
def test_cors_preflight_allows_configured_origin(client: TestClient):
origin = settings.cors_origins[0]
res = client.options(
f"{settings.app_api_prefix}/health",
headers={
"Origin": origin,
"Access-Control-Request-Method": "GET",
},
)
assert res.status_code == 200
assert res.headers.get("access-control-allow-origin") == origin