"""Integration tests for the complete workflow.""" import pytest from pathlib import Path from unittest.mock import Mock, patch from src.cli.commands import cli from src.search.searcher import Searcher from src.models.document import Document, SourceType, SearchResult class TestIntegration: """Integration tests for the complete workflow.""" @pytest.fixture def runner(self): """Create a CLI runner.""" from click.testing import CliRunner return CliRunner() def test_index_then_search_workflow(self, runner, tmp_path): """Test complete workflow: index then search.""" openapi_file = tmp_path / "api.yaml" openapi_file.write_text(""" openapi: "3.0.0" info: title: Test API version: "1.0.0" paths: /users: get: summary: Get users operationId: getUsers responses: '200': description: OK """) readme_file = tmp_path / "README.md" readme_file.write_text(""" # Test API This is a test API. ## Endpoints - GET /users - List users """) with patch("src.cli.commands.Searcher") as MockSearcher: mock_searcher = Mock() mock_searcher.index.return_value = 2 mock_doc = Document( id="test", content="test content", source_type=SourceType.OPENAPI, title="Test", ) mock_result = SearchResult(document=mock_doc, score=0.9) mock_searcher.hybrid_search.return_value = [mock_result] MockSearcher.return_value = mock_searcher index_result = runner.invoke(cli, ["index", str(tmp_path)]) assert index_result.exit_code == 0 search_result = runner.invoke(cli, ["search", "users"]) assert search_result.exit_code == 0 def test_search_with_no_index(self, runner): """Test search behavior when index is empty.""" with patch("src.cli.commands.Searcher") as MockSearcher: mock_searcher = Mock() mock_searcher.hybrid_search.return_value = [] MockSearcher.return_value = mock_searcher result = runner.invoke(cli, ["search", "test query"]) assert result.exit_code == 0 mock_searcher.hybrid_search.assert_called_once() def test_multiple_index_types(self, runner, tmp_path): """Test indexing multiple document types.""" (tmp_path / "api.yaml").write_text(""" openapi: "3.0.0" info: title: API version: "1.0.0" paths: {} """) (tmp_path / "README.md").write_text("# Test\n") (tmp_path / "code.py").write_text('"""Test."""\ndef foo(): pass\n') with patch("src.cli.commands.Searcher") as MockSearcher: mock_searcher = Mock() mock_searcher.index.return_value = 3 MockSearcher.return_value = mock_searcher result = runner.invoke(cli, ["index", str(tmp_path), "--type", "all"]) assert result.exit_code == 0 mock_searcher.index.assert_called_once() def test_list_with_stats(self, runner): """Test list command shows correct stats.""" from src.models.document import IndexStats with patch("src.cli.commands.Searcher") as MockSearcher: mock_searcher = Mock() mock_searcher.get_stats.return_value = IndexStats( total_documents=25, openapi_count=10, readme_count=8, code_count=7, ) MockSearcher.return_value = mock_searcher result = runner.invoke(cli, ["list"]) assert result.exit_code == 0 assert "25" in result.output or "total" in result.output.lower() def test_clear_index(self, runner): """Test clearing the index.""" with patch("src.cli.commands.Searcher") as MockSearcher: mock_searcher = Mock() mock_searcher._vector_store.count.return_value = 10 mock_searcher.clear_index.return_value = True MockSearcher.return_value = mock_searcher result = runner.invoke(cli, ["clear", "--force"]) assert result.exit_code == 0 def test_config_management(self, runner, tmp_path): """Test configuration show and reset.""" with patch("src.cli.commands.get_config") as mock_get_config: mock_config = Mock() mock_config.to_dict.return_value = { "index_path": "./docs", "model_name": "test-model", "embedding_device": "cpu", } mock_get_config.return_value = mock_config show_result = runner.invoke(cli, ["config", "--show"]) assert show_result.exit_code == 0 mock_get_config.return_value = mock_config reset_result = runner.invoke(cli, ["config", "--reset"]) assert reset_result.exit_code == 0 mock_config.reset.assert_called_once() class TestEdgeCases: """Edge case tests.""" @pytest.fixture def runner(self): """Create a CLI runner.""" from click.testing import CliRunner return CliRunner() def test_search_empty_query(self, runner): """Test search with empty query.""" with patch("src.cli.commands.Searcher") as MockSearcher: mock_searcher = Mock() mock_searcher.hybrid_search.return_value = [] MockSearcher.return_value = mock_searcher result = runner.invoke(cli, ["search", ""]) assert result.exit_code == 0 def test_search_very_long_query(self, runner): """Test search with very long query.""" with patch("src.cli.commands.Searcher") as MockSearcher: mock_searcher = Mock() mock_searcher.hybrid_search.return_value = [] MockSearcher.return_value = mock_searcher long_query = "test " * 1000 result = runner.invoke(cli, ["search", long_query]) assert result.exit_code == 0 def test_index_empty_directory(self, runner, tmp_path): """Test indexing an empty directory.""" with patch("src.cli.commands.Searcher") as MockSearcher: mock_searcher = Mock() mock_searcher.index.return_value = 0 MockSearcher.return_value = mock_searcher result = runner.invoke(cli, ["index", str(tmp_path)]) assert result.exit_code == 0 def test_list_json_output(self, runner): """Test list with JSON output.""" from src.models.document import IndexStats with patch("src.cli.commands.Searcher") as MockSearcher: mock_searcher = Mock() mock_searcher.get_stats.return_value = IndexStats( total_documents=5, openapi_count=2, readme_count=2, code_count=1, ) MockSearcher.return_value = mock_searcher result = runner.invoke(cli, ["list", "--json"]) assert result.exit_code == 0