feat: WP-0003 complete — LEVEL3 advanced features + error framework

Implements full LEVEL3 feature set: cross-references (xref.py), numbered
figures (figures.py), auto-diagrams (diagrams.py), bibliography/citations
(bibliography.py), LEVEL3 capability detection (level3.py), and structured
error/warning records (errors.py). Builder, importer, and differ updated for
LEVEL3 round-trip support. REST and MCP interfaces updated with structured
warning records. 259 tests passing.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-16 10:51:38 +00:00
parent 760047b82b
commit ac442ea41f
26 changed files with 3713 additions and 74 deletions

View File

@@ -0,0 +1,271 @@
"""Tests for LEVEL3 plumbing — feature-level gating & disclosure (FR-537539)."""
from __future__ import annotations
import textwrap
from pathlib import Path
from markidocx.level3 import (
Level3Support,
ProcessorDependency,
capabilities_entry,
check_level3_support,
)
from markidocx.manifest import FeatureLevel, load_manifest
# ---------------------------------------------------------------------------
# Level3 support detection (FR-537, FR-538)
# ---------------------------------------------------------------------------
class TestCheckLevel3Support:
def test_returns_level3_support(self) -> None:
support = check_level3_support()
assert isinstance(support, Level3Support)
def test_always_available(self) -> None:
support = check_level3_support()
assert support.available is True
def test_dependencies_are_processor_dependency_instances(self) -> None:
support = check_level3_support()
for dep in support.dependencies:
assert isinstance(dep, ProcessorDependency)
assert dep.name in ("mmdc", "dot", "plantuml")
assert isinstance(dep.available, bool)
assert dep.description
def test_partial_when_no_diagram_tools(self, monkeypatch) -> None:
"""When no diagram tool is found, partial=True and missing_coverage is populated."""
import shutil
monkeypatch.setattr(shutil, "which", lambda _cmd: None)
support = check_level3_support()
assert support.partial is True
assert len(support.missing_coverage) > 0
assert any("diagram" in m for m in support.missing_coverage)
def test_not_partial_when_diagram_tool_present(self, monkeypatch) -> None:
"""When at least one diagram tool is found, partial=False."""
import shutil
def fake_which(cmd: str) -> str | None:
return "/usr/bin/mmdc" if cmd == "mmdc" else None
monkeypatch.setattr(shutil, "which", fake_which)
support = check_level3_support()
assert support.partial is False
assert support.missing_coverage == []
# ---------------------------------------------------------------------------
# capabilities_entry (FR-537)
# ---------------------------------------------------------------------------
class TestCapabilitiesEntry:
def test_returns_dict_with_level(self) -> None:
entry = capabilities_entry()
assert entry["level"] == "level3"
def test_available_is_true(self) -> None:
entry = capabilities_entry()
assert entry["available"] is True
def test_has_dependencies_list(self) -> None:
entry = capabilities_entry()
assert isinstance(entry["dependencies"], list)
for dep in entry["dependencies"]:
assert "name" in dep
assert "available" in dep
assert "description" in dep
def test_has_partial_and_missing_coverage(self) -> None:
entry = capabilities_entry()
assert "partial" in entry
assert "missing_coverage" in entry
# ---------------------------------------------------------------------------
# Manifest accepts feature_level: level3 (FR-537)
# ---------------------------------------------------------------------------
class TestManifestLevel3:
def test_level3_accepted(self, tmp_path: Path) -> None:
(tmp_path / "doc.md").write_text("# Hello", encoding="utf-8")
(tmp_path / "manifest.yaml").write_text(
textwrap.dedent("""\
project:
name: test
feature_level: level3
family: article
sources:
- path: doc.md
output:
dir: ./dist
"""),
encoding="utf-8",
)
m = load_manifest(tmp_path / "manifest.yaml")
assert m.project.feature_level == FeatureLevel.LEVEL3
def test_level3_routes_to_level3_processing(self, tmp_path: Path) -> None:
"""Building with feature_level: level3 succeeds (processing path reached)."""
from markidocx.builder import build_document
(tmp_path / "doc.md").write_text("# Hello\n\nContent.", encoding="utf-8")
(tmp_path / "manifest.yaml").write_text(
textwrap.dedent("""\
project:
name: test-l3
feature_level: level3
family: article
sources:
- path: doc.md
output:
dir: ./dist
"""),
encoding="utf-8",
)
m = load_manifest(tmp_path / "manifest.yaml")
result = build_document(m)
assert result.success
assert result.feature_level == "level3"
# ---------------------------------------------------------------------------
# partial_level3 flag and processor-dependency disclosure (FR-538, FR-539)
# ---------------------------------------------------------------------------
class TestPartialLevel3Flag:
def test_partial_level3_set_when_no_diagram_tools(
self, tmp_path: Path, monkeypatch
) -> None:
import shutil
from markidocx.builder import build_document
monkeypatch.setattr(shutil, "which", lambda _cmd: None)
(tmp_path / "doc.md").write_text("# Hello\n\nContent.", encoding="utf-8")
(tmp_path / "manifest.yaml").write_text(
textwrap.dedent("""\
project:
name: test-partial
feature_level: level3
family: article
sources:
- path: doc.md
output:
dir: ./dist
"""),
encoding="utf-8",
)
m = load_manifest(tmp_path / "manifest.yaml")
result = build_document(m)
assert result.success
assert result.partial_level3 is True
assert len(result.missing_coverage) > 0
def test_partial_level3_false_for_level1(self, tmp_path: Path) -> None:
from markidocx.builder import build_document
(tmp_path / "doc.md").write_text("# Hello\n\nContent.", encoding="utf-8")
(tmp_path / "manifest.yaml").write_text(
textwrap.dedent("""\
project:
name: test-l1
feature_level: level1
family: article
sources:
- path: doc.md
output:
dir: ./dist
"""),
encoding="utf-8",
)
m = load_manifest(tmp_path / "manifest.yaml")
result = build_document(m)
assert result.partial_level3 is False
assert result.missing_coverage == []
def test_dependency_warning_emitted_for_unavailable_tool(
self, tmp_path: Path, monkeypatch
) -> None:
import shutil
from markidocx.builder import build_document
from markidocx.errors import Severity
monkeypatch.setattr(shutil, "which", lambda _cmd: None)
(tmp_path / "doc.md").write_text("# Hello", encoding="utf-8")
(tmp_path / "manifest.yaml").write_text(
textwrap.dedent("""\
project:
name: t
feature_level: level3
family: article
sources:
- path: doc.md
output:
dir: ./dist
"""),
encoding="utf-8",
)
m = load_manifest(tmp_path / "manifest.yaml")
result = build_document(m)
dep_warnings = [
w for w in result.warning_records
if w.reason == "processor-dependency-unavailable"
]
assert dep_warnings, "Expected processor-dependency-unavailable warning"
assert all(w.severity == Severity.WARNING for w in dep_warnings)
# ---------------------------------------------------------------------------
# REST capabilities includes level3 (FR-537)
# ---------------------------------------------------------------------------
class TestRestCapabilitiesLevel3:
def test_capabilities_includes_level3(self) -> None:
from fastapi.testclient import TestClient
from markidocx.rest import create_app
client = TestClient(create_app())
resp = client.get("/capabilities")
assert resp.status_code == 200
body = resp.json()
outputs = body["outputs"]
assert "level3" in outputs
assert outputs["level3"]["level"] == "level3"
assert outputs["level3"]["available"] is True
assert "dependencies" in outputs["level3"]
# ---------------------------------------------------------------------------
# MCP validate_project includes level3 in context (FR-537)
# ---------------------------------------------------------------------------
class TestMcpLevel3:
def test_validate_project_includes_level3(self) -> None:
from markidocx.mcp_server import validate_project
manifest_yaml = textwrap.dedent("""\
project:
name: test
feature_level: level3
family: article
sources:
- path: doc.md
output:
dir: ./dist
""")
result = validate_project(manifest_yaml)
assert result["status"] == "ok"
assert result["feature_level"] == "level3"
assert "level3" in result["context"]
assert result["context"]["level3"]["available"] is True