Newer
Older
navi-1 / tests / unit / core / test_events.py
"""Unit tests for event dataclass wire serialization."""

import pytest

from navi.core.events import (
    AIHelperTokensUsed,
    CompressionStarted,
    ContextCompressed,
    PlanReady,
    PlanningDebugData,
    PlanningStatus,
    ProfileSwitched,
    StreamEnd,
    StreamStopped,
    SubagentComplete,
    TextDelta,
    ThinkingDelta,
    ThinkingEnd,
    ToolEvent,
    ToolStarted,
    TurnThinking,
)


class TestToolStarted:
    def test_to_wire(self):
        ev = ToolStarted(tool_name="fs", arguments={"action": "read"})
        assert ev.to_wire() == {
            "type": "tool_started",
            "tool": "fs",
            "args": {"action": "read"},
            "is_subagent": False,
        }

    def test_to_wire_subagent(self):
        ev = ToolStarted(tool_name="spawn_agent", arguments={}, is_subagent=True)
        assert ev.to_wire()["is_subagent"] is True


class TestToolEvent:
    def test_to_wire(self):
        ev = ToolEvent(
            tool_name="fs",
            arguments={"path": "/tmp"},
            result="file contents",
            success=True,
            metadata={"size": 42},
        )
        assert ev.to_wire() == {
            "type": "tool_call",
            "tool": "fs",
            "args": {"path": "/tmp"},
            "result": "file contents",
            "success": True,
            "is_subagent": False,
            "metadata": {"size": 42},
        }


class TestTextDelta:
    def test_to_wire(self):
        assert TextDelta(delta="hello").to_wire() == {"type": "stream_delta", "delta": "hello"}


class TestThinkingDelta:
    def test_to_wire(self):
        assert ThinkingDelta(delta="hmm").to_wire() == {"type": "thinking_delta", "delta": "hmm"}


class TestThinkingEnd:
    def test_to_wire(self):
        assert ThinkingEnd().to_wire() == {"type": "thinking_end"}


class TestStreamEnd:
    def test_to_wire(self):
        ev = StreamEnd(
            full_content="done",
            context_tokens=150,
            max_context_tokens=8192,
            elapsed_seconds=1.5,
            tool_call_count=2,
            token_count=150,
        )
        wire = ev.to_wire()
        assert wire["type"] == "stream_end"
        assert wire["content"] == "done"
        assert wire["context_tokens"] == 150
        assert wire["tool_call_count"] == 2


class TestStreamStopped:
    def test_to_wire(self):
        assert StreamStopped().to_wire() == {"type": "stream_stopped"}


class TestContextCompressed:
    def test_to_wire(self):
        ev = ContextCompressed(messages_before=10, messages_after=3, summary="summary text")
        wire = ev.to_wire()
        assert wire["type"] == "context_compressed"
        assert wire["messages_before"] == 10
        assert wire["messages_after"] == 3
        assert wire["summary"] == "summary text"
        assert wire["context_tokens"] is None


class TestProfileSwitched:
    def test_to_wire(self):
        ev = ProfileSwitched(profile_id="dev", profile_name="Developer")
        assert ev.to_wire() == {
            "type": "profile_switched",
            "profile_id": "dev",
            "profile_name": "Developer",
        }


class TestPlanningStatus:
    def test_to_wire(self):
        ev = PlanningStatus(phase=1, label="Working on it...")
        assert ev.to_wire() == {
            "type": "planning_status",
            "phase": 1,
            "label": "Working on it...",
            "is_subagent": False,
        }


class TestPlanReady:
    def test_to_wire(self):
        ev = PlanReady(plan="1. Do thing")
        wire = ev.to_wire()
        assert wire["type"] == "plan_ready"
        assert wire["plan"] == "1. Do thing"
        assert wire["is_subagent"] is False


class TestTurnThinking:
    def test_to_wire(self):
        ev = TurnThinking(thinking="reasoning...", is_subagent=True)
        wire = ev.to_wire()
        assert wire["type"] == "turn_thinking"
        assert wire["thinking"] == "reasoning..."
        assert wire["is_subagent"] is True


class TestCompressionStarted:
    def test_to_wire(self):
        ev = CompressionStarted(context_tokens=5000, max_context_tokens=8192)
        wire = ev.to_wire()
        assert wire["type"] == "compression_started"
        assert wire["context_tokens"] == 5000
        assert wire["max_context_tokens"] == 8192


class TestInternalEvents:
    """Internal events must NOT serialize to the wire."""

    def test_subagent_complete(self):
        assert SubagentComplete(token_count=42).to_wire() is None

    def test_planning_debug_data(self):
        assert PlanningDebugData(log={"phases": {}}).to_wire() is None

    def test_ai_helper_tokens_used(self):
        ev = AIHelperTokensUsed(prompt_tokens=10, completion_tokens=20)
        assert ev.to_wire() is None
        assert ev.total == 30