Files

222 lines
6.5 KiB
Python

"""Test search tool functionality."""
import asyncio
import os
import tempfile
from pathlib import Path
import pytest
from hanzo_mcp.tools.search import create_find_tool, create_search_tool
@pytest.mark.asyncio
async def test_search_basic():
"""Test basic search functionality."""
# Create search tool
search_tool = create_search_tool()
# Test with current directory
result = await search_tool.run(
pattern="SearchTool", path=".", max_results_per_type=5
)
assert result.data is not None
assert "results" in result.data
assert "statistics" in result.data
# Should find the class definition
results = result.data["results"]
assert len(results) > 0
# Check statistics
stats = result.data["statistics"]
assert "query" in stats
assert stats["query"] == "SearchTool"
assert "search_types_used" in stats
assert "text" in stats["search_types_used"] # Should use text search
@pytest.mark.asyncio
async def test_search_auto_detection():
"""Test automatic search type detection."""
search_tool = create_search_tool()
# Test natural language query (should trigger vector search if available)
result = await search_tool.run(
pattern="how does search work in this codebase", max_results_per_type=5
)
assert result.data is not None
stats = result.data["statistics"]
# Should detect this as a natural language query
if "vector" in stats["search_types_used"]:
# Vector search was used
assert True
else:
# At minimum, text search should be used
assert "text" in stats["search_types_used"]
@pytest.mark.asyncio
async def test_search_code_patterns():
"""Test code pattern search."""
search_tool = create_search_tool()
# Test AST pattern detection
result = await search_tool.run(pattern="class SearchResult", max_results_per_type=5)
assert result.data is not None
stats = result.data["statistics"]
# Should use AST search for class patterns
if "ast" in stats["search_types_used"]:
assert True
else:
# At minimum text search
assert "text" in stats["search_types_used"]
@pytest.mark.asyncio
async def test_search_with_files():
"""Test file search integration."""
search_tool = create_search_tool()
# Test file search
result = await search_tool.run(
pattern="*.py", search_files=True, max_results_per_type=10
)
assert result.data is not None
stats = result.data["statistics"]
# Should include file search
assert "files" in stats["search_types_used"]
# Check for file results
results = result.data["results"]
file_results = [r for r in results if r["type"] == "file"]
assert len(file_results) > 0
@pytest.mark.asyncio
async def test_find_tool_basic():
"""Test basic find tool functionality."""
find_tool = create_find_tool()
# Find Python files
result = await find_tool.run(pattern="*.py", path=".", max_results=10)
assert result.data is not None
assert "results" in result.data
assert "statistics" in result.data
# Should find Python files
results = result.data["results"]
assert len(results) > 0
assert all(r["extension"] == ".py" for r in results)
@pytest.mark.asyncio
async def test_find_tool_with_filters():
"""Test find tool with filters."""
find_tool = create_find_tool()
# Create test directory structure
with tempfile.TemporaryDirectory() as tmpdir:
# Create some test files
(Path(tmpdir) / "small.txt").write_text("small file")
(Path(tmpdir) / "large.txt").write_text("x" * 10000) # 10KB
(Path(tmpdir) / "test_file.py").write_text("print('test')")
(Path(tmpdir) / "old_file.txt").write_text("old")
# Make old_file older
old_time = os.path.getmtime(Path(tmpdir) / "old_file.txt") - 86400 # 1 day ago
os.utime(Path(tmpdir) / "old_file.txt", (old_time, old_time))
# Test size filter
result = await find_tool.run(pattern="*.txt", path=tmpdir, min_size="5KB")
assert result.data is not None
results = result.data["results"]
assert len(results) == 1
assert results[0]["name"] == "large.txt"
# Test time filter
result = await find_tool.run(
pattern="*.txt", path=tmpdir, modified_after="12 hours ago"
)
results = result.data["results"]
assert len(results) == 2 # small.txt and large.txt
assert "old_file.txt" not in [r["name"] for r in results]
@pytest.mark.asyncio
async def test_find_tool_fuzzy_search():
"""Test fuzzy file name matching."""
find_tool = create_find_tool()
# Test fuzzy matching
result = await find_tool.run(
pattern="srchtl", # Misspelled "search_tool"
fuzzy=True,
max_results=5,
)
assert result.data is not None
# Fuzzy search might find "search_tool.py"
# But results depend on actual files in directory
@pytest.mark.asyncio
async def test_search_pagination():
"""Test pagination in search."""
search_tool = create_search_tool()
# First page
result1 = await search_tool.run(pattern="def", page_size=5, page=1)
assert result1.data is not None
assert "pagination" in result1.data
pagination = result1.data["pagination"]
assert pagination["page"] == 1
assert pagination["page_size"] == 5
if pagination["has_next"]:
# Get second page
result2 = await search_tool.run(pattern="def", page_size=5, page=2)
assert result2.data is not None
assert result2.data["pagination"]["page"] == 2
# Results should be different
results1 = result1.data["results"]
results2 = result2.data["results"]
# Check that we got different results
files1 = set(r["file"] for r in results1)
files2 = set(r["file"] for r in results2)
# Some overlap is OK but shouldn't be identical
assert files1 != files2 or len(files1) == 0 or len(files2) == 0
@pytest.mark.asyncio
async def test_search_no_vector():
"""Test that search tool is lightweight without vector/ML dependencies."""
# Vector search has been removed - tool should be fast and lightweight
search_tool = create_search_tool()
# No vector-related attributes should exist
assert not hasattr(search_tool, "_enable_vector_index")
assert not hasattr(search_tool, "embedder")
assert not hasattr(search_tool, "vector_db")
if __name__ == "__main__":
# Run tests
asyncio.run(test_search_basic())
asyncio.run(test_find_tool_basic())
print("Basic tests passed!")