playbook/test/test_tsl_entrypoints_consis...

1599 lines
78 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import re
import unittest
from pathlib import Path
ROOT = Path(__file__).resolve().parents[1]
RULESET_TSL = ROOT / "rulesets" / "tsl" / "index.md"
TSL_QUICKSTART = ROOT / "docs" / "tsl" / "syntax" / "01_quickstart.md"
TSL_CORE_MODEL = ROOT / "docs" / "tsl" / "syntax" / "02_core_model.md"
TSL_VALUES = ROOT / "docs" / "tsl" / "syntax" / "03_values_and_literals.md"
TSL_VARIABLES = ROOT / "docs" / "tsl" / "syntax" / "04_variables_and_constants.md"
TSL_FUNCTIONS = ROOT / "docs" / "tsl" / "syntax" / "05_functions_and_calls.md"
TSL_EXPRESSIONS = ROOT / "docs" / "tsl" / "syntax" / "06_expressions_and_operators.md"
TSL_CONTROL_FLOW = ROOT / "docs" / "tsl" / "syntax" / "07_control_flow.md"
TSL_TS_SQL = ROOT / "docs" / "tsl" / "syntax" / "14_ts_sql.md"
TSL_TYPES = ROOT / "docs" / "tsl" / "syntax" / "17_types_and_conversions.md"
TSL_EXTERNAL = ROOT / "docs" / "tsl" / "syntax" / "18_external_calls_and_threads.md"
TSL_MATRIX_DEEP_DIVE = ROOT / "docs" / "tsl" / "syntax" / "22_matrix_deep_dive.md"
TSL_FMARRAY = ROOT / "docs" / "tsl" / "syntax" / "23_fmarray.md"
TSL_OBJECT_RUNTIME = ROOT / "docs" / "tsl" / "syntax" / "20_object_runtime_and_introspection.md"
TSL_BUILTIN_RUNTIME_OBJECTS = ROOT / "docs" / "tsl" / "syntax" / "21_builtin_runtime_objects.md"
README = ROOT / "README.md"
PLAYBOOK_EXAMPLE = ROOT / "playbook.toml.example"
SKILLS_DOC = ROOT / "SKILLS.md"
TEMPLATES_CI_README = ROOT / "templates" / "ci" / "README.md"
TEMPLATES_README = ROOT / "templates" / "README.md"
REMOVED_TSL_GUIDE = ROOT / "skills" / "tsl-guide"
TSL_REFERENCE_INDEX = ROOT / "docs" / "tsl" / "reference" / "index.md"
TSL_REFERENCE_UNAVAILABLE = ROOT / "docs" / "tsl" / "reference" / "unavailable_methods.md"
TSL_MODULES_INDEX = ROOT / "docs" / "tsl" / "modules" / "index.md"
TSL_TOOLCHAIN = ROOT / "docs" / "tsl" / "toolchain.md"
TSL_REMAINING_SYNTAX_AGENT_SECTIONS = {
"07_control_flow.md": "智能体控制流判断流程",
"08_objects_and_classes.md": "智能体对象/类判断流程",
"09_units_and_scope.md": "智能体 unit/作用域判断流程",
"10_runtime_context_and_with.md": "智能体运行时上下文判断流程",
"11_pitfalls.md": "智能体常见误写判断流程",
"12_matrix_and_collections.md": "智能体数组/矩阵样数据判断流程",
"13_resultset_and_filters.md": "智能体结果集/过滤判断流程",
"14_ts_sql.md": "智能体 TS-SQL 判断流程",
"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": "智能体命名空间/Libpath 判断流程",
"20_object_runtime_and_introspection.md": "智能体对象运行时/反射判断流程",
"21_builtin_runtime_objects.md": "智能体内置运行时对象判断流程",
"22_matrix_deep_dive.md": "智能体矩阵深水判断流程",
"23_fmarray.md": "智能体 FMArray 判断流程",
"24_object_overloads_and_iteration.md": "智能体对象重载/迭代判断流程",
}
TSL_SYNTAX_MAINLINE_FILES = [
"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",
"12_matrix_and_collections.md",
"13_resultset_and_filters.md",
"14_ts_sql.md",
"15_debug_and_profiler.md",
]
TSL_SYNTAX_DEEP_DIVE_FILES = [
"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",
]
class TslEntrypointsConsistencyTests(unittest.TestCase):
def test_ruleset_lists_canonical_tsl_layers(self):
text = RULESET_TSL.read_text(encoding="utf-8")
self.assertIn("docs/tsl/index.md", text)
self.assertIn("docs/tsl/reference/catalog/datawarehouse.md", text)
self.assertIn("docs/tsl/modules/pytsl_api.md", text)
self.assertIn("docs/tsl/modules/tsbacktesting.md", text)
self.assertIn("docs/tsl/modules/index.md", text)
self.assertIn("docs/tsl/reference/index.md", text)
tsl_index = (ROOT / "docs/tsl/index.md").read_text(encoding="utf-8")
self.assertIn("syntax/index.md", tsl_index)
def test_readme_only_lists_two_official_deployment_routes(self):
text = README.read_text(encoding="utf-8")
self.assertIn("方式一git subtree", text)
self.assertIn("方式二:外部 clone 后执行部署", text)
self.assertIn("- `rulesets/typescript/index.md`TypeScript 核心约定", text)
self.assertIn("`project_root`:目标项目根目录", text)
self.assertIn("`playbook_root`:相对于 `project_root` 的项目内 Playbook 根目录", text)
self.assertIn("不是外部 clone 出来的 Playbook 仓库路径", text)
self.assertIn("外部 clone 场景下必须显式填写 `playbook_root`", text)
self.assertNotIn("方式二:手动复制快照", text)
self.assertNotIn("方式三CLI 裁剪复制", text)
self.assertNotIn("如果省略 `playbook_root`,默认仍部署到 `docs/standards/playbook`", text)
def test_playbook_example_defines_playbook_root_as_target_path(self):
text = PLAYBOOK_EXAMPLE.read_text(encoding="utf-8")
self.assertIn('playbook_root = "docs/standards/playbook"', text)
self.assertIn('install_mode = "subtree"', text)
self.assertIn("相对于 project_root", text)
self.assertIn("不是外部 clone 的 playbook 路径", text)
self.assertIn("snapshot 表示从外部 clone 安装快照", text)
self.assertNotIn("target_dir", text)
def test_deployment_docs_do_not_reference_legacy_terms(self):
self.assertNotIn("vendoring", README.read_text(encoding="utf-8"))
self.assertNotIn("`.tmp`", README.read_text(encoding="utf-8"))
self.assertNotIn("vendoring", SKILLS_DOC.read_text(encoding="utf-8"))
self.assertNotIn("vendoring", TEMPLATES_CI_README.read_text(encoding="utf-8"))
def test_repo_docs_use_platform_neutral_skills_source_dir(self):
readme_text = README.read_text(encoding="utf-8")
skills_text = SKILLS_DOC.read_text(encoding="utf-8")
templates_text = TEMPLATES_README.read_text(encoding="utf-8")
self.assertIn("`skills/`", readme_text)
self.assertIn("`skills/`", skills_text)
self.assertIn("├── skills/", templates_text)
self.assertNotIn("`codex/skills/`", readme_text)
self.assertNotIn("`codex/skills/`", skills_text)
self.assertNotIn("├── codex/skills/", templates_text)
def test_repo_no_longer_ships_tsl_guide_skill(self):
self.assertFalse(REMOVED_TSL_GUIDE.exists())
self.assertNotIn("tsl-guide", README.read_text(encoding="utf-8"))
self.assertNotIn("tsl-guide", SKILLS_DOC.read_text(encoding="utf-8"))
self.assertNotIn("$tsl-guide", RULESET_TSL.read_text(encoding="utf-8"))
def test_tsl_ruleset_describes_agent_decision_router(self):
text = RULESET_TSL.read_text(encoding="utf-8")
self.assertIn("# TSL 智能体规则", text)
self.assertEqual(len(text.splitlines()), 45)
self.assertIn("作用:控制智能体在阅读、修改、生成 TSL 代码前的判断顺序", text)
self.assertIn("本文件不是 TSL 语法手册", text)
self.assertIn("禁止凭 Pascal、Python、JavaScript、TypeScript", text)
self.assertIn("代码生成协议", text)
self.assertIn("TSL 核心事实", text)
self.assertIn("任务路由", text)
self.assertIn("文档事实使用策略", text)
self.assertRegex(
text,
r"\| 语法、文件模型、最短骨架、反例\s+\| `docs/tsl/index\.md`\s+\|",
)
self.assertNotIn("最终 TSL 自检", text)
self.assertNotIn("修改纪律", text)
self.assertNotIn("文档入口", text)
self.assertNotIn("## 安全规则", text)
self.assertNotIn("`docs/tsl/index.md` 或 `docs/tsl/syntax/index.md`", text)
self.assertNotIn("代码块身份:可直接照写示例", text)
self.assertNotIn("funcext", text)
def test_tsl_syntax_docs_keep_script_then_declarations_model(self):
quickstart = (ROOT / "docs/tsl/syntax/01_quickstart.md").read_text(
encoding="utf-8"
)
core_model = (ROOT / "docs/tsl/syntax/02_core_model.md").read_text(
encoding="utf-8"
)
functions = (ROOT / "docs/tsl/syntax/05_functions_and_calls.md").read_text(
encoding="utf-8"
)
pitfalls = (ROOT / "docs/tsl/syntax/11_pitfalls.md").read_text(
encoding="utf-8"
)
for text in (quickstart, core_model, functions, pitfalls):
self.assertIn("语句区", text)
self.assertIn("声明区", text)
self.assertIn("test();", quickstart)
self.assertIn("function test();", quickstart)
self.assertIn("脚本语句后可以接函数声明", functions)
self.assertIn("不要在声明区后面继续写脚本语句", pitfalls)
self.assertNotIn("不要把顶层函数定义和松散语句混写", quickstart)
self.assertNotIn("不要把顶层函数定义和松散语句混写", core_model)
self.assertNotIn("不要把顶层函数定义和松散语句混在同一个文件模型里", functions)
self.assertIn("后缀就是判断依据", quickstart)
self.assertIn("未给后缀", quickstart)
def test_tsl_agent_entrypoints_do_not_require_runtime_verification(self):
entrypoints = [
RULESET_TSL,
ROOT / "docs/tsl/index.md",
ROOT / "docs/tsl/syntax/index.md",
]
forbidden_phrases = [
"用当前解释器验证",
"本地用 `tsl` 验证",
"用 `tsl` 实测",
"最小实测",
"才写最小 `.tsl` / `.tsf` 例子",
]
for path in entrypoints:
text = path.read_text(encoding="utf-8")
for phrase in forbidden_phrases:
self.assertNotIn(phrase, text, msg=f"{phrase!r} found in {path}")
def test_tsl_reader_entrypoints_are_decision_routes_not_agent_process_docs(self):
entrypoints = [
ROOT / "docs/tsl/index.md",
ROOT / "docs/tsl/syntax/index.md",
]
for path in entrypoints:
with self.subTest(path=path.relative_to(ROOT)):
text = path.read_text(encoding="utf-8")
self.assertIn("任务信号", text)
self.assertIn("入口", text)
self.assertIn("阻断条件", text)
self.assertNotIn("智能体语法判断流程", text)
self.assertNotIn("智能体提交前最低自检", text)
self.assertNotIn("本套 TSL 文档面向智能体决策", text)
self.assertNotIn("## 决策顺序", text)
def test_tsl_docs_use_compact_uncertain_header(self):
docs_root = ROOT / "docs" / "tsl"
for path in docs_root.rglob("*.md"):
with self.subTest(path=path.relative_to(ROOT)):
text = path.read_text(encoding="utf-8")
self.assertNotIn("遇到不确定时跳转到", text)
def test_tsl_index_uses_documented_generation_facts(self):
text = (ROOT / "docs/tsl/index.md").read_text(encoding="utf-8")
self.assertIn("证据规则", text)
self.assertIn("停止条件", text)
self.assertIn("代码外形缺少 `代码块身份:可直接照写示例`", text)
self.assertIn("无文档事实、文件模型不明或项目执行事实缺失", text)
self.assertIn("语法页不承载执行环境、账户体系、真实接口或验证命令", text)
self.assertIn("项目执行信息会被写成通用 TSL 事实", text)
self.assertNotIn("## 路由冲突", text)
self.assertNotIn("## 生成前阻断条件", text)
def test_tsl_toolchain_is_template_not_execution_authority(self):
text = TSL_TOOLCHAIN.read_text(encoding="utf-8")
self.assertIn("# TSL 工具链与验证命令模板", text)
self.assertIn("文档类型:项目工具链模板页", text)
self.assertIn("不是通用执行手册", text)
self.assertIn("不是 TSL 语法事实", text)
self.assertIn("目标项目已真实填写", text)
self.assertIn("仍有 `<...>` 占位符时", text)
self.assertIn("不能作为执行依据", text)
self.assertIn("不提供真实可执行命令", text)
self.assertNotIn("真实 CLI 示例", text)
def test_tsl_execution_routes_do_not_treat_toolchain_template_as_facts(self):
tsl_index = (ROOT / "docs" / "tsl" / "index.md").read_text(encoding="utf-8")
code_style = (ROOT / "docs" / "tsl" / "code_style.md").read_text(
encoding="utf-8"
)
readme = README.read_text(encoding="utf-8")
self.assertIn(
"目标项目已真实填写且没有 `<...>` 占位符的 [toolchain.md](toolchain.md)",
tsl_index,
)
self.assertIn("验证命令或自动化入口", code_style)
self.assertIn("目标项目已真实填写且没有 `<...>` 占位符的 [toolchain.md](toolchain.md)", code_style)
self.assertNotIn(
"遇到不确定时:[naming.md](naming.md)、[syntax/index.md](syntax/index.md)、[toolchain.md](toolchain.md)",
code_style,
)
self.assertIn("已填写工具链事实", readme)
self.assertIn("模板未填写时不可作为执行依据", readme)
self.assertNotIn("完整语言手册、代码风格、工具链 | 最终权威", readme)
def test_tsl_naming_and_code_style_delegate_file_model_facts_to_core_model(self):
naming = (ROOT / "docs" / "tsl" / "naming.md").read_text(encoding="utf-8")
code_style = (ROOT / "docs" / "tsl" / "code_style.md").read_text(
encoding="utf-8"
)
self.assertIn("本页只规定命名后的实体如何取名", naming)
self.assertIn("syntax/02_core_model.md", naming)
self.assertNotIn("文件只能有一个顶层声明", naming)
self.assertNotIn("文件基名必须与该顶层声明名字一致", naming)
self.assertNotIn("部署到解释器 `funcext`", naming)
self.assertNotIn("语句区按顺序执行", naming)
self.assertNotIn("语句区在前,声明区在后", naming)
self.assertNotIn("语句区在前,并按顺序执行", naming)
self.assertIn("文件模型和顶层声明规则以", code_style)
self.assertIn("syntax/02_core_model.md", code_style)
self.assertNotIn("语句区在前并按顺序执行", code_style)
self.assertNotIn("部署到解释器 `funcext`", code_style)
self.assertNotIn("每个 `.tsf` 文件只能有一个顶层声明", code_style)
self.assertNotIn("文件基名必须与顶层声明同名", code_style)
def test_tsl_syntax_docs_do_not_keep_stale_manual_sequence_metadata(self):
self.assertFalse((ROOT / "docs/tsl/syntax/01_introduction.md").exists())
forbidden_phrases = [
"01_introduction.md",
"手册位置:",
"共 32 篇",
"上一篇:",
"下一篇:",
"完整吸收原 8 个",
"原 8 章",
"编号说明",
]
syntax_paths = sorted((ROOT / "docs" / "tsl" / "syntax").glob("*.md"))
numbered_paths = [path for path in syntax_paths if path.name != "index.md"]
numbers = []
for path in numbered_paths:
match = re.match(r"^(\d{2})_", path.name)
self.assertIsNotNone(match, msg=f"missing two-digit prefix: {path.name}")
numbers.append(int(match.group(1)))
self.assertEqual(list(range(1, len(numbered_paths) + 1)), numbers)
self.assertEqual("01_quickstart.md", numbered_paths[0].name)
for path in syntax_paths:
text = path.read_text(encoding="utf-8")
for phrase in forbidden_phrases:
self.assertNotIn(phrase, text, msg=f"{phrase!r} found in {path}")
def test_tsl_quickstart_is_agent_coding_gate(self):
text = TSL_QUICKSTART.read_text(encoding="utf-8")
self.assertIn("智能体快速落代码流程", text)
self.assertIn("先看用户有没有指定 `.tsl` / `.tsf` 后缀", text)
self.assertIn("再根据交付目标判断 `.tsl` 或 `.tsf`", text)
self.assertIn("入口流程、脚本任务或一次性执行逻辑用 `.tsl`", text)
self.assertIn("可复用交付物(函数、过程、类、模块或扩展文件)用 `.tsf`", text)
self.assertIn("如果只是脚本内部封装函数或类,仍按 `.tsl` 处理", text)
self.assertIn("只从 `代码块身份:可直接照写示例` 的骨架起手", text)
self.assertIn("不要发明语法", text)
self.assertIn("`procedure` 头后不允许写返回类型", text)
self.assertIn("不要在声明区后面继续追加脚本语句", text)
self.assertIn("普通对象创建默认优先 `new ClassName()`", text)
self.assertIn("`createObject(...)` 也是对象创建方式", text)
self.assertIn("obj := new MyClass();", text)
self.assertNotIn('obj := createObject("MyClass");', text)
def test_tsl_quickstart_labels_observable_outputs(self):
text = TSL_QUICKSTART.read_text(encoding="utf-8")
self.assertIn("代码块身份:输出片段", text)
self.assertIn("```text\ntest\n```", text)
self.assertIn("```text\n5\n```", text)
self.assertIn("```text\ntest1\n```", text)
self.assertIn("```text\nhello\n```", text)
def test_tsl_quickstart_omits_environment_verification_details(self):
text = TSL_QUICKSTART.read_text(encoding="utf-8")
for phrase in [
"Docker",
"docker exec",
"LD_LIBRARY_PATH",
"/data/workspace",
"U22Cli",
"gitea-runner",
]:
self.assertNotIn(phrase, text)
def test_tsl_core_model_is_agent_file_model_decision_page(self):
text = TSL_CORE_MODEL.read_text(encoding="utf-8")
self.assertIn("智能体文件模型判断流程", text)
self.assertIn("后缀是第一证据", text)
self.assertIn("入口流程、脚本任务或一次性执行逻辑", text)
self.assertIn("可复用交付物(函数、过程、类、模块或扩展文件)", text)
self.assertIn("只是脚本内部封装函数或类时,仍按 `.tsl` 处理", text)
self.assertIn("`.tsf` 顶层类声明", text)
self.assertIn("因为 `.tsl` 脚本内部需要函数或类,就自动改成 `.tsf`", text)
self.assertIn("类声明必须使用 `type Name = class ... end;`", text)
self.assertIn("普通对象创建默认优先 `new ClassName()`", text)
self.assertIn("`createObject(...)` 也是对象创建方式", text)
self.assertIn("obj := new MyClass();", text)
self.assertNotIn('obj := createObject("MyClass");', text)
self.assertIn("不要把 `.tsl` 写成只有顶层函数的模块", text)
self.assertIn("不要把 `.tsf` 写成会直接执行脚本语句的入口", text)
self.assertIn("没有文档证据时不要发明文件模型", text)
self.assertNotIn("误以为扩展名本身就完全决定能否写函数、类或 `unit`", text)
def test_tsl_core_model_examples_have_verified_outputs(self):
text = TSL_CORE_MODEL.read_text(encoding="utf-8")
self.assertIn("代码块身份:输出片段", text)
self.assertIn("```text\ntest\n```", text)
self.assertIn("```text\n5\n```", text)
self.assertIn("```text\n1\n```", text)
self.assertIn("```text\ninvalid statement\n```", text)
def test_tsl_core_model_omits_environment_verification_details(self):
text = TSL_CORE_MODEL.read_text(encoding="utf-8")
for phrase in [
"Docker",
"docker exec",
"LD_LIBRARY_PATH",
"/data/workspace",
"U22Cli",
"gitea-runner",
]:
self.assertNotIn(phrase, text)
def test_tsl_object_docs_prefer_new_for_plain_construction(self):
text = (ROOT / "docs/tsl/syntax/08_objects_and_classes.md").read_text(
encoding="utf-8"
)
self.assertIn("创建对象有两种方式:`new ClassName()` 最常用", text)
self.assertIn("[02_core_model.md](02_core_model.md)(优先)", text)
self.assertNotIn("[09_units_and_scope.md](09_units_and_scope.md)(优先)", text)
self.assertIn("命中对象反射 / 运行时状态、内置运行时对象、对象重载 / 迭代时", text)
self.assertIn("21_builtin_runtime_objects.md", text)
self.assertIn("24_object_overloads_and_iteration.md", text)
self.assertIn("`createObject(...)` 作为次选", text)
self.assertIn("普通本地类创建默认用 `new ClassName()`", text)
self.assertIn("普通本地类实例化默认生成 `new ClassName()`", text)
self.assertIn("普通类成员默认显式写 `public` 段", text)
self.assertIn("生成代码默认显式写 `public`", text)
self.assertIn("跨 `unit` 类路径创建和继承属于多文件边界", text)
self.assertIn("跨 `unit` 类路径骨架", text)
self.assertIn("跨 `unit` 父类路径骨架", text)
self.assertIn("type Class1 = class\npublic\n type Class2 = class", text)
self.assertNotIn("多文件文档事实", text)
self.assertNotIn("已在多文件环境下验证", text)
self.assertNotIn("`createObject(\"Unit1.Class1.Class2\")` 可以创建对象", text)
self.assertNotIn("`type MyNewClass = class(Unit1.Class1.Class2)` 可以通过", text)
self.assertIn("字段可以写类型注解", text)
self.assertIn("类方法参数和返回值可以写类型注解", text)
self.assertIn("类的声明和实现可以分离", text)
self.assertIn("类内可以只声明方法签名", text)
self.assertIn("类外实现属于声明区", text)
self.assertIn("字段和类方法类型注解", text)
self.assertIn("name_: string;", text)
self.assertIn("value_: any;", text)
self.assertIn("function create(_name: string; _value: any);", text)
self.assertIn("function ReadName(): string;", text)
self.assertIn("property Value: any read value_ write value_;", text)
self.assertIn("```text\nabc\n7\nabc\n```", text)
self.assertIn("类内声明、类外实现的带类型重载方法", text)
self.assertIn("function PairBox.create(_left: string); overload;", text)
self.assertIn(
"function PairBox.create(_left: string; _right: any); overload;", text
)
self.assertIn("function PairBox.ReadLeft(): string;", text)
self.assertIn("create(_left, nil);", text)
self.assertIn("```text\nleft\n<NIL>\nleft\n2\nleft\n```", text)
self.assertIn("成员读写为了性能不加 `self` 前缀", text)
self.assertIn("普通成员访问仍直接写成员名,不加 `self` 前缀", text)
self.assertIn("字段、方法和 `new`", text)
self.assertIn("return new Person();", text)
self.assertIn("obj := new MyClass();", text)
self.assertIn("static mCount;", text)
self.assertIn("单个静态字段默认写成 `static mCount;`", text)
self.assertIn("上述声明顺序可以编译通过", text)
self.assertLess(
text.index("a := new Person(22, c:99);"),
text.index('b := createObject("Person", 11);'),
)
self.assertNotIn("score_value", text)
self.assertNotIn("slot_type", text)
self.assertNotIn("裸 `class Person`", text)
self.assertNotIn("没有任何类声明证据", text)
self.assertNotIn("把普通当前实例成员访问写成带 `self` 前缀", text)
self.assertIn("不把裸类名成员访问当成文档事实", text)
self.assertIn("MathBox.Add(1, 2);\nTHuman.mCount := 7;", text)
self.assertIn("上面这种裸类名成员访问不作为可写事实", text)
self.assertNotIn("上面这种裸类名静态字段访问不作为可写事实", text)
self.assertNotIn("字段、方法和 `createObject`", text)
self.assertNotIn('obj := createObject("MyClass");', text)
self.assertNotIn("static\n mCount;", text)
self.assertNotIn("self.", text)
self.assertNotIn("上述 `.tsf` 例子", text)
self.assertNotIn("当前解释器", text)
self.assertNotIn("文档明确运行结果", text)
self.assertNotIn("文档结果", text)
self.assertNotIn("当前已记录验证", text)
self.assertNotIn("已做双文件运行验证", text)
def test_tsl_syntax_docs_do_not_use_self_member_access(self):
for path in (ROOT / "docs" / "tsl" / "syntax").glob("*.md"):
text = path.read_text(encoding="utf-8")
self.assertNotIn("self.", text, msg=f"self. found in {path}")
def test_tsl_values_page_is_agent_value_decision_page(self):
text = TSL_VALUES.read_text(encoding="utf-8")
self.assertIn("智能体值写法判断流程", text)
self.assertIn("普通值优先从整数、实数、普通字符串、布尔和 `array(...)` 起手", text)
self.assertIn("字符串默认用普通字符串", text)
self.assertIn("只有编码、宽串、UTF8、原始字符串、字符码或 ASCII `0` 需求明确时", text)
self.assertIn("先判断要顺序数组还是字符串键表", text)
self.assertIn("顺序数组和二进制缓冲区下标从 `0` 开始", text)
self.assertIn("字符串下标从 `1` 开始", text)
self.assertIn("矩阵、集合扩展", text)
self.assertIn("12_matrix_and_collections.md", text)
self.assertIn("普通示例默认按 `.tsl` 脚本语句区书写", text)
self.assertIn("没有对应代码块时不要发明值写法", text)
self.assertNotIn("20_strings_and_text.md", text)
def test_tsl_values_key_examples_have_verified_output_snippets(self):
text = TSL_VALUES.read_text(encoding="utf-8")
self.assertIn("代码块身份:输出片段", text)
self.assertIn("```text\n1\n12.5\nABC\n1\n0\n2\n```", text)
self.assertIn("```text\n10\n20\n0001\nA\nB\nC\n```", text)
self.assertIn("```text\nBCD\n```", text)
self.assertIn("```text\nABC\n```", text)
self.assertIn("```text\nString index out of bounds\n```", text)
def test_tsl_values_page_owns_string_boundaries(self):
text = TSL_VALUES.read_text(encoding="utf-8")
self.assertFalse((ROOT / "docs/tsl/syntax/20_strings_and_text.md").exists())
self.assertIn("字符串边界规则", text)
self.assertIn("宽串 / UTF8 前缀", text)
self.assertIn("原始字符串", text)
self.assertIn("字符码拼接", text)
self.assertIn("ASCII `0` 字符不会截断字符串", text)
self.assertIn("普通字符串里的 `\\uXXXX` 不要直接按“单字符宽串”理解", text)
self.assertIn("`U\"\"` 不是宽串", text)
self.assertIn("`#number` 可以直接把字符码拼进字符串", text)
self.assertIn("`\\0` 和 `#0` 都能把 ASCII `0` 放进字符串", text)
def test_tsl_values_string_boundary_examples_have_verified_output_snippets(self):
text = TSL_VALUES.read_text(encoding="utf-8")
self.assertIn("代码块身份:输出片段", text)
self.assertIn("```text\nABC\nA\"B'C\nA\nB\n```", text)
self.assertIn("writeLn(length(\"\\u0041\"));", text)
self.assertIn("writeLn(length(L\"\\u0041\"));", text)
self.assertIn("writeLn(length(U\"\\u0041\"));", text)
self.assertIn("依次输出 `2`、`1`、`1`", text)
self.assertIn("writeLn(ifWString(utf8_s));", text)
self.assertIn("依次输出 `0`、`1`、`1`、`1`", text)
self.assertIn('s2 := "A"#0"B";', text)
self.assertIn("依次输出 `3`、`3`、`1`、`1`", text)
def test_tsl_values_omits_environment_verification_details(self):
text = TSL_VALUES.read_text(encoding="utf-8")
for phrase in [
"Docker",
"docker exec",
"LD_LIBRARY_PATH",
"/data/workspace",
"U22Cli",
"gitea-runner",
]:
self.assertNotIn(phrase, text)
def test_tsl_variables_page_is_agent_binding_decision_page(self):
text = TSL_VARIABLES.read_text(encoding="utf-8")
self.assertIn("智能体变量/常量判断流程", text)
self.assertIn("普通变量默认直接用 `:=` 首次赋值", text)
self.assertIn("只有用户要求显式声明或遇到 `{$explicit+}` 时才优先写 `var`", text)
self.assertIn("常量声明必须同时初始化", text)
self.assertIn("默认只生成 `const name = value;`", text)
self.assertIn("单变量拆包必须写成 `[name, ] := array(...)`", text)
self.assertIn("06_expressions_and_operators.md", text)
self.assertIn("16_lexical_structure_and_compile_options.md", text)
self.assertIn("普通示例默认按 `.tsl` 脚本语句区书写", text)
self.assertIn("复制常量示例时只使用 `const name = value;`", text)
self.assertIn("没有对应代码块时不要发明变量/常量写法", text)
self.assertIn("运行时类型 / 转换", text)
self.assertIn("17_types_and_conversions.md", text)
self.assertNotIn("const name :=", text)
self.assertNotIn("语法设计兼容边界", text)
self.assertNotIn("兼容通过", text)
self.assertNotRegex(text, r"const\s+[A-Za-z_][A-Za-z0-9_]*\s*:=")
def test_tsl_types_page_does_not_own_declaration_rules(self):
text = TSL_TYPES.read_text(encoding="utf-8")
self.assertIn("智能体类型/转换判断流程", text)
self.assertIn("运行时值会变成什么类型", text)
self.assertIn("默认值是什么", text)
self.assertIn("怎样显式转换", text)
self.assertIn("`var` / `const` 声明选择回看", text)
self.assertIn("16_lexical_structure_and_compile_options.md", text)
self.assertNotIn("显式声明边界", text)
self.assertNotIn("怎样受编译选项影响", text)
def test_tsl_variables_key_examples_have_verified_output_snippets(self):
text = TSL_VARIABLES.read_text(encoding="utf-8")
self.assertIn("代码块身份:输出片段", text)
self.assertIn("```text\n1\n2\n```", text)
self.assertIn("```text\n7\n```", text)
self.assertIn("```text\n1\n3\n```", text)
self.assertIn("```text\n1\n1\n```", text)
self.assertIn("```text\n1\n2\n3\n4\n```", text)
self.assertIn("```text\n0\n7\n```", text)
self.assertIn("```text\n6\n```", text)
self.assertIn("```text\nvariable not defined\n```", text)
def test_tsl_variables_omits_environment_verification_details(self):
text = TSL_VARIABLES.read_text(encoding="utf-8")
for phrase in [
"Docker",
"docker exec",
"LD_LIBRARY_PATH",
"/data/workspace",
"U22Cli",
"gitea-runner",
]:
self.assertNotIn(phrase, text)
def test_tsl_functions_page_is_agent_function_decision_page(self):
text = TSL_FUNCTIONS.read_text(encoding="utf-8")
self.assertIn("智能体函数/调用判断流程", text)
self.assertIn("先判断目标文件是 `.tsl` 还是 `.tsf`", text)
self.assertIn("`.tsl` 中先写语句区,再把 `function` / `procedure` 声明放在后面", text)
self.assertIn("用户只说“写一个函数”且没有指定 `procedure` 时,默认用 `function`", text)
self.assertIn("即使任务没有返回值,也默认用 `function`", text)
self.assertIn("不要因为没有返回值就自动改用 `procedure`", text)
self.assertIn("用户提示词里的“函数”默认对应 `function`", text)
self.assertIn("命名参数只写 `name: value`", text)
self.assertIn("不要把 `name = value` 当成命名参数", text)
self.assertIn("函数头后默认保留分号", text)
self.assertIn("16_lexical_structure_and_compile_options.md", text)
self.assertIn("普通运行示例默认按 `.tsl` 脚本语句区书写", text)
self.assertIn("`.tsf` 函数 / 过程示例只按可复用顶层声明理解", text)
self.assertIn("`procedure` 示例只在用户明确要求 `procedure` / 过程时复制", text)
self.assertIn("默认从 `function` 骨架起步", text)
self.assertIn("跨 `unit` 声明边界", text)
self.assertIn("默认参数先按普通函数规则处理", text)
self.assertIn("匿名函数和 TSL 函数值", text)
self.assertIn("原生函数指针包装", text)
self.assertIn("C 回调", text)
self.assertIn("只按本页明确的 `call(f, ...)` / `##f(...)` 生成", text)
self.assertIn("没有对应代码块时不要发明函数/调用写法", text)
self.assertNotIn("在我于", text)
self.assertNotIn("LIBPATH", text)
self.assertNotIn("tsl .\\main.tsl", text)
self.assertNotIn("command", text)
self.assertNotIn("function MissingSemi()", text)
self.assertNotIn("input_value", text)
self.assertNotIn("handler): result_type", text)
self.assertNotIn("命名参数可以调换顺序", text)
self.assertNotIn("命名参数也可以和位置参数混用", text)
def test_tsl_functions_key_examples_have_verified_output_snippets(self):
text = TSL_FUNCTIONS.read_text(encoding="utf-8")
self.assertIn("代码块身份:输出片段", text)
self.assertIn("```text\ntest\n```", text)
self.assertIn("```text\n2\n```", text)
self.assertIn("```text\n9\n1\n7\n```", text)
self.assertIn("```text\n12\n```", text)
self.assertIn("```text\n888\n123\n```", text)
self.assertIn("```text\ninvalid statement\n```", text)
def test_tsl_functions_omits_environment_verification_details(self):
text = TSL_FUNCTIONS.read_text(encoding="utf-8")
for phrase in [
"Docker",
"docker exec",
"LD_LIBRARY_PATH",
"/data/workspace",
"U22Cli",
"gitea-runner",
]:
self.assertNotIn(phrase, text)
def test_tsl_expressions_page_is_agent_expression_decision_page(self):
text = TSL_EXPRESSIONS.read_text(encoding="utf-8")
text = text.replace("&#124;", "|")
self.assertIn("智能体表达式/运算符判断流程", text)
self.assertIn("先判断要写基础表达式运算符,还是集合、矩阵、对象重载、运行时调用或 TS-SQL 这类专题运算符", text)
self.assertIn("本页直接生成的运算符", text)
self.assertIn("专题运算符入口", text)
self.assertIn("默认生成规则", text)
self.assertIn("按需生成规则", text)
self.assertIn("边界规则", text)
self.assertIn("赋值只能用 `:=`", text)
self.assertIn("比较才用 `=`", text)
self.assertIn("`const name = value;` 是常量初始化规则", text)
self.assertIn("常量初始化不按普通赋值判断", text)
self.assertIn("函数签名里的默认参数 `name = value` 不是比较表达式", text)
self.assertIn("不要把函数签名里的 `name = value` 当成比较表达式", text)
self.assertIn("04_variables_and_constants.md", text)
self.assertIn("05_functions_and_calls.md", text)
self.assertIn("03_values_and_literals.md", text)
self.assertIn("12_matrix_and_collections.md", text)
self.assertIn("16_lexical_structure_and_compile_options.md", text)
self.assertNotIn("20_strings_and_text.md", text)
self.assertIn("`{$ifdef ...}` 只作为能力探测", text)
self.assertIn("条件求值优先用 `flag ? true_value : false_value`", text)
self.assertIn("`if condition then true_value else false_value` 必须带 `else`", text)
self.assertIn("需要延迟求值或动态表达式对象时,才用 `@expr` 或 `&\"...\"`", text)
self.assertIn("数组逐元素链式比较才用 `::>`", text)
self.assertIn("本页是 TSL 表达式与运算符的生成规则页", text)
self.assertIn("只使用本页或对应专题页明确记录的运算符", text)
self.assertIn("`div`、`mod`、`^`", text)
self.assertIn("一元倒数", text)
self.assertIn("`!x`", text)
self.assertIn("整型、实型输入返回实型倒数", text)
self.assertIn("`+`、`$`", text)
self.assertIn("`and`、`or`、`not`、`&&`、||", text)
self.assertIn("`.&`、`.|`、`.^`", text)
self.assertIn("`^=`、`.&=`、`.|=`、`.^=`", text)
self.assertIn("`a++`、`a--`、`++a`、`--a`", text)
self.assertIn("`union2`、`intersect`、`minus`、`outersect`", text)
self.assertIn("`union`、|、`:|`", text)
self.assertIn("矩阵并右方", text)
self.assertIn("`->`、`!matrix`", text)
self.assertIn("矩阵逆/广义逆", text)
self.assertIn("反引号转置", text)
self.assertIn("`operator +`", text)
self.assertIn("`select` / `sselect` / `vselect` / `mselect`", text)
self.assertIn("没有文档事实的运算符", text)
self.assertIn("不要生成猜测写法", text)
self.assertNotIn("`. ^`", text)
self.assertNotIn("补证", text)
self.assertNotIn("待确认", text)
self.assertNotIn("不是完整运算符支持清单", text)
self.assertNotIn("未列运算符不等于语法不支持", text)
self.assertNotIn("未列出的运算符不要直接判为不支持", text)
self.assertNotIn("直接写成该运算符不支持", text)
self.assertNotIn("当前环境里", text)
def test_tsl_expressions_key_examples_have_verified_output_snippets(self):
text = TSL_EXPRESSIONS.read_text(encoding="utf-8")
self.assertIn("代码块身份:输出片段", text)
self.assertIn("```text\n3\n```", text)
self.assertIn("```text\n3\n3\n12\n4\n1\n2\n1\n8\n2\n3\n```", text)
self.assertIn("```text\n1\n1\n1\n1\n1\n1\n```", text)
self.assertIn("```text\n0.5\n0.25\n1\n1\n```", text)
self.assertIn("```text\n1\n1\n1\n1\n1\n```", text)
self.assertIn("```text\n2\n5\n5\n```", text)
self.assertIn("```text\n8\n2\n5\n5\n```", text)
self.assertIn("```text\nAB\n```", text)
self.assertIn("```text\n222888\n1\n1\n1\n1\n```", text)
self.assertIn("```text\n1\n0\n```", text)
self.assertIn("```text\n2\n```", text)
self.assertIn("++a;", text)
self.assertIn("--a;", text)
self.assertIn("```text\n6\n```", text)
self.assertIn("```text\n1\n7\n1\n```", text)
self.assertIn("```text\ninvalid statement\n```", text)
def test_tsl_expressions_omits_environment_verification_details(self):
text = TSL_EXPRESSIONS.read_text(encoding="utf-8")
for phrase in [
"Docker",
"docker exec",
"LD_LIBRARY_PATH",
"/data/workspace",
"U22Cli",
"gitea-runner",
]:
self.assertNotIn(phrase, text)
def test_tsl_control_flow_page_routes_function_and_debug_boundaries(self):
text = TSL_CONTROL_FLOW.read_text(encoding="utf-8")
self.assertIn("智能体控制流判断流程", text)
self.assertIn("05_functions_and_calls.md", text)
self.assertIn("函数里的控制流只按控制流语法处理", text)
self.assertIn("15_debug_and_profiler.md", text)
self.assertIn("默认写成块式分支", text)
self.assertIn("控制流块的 `end` 默认不加分号", text)
self.assertIn("只按语句形态生成", text)
self.assertIn("begin\n value := 1;\nend\nelse\nbegin\n value := 0;\nend", text)
self.assertNotIn("begin\n value := 1;\nend\nelse\nbegin\n value := 0;\nend;", text)
self.assertIn("不要在 `else` 前提前加分号", text)
self.assertNotIn("then\n value := 1\nelse", text)
self.assertNotIn("b := case", text)
self.assertNotIn("b := @case", text)
self.assertNotIn("@case", text)
self.assertNotIn("case 表达式形态", text)
self.assertNotIn("当前解释器", text)
self.assertNotIn("文档明确运行结果", text)
self.assertNotIn("文档明确支持", text)
def test_tsl_control_flow_key_examples_have_verified_output_snippets(self):
text = TSL_CONTROL_FLOW.read_text(encoding="utf-8")
self.assertIn("代码块身份:输出片段", text)
self.assertIn("```text\n1\n0\n```", text)
self.assertIn("```text\n3\n```", text)
self.assertIn("```text\n10\n120\n230\n```", text)
self.assertIn("```text\n6\n4\n```", text)
self.assertIn("```text\ntwo\n```", text)
self.assertIn("```text\nbefore\ncaught\nraise: boom\nafter\n```", text)
self.assertIn("```text\nbefore\nbody\nfinally\n```", text)
def test_tsl_units_page_omits_environment_and_orders_uses_before_statements(self):
text = (ROOT / "docs/tsl/syntax/09_units_and_scope.md").read_text(
encoding="utf-8"
)
self.assertIn("多文件结构骨架", text)
self.assertIn("[02_core_model.md](02_core_model.md)(优先)", text)
self.assertNotIn("[19_namespace_libpath_and_unit_runtime.md](19_namespace_libpath_and_unit_runtime.md)(优先)", text)
self.assertIn("命中 `unit` 生命周期、命名空间、查找路径、`tsl.conf` 或命令行查找路径参数", text)
self.assertIn("没有对应代码块时不要发明 unit/作用域写法", text)
self.assertIn(
"interface\n\nfunction Ping();\n\nimplementation\n\nfunction Ping();",
text,
)
self.assertIn("`implementation` 前保留空行", text)
self.assertIn("`unit` 允许接口声明与实现段分离", text)
self.assertIn("类方法放在 `implementation` 段实现", text)
self.assertIn("### 接口声明与实现段分离", text)
self.assertIn("function UnitBox.create(_value);", text)
self.assertIn("function UnitBox.ReadValue();", text)
self.assertIn("只写在 `implementation` 段,对 `unit` 外部不可见", text)
self.assertIn("uses PrivateDemo;\nwriteLn(PublicFunc());\n```", text)
self.assertIn("代码块身份:反例 / 不可照写\n\n```text\nuses PrivateDemo;\nwriteLn(PrivateFunc());", text)
self.assertNotIn("interface\n function Ping();", text)
self.assertNotIn("implementation\n function Ping();", text)
self.assertNotIn("## 生成代码排版规则", text)
self.assertIn("type Worker = class\n uses DemoUnit;\npublic", text)
self.assertIn("// main.tsl\n\nuses UnitA;", text)
self.assertIn("// main.tsl\n\nuses PrivateDemo;", text)
self.assertIn("type TBox = class\npublic", text)
self.assertIn("type Box = class\npublic", text)
self.assertIn("function Box.create();", text)
self.assertIn("合并成单条 `uses UnitA, UnitB;`", text)
self.assertIn("失败点是“第二条 `uses`”", text)
self.assertIn("只生成单条逗号合并写法", text)
self.assertIn("连续多条顶层 `uses` 不再作为文档明确生成形态", text)
self.assertIn("uses UnitA, UnitB;", text)
self.assertIn("多个 `unit` 默认写成 `uses UnitA, UnitB;`", text)
self.assertNotIn("当前解释器", text)
self.assertNotIn("文档结果", text)
self.assertNotIn("已在多文件环境下验证", text)
self.assertNotIn("// command", text)
self.assertNotIn("LIBPATH", text)
self.assertNotIn("tsl .", text)
def test_tsl_runtime_context_page_omits_environment_process_details(self):
text = (ROOT / "docs/tsl/syntax/10_runtime_context_and_with.md").read_text(
encoding="utf-8"
)
self.assertIn("系统参数优先用本页明确的", text)
self.assertIn("运行时上下文、服务与全局缓存", text)
self.assertIn("本地函数后缀 `with` 属于反例", text)
self.assertIn("网格调用返回的不是最终值;需要最终结果时继续写 `dupvalue(...)`", text)
self.assertIn("全局缓存读写要成对出现", text)
self.assertIn("多文件结构骨架", text)
self.assertIn("依赖函数文件查找路径", text)
self.assertIn("结果说明:", text)
self.assertIn('r := #TestDo() with array("a": 101, "b": 202);', text)
self.assertIn("writeLn(dupvalue(#AddOne(5)));", text)
self.assertIn("writeLn(dupvalue(#AddOne(5) timeout 3000));", text)
self.assertIn('writeLn(setGlobalCache("PB_TEST_GC_BASIC", v1));', text)
self.assertIn("writeLn(checkGlobalCacheExpired(v));", text)
self.assertIn("对缓存值做 `select` 可以正常执行", text)
self.assertIn("这种“本地函数后缀 `with`”写法不作为可写事实", text)
self.assertFalse((ROOT / "docs/tsl/syntax/30_runtime_services_and_global_cache.md").exists())
self.assertNotIn("30_runtime_services_and_global_cache.md", text)
self.assertNotIn("当前解释器", text)
self.assertNotIn("文档明确运行结果", text)
self.assertNotIn("当前已做双文件运行验证", text)
def test_tsl_external_page_owns_native_pointer_and_callback_boundaries(self):
text = TSL_EXTERNAL.read_text(encoding="utf-8")
self.assertIn("智能体外部调用/线程判断流程", text)
self.assertIn("原生函数指针包装", text)
self.assertIn("C 回调", text)
self.assertIn("包装原生函数指针", text)
self.assertIn("生成 C 回调", text)
self.assertIn("makeInstance(thisFunction(Func), \"cdecl\", 0)", text)
self.assertIn("makeInstance(..., \"cdecl\", 1)", text)
self.assertIn("函数指针包装", text)
self.assertNotIn("普通函数怎么写已经清楚后,外部 DLL、函数指针、多线程", text)
self.assertNotIn("// command", text)
self.assertNotIn("LIBPATH", text)
self.assertNotIn("tsl .", text)
def test_tsl_pitfalls_page_uses_stable_boundary_language(self):
text = (ROOT / "docs/tsl/syntax/11_pitfalls.md").read_text(
encoding="utf-8"
)
self.assertIn("## 反例索引", text)
self.assertIn("必须跳回对应语法页找可直接照写示例", text)
self.assertIn("只有已经有明确反例边界的误写", text)
self.assertIn("细节型边界放回对应专题页", text)
self.assertIn("最容易诱导智能体写错的高频误写", text)
self.assertIn("只针对函数体和类定义体里的 `uses`", text)
self.assertIn("默认回到顶层写成单条 `uses UnitA, UnitB;`", text)
self.assertIn("类定义体里出现了第二条 `uses`", text)
self.assertIn("可用写法是先拿到类类型", text)
self.assertIn("不要把反例边界写成可用语法", text)
self.assertNotIn("当前解释器", text)
self.assertNotIn("当前命令行解释器", text)
self.assertNotIn("实测", text)
self.assertNotIn("当前文档明确", text)
self.assertNotIn("对应代码块", text)
self.assertNotIn("## 不可照写反例索引", text)
self.assertNotIn("LIBPATH", text)
self.assertNotIn("在同一作用域里重复写第二个 `uses`", text)
self.assertNotIn('writeLn(length("\\u0041"));', text)
self.assertNotIn('writeLn(ifWString(U"\\u5929\\u8F6F"));', text)
self.assertNotIn('s := "A"#0"B";', text)
self.assertNotIn("const value = 1;", text)
self.assertNotIn("unit UnitConst;", text)
self.assertNotIn("TickFromExpr", text)
self.assertNotIn("文件就在当前目录", text)
self.assertNotIn("DemoUnit.UnitCounter", text)
def test_tsl_matrix_collections_page_uses_entry_language(self):
text = (
ROOT / "docs/tsl/syntax/12_matrix_and_collections.md"
).read_text(encoding="utf-8")
self.assertIn("本页明确的矩阵样比较", text)
self.assertIn("本页文档明确形态", text)
self.assertIn("14_ts_sql.md", text)
self.assertIn("[14_ts_sql.md](14_ts_sql.md)", text)
self.assertIn("没有对应代码块时不要发明数组/矩阵样数据/集合关系写法", text)
self.assertIn("结果说明:", text)
self.assertIn("矩阵链式比较 `::>`、`::<`、`::<>`、`::==`、`::>=`、`::<=`", text)
self.assertIn("`in` / `not in` 处理的是元素存在关系", text)
self.assertIn("`sqlin` / `not sqlin` 处理的是行存在关系", text)
self.assertIn("`union2`、`intersect`、`minus`、`outersect` 都按“行”运算", text)
self.assertIn("集合运算结果会折叠重复行", text)
self.assertIn("writeLn(1 not in array(0, 2));", text)
self.assertIn("writeLn(array(5, 6) not sqlin array((1, 2), (3, 4)));", text)
self.assertIn("union_rows := left_rows union2 right_rows;", text)
self.assertIn("outersect_rows := left_rows outersect right_rows;", text)
self.assertNotIn("25_set_operations.md", text)
self.assertNotIn("当前解释器", text)
self.assertNotIn("文档明确运行结果", text)
self.assertNotIn("文档明确运行结果对应关系", text)
self.assertNotIn("稳定验证", text)
self.assertNotIn("稳定通过", text)
def test_tsl_resultset_filters_page_uses_complete_examples(self):
text = (
ROOT / "docs/tsl/syntax/13_resultset_and_filters.md"
).read_text(encoding="utf-8")
self.assertIn("字段访问优先照本页明确的", text)
self.assertIn("复杂查询需求优先跳转到 [14_ts_sql.md](14_ts_sql.md)", text)
self.assertIn("金融数据筛选要先确认数据来源", text)
self.assertIn("[../reference/catalog/datawarehouse.md](../reference/catalog/datawarehouse.md)", text)
self.assertIn("没有对应代码块时不要发明结果集/过滤写法", text)
self.assertIn("结果说明:", text)
self.assertIn('writeLn(keep_rows[0]["Code"]);', text)
self.assertIn('writeLn(drop_rows[1]["Code"]);', text)
self.assertIn('rows := array(\n ("Code": "0001"),\n ("Code": "0002")\n);', text)
self.assertIn('code_arr := array("0001");\nkeep_rows := filterIn(rows, code_arr, "Code");', text)
self.assertIn("本页只把显式写出 `nil` 作为可靠规则", text)
self.assertNotIn("文档明确运行结果", text)
self.assertNotIn("当前手册", text)
self.assertNotIn("当前解释器", text)
def test_tsl_ts_sql_page_uses_entry_and_route_language(self):
text = (ROOT / "docs/tsl/syntax/14_ts_sql.md").read_text(
encoding="utf-8"
)
self.assertIn("没有对应代码块时不要发明 TS-SQL 写法", text)
self.assertIn("基础查询文档骨架", text)
self.assertIn("文档字段访问写法", text)
self.assertIn("在一维数组上做 TS-SQL 时,优先使用", text)
self.assertIn("只想按已有结果集保留/排除行时跳到", text)
self.assertIn("13_resultset_and_filters.md", text)
self.assertIn("12_matrix_and_collections.md", text)
self.assertIn("23_fmarray.md", text)
self.assertIn("这一篇是 TS-SQL 的唯一语法入口", text)
self.assertIn("多表 `join` 时,字段访问应写成", text)
self.assertIn("结果说明:", text)
self.assertNotIn("当前的稳定骨架", text)
self.assertNotIn("当前稳定字段访问", text)
self.assertNotIn("当前应优先", text)
self.assertNotIn("文档明确运行结果", text)
def test_tsl_debug_profiler_page_uses_entry_language(self):
text = (ROOT / "docs/tsl/syntax/15_debug_and_profiler.md").read_text(
encoding="utf-8"
)
self.assertIn("本页明确的调试、计时、性能分析器", text)
self.assertIn("没有对应代码块时不要发明调试/性能分析器写法", text)
self.assertIn("只照文档最小调用写", text)
self.assertIn("普通控制流优先回到 [07_control_flow.md](07_control_flow.md)", text)
self.assertIn("属于文档明确写法", text)
self.assertIn("本页正向边界只覆盖", text)
self.assertIn("跨函数跳转不作为可写事实", text)
self.assertIn("目标 `label` 单独成行不作为可写事实", text)
self.assertIn("不作为本页输出事实", text)
self.assertIn("结果说明:", text)
self.assertIn("不要把调试客户端副作用写成普通输出事实", text)
self.assertNotIn("当前", text)
self.assertNotIn("当前解释器", text)
self.assertNotIn("文档明确运行结果", text)
self.assertNotIn("文档结果", text)
self.assertNotIn("未验证参数", text)
self.assertNotIn("当前命令行环境", text)
self.assertNotIn("命令行环境不可直接观察", text)
def test_tsl_runtime_context_routes_function_query_and_runtime_boundaries(self):
text = (ROOT / "docs/tsl/syntax/10_runtime_context_and_with.md").read_text(
encoding="utf-8"
)
self.assertIn("智能体运行时上下文判断流程", text)
self.assertIn("05_functions_and_calls.md", text)
self.assertIn("14_ts_sql.md", text)
self.assertIn("普通函数调用回到 [05_functions_and_calls.md](05_functions_and_calls.md)", text)
self.assertIn("缓存值参与 `select` 时,查询语法仍回到 [14_ts_sql.md](14_ts_sql.md)", text)
self.assertIn("内置运行时对象", text)
def test_tsl_complex_and_weakref_content_is_split_by_owner_page(self):
self.assertFalse((ROOT / "docs/tsl/syntax/31_complex_and_weakref.md").exists())
types_text = TSL_TYPES.read_text(encoding="utf-8")
object_text = TSL_OBJECT_RUNTIME.read_text(encoding="utf-8")
self.assertIn("复数类型", types_text)
self.assertIn("复数字面量可以直接写成 `a + bj`", types_text)
self.assertIn("z1 := 4 + 3j;", types_text)
self.assertIn("z2 := complex(5, -2);", types_text)
self.assertIn("`dataType(z)` 对复数返回 `41`", types_text)
self.assertIn("complex(array(1, 2, 3), 5.5)", types_text)
self.assertIn("complex(fmarray[1, 2, 3], 5.5)", types_text)
self.assertIn("弱引用与自动弱引用", object_text)
self.assertIn("访问弱引用前先做 `checkWeakRef(...)` 判定", object_text)
self.assertIn("弱引用能力的条件编译宏是 `weakptr`", object_text)
self.assertIn("自动弱引用相关宏是 `autoWeak`", object_text)
self.assertIn("w := weakRef(a);", object_text)
self.assertIn("s := weakref_get(w);", object_text)
self.assertIn("[weakRef] owner1;", object_text)
self.assertIn("[autoRef] owner2;", object_text)
self.assertIn("invalid class definition", object_text)
self.assertNotIn("31_complex_and_weakref.md", types_text)
self.assertNotIn("31_complex_and_weakref.md", object_text)
def test_tsl_syntax_examples_do_not_use_unterminated_if_else_branches(self):
pattern = re.compile(r"if[^\n]*then\s*\n\s*[^\n;]+\n\s*else")
for path in (ROOT / "docs" / "tsl" / "syntax").glob("*.md"):
text = path.read_text(encoding="utf-8")
self.assertIsNone(pattern.search(text), msg=f"裸 if/else 分支缺分号: {path}")
def test_tsl_matrix_deep_dive_documents_inverse_operator(self):
text = TSL_MATRIX_DEEP_DIVE.read_text(encoding="utf-8")
self.assertIn("矩阵逆/广义逆", text)
self.assertIn("`!A` 是一元倒数运算符作用于矩阵的形态", text)
self.assertIn("inverse_value := !matrix_value;", text)
self.assertIn("不要把它改写成 `1 / matrix_value`", text)
self.assertIn("`!` 不表示逻辑非", text)
self.assertIn("```text\n2\n2\n-2\n1\n1.5\n-0.5\n```", text)
self.assertIn(
"```text\n"
"3\n"
"2\n"
"-0.944444444444444\n"
"0.444444444444444\n"
"-0.111111111111111\n"
"0.111111111111111\n"
"0.722222222222222\n"
"-0.222222222222222\n"
"```",
text,
)
def test_tsl_fmarray_documents_right_matrix_merge_operator(self):
text = TSL_FMARRAY.read_text(encoding="utf-8")
self.assertIn("矩阵连接 / 矩阵并右方:`union`、`|`、`:|`", text)
self.assertIn("`|` 和 `:|` 都可做矩阵并右方(按列连接)", text)
self.assertIn("`|` 和 `:|` 会执行矩阵并右方,也就是按列拼接", text)
self.assertIn(
"```text\n"
"27\n"
"3\n"
"4\n"
"1,2,3,4\n"
"3,4,7,8\n"
"5,5,6,9\n"
"1,2,3,4\n"
"3,4,7,8\n"
"5,5,6,9\n"
"```",
text,
)
self.assertIn(
"```text\n"
"1,2,3,4\n"
"3,4,0,0\n"
"5,5,0,0\n"
"3,4,1,2\n"
"0,0,3,4\n"
"0,0,5,5\n"
"```",
text,
)
def test_tsl_deep_dive_pages_avoid_process_verification_language(self):
syntax_root = ROOT / "docs" / "tsl" / "syntax"
forbidden_phrases = [
"当前",
"当前解释器",
"当前命令行",
"文档明确运行结果",
"文档结果",
"文档明确支持",
"实测",
"补证",
"待确认",
"没有文档明确",
"未验证",
"// command",
]
for filename in TSL_SYNTAX_DEEP_DIVE_FILES:
with self.subTest(filename=filename):
text = (syntax_root / filename).read_text(encoding="utf-8")
for phrase in forbidden_phrases:
self.assertNotIn(phrase, text)
def test_tsl_object_runtime_prefers_new_for_normal_object_creation(self):
text = TSL_OBJECT_RUNTIME.read_text(encoding="utf-8")
self.assertIn("obj := new RuntimeBox();", text)
self.assertIn("type RuntimeBox = class", text)
self.assertIn('t := findOverLoad(2, "fun", new TestClass());', text)
self.assertNotIn("new TStringList()", text)
self.assertNotIn('obj := createObject("TStringList");', text)
self.assertNotIn('findOverLoad(2, "fun", createObject("TestClass"))', text)
def test_tsl_builtin_runtime_objects_default_to_new_syntax(self):
text = TSL_BUILTIN_RUNTIME_OBJECTS.read_text(encoding="utf-8")
self.assertIn("`TStringList` 可以直接用 `new TStringList()` 创建", text)
self.assertIn("obj := new TStringList();", text)
self.assertIn("mem := new TMemoryStream();", text)
self.assertIn("cipher := new TCipher(2);", text)
self.assertIn("bad := new TCipher();", text)
self.assertIn("`new TStream()`", text)
self.assertIn("New Class TCipher Error.", text)
self.assertNotIn('createObject("TStringList")', text)
self.assertNotIn('createObject("TMemoryStream")', text)
self.assertNotIn('createObject("TCipher"', text)
def test_tsl_ts_sql_page_documents_thisrow_field_order(self):
text = TSL_TS_SQL.read_text(encoding="utf-8")
self.assertIn("第一行 `Value=20, Idx=1`,第二行 `Value=30, Idx=2`", text)
self.assertNotIn("`(1,20)`、`(2,30)`", text)
def test_tsl_ts_sql_has_single_authoritative_syntax_page(self):
syntax_root = ROOT / "docs" / "tsl" / "syntax"
text = TSL_TS_SQL.read_text(encoding="utf-8")
self.assertFalse((syntax_root / "28_ts_sql_core.md").exists())
self.assertFalse((syntax_root / "29_ts_sql_advanced.md").exists())
self.assertIn("这一篇是 TS-SQL 的唯一语法入口", text)
self.assertIn("多表 `join` 时,字段访问应写成 `[表序号].[\"字段名\"]`", text)
self.assertIn("`thisGroup` 不是普通值,而是分组后的子结果集", text)
self.assertIn("`refMaxOf(...)` 和 `refMinOf(...)` 可与", text)
def test_remaining_tsl_syntax_pages_are_agent_decision_pages(self):
syntax_root = ROOT / "docs" / "tsl" / "syntax"
for filename, section in TSL_REMAINING_SYNTAX_AGENT_SECTIONS.items():
with self.subTest(filename=filename):
text = (syntax_root / filename).read_text(encoding="utf-8")
self.assertIn(section, text)
self.assertIn("智能体", text)
self.assertIn("不要发明", text)
def test_tsl_syntax_pages_label_mainline_and_deep_dive_roles(self):
syntax_root = ROOT / "docs" / "tsl" / "syntax"
for filename in TSL_SYNTAX_MAINLINE_FILES:
with self.subTest(filename=filename):
text = (syntax_root / filename).read_text(encoding="utf-8")
self.assertIn("文档类型:语法主线", text)
text = (syntax_root / "11_pitfalls.md").read_text(encoding="utf-8")
self.assertIn("文档类型:反例索引页", text)
for filename in TSL_SYNTAX_DEEP_DIVE_FILES:
with self.subTest(filename=filename):
text = (syntax_root / filename).read_text(encoding="utf-8")
self.assertIn("文档类型:语法深水专题", text)
def test_tsl_syntax_uncertain_routes_have_center_fallbacks(self):
syntax_root = ROOT / "docs" / "tsl" / "syntax"
for path in syntax_root.glob("*.md"):
with self.subTest(filename=path.name):
text = path.read_text(encoding="utf-8")
self.assertNotIn("遇到不确定时跳转到", text)
self.assertNotIn("补证", text)
self.assertNotIn("待确认", text)
self.assertIn("遇到不确定时:", text)
self.assertIn("TSL 总入口 [../index.md](../index.md)", text)
if path.name == "index.md":
self.assertIn("按任务跳转", text)
continue
self.assertIn("继续判断", text)
self.assertIn("语法路由中心 [index.md](index.md)", text)
def test_tsl_syntax_examples_do_not_use_program_test_shell(self):
syntax_root = ROOT / "docs" / "tsl" / "syntax"
for path in syntax_root.glob("*.md"):
with self.subTest(filename=path.name):
text = path.read_text(encoding="utf-8")
self.assertNotIn("program test;", text)
self.assertNotIn("`program test; begin ... end.` 只是自包含示例外壳", text)
def test_tsl_syntax_code_block_identity_labels_are_fixed(self):
syntax_root = ROOT / "docs" / "tsl" / "syntax"
allowed_labels = {
"可直接照写示例",
"输出片段",
"反例 / 不可照写",
"配置片段 / 概念骨架",
}
for path in syntax_root.glob("*.md"):
text = path.read_text(encoding="utf-8")
for match in re.finditer(r"^代码块身份:(.+)$", text, re.MULTILINE):
with self.subTest(filename=path.name, label=match.group(1)):
self.assertIn(match.group(1), allowed_labels)
def test_remaining_tsl_syntax_pages_have_verified_output_snippets(self):
syntax_root = ROOT / "docs" / "tsl" / "syntax"
for filename in TSL_REMAINING_SYNTAX_AGENT_SECTIONS:
with self.subTest(filename=filename):
text = (syntax_root / filename).read_text(encoding="utf-8")
if (
"代码块身份:可直接照写示例" in text
or "代码块身份:反例 / 不可照写" in text
):
self.assertIn("代码块身份:输出片段", text)
def test_remaining_tsl_syntax_pages_omit_personal_and_environment_details(self):
syntax_root = ROOT / "docs" / "tsl" / "syntax"
forbidden_phrases = [
"我在",
"当前环境里",
"Docker",
"docker exec",
"LD_LIBRARY_PATH",
"/data/workspace",
"U22Cli",
"gitea-runner",
]
for filename in TSL_REMAINING_SYNTAX_AGENT_SECTIONS:
with self.subTest(filename=filename):
text = (syntax_root / filename).read_text(encoding="utf-8")
for phrase in forbidden_phrases:
self.assertNotIn(phrase, text)
def test_tsl_reference_index_requires_function_fact_workflow(self):
text = TSL_REFERENCE_INDEX.read_text(encoding="utf-8")
self.assertIn("智能体函数使用规则", text)
self.assertIn("进入 catalog 分类页后按函数事实条目生成调用", text)
self.assertIn("只从带完整参数表的函数事实条目读取签名", text)
self.assertIn("catalog 使用推荐大小写展示函数名", text)
self.assertIn("以函数事实条目的函数名拼写为准", text)
self.assertIn("没在 catalog 正式条目里的函数,不要根据函数名猜调用", text)
self.assertIn("函数名属于哪个模块", text)
self.assertIn("模块目录", text)
self.assertIn("catalog/base.md", text)
self.assertIn("catalog/math.md", text)
self.assertIn("catalog/resource.md", text)
self.assertNotIn("函数参数事实", text)
self.assertNotIn("catalog 里的裸函数名只是候选名", text)
self.assertNotIn("裸函数名仍只表示候选名", text)
self.assertNotIn("catalog/index.md", text)
self.assertNotIn("core.md", text)
self.assertNotIn("functions/", text)
self.assertNotIn("verified", text)
self.assertNotIn("unavailable_methods.md", text)
self.assertNotIn("不可用方法", text)
self.assertNotIn("verification_failures", text)
self.assertNotIn("函数入库验证流程", text)
self.assertNotIn("验证过程", text)
self.assertNotIn("代码块身份", text)
self.assertNotIn("输出片段", text)
self.assertNotIn("验证失败", text)
def test_tsl_reference_index_contains_function_fact_catalog(self):
catalog_index = TSL_REFERENCE_INDEX.read_text(encoding="utf-8")
self.assertIn("函数模块目录", catalog_index)
self.assertIn("进入分类页后,按函数事实条目的参数表生成调用", catalog_index)
self.assertIn("函数事实数", catalog_index)
self.assertIn("catalog/base.md", catalog_index)
self.assertIn("catalog/math.md", catalog_index)
self.assertIn("catalog/system.md", catalog_index)
self.assertIn("catalog/third_party.md", catalog_index)
self.assertIn("catalog 使用推荐大小写展示函数名", catalog_index)
self.assertIn("以函数事实条目的函数名拼写为准", catalog_index)
self.assertNotIn("catalog/index.md", catalog_index)
self.assertNotIn("core.md", catalog_index)
self.assertNotIn("functions/", catalog_index)
self.assertNotIn("verified", catalog_index)
self.assertNotIn("错误参数组合", catalog_index)
self.assertNotIn("参数验证", catalog_index)
catalog_root = ROOT / "docs" / "tsl" / "reference" / "catalog"
route_pages = {
"base.md",
"math.md",
"datawarehouse.md",
"system.md",
"resource.md",
"graphics.md",
}
for path in catalog_root.rglob("*.md"):
text = path.read_text(encoding="utf-8")
if path.parent == catalog_root and path.name in route_pages:
self.assertIn("文档类型:函数事实路由页", text)
self.assertIn("分类目录", text)
self.assertNotIn("### `", text)
continue
self.assertIn("文档类型:函数事实页", text)
self.assertIn("参数位置", text, msg=f"{path.name} missing parameter facts")
self.assertNotIn("候选函数索引", text)
self.assertIn("../index.md", text)
def test_tsl_reference_catalog_function_names_start_lowercase(self):
catalog_root = ROOT / "docs" / "tsl" / "reference" / "catalog"
for path in catalog_root.rglob("*.md"):
if path.name == "index.md":
continue
text = path.read_text(encoding="utf-8")
for match in re.finditer(r"^(?:- |#{3,6} )`([^`(]+)", text, re.MULTILINE):
with self.subTest(path=path.relative_to(ROOT), name=match.group(1)):
name = match.group(1)
self.assertFalse(name[0].isupper(), msg=f"{name} starts uppercase")
def test_tsl_reference_completed_catalog_pages_record_parameter_facts(self):
catalog_root = ROOT / "docs" / "tsl" / "reference" / "catalog"
completed_pages = {
"base.md": [
"dateToStr",
"dateTimeToStr",
"formatDateTime",
"time",
"now",
"date",
"currentYear",
"dayOfWeek",
"encodeDate",
"encodeTime",
"strToDate",
"strToTime",
"strToDateTime",
"strToDateDef",
"strToTimeDef",
"timeToStr",
"dateToInt",
"intToDate",
"today",
"yesterday",
"tomorrow",
"format",
"setLength",
"copy",
"upperCase",
"lowerCase",
"trim",
"trimLeft",
"trimRight",
"pos",
"strToIntDef",
"floatToStr",
"strToInt",
"intToStr",
"length",
],
"compression.md": [
"zipCompress",
"zipExtract",
"rarExtract",
"unicompress",
"uniuncompress",
],
"digest_encoding.md": [
"getMsgDigest",
"strToBase64",
"base64ToStr",
"encodeRadixstr",
"decodeRadixstr",
"encoderadixwstr",
"decoderadixwstr",
],
"system.md": [
"dataType",
"ifInt",
"ifInt64",
"ifReal",
"ifNumber",
"ifString",
"ifArray",
"ifNil",
"ifExp",
"ifGraph",
"ifGraphGroup",
"ifBinary",
"ifMatrix",
"ifObj",
"createMatrix",
"matrixToArray",
"getMatrixFields",
"createBinary",
"convertDataToBuf",
"convertBufToData",
"integer",
"int64",
"real",
"string",
"wideString",
"binary",
"setPrecision",
"toSTM",
"toSTN",
"exportCsv",
"importCsv",
"dupValue",
"randomize",
"random",
"randomFrom",
"ifThen",
"eval",
"call",
"callInArray",
"invoke",
"invokeinarray",
"setProfiler",
"getProfilerInfo",
"sysgettsllibpath",
"syssettsllibpath",
],
}
for filename, signatures in completed_pages.items():
path = catalog_root / filename
text = path.read_text(encoding="utf-8")
if "文档类型:函数事实路由页" in text:
text = "\n".join(
detail.read_text(encoding="utf-8")
for detail in sorted((catalog_root / path.stem).glob("*.md"))
)
with self.subTest(filename=filename):
self.assertIn("文档类型:函数事实页", text)
self.assertIn("参数个数:", text)
self.assertIn("返回值:", text)
self.assertRegex(
text,
r"\|\s*参数位置\s*\|\s*参数名\s*\|\s*必填\s*\|\s*接收类型\s*\|\s*说明\s*\|",
)
for name in signatures:
self.assertRegex(text, rf"(?m)^### `{re.escape(name)}\(", msg=name)
def test_tsl_reference_index_records_parameter_facts(self):
text = TSL_REFERENCE_INDEX.read_text(encoding="utf-8")
self.assertIn("函数事实路由页", text)
self.assertIn("查可调用函数参数类型", text)
self.assertIn("带完整参数表的函数事实条目", text)
self.assertIn("函数事实数", text)
self.assertIn("catalog/base.md", text)
self.assertIn("catalog/system.md", text)
self.assertNotIn("## 函数参数事实", text)
self.assertNotIn("`abs(value)`", text)
self.assertNotIn("verified", text)
self.assertNotIn("代码块身份", text)
self.assertNotIn("输出片段", text)
self.assertNotIn("Function Abs execution error", text)
self.assertNotIn("错误参数", text)
def test_tsl_reference_all_markdown_avoid_verification_logs(self):
for path in (ROOT / "docs" / "tsl" / "reference").rglob("*.md"):
text = path.read_text(encoding="utf-8")
with self.subTest(path=path.relative_to(ROOT)):
self.assertNotIn("验证过程", text)
self.assertNotIn("函数入库验证流程", text)
self.assertNotIn("代码块身份", text)
self.assertNotIn("输出片段", text)
self.assertNotIn("Function Abs execution error", text)
def test_tsl_reference_does_not_ship_verified_or_unavailable_layers(self):
reference_root = ROOT / "docs" / "tsl" / "reference"
self.assertFalse((reference_root / "verified").exists())
self.assertFalse((reference_root / "functions").exists())
self.assertFalse((reference_root / "core.md").exists())
self.assertFalse((reference_root / "catalog" / "index.md").exists())
self.assertFalse(TSL_REFERENCE_UNAVAILABLE.exists())
for path in reference_root.rglob("*.md"):
text = path.read_text(encoding="utf-8")
with self.subTest(path=path.relative_to(ROOT)):
self.assertNotIn("verified", text)
self.assertNotIn("unavailable_methods", text)
self.assertNotIn("不可用方法", text)
self.assertNotIn("已写入文档", text)
def test_tsl_reference_docs_do_not_name_valid_parameter_mismatches_as_failures(self):
reference_root = ROOT / "docs" / "tsl" / "reference"
for path in reference_root.rglob("*.md"):
text = path.read_text(encoding="utf-8")
with self.subTest(path=path.relative_to(ROOT)):
self.assertNotIn("verification_failures", text)
self.assertNotIn("错误参数", text)
self.assertNotIn("验证失败", text)
self.assertNotIn("Abs(\"-3\")", text)
self.assertNotIn("DateToStr(\"2011-12-31\")", text)
self.assertNotIn("Length(123)", text)
self.assertNotIn("abs(\"-3\")", text)
self.assertNotIn("dateToStr(\"2011-12-31\")", text)
self.assertNotIn("length(123)", text)
def test_tsl_finance_directory_is_removed(self):
finance_root = ROOT / "docs" / "tsl" / "finance"
self.assertFalse(finance_root.exists())
def test_tsl_modules_docs_are_agent_integration_boundary_pages(self):
modules_root = ROOT / "docs" / "tsl" / "modules"
for path in modules_root.glob("*.md"):
text = path.read_text(encoding="utf-8")
with self.subTest(path=path.name):
self.assertIn("智能体", text)
self.assertIn("不要发明", text)
self.assertNotIn("是否含文档明确", text)
self.assertNotIn("代码块身份", text)
self.assertNotIn("验证过程", text)
def test_tsl_modules_index_routes_all_module_pages(self):
text = TSL_MODULES_INDEX.read_text(encoding="utf-8")
self.assertIn("智能体模块路由规则", text)
self.assertIn("tsbacktesting.md", text)
self.assertIn("wechat_message.md", text)
self.assertIn("pytsl_api.md", text)
def test_tsl_modules_omit_environment_verification_details(self):
forbidden_phrases = [
"Docker",
"docker exec",
"LD_LIBRARY_PATH",
"/data/workspace",
"U22Cli",
"gitea-runner",
"当前环境里",
"实测",
]
for path in (ROOT / "docs" / "tsl" / "modules").glob("*.md"):
text = path.read_text(encoding="utf-8")
for phrase in forbidden_phrases:
self.assertNotIn(phrase, text, msg=f"{phrase!r} found in {path}")
if __name__ == "__main__":
unittest.main()