test: expand coverage to 70%, fix mcp_server CI crash (threshold 60%)
Add/expand tests for normalize (39%→97%), searcher (39%→100%), layers (28%→97%), split_mega_files (34%→72%). Fix mcp_server.py parse_args→parse_known_args to prevent SystemExit when imported during pytest (CI was crashing on all test jobs). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
+83
-3
@@ -1,10 +1,18 @@
|
||||
"""
|
||||
test_searcher.py — Tests for the programmatic search_memories API.
|
||||
test_searcher.py -- Tests for both search() (CLI) and search_memories() (API).
|
||||
|
||||
Tests the library-facing search interface (not the CLI print variant).
|
||||
Uses the real ChromaDB fixtures from conftest.py for integration tests,
|
||||
plus mock-based tests for error paths.
|
||||
"""
|
||||
|
||||
from mempalace.searcher import search_memories
|
||||
from unittest.mock import MagicMock, patch
|
||||
|
||||
import pytest
|
||||
|
||||
from mempalace.searcher import SearchError, search, search_memories
|
||||
|
||||
|
||||
# ── search_memories (API) ──────────────────────────────────────────────
|
||||
|
||||
|
||||
class TestSearchMemories:
|
||||
@@ -43,3 +51,75 @@ class TestSearchMemories:
|
||||
assert "source_file" in hit
|
||||
assert "similarity" in hit
|
||||
assert isinstance(hit["similarity"], float)
|
||||
|
||||
def test_search_memories_query_error(self):
|
||||
"""search_memories returns error dict when query raises."""
|
||||
mock_col = MagicMock()
|
||||
mock_col.query.side_effect = RuntimeError("query failed")
|
||||
mock_client = MagicMock()
|
||||
mock_client.get_collection.return_value = mock_col
|
||||
|
||||
with patch("mempalace.searcher.chromadb.PersistentClient", return_value=mock_client):
|
||||
result = search_memories("test", "/fake/path")
|
||||
assert "error" in result
|
||||
assert "query failed" in result["error"]
|
||||
|
||||
def test_search_memories_filters_in_result(self, palace_path, seeded_collection):
|
||||
result = search_memories("test", palace_path, wing="project", room="backend")
|
||||
assert result["filters"]["wing"] == "project"
|
||||
assert result["filters"]["room"] == "backend"
|
||||
|
||||
|
||||
# ── search() (CLI print function) ─────────────────────────────────────
|
||||
|
||||
|
||||
class TestSearchCLI:
|
||||
def test_search_prints_results(self, palace_path, seeded_collection, capsys):
|
||||
search("JWT authentication", palace_path)
|
||||
captured = capsys.readouterr()
|
||||
assert "JWT" in captured.out or "authentication" in captured.out
|
||||
|
||||
def test_search_with_wing_filter(self, palace_path, seeded_collection, capsys):
|
||||
search("planning", palace_path, wing="notes")
|
||||
captured = capsys.readouterr()
|
||||
assert "Results for" in captured.out
|
||||
|
||||
def test_search_with_room_filter(self, palace_path, seeded_collection, capsys):
|
||||
search("database", palace_path, room="backend")
|
||||
captured = capsys.readouterr()
|
||||
assert "Room:" in captured.out
|
||||
|
||||
def test_search_with_wing_and_room(self, palace_path, seeded_collection, capsys):
|
||||
search("code", palace_path, wing="project", room="frontend")
|
||||
captured = capsys.readouterr()
|
||||
assert "Wing:" in captured.out
|
||||
assert "Room:" in captured.out
|
||||
|
||||
def test_search_no_palace_raises(self, tmp_path):
|
||||
with pytest.raises(SearchError, match="No palace found"):
|
||||
search("anything", str(tmp_path / "missing"))
|
||||
|
||||
def test_search_no_results(self, palace_path, collection, capsys):
|
||||
"""Empty collection returns no results message."""
|
||||
# collection is empty (no seeded data)
|
||||
result = search("xyzzy_nonexistent_query", palace_path, n_results=1)
|
||||
captured = capsys.readouterr()
|
||||
# Either prints "No results" or returns None
|
||||
assert result is None or "No results" in captured.out
|
||||
|
||||
def test_search_query_error_raises(self):
|
||||
"""search raises SearchError when query fails."""
|
||||
mock_col = MagicMock()
|
||||
mock_col.query.side_effect = RuntimeError("boom")
|
||||
mock_client = MagicMock()
|
||||
mock_client.get_collection.return_value = mock_col
|
||||
|
||||
with patch("mempalace.searcher.chromadb.PersistentClient", return_value=mock_client):
|
||||
with pytest.raises(SearchError, match="Search error"):
|
||||
search("test", "/fake/path")
|
||||
|
||||
def test_search_n_results(self, palace_path, seeded_collection, capsys):
|
||||
search("code", palace_path, n_results=1)
|
||||
captured = capsys.readouterr()
|
||||
# Should have output with at least one result block
|
||||
assert "[1]" in captured.out
|
||||
|
||||
Reference in New Issue
Block a user