145 lines
4.3 KiB
Python
145 lines
4.3 KiB
Python
from __future__ import annotations
|
|
|
|
from pathlib import Path
|
|
from textwrap import dedent
|
|
|
|
import pytest
|
|
|
|
from personal_mcp.skills import document_loader
|
|
|
|
|
|
def _write_skill(
|
|
root: Path,
|
|
*,
|
|
skill_id: str,
|
|
references_block: str = "",
|
|
) -> Path:
|
|
skill_dir = root / "docs" / "skills" / skill_id
|
|
skill_dir.mkdir(parents=True, exist_ok=True)
|
|
frontmatter_lines = [
|
|
"---",
|
|
f"name: {skill_id}",
|
|
"description: Example skill",
|
|
"x-personal-mcp:",
|
|
f" id: {skill_id}",
|
|
" version: 1.0.0",
|
|
" tags: [example]",
|
|
" capabilities:",
|
|
f" - resource://skills/{skill_id}/document",
|
|
]
|
|
if references_block:
|
|
frontmatter_lines.extend(f" {line}" for line in references_block.splitlines())
|
|
frontmatter_lines.append("---")
|
|
|
|
skill_doc = "\n".join(frontmatter_lines) + f"\n\n# {skill_id}\n"
|
|
(skill_dir / "SKILL.md").write_text(skill_doc, encoding="utf-8")
|
|
return skill_dir
|
|
|
|
|
|
def _load_registry(monkeypatch: pytest.MonkeyPatch, root: Path):
|
|
monkeypatch.setattr(document_loader, "files", lambda _: root)
|
|
return document_loader.load_docs_registry(package_anchor="unused")
|
|
|
|
|
|
def test_auto_discovers_top_level_references(monkeypatch, tmp_path: Path) -> None:
|
|
skill_dir = _write_skill(tmp_path, skill_id="demo-skill")
|
|
refs = skill_dir / "references"
|
|
refs.mkdir()
|
|
(refs / "index.md").write_text("# Source Map\n", encoding="utf-8")
|
|
(refs / "feature-catalog.md").write_text("# Feature Catalog\n", encoding="utf-8")
|
|
|
|
registry = _load_registry(monkeypatch, tmp_path)
|
|
skill = registry.skills_by_id["demo-skill"]
|
|
|
|
assert set(skill.references) == {"index", "feature-catalog"}
|
|
assert skill.references["index"].title == "Index"
|
|
assert skill.references["feature-catalog"].title == "Feature Catalog"
|
|
|
|
|
|
def test_explicit_reference_overrides_discovered(monkeypatch, tmp_path: Path) -> None:
|
|
references_block = dedent(
|
|
"""\
|
|
references:
|
|
index:
|
|
path: references/index.md
|
|
mime_type: text/plain
|
|
title: Explicit Title
|
|
"""
|
|
).rstrip()
|
|
skill_dir = _write_skill(
|
|
tmp_path,
|
|
skill_id="override-skill",
|
|
references_block=references_block,
|
|
)
|
|
refs = skill_dir / "references"
|
|
refs.mkdir()
|
|
(refs / "index.md").write_text("# Source Map\n", encoding="utf-8")
|
|
|
|
registry = _load_registry(monkeypatch, tmp_path)
|
|
record = registry.skills_by_id["override-skill"].references["index"]
|
|
|
|
assert record.mime_type == "text/plain"
|
|
assert record.title == "Explicit Title"
|
|
|
|
|
|
def test_nested_references_are_not_auto_discovered(monkeypatch, tmp_path: Path) -> None:
|
|
references_block = dedent(
|
|
"""\
|
|
references:
|
|
architecture-overview:
|
|
path: references/nested/architecture-overview.md
|
|
title: Architecture Overview
|
|
"""
|
|
).rstrip()
|
|
skill_dir = _write_skill(
|
|
tmp_path,
|
|
skill_id="nested-skill",
|
|
references_block=references_block,
|
|
)
|
|
refs = skill_dir / "references"
|
|
(refs / "nested").mkdir(parents=True)
|
|
(refs / "nested" / "architecture-overview.md").write_text(
|
|
"# Architecture\n",
|
|
encoding="utf-8",
|
|
)
|
|
|
|
registry = _load_registry(monkeypatch, tmp_path)
|
|
skill = registry.skills_by_id["nested-skill"]
|
|
|
|
assert set(skill.references) == {"architecture-overview"}
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
("skill_id", "files", "expected_refs"),
|
|
[
|
|
(
|
|
"non-markdown-skill",
|
|
{"guide.txt": "plain text", "guide.md": "# Guide\n"},
|
|
{"guide"},
|
|
),
|
|
(
|
|
"normalized-id-skill",
|
|
{"implicit_io.md": "# Implicit IO\n"},
|
|
{"implicit-io"},
|
|
),
|
|
],
|
|
)
|
|
def test_auto_discovery_file_filtering_and_ref_id_normalization(
|
|
monkeypatch,
|
|
tmp_path: Path,
|
|
skill_id: str,
|
|
files: dict[str, str],
|
|
expected_refs: set[str],
|
|
) -> None:
|
|
skill_dir = _write_skill(tmp_path, skill_id=skill_id)
|
|
refs = skill_dir / "references"
|
|
refs.mkdir()
|
|
|
|
for filename, content in files.items():
|
|
(refs / filename).write_text(content, encoding="utf-8")
|
|
|
|
registry = _load_registry(monkeypatch, tmp_path)
|
|
skill = registry.skills_by_id[skill_id]
|
|
|
|
assert set(skill.references) == expected_refs
|