- Use app.run_stdio_async() instead of deprecated stdio_server import - Aligns with FastMCP 2.11.3 API - Server now starts correctly with uv run mcp-office-tools - Maintains all MCPMixin functionality and tool registration
147 lines
5.5 KiB
Python
147 lines
5.5 KiB
Python
"""Test suite for MCP Office Tools server with mixin architecture."""
|
|
|
|
import pytest
|
|
import tempfile
|
|
import os
|
|
from pathlib import Path
|
|
from unittest.mock import patch, MagicMock
|
|
|
|
# FastMCP testing - using direct tool access
|
|
|
|
from mcp_office_tools.server import app
|
|
from mcp_office_tools.utils import OfficeFileError
|
|
|
|
|
|
class TestServerInitialization:
|
|
"""Test server initialization and basic functionality."""
|
|
|
|
def test_app_creation(self):
|
|
"""Test that FastMCP app is created correctly."""
|
|
assert app is not None
|
|
assert hasattr(app, 'get_tools')
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_all_mixins_tools_registered(self):
|
|
"""Test that all mixin tools are registered correctly."""
|
|
# Get all registered tool names
|
|
tool_names = await app.get_tools()
|
|
tool_names_set = set(tool_names)
|
|
|
|
# Expected tools from all mixins
|
|
expected_universal_tools = {
|
|
"extract_text",
|
|
"extract_images",
|
|
"extract_metadata",
|
|
"detect_office_format",
|
|
"analyze_document_health",
|
|
"get_supported_formats"
|
|
}
|
|
expected_word_tools = {"convert_to_markdown"}
|
|
|
|
# Verify universal tools are registered
|
|
assert expected_universal_tools.issubset(tool_names_set), f"Missing universal tools: {expected_universal_tools - tool_names_set}"
|
|
|
|
# Verify word tools are registered
|
|
assert expected_word_tools.issubset(tool_names_set), f"Missing word tools: {expected_word_tools - tool_names_set}"
|
|
|
|
# Verify minimum number of tools
|
|
assert len(tool_names) >= 7 # 6 universal + 1 word (+ future Excel/PowerPoint tools)
|
|
|
|
def test_mixin_composition_works(self):
|
|
"""Test that mixin composition created the expected server structure."""
|
|
# Import the server module to ensure all mixins are initialized
|
|
import mcp_office_tools.server as server_module
|
|
|
|
# Verify the mixins were created
|
|
assert hasattr(server_module, 'universal_mixin')
|
|
assert hasattr(server_module, 'word_mixin')
|
|
assert hasattr(server_module, 'excel_mixin')
|
|
assert hasattr(server_module, 'powerpoint_mixin')
|
|
|
|
# Verify each mixin has the correct app reference
|
|
assert server_module.universal_mixin.app == app
|
|
assert server_module.word_mixin.app == app
|
|
assert server_module.excel_mixin.app == app
|
|
assert server_module.powerpoint_mixin.app == app
|
|
|
|
|
|
class TestToolAccess:
|
|
"""Test tool accessibility and metadata."""
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_get_tool_metadata(self):
|
|
"""Test getting tool metadata through FastMCP API."""
|
|
# Test that we can get tool metadata
|
|
tool = await app.get_tool("get_supported_formats")
|
|
|
|
assert tool is not None
|
|
assert tool.name == "get_supported_formats"
|
|
assert "Get list of all supported Office document formats" in tool.description
|
|
assert hasattr(tool, 'fn') # Has the actual function
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_all_expected_tools_accessible(self):
|
|
"""Test that all expected tools are accessible via get_tool."""
|
|
expected_tools = [
|
|
"extract_text",
|
|
"extract_images",
|
|
"extract_metadata",
|
|
"detect_office_format",
|
|
"analyze_document_health",
|
|
"get_supported_formats",
|
|
"convert_to_markdown"
|
|
]
|
|
|
|
for tool_name in expected_tools:
|
|
tool = await app.get_tool(tool_name)
|
|
assert tool is not None, f"Tool {tool_name} should be accessible"
|
|
assert tool.name == tool_name
|
|
assert hasattr(tool, 'fn'), f"Tool {tool_name} should have a function"
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_tool_function_binding(self):
|
|
"""Test that tools are properly bound to mixin instances."""
|
|
# Get a universal tool
|
|
universal_tool = await app.get_tool("get_supported_formats")
|
|
assert 'UniversalMixin' in str(type(universal_tool.fn.__self__))
|
|
|
|
# Get a word tool
|
|
word_tool = await app.get_tool("convert_to_markdown")
|
|
assert 'WordMixin' in str(type(word_tool.fn.__self__))
|
|
|
|
|
|
class TestMixinIntegration:
|
|
"""Test integration between different mixins."""
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_universal_and_word_tools_coexist(self):
|
|
"""Test that universal and word tools can coexist properly."""
|
|
# Verify both universal and word tools are available
|
|
# This test confirms the mixin composition works correctly
|
|
|
|
# Get tools from both mixins
|
|
universal_tool = await app.get_tool("get_supported_formats")
|
|
word_tool = await app.get_tool("convert_to_markdown")
|
|
|
|
# Verify they're bound to different mixin instances
|
|
assert universal_tool.fn.__self__ != word_tool.fn.__self__
|
|
assert 'UniversalMixin' in str(type(universal_tool.fn.__self__))
|
|
assert 'WordMixin' in str(type(word_tool.fn.__self__))
|
|
|
|
# Verify both mixins have the same app reference
|
|
assert universal_tool.fn.__self__.app == word_tool.fn.__self__.app == app
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_no_tool_name_conflicts(self):
|
|
"""Test that there are no tool name conflicts between mixins."""
|
|
tool_names = await app.get_tools()
|
|
|
|
# Verify no duplicates
|
|
assert len(tool_names) == len(set(tool_names)), "Tool names should be unique"
|
|
|
|
# Verify expected count
|
|
assert len(tool_names) == 7, f"Expected 7 tools, got {len(tool_names)}: {tool_names}"
|
|
|
|
|
|
if __name__ == "__main__":
|
|
pytest.main([__file__, "-v"]) |