♻️ refactor(tsl-api-reference): split lookup workflows and maintenance tooling
This commit is contained in:
@@ -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
@@ -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),
|
||||
)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user