from __future__ import annotations import io import json from pathlib import Path from gyxx_flow.catalog import WorkflowCatalog from gyxx_flow.cli import EXIT_USAGE, build_parser, main from gyxx_flow.core.config import Settings from gyxx_flow.workflow import WorkflowDefinition from gyxx_flow.workflow.registry import WorkflowRegistry PROJECT_ROOT = Path(__file__).resolve().parents[1] def test_schedule_cli_exposes_only_project_owned_service_commands() -> None: parser = build_parser() command_action = next(action for action in parser._actions if action.dest == "command") schedule_parser = command_action.choices["schedule"] schedule_action = next( action for action in schedule_parser._actions if action.dest == "schedule_command" ) assert set(schedule_action.choices) == {"run", "status"} def test_run_rejects_implicit_scheduled_execution(tmp_path: Path) -> None: catalog = WorkflowCatalog.load(PROJECT_ROOT / "config") registry = WorkflowRegistry(catalog) registry.register(WorkflowDefinition("product.daily", ())) output = io.StringIO() exit_code = main( ["run", "product.daily", "--scheduled"], registry=registry, settings=Settings(project_root=PROJECT_ROOT, data_root=tmp_path), stdout=output, stderr=io.StringIO(), ) assert exit_code == EXIT_USAGE assert output.getvalue() == "" def test_python_scheduler_status_is_project_owned_and_cross_platform(tmp_path: Path) -> None: output = io.StringIO() settings = Settings(project_root=PROJECT_ROOT, data_root=tmp_path / "data") exit_code = main( ["schedule", "status"], settings=settings, stdout=output, stderr=io.StringIO(), ) assert exit_code == 0 payload = json.loads(output.getvalue()) assert payload["timezone"] == "Asia/Shanghai" assert payload["scheduled_count"] == 29 assert payload["state"]["schema_version"] == 1 def test_python_scheduler_dry_run_never_launches_or_writes_state(tmp_path: Path) -> None: output = io.StringIO() settings = Settings(project_root=PROJECT_ROOT, data_root=tmp_path / "data") exit_code = main( ["schedule", "run", "--dry-run", "--once", "--misfire-grace-seconds", "3456000"], settings=settings, stdout=output, stderr=io.StringIO(), ) assert exit_code == 0 payload = json.loads(output.getvalue()) assert payload["mode"] == "dry-run" assert "content.metrics.daily" in payload["due_or_started"] assert "content.metrics.backfill" not in payload["due_or_started"] assert "content.relogin.weekly" not in payload["due_or_started"] assert "product.market_rank" in payload["due_or_started"] assert not (settings.data_root / "state" / "scheduler" / "state.json").exists()