172 lines
6.4 KiB
Python
172 lines
6.4 KiB
Python
from __future__ import annotations
|
|
|
|
from collections.abc import Callable
|
|
from pathlib import Path
|
|
|
|
import pytest
|
|
|
|
from personal_mcp.registry.ingest.document import MarkdownDocument
|
|
from personal_mcp.registry.ingest.skill import SkillFilesBundle
|
|
from personal_mcp.registry.ingest.skill import group_skill_paths
|
|
|
|
pytestmark = pytest.mark.unit
|
|
|
|
MakeDoc = Callable[[str, str], MarkdownDocument]
|
|
|
|
|
|
class TestSkillFilesBundle:
|
|
"""Covers SkillFilesBundle construction and path-based categorization."""
|
|
|
|
class TestFromRoot:
|
|
"""Covers bundle creation from resource roots."""
|
|
|
|
def test_builds_bundles(self, tmp_path: Path) -> None:
|
|
"""Ensures from_root builds bundles from discovered markdown docs."""
|
|
alpha = tmp_path / "skills" / "alpha"
|
|
beta = tmp_path / "skills" / "beta"
|
|
(alpha / "references").mkdir(parents=True)
|
|
beta.mkdir(parents=True)
|
|
(alpha / "SKILL.md").write_text("# alpha\n", encoding="utf-8")
|
|
(alpha / "references" / "one.md").write_text("ref\n", encoding="utf-8")
|
|
(beta / "SKILL.md").write_text("# beta\n", encoding="utf-8")
|
|
|
|
bundles = SkillFilesBundle.from_root(tmp_path)
|
|
|
|
assert {bundle.slug for bundle in bundles} == {"alpha", "beta"}
|
|
|
|
def test_delegates_to_from_docs(
|
|
self,
|
|
tmp_path: Path,
|
|
) -> None:
|
|
"""Ensures from_root delegates bundle assembly to from_docs."""
|
|
alpha = tmp_path / "skills" / "alpha"
|
|
beta = tmp_path / "skills" / "beta"
|
|
(alpha / "references").mkdir(parents=True)
|
|
beta.mkdir(parents=True)
|
|
(alpha / "SKILL.md").write_text("# alpha\n", encoding="utf-8")
|
|
(alpha / "references" / "one.md").write_text("ref\n", encoding="utf-8")
|
|
(beta / "SKILL.md").write_text("# beta\n", encoding="utf-8")
|
|
|
|
from_root = SkillFilesBundle.from_root(tmp_path)
|
|
from_docs = SkillFilesBundle.from_docs(MarkdownDocument.from_root(tmp_path).values())
|
|
|
|
assert tuple(from_root) == from_docs
|
|
|
|
class TestFromDocs:
|
|
"""Covers bundle creation from preloaded markdown documents."""
|
|
|
|
def test_groups_by_slug(self, make_doc: MakeDoc) -> None:
|
|
"""Ensures from_docs groups documents by skill slug."""
|
|
docs = [
|
|
make_doc("skills/alpha/SKILL.md"),
|
|
make_doc("skills/alpha/references/a.md"),
|
|
make_doc("skills/beta/SKILL.md"),
|
|
]
|
|
|
|
bundles = SkillFilesBundle.from_docs(docs)
|
|
|
|
assert {bundle.slug for bundle in bundles} == {"alpha", "beta"}
|
|
|
|
def test_one_bundle_per_slug(self, make_doc: MakeDoc) -> None:
|
|
"""Ensures from_docs produces one SkillFilesBundle per slug."""
|
|
docs = [
|
|
make_doc("skills/alpha/SKILL.md"),
|
|
make_doc("skills/alpha/references/r1.md"),
|
|
make_doc("skills/alpha/references/r2.md"),
|
|
]
|
|
|
|
bundles = SkillFilesBundle.from_docs(docs)
|
|
|
|
assert len(bundles) == 1
|
|
assert bundles[0].slug == "alpha"
|
|
|
|
class TestFromPaths:
|
|
"""Covers classification of skill, reference, and other documents."""
|
|
|
|
def test_selects_skill_md(self, make_doc: MakeDoc) -> None:
|
|
"""Ensures from_paths selects SKILL.md as the primary document."""
|
|
docs = {
|
|
make_doc("skills/alpha/SKILL.md"),
|
|
make_doc("skills/alpha/notes.md"),
|
|
}
|
|
|
|
bundle = SkillFilesBundle.from_paths("alpha", docs)
|
|
|
|
assert bundle.skill.relpath.name == "SKILL.md"
|
|
|
|
def test_collects_references(self, make_doc: MakeDoc) -> None:
|
|
"""Ensures from_paths captures reference docs under references/."""
|
|
docs = {
|
|
make_doc("skills/alpha/SKILL.md"),
|
|
make_doc("skills/alpha/references/r1.md"),
|
|
make_doc("skills/alpha/references/r2.md"),
|
|
make_doc("skills/alpha/notes.md"),
|
|
}
|
|
|
|
bundle = SkillFilesBundle.from_paths("alpha", docs)
|
|
|
|
assert {doc.relpath.as_posix() for doc in bundle.references} == {
|
|
"skills/alpha/references/r1.md",
|
|
"skills/alpha/references/r2.md",
|
|
}
|
|
|
|
def test_collects_other_docs(self, make_doc: MakeDoc) -> None:
|
|
"""Ensures from_paths classifies non-reference docs as other docs."""
|
|
docs = {
|
|
make_doc("skills/alpha/SKILL.md"),
|
|
make_doc("skills/alpha/references/r1.md"),
|
|
make_doc("skills/alpha/notes.md"),
|
|
make_doc("skills/alpha/changelog.md"),
|
|
}
|
|
|
|
bundle = SkillFilesBundle.from_paths("alpha", docs)
|
|
|
|
assert {doc.relpath.as_posix() for doc in bundle.other} == {
|
|
"skills/alpha/changelog.md",
|
|
"skills/alpha/notes.md",
|
|
}
|
|
|
|
|
|
class TestGroupSkillPaths:
|
|
"""Covers grouping markdown documents by derived skill slug."""
|
|
|
|
def test_groups_slugged_docs(self, make_doc: MakeDoc) -> None:
|
|
"""Ensures group_skill_paths groups only documents with a slug."""
|
|
docs = [
|
|
make_doc("skills/alpha/SKILL.md"),
|
|
make_doc("skills/alpha/references/r.md"),
|
|
make_doc("skills/beta/SKILL.md"),
|
|
]
|
|
|
|
grouped = group_skill_paths(docs)
|
|
|
|
assert set(grouped) == {"alpha", "beta"}
|
|
|
|
def test_excludes_unslugged_docs(self, make_doc: MakeDoc) -> None:
|
|
"""Ensures group_skill_paths excludes documents without skill slugs."""
|
|
docs = [
|
|
make_doc("docs/index.md"),
|
|
make_doc("content/usage.md"),
|
|
]
|
|
|
|
assert group_skill_paths(docs) == {}
|
|
|
|
def test_returns_sets(self, make_doc: MakeDoc) -> None:
|
|
"""Ensures group_skill_paths returns sets of docs per slug."""
|
|
grouped = group_skill_paths([make_doc("skills/alpha/SKILL.md")])
|
|
|
|
assert isinstance(grouped["alpha"], set)
|
|
|
|
def test_stable_grouping(self, make_doc: MakeDoc) -> None:
|
|
"""Ensures group_skill_paths behaves consistently after internal sorting."""
|
|
docs = [
|
|
make_doc("skills/beta/SKILL.md"),
|
|
make_doc("skills/alpha/references/a.md"),
|
|
make_doc("skills/alpha/SKILL.md"),
|
|
]
|
|
|
|
grouped_forward = group_skill_paths(docs)
|
|
grouped_reverse = group_skill_paths(list(reversed(docs)))
|
|
|
|
assert grouped_forward == grouped_reverse
|