Ryan Malloy 31948d6ffc
Some checks are pending
Test Dashboard / test-and-dashboard (push) Waiting to run
Rename package to mcwaddams
Named for Milton Waddams, who was relocated to the basement with
boxes of legacy documents. He handles the .doc and .xls files from
1997 that nobody else wants to touch.

- Rename package from mcp-office-tools to mcwaddams
- Update author to Ryan Malloy
- Update all imports and references
- Add Office Space themed README narrative
- All 53 tests passing
2026-01-11 11:35:35 -07:00
..
2026-01-11 11:35:35 -07:00
2025-09-26 15:49:00 -06:00
2026-01-11 11:35:35 -07:00
2026-01-11 11:35:35 -07:00
2026-01-11 11:35:35 -07:00

MCP Office Tools Testing Strategy

This document outlines the comprehensive testing strategy for the mixin-based FastMCP Office Tools server.

Testing Architecture Overview

The testing suite is designed around the mixin architecture pattern and follows FastMCP best practices:

Test Organization

tests/
├── conftest.py              # Shared fixtures and configuration
├── test_server.py           # Integration tests for the composed server
├── test_mixins.py           # Mixin architecture and composition tests
├── test_universal_mixin.py  # Unit tests for UniversalMixin
├── test_word_mixin.py       # Unit tests for WordMixin
├── test_excel_mixin.py      # Unit tests for ExcelMixin (future)
├── test_powerpoint_mixin.py # Unit tests for PowerPointMixin (future)
└── README.md               # This file

Testing Patterns

1. Mixin Unit Testing

Each mixin is tested independently with comprehensive mocking:

@pytest.mark.asyncio
@patch('mcp_office_tools.utils.validation.resolve_office_file_path')
@patch('mcp_office_tools.utils.validation.validate_office_file')
@patch('mcp_office_tools.utils.file_detection.detect_format')
async def test_extract_text_success(mock_detect, mock_validate, mock_resolve, mixin):
    # Setup mocks
    mock_resolve.return_value = "/test.csv"
    mock_validate.return_value = {"is_valid": True, "errors": []}
    mock_detect.return_value = {"category": "data", "extension": ".csv"}

    # Mock internal methods
    with patch.object(mixin, '_extract_text_by_category') as mock_extract:
        mock_extract.return_value = {"text": "test", "method_used": "pandas"}

        result = await mixin.extract_text("/test.csv")
        assert "text" in result

2. Tool Registration Testing

Verify that mixins register tools correctly:

def test_tool_registration_count(self):
    """Test that all expected tools are registered."""
    app = FastMCP("Test Office Tools")

    universal = UniversalMixin(app)
    assert len(app._tools) == 6  # 6 universal tools

    word = WordMixin(app)
    assert len(app._tools) == 7  # 6 universal + 1 word tool

3. FastMCP Session Testing

Test tools through FastMCP's testing framework:

@pytest.mark.asyncio
async def test_tool_execution_via_session(self):
    """Test tool execution through FastMCP test session."""
    session = create_test_session(app)

    result = await session.call_tool("get_supported_formats", {})
    assert "supported_extensions" in result

4. Error Handling Testing

Comprehensive error handling with proper exception types:

@pytest.mark.asyncio
async def test_extract_text_nonexistent_file(self, mixin):
    """Test extract_text with nonexistent file raises OfficeFileError."""
    with pytest.raises(OfficeFileError):
        await mixin.extract_text("/nonexistent/file.docx")

Mocking Strategies

File Operations

Use the MockValidationContext for consistent file operation mocking:

def test_with_mock_validation(mock_validation_context):
    with mock_validation_context(
        resolve_path="/test.docx",
        validation_result={"is_valid": True, "errors": []},
        format_detection={"category": "word", "extension": ".docx"}
    ):
        # Test with mocked file operations
        pass

Office Document Processing

Mock internal processing methods to test tool logic without file dependencies:

with patch.object(mixin, '_extract_text_by_category') as mock_extract:
    mock_extract.return_value = {
        "text": "extracted text",
        "method_used": "python-docx",
        "methods_tried": ["python-docx"]
    }

    result = await mixin.extract_text(file_path)

Test Categories

Unit Tests (@pytest.mark.unit)

  • Individual mixin functionality
  • Helper method testing
  • Parameter validation
  • Error handling

Integration Tests (@pytest.mark.integration)

  • Full server composition
  • Cross-mixin interactions
  • Tool execution via sessions
  • End-to-end workflows

Tool Functionality Tests (@pytest.mark.tool_functionality)

  • Specific tool behavior
  • Parameter handling
  • Output validation
  • Method selection logic

Running Tests

All Tests

uv run pytest

Specific Test Categories

# Unit tests only
uv run pytest -m unit

# Integration tests only
uv run pytest -m integration

# Tool functionality tests
uv run pytest -m tool_functionality

# Specific mixin tests
uv run pytest tests/test_universal_mixin.py

# With coverage
uv run pytest --cov=mcp_office_tools

Fast Development Cycle

# Skip integration tests for faster feedback
uv run pytest -m "not integration"

Test Fixtures

Shared Fixtures (conftest.py)

  • fast_mcp_app: Clean FastMCP app instance
  • universal_mixin: UniversalMixin instance
  • word_mixin: WordMixin instance
  • composed_app: Fully composed app with all mixins
  • test_session: FastMCP test session
  • temp_dir: Temporary directory for test files
  • mock_csv_file: Temporary CSV file with test data
  • mock_docx_file: Mock DOCX file structure

Mock Data Fixtures

  • mock_file_validation: Standard validation response
  • mock_format_detection: Standard format detection response
  • mock_text_extraction_result: Standard text extraction result
  • mock_document_metadata: Standard document metadata

Best Practices

1. Fast Test Execution

  • Mock all file I/O operations
  • Use temporary files only when necessary
  • Keep tests under 1 second unless marked as integration

2. Comprehensive Mocking

  • Mock external dependencies at the boundary
  • Test internal logic without external dependencies
  • Use realistic mock data that reflects actual tool behavior

3. Clear Test Intent

  • One behavior per test
  • Descriptive test names
  • Clear arrange/act/assert structure

4. Error Testing

  • Test all error conditions
  • Verify specific exception types
  • Test error messages for helpfulness

5. Tool Functionality Focus

  • Test tool behavior, not just registration
  • Verify output structure and content
  • Test parameter combinations and edge cases

Advanced Testing Patterns

Testing Async Tool Methods Directly

@pytest.mark.asyncio
async def test_tool_method_directly(universal_mixin):
    """Test tool method directly without session overhead."""
    # Direct method testing for unit-level validation
    with patch('mcp_office_tools.utils.validation.validate_office_file'):
        result = await universal_mixin.extract_text("/test.csv")
        assert result is not None

Testing Tool Parameter Validation

@pytest.mark.asyncio
async def test_parameter_validation(mixin):
    """Test tool parameter validation and handling."""
    # Test various parameter combinations
    result = await mixin.extract_text(
        file_path="/test.csv",
        preserve_formatting=True,
        include_metadata=False,
        method="primary"
    )

    # Verify parameters were used correctly
    assert result["metadata"]["extraction_method"] != "auto"

Testing Mixin Composition

def test_mixin_composition(self):
    """Test that mixin composition works correctly."""
    app = FastMCP("Test")

    # Initialize mixins in order
    universal = UniversalMixin(app)
    word = WordMixin(app)

    # Verify no tool conflicts
    tool_names = set(app._tools.keys())
    assert len(tool_names) == 7  # 6 + 1, no duplicates

Future Enhancements

  • Property-based testing for document processing
  • Performance benchmarking tests
  • Memory usage validation
  • Stress testing with large documents
  • Network operation testing for URL processing
  • Security testing for malicious document handling

This testing strategy ensures comprehensive coverage of the mixin-based architecture while maintaining fast test execution and clear test organization.