Co-Study4Grid / expert_backend /tests /test_openapi_contract.py
github-actions[bot]
Deploy 7688ef1
13d4e44
Raw
History Blame Contribute Delete
2.39 kB
# Copyright (c) 2025-2026, RTE (https://www.rte-france.com)
# This Source Code Form is subject to the terms of the Mozilla Public License, version 2.0.
# SPDX-License-Identifier: MPL-2.0
"""The OpenAPI contract snapshot must stay in sync with the live app (D2).
Runs the same normalization as ``scripts/check_openapi_contract.py`` in
the pytest process, so an endpoint / request-model / response-model /
status-code change that isn't accompanied by a regenerated
``openapi.snapshot.json`` fails here (and in CI) instead of silently
drifting from the hand-mirrored ``types.ts``.
Regenerate intentionally with:
python scripts/check_openapi_contract.py --write
"""
from __future__ import annotations
import importlib.util
from pathlib import Path
import pytest
pytest.importorskip("fastapi")
_REPO_ROOT = Path(__file__).resolve().parent.parent.parent
_SCRIPT = _REPO_ROOT / "scripts" / "check_openapi_contract.py"
_SNAPSHOT = _REPO_ROOT / "expert_backend" / "openapi.snapshot.json"
def _load_checker():
spec = importlib.util.spec_from_file_location("_openapi_checker", _SCRIPT)
mod = importlib.util.module_from_spec(spec)
spec.loader.exec_module(mod)
return mod
def test_openapi_snapshot_matches_live_app():
checker = _load_checker()
rendered = checker._normalize(checker._render_spec())
committed = _SNAPSHOT.read_text(encoding="utf-8")
assert rendered == committed, (
"OpenAPI contract drift — the live app.openapi() no longer matches "
"expert_backend/openapi.snapshot.json. If the change is intentional, "
"run: python scripts/check_openapi_contract.py --write"
)
def test_error_responses_use_the_unified_envelope():
"""Every 4xx/5xx the app can raise renders as {detail, code}."""
from fastapi.testclient import TestClient
from expert_backend.main import app
client = TestClient(app, raise_server_exceptions=False)
# 422 validation error (missing required body field) — uniform envelope.
r = client.post("/api/run-analysis-step1", json={})
assert r.status_code == 422
body = r.json()
assert "code" in body and body["code"] == "VALIDATION"
assert "detail" in body
# 404 from the artifact route — uniform envelope with NOT_FOUND.
r = client.get("/results/pdf/does-not-exist.html")
assert r.status_code == 404
assert r.json()["code"] == "NOT_FOUND"