8384f53e28
Add Robot regression coverage for `actor remove --format json`, document the flag in the CLI synopsis, and record the fix in the changelog.\n\nISSUES CLOSED: #6491
121 lines
3.6 KiB
Python
121 lines
3.6 KiB
Python
"""Helper script for Robot tests covering ``actor remove --format`` output."""
|
|
|
|
from __future__ import annotations
|
|
|
|
import json
|
|
import sys
|
|
from typing import Any
|
|
from unittest.mock import MagicMock, patch
|
|
|
|
from typer.testing import CliRunner
|
|
|
|
from cleveragents.cli.commands.actor import app as actor_app
|
|
from cleveragents.domain.models.core.actor import Actor
|
|
|
|
|
|
def _make_actor(
|
|
*,
|
|
name: str = "local/robot-remove-actor",
|
|
provider: str = "openai",
|
|
model: str = "gpt-4",
|
|
config: dict[str, Any] | None = None,
|
|
graph_descriptor: dict[str, Any] | None = None,
|
|
unsafe: bool = False,
|
|
is_default: bool = False,
|
|
is_built_in: bool = False,
|
|
) -> Actor:
|
|
"""Create a minimal :class:`Actor` instance for CLI testing."""
|
|
|
|
blob = config or {}
|
|
return Actor(
|
|
id=1,
|
|
name=name,
|
|
provider=provider,
|
|
model=model,
|
|
config_blob=blob,
|
|
config_hash=Actor.compute_hash(blob),
|
|
graph_descriptor=graph_descriptor,
|
|
unsafe=unsafe,
|
|
is_default=is_default,
|
|
is_built_in=is_built_in,
|
|
)
|
|
|
|
|
|
def _invoke_actor_remove(actor: Actor, *, impact: tuple[int, int, int]) -> str:
|
|
"""Invoke ``actor remove`` with Typer's :class:`CliRunner` and return stdout."""
|
|
|
|
runner = CliRunner()
|
|
|
|
with (
|
|
patch("cleveragents.cli.commands.actor._get_services") as mock_services,
|
|
patch("cleveragents.cli.commands.actor._compute_actor_impact") as mock_impact,
|
|
):
|
|
registry = MagicMock()
|
|
service = MagicMock()
|
|
registry.get_actor.return_value = actor
|
|
mock_services.return_value = (service, registry)
|
|
mock_impact.return_value = impact
|
|
|
|
result = runner.invoke(
|
|
actor_app,
|
|
["remove", actor.name, "--format", "json"],
|
|
)
|
|
|
|
assert result.exit_code == 0, (
|
|
"actor remove --format json failed"
|
|
f" (exit_code={result.exit_code}):\n{result.output}"
|
|
)
|
|
return result.output
|
|
|
|
|
|
def test_remove_format_json() -> None:
|
|
"""Ensure ``actor remove --format json`` emits a spec-compliant envelope."""
|
|
|
|
actor = _make_actor()
|
|
impact = (2, 1, 3)
|
|
output = _invoke_actor_remove(actor, impact=impact)
|
|
|
|
payload = json.loads(output.strip())
|
|
|
|
assert payload["command"] == f"agents actor remove {actor.name}", payload
|
|
assert payload["status"] == "ok", payload
|
|
assert payload["exit_code"] == 0, payload
|
|
|
|
data = payload["data"]
|
|
removed = data.get("actor_removed", {})
|
|
assert removed.get("name") == actor.name, removed
|
|
assert removed.get("provider") == actor.provider, removed
|
|
assert removed.get("model") == actor.model, removed
|
|
|
|
impact_section = data.get("impact", {})
|
|
assert impact_section.get("sessions") == impact[0], impact_section
|
|
assert impact_section.get("active_plans") == impact[1], impact_section
|
|
assert impact_section.get("actions_referencing") == impact[2], impact_section
|
|
|
|
cleanup = data.get("cleanup", {})
|
|
assert cleanup.get("config") == "kept on disk", cleanup
|
|
assert cleanup.get("contexts") == "0 orphaned", cleanup
|
|
|
|
messages = payload.get("messages", [])
|
|
assert messages, payload
|
|
assert messages[0].get("level") == "ok", messages
|
|
assert "Actor removed" in messages[0].get("text", ""), messages
|
|
|
|
print("actor-remove-json-format-ok")
|
|
|
|
|
|
def main() -> None:
|
|
command = sys.argv[1] if len(sys.argv) > 1 else "remove-json"
|
|
dispatch: dict[str, Any] = {
|
|
"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)
|
|
handler()
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|