"""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 Details" 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]