Files
dotfiles/tests/test_ebook_search_health.py
T
2026-06-15 21:21:01 -04:00

117 lines
3.8 KiB
Python

"""Tests for EPUB search health and readiness routes."""
from __future__ import annotations
from fastapi.testclient import TestClient
from sqlalchemy import create_engine
from python.ebook_search.api.main import create_app
from python.ebook_search.config import EbookSearchConfig, RerankConfig
HEALTH_MODULE = "python.ebook_search.api.routes.health"
def fake_get_postgres_engine(**_kwargs):
"""Return an in-memory engine for route tests."""
return create_engine("sqlite+pysqlite:///:memory:", future=True)
def patch_app_runtime(monkeypatch):
monkeypatch.setattr("python.ebook_search.api.main.get_postgres_engine", fake_get_postgres_engine)
monkeypatch.setattr("python.ebook_search.api.main.ensure_bm25_corpus", lambda _session, _config: None)
def patch_dependencies(monkeypatch, *, database=True, embedding=True, chat=True, bm25="ok"):
monkeypatch.setattr(f"{HEALTH_MODULE}.check_database", lambda _session: database)
monkeypatch.setattr(f"{HEALTH_MODULE}.check_embedding_endpoint", lambda _config: embedding)
monkeypatch.setattr(f"{HEALTH_MODULE}.check_chat_endpoint", lambda _config: chat)
monkeypatch.setattr(f"{HEALTH_MODULE}.check_bm25_status", lambda _config: bm25)
def build_client(monkeypatch, config=None):
patch_app_runtime(monkeypatch)
app = create_app()
app.state.config = config or EbookSearchConfig(rerank=RerankConfig(enabled=False))
return TestClient(app)
def test_health_returns_ok(monkeypatch) -> None:
with build_client(monkeypatch) as client:
response = client.get("/health")
assert response.status_code == 200
assert response.json() == {"status": "ok"}
def test_ready_all_dependencies_ok(monkeypatch) -> None:
patch_dependencies(monkeypatch)
with build_client(monkeypatch) as client:
response = client.get("/ready")
assert response.status_code == 200
body = response.json()
assert body["status"] == "ready"
assert body["checks"] == {"database": "ok", "embedding": "ok", "chat": "ok", "bm25": "ok"}
def test_ready_embedding_down_is_degraded(monkeypatch) -> None:
patch_dependencies(monkeypatch, embedding=False)
with build_client(monkeypatch) as client:
response = client.get("/ready")
assert response.status_code == 200
body = response.json()
assert body["status"] == "degraded"
assert body["checks"]["embedding"] == "fail"
def test_ready_chat_down_is_degraded(monkeypatch) -> None:
patch_dependencies(monkeypatch, chat=False)
with build_client(monkeypatch) as client:
response = client.get("/ready")
assert response.status_code == 200
body = response.json()
assert body["status"] == "degraded"
assert body["checks"]["chat"] == "fail"
def test_ready_chat_disabled_when_answers_off(monkeypatch) -> None:
patch_dependencies(monkeypatch)
config = EbookSearchConfig(rerank=RerankConfig(enabled=False), answer_enabled=False)
with build_client(monkeypatch, config) as client:
response = client.get("/ready")
assert response.status_code == 200
body = response.json()
assert body["status"] == "ready"
assert body["checks"]["chat"] == "disabled"
def test_ready_database_down_is_unavailable(monkeypatch) -> None:
patch_dependencies(monkeypatch, database=False)
with build_client(monkeypatch) as client:
response = client.get("/ready")
assert response.status_code == 503
body = response.json()
assert body["status"] == "unavailable"
assert body["checks"]["database"] == "fail"
def test_ready_bm25_missing_is_degraded(monkeypatch) -> None:
patch_dependencies(monkeypatch, bm25="missing")
with build_client(monkeypatch) as client:
response = client.get("/ready")
assert response.status_code == 200
body = response.json()
assert body["status"] == "degraded"
assert body["checks"]["bm25"] == "missing"