mcp-office-tools/tests/test_server.py
Ryan Malloy 4b38f6455c Add document navigation tools and MCP prompts
New tools for Word document analysis:
- extract_entities: Pattern-based extraction of people, places, organizations
- get_chapter_summaries: Chapter previews with opening sentences and word counts
- save_reading_progress: Bookmark reading position to JSON file
- get_reading_progress: Resume reading from saved position

New MCP prompts (basic to advanced workflows):
- explore-document: Get started with a new document
- find-character: Track character mentions
- chapter-preview: Quick chapter overviews
- resume-reading: Continue where you left off
- document-analysis: Comprehensive multi-tool analysis
- character-journey: Track character arc through narrative
- document-comparison: Compare entities between chapters
- full-reading-session: Guided reading with bookmarking
- manuscript-review: Complete editorial workflow

Updated test counts for 19 total tools (6 universal + 10 word + 3 excel)
2026-01-11 07:23:15 -07:00

157 lines
6.2 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", "extract_word_tables", "analyze_word_structure"}
expected_excel_tools = {"analyze_excel_data", "extract_excel_formulas", "create_excel_chart_data"}
# 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 excel tools are registered
assert expected_excel_tools.issubset(tool_names_set), f"Missing excel tools: {expected_excel_tools - tool_names_set}"
# Verify minimum number of tools
assert len(tool_names) >= 12 # 6 universal + 3 word + 3 excel (+ future 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 mixin instances are correct types
from mcp_office_tools.mixins import UniversalMixin, WordMixin, ExcelMixin, PowerPointMixin
assert isinstance(server_module.universal_mixin, UniversalMixin)
assert isinstance(server_module.word_mixin, WordMixin)
assert isinstance(server_module.excel_mixin, ExcelMixin)
assert isinstance(server_module.powerpoint_mixin, PowerPointMixin)
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 = [
# Universal tools
"extract_text",
"extract_images",
"extract_metadata",
"detect_office_format",
"analyze_document_health",
"get_supported_formats",
# Word tools
"convert_to_markdown",
"extract_word_tables",
"analyze_word_structure",
# Excel tools
"analyze_excel_data",
"extract_excel_formulas",
"create_excel_chart_data"
]
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__))
@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: 6 universal + 10 word + 3 excel = 19
assert len(tool_names) == 19, f"Expected 19 tools, got {len(tool_names)}: {list(tool_names.keys())}"
if __name__ == "__main__":
pytest.main([__file__, "-v"])