♻️ refactor(tsl-api-reference): split lookup workflows and maintenance tooling

This commit is contained in:
csh
2026-08-20 17:47:48 +08:00
parent 9b95bf2682
commit cd0405d64b
18 changed files with 707 additions and 374 deletions
+54
View File
@@ -338,6 +338,60 @@ no_backup = true
).is_file()
)
def test_tsl_api_install_excludes_codegen_dictionary_builder(self):
self.assertTrue(
(ROOT / "tools" / "tsl-codegen" / "scripts" / "build_dictionary.py").is_file()
)
self.assertFalse(
(
ROOT
/ "skills"
/ "tsl-api-reference"
/ "scripts"
/ "build_dictionary.py"
).exists()
)
with tempfile.TemporaryDirectory() as tmp_dir:
project_root = Path(tmp_dir) / "project"
project_root.mkdir()
config = project_root / "playbook.toml"
config.write_text(
"""
[playbook]
project_root = "."
playbook_root = "custom/playbook"
install_mode = "snapshot"
[install_skills]
agents_home = ".test-agents"
mode = "list"
skills = ["tsl-api-reference"]
no_backup = true
""".lstrip(),
encoding="utf-8",
newline="\n",
)
result = run_playbook(SCRIPT, config, project_root)
self.assertEqual(0, result.returncode, result.stderr)
installed = (
project_root
/ ".test-agents"
/ "skills"
/ "tsl-api-reference"
)
self.assertTrue((installed / "scripts" / "class_lookup.py").is_file())
self.assertTrue((installed / "workflows" / "api-lookup.md").is_file())
self.assertTrue(
(installed / "workflows" / "class-and-framework.md").is_file()
)
self.assertTrue(
(installed / "workflows" / "dictionary-lookup.md").is_file()
)
self.assertFalse((installed / "scripts" / "build_dictionary.py").exists())
self.assertFalse((installed / "references" / "maintenance.md").exists())
def test_invalid_toml_is_reported_without_a_traceback(self):
with tempfile.TemporaryDirectory() as tmp_dir:
project_root = Path(tmp_dir) / "project"
+163 -32
View File
@@ -1,3 +1,4 @@
import ast
import csv
import json
import re
@@ -10,7 +11,6 @@ from pathlib import Path
ROOT = Path(__file__).resolve().parents[1]
SKILL_ROOT = ROOT / "skills" / "tsl-api-reference"
FRAMEWORK_LOOKUP = SKILL_ROOT / "scripts" / "framework_lookup.py"
CLASS_LOOKUP = SKILL_ROOT / "scripts" / "class_lookup.py"
API_LOOKUP = SKILL_ROOT / "scripts" / "lookup.py"
FRAMEWORK_INDEX = SKILL_ROOT / "data" / "framework_index.json"
@@ -357,6 +357,121 @@ class TslApiFrameworkTests(unittest.TestCase):
self.assertIsNone(packet["profile"])
self.assertEqual({"method": 25, "property": 15}, packet["member_summary"])
def test_custom_index_without_profiles_queries_generic_class(self):
fieldnames = [
"name",
"scope",
"module",
"signature",
"page",
"anchor",
"tags",
"summary",
"kind",
"binding",
"visibility",
"owner",
"qualified_name",
]
rows = [
{
"name": "MyClass",
"scope": "project",
"module": "demo",
"signature": "MyClass",
"page": "project/demo.md",
"anchor": "myclass",
"tags": "示例 class",
"summary": "示例类。",
"kind": "class",
"binding": "",
"visibility": "",
"owner": "",
"qualified_name": "MyClass",
},
{
"name": "Run",
"scope": "project",
"module": "demo",
"signature": "Run()",
"page": "project/demo.md",
"anchor": "run",
"tags": "运行",
"summary": "运行示例。",
"kind": "method",
"binding": "instance",
"visibility": "public",
"owner": "MyClass",
"qualified_name": "MyClass.Run",
},
]
with tempfile.TemporaryDirectory() as temp_dir:
data_dir = Path(temp_dir) / "data"
data_dir.mkdir()
tsv_path = data_dir / "function_index.tsv"
with tsv_path.open("w", encoding="utf-8", newline="") as handle:
writer = csv.DictWriter(
handle,
fieldnames=fieldnames,
delimiter="\t",
lineterminator="\n",
)
writer.writeheader()
writer.writerows(rows)
result = run_script(
CLASS_LOOKUP,
"--tsv",
str(tsv_path),
"--class",
"MyClass",
"--include-members",
"--format",
"json",
)
self.assertEqual(0, result.returncode, result.stderr)
packet = json.loads(result.stdout)
self.assertEqual("resolved", packet["status"])
self.assertEqual("not_profiled", packet["profile_status"])
self.assertIsNone(packet["profile"])
self.assertEqual({"method": 1}, packet["member_summary"])
self.assertEqual("MyClass.Run", packet["members"][0]["qualified_name"])
def test_explicit_missing_framework_file_remains_a_data_error(self):
with tempfile.TemporaryDirectory() as temp_dir:
missing = Path(temp_dir) / "missing-framework-index.json"
result = run_script(
CLASS_LOOKUP,
"--profiles",
str(missing),
"--class",
"TStringList",
)
self.assertEqual(1, result.returncode)
self.assertIn("failed to load", result.stderr)
def test_configuration_on_generic_class_requires_curated_profile(self):
result = run_script(
CLASS_LOOKUP,
"--class",
"TStringList",
"--config",
"Mode=1",
"--format",
"json",
)
self.assertEqual(0, result.returncode, result.stderr)
packet = json.loads(result.stdout)
self.assertEqual("not_profiled", packet["profile_status"])
self.assertIn(
"CLASS_NOT_PROFILED",
{item["code"] for item in packet["diagnostics"]},
)
def test_profiled_class_adds_framework_lifecycle(self):
result = run_script(
CLASS_LOOKUP,
@@ -475,16 +590,29 @@ class TslApiFrameworkTests(unittest.TestCase):
self.assertIn(f"OK: {indexed_class_count()} classes indexed", result.stdout)
self.assertIn("6 framework profile(s) validated", result.stdout)
def test_framework_index_references_existing_api_entries(self):
result = run_script(FRAMEWORK_LOOKUP, "--check")
def test_framework_module_has_no_independent_cli(self):
source = (SKILL_ROOT / "scripts" / "framework_lookup.py").read_text(
encoding="utf-8"
)
tree = ast.parse(source)
functions = {
node.name for node in tree.body if isinstance(node, ast.FunctionDef)
}
imported_modules = {
alias.name
for node in tree.body
if isinstance(node, ast.Import)
for alias in node.names
}
self.assertEqual(0, result.returncode, result.stderr)
self.assertIn("OK: 6 framework profile(s) validated", result.stdout)
self.assertNotIn("main", functions)
self.assertNotIn("argparse", imported_modules)
self.assertNotIn('__name__ == "__main__"', source)
def test_ts_backtesting_returns_class_first_scaffold(self):
result = run_script(
FRAMEWORK_LOOKUP,
"--framework",
CLASS_LOOKUP,
"--class",
"tsBackTesting",
"--format",
"json",
@@ -492,25 +620,26 @@ class TslApiFrameworkTests(unittest.TestCase):
self.assertEqual(0, result.returncode, result.stderr)
packet = json.loads(result.stdout)
self.assertEqual("resolved", packet["scaffold_status"])
self.assertEqual("incomplete", packet["contract_status"])
profile = packet["profile"]
self.assertEqual("resolved", profile["scaffold_status"])
self.assertEqual("incomplete", profile["contract_status"])
self.assertEqual(
["construct", "configure", "schedule", "callback", "execute", "inspect"],
[phase["phase"] for phase in packet["lifecycle"]],
[phase["phase"] for phase in profile["lifecycle"]],
)
self.assertEqual(
["tsBackTesting.GetTradeOrder"],
[hook["api"] for hook in packet["hooks"] if hook["required"]],
[hook["api"] for hook in profile["hooks"] if hook["required"]],
)
self.assertEqual(
"CONTRACT_INCOMPLETE",
packet["diagnostics"][0]["code"],
profile["diagnostics"][0]["code"],
)
def test_candidate_discriminator_does_not_invent_a_mode(self):
result = run_script(
FRAMEWORK_LOOKUP,
"--framework",
CLASS_LOOKUP,
"--class",
"tsBackTesting",
"--config",
"FGroupType=1",
@@ -520,17 +649,18 @@ class TslApiFrameworkTests(unittest.TestCase):
self.assertEqual(0, result.returncode, result.stderr)
packet = json.loads(result.stdout)
self.assertEqual({"FGroupType": "1"}, packet["configured_values"])
self.assertEqual("incomplete", packet["contract_status"])
profile = packet["profile"]
self.assertEqual({"FGroupType": "1"}, profile["configured_values"])
self.assertEqual("incomplete", profile["contract_status"])
self.assertIn(
"CONTRACT_MODE_UNRESOLVED",
{item["code"] for item in packet["diagnostics"]},
{item["code"] for item in profile["diagnostics"]},
)
def test_unknown_configuration_is_reported(self):
result = run_script(
FRAMEWORK_LOOKUP,
"--framework",
CLASS_LOOKUP,
"--class",
"tsBackTesting",
"--config",
"notAField=1",
@@ -542,13 +672,13 @@ class TslApiFrameworkTests(unittest.TestCase):
packet = json.loads(result.stdout)
self.assertIn(
"UNKNOWN_CONFIGURATION",
{item["code"] for item in packet["diagnostics"]},
{item["code"] for item in packet["profile"]["diagnostics"]},
)
def test_unknown_framework_uses_query_success_exit_code(self):
def test_unknown_class_uses_query_success_exit_code(self):
result = run_script(
FRAMEWORK_LOOKUP,
"--framework",
CLASS_LOOKUP,
"--class",
"MissingFramework",
"--format",
"json",
@@ -566,16 +696,16 @@ class TslApiFrameworkTests(unittest.TestCase):
index_path = Path(temp_dir) / "framework_index.json"
index_path.write_text(json.dumps(data), encoding="utf-8")
result = run_script(
FRAMEWORK_LOOKUP,
CLASS_LOOKUP,
"--check",
"--index",
"--profiles",
str(index_path),
)
self.assertEqual(1, result.returncode)
self.assertIn("does not identify an indexed overload", result.stderr)
def test_query_reports_invalid_profile_as_data_error(self):
def test_query_rejects_invalid_profile_data(self):
data = json.loads(FRAMEWORK_INDEX.read_text(encoding="utf-8"))
data["frameworks"][0]["execution"][0]["evidence"] = [
"module/ts-backtesting.md#missing"
@@ -584,17 +714,18 @@ class TslApiFrameworkTests(unittest.TestCase):
index_path = Path(temp_dir) / "framework_index.json"
index_path.write_text(json.dumps(data), encoding="utf-8")
result = run_script(
FRAMEWORK_LOOKUP,
"--framework",
CLASS_LOOKUP,
"--class",
"tsBackTesting",
"--format",
"json",
"--index",
"--profiles",
str(index_path),
)
self.assertEqual(1, result.returncode)
self.assertEqual("data_error", json.loads(result.stdout)["status"])
self.assertEqual("", result.stdout)
self.assertIn("does not identify an indexed overload", result.stderr)
def test_check_rejects_profile_without_indexed_class(self):
data = json.loads(FRAMEWORK_INDEX.read_text(encoding="utf-8"))
@@ -603,9 +734,9 @@ class TslApiFrameworkTests(unittest.TestCase):
index_path = Path(temp_dir) / "framework_index.json"
index_path.write_text(json.dumps(data), encoding="utf-8")
result = run_script(
FRAMEWORK_LOOKUP,
CLASS_LOOKUP,
"--check",
"--index",
"--profiles",
str(index_path),
)