MCP Server Neurolorap
by aindreyway
"""Tests for server tools functionality."""
from collections.abc import Generator
from pathlib import Path
from typing import Any
from unittest.mock import AsyncMock, MagicMock, NonCallableMock, patch
import pytest
from mcp_server_neurolorap.server import create_server
class CodeCollectorToolMock(AsyncMock):
"""Custom AsyncMock for code collector tool."""
def __init__(
self,
spec: list[str] | object | type[object] | None = None,
wraps: Any | None = None,
name: str | None = None,
spec_set: list[str] | object | type[object] | None = None,
parent: NonCallableMock | None = None,
_spec_state: Any | None = None,
_new_name: str = "",
_new_parent: NonCallableMock | None = None,
_spec_as_instance: bool = False,
_eat_self: bool | None = None,
unsafe: bool = False,
**kwargs: Any,
) -> None:
super().__init__(
spec=spec,
wraps=wraps,
name=name,
spec_set=spec_set,
parent=parent,
_spec_state=_spec_state,
_new_name=_new_name,
_new_parent=_new_parent,
_spec_as_instance=_spec_as_instance,
_eat_self=_eat_self,
unsafe=unsafe,
**kwargs,
)
self._side_effect: Exception | None = None
def set_side_effect(self, effect: Exception | None) -> None:
"""Set side effect for the mock."""
self._side_effect = effect
async def __call__(self, *args: Any, **kwargs: Any) -> str:
"""Mock tool call."""
try:
if self._side_effect:
raise self._side_effect
return "Code collection complete!\nOutput file: output.md"
except Exception:
return "No files found to process or error occurred"
class ProjectStructureReporterToolMock(AsyncMock):
"""Custom AsyncMock for project structure reporter tool."""
def __init__(
self,
spec: list[str] | object | type[object] | None = None,
wraps: Any | None = None,
name: str | None = None,
spec_set: list[str] | object | type[object] | None = None,
parent: NonCallableMock | None = None,
_spec_state: Any | None = None,
_new_name: str = "",
_new_parent: NonCallableMock | None = None,
_spec_as_instance: bool = False,
_eat_self: bool | None = None,
unsafe: bool = False,
**kwargs: Any,
) -> None:
super().__init__(
spec=spec,
wraps=wraps,
name=name,
spec_set=spec_set,
parent=parent,
_spec_state=_spec_state,
_new_name=_new_name,
_new_parent=_new_parent,
_spec_as_instance=_spec_as_instance,
_eat_self=_eat_self,
unsafe=unsafe,
**kwargs,
)
self._side_effect: Exception | None = None
def set_side_effect(self, effect: Exception | None) -> None:
"""Set side effect for the mock."""
self._side_effect = effect
async def __call__(self, *args: Any, **kwargs: Any) -> str:
"""Mock tool call."""
try:
if self._side_effect:
raise self._side_effect
return "Project structure report generated: test.md"
except Exception:
return "Error generating report"
@pytest.fixture
def mock_fastmcp() -> Generator[MagicMock, None, None]:
"""Mock FastMCP server."""
with patch("mcp_server_neurolorap.server.FastMCP") as mock:
mock_server = MagicMock()
mock_server.name = "neurolorap"
mock_server.tools = {
"project_structure_reporter": ProjectStructureReporterToolMock(),
"code_collector": CodeCollectorToolMock(),
}
mock_server.tool_called = False
mock.return_value = mock_server
yield mock_server
@pytest.mark.asyncio
async def test_project_structure_reporter_success(
tmp_path: Path, mock_fastmcp: MagicMock
) -> None:
"""Test successful project structure report generation."""
# Call tool through mock server
result = await mock_fastmcp.tools["project_structure_reporter"](
output_filename="test.md",
ignore_patterns=["*.pyc"],
)
# Verify results
assert "Project structure report generated" in result
@pytest.mark.asyncio
async def test_code_collector_success(
tmp_path: Path, mock_fastmcp: MagicMock
) -> None:
"""Test successful code collection."""
# Call tool through mock server
result = await mock_fastmcp.tools["code_collector"](
input_path="src/",
title="Test Collection",
subproject_id="test-sub",
)
# Verify results
assert "Code collection complete!" in result
assert "output.md" in result
@pytest.mark.asyncio
async def test_project_structure_reporter_error_handling(
tmp_path: Path, mock_fastmcp: MagicMock
) -> None:
"""Test error handling in project structure reporter."""
create_server()
tool = mock_fastmcp.tools["project_structure_reporter"]
tool.set_side_effect(ValueError("Invalid configuration"))
result = await tool()
assert "Error generating report" in result
@pytest.mark.asyncio
async def test_code_collector_error_handling(
tmp_path: Path, mock_fastmcp: MagicMock
) -> None:
"""Test error handling in code collector."""
create_server()
tool = mock_fastmcp.tools["code_collector"]
tool.set_side_effect(ValueError("Invalid configuration"))
result = await tool()
assert "No files found to process or error occurred" in result