Files
playbook/test/test_tsl_syntax_reference_skill.py
T

204 lines
8.0 KiB
Python

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()