diff --git a/README.md b/README.md index fe474af..986a7ef 100644 --- a/README.md +++ b/README.md @@ -35,7 +35,7 @@ Set `MIGRATIONS_OUTPUT_JSON=1` when running `run_migrations.py` for a machine-re - `python3 scripts/run_backtest.py` - run backtest and write results - `python3 scripts/run_autonomous_cycle.py` - run the unattended cycle: offline checks, database probe, readiness checks, then guarded pipeline when the database is available; prints `Next actions` for the next unattended step - `python3 scripts/run_database_readiness.py` - run read-only database readiness checks for connection, schema, seed data, and candle availability; prints `next_actions` and `action_plan` in JSON mode -- `python3 scripts/run_offline_checks.py` - run database-free automated checks +- `.venv/bin/python scripts/run_offline_checks.py` - run the broader offline check suite, including the Training Case package smoke - `.venv/bin/python scripts/run_training_case_package.py --input tests/fixtures/training_case_package_m6.json --output /tmp/training_case_public_export.json` - run the Training Case offline package smoke and write a public export that omits private answer detail - `python3 scripts/run_migration_static_checks.py` - check migration order and required core schema without connecting to the database - `python3 scripts/run_script_static_checks.py` - check automation scripts for unsafe ambient environment fallback patterns @@ -105,7 +105,7 @@ Example offline replay gate: OFFLINE_REPLAY_FILE=tests/fixtures/offline_replay_sample.jsonl \ REPLAY_LEFT_BARS=1 REPLAY_RIGHT_BARS=1 \ REPLAY_MIN_CANDLES=5 REPLAY_MIN_SWINGS=2 \ -python3 scripts/run_offline_checks.py +.venv/bin/python scripts/run_offline_checks.py ``` Optional OKX backfill environment variables used by `run_okx_backfill.py`: diff --git a/docs/examples/training_case_package_smoke_m6.md b/docs/examples/training_case_package_smoke_m6.md index 443c851..4386c8c 100644 --- a/docs/examples/training_case_package_smoke_m6.md +++ b/docs/examples/training_case_package_smoke_m6.md @@ -10,4 +10,10 @@ Run the packaged fixture through the local import and public export path: The command uses local files only. The written export is the public package shape and omits private answer values. +The broader offline check suite includes this same Training Case smoke: + +```bash +.venv/bin/python scripts/run_offline_checks.py +``` + On success, stdout is one JSON object with `passed`, `input`, `output`, `imported`, and `exported`. `--output` writes only the public export JSON shown by `output`; it does not add private answer detail. diff --git a/tests/unit/test_training_case_docs_cli_m10.py b/tests/unit/test_training_case_docs_cli_m10.py index 2de8c42..8b5e03e 100644 --- a/tests/unit/test_training_case_docs_cli_m10.py +++ b/tests/unit/test_training_case_docs_cli_m10.py @@ -3,10 +3,13 @@ import io import json import re import shlex +import sys import tempfile import unittest from pathlib import Path +from types import SimpleNamespace +from scripts import run_offline_checks from scripts import run_training_case_package as training_cli from src.services.training import TrainingCaseService @@ -17,6 +20,7 @@ PRODUCT_README = ROOT / "docs" / "training_case_engine_m1_m6_product_readme.md" README = ROOT / "README.md" FIXTURE = ROOT / "tests" / "fixtures" / "training_case_package_m6.json" FIXTURE_REF = "tests/fixtures/training_case_package_m6.json" +OFFLINE_CHECK_COMMAND = ".venv/bin/python scripts/run_offline_checks.py" PUBLIC_FIELD = "answer" + "_key" RESTRICTED_PUBLIC_TEXT = ( "data" + "base", @@ -247,6 +251,42 @@ class TrainingCaseDocsCliM10Tests(unittest.TestCase): self.assertIn("private answer", lower_text) self.assertNotIn(PUBLIC_FIELD, text) + def test_offline_checks_registry_runs_training_package_smoke_entry(self) -> None: + commands = run_offline_checks.build_check_commands(env={}) + names = [name for name, _command in commands] + command = dict(commands)["training_case_package"] + calls = [] + + def fake_runner(command_parts, **kwargs): + calls.append((command_parts, kwargs["cwd"])) + return SimpleNamespace(returncode=0, stdout="", stderr="") + + output = io.StringIO() + with contextlib.redirect_stdout(output): + exit_code = run_offline_checks.run_checks( + commands=[("training_case_package", command)], + runner=fake_runner, + node_available=True, + env={}, + ) + + self.assertEqual(names.count("training_case_package"), 1) + self.assertEqual(command, [sys.executable, "scripts/run_training_case_package.py"]) + self.assertEqual((ROOT / command[1]).resolve(), Path(training_cli.__file__).resolve()) + self.assertEqual(exit_code, 0) + self.assertEqual(calls, [(command, run_offline_checks.ROOT)]) + self.assertIn("Offline check passed: training_case_package", output.getvalue()) + + def test_training_docs_name_broader_offline_check_path(self) -> None: + for path in (DOC_EXAMPLE, README, PRODUCT_README): + text = path.read_text(encoding="utf-8") + lower_text = text.lower() + + with self.subTest(path=path.name): + self.assertIn(OFFLINE_CHECK_COMMAND, text) + self.assertIn("broader offline", lower_text) + self.assertIn("training case", lower_text) + if __name__ == "__main__": unittest.main()