Newer
Older
navi-1 / tests / clients / test_subagent_renderers.py
"""Tests for sub-agent card renderers (spawn_agent + nested styling)."""

from __future__ import annotations

from rich.console import Console

from clients.terminal.tui.renderers.planning import PlanningStatusRenderer, PlanReadyRenderer
from clients.terminal.tui.renderers.subagent import (
    SpawnAgentStartedRenderer,
    SpawnAgentResultRenderer,
)
from clients.terminal.tui.renderers.thinking import ThinkingRenderer
from clients.terminal.tui.renderers.tool import ToolStartedRenderer, ToolResultRenderer
from clients.terminal.tui.themes import 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 test_spawn_started_card_shows_profile_and_task() -> None:
    set_active_theme("gnexus-dark")
    renderer = SpawnAgentStartedRenderer()
    msg = {
        "type": "tool_started",
        "tool": "spawn_agent",
        "args": {"task": "audit the auth flow", "profile_id": "developer"},
        "is_subagent": False,
    }
    assert renderer.accepts(msg)
    panel = renderer.render(msg)
    assert "subagent" in str(panel.title)
    assert "developer" in str(panel.title)
    assert "audit the auth flow" in _render_text(panel)


def test_spawn_started_does_not_accept_other_tools() -> None:
    renderer = SpawnAgentStartedRenderer()
    assert not renderer.accepts({"type": "tool_started", "tool": "filesystem", "args": {}})


def test_spawn_result_card_shows_status() -> None:
    set_active_theme("gnexus-dark")
    renderer = SpawnAgentResultRenderer()
    msg = {
        "type": "tool_call",
        "tool": "spawn_agent",
        "args": {"profile_id": "developer"},
        "result": "auth flow is fine",
        "success": True,
        "is_subagent": False,
    }
    assert renderer.accepts(msg)
    panel = renderer.render(msg)
    title = str(panel.title)
    assert "developer" in title
    assert "✓" in title
    assert "auth flow is fine" in _render_text(panel)


def test_subagent_thinking_is_indented_and_labeled() -> None:
    set_active_theme("gnexus-dark")
    renderer = ThinkingRenderer()
    msg = {"type": "thinking_block", "content": "hmm nested", "is_subagent": True}
    out = _render_text(renderer.render(msg))
    assert "subagent thinking" in out
    assert "hmm nested" in out
    # Indented: the content lines start with spaces.
    assert any(line.startswith("  ") for line in out.splitlines())


def test_subagent_planning_status_is_indented() -> None:
    set_active_theme("gnexus-dark")
    renderer = PlanningStatusRenderer()
    msg = {"type": "planning_status", "phase": 1, "label": "Analysis", "is_subagent": True}
    out = _render_text(renderer.render(msg))
    assert "(subagent)" in out
    assert any(line.startswith("  ") for line in out.splitlines())


def test_subagent_plan_card_is_indented() -> None:
    set_active_theme("gnexus-dark")
    renderer = PlanReadyRenderer()
    msg = {"type": "plan_ready", "plan": "## Plan\n**Steps:**\n1. x\n", "is_subagent": True}
    out = _render_text(renderer.render(msg))
    assert "subagent plan" in out
    assert any(line.startswith("  ") for line in out.splitlines())


def test_subagent_tool_card_is_indented() -> None:
    set_active_theme("gnexus-dark")
    started = ToolStartedRenderer().render(
        {"type": "tool_started", "tool": "filesystem", "args": {}, "is_subagent": True}
    )
    result = ToolResultRenderer().render(
        {"type": "tool_call", "tool": "filesystem", "result": "ok", "success": True, "is_subagent": True}
    )
    assert any(line.startswith("  ") for line in _render_text(started).splitlines())
    assert any(line.startswith("  ") for line in _render_text(result).splitlines())


def test_non_subagent_tool_card_not_indented() -> None:
    set_active_theme("gnexus-dark")
    started = ToolStartedRenderer().render(
        {"type": "tool_started", "tool": "filesystem", "args": {}, "is_subagent": False}
    )
    # A top-level card has border lines starting at column 0 (e.g. "╭──").
    assert any(line.startswith("╭") for line in _render_text(started).splitlines())