Files
Claude-Code-Workflow/codex-lens/tests/test_cli_hybrid_search.py
catlog22 3da0ef2adb Add comprehensive tests for query parsing and Reciprocal Rank Fusion
- Implemented tests for the QueryParser class, covering various identifier splitting methods (CamelCase, snake_case, kebab-case), OR expansion, and FTS5 operator preservation.
- Added parameterized tests to validate expected token outputs for different query formats.
- Created edge case tests to ensure robustness against unusual input scenarios.
- Developed tests for the Reciprocal Rank Fusion (RRF) algorithm, including score computation, weight handling, and result ranking across multiple sources.
- Included tests for normalization of BM25 scores and tagging search results with source metadata.
2025-12-16 10:20:19 +08:00

123 lines
4.8 KiB
Python

"""Tests for CLI hybrid search integration (T6)."""
import pytest
from typer.testing import CliRunner
from codexlens.cli.commands import app
class TestCLIHybridSearch:
"""Test CLI integration for hybrid search modes."""
@pytest.fixture
def runner(self):
"""Create CLI test runner."""
return CliRunner()
def test_search_mode_parameter_validation(self, runner):
"""Test --mode parameter accepts valid modes and rejects invalid ones."""
# Valid modes should pass validation (even if no index exists)
valid_modes = ["exact", "fuzzy", "hybrid", "vector"]
for mode in valid_modes:
result = runner.invoke(app, ["search", "test", "--mode", mode])
# Should fail due to no index, not due to invalid mode
assert "Invalid mode" not in result.output
# Invalid mode should fail
result = runner.invoke(app, ["search", "test", "--mode", "invalid"])
assert result.exit_code == 1
assert "Invalid mode" in result.output
def test_weights_parameter_parsing(self, runner):
"""Test --weights parameter parses and validates correctly."""
# Valid weights (3 values summing to ~1.0)
result = runner.invoke(
app, ["search", "test", "--mode", "hybrid", "--weights", "0.5,0.3,0.2"]
)
# Should not show weight warning
assert "Invalid weights" not in result.output
# Invalid weights (wrong number of values)
result = runner.invoke(
app, ["search", "test", "--mode", "hybrid", "--weights", "0.5,0.5"]
)
assert "Invalid weights format" in result.output
# Invalid weights (non-numeric)
result = runner.invoke(
app, ["search", "test", "--mode", "hybrid", "--weights", "a,b,c"]
)
assert "Invalid weights format" in result.output
def test_weights_normalization(self, runner):
"""Test weights are normalized when they don't sum to 1.0."""
# Weights summing to 2.0 should trigger normalization warning
result = runner.invoke(
app, ["search", "test", "--mode", "hybrid", "--weights", "0.8,0.6,0.6"]
)
# Should show normalization warning
if "Normalizing" in result.output or "Warning" in result.output:
# Expected behavior
pass
def test_search_help_shows_modes(self, runner):
"""Test search --help displays all available modes."""
result = runner.invoke(app, ["search", "--help"])
assert result.exit_code == 0
assert "exact" in result.output
assert "fuzzy" in result.output
assert "hybrid" in result.output
assert "vector" in result.output
assert "RRF fusion" in result.output
def test_migrate_command_exists(self, runner):
"""Test migrate command is registered and accessible."""
result = runner.invoke(app, ["migrate", "--help"])
assert result.exit_code == 0
assert "Dual-FTS upgrade" in result.output
assert "schema version 4" in result.output
def test_status_command_shows_backends(self, runner):
"""Test status command displays search backend availability."""
result = runner.invoke(app, ["status"])
# Should show backend status (even if no indexes)
assert "Search Backends" in result.output or result.exit_code == 0
class TestSearchModeMapping:
"""Test mode parameter maps correctly to SearchOptions."""
@pytest.fixture
def runner(self):
"""Create CLI test runner."""
return CliRunner()
def test_exact_mode_disables_fuzzy(self, runner):
"""Test --mode exact disables fuzzy search."""
# This would require mocking, but we can verify the parameter is accepted
result = runner.invoke(app, ["search", "test", "--mode", "exact"])
# Should not show mode validation error
assert "Invalid mode" not in result.output
def test_fuzzy_mode_enables_only_fuzzy(self, runner):
"""Test --mode fuzzy enables fuzzy search only."""
result = runner.invoke(app, ["search", "test", "--mode", "fuzzy"])
assert "Invalid mode" not in result.output
def test_hybrid_mode_enables_both(self, runner):
"""Test --mode hybrid enables both exact and fuzzy."""
result = runner.invoke(app, ["search", "test", "--mode", "hybrid"])
assert "Invalid mode" not in result.output
def test_vector_mode_accepted(self, runner):
"""Test --mode vector is accepted (future feature)."""
result = runner.invoke(app, ["search", "test", "--mode", "vector"])
assert "Invalid mode" not in result.output
def test_cli_imports_successfully():
"""Test CLI modules import without errors."""
from codexlens.cli import commands, output
assert hasattr(commands, "app")
assert hasattr(output, "render_search_results")