forked from cleveragents/cleveragents-core
c301fc13dd
- 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
258 lines
7.4 KiB
Python
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]
|