MCP Server Neurolorap
by aindreyway
"""Unit tests for developer mode functionality."""
from collections.abc import Generator
from unittest.mock import AsyncMock, MagicMock, patch
import pytest
from mcp_server_neurolorap.server import run_dev_mode
@pytest.fixture
def mock_terminal_fixture() -> Generator[MagicMock, None, None]:
"""Mock terminal fixture."""
with patch("mcp_server_neurolorap.server.JsonRpcTerminal") as mock_class:
mock_instance = MagicMock()
mock_instance.parse_request = MagicMock()
mock_instance.handle_command = AsyncMock()
mock_class.return_value = mock_instance
yield mock_instance
@pytest.mark.asyncio
async def test_dev_mode_commands(mock_terminal_fixture: MagicMock) -> None:
"""Test developer mode command handling."""
# Setup mock terminal responses
mock_terminal_fixture.parse_request.side_effect = [
{
"jsonrpc": "2.0",
"method": "help",
"id": 1,
},
{
"jsonrpc": "2.0",
"method": "exit",
"id": 2,
},
]
mock_terminal_fixture.handle_command.side_effect = [
{
"jsonrpc": "2.0",
"result": "Help message",
"id": 1,
},
{
"jsonrpc": "2.0",
"result": "Goodbye!",
"id": 2,
},
]
# Mock input/print functions
with patch("builtins.input", side_effect=["help", "exit"]), patch(
"builtins.print"
) as mock_print:
await run_dev_mode()
# Verify output
mock_print.assert_any_call("Help message")
mock_print.assert_any_call("Goodbye!")
@pytest.mark.asyncio
async def test_dev_mode_error_handling(
mock_terminal_fixture: MagicMock,
) -> None:
"""Test error handling in developer mode."""
error_cases: list[tuple[type[Exception], str]] = [
(ValueError, "Value error: Invalid command"),
(TypeError, "Type error: Type error"),
(Exception, "Unexpected error: Unexpected error"),
]
for error, expected_msg in error_cases:
# Reset mocks for each test case
mock_terminal_fixture.parse_request.reset_mock()
mock_terminal_fixture.handle_command.reset_mock()
# Set up the error case
mock_terminal_fixture.parse_request.side_effect = [
error("Invalid command"),
{"jsonrpc": "2.0", "method": "exit", "id": 1},
]
mock_terminal_fixture.handle_command.side_effect = [
{"jsonrpc": "2.0", "result": "Goodbye!", "id": 1}
]
with patch("builtins.input", side_effect=["invalid", "exit"]), patch(
"builtins.print"
) as mock_print:
await run_dev_mode()
expected_error = expected_msg.split(":")[0]
mock_print.assert_any_call(f"{expected_error}: Invalid command")
@pytest.mark.asyncio
async def test_dev_mode_empty_input(mock_terminal_fixture: MagicMock) -> None:
"""Test empty input handling in developer mode."""
mock_terminal_fixture.parse_request.side_effect = [
{"jsonrpc": "2.0", "method": "exit", "id": 1},
]
mock_terminal_fixture.handle_command.side_effect = [
{"jsonrpc": "2.0", "result": "Goodbye!", "id": 1},
]
with patch("builtins.input", side_effect=["", "exit"]), patch(
"builtins.print"
) as mock_print:
await run_dev_mode()
# Verify that no error was printed for empty input
assert mock_print.call_count == 5
@pytest.mark.asyncio
async def test_dev_mode_interrupts(mock_terminal_fixture: MagicMock) -> None:
"""Test interrupt handling in developer mode."""
# Test KeyboardInterrupt
with patch("builtins.input", side_effect=KeyboardInterrupt), patch(
"builtins.print"
) as mock_print:
await run_dev_mode()
mock_print.assert_called_with("\nExiting developer mode")
# Test EOFError
with patch("builtins.input", side_effect=EOFError), patch(
"builtins.print"
) as mock_print:
await run_dev_mode()
mock_print.assert_called_with("\nExiting developer mode")