generated from coulomb/repo-seed
WP-0001 (Foundation & LEVEL1 Core):
- manifest model (FR-100), MD→DOCX builder (FR-200), DOCX→MD importer
(FR-300/400), template family registry (FR-600), drift detector (FR-700),
CLI wiring, pre-commit config, CI skeleton, regression harness
WP-0002 (Service Interfaces & Workflow Orchestration):
- REST service via FastAPI (FR-900): /health, /version, /capabilities,
/templates, /styles, /validate, /build, /import, /compare,
/templates/register, /workflows/{name}, /evidence/{run_id}
- Evidence & report store (FR-1400): JSON-backed, per-run, retrievable
through all interfaces, classification (pass/warnings/failed)
- Composite workflow orchestration (FR-1300): single-file-roundtrip,
multi-file-roundtrip, release-regression, family-switch-build
- MCP server via FastMCP (FR-1000): all tools + resources
- CLI additions: `markidocx serve`, `markidocx workflow`, `markidocx mcp`
- Interface parity tests: CLI / REST / MCP produce equivalent results
135 tests passing, ruff + mypy clean.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
134 lines
4.1 KiB
Python
134 lines
4.1 KiB
Python
"""Tests for T01 — REST service foundation (FR-900 core)."""
|
|
|
|
from __future__ import annotations
|
|
|
|
import pytest
|
|
from fastapi.testclient import TestClient
|
|
|
|
from markidocx import __version__
|
|
from markidocx.manifest import SUPPORTED_FAMILIES, FeatureLevel
|
|
from markidocx.rest import create_app
|
|
|
|
|
|
@pytest.fixture()
|
|
def client() -> TestClient:
|
|
return TestClient(create_app())
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# GET /health (FR-910)
|
|
# ---------------------------------------------------------------------------
|
|
|
|
|
|
def test_health_returns_200(client: TestClient) -> None:
|
|
resp = client.get("/health")
|
|
assert resp.status_code == 200
|
|
|
|
|
|
def test_health_body(client: TestClient) -> None:
|
|
body = client.get("/health").json()
|
|
assert body["status"] == "ok"
|
|
assert body["version"] == __version__
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# GET /version (FR-911)
|
|
# ---------------------------------------------------------------------------
|
|
|
|
|
|
def test_version_returns_200(client: TestClient) -> None:
|
|
assert client.get("/version").status_code == 200
|
|
|
|
|
|
def test_version_envelope(client: TestClient) -> None:
|
|
body = client.get("/version").json()
|
|
assert body["status"] == "ok"
|
|
assert body["outputs"]["version"] == __version__
|
|
assert "warnings" in body
|
|
assert "errors" in body
|
|
assert "context" in body
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# GET /capabilities (FR-909)
|
|
# ---------------------------------------------------------------------------
|
|
|
|
|
|
def test_capabilities_returns_200(client: TestClient) -> None:
|
|
assert client.get("/capabilities").status_code == 200
|
|
|
|
|
|
def test_capabilities_feature_levels(client: TestClient) -> None:
|
|
body = client.get("/capabilities").json()
|
|
assert body["status"] == "ok"
|
|
levels = body["outputs"]["feature_levels"]
|
|
for level in FeatureLevel:
|
|
assert level.value in levels
|
|
|
|
|
|
def test_capabilities_families(client: TestClient) -> None:
|
|
body = client.get("/capabilities").json()
|
|
families = body["outputs"]["families"]
|
|
for family in SUPPORTED_FAMILIES:
|
|
assert family in families
|
|
|
|
|
|
def test_capabilities_has_context(client: TestClient) -> None:
|
|
body = client.get("/capabilities").json()
|
|
assert "version" in body["context"]
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# GET /templates (FR-906)
|
|
# ---------------------------------------------------------------------------
|
|
|
|
|
|
def test_templates_returns_200(client: TestClient) -> None:
|
|
assert client.get("/templates").status_code == 200
|
|
|
|
|
|
def test_templates_lists_built_ins(client: TestClient) -> None:
|
|
body = client.get("/templates").json()
|
|
assert body["status"] == "ok"
|
|
names = {f["name"] for f in body["outputs"]}
|
|
assert "article" in names
|
|
assert "book" in names
|
|
assert "website" in names
|
|
|
|
|
|
def test_templates_envelope_shape(client: TestClient) -> None:
|
|
body = client.get("/templates").json()
|
|
assert "warnings" in body
|
|
assert "errors" in body
|
|
assert "context" in body
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# GET /styles (FR-907)
|
|
# ---------------------------------------------------------------------------
|
|
|
|
|
|
def test_styles_returns_200(client: TestClient) -> None:
|
|
assert client.get("/styles").status_code == 200
|
|
|
|
|
|
def test_styles_is_list(client: TestClient) -> None:
|
|
body = client.get("/styles").json()
|
|
assert body["status"] == "ok"
|
|
assert isinstance(body["outputs"], list)
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Response envelope shape (FR-912)
|
|
# ---------------------------------------------------------------------------
|
|
|
|
|
|
@pytest.mark.parametrize("endpoint", ["/version", "/capabilities", "/templates", "/styles"])
|
|
def test_envelope_fields_present(client: TestClient, endpoint: str) -> None:
|
|
body = client.get(endpoint).json()
|
|
assert "status" in body
|
|
assert "outputs" in body
|
|
assert "warnings" in body
|
|
assert "errors" in body
|
|
assert "context" in body
|