import re import unittest from pathlib import Path, PurePosixPath ROOT = Path(__file__).resolve().parents[1] SKILL_DIR = ROOT / "skills" / "tsl-syntax-reference" SKILL_FILE = SKILL_DIR / "SKILL.md" REFERENCES_DIR = SKILL_DIR / "references" COMPATIBILITY_INDEX = ROOT / "docs" / "tsl" / "syntax" / "index.md" RULESET_FILE = ROOT / "rulesets" / "tsl" / "index.md" TOPIC_REFERENCES = { "01_quickstart.md", "02_core_model.md", "03_values_and_literals.md", "04_variables_and_constants.md", "05_functions_and_calls.md", "06_expressions_and_operators.md", "07_control_flow.md", "08_objects_and_classes.md", "09_units_and_scope.md", "10_runtime_context_and_with.md", "11_pitfalls.md", "12_matrix_and_collections.md", "13_resultset_and_filters.md", "14_ts_sql.md", "15_debug_and_profiler.md", "16_lexical_structure_and_compile_options.md", "17_types_and_conversions.md", "18_external_calls_and_threads.md", "19_namespace_libpath_and_unit_runtime.md", "20_object_runtime_and_introspection.md", "21_builtin_runtime_objects.md", "22_matrix_deep_dive.md", "23_fmarray.md", "24_object_overloads_and_iteration.md", } MARKDOWN_LINK = re.compile(r"!?\[[^\]]*\]\(([^)]+)\)") def read_text(path: Path) -> str: return path.read_text(encoding="utf-8") def frontmatter(path: Path) -> dict[str, str]: text = read_text(path) match = re.match(r"\A---\s*\n(.*?)\n---\s*\n", text, re.DOTALL) if not match: raise AssertionError(f"missing YAML frontmatter: {path}") fields: dict[str, str] = {} for line in match.group(1).splitlines(): key, separator, value = line.partition(":") if separator: fields[key.strip()] = value.strip().strip("\"'") return fields def local_link_targets(text: str) -> list[str]: text = re.sub(r"```.*?```", "", text, flags=re.DOTALL) text = re.sub(r"`[^`\n]*`", "", text) targets = [] for match in MARKDOWN_LINK.finditer(text): target = match.group(1).strip().split(maxsplit=1)[0].strip("<>") if target.startswith(("#", "http://", "https://", "mailto:")): continue targets.append(target) return targets class TslSyntaxReferenceSkillStructureTest(unittest.TestCase): def test_local_link_targets_includes_local_images(self) -> None: text = "![local diagram](images/router.png)\n![remote](https://example.com/router.png)" self.assertEqual(local_link_targets(text), ["images/router.png"]) def test_skill_name_matches_directory(self) -> None: self.assertEqual(frontmatter(SKILL_FILE)["name"], SKILL_DIR.name) def test_frontmatter_description_covers_tsl_and_tsf_tasks(self) -> None: description = frontmatter(SKILL_FILE)["description"] self.assertRegex(description, r"(?i)\bTSL\b") self.assertRegex(description, r"(?i)\bTSF\b") self.assertRegex(description, r"写|编写|修改|审查|解释|语法错误") def test_reference_inventory_contains_topics_without_router(self) -> None: actual = {path.name for path in REFERENCES_DIR.iterdir() if path.is_file()} self.assertEqual(actual, TOPIC_REFERENCES) self.assertFalse((REFERENCES_DIR / "index.md").exists()) def test_lookup_script_is_bundled(self) -> None: self.assertTrue((SKILL_DIR / "scripts" / "lookup.py").is_file()) def test_manual_router_protocol_is_absent(self) -> None: corpus = read_text(SKILL_FILE) + "\n" + "\n".join( read_text(path) for path in REFERENCES_DIR.glob("*.md") ) for forbidden in ("路由中心", "选择一个主专题", "候选页继续判断"): self.assertNotIn(forbidden, corpus) def test_json_router_is_absent(self) -> None: self.assertFalse((REFERENCES_DIR / "00_agent_index.json").exists()) self.assertFalse((ROOT / "docs" / "tsl" / "syntax" / "00_agent_index.json").exists()) def test_references_do_not_escape_skill(self) -> None: for path in REFERENCES_DIR.glob("*.md"): for target in local_link_targets(read_text(path)): link_path = target.split("#", 1)[0].replace("\\", "/") self.assertNotIn("docs/tsl/", link_path, f"{path}: {target}") self.assertFalse(link_path.startswith("/"), f"{path}: {target}") self.assertNotIn("..", PurePosixPath(link_path).parts, f"{path}: {target}") def test_reference_markdown_links_resolve(self) -> None: for path in REFERENCES_DIR.glob("*.md"): for target in local_link_targets(read_text(path)): link_path = target.split("#", 1)[0] self.assertTrue((REFERENCES_DIR / link_path).is_file(), f"{path}: {target}") def test_skill_uses_lookup_as_its_only_retrieval_entry(self) -> None: text = read_text(SKILL_FILE) self.assertIn("scripts/lookup.py", text) for mode in ("write", "diagnose", "explain"): self.assertIn(f"--mode {mode}", text) self.assertEqual(local_link_targets(text), []) self.assertNotIn("references/index.md", text) def test_skill_does_not_bundle_external_domains(self) -> None: forbidden = { "naming.md", "code_style.md", "toolchain.md", "modules", "tsl-api-reference", "00_agent_index.json", } actual = {path.name for path in SKILL_DIR.rglob("*")} self.assertTrue(forbidden.isdisjoint(actual), forbidden & actual) def test_compatibility_index_contains_no_syntax_facts(self) -> None: text = read_text(COMPATIBILITY_INDEX) self.assertIn("../../../skills/tsl-syntax-reference/SKILL.md", text) for forbidden in ( "```", "代码块身份", "01_quickstart.md", "02_core_model.md", "| 任务", ): self.assertNotIn(forbidden, text) def test_ruleset_file_remains_tsl_router(self) -> None: self.assertTrue(RULESET_FILE.is_file()) text = read_text(RULESET_FILE) for route in ( "tsl-syntax-reference", "tsl-api-reference", "naming.md", "code_style.md", "toolchain.md", "modules/index.md", "项目", ): self.assertIn(route, text) def test_ruleset_routes_both_tsl_skills(self) -> None: text = read_text(RULESET_FILE) self.assertRegex(text, r"语法[^\n]*tsl-syntax-reference|tsl-syntax-reference[^\n]*语法") self.assertRegex(text, r"API|函数") self.assertIn("tsl-api-reference", text) def test_ruleset_syntax_constraints_moved_to_skill(self) -> None: ruleset = read_text(RULESET_FILE) owner = read_text(SKILL_FILE) + "\n" + "\n".join( read_text(path) for path in REFERENCES_DIR.glob("*.md") ) self.assertRegex(ruleset, r"缺失|不可用") self.assertRegex(ruleset, r"停止|阻断") for forbidden in ( "```tsl", "代码块身份", ".tsl` / `.tsf` 后缀由用户指定时", ".tsl` 代码需要本文件内函数或类时", ): self.assertNotIn(forbidden, ruleset) self.assertIn("代码块身份", owner) self.assertIn(".tsl", owner) self.assertIn(".tsf", owner) def test_static_docs_route_to_fact_owner(self) -> None: routes = { ROOT / "docs" / "tsl" / "index.md": "../../skills/tsl-syntax-reference/SKILL.md", ROOT / "docs" / "tsl" / "naming.md": "../../skills/tsl-syntax-reference/SKILL.md", ROOT / "docs" / "tsl" / "code_style.md": "../../skills/tsl-syntax-reference/SKILL.md", ROOT / "docs" / "index.md": "../skills/tsl-syntax-reference/SKILL.md", } legacy_deep_link = re.compile(r"(?:docs/tsl/)?syntax/(?:0[1-9]|1[0-9]|2[0-4])_") for path, expected_link in routes.items(): text = read_text(path) self.assertIn(expected_link, text, path) self.assertIsNone(legacy_deep_link.search(text), path) if __name__ == "__main__": unittest.main()