"""Helper script for action_cli_spec.robot smoke tests. Each subcommand is a self-contained check that prints a sentinel on success. """ from __future__ import annotations import os import sys import tempfile from datetime import datetime from pathlib import Path from unittest.mock import MagicMock, patch # Ensure local source tree is importable _SRC = str(Path(__file__).resolve().parents[1] / "src") if _SRC not in sys.path: sys.path.insert(0, _SRC) from typer.testing import CliRunner # noqa: E402 from cleveragents.cli.commands.action import app as action_app # noqa: E402 from cleveragents.domain.models.core.action import Action, ActionState # noqa: E402 from cleveragents.domain.models.core.plan import NamespacedName # noqa: E402 runner = CliRunner() _VALID_YAML = """\ name: local/smoke-action description: Smoke test action strategy_actor: openai/gpt-4 execution_actor: openai/gpt-4 definition_of_done: All smoke tests pass """ def _mock_action(name: str = "local/smoke-action") -> Action: return Action( namespaced_name=NamespacedName.parse(name), description="Smoke test action", definition_of_done="All smoke tests pass", strategy_actor="openai/gpt-4", execution_actor="openai/gpt-4", state=ActionState.AVAILABLE, created_at=datetime.now(), updated_at=datetime.now(), ) def _write_yaml(content: str) -> str: fd, path = tempfile.mkstemp(suffix=".yaml") with os.fdopen(fd, "w") as fh: fh.write(content) return path # --------------------------------------------------------------------------- # Subcommands # --------------------------------------------------------------------------- def create_requires_config() -> None: """Verify that create without --config fails.""" result = runner.invoke(action_app, ["create"]) if result.exit_code != 0: print("action-cli-create-requires-config-ok") else: print("FAIL: create without --config should fail", file=sys.stderr) sys.exit(1) def create_rejects_name() -> None: """Verify that --name flag is rejected.""" result = runner.invoke(action_app, ["create", "--name", "local/test"]) if result.exit_code != 0: print("action-cli-legacy-flag-rejected") else: print("FAIL: --name should be rejected", file=sys.stderr) sys.exit(1) def available_removed() -> None: """Verify that available subcommand no longer exists.""" result = runner.invoke(action_app, ["available", "local/test"]) if result.exit_code != 0: print("action-cli-available-removed-ok") else: print("FAIL: available should not exist", file=sys.stderr) sys.exit(1) def list_filters() -> None: """Verify that list accepts --namespace and --state.""" mock_service = MagicMock() mock_service.list_actions.return_value = [_mock_action()] with patch( "cleveragents.cli.commands.action._get_lifecycle_service", return_value=mock_service, ): r1 = runner.invoke(action_app, ["list", "--namespace", "local"]) r2 = runner.invoke(action_app, ["list", "--state", "available"]) if r1.exit_code == 0 and r2.exit_code == 0: print("action-cli-list-filters-ok") else: print("FAIL: list filters", file=sys.stderr) sys.exit(1) def show_name() -> None: """Verify show accepts a namespaced name.""" mock_service = MagicMock() mock_service.get_action_by_name.return_value = _mock_action() with patch( "cleveragents.cli.commands.action._get_lifecycle_service", return_value=mock_service, ): result = runner.invoke(action_app, ["show", "local/smoke-action"]) if result.exit_code == 0: print("action-cli-show-name-ok") else: print(f"FAIL: show returned {result.exit_code}", file=sys.stderr) sys.exit(1) def archive_name() -> None: """Verify archive accepts a namespaced name.""" mock_service = MagicMock() mock_service.get_action_by_name.return_value = _mock_action() mock_service.archive_action.return_value = _mock_action() with patch( "cleveragents.cli.commands.action._get_lifecycle_service", return_value=mock_service, ): result = runner.invoke(action_app, ["archive", "local/smoke-action"]) if result.exit_code == 0: print("action-cli-archive-name-ok") else: print(f"FAIL: archive returned {result.exit_code}", file=sys.stderr) sys.exit(1) # --------------------------------------------------------------------------- # Main dispatcher # --------------------------------------------------------------------------- _COMMANDS = { "create-requires-config": create_requires_config, "create-rejects-name": create_rejects_name, "available-removed": available_removed, "list-filters": list_filters, "show-name": show_name, "archive-name": archive_name, } def main() -> None: if len(sys.argv) < 2 or sys.argv[1] not in _COMMANDS: print(f"Usage: {sys.argv[0]} <{'|'.join(_COMMANDS)}>", file=sys.stderr) sys.exit(2) _COMMANDS[sys.argv[1]]() if __name__ == "__main__": main()