docs(ocr): explain why two metrics tests skip fresh_metrics fixture
Some checks failed
CI / Backend Unit Tests (push) Successful in 3m42s
CI / fail2ban Regex (push) Successful in 43s
CI / Semgrep Security Scan (push) Successful in 19s
CI / Compose Bucket Idempotency (push) Successful in 1m0s
CI / Unit & Component Tests (pull_request) Successful in 3m24s
CI / OCR Service Tests (pull_request) Successful in 20s
CI / Backend Unit Tests (pull_request) Successful in 3m28s
CI / fail2ban Regex (pull_request) Successful in 43s
CI / Semgrep Security Scan (pull_request) Successful in 19s
CI / Compose Bucket Idempotency (pull_request) Successful in 1m1s
CI / Unit & Component Tests (push) Failing after 2m44s
CI / OCR Service Tests (push) Successful in 20s

Sara's cycle-2 S2: clarify the latent (but not actual) cross-test state
risk on the two metrics tests that hit the global REGISTRY instead of
the per-test fresh_metrics fixture. Migrating them would actually break
them — the /metrics endpoint is served by prometheus-fastapi-instrumentator
which binds to the default REGISTRY at app-construction time, and the
http_requests_total assertion only finds counters on that global
registry. Both tests already assert response shape only (status code,
content-type substring, body substrings), not numeric values, so the
shared-registry caveat is documented for future readers rather than
treated as a bug to fix.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit was merged in pull request #653.
This commit is contained in:
Marcel
2026-05-21 17:23:32 +02:00
parent e0e1578bdd
commit 0801da8df0

View File

@@ -61,7 +61,15 @@ def fresh_metrics(monkeypatch):
@pytest.mark.asyncio
async def test_metrics_endpoint_returns_200():
"""`GET /metrics` returns 200 with Prometheus exposition content."""
"""`GET /metrics` returns 200 with Prometheus exposition content.
Uses the global REGISTRY by design — does NOT take the `fresh_metrics` fixture.
The `/metrics` endpoint is wired by `prometheus-fastapi-instrumentator`, which
binds to the default REGISTRY at app-construction time; swapping `main.metrics`
via the fixture would not redirect what `/metrics` exposes. This test only
asserts response shape (status code + content-type substring), not numeric
counter values, so cross-test state leakage cannot affect it.
"""
with patch("main.kraken_engine.load_models"), \
patch("main.load_spell_checker"):
async with AsyncClient(transport=ASGITransport(app=app), base_url="http://test") as client:
@@ -73,7 +81,15 @@ async def test_metrics_endpoint_returns_200():
@pytest.mark.asyncio
async def test_metrics_includes_http_request_metrics_after_ocr_call():
"""After a request to /ocr, `/metrics` exposes auto-instrumented http_* metrics."""
"""After a request to /ocr, `/metrics` exposes auto-instrumented http_* metrics.
Uses the global REGISTRY by design — does NOT take the `fresh_metrics` fixture.
The `http_requests_total` / `http_request_duration_seconds` metrics live on
the instrumentator's default REGISTRY (not on `main.metrics`), so a fresh
CollectorRegistry would never see them. This test only asserts response shape
(substring presence in the exposition body), not numeric counter values, so
cross-test state leakage cannot affect it.
"""
mock_images = [Image.new("RGB", (100, 100))]
mock_blocks = [{"pageNumber": 1, "x": 0.0, "y": 0.0, "width": 1.0, "height": 1.0,
"polygon": None, "text": "hi", "words": []}]