from __future__ import annotations from pathlib import Path from pathlib import PurePosixPath import pytest from personal_mcp.registry.ingest.document import MarkdownDocument from personal_mcp.registry.ingest.document import get_frontmatter_delim_idx from personal_mcp.registry.ingest.document import get_raw_frontmatter from personal_mcp.registry.ingest.document import walk_resources pytestmark = pytest.mark.unit class TestMarkdownDocument: """Covers MarkdownDocument construction and derived properties.""" class TestFromRoot: """Covers loading markdown documents from a resource root.""" def test_keys_by_relpath(self, tmp_path: Path) -> None: """Ensures from_root returns a mapping keyed by relative path.""" skills_dir = tmp_path / "skills" / "demo" skills_dir.mkdir(parents=True) (skills_dir / "SKILL.md").write_text("# demo\n", encoding="utf-8") docs = MarkdownDocument.from_root(tmp_path) assert set(docs) == {PurePosixPath("skills/demo/SKILL.md")} def test_loads_markdown_only(self, tmp_path: Path) -> None: """Ensures from_root includes only markdown resources.""" (tmp_path / "a.md").write_text("a\n", encoding="utf-8") (tmp_path / "b.MD").write_text("b\n", encoding="utf-8") (tmp_path / "c.txt").write_text("c\n", encoding="utf-8") docs = MarkdownDocument.from_root(tmp_path) assert set(docs) == {PurePosixPath("a.md"), PurePosixPath("b.MD")} def test_preserves_relpaths(self, tmp_path: Path) -> None: """Ensures from_root preserves PurePosixPath-style relative paths.""" nested = tmp_path / "skills" / "slug" nested.mkdir(parents=True) (nested / "SKILL.md").write_text("# slug\n", encoding="utf-8") docs = MarkdownDocument.from_root(tmp_path) [relpath] = docs.keys() assert isinstance(relpath, PurePosixPath) assert relpath == PurePosixPath("skills/slug/SKILL.md") class TestFromResource: """Covers loading a single markdown document from a resource.""" def test_reads_utf8(self, tmp_path: Path) -> None: """Ensures from_resource reads text using UTF-8.""" resource = tmp_path / "index.md" resource.write_text("caf\u00e9\n", encoding="utf-8") doc = MarkdownDocument.from_resource(PurePosixPath("index.md"), resource) assert doc.content == "caf\u00e9\n" def test_sets_frontmatter(self, tmp_path: Path) -> None: """Ensures from_resource stores frontmatter when delimiters exist.""" resource = tmp_path / "index.md" resource.write_text("---\nname: demo\n---\n# body\n", encoding="utf-8") doc = MarkdownDocument.from_resource(PurePosixPath("index.md"), resource) assert doc.frontmatter == "name: demo" def test_none_frontmatter(self, tmp_path: Path) -> None: """Ensures from_resource sets frontmatter to None when absent.""" resource = tmp_path / "index.md" resource.write_text("# no frontmatter\n", encoding="utf-8") doc = MarkdownDocument.from_resource(PurePosixPath("index.md"), resource) assert doc.frontmatter is None class TestSkillSlugProperty: """Covers skill_slug derivation from document relative paths.""" def test_returns_slug(self) -> None: """Ensures skill_slug returns the slug for valid skills paths.""" doc = MarkdownDocument(relpath=PurePosixPath("skills/demo/SKILL.md"), content="#") assert doc.skill_slug == "demo" def test_none_for_non_skill(self) -> None: """Ensures skill_slug is None for non-skills paths.""" doc = MarkdownDocument(relpath=PurePosixPath("docs/index.md"), content="#") assert doc.skill_slug is None def test_none_for_incomplete_skill(self) -> None: """Ensures skill_slug is None for incomplete skills paths.""" doc = MarkdownDocument(relpath=PurePosixPath("skills/demo.md"), content="#") assert doc.skill_slug is None class TestPromptSlugProperty: """Covers prompt_slug derivation from document relative paths.""" def test_returns_slug(self) -> None: """Ensures prompt_slug returns the slug for valid prompt paths.""" doc = MarkdownDocument(relpath=PurePosixPath("prompts/demo/PROMPT.md"), content="#") assert doc.prompt_slug == "demo" def test_none_for_non_prompt(self) -> None: """Ensures prompt_slug is None for non-prompt paths.""" doc = MarkdownDocument(relpath=PurePosixPath("docs/index.md"), content="#") assert doc.prompt_slug is None def test_none_for_incomplete_prompt(self) -> None: """Ensures prompt_slug is None for incomplete prompt paths.""" doc = MarkdownDocument(relpath=PurePosixPath("prompts/demo.md"), content="#") assert doc.prompt_slug is None class TestWalkResources: """Covers recursive resource walking and markdown filtering behavior.""" def test_yields_markdown(self, tmp_path: Path) -> None: """Ensures walk_resources yields markdown files from nested directories.""" nested = tmp_path / "skills" / "alpha" nested.mkdir(parents=True) (nested / "SKILL.md").write_text("# alpha\n", encoding="utf-8") (tmp_path / "index.md").write_text("# index\n", encoding="utf-8") relpaths = [path for path, _ in walk_resources(tmp_path)] assert relpaths == [ PurePosixPath("index.md"), PurePosixPath("skills/alpha/SKILL.md"), ] def test_ignores_other_suffixes(self, tmp_path: Path) -> None: """Ensures walk_resources excludes files with non-matching suffixes.""" (tmp_path / "a.md").write_text("a\n", encoding="utf-8") (tmp_path / "b.txt").write_text("b\n", encoding="utf-8") (tmp_path / "c.json").write_text("c\n", encoding="utf-8") relpaths = [path for path, _ in walk_resources(tmp_path)] assert relpaths == [PurePosixPath("a.md")] def test_sorted_output(self, tmp_path: Path) -> None: """Ensures walk_resources yields entries in sorted child-name order.""" (tmp_path / "b.md").write_text("b\n", encoding="utf-8") (tmp_path / "a.md").write_text("a\n", encoding="utf-8") (tmp_path / "skills").mkdir() (tmp_path / "skills" / "z.md").write_text("z\n", encoding="utf-8") relpaths = [path for path, _ in walk_resources(tmp_path)] assert relpaths == [ PurePosixPath("a.md"), PurePosixPath("b.md"), PurePosixPath("skills/z.md"), ] def test_applies_prefix(self, tmp_path: Path) -> None: """Ensures walk_resources prepends the provided prefix to relpaths.""" (tmp_path / "index.md").write_text("# index\n", encoding="utf-8") relpaths = [path for path, _ in walk_resources(tmp_path, prefix=PurePosixPath("docs"))] assert relpaths == [PurePosixPath("docs/index.md")] class TestFrontmatterParsing: """Covers frontmatter delimiter discovery and raw block extraction.""" def test_extracts_between_delimiters(self) -> None: """Ensures get_raw_frontmatter returns lines between first delimiters.""" raw = "---\nname: demo\ntags:\n - test\n---\n# body\n" assert get_raw_frontmatter(raw) == "name: demo\ntags:\n - test" def test_none_without_two_delimiters(self) -> None: """Ensures get_raw_frontmatter returns None without two delimiters.""" raw = "---\nname: demo\n# body\n" assert get_raw_frontmatter(raw) is None def test_allows_leading_whitespace(self) -> None: """Ensures delimiter detection accepts lines with leading whitespace.""" raw = " ---\nname: demo\n ---\n# body\n" assert list(get_frontmatter_delim_idx(raw)) == [0, 2]