haiku.rag/tests/agui/test_cli_renderer.py
Yiorgis Gozadinos dccef431b0
Update tests
2025-11-13 13:22:02 +02:00

158 lines
4.2 KiB
Python

"""Tests for AGUIConsoleRenderer."""
import pytest
from pydantic import BaseModel
from rich.console import Console
from haiku.rag.agui.cli_renderer import AGUIConsoleRenderer
from haiku.rag.agui.events import (
emit_activity,
emit_run_error,
emit_run_finished,
emit_run_started,
emit_state_snapshot,
emit_step_finished,
emit_step_started,
emit_text_message,
)
class SimpleState(BaseModel):
"""Simple state for testing."""
value: int
async def async_gen(items):
"""Helper to create async generator from list."""
for item in items:
yield item
@pytest.mark.asyncio
async def test_renderer_basic_flow():
"""Test basic event rendering flow."""
console = Console(file=None, force_terminal=False) # Don't actually print
renderer = AGUIConsoleRenderer(console)
events = [
emit_run_started("t1", "r1"),
emit_state_snapshot(SimpleState(value=1)),
emit_step_started("plan"),
emit_activity("m1", "planning", "Planning research"),
emit_run_finished("t1", "r1", {"status": "complete"}),
]
result = await renderer.render(async_gen(events))
assert result == {"status": "complete"}
@pytest.mark.asyncio
async def test_renderer_state_diff():
"""Test that renderer computes state diffs correctly."""
renderer = AGUIConsoleRenderer()
# First state
old_state = {"value": 1, "text": "old", "nested": {"a": 1, "b": 2}}
# Second state with changes
new_state = {"value": 2, "text": "old", "nested": {"a": 1, "b": 3, "c": 4}}
diff = renderer._compute_diff(old_state, new_state)
assert diff == {
"value": 2,
"nested": {"b": 3, "c": 4}, # Only changed/new fields in nested
}
@pytest.mark.asyncio
async def test_renderer_state_diff_no_changes():
"""Test that no diff is computed when states are equal."""
renderer = AGUIConsoleRenderer()
state = {"value": 1, "text": "test"}
diff = renderer._compute_diff(state, state)
assert diff == {}
@pytest.mark.asyncio
async def test_renderer_handles_all_event_types():
"""Test that renderer handles all event types without errors."""
renderer = AGUIConsoleRenderer()
events = [
emit_run_started("t1", "r1"),
emit_state_snapshot(SimpleState(value=1)),
emit_step_started("step1"),
emit_step_finished("step1"),
emit_text_message("message"),
emit_activity("m1", "work", "Working"),
emit_run_error("error occurred"),
emit_run_finished("t1", "r1", {"result": "done"}),
]
# Should not raise any exceptions
result = await renderer.render(async_gen(events))
assert result == {"result": "done"}
@pytest.mark.asyncio
async def test_renderer_initial_state():
"""Test that initial state is rendered."""
renderer = AGUIConsoleRenderer()
events = [
emit_state_snapshot(SimpleState(value=1)),
emit_state_snapshot(SimpleState(value=2)),
]
await renderer.render(async_gen(events))
# After processing, internal state should be the last state
assert renderer._state == {"value": 2}
@pytest.mark.asyncio
async def test_renderer_no_result():
"""Test renderer when no RUN_FINISHED event."""
renderer = AGUIConsoleRenderer()
events = [
emit_run_started("t1", "r1"),
emit_step_started("step1"),
]
result = await renderer.render(async_gen(events))
assert result is None
@pytest.mark.asyncio
async def test_renderer_nested_state_diff():
"""Test nested state diff computation."""
renderer = AGUIConsoleRenderer()
old = {"level1": {"level2": {"value": 1, "text": "old"}, "other": "same"}}
new = {"level1": {"level2": {"value": 2, "text": "old"}, "other": "same"}}
diff = renderer._compute_diff(old, new)
# Should only show the changed nested value
assert diff == {"level1": {"level2": {"value": 2}}}
@pytest.mark.asyncio
async def test_renderer_with_empty_state():
"""Test renderer handles empty state gracefully."""
renderer = AGUIConsoleRenderer()
# Create a state with no changes
events = [
emit_step_started("step1"), # No state snapshot
emit_run_finished("t1", "r1", {"result": "ok"}),
]
result = await renderer.render(async_gen(events))
assert result == {"result": "ok"}