Files
cleveragents-core/robot/helper_session_cli.py
freemo c301fc13dd fix(ci): fix remaining Robot Framework integration test failures
- A2A JSON-RPC 2.0 migration: updated 3 robot helpers still using the
  old API (operation= → method=, resp.status/resp.data → resp.result):
  helper_m6_autonomy_acceptance.py, helper_wf03_plan_prompt_confidence.py,
  wf02_test_generation_artifacts.py
- Session CLI: updated 'Session Details' → 'Session Summary' panel title
  assertion in helper_session_cli.py to match current CLI output
- Audit wiring: fixed container_wiring test to create DB tables via
  Base.metadata.create_all() and disable async mode for deterministic
  verification (container's in-memory DB had no schema)
- Missing migration: added m9_001_session_name_column.py to add the
  'name' column to sessions table (ORM model had it, Alembic migration
  was missing, causing 'session create' to fail after 'agents init')

All 1908 integration tests now pass (0 failed, 0 skipped).

ISSUES CLOSED: #2597
2026-04-04 20:38:16 +00:00

258 lines
7.4 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 Imported" in result.output
print("session-cli-export-import-roundtrip-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,
"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]