From 10fa9462fe33eca8571c0bab9c481eaf1cc410f0 Mon Sep 17 00:00:00 2001 From: 7000pctAUTO Date: Tue, 3 Feb 2026 01:25:56 +0000 Subject: [PATCH] Add test files --- tests/test_search.py | 218 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 218 insertions(+) create mode 100644 tests/test_search.py diff --git a/tests/test_search.py b/tests/test_search.py new file mode 100644 index 0000000..88cd6cf --- /dev/null +++ b/tests/test_search.py @@ -0,0 +1,218 @@ +"""Tests for the search functionality.""" + +import pytest +from unittest.mock import Mock, patch + +from src.search.searcher import Searcher, SearchOptions +from src.models.document import Document, SourceType, SearchResult +from src.search.embeddings import EmbeddingManager +from src.search.vectorstore import VectorStore + + +class TestSearcher: + """Tests for the Searcher class.""" + + @pytest.fixture + def mock_embedding_manager(self): + """Create a mock embedding manager.""" + manager = Mock(spec=EmbeddingManager) + manager.embed_query.return_value = [0.1, 0.2, 0.3] + manager.embed.return_value = [[0.1, 0.2, 0.3]] + return manager + + @pytest.fixture + def mock_vector_store(self): + """Create a mock vector store.""" + store = Mock(spec=VectorStore) + store.search.return_value = [ + { + "id": "doc1", + "content": "Test content", + "metadata": { + "source_type": "openapi", + "title": "Test Doc", + "file_path": "/test/doc.yaml", + }, + "distance": 0.1, + "score": 0.9, + } + ] + store.get_all_documents.return_value = [] + store.get_stats.return_value = Mock( + total_documents=1, + openapi_count=1, + readme_count=0, + code_count=0, + ) + return store + + @pytest.fixture + def searcher(self, mock_embedding_manager, mock_vector_store): + """Create a searcher with mocked dependencies.""" + with patch.object(Searcher, "__init__", lambda self: None): + searcher = Searcher() + searcher._embedding_manager = mock_embedding_manager + searcher._vector_store = mock_vector_store + searcher._config = Mock() + searcher._config.default_limit = 10 + return searcher + + def test_search_returns_results(self, searcher, mock_vector_store): + """Test that search returns results.""" + results = searcher.search("test query") + + assert len(results) > 0 + result = results[0] + assert isinstance(result, SearchResult) + assert result.document.id == "doc1" + assert result.score > 0 + + def test_search_with_limit(self, searcher, mock_vector_store): + """Test search with limit option.""" + options = SearchOptions(limit=5) + searcher.search("test query", options) + + mock_vector_store.search.assert_called_once() + call_args = mock_vector_store.search.call_args + assert call_args.kwargs.get("n_results") == 10 + + def test_search_with_source_filter(self, searcher, mock_vector_store): + """Test search with source type filter.""" + options = SearchOptions(source_type=SourceType.OPENAPI) + searcher.search("test query", options) + + mock_vector_store.search.assert_called_once() + call_args = mock_vector_store.search.call_args + assert call_args.kwargs.get("source_type") == SourceType.OPENAPI + + def test_empty_query_returns_empty(self, searcher): + """Test that empty query returns empty list.""" + results = searcher.search("") + assert results == [] + + results = searcher.search(" ") + assert results == [] + + def test_search_creates_document_from_result(self, searcher, mock_vector_store): + """Test that search properly creates Document objects.""" + results = searcher.search("test query") + + assert len(results) > 0 + result = results[0] + assert isinstance(result.document, Document) + assert result.document.source_type == SourceType.OPENAPI + assert result.document.title == "Test Doc" + + def test_keyword_extraction(self, searcher): + """Test keyword extraction from query.""" + keywords = searcher._extract_keywords("How do I authenticate users") + + assert "authenticate" in keywords + assert "users" in keywords + assert "how" not in keywords + assert "do" not in keywords + assert "i" not in keywords + + def test_keyword_score(self, searcher): + """Test keyword matching score calculation.""" + keywords = ["authenticate", "users"] + + high_score = searcher._calculate_keyword_score( + keywords, "How to authenticate users with JWT" + ) + assert high_score > 0 + + low_score = searcher._calculate_keyword_score( + keywords, "This is unrelated content" + ) + assert low_score == 0 + + def test_highlight_generation(self, searcher): + """Test highlight snippet generation.""" + highlights = searcher._generate_highlights( + "authenticate users", + "To authenticate users, you need to provide valid credentials." + ) + + assert len(highlights) > 0 + assert any("authenticate" in h.lower() or "users" in h.lower() for h in highlights) + + def test_hybrid_search(self, searcher, mock_vector_store): + """Test hybrid search combines semantic and keyword results.""" + mock_vector_store.search.return_value = [ + { + "id": "doc1", + "content": "Test content about authentication", + "metadata": { + "source_type": "openapi", + "title": "Auth Doc", + "file_path": "/test/auth.yaml", + }, + "distance": 0.1, + "score": 0.9, + } + ] + + results = searcher.hybrid_search("authentication") + + assert len(results) > 0 + mock_vector_store.search.assert_called() + + +class TestSearchOptions: + """Tests for SearchOptions.""" + + def test_default_options(self): + """Test default search options.""" + options = SearchOptions() + + assert options.limit == 10 + assert options.source_type is None + assert options.min_score == 0.0 + assert options.include_scores is True + + def test_custom_options(self): + """Test custom search options.""" + options = SearchOptions( + limit=20, + source_type=SourceType.README, + min_score=0.5, + ) + + assert options.limit == 20 + assert options.source_type == SourceType.README + assert options.min_score == 0.5 + + +class TestSearchResult: + """Tests for SearchResult.""" + + def test_search_result_creation(self): + """Test creating a search result.""" + doc = Document( + id="test", + content="test content", + source_type=SourceType.OPENAPI, + title="Test", + ) + result = SearchResult(document=doc, score=0.95) + + assert result.document == doc + assert result.score == 0.95 + assert result.highlights == [] + + def test_search_result_to_dict(self): + """Test converting search result to dictionary.""" + doc = Document( + id="test", + content="test content", + source_type=SourceType.OPENAPI, + title="Test", + ) + result = SearchResult(document=doc, score=0.95, highlights=["test"]) + + result_dict = result.to_dict() + + assert result_dict["id"] == "test" + assert result_dict["score"] == 0.95 + assert "test" in result_dict["highlights"]