213 lines
6.9 KiB
Python
213 lines
6.9 KiB
Python
"""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
|