"""Tests for DocumentationToolManager."""
import pytest
from pathlib import Path
from mkdocs_mcp.tools import DocumentationToolManager
from mcp.types import CallToolResult, TextContent
class TestDocumentationToolManager:
"""Test cases for DocumentationToolManager."""
@pytest.fixture
def tool_manager(self, temp_docs_dir: Path) -> DocumentationToolManager:
"""Create a tool manager instance for testing."""
return DocumentationToolManager(temp_docs_dir)
@pytest.fixture
def empty_tool_manager(self, empty_docs_dir: Path) -> DocumentationToolManager:
"""Create a tool manager for an empty directory."""
return DocumentationToolManager(empty_docs_dir)
async def test_list_tools(self, tool_manager: DocumentationToolManager):
"""Test listing available tools."""
tools = await tool_manager.list_tools()
# Check that all expected tools are present
tool_names = [tool.name for tool in tools]
expected_tools = [
"search_docs",
"find_by_title",
"list_pages",
"get_page_outline",
"search_code_blocks"
]
for expected_tool in expected_tools:
assert expected_tool in tool_names
# Check that tools have proper schemas
for tool in tools:
assert tool.name
assert tool.description
assert tool.inputSchema
assert "type" in tool.inputSchema
assert tool.inputSchema["type"] == "object"
async def test_search_docs_basic(self, tool_manager: DocumentationToolManager):
"""Test basic documentation search."""
result = await tool_manager.call_tool("search_docs", {"query": "Python"})
assert isinstance(result, CallToolResult)
assert not result.isError
assert len(result.content) == 1
assert isinstance(result.content[0], TextContent)
text = result.content[0].text
assert "Found" in text
assert "Python" in text
async def test_search_docs_case_sensitive(self, tool_manager: DocumentationToolManager):
"""Test case sensitive search."""
# Case insensitive (default)
result1 = await tool_manager.call_tool("search_docs", {"query": "python"})
# Case sensitive
result2 = await tool_manager.call_tool("search_docs", {
"query": "python",
"case_sensitive": True
})
# Should get different results
text1 = result1.content[0].text
text2 = result2.content[0].text
# Case insensitive should find "Python" (capital P)
assert "Found" in text1
# Case sensitive should find fewer or no results
assert result2.content[0].text
async def test_search_docs_no_results(self, tool_manager: DocumentationToolManager):
"""Test search with no results."""
result = await tool_manager.call_tool("search_docs", {"query": "nonexistentterm12345"})
assert not result.isError
text = result.content[0].text
assert "No results found" in text
async def test_search_docs_max_results(self, tool_manager: DocumentationToolManager):
"""Test limiting search results."""
result = await tool_manager.call_tool("search_docs", {
"query": "the",
"max_results": 2
})
assert not result.isError
# Should respect the limit (though exact count may vary)
text = result.content[0].text
assert "Found" in text
async def test_find_by_title_exact(self, tool_manager: DocumentationToolManager):
"""Test finding pages by exact title match."""
result = await tool_manager.call_tool("find_by_title", {
"title": "Installation Guide",
"exact_match": True
})
assert not result.isError
text = result.content[0].text
assert "Installation Guide" in text
assert "installation.md" in text
async def test_find_by_title_fuzzy(self, tool_manager: DocumentationToolManager):
"""Test finding pages by fuzzy title match."""
result = await tool_manager.call_tool("find_by_title", {
"title": "Install",
"exact_match": False
})
assert not result.isError
text = result.content[0].text
# Should find "Installation" or "Installation Guide"
assert "Found" in text or "Install" in text
async def test_find_by_title_not_found(self, tool_manager: DocumentationToolManager):
"""Test finding pages with non-existent title."""
result = await tool_manager.call_tool("find_by_title", {
"title": "Nonexistent Page Title"
})
assert not result.isError
text = result.content[0].text
assert "No pages found" in text
async def test_list_pages_basic(self, tool_manager: DocumentationToolManager):
"""Test listing all pages."""
result = await tool_manager.call_tool("list_pages", {})
assert not result.isError
text = result.content[0].text
assert "Found 4 documentation pages" in text
assert "index.md" in text
assert "installation.md" in text
async def test_list_pages_with_content(self, tool_manager: DocumentationToolManager):
"""Test listing pages with content preview."""
result = await tool_manager.call_tool("list_pages", {"include_content": True})
assert not result.isError
text = result.content[0].text
assert "Preview:" in text
async def test_list_pages_with_pattern(self, tool_manager: DocumentationToolManager):
"""Test listing pages with filename pattern."""
result = await tool_manager.call_tool("list_pages", {"pattern": "install"})
assert not result.isError
text = result.content[0].text
# Should only find installation.md
assert "installation.md" in text
# Should not find index.md
assert "index.md" not in text
async def test_get_page_outline(self, tool_manager: DocumentationToolManager):
"""Test getting page outline."""
result = await tool_manager.call_tool("get_page_outline", {
"page_path": "getting-started/installation.md"
})
assert not result.isError
text = result.content[0].text
assert "Outline for" in text
assert "Installation" in text
assert "Step 1" in text
assert "Step 2" in text
async def test_get_page_outline_not_found(self, tool_manager: DocumentationToolManager):
"""Test getting outline for non-existent page."""
result = await tool_manager.call_tool("get_page_outline", {
"page_path": "nonexistent.md"
})
assert result.isError
text = result.content[0].text
assert "Page not found" in text
async def test_search_code_blocks_all(self, tool_manager: DocumentationToolManager):
"""Test searching all code blocks."""
result = await tool_manager.call_tool("search_code_blocks", {})
assert not result.isError
text = result.content[0].text
assert "Found" in text
assert "```" in text # Should contain code blocks
async def test_search_code_blocks_by_language(self, tool_manager: DocumentationToolManager):
"""Test searching code blocks by language."""
result = await tool_manager.call_tool("search_code_blocks", {
"language": "python"
})
assert not result.isError
text = result.content[0].text
if "Found" in text:
assert "python" in text.lower()
async def test_search_code_blocks_by_query(self, tool_manager: DocumentationToolManager):
"""Test searching code blocks with query."""
result = await tool_manager.call_tool("search_code_blocks", {
"query": "print"
})
assert not result.isError
text = result.content[0].text
if "Found" in text:
assert "print" in text
async def test_search_code_blocks_combined(self, tool_manager: DocumentationToolManager):
"""Test searching code blocks with both language and query."""
result = await tool_manager.call_tool("search_code_blocks", {
"language": "python",
"query": "print"
})
assert not result.isError
# Should work without error (may or may not find results)
text = result.content[0].text
assert text # Should have some response
async def test_call_unknown_tool(self, tool_manager: DocumentationToolManager):
"""Test calling an unknown tool."""
with pytest.raises(ValueError, match="Unknown tool"):
await tool_manager.call_tool("nonexistent_tool", {})
async def test_empty_directory(self, empty_tool_manager: DocumentationToolManager):
"""Test tools with empty documentation directory."""
result = await empty_tool_manager.call_tool("search_docs", {"query": "test"})
assert result.isError
text = result.content[0].text
assert "Documentation directory not found" in text
async def test_list_pages_empty_directory(self, empty_tool_manager: DocumentationToolManager):
"""Test listing pages in empty directory."""
result = await empty_tool_manager.call_tool("list_pages", {})
assert result.isError
text = result.content[0].text
assert "Documentation directory not found" in text