Files
EverOS/tests/e2e/test_openapi_endpoint_matches_docs.py
Elliot Chen 518b8eca85 chore: initialize EverOS 1.0.0
md-first memory extraction framework for AI agents.

Markdown is the single source of truth; SQLite holds state and LanceDB
provides the rebuildable vector + BM25 + scalar index. The codebase follows
a single-direction DDD layering (entrypoints -> service -> memory -> infra,
with component / core / config cross-cutting) enforced by import-linter.

Engineering surface:
- Coding conventions in .claude/rules/ (path-scoped) and workflows in
  .claude/skills/ (/commit, /new-branch, /pr).
- GitHub Actions CI runs make lint + test + integration; pre-commit mirrors
  the gates locally (ruff, hygiene hooks, gitlint commit-msg).
- Commit messages follow Conventional Commits, enforced by gitlint.
- make lint also enforces datetime two-zone discipline and OpenAPI drift.
2026-06-06 07:33:17 +08:00

88 lines
3.2 KiB
Python

"""Belt-and-braces gate: dev-mode ``GET /openapi.json`` ≡ ``docs/openapi.json``.
The lint-time ``make check-openapi`` already diffs ``app.openapi()``
against the committed ``docs/openapi.json``. This e2e test closes the
remaining theoretical gap: if anyone ever adds a *lifespan-mutated*
OpenAPI schema (e.g. ``app.openapi_schema = ...`` inside a startup
handler), the in-memory ``app.openapi()`` and the runtime
``GET /openapi.json`` response would diverge — the lint gate would
miss it, but this test wouldn't.
How:
1. Force ``ENV=DEV`` so the ``openapi_url`` route is enabled.
2. Construct the app via ``create_app(lifespan_providers=[])`` to skip
SQLite / LanceDB / OME (the schema is route-driven, not state-
driven) — but *do* run the lifespan context, so any startup hook
that mutates ``app.openapi_schema`` is exercised.
3. ``GET /openapi.json`` through ``httpx.AsyncClient``.
4. Diff against ``docs/openapi.json`` byte-for-byte (after JSON
normalisation to defeat ordering nondeterminism).
"""
from __future__ import annotations
import json
import os
from pathlib import Path
import httpx
import pytest
_REPO_ROOT = Path(__file__).resolve().parents[2]
_COMMITTED_OPENAPI = _REPO_ROOT / "docs" / "openapi.json"
async def test_dev_mode_openapi_endpoint_matches_committed_docs(
monkeypatch: pytest.MonkeyPatch,
) -> None:
"""Runtime ``GET /openapi.json`` (dev mode) must equal ``docs/openapi.json``."""
# The gate's own committed snapshot must exist — otherwise the dev
# workflow ``make openapi`` has been skipped.
assert _COMMITTED_OPENAPI.is_file(), (
f"{_COMMITTED_OPENAPI} not found — run `make openapi`"
)
# Force dev-mode so ``openapi_url="/openapi.json"`` is registered.
monkeypatch.setenv("ENV", "DEV")
from everos.entrypoints.api.app import create_app
app = create_app(lifespan_providers=[])
transport = httpx.ASGITransport(app=app)
async with (
app.router.lifespan_context(app),
httpx.AsyncClient(transport=transport, base_url="http://test") as client,
):
resp = await client.get("/openapi.json")
assert resp.status_code == 200, resp.text
runtime_schema = resp.json()
committed_schema = json.loads(_COMMITTED_OPENAPI.read_text(encoding="utf-8"))
if runtime_schema != committed_schema:
# Emit a concise diff to help locate the drift cause.
import difflib
runtime_rendered = json.dumps(runtime_schema, indent=2, ensure_ascii=False)
committed_rendered = json.dumps(committed_schema, indent=2, ensure_ascii=False)
diff = "\n".join(
list(
difflib.unified_diff(
committed_rendered.splitlines(),
runtime_rendered.splitlines(),
fromfile="docs/openapi.json (committed)",
tofile="GET /openapi.json (runtime)",
lineterm="",
)
)[:120]
)
raise AssertionError(
"runtime /openapi.json drifts from docs/openapi.json; "
"run `make openapi` and commit the result.\n\n" + diff
)
# Keep ``os`` legit in case future scenarios need direct env reads.
_ = os