"""Helper script for tool_cli.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 pathlib import Path from typing import Any 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.tool import app as tool_app # noqa: E402 from cleveragents.cli.commands.validation import app as validation_app # noqa: E402 runner = CliRunner() _TOOL_YAML = """\ name: local/smoke-tool description: Smoke test tool source: custom code: | def run(inputs): return {"result": True} """ _VALIDATION_YAML = """\ name: local/smoke-val description: Smoke test validation source: custom mode: required code: | def run(inputs): return {"passed": True} """ def _mock_tool( name: str = "local/smoke-tool", tool_type: str = "tool", ) -> dict[str, Any]: ns, sn = name.split("/", 1) return { "name": name, "description": f"Smoke test {tool_type}", "source": "custom", "tool_type": tool_type, "namespace": ns, "short_name": sn, "capability": {"read_only": False, "writes": False, "checkpointable": False}, "timeout": 300, } def _mock_attachment() -> dict[str, Any]: return { "attachment_id": "att-smoke-001", "validation_name": "local/smoke-val", "resource_id": "resource/r1", "mode": "required", "project_name": None, "plan_id": None, "created_at": "2026-01-01T00:00:00", } 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 tool_add_config() -> None: """Verify tool add --config works.""" path = _write_yaml(_TOOL_YAML) try: svc = MagicMock() svc.register_tool.return_value = _mock_tool() svc.get_tool.return_value = None with patch( "cleveragents.cli.commands.tool._get_tool_registry_service", return_value=svc, ): result = runner.invoke(tool_app, ["add", "--config", path]) assert result.exit_code == 0, f"exit={result.exit_code}: {result.output}" print("tool-cli-add-config-ok") finally: Path(path).unlink(missing_ok=True) def tool_list_all() -> None: """Verify tool list works.""" svc = MagicMock() svc.list_tools.return_value = [_mock_tool()] with patch( "cleveragents.cli.commands.tool._get_tool_registry_service", return_value=svc, ): result = runner.invoke(tool_app, ["list"]) assert result.exit_code == 0, f"exit={result.exit_code}: {result.output}" print("tool-cli-list-ok") def tool_show_name() -> None: """Verify tool show accepts a namespaced name.""" svc = MagicMock() svc.get_tool.return_value = _mock_tool() with patch( "cleveragents.cli.commands.tool._get_tool_registry_service", return_value=svc, ): result = runner.invoke(tool_app, ["show", "local/smoke-tool"]) assert result.exit_code == 0, f"exit={result.exit_code}: {result.output}" print("tool-cli-show-ok") def tool_remove_name() -> None: """Verify tool remove accepts a namespaced name.""" svc = MagicMock() svc.get_tool.return_value = _mock_tool() svc.remove_tool.return_value = True with patch( "cleveragents.cli.commands.tool._get_tool_registry_service", return_value=svc, ): result = runner.invoke(tool_app, ["remove", "local/smoke-tool", "--yes"]) assert result.exit_code == 0, f"exit={result.exit_code}: {result.output}" print("tool-cli-remove-ok") def validation_add_config() -> None: """Verify validation add --config works.""" path = _write_yaml(_VALIDATION_YAML) try: svc = MagicMock() svc.register_tool.return_value = _mock_tool("local/smoke-val", "validation") svc.get_tool.return_value = None with patch( "cleveragents.cli.commands.validation._get_tool_registry_service", return_value=svc, ): result = runner.invoke(validation_app, ["add", "--config", path]) assert result.exit_code == 0, f"exit={result.exit_code}: {result.output}" print("validation-cli-add-ok") finally: Path(path).unlink(missing_ok=True) def validation_attach() -> None: """Verify validation attach works.""" svc = MagicMock() svc.attach_validation.return_value = _mock_attachment() with patch( "cleveragents.cli.commands.validation._get_tool_registry_service", return_value=svc, ): result = runner.invoke( validation_app, ["attach", "resource/r1", "local/smoke-val"], ) assert result.exit_code == 0, f"exit={result.exit_code}: {result.output}" print("validation-cli-attach-ok") def validation_detach() -> None: """Verify validation detach works.""" svc = MagicMock() svc.detach_validation.return_value = True with patch( "cleveragents.cli.commands.validation._get_tool_registry_service", return_value=svc, ): result = runner.invoke( validation_app, ["detach", "att-smoke-001", "--yes"], ) assert result.exit_code == 0, f"exit={result.exit_code}: {result.output}" print("validation-cli-detach-ok") def tool_list_type_tool() -> None: """Verify tool list --type tool returns only tools.""" svc = MagicMock() svc.list_tools.return_value = [_mock_tool("local/smoke-tool", "tool")] with patch( "cleveragents.cli.commands.tool._get_tool_registry_service", return_value=svc, ): result = runner.invoke(tool_app, ["list", "--type", "tool"]) assert result.exit_code == 0, f"exit={result.exit_code}: {result.output}" svc.list_tools.assert_called_once_with( namespace=None, tool_type="tool", source=None ) print("tool-cli-list-type-tool-ok") def tool_list_type_validation() -> None: """Verify tool list --type validation returns only validations.""" svc = MagicMock() svc.list_tools.return_value = [_mock_tool("local/smoke-val", "validation")] with patch( "cleveragents.cli.commands.tool._get_tool_registry_service", return_value=svc, ): result = runner.invoke(tool_app, ["list", "--type", "validation"]) assert result.exit_code == 0, f"exit={result.exit_code}: {result.output}" svc.list_tools.assert_called_once_with( namespace=None, tool_type="validation", source=None ) print("tool-cli-list-type-validation-ok") _COMMANDS = { "tool-add-config": tool_add_config, "tool-list": tool_list_all, "tool-list-type-tool": tool_list_type_tool, "tool-list-type-validation": tool_list_type_validation, "tool-show": tool_show_name, "tool-remove": tool_remove_name, "validation-add-config": validation_add_config, "validation-attach": validation_attach, "validation-detach": validation_detach, } if __name__ == "__main__": if len(sys.argv) < 2 or sys.argv[1] not in _COMMANDS: print(f"Usage: {sys.argv[0]} <{'|'.join(_COMMANDS)}>") sys.exit(1) _COMMANDS[sys.argv[1]]()