"""Helper script for Robot integration tests covering ``actor remove --format`` output. Exercises the real ``agents`` CLI via subprocess — no mocking of any kind. A test actor is seeded via ``agents actor add``, then removed via ``agents actor remove --format json``, and the resulting JSON envelope is validated against the spec. Usage:: python helper_actor_remove_cli.py Where *command* is one of: - ``remove-json`` — seed an actor, remove it with ``--format json``, validate envelope """ from __future__ import annotations import json import os import sys from pathlib import Path # Ensure src is importable when run from workspace root _SRC = str(Path(__file__).resolve().parents[1] / "src") if _SRC not in sys.path: sys.path.insert(0, _SRC) # Ensure robot/ is on the import path for helper_e2e_common. _ROBOT = str(Path(__file__).resolve().parent) if _ROBOT not in sys.path: sys.path.insert(0, _ROBOT) from helper_e2e_common import cleanup_workspace, run_cli, setup_workspace # noqa: E402 _ACTOR_NAME = "local/robot-remove-actor" _ACTOR_CONFIG: dict[str, object] = { "name": _ACTOR_NAME, "provider": "openai", "model": "gpt-4", } def _write_actor_config(workspace: str) -> str: """Write actor config JSON to a temp file and return its path.""" config_path = os.path.join(workspace, "robot_remove_actor.json") with open(config_path, "w", encoding="utf-8") as fh: json.dump(_ACTOR_CONFIG, fh) return config_path def test_remove_format_json() -> None: """Seed an actor via the real CLI, remove it with ``--format json``. Validates the resulting JSON envelope against the spec. """ workspace = setup_workspace(prefix="robot_actor_remove_") try: config_path = _write_actor_config(workspace) # Step 1: Add the actor using the real CLI. add_result = run_cli( "actor", "add", _ACTOR_NAME, "--config", config_path, workspace=workspace, ) assert add_result.returncode == 0, ( f"actor add failed (rc={add_result.returncode}):\n" f"stdout: {add_result.stdout}\nstderr: {add_result.stderr}" ) # Step 2: Remove the actor with --format json using the real CLI. remove_result = run_cli( "actor", "remove", _ACTOR_NAME, "--format", "json", workspace=workspace, ) assert remove_result.returncode == 0, ( f"actor remove --format json failed (rc={remove_result.returncode}):\n" f"stdout: {remove_result.stdout}\nstderr: {remove_result.stderr}" ) # Step 3: Parse and validate the JSON envelope. output = remove_result.stdout.strip() assert output, ( f"actor remove --format json produced no output.\n" f"stderr: {remove_result.stderr}" ) payload = json.loads(output) assert payload["command"] == f"agents actor remove {_ACTOR_NAME}", ( f"Unexpected command field: {payload.get('command')!r}" ) assert payload["status"] == "ok", ( f"Unexpected status: {payload.get('status')!r}" ) assert payload["exit_code"] == 0, ( f"Unexpected exit_code: {payload.get('exit_code')!r}" ) data = payload["data"] removed = data.get("actor_removed", {}) assert removed.get("name") == _ACTOR_NAME, ( f"actor_removed.name mismatch: {removed.get('name')!r}" ) assert removed.get("provider") == _ACTOR_CONFIG["provider"], ( f"actor_removed.provider mismatch: {removed.get('provider')!r}" ) assert removed.get("model") == _ACTOR_CONFIG["model"], ( f"actor_removed.model mismatch: {removed.get('model')!r}" ) impact = data.get("impact", {}) assert "sessions" in impact, f"Missing 'sessions' in impact: {impact}" assert "active_plans" in impact, f"Missing 'active_plans' in impact: {impact}" assert "actions_referencing" in impact, ( f"Missing 'actions_referencing' in impact: {impact}" ) cleanup = data.get("cleanup", {}) assert cleanup.get("config") == "kept on disk", ( f"cleanup.config mismatch: {cleanup.get('config')!r}" ) assert "contexts" in cleanup, f"Missing 'contexts' in cleanup: {cleanup}" messages = payload.get("messages", []) assert messages, f"Expected non-empty messages list, got: {messages}" assert messages[0].get("level") == "ok", ( f"Unexpected message level: {messages[0].get('level')!r}" ) assert "Actor removed" in messages[0].get("text", ""), ( f"Expected 'Actor removed' in message text: {messages[0].get('text')!r}" ) print("actor-remove-json-format-ok") finally: cleanup_workspace(workspace) def main() -> None: command = sys.argv[1] if len(sys.argv) > 1 else "remove-json" dispatch: dict[str, object] = { "remove-json": test_remove_format_json, } handler = dispatch.get(command) if handler is None: print(f"Unknown command: {command}", file=sys.stderr) sys.exit(1) assert callable(handler) handler() if __name__ == "__main__": main()