"""Tests for the terminal tool-call renderer (action-aware cards)."""
from __future__ import annotations
from rich.console import Console
from rich.text import Text
from clients.terminal.tui.renderers.terminal import (
TerminalToolResultRenderer,
TerminalToolStartedRenderer,
)
from clients.terminal.tui.themes import get_active_theme, set_active_theme
def _render_text(renderable) -> str:
console = Console(record=True, width=80, force_terminal=True, color_system=None)
console.print(renderable)
return console.export_text()
def _body(panel):
r = panel.renderable
if isinstance(r, Text):
return r
return r
def _msg(action, **kw):
msg = {"type": "tool_call", "tool": "terminal", "args": {"action": action}, "success": True}
msg.update(kw)
return msg
def test_accepts_only_terminal_tool_call() -> None:
r = TerminalToolResultRenderer()
assert r.accepts(_msg("run"))
assert not r.accepts({"type": "tool_call", "tool": "filesystem", "args": {"action": "read"}})
assert not r.accepts({"type": "tool_started", "tool": "terminal"})
def test_run_shows_command_and_exit_code() -> None:
set_active_theme("gnexus-dark")
theme = get_active_theme()
panel = TerminalToolResultRenderer().render(
_msg("run", args={"action": "run", "command": "echo hi"}, result="hi", success=True, metadata={"returncode": 0})
)
text = _render_text(panel)
assert "$ echo hi" in text
assert "hi" in text
assert "exit 0" in text
assert "✓" in str(panel.title)
def test_run_failure_marks_exit_code_red() -> None:
set_active_theme("gnexus-dark")
panel = TerminalToolResultRenderer().render(
_msg("run", args={"action": "run", "command": "false"}, result="", success=False, metadata={"returncode": 1})
)
text = _render_text(panel)
assert "$ false" in text
assert "exit 1" in text
assert "✗" in str(panel.title)
def test_open_shows_name_description_pid() -> None:
set_active_theme("gnexus-dark")
panel = TerminalToolResultRenderer().render(
_msg(
"open",
args={"action": "open", "terminal_name": "dev", "description": "dev server", "background": True},
result="Terminal 'dev' opened in background. PID: 123.",
success=True,
metadata={"name": "dev", "description": "dev server", "pid": 123},
)
)
text = _render_text(panel)
assert "dev" in text
assert "dev server" in text
assert "background" in text
assert "123" in text
def test_list_renders_output_dim() -> None:
set_active_theme("gnexus-dark")
panel = TerminalToolResultRenderer().render(
_msg("list", result="Active terminals (1):\n 🟢 dev: server", success=True)
)
text = _render_text(panel)
assert "Active terminals" in text
assert "dev" in text
def test_status_renders_output() -> None:
set_active_theme("gnexus-dark")
panel = TerminalToolResultRenderer().render(
_msg("status", result="Terminal: dev\nStatus: busy\nPID: 7", success=True)
)
text = _render_text(panel)
assert "Terminal: dev" in text
assert "busy" in text
def test_send_input_echo() -> None:
set_active_theme("gnexus-dark")
panel = TerminalToolResultRenderer().render(
_msg("send_input", result="Sent input to 'dev'.", success=True)
)
text = _render_text(panel)
assert "Sent input" in text
def test_close_message() -> None:
set_active_theme("gnexus-dark")
panel = TerminalToolResultRenderer().render(
_msg("close", result="Terminal 'dev' closed.", success=True)
)
text = _render_text(panel)
assert "closed" in text
def test_run_truncates_long_output() -> None:
set_active_theme("gnexus-dark")
big = "\n".join(f"line {i}" for i in range(300))
panel = TerminalToolResultRenderer().render(
_msg("run", args={"action": "run", "command": "heavy"}, result=big, success=True, metadata={"returncode": 0})
)
text = _render_text(panel)
assert "lines truncated]" in text
assert "line 299" in text
assert "line 0" not in text
# ── tool_started cards ─────────────────────────────────────────────────────
def _started(action, **kw):
msg = {"type": "tool_started", "tool": "terminal", "args": {"action": action}}
msg.update(kw)
return msg
def test_started_accepts_only_terminal_tool_started() -> None:
r = TerminalToolStartedRenderer()
assert r.accepts(_started("run"))
assert not r.accepts({"type": "tool_started", "tool": "filesystem", "args": {}})
assert not r.accepts({"type": "tool_call", "tool": "terminal"})
def test_started_run_shows_command() -> None:
set_active_theme("gnexus-dark")
panel = TerminalToolStartedRenderer().render(
_started("run", args={"action": "run", "command": "echo hi"})
)
assert "→ terminal run" in str(panel.title)
text = _render_text(panel)
assert "$ echo hi" in text
def test_started_open_shows_name_desc_background() -> None:
set_active_theme("gnexus-dark")
panel = TerminalToolStartedRenderer().render(
_started(
"open",
args={"action": "open", "terminal_name": "dev", "description": "dev server",
"background": True, "command": "npm start"},
)
)
assert "→ terminal open" in str(panel.title)
text = _render_text(panel)
assert "dev" in text
assert "dev server" in text
assert "background: true" in text
assert "npm start" in text
def test_started_close_shows_name() -> None:
set_active_theme("gnexus-dark")
panel = TerminalToolStartedRenderer().render(
_started("close", args={"action": "close", "terminal_name": "dev"})
)
assert "→ terminal close" in str(panel.title)
text = _render_text(panel)
assert "dev" in text
def test_started_send_input_shows_name_and_input() -> None:
set_active_theme("gnexus-dark")
panel = TerminalToolStartedRenderer().render(
_started("send_input", args={"action": "send_input", "terminal_name": "dev", "input": "yes\n"})
)
text = _render_text(panel)
assert "dev" in text
assert "yes" in text
def test_started_list_empty_body() -> None:
set_active_theme("gnexus-dark")
panel = TerminalToolStartedRenderer().render(_started("list", args={"action": "list"}))
assert "→ terminal list" in str(panel.title)
# list has no per-call headline argument; body is empty (no crash, no JSON dump).
assert panel.renderable.plain == ""