Skip to main content
Glama
test_documentation_manager.py18.8 kB
""" Comprehensive tests for DocumentationManager loadout-aware documentation. Tests that: - Loadout documentation scales properly - MCP client receives appropriate detail level - Documentation manager handles all loadout levels correctly - Backward compatibility is maintained """ import pytest import os from unittest.mock import patch from src.Omnispindle.documentation_manager import ( DocumentationManager, DocumentationLevel, get_documentation_manager, get_tool_doc, get_param_hint, TOOL_DOCUMENTATION, PARAMETER_HINTS ) class TestDocumentationLevel: """Test DocumentationLevel enum.""" def test_documentation_levels_exist(self): """Test that all expected documentation levels exist.""" expected_levels = ["minimal", "basic", "lessons", "admin", "full"] for level in expected_levels: assert hasattr(DocumentationLevel, level.upper()) assert DocumentationLevel(level) == level def test_documentation_level_values(self): """Test documentation level enum values.""" assert DocumentationLevel.MINIMAL == "minimal" assert DocumentationLevel.BASIC == "basic" assert DocumentationLevel.LESSONS == "lessons" assert DocumentationLevel.ADMIN == "admin" assert DocumentationLevel.FULL == "full" class TestDocumentationManager: """Test DocumentationManager class.""" def test_init_with_explicit_loadout(self): """Test initialization with explicit loadout.""" manager = DocumentationManager(loadout="minimal") assert manager.loadout == "minimal" assert manager.level == DocumentationLevel.MINIMAL @patch.dict(os.environ, {"OMNISPINDLE_TOOL_LOADOUT": "admin"}) def test_init_with_env_var(self): """Test initialization with environment variable.""" manager = DocumentationManager() assert manager.loadout == "admin" assert manager.level == DocumentationLevel.ADMIN @patch.dict(os.environ, {}, clear=True) def test_init_with_default(self): """Test initialization with default when no env var.""" # Remove the env var if it exists if "OMNISPINDLE_TOOL_LOADOUT" in os.environ: del os.environ["OMNISPINDLE_TOOL_LOADOUT"] manager = DocumentationManager() assert manager.loadout == "full" assert manager.level == DocumentationLevel.FULL def test_loadout_mapping(self): """Test loadout to documentation level mapping.""" test_cases = [ ("minimal", DocumentationLevel.MINIMAL), ("basic", DocumentationLevel.BASIC), ("lessons", DocumentationLevel.BASIC), # lessons maps to basic ("admin", DocumentationLevel.ADMIN), ("full", DocumentationLevel.FULL), ("hybrid_test", DocumentationLevel.BASIC), ("unknown_loadout", DocumentationLevel.FULL) # fallback to full ] for loadout, expected_level in test_cases: manager = DocumentationManager(loadout=loadout) assert manager.level == expected_level, f"Loadout '{loadout}' should map to '{expected_level}'" def test_case_insensitive_loadout(self): """Test that loadout handling works with different cases.""" # The current implementation doesn't normalize explicit loadout case, # only environment variables. Test the actual behavior. manager = DocumentationManager(loadout="ADMIN") assert manager.loadout == "ADMIN" # Case preserved for explicit loadout # But mapping should still work case-insensitively through the mapping logic # (This test verifies current behavior - could be enhanced to normalize in future) class TestToolDocumentation: """Test tool documentation retrieval.""" def test_get_documentation_for_all_levels(self): """Test getting documentation for all levels.""" tool_name = "add_todo" test_cases = [ ("minimal", "Create task"), ("basic", "Creates a task in the specified project"), ("admin", "Creates a task in the specified project. Supports"), ("full", "Creates a task in the specified project with the given priority") ] for loadout, expected_start in test_cases: manager = DocumentationManager(loadout=loadout) doc = manager.get_tool_documentation(tool_name) assert doc.startswith(expected_start), f"Level '{loadout}' doc should start with '{expected_start}'" def test_documentation_length_scaling(self): """Test that documentation length scales appropriately with loadout.""" tool_name = "add_todo" managers = { "minimal": DocumentationManager(loadout="minimal"), "basic": DocumentationManager(loadout="basic"), "admin": DocumentationManager(loadout="admin"), "full": DocumentationManager(loadout="full") } docs = {level: manager.get_tool_documentation(tool_name) for level, manager in managers.items()} # Verify length progression (minimal <= basic <= admin <= full) assert len(docs["minimal"]) <= len(docs["basic"]) assert len(docs["basic"]) <= len(docs["admin"]) assert len(docs["admin"]) <= len(docs["full"]) # Verify minimal is actually minimal (should be very short) assert len(docs["minimal"]) < 20, "Minimal docs should be very short" # Verify full is comprehensive (should be substantial) assert len(docs["full"]) > 100, "Full docs should be comprehensive" def test_missing_tool_documentation(self): """Test handling of missing tool documentation.""" manager = DocumentationManager(loadout="full") doc = manager.get_tool_documentation("nonexistent_tool") assert doc == "Tool documentation not found." def test_missing_level_fallback(self): """Test fallback to 'full' when specific level is missing.""" # Create a manager and test with a tool that might not have all levels manager = DocumentationManager(loadout="admin") # Mock a tool that only has 'full' documentation with patch.dict(TOOL_DOCUMENTATION, { "test_tool": {"full": "Full documentation only"} }): doc = manager.get_tool_documentation("test_tool") assert doc == "Full documentation only" def test_all_documented_tools_have_required_levels(self): """Test that all tools have minimal and full documentation levels.""" required_levels = ["minimal", "full"] for tool_name, tool_docs in TOOL_DOCUMENTATION.items(): for level in required_levels: assert level in tool_docs, f"Tool '{tool_name}' missing '{level}' documentation" assert len(tool_docs[level].strip()) > 0, f"Tool '{tool_name}' has empty '{level}' documentation" def test_documentation_content_consistency(self): """Test that documentation content is consistent across levels.""" for tool_name, tool_docs in TOOL_DOCUMENTATION.items(): # All levels should describe the same tool functionality # Full docs should contain key terms or related concepts from minimal docs if "minimal" in tool_docs and "full" in tool_docs: minimal_text = tool_docs["minimal"].lower() full_text = tool_docs["full"].lower() # Extract key functional words from minimal docs minimal_words = set(minimal_text.split()) full_words = set(full_text.split()) # Remove very common words to focus on functional terms common_words = {"a", "an", "the", "and", "or", "but", "with", "for", "to", "of", "in", "on", "by", "is"} minimal_functional = minimal_words - common_words # Check for direct overlap or semantic relationship overlap = minimal_functional & full_words # For tools with very short minimal docs, allow for semantic consistency # (e.g., "explain" vs "explanation", "todo" vs "task") if len(overlap) == 0 and len(minimal_functional) <= 3: # Check for word stems or related terms semantic_matches = False for minimal_word in minimal_functional: # Check if any full doc word contains the minimal word or vice versa for full_word in full_words: if (minimal_word in full_word or full_word in minimal_word or len(minimal_word) > 3 and minimal_word[:4] == full_word[:4]): semantic_matches = True break if semantic_matches: break assert semantic_matches, f"Tool '{tool_name}' minimal '{minimal_text}' and full docs should share semantic terms" else: assert len(overlap) > 0, f"Tool '{tool_name}' minimal and full docs should share functional terms" class TestParameterHints: """Test parameter hints functionality.""" def test_parameter_hints_for_minimal_loadout(self): """Test that minimal loadout returns no parameter hints.""" manager = DocumentationManager(loadout="minimal") # Should return None for all tools in minimal mode for tool_name in PARAMETER_HINTS.keys(): hint = manager.get_parameter_hint(tool_name) assert hint is None, f"Minimal loadout should return no hints for '{tool_name}'" def test_parameter_hints_for_non_minimal_loadouts(self): """Test parameter hints for non-minimal loadouts.""" loadouts_to_test = ["basic", "admin", "full"] for loadout in loadouts_to_test: manager = DocumentationManager(loadout=loadout) # Should return hints for tools that have them hint = manager.get_parameter_hint("add_todo") assert hint is not None, f"Loadout '{loadout}' should return hints for 'add_todo'" assert len(hint.strip()) > 0, f"Hint should not be empty for loadout '{loadout}'" def test_parameter_hints_fallback(self): """Test parameter hints fallback to basic level.""" manager = DocumentationManager(loadout="admin") # Mock a tool with only basic hints with patch.dict(PARAMETER_HINTS, { "test_tool": {"basic": "Basic hint only"} }): hint = manager.get_parameter_hint("test_tool") assert hint == "Basic hint only" def test_parameter_hints_content_quality(self): """Test that parameter hints contain useful information.""" manager = DocumentationManager(loadout="full") for tool_name in PARAMETER_HINTS.keys(): hint = manager.get_parameter_hint(tool_name) if hint: # Hints should mention parameters or usage hint_lower = hint.lower() useful_terms = ["parameter", "required", "optional", "field", "example", "format"] has_useful_term = any(term in hint_lower for term in useful_terms) assert has_useful_term, f"Parameter hint for '{tool_name}' should contain useful guidance" class TestGlobalFunctions: """Test global convenience functions.""" @patch.dict(os.environ, {"OMNISPINDLE_TOOL_LOADOUT": "basic"}) def test_get_documentation_manager_singleton(self): """Test that get_documentation_manager returns singleton.""" # Clear any existing global manager import src.Omnispindle.documentation_manager as doc_module doc_module._doc_manager = None manager1 = get_documentation_manager() manager2 = get_documentation_manager() assert manager1 is manager2, "Should return the same instance" assert manager1.loadout == "basic" def test_get_tool_doc_convenience_function(self): """Test get_tool_doc convenience function.""" with patch('src.Omnispindle.documentation_manager.get_documentation_manager') as mock_get_manager: mock_manager = DocumentationManager(loadout="minimal") mock_get_manager.return_value = mock_manager doc = get_tool_doc("add_todo") assert doc == "Create task" mock_get_manager.assert_called_once() def test_get_param_hint_convenience_function(self): """Test get_param_hint convenience function.""" with patch('src.Omnispindle.documentation_manager.get_documentation_manager') as mock_get_manager: mock_manager = DocumentationManager(loadout="basic") mock_get_manager.return_value = mock_manager hint = get_param_hint("add_todo") assert hint is not None mock_get_manager.assert_called_once() class TestLoadoutScaling: """Test that documentation scales appropriately across loadouts.""" def test_token_efficiency_minimal_vs_full(self): """Test that minimal loadout is significantly more token-efficient.""" minimal_manager = DocumentationManager(loadout="minimal") full_manager = DocumentationManager(loadout="full") total_minimal_length = 0 total_full_length = 0 for tool_name in TOOL_DOCUMENTATION.keys(): minimal_doc = minimal_manager.get_tool_documentation(tool_name) full_doc = full_manager.get_tool_documentation(tool_name) total_minimal_length += len(minimal_doc) total_full_length += len(full_doc) # Minimal should use significantly fewer tokens efficiency_ratio = total_minimal_length / total_full_length assert efficiency_ratio < 0.2, f"Minimal docs should use <20% of full docs tokens, got {efficiency_ratio:.2%}" def test_progressive_detail_increase(self): """Test that detail progressively increases across loadouts.""" loadouts = ["minimal", "basic", "admin", "full"] tool_name = "query_todos" # Complex tool with detailed docs doc_lengths = [] for loadout in loadouts: manager = DocumentationManager(loadout=loadout) doc = manager.get_tool_documentation(tool_name) doc_lengths.append(len(doc)) # Each level should have equal or more detail than the previous for i in range(1, len(doc_lengths)): assert doc_lengths[i] >= doc_lengths[i-1], f"Level {loadouts[i]} should have >= detail than {loadouts[i-1]}" def test_mcp_client_detail_levels(self): """Test MCP client appropriate detail levels.""" # Test scenarios representing different MCP client needs test_scenarios = [ { "scenario": "Token-constrained client", "loadout": "minimal", "max_doc_length": 30, "should_have_hints": False }, { "scenario": "Balanced client", "loadout": "basic", "max_doc_length": 200, "should_have_hints": True }, { "scenario": "Administrative client", "loadout": "admin", "max_doc_length": 500, "should_have_hints": True }, { "scenario": "Development client", "loadout": "full", "max_doc_length": float('inf'), "should_have_hints": True } ] for scenario in test_scenarios: manager = DocumentationManager(loadout=scenario["loadout"]) # Test a representative tool tool_name = "add_todo" doc = manager.get_tool_documentation(tool_name) hint = manager.get_parameter_hint(tool_name) # Check documentation length constraint assert len(doc) <= scenario["max_doc_length"], \ f"{scenario['scenario']} docs too long: {len(doc)} > {scenario['max_doc_length']}" # Check hints availability if scenario["should_have_hints"]: assert hint is not None, f"{scenario['scenario']} should provide parameter hints" else: assert hint is None, f"{scenario['scenario']} should not provide parameter hints" class TestBackwardCompatibility: """Test backward compatibility of documentation manager.""" def test_legacy_loadout_values(self): """Test that legacy/unknown loadout values work.""" # Test some potential legacy values legacy_loadouts = ["verbose", "debug", "compact", ""] for loadout in legacy_loadouts: manager = DocumentationManager(loadout=loadout) # Should not crash and should fall back to full assert manager.level == DocumentationLevel.FULL # Should still return valid documentation doc = manager.get_tool_documentation("add_todo") assert len(doc) > 0 def test_case_variations(self): """Test various case combinations for loadout values.""" # Test that different case loadouts still work (even if not normalized) case_variations = [ ("MINIMAL", DocumentationLevel.FULL), # Falls back to FULL because mapping is case-sensitive ("minimal", DocumentationLevel.MINIMAL), # Exact match works ("admin", DocumentationLevel.ADMIN), # Exact match works ("ADMIN", DocumentationLevel.FULL), # Falls back to FULL because mapping is case-sensitive ] for input_loadout, expected_level in case_variations: manager = DocumentationManager(loadout=input_loadout) assert manager.level == expected_level, f"Loadout '{input_loadout}' should result in level '{expected_level}'" def test_whitespace_handling(self): """Test handling of whitespace in loadout values.""" whitespace_loadouts = [" minimal ", "\tbasic\t", "\n admin \n"] expected_levels = [DocumentationLevel.MINIMAL, DocumentationLevel.BASIC, DocumentationLevel.ADMIN] for loadout, expected_level in zip(whitespace_loadouts, expected_levels): manager = DocumentationManager(loadout=loadout) # Should handle whitespace gracefully (though current implementation might not strip) # This test ensures we don't crash on whitespace doc = manager.get_tool_documentation("add_todo") assert len(doc) > 0 if __name__ == "__main__": pytest.main([__file__, "-v"])

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/MadnessEngineering/fastmcp-todo-server'

If you have feedback or need assistance with the MCP directory API, please join our Discord server