🐛 fix(tsl-syntax): close lookup review gaps

This commit is contained in:
csh
2026-07-13 09:17:00 +08:00
parent 455661c936
commit d663c2dae5
2 changed files with 299 additions and 66 deletions
+150 -1
View File
@@ -72,6 +72,61 @@ class TslSyntaxLookupTests(unittest.TestCase):
result = lookup.query_sections("数据库左连接", "write")
self.assertEqual(result.matches[0].section.page.name, "14_ts_sql.md")
self.assertTrue(
any(reason.startswith("synonym=") for reason in result.matches[0].reasons)
)
def test_synonym_reason_only_appears_when_expansion_matches_candidate(self):
with tempfile.TemporaryDirectory() as tmp_dir:
references = Path(tmp_dir)
(references / "sample.md").write_text(
"# Sample\n\n"
"## 本篇职责\n\n"
"Synthetic synonym provenance.\n\n"
"## Exact\n\n"
"needle appears here without the expanded collection term.\n",
encoding="utf-8",
newline="\n",
)
result = lookup.query_sections(
"列表 needle", "explain", references_dir=references
)
exact = next(
match for match in result.matches if match.section.heading_path == ("Exact",)
)
self.assertFalse(
any(reason.startswith("synonym=") for reason in exact.reasons),
exact.reasons,
)
def test_ascii_synonym_and_intent_triggers_require_token_boundaries(self):
with tempfile.TemporaryDirectory() as tmp_dir:
references = Path(tmp_dir)
(references / "15_debug_and_profiler.md").write_text(
"# Debug Probe\n\n"
"## 本篇职责\n\n"
"Synthetic boundary probe.\n\n"
"## Probe\n\n"
"probe body.\n",
encoding="utf-8",
newline="\n",
)
result = lookup.query_sections(
"debugger programmer probe",
"explain",
references_dir=references,
)
probe = next(
match for match in result.matches if match.section.heading_path == ("Probe",)
)
self.assertFalse(
any(reason.startswith(("synonym=", "intent=")) for reason in probe.reasons),
probe.reasons,
)
def test_query_renders_compact_candidates_without_bodies_or_absolute_paths(self):
result = lookup.query_sections("函数 默认参数", "write", limit=5)
@@ -81,6 +136,8 @@ class TslSyntaxLookupTests(unittest.TestCase):
self.assertIn("## Candidate 1", rendered)
self.assertNotIn("```tsl", rendered)
self.assertNotIn(str(lookup.DEFAULT_REFERENCES_DIR.resolve()), rendered)
self.assertNotIn("Why: `rank=", rendered)
self.assertRegex(rendered, r"Why: `[^`]*(intent|heading|identifier|body)=\d+")
self.assertLess(len(rendered.encode("utf-8")), 8192)
def test_query_echo_is_json_encoded_and_cannot_inject_markdown(self):
@@ -96,6 +153,31 @@ class TslSyntaxLookupTests(unittest.TestCase):
self.assertEqual(rendered.count("## Match 999"), 1)
self.assertNotIn("\n## Match 999\n", rendered)
def test_query_echo_escapes_all_unicode_line_separators(self):
for separator in ("\u0085", "\u2028", "\u2029"):
with self.subTest(separator=hex(ord(separator))):
query = f"数组{separator}## Candidate 999"
rendered = lookup.render_candidates(
lookup.query_sections(query, "explain")
)
self.assertNotIn(separator, rendered)
self.assertIn(f"\\u{ord(separator):04x}", rendered)
self.assertNotIn("\n## Candidate 999\n", rendered)
def test_mixed_language_paraphrases_ignore_unknown_ascii_fillers(self):
cases = (
("please 帮我写个最简单能跑的天软脚本", "write", "01_quickstart.md"),
("TSL 中两个表如何做左外连接并聚合排序", "write", "14_ts_sql.md"),
("debug 一下程序的性能问题", "diagnose", "15_debug_and_profiler.md"),
("请写一个可以运行的最小 Tinysoft program", "write", "01_quickstart.md"),
)
for query, mode, expected_page in cases:
with self.subTest(query=query):
result = lookup.query_sections(query, mode)
self.assertTrue(result.matches, query)
self.assertEqual(result.matches[0].section.page.name, expected_page)
def test_section_is_only_cli_path_that_returns_body(self):
result = lookup.query_sections("基础函数", "write", limit=5)
section = result.matches[0].section
@@ -124,6 +206,61 @@ class TslSyntaxLookupTests(unittest.TestCase):
self.assertNotIn(section.body.rstrip(), query_completed.stdout)
self.assertIn(section.body.rstrip(), section_completed.stdout)
def test_body_only_match_prefers_deepest_section(self):
with tempfile.TemporaryDirectory() as tmp_dir:
references = Path(tmp_dir)
(references / "sample.md").write_text(
"# Sample\n\n"
"## 本篇职责\n\n"
"Synthetic hierarchy.\n\n"
"## Parent\n\n"
"Parent introduction.\n\n"
"### Child\n\n"
"Child introduction.\n\n"
"#### Exact Leaf\n\n"
"needle appears only in the leaf body.\n",
encoding="utf-8",
newline="\n",
)
result = lookup.query_sections(
"needle", "explain", references_dir=references
)
self.assertEqual(result.matches[0].section.heading_path[-1], "Exact Leaf")
def test_limit_applies_to_final_rendered_candidate_list(self):
for limit in (1, 2, 5):
with self.subTest(limit=limit):
result = lookup.query_sections("函数 默认参数", "write", limit=limit)
rendered = lookup.render_candidates(result)
self.assertEqual(rendered.count("## Candidate "), limit)
def test_required_context_stays_required_when_query_also_matches_it(self):
result = lookup.query_sections("语言核心事实速查", "write", limit=5)
rendered = lookup.render_candidates(result)
required_id = "01_quickstart--语言核心事实速查"
self.assertIn(
f"Required: yes\nSection ID: `{required_id}`",
rendered,
)
self.assertEqual(rendered.count(f"Section ID: `{required_id}`"), 1)
def test_displayed_scores_are_sorted_descending(self):
for query, mode in (
("函数 默认参数", "write"),
("变量怎么改", "write"),
("数组", "diagnose"),
("invalid statement 声明区", "diagnose"),
("程序慢怎么计时找瓶颈", "diagnose"),
):
with self.subTest(query=query, mode=mode):
result = lookup.query_sections(query, mode, limit=5)
scores = [match.score for match in result.matches]
self.assertEqual(scores, sorted(scores, reverse=True))
def test_parser_creates_unique_h2_h3_h4_section_ids(self):
sections = lookup.load_sections(lookup.DEFAULT_REFERENCES_DIR)
ids = [section.id for section in sections]
@@ -143,6 +280,14 @@ class TslSyntaxLookupTests(unittest.TestCase):
self.assertNotEqual(star, double_star)
self.assertNotRegex(double_star, r"-2$")
def test_all_promised_symbolic_heading_slugs_are_distinct(self):
ids = {
lookup.section_id("sample.md", ("Examples", heading))
for heading in ("with *", "with **", "operator[]", "walk ::", "walk :.")
}
self.assertEqual(len(ids), 5)
def test_check_requires_one_nonempty_duty_section_per_page(self):
with tempfile.TemporaryDirectory() as tmp_dir:
references = Path(tmp_dir)
@@ -206,6 +351,7 @@ class TslSyntaxLookupTests(unittest.TestCase):
self.assertEqual(result.prelude, [])
def test_no_match_returns_exit_code_two_in_every_mode(self):
query = "夔魍魉xyzqv"
for mode in ("write", "diagnose", "explain"):
with self.subTest(mode=mode):
completed = subprocess.run(
@@ -213,7 +359,7 @@ class TslSyntaxLookupTests(unittest.TestCase):
sys.executable,
str(SCRIPT),
"--query",
"不存在的孤立语法词xyz",
query,
"--mode",
mode,
],
@@ -222,6 +368,9 @@ class TslSyntaxLookupTests(unittest.TestCase):
encoding="utf-8",
)
self.assertEqual(completed.returncode, 2)
self.assertIn("# TSL Syntax Candidates", completed.stdout)
self.assertIn(f'Query: "{query}"', completed.stdout)
self.assertIn("no matching sections", completed.stderr)
def test_missing_section_returns_nearest_section_ids(self):
with tempfile.TemporaryDirectory() as tmp_dir: