ad31e75af6
CI / benchmark-regression (push) Has been skipped
CI / push-validation (push) Successful in 38s
CI / helm (push) Successful in 56s
CI / build (push) Successful in 1m9s
CI / lint (push) Successful in 1m27s
CI / quality (push) Successful in 1m26s
CI / typecheck (push) Successful in 1m36s
CI / security (push) Successful in 1m35s
CI / integration_tests (push) Successful in 4m20s
CI / e2e_tests (push) Successful in 5m37s
CI / unit_tests (push) Failing after 6m57s
CI / coverage (push) Has been skipped
CI / docker (push) Has been skipped
CI / status-check (push) Failing after 3s
CI / benchmark-publish (push) Successful in 1h17m19s
CI / benchmark-publish (pull_request) Has been skipped
CI / push-validation (pull_request) Successful in 35s
CI / helm (pull_request) Successful in 42s
CI / lint (pull_request) Successful in 1m7s
CI / build (pull_request) Successful in 1m1s
CI / quality (pull_request) Successful in 1m14s
CI / typecheck (pull_request) Successful in 1m28s
CI / benchmark-regression (pull_request) Failing after 56s
CI / security (pull_request) Successful in 1m50s
CI / integration_tests (pull_request) Successful in 4m24s
CI / e2e_tests (pull_request) Successful in 5m16s
CI / unit_tests (pull_request) Failing after 7m54s
CI / coverage (pull_request) Has been skipped
CI / docker (pull_request) Has been skipped
CI / status-check (pull_request) Failing after 4s
- Validate --format argument before any side effects; raise typer.BadParameter with a clear message for unsupported format values (fail-fast principle) - Pass normalised fmt_value (lowercased) to format_output instead of raw fmt to ensure consistent behaviour regardless of input casing - Rewrite robot/helper_actor_remove_cli.py to exercise the real CLI end-to-end via subprocess (no mocking); seeds a test actor via agents actor add, then removes it with --format json and validates the JSON envelope ISSUES CLOSED: #6491
165 lines
5.3 KiB
Python
165 lines
5.3 KiB
Python
"""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 <command>
|
|
|
|
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()
|