Diff
2 set -euo pipefail
3
4 # Fast pre-check: syntax errors
5-python -m py_compile wheresmy/cli/import_metadata.py 2>&1 || { echo "FAIL: py_compile import_metadata"; exit 1; }
6-python -m py_compile wheresmy/cli/search.py 2>&1 || { echo "FAIL: py_compile search"; exit 1; }
7-python -m py_compile wheresmy/cli/extract.py 2>&1 || { echo "FAIL: py_compile extract"; exit 1; }
5+python3 -c "import py_compile; py_compile.compile('wheresmy/cli/import_metadata.py', doraise=True)" 2>&1 || { echo "FAIL: py_compile import_metadata"; exit 1; }
6+python3 -c "import py_compile; py_compile.compile('wheresmy/cli/search.py', doraise=True)" 2>&1 || { echo "FAIL: py_compile search"; exit 1; }
7+python3 -c "import py_compile; py_compile.compile('wheresmy/cli/extract.py', doraise=True)" 2>&1 || { echo "FAIL: py_compile extract"; exit 1; }
8
9 # Run full test suite (excluding integration + VLM)
10 output=$(uv run pytest \
1+"""Helper for running CLI main() functions in-process for fast testing.
2+
3+Replaces subprocess.run() calls with direct function invocation,
4+avoiding the ~6s overhead of spawning a new Python process that
5+imports the heavy wheresmy stack (sentence-transformers, FAISS, etc.).
6+"""
7+
8+import io
9+import sys
10+from contextlib import redirect_stderr, redirect_stdout
11+from dataclasses import dataclass
12+from typing import List, Optional
13+
14+
15+@dataclass
16+class CLIResult:
17+ """Mimics subprocess.CompletedProcess interface for CLI tests."""
18+
19+ returncode: int
20+ stdout: str
21+ stderr: str
22+
23+
24+def run_cli(
25+ module_name: str,
26+ args: List[str],
27+ stdin_input: Optional[str] = None,
28+) -> CLIResult:
29+ """Run a CLI module's main() function in-process.
30+
31+ Args:
32+ module_name: Dotted module path, e.g. "wheresmy.cli.extract"
33+ args: Command-line arguments (not including the program name)
34+ stdin_input: Optional string to feed to stdin
35+
36+ Returns:
37+ CLIResult with returncode, stdout, stderr
38+ """
39+ saved_argv = sys.argv
40+ saved_stdin = sys.stdin
41+
42+ stdout_buf = io.StringIO()
43+ stderr_buf = io.StringIO()
44+
45+ try:
46+ sys.argv = [module_name] + args
47+ if stdin_input is not None:
48+ sys.stdin = io.StringIO(stdin_input)
49+
50+ with redirect_stdout(stdout_buf), redirect_stderr(stderr_buf):
51+ # Import and call main()
52+ # Use a cache-friendly import approach
53+ if module_name == "wheresmy.cli.extract":
54+ from wheresmy.cli.extract import main
55+ elif module_name == "wheresmy.cli.search":
56+ from wheresmy.cli.search import main
57+ elif module_name == "wheresmy.cli.import_metadata":
58+ from wheresmy.cli.import_metadata import main
59+ else:
60+ raise ValueError(f"Unknown CLI module: {module_name}")
61+
62+ returncode = main()
63+
64+ except SystemExit as e:
65+ returncode = e.code if isinstance(e.code, int) else 1
66+ finally:
67+ sys.argv = saved_argv
68+ sys.stdin = saved_stdin
69+
70+ return CLIResult(
71+ returncode=returncode,
72+ stdout=stdout_buf.getvalue(),
73+ stderr=stderr_buf.getvalue(),
74+ )
2 """Tests for wheresmy-extract CLI tool."""
3
4 import json
5-import subprocess
6-import sys
5 from pathlib import Path
6
7 import pytest
8 from PIL import Image
9
10+from wheresmy.tests.cli.cli_runner import run_cli
11+
12
13 @pytest.fixture
14 def test_images(tmp_path: Path) -> Path:
44 """Should extract metadata from a single image."""
45 image_path = test_images / "basic.jpg"
46
47- result = subprocess.run(
48- [
49- sys.executable,
50- "-m",
51- "wheresmy.cli.extract",
52- str(image_path),
53- ],
54- capture_output=True,
55- text=True,
56- )
47+ result = run_cli("wheresmy.cli.extract", [str(image_path)])
48
49 assert result.returncode == 0
50
61
62 def test_extract_directory(test_images: Path):
63 """Should extract metadata from all images in directory."""
73- result = subprocess.run(
74- [
75- sys.executable,
76- "-m",
77- "wheresmy.cli.extract",
78- "--directory",
79- str(test_images),
80- ],
81- capture_output=True,
82- text=True,
83- )
64+ result = run_cli("wheresmy.cli.extract", ["--directory", str(test_images)])
65
66 assert result.returncode == 0
67
73
74 def test_extract_directory_recursive(test_images: Path):
75 """Should extract metadata recursively when requested."""
95- result = subprocess.run(
96- [
97- sys.executable,
98- "-m",
99- "wheresmy.cli.extract",
100- "--directory",
101- str(test_images),
102- "--recursive",
103- ],
104- capture_output=True,
105- text=True,
76+ result = run_cli(
77+ "wheresmy.cli.extract",
78+ ["--directory", str(test_images), "--recursive"],
79 )
80
81 assert result.returncode == 0
87
88 def test_extract_multiple_files(test_images: Path):
89 """Should extract metadata from multiple specified files."""
117- result = subprocess.run(
118- [
119- sys.executable,
120- "-m",
121- "wheresmy.cli.extract",
122- str(test_images / "basic.jpg"),
123- str(test_images / "test.png"),
124- ],
125- capture_output=True,
126- text=True,
90+ result = run_cli(
91+ "wheresmy.cli.extract",
92+ [str(test_images / "basic.jpg"), str(test_images / "test.png")],
93 )
94
95 assert result.returncode == 0
105 empty_dir = tmp_path / "empty"
106 empty_dir.mkdir()
107
142- result = subprocess.run(
143- [
144- sys.executable,
145- "-m",
146- "wheresmy.cli.extract",
147- "--directory",
148- str(empty_dir),
149- ],
150- capture_output=True,
151- text=True,
152- )
108+ result = run_cli("wheresmy.cli.extract", ["--directory", str(empty_dir)])
109
110 assert result.returncode == 1
111 output = json.loads(result.stdout)
114
115 def test_error_handling(tmp_path: Path):
116 """Should return exit code 1 when no valid images found."""
161- result = subprocess.run(
162- [
163- sys.executable,
164- "-m",
165- "wheresmy.cli.extract",
166- str(tmp_path / "nonexistent.jpg"),
167- ],
168- capture_output=True,
169- text=True,
170- )
117+ result = run_cli("wheresmy.cli.extract", [str(tmp_path / "nonexistent.jpg")])
118
119 assert result.returncode == 1
120 output = json.loads(result.stdout)
125 """Should output proper JSON structure matching metadata extractor."""
126 image_path = test_images / "basic.jpg"
127
181- result = subprocess.run(
182- [
183- sys.executable,
184- "-m",
185- "wheresmy.cli.extract",
186- str(image_path),
187- ],
188- capture_output=True,
189- text=True,
190- )
128+ result = run_cli("wheresmy.cli.extract", [str(image_path)])
129
130 assert result.returncode == 0
131 output = json.loads(result.stdout)
139
140 def test_quiet_initialization(test_images: Path):
141 """Should not output initialization messages to stdout."""
204- result = subprocess.run(
205- [
206- sys.executable,
207- "-m",
208- "wheresmy.cli.extract",
209- str(test_images / "basic.jpg"),
210- ],
211- capture_output=True,
212- text=True,
213- )
142+ result = run_cli("wheresmy.cli.extract", [str(test_images / "basic.jpg")])
143
144 assert result.returncode == 0
145 # stdout should only contain valid JSON
2 """Tests for wheresmy-extract CLI tool --jsonl flag."""
3
4 import json
5-import subprocess
6-import sys
5 from pathlib import Path
6
7 import pytest
8 from PIL import Image
9
10+from wheresmy.tests.cli.cli_runner import run_cli
11+
12
13 @pytest.fixture
14 def test_images(tmp_path: Path) -> Path:
34 """Should extract metadata in JSONL format from a single image."""
35 image_path = test_images / "basic.jpg"
36
37- result = subprocess.run(
38- [
39- sys.executable,
40- "-m",
41- "wheresmy.cli.extract",
42- str(image_path),
43- "--jsonl",
44- ],
45- capture_output=True,
46- text=True,
47- )
37+ result = run_cli("wheresmy.cli.extract", [str(image_path), "--jsonl"])
38
39 assert result.returncode == 0
40
57
58 def test_extract_jsonl_multiple_files(test_images: Path):
59 """Should extract metadata in JSONL format from multiple files."""
70- result = subprocess.run(
71- [
72- sys.executable,
73- "-m",
74- "wheresmy.cli.extract",
75- str(test_images / "basic.jpg"),
76- str(test_images / "test.png"),
77- "--jsonl",
78- ],
79- capture_output=True,
80- text=True,
60+ result = run_cli(
61+ "wheresmy.cli.extract",
62+ [str(test_images / "basic.jpg"), str(test_images / "test.png"), "--jsonl"],
63 )
64
65 assert result.returncode == 0
82
83 def test_extract_jsonl_directory(test_images: Path):
84 """Should extract metadata in JSONL format from directory."""
103- result = subprocess.run(
104- [
105- sys.executable,
106- "-m",
107- "wheresmy.cli.extract",
108- "--directory",
109- str(test_images),
110- "--jsonl",
111- ],
112- capture_output=True,
113- text=True,
85+ result = run_cli(
86+ "wheresmy.cli.extract",
87+ ["--directory", str(test_images), "--jsonl"],
88 )
89
90 assert result.returncode == 0
102
103 def test_extract_jsonl_recursive(test_images: Path):
104 """Should extract metadata recursively in JSONL format."""
131- result = subprocess.run(
132- [
133- sys.executable,
134- "-m",
135- "wheresmy.cli.extract",
136- "--directory",
137- str(test_images),
138- "--recursive",
139- "--jsonl",
140- ],
141- capture_output=True,
142- text=True,
105+ result = run_cli(
106+ "wheresmy.cli.extract",
107+ ["--directory", str(test_images), "--recursive", "--jsonl"],
108 )
109
110 assert result.returncode == 0
125 empty_dir = tmp_path / "empty"
126 empty_dir.mkdir()
127
163- result = subprocess.run(
164- [
165- sys.executable,
166- "-m",
167- "wheresmy.cli.extract",
168- "--directory",
169- str(empty_dir),
170- "--jsonl",
171- ],
172- capture_output=True,
173- text=True,
128+ result = run_cli(
129+ "wheresmy.cli.extract",
130+ ["--directory", str(empty_dir), "--jsonl"],
131 )
132
133 assert result.returncode == 1
137
138 def test_extract_jsonl_with_errors(test_images: Path):
139 """Should handle errors gracefully in JSONL mode."""
183- result = subprocess.run(
140+ result = run_cli(
141+ "wheresmy.cli.extract",
142 [
185- sys.executable,
186- "-m",
187- "wheresmy.cli.extract",
143 str(test_images / "basic.jpg"),
144 str(test_images / "nonexistent.jpg"),
145 "--jsonl",
146 ],
192- capture_output=True,
193- text=True,
147 )
148
149 # When given specific files, it processes what it can find
167 img = Image.new("RGB", (100, 100), color="white")
168 img.save(test_images / f"img{i}.jpg", "JPEG")
169
217- result = subprocess.run(
218- [
219- sys.executable,
220- "-m",
221- "wheresmy.cli.extract",
222- "--directory",
223- str(test_images),
224- "--jsonl",
225- ],
226- capture_output=True,
227- text=True,
170+ result = run_cli(
171+ "wheresmy.cli.extract",
172+ ["--directory", str(test_images), "--jsonl"],
173 )
174
175 assert result.returncode == 0
2 """Tests for wheresmy-import CLI tool."""
3
4 import json
5-import subprocess
6-import sys
5 from pathlib import Path
6
7 import pytest
8 from PIL import Image
9
10 from wheresmy.storage.combined_system import CompleteImageSystem
11+from wheresmy.tests.cli.cli_runner import run_cli
12
13
14 @pytest.fixture
70 """Should import images from metadata file."""
71 metadata_file, metadata = test_metadata
72
74- result = subprocess.run(
75- [
76- sys.executable,
77- "-m",
78- "wheresmy.cli.import_metadata",
79- str(metadata_file),
80- "--library",
81- str(test_library),
82- ],
83- capture_output=True,
84- text=True,
73+ result = run_cli(
74+ "wheresmy.cli.import_metadata",
75+ [str(metadata_file), "--library", str(test_library)],
76 )
77
78 assert result.returncode == 0
98 with open(metadata_file) as f:
99 metadata_json = f.read()
100
110- result = subprocess.run(
111- [
112- sys.executable,
113- "-m",
114- "wheresmy.cli.import_metadata",
115- "-", # Read from stdin
116- "--library",
117- str(test_library),
118- ],
119- input=metadata_json,
120- capture_output=True,
121- text=True,
101+ result = run_cli(
102+ "wheresmy.cli.import_metadata",
103+ ["-", "--library", str(test_library)],
104+ stdin_input=metadata_json,
105 )
106
107 assert result.returncode == 0
119 CompleteImageSystem(str(test_library))
120
121 # Import once
139- result1 = subprocess.run(
140- [
141- sys.executable,
142- "-m",
143- "wheresmy.cli.import_metadata",
144- str(metadata_file),
145- "--library",
146- str(test_library),
147- ],
148- capture_output=True,
149- text=True,
122+ result1 = run_cli(
123+ "wheresmy.cli.import_metadata",
124+ [str(metadata_file), "--library", str(test_library)],
125 )
126
127 assert result1.returncode == 0
128 json.loads(result1.stdout) # Verify valid JSON
129
130 # Import again - should handle duplicates
156- result2 = subprocess.run(
157- [
158- sys.executable,
159- "-m",
160- "wheresmy.cli.import_metadata",
161- str(metadata_file),
162- "--library",
163- str(test_library),
164- ],
165- capture_output=True,
166- text=True,
131+ result2 = run_cli(
132+ "wheresmy.cli.import_metadata",
133+ [str(metadata_file), "--library", str(test_library)],
134 )
135
136 assert result2.returncode == 0
160 with open(bad_metadata_file, "w") as f:
161 json.dump(metadata_copy, f)
162
196- result = subprocess.run(
197- [
198- sys.executable,
199- "-m",
200- "wheresmy.cli.import_metadata",
201- str(bad_metadata_file),
202- "--library",
203- str(test_library),
204- ],
205- capture_output=True,
206- text=True,
163+ result = run_cli(
164+ "wheresmy.cli.import_metadata",
165+ [str(bad_metadata_file), "--library", str(test_library)],
166 )
167
168 assert result.returncode == 0 # Should succeed overall
186 with open(metadata_file, "w") as f:
187 json.dump(empty_metadata, f)
188
230- result = subprocess.run(
231- [
232- sys.executable,
233- "-m",
234- "wheresmy.cli.import_metadata",
235- str(metadata_file),
236- "--library",
237- str(test_library),
238- ],
239- capture_output=True,
240- text=True,
189+ result = run_cli(
190+ "wheresmy.cli.import_metadata",
191+ [str(metadata_file), "--library", str(test_library)],
192 )
193
194 assert result.returncode == 1 # No images to import
202 with open(invalid_file, "w") as f:
203 f.write("{ invalid json")
204
254- result = subprocess.run(
255- [
256- sys.executable,
257- "-m",
258- "wheresmy.cli.import_metadata",
259- str(invalid_file),
260- "--library",
261- str(test_library),
262- ],
263- capture_output=True,
264- text=True,
205+ result = run_cli(
206+ "wheresmy.cli.import_metadata",
207+ [str(invalid_file), "--library", str(test_library)],
208 )
209
210 assert result.returncode == 2 # Error
215 """Should output proper JSON structure."""
216 metadata_file, metadata = test_metadata
217
275- result = subprocess.run(
276- [
277- sys.executable,
278- "-m",
279- "wheresmy.cli.import_metadata",
280- str(metadata_file),
281- "--library",
282- str(test_library),
283- ],
284- capture_output=True,
285- text=True,
218+ result = run_cli(
219+ "wheresmy.cli.import_metadata",
220+ [str(metadata_file), "--library", str(test_library)],
221 )
222
223 assert result.returncode == 0
239 """Should not output initialization messages to stdout."""
240 metadata_file, metadata = test_metadata
241
307- result = subprocess.run(
308- [
309- sys.executable,
310- "-m",
311- "wheresmy.cli.import_metadata",
312- str(metadata_file),
313- "--library",
314- str(test_library),
315- ],
316- capture_output=True,
317- text=True,
242+ result = run_cli(
243+ "wheresmy.cli.import_metadata",
244+ [str(metadata_file), "--library", str(test_library)],
245 )
246
247 assert result.returncode == 0
2 """Tests for wheresmy-import CLI tool --jsonl flag."""
3
4 import json
5-import subprocess
6-import sys
5 from pathlib import Path
6
7 import pytest
8 from PIL import Image
9
10+from wheresmy.tests.cli.cli_runner import run_cli
11+
12
13 @pytest.fixture
14 def test_images(tmp_path: Path) -> Path:
74
75 def test_import_jsonl_from_file(test_jsonl_file: Path, test_library: Path):
76 """Should import images from JSONL file."""
77- result = subprocess.run(
78- [
79- sys.executable,
80- "-m",
81- "wheresmy.cli.import_metadata",
82- str(test_jsonl_file),
83- "--library",
84- str(test_library),
85- "--jsonl",
86- ],
87- capture_output=True,
88- text=True,
77+ result = run_cli(
78+ "wheresmy.cli.import_metadata",
79+ [str(test_jsonl_file), "--library", str(test_library), "--jsonl"],
80 )
81
82 assert result.returncode == 0
89 results = [json.loads(line) for line in lines]
90
91 # Check both results
101- for result in results:
102- assert "id" in result
103- assert "filename" in result
104- assert "status" in result
105- assert result["status"] == "success"
106- assert result["filename"] in ["test1.jpg", "test2.jpg"]
107- assert len(result["id"]) > 0 # Should have UUID
92+ for r in results:
93+ assert "id" in r
94+ assert "filename" in r
95+ assert "status" in r
96+ assert r["status"] == "success"
97+ assert r["filename"] in ["test1.jpg", "test2.jpg"]
98+ assert len(r["id"]) > 0 # Should have UUID
99
100
101 def test_import_jsonl_from_stdin(test_jsonl_file: Path, test_library: Path):
104 with open(test_jsonl_file) as f:
105 jsonl_content = f.read()
106
116- result = subprocess.run(
117- [
118- sys.executable,
119- "-m",
120- "wheresmy.cli.import_metadata",
121- "-", # Read from stdin
122- "--library",
123- str(test_library),
124- "--jsonl",
125- ],
126- input=jsonl_content,
127- capture_output=True,
128- text=True,
107+ result = run_cli(
108+ "wheresmy.cli.import_metadata",
109+ ["-", "--library", str(test_library), "--jsonl"],
110+ stdin_input=jsonl_content,
111 )
112
113 assert result.returncode == 0
123
124 def test_import_jsonl_streaming_behavior(test_jsonl_file: Path, test_library: Path):
125 """Should output results immediately (streaming)."""
144- result = subprocess.run(
145- [
146- sys.executable,
147- "-m",
148- "wheresmy.cli.import_metadata",
149- str(test_jsonl_file),
150- "--library",
151- str(test_library),
152- "--jsonl",
153- ],
154- capture_output=True,
155- text=True,
126+ result = run_cli(
127+ "wheresmy.cli.import_metadata",
128+ [str(test_jsonl_file), "--library", str(test_library), "--jsonl"],
129 )
130
131 assert result.returncode == 0
147 def test_import_jsonl_with_duplicates(test_jsonl_file: Path, test_library: Path):
148 """Should handle duplicates in JSONL mode."""
149 # Import once
177- result1 = subprocess.run(
178- [
179- sys.executable,
180- "-m",
181- "wheresmy.cli.import_metadata",
182- str(test_jsonl_file),
183- "--library",
184- str(test_library),
185- "--jsonl",
186- ],
187- capture_output=True,
188- text=True,
150+ result1 = run_cli(
151+ "wheresmy.cli.import_metadata",
152+ [str(test_jsonl_file), "--library", str(test_library), "--jsonl"],
153 )
154
155 assert result1.returncode == 0
156
157 # Import again - should handle duplicates
194- result2 = subprocess.run(
195- [
196- sys.executable,
197- "-m",
198- "wheresmy.cli.import_metadata",
199- str(test_jsonl_file),
200- "--library",
201- str(test_library),
202- "--jsonl",
203- ],
204- capture_output=True,
205- text=True,
158+ result2 = run_cli(
159+ "wheresmy.cli.import_metadata",
160+ [str(test_jsonl_file), "--library", str(test_library), "--jsonl"],
161 )
162
163 assert result2.returncode == 0
167 results = [json.loads(line) for line in lines]
168
169 # Should indicate duplicates
215- for result in results:
216- assert result["status"] in ["duplicate", "success"]
170+ for r in results:
171+ assert r["status"] in ["duplicate", "success"]
172
173
174 def test_import_jsonl_with_errors(test_library: Path, tmp_path: Path):
192 for entry in entries:
193 f.write(json.dumps(entry) + "\n")
194
240- result = subprocess.run(
241- [
242- sys.executable,
243- "-m",
244- "wheresmy.cli.import_metadata",
245- str(bad_jsonl),
246- "--library",
247- str(test_library),
248- "--jsonl",
249- ],
250- capture_output=True,
251- text=True,
195+ result = run_cli(
196+ "wheresmy.cli.import_metadata",
197+ [str(bad_jsonl), "--library", str(test_library), "--jsonl"],
198 )
199
200 assert result.returncode == 2 # All failed
213 empty_jsonl = tmp_path / "empty.jsonl"
214 empty_jsonl.write_text("")
215
270- result = subprocess.run(
271- [
272- sys.executable,
273- "-m",
274- "wheresmy.cli.import_metadata",
275- str(empty_jsonl),
276- "--library",
277- str(test_library),
278- "--jsonl",
279- ],
280- capture_output=True,
281- text=True,
216+ result = run_cli(
217+ "wheresmy.cli.import_metadata",
218+ [str(empty_jsonl), "--library", str(test_library), "--jsonl"],
219 )
220
221 assert result.returncode == 1 # No images to import
230 f.write("{ invalid json\n")
231 f.write('{"also": "valid"}\n')
232
296- result = subprocess.run(
297- [
298- sys.executable,
299- "-m",
300- "wheresmy.cli.import_metadata",
301- str(invalid_jsonl),
302- "--library",
303- str(test_library),
304- "--jsonl",
305- ],
306- capture_output=True,
307- text=True,
233+ result = run_cli(
234+ "wheresmy.cli.import_metadata",
235+ [str(invalid_jsonl), "--library", str(test_library), "--jsonl"],
236 )
237
238 # Should continue processing despite invalid line
273 for entry in entries:
274 f.write(json.dumps(entry) + "\n")
275
348- result = subprocess.run(
349- [
350- sys.executable,
351- "-m",
352- "wheresmy.cli.import_metadata",
353- str(mixed_jsonl),
354- "--library",
355- str(test_library),
356- "--jsonl",
357- ],
358- capture_output=True,
359- text=True,
276+ result = run_cli(
277+ "wheresmy.cli.import_metadata",
278+ [str(mixed_jsonl), "--library", str(test_library), "--jsonl"],
279 )
280
281 assert result.returncode == 0 # Some succeeded
299 def test_import_jsonl_pipeline_integration(test_images: Path, test_library: Path):
300 """Should work as part of extract -> import pipeline."""
301 # First extract metadata in JSONL format
383- extract_result = subprocess.run(
384- [
385- sys.executable,
386- "-m",
387- "wheresmy.cli.extract",
388- str(test_images / "test1.jpg"),
389- str(test_images / "test2.jpg"),
390- "--jsonl",
391- ],
392- capture_output=True,
393- text=True,
302+ extract_result = run_cli(
303+ "wheresmy.cli.extract",
304+ [str(test_images / "test1.jpg"), str(test_images / "test2.jpg"), "--jsonl"],
305 )
306
307 assert extract_result.returncode == 0
308
309 # Then import using the extracted JSONL
399- import_result = subprocess.run(
400- [
401- sys.executable,
402- "-m",
403- "wheresmy.cli.import_metadata",
404- "-", # Read from stdin
405- "--library",
406- str(test_library),
407- "--jsonl",
408- ],
409- input=extract_result.stdout,
410- capture_output=True,
411- text=True,
310+ import_result = run_cli(
311+ "wheresmy.cli.import_metadata",
312+ ["-", "--library", str(test_library), "--jsonl"],
313+ stdin_input=extract_result.stdout,
314 )
315
316 assert import_result.returncode == 0
2 """Tests for wheresmy-search CLI tool."""
3
4 import json
5-import subprocess
6-import sys
5 from pathlib import Path
6
7 import pytest
8
9 from wheresmy.storage.combined_system import CompleteImageSystem
10+from wheresmy.tests.cli.cli_runner import run_cli
11
12
13 @pytest.fixture
72
73 def test_basic_text_search(test_library: CompleteImageSystem, tmp_path: Path):
74 """Should find images by text search."""
76- # Run search command
77- result = subprocess.run(
78- [
79- sys.executable,
80- "-m",
81- "wheresmy.cli.search",
82- "sunset",
83- "--library",
84- str(test_library.library_root),
85- ],
86- capture_output=True,
87- text=True,
75+ result = run_cli(
76+ "wheresmy.cli.search",
77+ ["sunset", "--library", str(test_library.library_root)],
78 )
79
80 assert result.returncode == 0
88
89 def test_semantic_search_mode(test_library: CompleteImageSystem, tmp_path: Path):
90 """Should support semantic search mode."""
101- result = subprocess.run(
91+ result = run_cli(
92+ "wheresmy.cli.search",
93 [
103- sys.executable,
104- "-m",
105- "wheresmy.cli.search",
94 "water reflection",
95 "--mode",
96 "semantic",
97 "--library",
98 str(test_library.library_root),
99 ],
112- capture_output=True,
113- text=True,
100 )
101
102 assert result.returncode == 0
106
107 def test_limit_results(test_library: CompleteImageSystem, tmp_path: Path):
108 """Should respect result limit."""
123- result = subprocess.run(
124- [
125- sys.executable,
126- "-m",
127- "wheresmy.cli.search",
128- "ocean", # This appears in our sunset description
129- "--limit",
130- "2",
131- "--library",
132- str(test_library.library_root),
133- ],
134- capture_output=True,
135- text=True,
109+ result = run_cli(
110+ "wheresmy.cli.search",
111+ ["ocean", "--limit", "2", "--library", str(test_library.library_root)],
112 )
113
114 assert result.returncode == 0
118
119 def test_no_results_found(test_library: CompleteImageSystem, tmp_path: Path):
120 """Should return empty array when no matches found."""
145- result = subprocess.run(
146- [
147- sys.executable,
148- "-m",
149- "wheresmy.cli.search",
150- "nonexistent_query_xyz123",
151- "--library",
152- str(test_library.library_root),
153- ],
154- capture_output=True,
155- text=True,
121+ result = run_cli(
122+ "wheresmy.cli.search",
123+ ["nonexistent_query_xyz123", "--library", str(test_library.library_root)],
124 )
125
126 assert result.returncode == 1 # Exit code 1 for no results
130
131 def test_json_output_structure(test_library: CompleteImageSystem, tmp_path: Path):
132 """Should output proper JSON structure."""
165- result = subprocess.run(
166- [
167- sys.executable,
168- "-m",
169- "wheresmy.cli.search",
170- "mountain",
171- "--library",
172- str(test_library.library_root),
173- ],
174- capture_output=True,
175- text=True,
133+ result = run_cli(
134+ "wheresmy.cli.search",
135+ ["mountain", "--library", str(test_library.library_root)],
136 )
137
138 assert result.returncode == 0
149
150 def test_offset_parameter(test_library: CompleteImageSystem, tmp_path: Path):
151 """Should support offset parameter for pagination."""
192- result = subprocess.run(
152+ result = run_cli(
153+ "wheresmy.cli.search",
154 [
194- sys.executable,
195- "-m",
196- "wheresmy.cli.search",
155 "test",
156 "--mode",
157 "text",
162 "--library",
163 str(test_library.library_root),
164 ],
207- capture_output=True,
208- text=True,
165 )
166
167 assert result.returncode in [0, 1] # 0 if results found, 1 if none