Files
local-api-docs-search/tests/test_integration.py
7000pctAUTO 371a6799df
Some checks failed
CI / test (3.11) (push) Has been cancelled
CI / test (3.10) (push) Has been cancelled
CI / test (3.12) (push) Has been cancelled
CI / build (push) Has been cancelled
Add test files (indexers, search, CLI, integration)
2026-02-03 02:41:16 +00:00

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