139 lines
6.0 KiB
Python
139 lines
6.0 KiB
Python
import ast
|
|
import unittest
|
|
from pathlib import Path
|
|
|
|
from scripts import check_local_demo_readiness, check_trading_delivery_readiness, run_offline_checks
|
|
|
|
|
|
ROOT = Path(__file__).resolve().parents[2]
|
|
README = ROOT / "README.md"
|
|
RUNBOOK = ROOT / "RUNBOOK.md"
|
|
TRADING_PLAN_DOC = ROOT / "docs" / "AI_ICT_实盘交易分析系统开发计划_v1.md"
|
|
ICT_BOOK_DOC = ROOT / "docs" / "ICT交易全书_出版终校版.md"
|
|
TRAINING_PUBLIC_DOC = ROOT / "docs" / "training_console_public_surface_v1.md"
|
|
TRAINING_PACKAGE_SMOKE_DOC = ROOT / "docs" / "examples" / "training_case_package_smoke_m6.md"
|
|
TRADING_INDEX_HTML = ROOT / "src" / "web" / "index.html"
|
|
TRADING_APP_JS = ROOT / "src" / "web" / "app.js"
|
|
TRAINING_HTML = ROOT / "src" / "web" / "training.html"
|
|
TRAINING_JS = ROOT / "src" / "web" / "training.js"
|
|
OLD_TRAINING_DOC = ROOT / "docs" / "training_case_engine_m1_m6_product_readme.md"
|
|
|
|
|
|
class DocumentationConsistencyTests(unittest.TestCase):
|
|
def test_docs_inventory_is_current_and_old_training_product_doc_is_gone(self) -> None:
|
|
top_level_docs = {
|
|
path.name
|
|
for path in (ROOT / "docs").iterdir()
|
|
if path.is_file()
|
|
}
|
|
self.assertEqual(
|
|
top_level_docs,
|
|
{
|
|
"AI_ICT_全自动开发执行流程_v1.md",
|
|
"AI_ICT_实盘交易分析系统开发计划_v1.md",
|
|
"AI_ICT_实盘辅助决策系统开发方案_v2.md",
|
|
"ICT交易全书_出版终校版.md",
|
|
"training_console_public_surface_v1.md",
|
|
},
|
|
)
|
|
self.assertTrue(TRAINING_PACKAGE_SMOKE_DOC.is_file())
|
|
self.assertFalse(OLD_TRAINING_DOC.exists())
|
|
|
|
def test_readme_and_runbook_use_current_training_entry_and_not_old_doc(self) -> None:
|
|
for path in (README, RUNBOOK):
|
|
text = path.read_text(encoding="utf-8")
|
|
with self.subTest(path=path.name):
|
|
self.assertIn("/training", text)
|
|
self.assertNotIn("training_case_engine_m1_m6_product_readme.md", text)
|
|
self.assertIn("Training Console at `/training`", README.read_text(encoding="utf-8"))
|
|
|
|
def test_training_public_doc_matches_current_console_and_routes(self) -> None:
|
|
text = TRAINING_PUBLIC_DOC.read_text(encoding="utf-8")
|
|
for snippet in (
|
|
"The public browser entry is `/training`.",
|
|
"The trading workbench stays at `/`.",
|
|
"| GET | `/training/cases` |",
|
|
"| GET | `/training/progress` |",
|
|
"| POST | `/training/attempts` |",
|
|
"Training public error catalog:",
|
|
):
|
|
self.assertIn(snippet, text)
|
|
|
|
def test_training_package_smoke_doc_matches_current_fixture_and_cli(self) -> None:
|
|
text = TRAINING_PACKAGE_SMOKE_DOC.read_text(encoding="utf-8")
|
|
for snippet in (
|
|
"tests/fixtures/training_case_package_m6.json",
|
|
".venv/bin/python scripts/run_training_case_package.py",
|
|
"--input",
|
|
"--output",
|
|
"public package",
|
|
"public export",
|
|
"public output",
|
|
):
|
|
self.assertIn(snippet, text)
|
|
|
|
def test_trading_root_and_training_console_are_split_cleanly(self) -> None:
|
|
trading_html = TRADING_INDEX_HTML.read_text(encoding="utf-8")
|
|
trading_js = TRADING_APP_JS.read_text(encoding="utf-8")
|
|
training_html = TRAINING_HTML.read_text(encoding="utf-8")
|
|
training_js = TRAINING_JS.read_text(encoding="utf-8")
|
|
|
|
self.assertNotIn('id="trainingSection"', trading_html)
|
|
self.assertIn('id="trainingSection"', training_html)
|
|
self.assertNotIn("TRAINING_PUBLIC_ROUTES", trading_js)
|
|
self.assertIn("TRAINING_PUBLIC_ROUTES", training_js)
|
|
|
|
def test_offline_checks_include_training_console_syntax(self) -> None:
|
|
commands = dict(run_offline_checks.build_check_commands(env={}))
|
|
self.assertEqual(commands["dashboard_js_syntax"], ["node", "--check", "src/web/app.js"])
|
|
self.assertEqual(commands["training_console_js_syntax"], ["node", "--check", "src/web/training.js"])
|
|
|
|
def test_local_demo_readiness_uses_current_docs(self) -> None:
|
|
self.assertEqual(
|
|
check_local_demo_readiness.LOCAL_DEMO_DOCS,
|
|
(README, RUNBOOK),
|
|
)
|
|
|
|
def test_trading_delivery_readiness_uses_canonical_smoke_route(self) -> None:
|
|
commands = dict(check_trading_delivery_readiness.TRADING_DELIVERY_COMMAND_GROUPS)["deploy_and_health"]
|
|
self.assertIn('curl -fsS "http://127.0.0.1:8000/trading/candidates?instrument_id=1&limit=1"', commands)
|
|
|
|
def test_readme_lists_current_json_output_variables(self) -> None:
|
|
text = README.read_text(encoding="utf-8")
|
|
for variable in (
|
|
"LIVE_ADAPTER_OUTPUT_JSON",
|
|
"TRADING_JOURNAL_OUTPUT_JSON",
|
|
"TRADE_PLAN_CANDIDATES_OUTPUT_JSON",
|
|
):
|
|
self.assertIn(variable, text)
|
|
|
|
def test_scripts_do_not_reference_deleted_training_product_doc(self) -> None:
|
|
for path in (
|
|
ROOT / "scripts" / "check_local_demo_readiness.py",
|
|
ROOT / "scripts" / "run_offline_checks.py",
|
|
ROOT / "scripts" / "check_trading_delivery_readiness.py",
|
|
):
|
|
text = path.read_text(encoding="utf-8")
|
|
with self.subTest(path=path.name):
|
|
self.assertNotIn("training_case_engine_m1_m6_product_readme.md", text)
|
|
|
|
def test_current_readiness_scripts_are_static_and_offline(self) -> None:
|
|
for module_path in (
|
|
ROOT / "scripts" / "check_local_demo_readiness.py",
|
|
ROOT / "scripts" / "check_trading_delivery_readiness.py",
|
|
):
|
|
source = module_path.read_text(encoding="utf-8")
|
|
tree = ast.parse(source)
|
|
imported_modules = {
|
|
alias.name
|
|
for node in ast.walk(tree)
|
|
if isinstance(node, ast.Import)
|
|
for alias in node.names
|
|
}
|
|
with self.subTest(path=module_path.name):
|
|
self.assertNotIn("subprocess", imported_modules)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
unittest.main()
|