"""app/search/skills 模块的单元测试。 覆盖 parse_frontmatter、strip_frontmatter、SkillRegistry、discover_skills。 """ from __future__ import annotations from typing import TYPE_CHECKING import pytest from app.search.skills import ( SkillRegistry, discover_skills, parse_frontmatter, strip_frontmatter, ) if TYPE_CHECKING: from pathlib import Path # ── parse_frontmatter ────────────────────────────────────────────── class TestParseFrontmatter: """parse_frontmatter 的测试集。""" def test_normal(self) -> None: """正常 frontmatter 应解析出目标字段。""" text = ( "---\n" "name: my_skill\n" 'description: "A cool skill"\n' "always: true\n" "task_type: qa\n" "---\n" "Body text here.\n" ) result = parse_frontmatter(text) assert result == { "name": "my_skill", "description": "A cool skill", "always": "true", "task_type": "qa", } def test_missing_closing_delimiter(self) -> None: """缺少结束 --- 应返回空字典。""" text = "---\nname: orphan\ndescription: no end\n" assert parse_frontmatter(text) == {} def test_no_frontmatter(self) -> None: """不以 --- 开头的文本应返回空字典。""" text = "Just plain markdown.\n" assert parse_frontmatter(text) == {} def test_ignores_unknown_fields(self) -> None: """非目标字段应被忽略。""" text = "---\nname: s1\nauthor: someone\n---\nBody\n" result = parse_frontmatter(text) assert result == {"name": "s1"} assert "author" not in result def test_single_quoted_value(self) -> None: """单引号包裹的值应去除引号。""" text = "---\nname: 'quoted_name'\n---\nBody\n" result = parse_frontmatter(text) assert result["name"] == "quoted_name" # ── strip_frontmatter ────────────────────────────────────────────── class TestStripFrontmatter: """strip_frontmatter 的测试集。""" def test_strips_frontmatter(self) -> None: """正常情况下应去除 frontmatter,返回正文。""" text = "---\nname: x\n---\nBody content.\n" assert strip_frontmatter(text) == "Body content.\n" def test_no_frontmatter_returns_original(self) -> None: """无 frontmatter 时应返回原文。""" text = "No frontmatter here.\n" assert strip_frontmatter(text) == text def test_incomplete_frontmatter_returns_original(self) -> None: """不完整 frontmatter(缺结束符)应返回原文。""" text = "---\nname: x\nstill going\n" assert strip_frontmatter(text) == text # ── SkillRegistry ────────────────────────────────────────────────── class TestSkillRegistry: """SkillRegistry 的测试集。""" def test_read_normal(self, tmp_path: Path) -> None: """read 应返回去除 frontmatter 后的正文。""" skill_file = tmp_path / "skill_a.md" skill_file.write_text("---\nname: skill_a\n---\nSkill A body.\n", encoding="utf-8") registry = SkillRegistry() registry.set_paths({"skill_a": skill_file}) assert registry.read("skill_a") == "Skill A body.\n" def test_read_unregistered_raises_key_error(self) -> None: """读取未注册的技能应抛出 KeyError。""" registry = SkillRegistry() with pytest.raises(KeyError): registry.read("nonexistent") # ── discover_skills ──────────────────────────────────────────────── class TestDiscoverSkills: """discover_skills 的测试集。""" def _write_skill(self, path: Path, content: str) -> None: """辅助方法:写入技能文件。""" path.write_text(content, encoding="utf-8") def test_always_skill(self, tmp_path: Path) -> None: """always=true 的技能应出现在 always_text 中。""" self._write_skill( tmp_path / "always_skill.md", "---\nname: a1\ndescription: always on\nalways: true\n---\nAlways body.\n", ) always_text, task_map, catalog_text, registry = discover_skills(tmp_path) assert "Always body." in always_text assert task_map == {} assert "a1" not in catalog_text def test_task_type_skill(self, tmp_path: Path) -> None: """有 task_type 的非 always 技能应出现在 task_skill_map 中。""" self._write_skill( tmp_path / "task_skill.md", "---\nname: t1\ndescription: task skill\ntask_type: qa\n---\nTask body.\n", ) always_text, task_map, catalog_text, registry = discover_skills(tmp_path) assert always_text == "" assert task_map == {"qa": "Task body.\n"} assert "t1" in catalog_text def test_catalog_skill(self, tmp_path: Path) -> None: """普通技能应出现在 catalog_text 和 registry 中。""" self._write_skill( tmp_path / "cat_skill.md", "---\nname: c1\ndescription: catalog skill\n---\nCatalog body.\n", ) always_text, task_map, catalog_text, registry = discover_skills(tmp_path) assert always_text == "" assert task_map == {} assert "**c1**" in catalog_text assert "catalog skill" in catalog_text assert registry.read("c1") == "Catalog body.\n" def test_empty_directory(self, tmp_path: Path) -> None: """空目录应返回所有空值。""" always_text, task_map, catalog_text, registry = discover_skills(tmp_path) assert always_text == "" assert task_map == {} assert catalog_text == "" def test_nonexistent_directory(self, tmp_path: Path) -> None: """不存在的目录应返回所有空值。""" missing = tmp_path / "no_such_dir" always_text, task_map, catalog_text, registry = discover_skills(missing) assert always_text == "" assert task_map == {} assert catalog_text == "" def test_mixed_skills(self, tmp_path: Path) -> None: """混合 always / task_type / catalog 技能应正确分类。""" self._write_skill( tmp_path / "01_always.md", "---\nname: a1\ndescription: always\nalways: true\n---\nA body.\n", ) self._write_skill( tmp_path / "02_task.md", "---\nname: t1\ndescription: task\ntask_type: summary\n---\nT body.\n", ) self._write_skill( tmp_path / "03_catalog.md", "---\nname: c1\ndescription: catalog\n---\nC body.\n", ) always_text, task_map, catalog_text, registry = discover_skills(tmp_path) assert "A body." in always_text assert task_map == {"summary": "T body.\n"} assert "**t1**" in catalog_text assert "**c1**" in catalog_text # always 技能不应出现在 catalog 或 registry 中 assert "a1" not in catalog_text def test_skip_no_name(self, tmp_path: Path) -> None: """没有 name 字段的技能文件应被跳过。""" self._write_skill( tmp_path / "bad.md", "---\ndescription: no name\n---\nBody.\n", ) always_text, task_map, catalog_text, registry = discover_skills(tmp_path) assert always_text == "" assert task_map == {} assert catalog_text == ""