618 lines
29 KiB
Python
618 lines
29 KiB
Python
import unittest
|
||
from pathlib import Path
|
||
|
||
ROOT = Path(__file__).resolve().parents[1]
|
||
RULESET_TSL = ROOT / "rulesets" / "tsl" / "index.md"
|
||
TSL_INTRODUCTION = ROOT / "docs" / "tsl" / "syntax" / "01_introduction.md"
|
||
TSL_QUICKSTART = ROOT / "docs" / "tsl" / "syntax" / "02_quickstart.md"
|
||
TSL_CORE_MODEL = ROOT / "docs" / "tsl" / "syntax" / "03_core_model.md"
|
||
TSL_VALUES = ROOT / "docs" / "tsl" / "syntax" / "04_values_and_literals.md"
|
||
TSL_VARIABLES = ROOT / "docs" / "tsl" / "syntax" / "05_variables_and_constants.md"
|
||
TSL_FUNCTIONS = ROOT / "docs" / "tsl" / "syntax" / "06_functions_and_calls.md"
|
||
TSL_EXPRESSIONS = ROOT / "docs" / "tsl" / "syntax" / "07_expressions_and_operators.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_CATALOG_INDEX = ROOT / "docs" / "tsl" / "reference" / "catalog" / "index.md"
|
||
TSL_REFERENCE_VERIFIED_INDEX = ROOT / "docs" / "tsl" / "reference" / "verified" / "index.md"
|
||
TSL_REFERENCE_VERIFIED_CORE = ROOT / "docs" / "tsl" / "reference" / "verified" / "core.md"
|
||
TSL_REFERENCE_UNAVAILABLE = ROOT / "docs" / "tsl" / "reference" / "unavailable_methods.md"
|
||
TSL_FINANCE_INDEX = ROOT / "docs" / "tsl" / "finance" / "index.md"
|
||
TSL_MODULES_INDEX = ROOT / "docs" / "tsl" / "modules" / "index.md"
|
||
|
||
TSL_REMAINING_SYNTAX_AGENT_SECTIONS = {
|
||
"08_control_flow.md": "Agent 控制流判断流程",
|
||
"09_objects_and_classes.md": "Agent 对象/类判断流程",
|
||
"10_units_and_scope.md": "Agent unit/作用域判断流程",
|
||
"11_runtime_context_and_with.md": "Agent 运行时上下文判断流程",
|
||
"12_pitfalls.md": "Agent 常见误写判断流程",
|
||
"13_matrix_and_collections.md": "Agent 矩阵/集合判断流程",
|
||
"14_resultset_and_filters.md": "Agent 结果集/过滤判断流程",
|
||
"15_ts_sql.md": "Agent TS-SQL 判断流程",
|
||
"16_debug_and_profiler.md": "Agent 调试/Profiler 判断流程",
|
||
"18_lexical_structure_and_compile_options.md": "Agent 词法/编译选项判断流程",
|
||
"19_types_and_conversions.md": "Agent 类型/转换判断流程",
|
||
"20_strings_and_text.md": "Agent 字符串/文本判断流程",
|
||
"21_external_calls_and_threads.md": "Agent 外部调用/线程判断流程",
|
||
"22_namespace_libpath_and_unit_runtime.md": "Agent 命名空间/Libpath 判断流程",
|
||
"23_object_runtime_and_introspection.md": "Agent 对象运行时/反射判断流程",
|
||
"24_builtin_runtime_objects.md": "Agent 内置运行时对象判断流程",
|
||
"25_set_operations.md": "Agent 集合运算判断流程",
|
||
"26_matrix_deep_dive.md": "Agent 矩阵深水判断流程",
|
||
"27_fmarray.md": "Agent FMArray 判断流程",
|
||
"28_ts_sql_core.md": "Agent TS-SQL Core 判断流程",
|
||
"29_ts_sql_advanced.md": "Agent TS-SQL Advanced 判断流程",
|
||
"30_runtime_services_and_global_cache.md": "Agent 运行时服务/全局缓存判断流程",
|
||
"31_complex_and_weakref.md": "Agent 复数/弱引用判断流程",
|
||
"32_object_overloads_and_iteration.md": "Agent 对象重载/迭代判断流程",
|
||
}
|
||
|
||
|
||
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/syntax/index.md", text)
|
||
self.assertIn("docs/tsl/finance/index.md", text)
|
||
self.assertIn("docs/tsl/modules/index.md", text)
|
||
self.assertIn("docs/tsl/reference/index.md", text)
|
||
|
||
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("`project_root`:目标项目根目录", text)
|
||
self.assertIn("`deploy_root`:相对于 `project_root` 的项目内目标目录", text)
|
||
self.assertIn("不是外部 clone 出来的 Playbook 仓库路径", text)
|
||
self.assertIn("外部 clone 场景下必须显式填写 `deploy_root`", text)
|
||
self.assertNotIn("方式二:手动复制快照", text)
|
||
self.assertNotIn("方式三:CLI 裁剪复制", text)
|
||
self.assertNotIn("如果省略 `deploy_root`,默认仍部署到 `docs/standards/playbook`", text)
|
||
|
||
def test_playbook_example_defines_deploy_root_as_target_path(self):
|
||
text = PLAYBOOK_EXAMPLE.read_text(encoding="utf-8")
|
||
self.assertIn('deploy_root = "docs/standards/playbook"', text)
|
||
self.assertIn("相对于 project_root", text)
|
||
self.assertIn("不是外部 clone 的 playbook 路径", text)
|
||
self.assertIn("从外部 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_first_file_model(self):
|
||
text = RULESET_TSL.read_text(encoding="utf-8")
|
||
|
||
self.assertIn("agent 判断流程", text)
|
||
self.assertIn("用户已给出 `.tsl` / `.tsf` 后缀时,后缀就是判断依据", text)
|
||
self.assertIn("`.tsl` 是可执行脚本", text)
|
||
self.assertIn("语句区按顺序执行", text)
|
||
self.assertIn("函数/类声明区", text)
|
||
self.assertIn("`.tsf` 是模块/函数扩展文件", text)
|
||
self.assertIn("funcext", text)
|
||
self.assertIn("仍不明确时向用户确认", text)
|
||
self.assertNotIn("`.tsl` 仅 `function`", text)
|
||
self.assertNotIn("脚本优先 `.tsl`,可复用扩展优先 `.tsf`", text)
|
||
|
||
def test_tsl_syntax_docs_keep_script_then_declarations_model(self):
|
||
quickstart = (ROOT / "docs/tsl/syntax/02_quickstart.md").read_text(
|
||
encoding="utf-8"
|
||
)
|
||
core_model = (ROOT / "docs/tsl/syntax/03_core_model.md").read_text(
|
||
encoding="utf-8"
|
||
)
|
||
functions = (ROOT / "docs/tsl/syntax/06_functions_and_calls.md").read_text(
|
||
encoding="utf-8"
|
||
)
|
||
pitfalls = (ROOT / "docs/tsl/syntax/12_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_index_requires_maintainer_verified_examples_before_publication(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("agent 不需要自行执行验证", text)
|
||
|
||
def test_tsl_introduction_is_agent_first_decision_model(self):
|
||
text = TSL_INTRODUCTION.read_text(encoding="utf-8")
|
||
|
||
self.assertIn("agent", text)
|
||
self.assertIn("后缀就是判断依据", text)
|
||
self.assertIn("用户未给后缀", text)
|
||
self.assertIn("可执行代码对应 `.tsl`", text)
|
||
self.assertIn("通用模块对应 `.tsf`", text)
|
||
self.assertIn("`.tsl` 可执行脚本第一印象", text)
|
||
self.assertIn("`.tsf` 通用模块第一印象", text)
|
||
self.assertIn("不要发明语法", text)
|
||
self.assertIn("test();", text)
|
||
self.assertIn("function test();", text)
|
||
self.assertIn("function Test1();", text)
|
||
|
||
self.assertNotIn("先看顶层主体", text)
|
||
self.assertNotIn("顶层主体优先收敛成四类", text)
|
||
|
||
def test_tsl_quickstart_is_agent_coding_gate(self):
|
||
text = TSL_QUICKSTART.read_text(encoding="utf-8")
|
||
|
||
self.assertIn("Agent 快速落代码流程", text)
|
||
self.assertIn("先看用户有没有指定 `.tsl` / `.tsf` 后缀", text)
|
||
self.assertIn("再根据交付目标判断 `.tsl` 或 `.tsf`", text)
|
||
self.assertIn("只从 `代码块身份:已验证可执行示例` 的骨架起手", text)
|
||
self.assertIn("不要发明语法", text)
|
||
self.assertIn("不要在声明区后面继续追加脚本语句", 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("Agent 文件模型判断流程", text)
|
||
self.assertIn("后缀是第一证据", text)
|
||
self.assertIn("可执行交付", text)
|
||
self.assertIn("可复用扩展交付", 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_values_page_is_agent_value_decision_page(self):
|
||
text = TSL_VALUES.read_text(encoding="utf-8")
|
||
|
||
self.assertIn("Agent 值写法判断流程", text)
|
||
self.assertIn("普通值优先从整数、实数、普通字符串、布尔和 `array(...)` 起手", text)
|
||
self.assertIn("字符串默认用普通字符串", text)
|
||
self.assertIn("`L\"\"` / `U\"\"` 只在编码或宽串场景", text)
|
||
self.assertIn("先判断要顺序数组还是字符串键表", text)
|
||
self.assertIn("顺序数组和二进制缓冲区下标从 `0` 开始", text)
|
||
self.assertIn("字符串下标从 `1` 开始", text)
|
||
self.assertIn("没有已验证代码块时不要发明值写法", 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\nString index out of bounds\n```", 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("Agent 变量/常量判断流程", text)
|
||
self.assertIn("普通变量默认直接用 `:=` 首次赋值", text)
|
||
self.assertIn("只有用户要求显式声明或遇到 `{$Explicit+}` 时才优先写 `var`", text)
|
||
self.assertIn("顶层脚本常量优先用 `const name := value;`", text)
|
||
self.assertIn("`const Name = value;` 优先放在函数 `const` 段、`unit` 接口或类成员里", text)
|
||
self.assertIn("单变量拆包必须写成 `[name, ] := array(...)`", text)
|
||
self.assertIn("没有已验证代码块时不要发明变量/常量写法", 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\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("Agent 函数/调用判断流程", text)
|
||
self.assertIn("先判断当前文件是 `.tsl` 还是 `.tsf`", text)
|
||
self.assertIn("`.tsl` 中先写语句区,再把 `function` / `procedure` 声明放在后面", text)
|
||
self.assertIn("需要返回值时用 `function`,不需要返回值时用 `procedure`", text)
|
||
self.assertIn("命名参数只写 `name: value`", text)
|
||
self.assertIn("不要把 `name = value` 当成命名参数", text)
|
||
self.assertIn("没有已验证代码块时不要发明函数/调用写法", 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\n12\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")
|
||
|
||
self.assertIn("Agent 表达式/运算符判断流程", text)
|
||
self.assertIn("先判断要写赋值、比较、条件求值、表达式对象、空安全访问还是链式比较", text)
|
||
self.assertIn("赋值只能用 `:=`", text)
|
||
self.assertIn("比较才用 `=`", text)
|
||
self.assertIn("条件求值优先用 `flag ? true_value : false_value`", text)
|
||
self.assertIn("`if condition then true_value else false_value` 必须带 `else`", text)
|
||
self.assertIn("没有已验证代码块时不要发明表达式/运算符写法", 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\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("```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_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("agent", text.lower())
|
||
self.assertIn("不要发明", text)
|
||
|
||
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_verified_function_workflow(self):
|
||
text = TSL_REFERENCE_INDEX.read_text(encoding="utf-8")
|
||
|
||
self.assertIn("Agent 函数使用规则", text)
|
||
self.assertIn("只从 verified 函数页读取参数类型", text)
|
||
self.assertIn("catalog 只是候选函数索引", text)
|
||
self.assertIn("确认每个方法的参数类型", text)
|
||
self.assertIn("verified/index.md", text)
|
||
self.assertIn("verified/core.md", text)
|
||
self.assertIn("unavailable_methods.md", text)
|
||
self.assertIn("不能把 catalog 里的函数名直接当成可调用事实", 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_catalog_is_candidate_index_not_callable_fact(self):
|
||
catalog_index = TSL_REFERENCE_CATALOG_INDEX.read_text(encoding="utf-8")
|
||
|
||
self.assertIn("候选函数索引", catalog_index)
|
||
self.assertIn("候选名没有进入 verified 函数页前不能当成可调用事实", catalog_index)
|
||
self.assertIn("../verified/index.md", catalog_index)
|
||
self.assertIn("../verified/core.md", catalog_index)
|
||
self.assertIn("只从 verified 函数页读取参数类型", catalog_index)
|
||
self.assertNotIn("错误参数组合", catalog_index)
|
||
self.assertNotIn("参数验证", catalog_index)
|
||
|
||
for path in (ROOT / "docs" / "tsl" / "reference" / "catalog").glob("*.md"):
|
||
text = path.read_text(encoding="utf-8")
|
||
self.assertIn("候选函数索引", text, msg=f"{path.name} missing candidate warning")
|
||
self.assertIn("只从 verified 函数页读取参数类型", text)
|
||
|
||
def test_tsl_reference_verified_index_routes_to_parameter_fact_pages(self):
|
||
text = TSL_REFERENCE_VERIFIED_INDEX.read_text(encoding="utf-8")
|
||
|
||
self.assertIn("文档类型:agent 参数事实索引", text)
|
||
self.assertIn("core.md", text)
|
||
self.assertIn("Abs", text)
|
||
self.assertIn("ifInt", text)
|
||
self.assertIn("DateToStr", text)
|
||
self.assertIn("Length", text)
|
||
self.assertIn("只从具体函数页读取接收类型", text)
|
||
self.assertNotIn("验证过程", text)
|
||
self.assertNotIn("代码块身份", text)
|
||
|
||
def test_tsl_reference_verified_core_records_agent_parameter_facts(self):
|
||
text = TSL_REFERENCE_VERIFIED_CORE.read_text(encoding="utf-8")
|
||
|
||
self.assertIn("文档类型:agent 参数事实表", text)
|
||
self.assertIn("接收类型", text)
|
||
self.assertIn("返回", text)
|
||
self.assertIn("`Abs(value)`", text)
|
||
self.assertIn("`ifInt(value)`", text)
|
||
self.assertIn("`DateToStr(value)`", text)
|
||
self.assertIn("`Length(value)`", text)
|
||
self.assertIn("整数", text)
|
||
self.assertIn("实数", text)
|
||
self.assertIn("字符串", text)
|
||
self.assertIn("数组", text)
|
||
self.assertIn("日期时间", 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_unavailable_records_only_unavailable_methods(self):
|
||
text = TSL_REFERENCE_UNAVAILABLE.read_text(encoding="utf-8")
|
||
|
||
self.assertIn("文档类型:当前测试环境不支持的方法清单", text)
|
||
self.assertIn("当前测试环境不支持的方法", text)
|
||
self.assertIn("暂无已入档记录", text)
|
||
self.assertNotIn("failure", text.lower())
|
||
self.assertNotIn("失败", text)
|
||
self.assertNotIn("验证过程", text)
|
||
self.assertNotIn("参数类型错误", text)
|
||
self.assertNotIn("`Abs(\"-3\")`", text)
|
||
self.assertNotIn("`Abs()`", text)
|
||
self.assertNotIn("`DateToStr(\"2011-12-31\")`", text)
|
||
self.assertNotIn("`DateToStr()`", text)
|
||
self.assertNotIn("`Length(123)`", text)
|
||
self.assertNotIn("Function Abs execution error", 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)
|
||
|
||
def test_tsl_finance_docs_are_agent_business_decision_pages(self):
|
||
finance_root = ROOT / "docs" / "tsl" / "finance"
|
||
|
||
for path in finance_root.glob("*.md"):
|
||
text = path.read_text(encoding="utf-8")
|
||
with self.subTest(path=path.name):
|
||
self.assertIn("Agent", text)
|
||
self.assertIn("不要发明", text)
|
||
self.assertIn("项目实际接口", text)
|
||
self.assertNotIn("是否含已验证", text)
|
||
self.assertNotIn("代码块身份", text)
|
||
self.assertNotIn("验证过程", text)
|
||
|
||
def test_tsl_finance_index_routes_all_finance_pages(self):
|
||
text = TSL_FINANCE_INDEX.read_text(encoding="utf-8")
|
||
|
||
self.assertIn("Agent Finance 路由规则", text)
|
||
self.assertIn("entry_decision.md", text)
|
||
self.assertIn("market_data_context.md", text)
|
||
self.assertIn("series_and_indicator_model.md", text)
|
||
self.assertIn("selection_and_signal_patterns.md", text)
|
||
self.assertIn("backtest_and_trade_flow.md", text)
|
||
|
||
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("Agent", 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("Agent Modules 路由规则", text)
|
||
self.assertIn("tsbacktesting.md", text)
|
||
self.assertIn("tsl_python_interop.md", text)
|
||
self.assertIn("wechat_message.md", text)
|
||
self.assertIn("pytsl_api.md", text)
|
||
|
||
def test_tsl_finance_and_modules_omit_environment_verification_details(self):
|
||
forbidden_phrases = [
|
||
"Docker",
|
||
"docker exec",
|
||
"LD_LIBRARY_PATH",
|
||
"/data/workspace",
|
||
"U22Cli",
|
||
"gitea-runner",
|
||
"当前环境里",
|
||
"实测",
|
||
]
|
||
|
||
for root_name in ("finance", "modules"):
|
||
for path in (ROOT / "docs" / "tsl" / root_name).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()
|