"""Integration tests for the complete workflow.""" import pytest from unittest.mock import Mock, patch from src.cli.commands import cli 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