Files
MoviePilot/tests/test_agent_api_surface_audit.py
jxxghp 2d73cbc43e docs: drop process records and internal implementation docs
Keep only install, usage, development-workflow and architecture-boundary
documents; remove the one-off refactor plans, audits and ledgers that had
already served their purpose.

- delete docs/refactor plus the agent API surface audit generator and its
  drift-only tests; the fixed route registry remains the source of truth
- delete internal implementation notes (agent, music, notification, transfer,
  subscription activity, docker startup, V3t runtime governance)
- delete site adapter capture/release shims and the ADR records; external
  links point at MoviePilot-Site-Collector instead
- keep docs/architecture-overview.md and gate its dependency and event
  figures against the generated fixtures
- drop doc-only assertions from the architecture baseline test and retarget
  remaining references at code and rule documents
2026-10-18 14:24:49 +08:00

80 lines
3.1 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""
Agent API 工具合同检查:Skill 分类文档、MCP schema 与固定路由注册表保持一致。
"""
import re
from pathlib import Path
from app.agent.policy.api import API_OPERATION_ROUTES
from app.agent.tools.impl.api import MoviePilotApiTool
PROJECT_ROOT = Path(__file__).resolve().parents[1]
API_SKILL = PROJECT_ROOT / "skills/moviepilot-api/SKILL.md"
def _read_api_skill_contract() -> str:
"""
读取 API Skill 入口及全部分类契约文档。
"""
skill_root = API_SKILL.parent
paths = [API_SKILL, *sorted((skill_root / "api").glob("*.md"))]
return "\n".join(path.read_text(encoding="utf-8") for path in paths)
def test_moviepilot_api_skill_routes_contracts_to_category_files() -> None:
"""
API Skill 通过分类文档索引全部 operation 及请求体合同。
"""
entrypoint = API_SKILL.read_text(encoding="utf-8")
category_files = {
path.stem: path.read_text(encoding="utf-8")
for path in sorted((API_SKILL.parent / "api").glob("*.md"))
}
assert "### `" not in entrypoint
assert len(category_files) == 21
assert "models" not in category_files
assert all(
f"### `{operation_id}`" in category_files[operation_id.split(".", 1)[0]]
for operation_id in API_OPERATION_ROUTES
)
assert all("## Body Models" in content for content in category_files.values())
def test_every_gateway_operation_has_exact_skill_and_chinese_mcp_contract() -> None:
"""
每个获准操作都必须有唯一的 Skill 契约及对应的中文 MCP 契约。
"""
skill = _read_api_skill_contract()
schema = MoviePilotApiTool(session_id="audit", user_id="1").get_mcp_input_schema()
branches = {
branch["properties"]["operation_id"]["const"]: branch
for branch in schema["oneOf"]
}
assert set(branches) == set(API_OPERATION_ROUTES)
for operation_id, route in API_OPERATION_ROUTES.items():
assert skill.count(f"### `{operation_id}`") == 1
branch = branches[operation_id]
assert branch["description"].strip()
assert re.search(r"[\u3400-\u9fff]", branch["description"]), operation_id
assert route.method in skill.split(f"### `{operation_id}`", 1)[1].split("\n### `", 1)[0]
assert route.path in skill.split(f"### `{operation_id}`", 1)[1].split("\n### `", 1)[0]
def test_every_gateway_path_placeholder_is_required_by_its_mcp_branch() -> None:
"""
固定路由的每个路径占位符都必须在工具 schema 中以同名必填字段暴露。
"""
schema = MoviePilotApiTool(session_id="audit", user_id="1").get_mcp_input_schema()
branches = {
branch["properties"]["operation_id"]["const"]: branch
for branch in schema["oneOf"]
}
for operation_id, route in API_OPERATION_ROUTES.items():
expected = set(re.findall(r"{([^}]+)}", route.path))
path_schema = branches[operation_id].get("properties", {}).get("path_params", {})
assert set(path_schema.get("properties", {})) == expected, operation_id
assert set(path_schema.get("required", [])) == expected, operation_id