"""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"])