mirror of
https://github.com/catlog22/Claude-Code-Workflow.git
synced 2026-02-05 01:50:27 +08:00
- Implemented tests for the ChunkConfig and Chunker classes, covering default and custom configurations. - Added tests for symbol-based chunking, including single and multiple symbols, handling of empty symbols, and preservation of line numbers. - Developed tests for sliding window chunking, ensuring correct chunking behavior with various content sizes and configurations. - Created integration tests for semantic search, validating embedding generation, vector storage, and search accuracy across a complex codebase. - Included performance tests for embedding generation and search operations. - Established tests for chunking strategies, comparing symbol-based and sliding window approaches. - Enhanced test coverage for edge cases, including handling of unicode characters and out-of-bounds symbol ranges.
281 lines
9.4 KiB
Python
281 lines
9.4 KiB
Python
"""Tests for CodexLens CLI output functions."""
|
|
|
|
import json
|
|
from dataclasses import dataclass
|
|
from io import StringIO
|
|
from pathlib import Path
|
|
from unittest.mock import patch
|
|
|
|
import pytest
|
|
from rich.console import Console
|
|
|
|
from codexlens.cli.output import (
|
|
_to_jsonable,
|
|
print_json,
|
|
render_file_inspect,
|
|
render_search_results,
|
|
render_status,
|
|
render_symbols,
|
|
)
|
|
from codexlens.entities import SearchResult, Symbol
|
|
|
|
|
|
class TestToJsonable:
|
|
"""Tests for _to_jsonable helper function."""
|
|
|
|
def test_none_value(self):
|
|
"""Test converting None."""
|
|
assert _to_jsonable(None) is None
|
|
|
|
def test_primitive_values(self):
|
|
"""Test converting primitive values."""
|
|
assert _to_jsonable("string") == "string"
|
|
assert _to_jsonable(42) == 42
|
|
assert _to_jsonable(3.14) == 3.14
|
|
assert _to_jsonable(True) is True
|
|
|
|
def test_path_conversion(self):
|
|
"""Test converting Path to string."""
|
|
path = Path("/test/file.py")
|
|
result = _to_jsonable(path)
|
|
assert result == str(path)
|
|
|
|
def test_dict_conversion(self):
|
|
"""Test converting dict with nested values."""
|
|
data = {"key": "value", "path": Path("/test.py"), "nested": {"a": 1}}
|
|
result = _to_jsonable(data)
|
|
assert result["key"] == "value"
|
|
# Path conversion uses str(), which may differ by OS
|
|
assert result["path"] == str(Path("/test.py"))
|
|
assert result["nested"]["a"] == 1
|
|
|
|
def test_list_conversion(self):
|
|
"""Test converting list with various items."""
|
|
data = ["string", 42, Path("/test.py")]
|
|
result = _to_jsonable(data)
|
|
assert result == ["string", 42, str(Path("/test.py"))]
|
|
|
|
def test_tuple_conversion(self):
|
|
"""Test converting tuple."""
|
|
data = ("a", "b", Path("/test.py"))
|
|
result = _to_jsonable(data)
|
|
assert result == ["a", "b", str(Path("/test.py"))]
|
|
|
|
def test_set_conversion(self):
|
|
"""Test converting set."""
|
|
data = {1, 2, 3}
|
|
result = _to_jsonable(data)
|
|
assert set(result) == {1, 2, 3}
|
|
|
|
def test_pydantic_model_conversion(self):
|
|
"""Test converting Pydantic model."""
|
|
symbol = Symbol(name="test", kind="function", range=(1, 5))
|
|
result = _to_jsonable(symbol)
|
|
assert result["name"] == "test"
|
|
assert result["kind"] == "function"
|
|
assert result["range"] == (1, 5)
|
|
|
|
def test_dataclass_conversion(self):
|
|
"""Test converting dataclass."""
|
|
@dataclass
|
|
class TestData:
|
|
name: str
|
|
value: int
|
|
|
|
data = TestData(name="test", value=42)
|
|
result = _to_jsonable(data)
|
|
assert result["name"] == "test"
|
|
assert result["value"] == 42
|
|
|
|
|
|
class TestPrintJson:
|
|
"""Tests for print_json function."""
|
|
|
|
def test_print_success_json(self, capsys):
|
|
"""Test printing success JSON."""
|
|
with patch("codexlens.cli.output.console") as mock_console:
|
|
captured_output = []
|
|
mock_console.print_json = lambda x: captured_output.append(x)
|
|
|
|
print_json(success=True, result={"key": "value"})
|
|
|
|
output = json.loads(captured_output[0])
|
|
assert output["success"] is True
|
|
assert output["result"]["key"] == "value"
|
|
|
|
def test_print_error_json(self, capsys):
|
|
"""Test printing error JSON."""
|
|
with patch("codexlens.cli.output.console") as mock_console:
|
|
captured_output = []
|
|
mock_console.print_json = lambda x: captured_output.append(x)
|
|
|
|
print_json(success=False, error="Something went wrong")
|
|
|
|
output = json.loads(captured_output[0])
|
|
assert output["success"] is False
|
|
assert output["error"] == "Something went wrong"
|
|
|
|
def test_print_error_default_message(self, capsys):
|
|
"""Test printing error with default message."""
|
|
with patch("codexlens.cli.output.console") as mock_console:
|
|
captured_output = []
|
|
mock_console.print_json = lambda x: captured_output.append(x)
|
|
|
|
print_json(success=False)
|
|
|
|
output = json.loads(captured_output[0])
|
|
assert output["error"] == "Unknown error"
|
|
|
|
|
|
class TestRenderSearchResults:
|
|
"""Tests for render_search_results function."""
|
|
|
|
def test_render_empty_results(self):
|
|
"""Test rendering empty results."""
|
|
with patch("codexlens.cli.output.console") as mock_console:
|
|
render_search_results([])
|
|
mock_console.print.assert_called_once()
|
|
|
|
def test_render_results_with_data(self):
|
|
"""Test rendering results with data."""
|
|
results = [
|
|
SearchResult(path="/test/a.py", score=0.95, excerpt="test excerpt"),
|
|
SearchResult(path="/test/b.py", score=0.85, excerpt="another excerpt"),
|
|
]
|
|
|
|
with patch("codexlens.cli.output.console") as mock_console:
|
|
render_search_results(results)
|
|
mock_console.print.assert_called_once()
|
|
|
|
def test_render_results_custom_title(self):
|
|
"""Test rendering results with custom title."""
|
|
results = [SearchResult(path="/test.py", score=0.5)]
|
|
|
|
with patch("codexlens.cli.output.console") as mock_console:
|
|
render_search_results(results, title="Custom Title")
|
|
mock_console.print.assert_called_once()
|
|
|
|
|
|
class TestRenderSymbols:
|
|
"""Tests for render_symbols function."""
|
|
|
|
def test_render_empty_symbols(self):
|
|
"""Test rendering empty symbols list."""
|
|
with patch("codexlens.cli.output.console") as mock_console:
|
|
render_symbols([])
|
|
mock_console.print.assert_called_once()
|
|
|
|
def test_render_symbols_with_data(self):
|
|
"""Test rendering symbols with data."""
|
|
symbols = [
|
|
Symbol(name="MyClass", kind="class", range=(1, 10)),
|
|
Symbol(name="my_func", kind="function", range=(12, 20)),
|
|
]
|
|
|
|
with patch("codexlens.cli.output.console") as mock_console:
|
|
render_symbols(symbols)
|
|
mock_console.print.assert_called_once()
|
|
|
|
def test_render_symbols_custom_title(self):
|
|
"""Test rendering symbols with custom title."""
|
|
symbols = [Symbol(name="test", kind="function", range=(1, 1))]
|
|
|
|
with patch("codexlens.cli.output.console") as mock_console:
|
|
render_symbols(symbols, title="Functions Found")
|
|
mock_console.print.assert_called_once()
|
|
|
|
|
|
class TestRenderStatus:
|
|
"""Tests for render_status function."""
|
|
|
|
def test_render_basic_stats(self):
|
|
"""Test rendering basic stats."""
|
|
stats = {"files": 100, "symbols": 500}
|
|
|
|
with patch("codexlens.cli.output.console") as mock_console:
|
|
render_status(stats)
|
|
mock_console.print.assert_called_once()
|
|
|
|
def test_render_stats_with_nested_dict(self):
|
|
"""Test rendering stats with nested dict."""
|
|
stats = {
|
|
"files": 100,
|
|
"languages": {"python": 50, "javascript": 30, "go": 20},
|
|
}
|
|
|
|
with patch("codexlens.cli.output.console") as mock_console:
|
|
render_status(stats)
|
|
mock_console.print.assert_called_once()
|
|
|
|
def test_render_stats_with_list(self):
|
|
"""Test rendering stats with list value."""
|
|
stats = {
|
|
"files": 100,
|
|
"recent_files": ["/a.py", "/b.py", "/c.py"],
|
|
}
|
|
|
|
with patch("codexlens.cli.output.console") as mock_console:
|
|
render_status(stats)
|
|
mock_console.print.assert_called_once()
|
|
|
|
|
|
class TestRenderFileInspect:
|
|
"""Tests for render_file_inspect function."""
|
|
|
|
def test_render_file_with_symbols(self):
|
|
"""Test rendering file inspection with symbols."""
|
|
symbols = [
|
|
Symbol(name="hello", kind="function", range=(1, 5)),
|
|
Symbol(name="MyClass", kind="class", range=(7, 20)),
|
|
]
|
|
|
|
with patch("codexlens.cli.output.console") as mock_console:
|
|
render_file_inspect("/test/file.py", "python", symbols)
|
|
# Should be called twice: once for header, once for symbols table
|
|
assert mock_console.print.call_count == 2
|
|
|
|
def test_render_file_without_symbols(self):
|
|
"""Test rendering file inspection without symbols."""
|
|
with patch("codexlens.cli.output.console") as mock_console:
|
|
render_file_inspect("/test/file.py", "python", [])
|
|
assert mock_console.print.call_count == 2
|
|
|
|
|
|
class TestJsonOutputIntegration:
|
|
"""Integration tests for JSON output."""
|
|
|
|
def test_search_result_to_json(self):
|
|
"""Test converting SearchResult to JSON."""
|
|
result = SearchResult(
|
|
path="/test.py",
|
|
score=0.95,
|
|
excerpt="test code here",
|
|
metadata={"line": 10},
|
|
)
|
|
|
|
jsonable = _to_jsonable(result)
|
|
# Verify it can be JSON serialized
|
|
json_str = json.dumps(jsonable)
|
|
parsed = json.loads(json_str)
|
|
|
|
assert parsed["path"] == "/test.py"
|
|
assert parsed["score"] == 0.95
|
|
assert parsed["excerpt"] == "test code here"
|
|
|
|
def test_nested_results_to_json(self):
|
|
"""Test converting nested structure to JSON."""
|
|
data = {
|
|
"query": "test",
|
|
"results": [
|
|
SearchResult(path="/a.py", score=0.9),
|
|
SearchResult(path="/b.py", score=0.8),
|
|
],
|
|
}
|
|
|
|
jsonable = _to_jsonable(data)
|
|
json_str = json.dumps(jsonable)
|
|
parsed = json.loads(json_str)
|
|
|
|
assert parsed["query"] == "test"
|
|
assert len(parsed["results"]) == 2
|