Skip to content

Commit 21f31dc

Browse files
mmacphersonclaude
andcommitted
test: add CLI search subcommand and cluster loading tests
- Test `lucide search` with a mock search DB - Test `lucide search` with nonexistent DB returns error - Verify cluster data is loaded into search DB by build_search_db - 62 tests total, all passing Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent fc31a2e commit 21f31dc

2 files changed

Lines changed: 70 additions & 1 deletion

File tree

tests/build_search_test.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -266,6 +266,15 @@ def test_builds_db_from_jsonl(self, sample_jsonl, search_db, sample_clusters):
266266
assert meta["embedding_dim"] == str(DEFAULT_EMBEDDING_DIM)
267267
assert "built_at" in meta
268268

269+
# Check clusters were loaded
270+
cluster_rows = conn.execute(
271+
"SELECT name, theme FROM icon_clusters ORDER BY name"
272+
).fetchall()
273+
assert len(cluster_rows) == 3
274+
themes = {r[1] for r in cluster_rows}
275+
assert "Love & Favorites" in themes
276+
assert "Shapes" in themes
277+
269278
conn.close()
270279

271280
def test_rebuilds_from_scratch(self, sample_jsonl, search_db, sample_clusters):

tests/cli_test.py

Lines changed: 61 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,13 @@
11
import pathlib
2+
import sqlite3
23
import subprocess
34
from unittest import mock
45

6+
import numpy as np
57
import pytest
68

7-
from lucide import cli
9+
from lucide import cli, search
10+
from lucide.config import DEFAULT_EMBEDDING_DIM
811

912

1013
@pytest.fixture
@@ -151,3 +154,60 @@ def test_legacy_lucide_db_alias(self):
151154

152155
mock_build.assert_called_once()
153156
assert result == 0
157+
158+
def test_search_subcommand(self, tmp_path):
159+
search_db = tmp_path / "test-search.db"
160+
conn = sqlite3.connect(search_db)
161+
conn.execute(
162+
"CREATE TABLE icon_descriptions"
163+
" (name TEXT PRIMARY KEY, description TEXT, model TEXT)"
164+
)
165+
conn.execute(
166+
"CREATE TABLE icon_embeddings"
167+
" (name TEXT PRIMARY KEY, embedding BLOB, model TEXT)"
168+
)
169+
conn.execute(
170+
"CREATE TABLE icon_clusters (name TEXT, cluster_id INTEGER, theme TEXT)"
171+
)
172+
conn.execute("CREATE TABLE metadata (key TEXT PRIMARY KEY, value TEXT)")
173+
emb = (
174+
np.random.default_rng(42)
175+
.standard_normal(DEFAULT_EMBEDDING_DIM)
176+
.astype(np.float32)
177+
)
178+
conn.execute(
179+
"INSERT INTO icon_descriptions VALUES (?, ?, ?)",
180+
("heart", "A heart icon", "test"),
181+
)
182+
conn.execute(
183+
"INSERT INTO icon_embeddings VALUES (?, ?, ?)",
184+
("heart", emb.tobytes(), "test"),
185+
)
186+
conn.execute("INSERT INTO metadata VALUES ('version', 'test')")
187+
conn.commit()
188+
conn.close()
189+
190+
with mock.patch(
191+
"sys.argv",
192+
["lucide", "search", "love", "--search-db", str(search_db)],
193+
):
194+
result = cli.main()
195+
assert result == 0
196+
197+
def test_search_subcommand_no_db(self, monkeypatch):
198+
monkeypatch.delenv("LUCIDE_SEARCH_DB_PATH", raising=False)
199+
search._search_index = None
200+
search._embedder_instance = None
201+
202+
with mock.patch(
203+
"sys.argv",
204+
[
205+
"lucide",
206+
"search",
207+
"love",
208+
"--search-db",
209+
"/nonexistent/search.db",
210+
],
211+
):
212+
result = cli.main()
213+
assert result == 1

0 commit comments

Comments
 (0)