Spaces:
Sleeping
Sleeping
| # 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" | |