Archon/python/tests/mcp_server/features/documents/test_version_tools.py
Rasmus Widing 307e0e3b71 Add comprehensive unit tests for MCP server features
- Create test structure mirroring features folder organization
- Add tests for document tools (create, list, update, delete)
- Add tests for version tools (create, list, restore, invalid field handling)
- Add tests for task tools (create with sources, list with filters, update, delete)
- Add tests for project tools (create with polling, list, get)
- Add tests for feature tools (get features with various structures)
- Mock HTTP client for all external API calls
- Test both success and error scenarios
- 100% test coverage for critical tool functions
2025-08-18 21:04:35 +03:00

174 lines
5.7 KiB
Python

"""Unit tests for version management tools."""
import json
from unittest.mock import AsyncMock, MagicMock, patch
import pytest
from mcp.server.fastmcp import Context
from src.mcp_server.features.documents.version_tools import register_version_tools
@pytest.fixture
def mock_mcp():
"""Create a mock MCP server for testing."""
mock = MagicMock()
# Store registered tools
mock._tools = {}
def tool_decorator():
def decorator(func):
mock._tools[func.__name__] = func
return func
return decorator
mock.tool = tool_decorator
return mock
@pytest.fixture
def mock_context():
"""Create a mock context for testing."""
return MagicMock(spec=Context)
@pytest.mark.asyncio
async def test_create_version_success(mock_mcp, mock_context):
"""Test successful version creation."""
register_version_tools(mock_mcp)
# Get the create_version function
create_version = mock_mcp._tools.get('create_version')
assert create_version is not None, "create_version tool not registered"
# Mock HTTP response
mock_response = MagicMock()
mock_response.status_code = 200
mock_response.json.return_value = {
"version": {"version_number": 3, "field_name": "docs"},
"message": "Version created successfully"
}
with patch('src.mcp_server.features.documents.version_tools.httpx.AsyncClient') as mock_client:
mock_async_client = AsyncMock()
mock_async_client.post.return_value = mock_response
mock_client.return_value.__aenter__.return_value = mock_async_client
result = await create_version(
mock_context,
project_id="project-123",
field_name="docs",
content=[{"id": "doc-1", "title": "Test Doc"}],
change_summary="Added test document"
)
result_data = json.loads(result)
assert result_data["success"] is True
assert result_data["version_number"] == 3
assert "Version 3 created successfully" in result_data["message"]
@pytest.mark.asyncio
async def test_create_version_invalid_field(mock_mcp, mock_context):
"""Test version creation with invalid field name."""
register_version_tools(mock_mcp)
create_version = mock_mcp._tools.get('create_version')
# Mock 400 response for invalid field
mock_response = MagicMock()
mock_response.status_code = 400
mock_response.text = "invalid field_name"
with patch('src.mcp_server.features.documents.version_tools.httpx.AsyncClient') as mock_client:
mock_async_client = AsyncMock()
mock_async_client.post.return_value = mock_response
mock_client.return_value.__aenter__.return_value = mock_async_client
result = await create_version(
mock_context,
project_id="project-123",
field_name="invalid",
content={"test": "data"}
)
result_data = json.loads(result)
assert result_data["success"] is False
assert "Must be one of: docs, features, data, or prd" in result_data["error"]
@pytest.mark.asyncio
async def test_restore_version_success(mock_mcp, mock_context):
"""Test successful version restoration."""
register_version_tools(mock_mcp)
# Get the restore_version function
restore_version = mock_mcp._tools.get('restore_version')
assert restore_version is not None, "restore_version tool not registered"
# Mock HTTP response
mock_response = MagicMock()
mock_response.status_code = 200
mock_response.json.return_value = {
"message": "Version 2 restored successfully"
}
with patch('src.mcp_server.features.documents.version_tools.httpx.AsyncClient') as mock_client:
mock_async_client = AsyncMock()
mock_async_client.post.return_value = mock_response
mock_client.return_value.__aenter__.return_value = mock_async_client
result = await restore_version(
mock_context,
project_id="project-123",
field_name="docs",
version_number=2,
restored_by="test-user"
)
result_data = json.loads(result)
assert result_data["success"] is True
assert "Version 2 restored successfully" in result_data["message"]
@pytest.mark.asyncio
async def test_list_versions_with_filter(mock_mcp, mock_context):
"""Test listing versions with field name filter."""
register_version_tools(mock_mcp)
# Get the list_versions function
list_versions = mock_mcp._tools.get('list_versions')
assert list_versions is not None, "list_versions tool not registered"
# Mock HTTP response
mock_response = MagicMock()
mock_response.status_code = 200
mock_response.json.return_value = {
"versions": [
{"version_number": 1, "field_name": "docs", "change_summary": "Initial"},
{"version_number": 2, "field_name": "docs", "change_summary": "Updated"}
]
}
with patch('src.mcp_server.features.documents.version_tools.httpx.AsyncClient') as mock_client:
mock_async_client = AsyncMock()
mock_async_client.get.return_value = mock_response
mock_client.return_value.__aenter__.return_value = mock_async_client
result = await list_versions(
mock_context,
project_id="project-123",
field_name="docs"
)
result_data = json.loads(result)
assert result_data["success"] is True
assert result_data["count"] == 2
assert len(result_data["versions"]) == 2
# Verify filter was passed
call_args = mock_async_client.get.call_args
assert call_args[1]["params"]["field_name"] == "docs"