"""Unit tests for the internal navi_ui MCP server."""
import pytest
from unittest.mock import AsyncMock
from navi.mcp import ui_server
@pytest.fixture(autouse=True)
def reset_ui_server_state():
"""Reset global orchestrator holder before/after every test."""
ui_server.clear_orchestrator()
yield
ui_server.clear_orchestrator()
class TestRenderComponent:
async def test_requires_component_name(self):
result = await ui_server.render_component("", {"x": 1}, "s1")
assert "component_name" in result.lower()
assert "Error" in result
async def test_requires_dict_payload(self):
result = await ui_server.render_component("table", [1, 2, 3], "s1")
assert "payload must be a JSON object" in result
async def test_requires_session_id(self):
result = await ui_server.render_component("table", {"x": 1}, None)
assert "session_id" in result.lower()
assert "Error" in result
async def test_sends_event_when_orchestrator_ready(self):
mock_orchestrator = AsyncMock()
mock_orchestrator._notify_session = AsyncMock()
ui_server.set_orchestrator(mock_orchestrator)
result = await ui_server.render_component("table", {"rows": 3}, "sess-123")
assert "table" in result
assert "sess-123" in result
assert "Error" not in result
mock_orchestrator._notify_session.assert_awaited_once_with(
"sess-123",
{
"type": "ui_component",
"component": "table",
"payload": {"rows": 3},
},
)
async def test_times_out_when_orchestrator_never_set(self):
original_timeout = ui_server._ORCHESTRATOR_TIMEOUT
ui_server._ORCHESTRATOR_TIMEOUT = 0.05
try:
result = await ui_server.render_component("table", {"x": 1}, "s1")
assert "not ready" in result.lower() or "unavailable" in result.lower()
assert "Error" in result
finally:
ui_server._ORCHESTRATOR_TIMEOUT = original_timeout
async def test_returns_error_when_notify_session_fails(self):
mock_orchestrator = AsyncMock()
mock_orchestrator._notify_session.side_effect = RuntimeError("boom")
ui_server.set_orchestrator(mock_orchestrator)
result = await ui_server.render_component("table", {"x": 1}, "s1")
assert "failed to send" in result.lower()
assert "Error" in result
class TestOrchestratorHolder:
def test_set_and_clear(self):
mock_orchestrator = AsyncMock()
ui_server.set_orchestrator(mock_orchestrator)
assert ui_server._orchestrator is mock_orchestrator
assert ui_server._orchestrator_ready.is_set()
ui_server.clear_orchestrator()
assert ui_server._orchestrator is None
assert not ui_server._orchestrator_ready.is_set()