Files
cleveragents-core/robot/helper_session_cli.py
T
freemo b82a9b6962
CI / lint (push) Waiting to run
CI / typecheck (push) Waiting to run
CI / security (push) Waiting to run
CI / quality (push) Waiting to run
CI / unit_tests (push) Waiting to run
CI / integration_tests (push) Waiting to run
CI / e2e_tests (push) Waiting to run
CI / coverage (push) Blocked by required conditions
CI / benchmark-regression (push) Blocked by required conditions
CI / benchmark-publish (push) Waiting to run
CI / build (push) Waiting to run
CI / docker (push) Blocked by required conditions
CI / helm (push) Waiting to run
CI / status-check (push) Blocked by required conditions
fix(cli): add spec-required Validation and Merge panels and correct title/message in agents session import (#3460)
Co-authored-by: Jeffrey Phillips Freeman <the@jeffreyfreeman.me>
Co-committed-by: Jeffrey Phillips Freeman <the@jeffreyfreeman.me>
2026-04-05 18:08:09 +00:00

312 lines
9.5 KiB
Python

"""Helper script for session_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 datetime import datetime
from pathlib import Path
from unittest.mock import MagicMock
# 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 ulid import ULID # noqa: E402
from cleveragents.cli.commands import session as session_mod # noqa: E402
from cleveragents.cli.commands.session import app as session_app # noqa: E402
from cleveragents.domain.models.core.session import ( # noqa: E402
MessageRole,
Session,
SessionMessage,
SessionNotFoundError,
SessionTokenUsage,
)
runner = CliRunner()
def _mock_session(
session_id: str | None = None,
actor_name: str | None = None,
messages: list[SessionMessage] | None = None,
) -> Session:
"""Create a test Session instance."""
return Session(
session_id=session_id or str(ULID()),
actor_name=actor_name,
namespace="local",
messages=messages or [],
token_usage=SessionTokenUsage(
input_tokens=100,
output_tokens=50,
estimated_cost=0.005,
),
created_at=datetime.now(),
updated_at=datetime.now(),
)
def _mock_message(
role: MessageRole = MessageRole.USER,
content: str = "Hello",
sequence: int = 0,
) -> SessionMessage:
return SessionMessage(
message_id=str(ULID()),
role=role,
content=content,
sequence=sequence,
timestamp=datetime.now(),
)
def _setup_service() -> MagicMock:
"""Create and install a mock service."""
svc = MagicMock()
session_mod._service = svc
return svc
def _teardown() -> None:
session_mod._service = None
# ---------------------------------------------------------------------------
# Subcommands
# ---------------------------------------------------------------------------
def create_default() -> None:
svc = _setup_service()
svc.create.return_value = _mock_session()
try:
result = runner.invoke(session_app, ["create"])
assert result.exit_code == 0, f"exit={result.exit_code}: {result.output}"
print("session-cli-create-default-ok")
finally:
_teardown()
def create_actor() -> None:
svc = _setup_service()
svc.create.return_value = _mock_session(actor_name="openai/gpt-4")
try:
result = runner.invoke(session_app, ["create", "--actor", "openai/gpt-4"])
assert result.exit_code == 0, f"exit={result.exit_code}: {result.output}"
assert "openai/gpt-4" in result.output
print("session-cli-create-actor-ok")
finally:
_teardown()
def list_empty() -> None:
svc = _setup_service()
svc.list.return_value = []
try:
result = runner.invoke(session_app, ["list"])
assert result.exit_code == 0
assert "No sessions found" in result.output
print("session-cli-list-empty-ok")
finally:
_teardown()
def list_populated() -> None:
svc = _setup_service()
svc.list.return_value = [
_mock_session(actor_name="openai/gpt-4"),
_mock_session(),
]
try:
result = runner.invoke(session_app, ["list"])
assert result.exit_code == 0
assert "Sessions" in result.output
print("session-cli-list-populated-ok")
finally:
_teardown()
def show_valid() -> None:
sid = str(ULID())
svc = _setup_service()
session = _mock_session(
session_id=sid,
messages=[
_mock_message(MessageRole.USER, "Hello", 0),
_mock_message(MessageRole.ASSISTANT, "Hi there", 1),
],
)
svc.get.return_value = session
try:
result = runner.invoke(session_app, ["show", sid])
assert result.exit_code == 0, f"exit={result.exit_code}: {result.output}"
assert "Session Summary" in result.output
print("session-cli-show-valid-ok")
finally:
_teardown()
def show_not_found() -> None:
svc = _setup_service()
svc.get.side_effect = SessionNotFoundError("not found")
try:
result = runner.invoke(session_app, ["show", "INVALID"])
assert result.exit_code != 0
assert "Session not found" in result.output
print("session-cli-show-not-found-ok")
finally:
_teardown()
def delete_yes() -> None:
sid = str(ULID())
svc = _setup_service()
svc.get.return_value = _mock_session(session_id=sid)
svc.delete.return_value = None
try:
result = runner.invoke(session_app, ["delete", sid, "--yes"])
assert result.exit_code == 0, f"exit={result.exit_code}: {result.output}"
assert "deleted" in result.output
print("session-cli-delete-yes-ok")
finally:
_teardown()
def export_import_roundtrip() -> None:
"""Test export to file then import back."""
sid = str(ULID())
svc = _setup_service()
session = _mock_session(session_id=sid, actor_name="openai/gpt-4")
svc.get.return_value = session
export_data = session.as_export_dict()
svc.export_session.return_value = export_data
fd, path = tempfile.mkstemp(suffix=".json")
os.close(fd)
os.unlink(path)
try:
# Export
result = runner.invoke(session_app, ["export", sid, "--output", path])
assert result.exit_code == 0, f"export exit={result.exit_code}: {result.output}"
assert os.path.exists(path)
# Set up import mock
imported = _mock_session(actor_name="openai/gpt-4")
svc.import_session.return_value = imported
# Import
result = runner.invoke(session_app, ["import", "--input", path])
assert result.exit_code == 0, f"import exit={result.exit_code}: {result.output}"
assert "Session Import" in result.output
assert "Validation" in result.output
assert "Merge" in result.output
assert "Import completed" in result.output
print("session-cli-export-import-roundtrip-ok")
finally:
if os.path.exists(path):
os.unlink(path)
_teardown()
def import_rich_panels() -> None:
"""Test that session import renders Session Import, Validation, and Merge panels."""
import json as _json
svc = _setup_service()
session = _mock_session(actor_name="openai/gpt-4")
imported = _mock_session(actor_name="openai/gpt-4")
svc.import_session.return_value = imported
fd, path = tempfile.mkstemp(suffix=".json")
with os.fdopen(fd, "w") as fh:
_json.dump(session.as_export_dict(), fh, default=str)
try:
result = runner.invoke(session_app, ["import", "--input", path])
assert result.exit_code == 0, f"exit={result.exit_code}: {result.output}"
assert "Session Import" in result.output, (
f"Missing 'Session Import' panel: {result.output}"
)
assert "Validation" in result.output, (
f"Missing 'Validation' panel: {result.output}"
)
assert "Merge" in result.output, f"Missing 'Merge' panel: {result.output}"
assert "Import completed" in result.output, (
f"Missing 'Import completed': {result.output}"
)
assert "Input:" in result.output, f"Missing 'Input:' field: {result.output}"
assert "Session ID:" in result.output, (
f"Missing 'Session ID:' field: {result.output}"
)
assert "Checksum:" in result.output, (
f"Missing 'Checksum:' field: {result.output}"
)
assert "Actor Ref:" in result.output, (
f"Missing 'Actor Ref:' field: {result.output}"
)
assert "Existing:" in result.output, (
f"Missing 'Existing:' field: {result.output}"
)
assert "Strategy:" in result.output, (
f"Missing 'Strategy:' field: {result.output}"
)
print("session-cli-import-rich-panels-ok")
finally:
if os.path.exists(path):
os.unlink(path)
_teardown()
def tell_message() -> None:
sid = str(ULID())
svc = _setup_service()
svc.get.return_value = _mock_session(session_id=sid)
svc.append_message.side_effect = [
_mock_message(MessageRole.USER, "Hello", 0),
_mock_message(MessageRole.ASSISTANT, "Acknowledged: Hello", 1),
]
try:
result = runner.invoke(session_app, ["tell", "--session", sid, "Hello"])
assert result.exit_code == 0, f"exit={result.exit_code}: {result.output}"
assert "Acknowledged" in result.output
print("session-cli-tell-message-ok")
finally:
_teardown()
# ---------------------------------------------------------------------------
# Dispatcher
# ---------------------------------------------------------------------------
_COMMANDS: dict[str, object] = {
"create-default": create_default,
"create-actor": create_actor,
"list-empty": list_empty,
"list-populated": list_populated,
"show-valid": show_valid,
"show-not-found": show_not_found,
"delete-yes": delete_yes,
"export-import-roundtrip": export_import_roundtrip,
"import-rich-panels": import_rich_panels,
"tell-message": tell_message,
}
if __name__ == "__main__":
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(1)
cmd = _COMMANDS[sys.argv[1]]
cmd() # type: ignore[operator]