personal memory agent
1# SPDX-License-Identifier: AGPL-3.0-only
2# Copyright (c) 2026 sol pbc
3
4from __future__ import annotations
5
6import hashlib
7import sys
8from pathlib import Path
9
10import pytest
11
12from solstone.think import install_models, parakeet_readiness
13from solstone.think.providers import fit_report
14
15
16@pytest.fixture(autouse=True)
17def _skip_provider_cache_seed(monkeypatch: pytest.MonkeyPatch) -> None:
18 monkeypatch.setattr(install_models, "seed_provider_cache", lambda: None)
19
20
21def _fit(severity: fit_report.FitSeverity) -> fit_report.FitReport:
22 return fit_report.FitReport(
23 artifact="test install models",
24 checks=(fit_report.FitCheck("test", severity, f"{severity} detail"),),
25 )
26
27
28def _sha256(data: bytes) -> str:
29 return hashlib.sha256(data).hexdigest()
30
31
32def _write_model_files(base_dir: Path, relative_paths: tuple[str, ...]) -> None:
33 for relative_path in relative_paths:
34 target = base_dir / relative_path
35 target.parent.mkdir(parents=True, exist_ok=True)
36 target.write_bytes(b"ok")
37
38
39@pytest.mark.parametrize(
40 ("flag_value", "env_value", "os_name", "arch", "expected"),
41 [
42 ("cpu", None, "linux", "x86_64", "cpu"),
43 ("cpu", "cuda", "linux", "x86_64", "cpu"),
44 ("auto", "cpu", "linux", "x86_64", "cpu"),
45 ("auto", "cuda", "linux", "x86_64", "cuda"),
46 ("auto", None, "linux", "aarch64", "cpu"),
47 ("cpu", None, "linux", "aarch64", "cpu"),
48 ("auto", None, "darwin", "arm64", "coreml"),
49 ("auto", None, "windows", "amd64", None),
50 ],
51)
52def test_resolve_variant_precedence(
53 monkeypatch: pytest.MonkeyPatch,
54 flag_value: str,
55 env_value: str | None,
56 os_name: str,
57 arch: str,
58 expected: str | None,
59):
60 monkeypatch.setattr(install_models, "_detect_linux_variant", lambda: "cpu")
61
62 assert (
63 install_models._resolve_variant(flag_value, env_value, os_name, arch)
64 == expected
65 )
66
67
68def test_resolve_variant_autodetects_linux_gpu(monkeypatch: pytest.MonkeyPatch):
69 monkeypatch.setattr(install_models, "_detect_linux_variant", lambda: "cuda")
70
71 assert install_models._resolve_variant("auto", None, "linux", "x86_64") == "cuda"
72
73
74def test_resolve_variant_rejects_invalid_env_value():
75 with pytest.raises(SystemExit, match="invalid JOURNAL_VARIANT='bogus'"):
76 install_models._resolve_variant("auto", "bogus", "linux", "x86_64")
77
78
79def test_resolve_variant_rejects_incompatible_explicit_variant():
80 with pytest.raises(SystemExit, match="variant 'coreml' not supported on linux"):
81 install_models._resolve_variant("coreml", None, "linux", "x86_64")
82 with pytest.raises(SystemExit, match="variant 'cpu' not supported on darwin"):
83 install_models._resolve_variant("cpu", None, "darwin", "arm64")
84 with pytest.raises(SystemExit, match="variant 'cuda' not supported"):
85 install_models._resolve_variant("cuda", None, "linux", "aarch64")
86
87
88def test_verify_bundled_assets_returns_when_hashes_match(
89 monkeypatch: pytest.MonkeyPatch,
90 tmp_path: Path,
91):
92 wespeaker = tmp_path / "wespeaker.onnx"
93 pyannote = tmp_path / "pyannote.onnx"
94 wespeaker.write_bytes(b"wespeaker")
95 pyannote.write_bytes(b"pyannote")
96 monkeypatch.setattr(install_models, "resolve_wespeaker_model", lambda: wespeaker)
97 monkeypatch.setattr(install_models, "WESPEAKER_MODEL_SHA256", _sha256(b"wespeaker"))
98 monkeypatch.setattr(
99 install_models, "resolve_pyannote_segmentation_model", lambda: pyannote
100 )
101 monkeypatch.setattr(
102 install_models,
103 "PYANNOTE_OVERLAP_MODEL_SHA256",
104 _sha256(b"pyannote"),
105 )
106
107 install_models._verify_bundled_assets()
108
109
110def test_verify_bundled_assets_reports_mutated_asset(
111 monkeypatch: pytest.MonkeyPatch,
112 tmp_path: Path,
113):
114 wespeaker = tmp_path / "wespeaker.onnx"
115 pyannote = tmp_path / "pyannote.onnx"
116 wespeaker.write_bytes(b"mutated")
117 pyannote.write_bytes(b"pyannote")
118 expected = _sha256(b"original")
119 actual = _sha256(b"mutated")
120 monkeypatch.setattr(install_models, "resolve_wespeaker_model", lambda: wespeaker)
121 monkeypatch.setattr(install_models, "WESPEAKER_MODEL_SHA256", expected)
122 monkeypatch.setattr(
123 install_models, "resolve_pyannote_segmentation_model", lambda: pyannote
124 )
125 monkeypatch.setattr(
126 install_models,
127 "PYANNOTE_OVERLAP_MODEL_SHA256",
128 _sha256(b"pyannote"),
129 )
130
131 with pytest.raises(RuntimeError) as exc_info:
132 install_models._verify_bundled_assets()
133
134 message = str(exc_info.value)
135 assert f"bundled asset SHA mismatch: {wespeaker}" in message
136 assert f"expected: {expected}" in message
137 assert f"actual: {actual}" in message
138
139
140def test_verify_returns_true_when_files_at_fluidaudio_sibling(tmp_path: Path):
141 cache_dir = tmp_path / "models"
142 repo_dir = tmp_path / parakeet_readiness.MAC_FLUIDAUDIO_REPO_NAME
143 _write_model_files(repo_dir, parakeet_readiness.MAC_MODEL_FILES)
144
145 assert parakeet_readiness._verify_mac_cache(cache_dir) is True
146
147
148def test_verify_returns_false_when_sibling_empty(tmp_path: Path):
149 cache_dir = tmp_path / "models"
150 cache_dir.mkdir()
151 (tmp_path / parakeet_readiness.MAC_FLUIDAUDIO_REPO_NAME).mkdir()
152
153 assert parakeet_readiness._verify_mac_cache(cache_dir) is False
154
155
156def test_verify_returns_false_when_files_at_literal_path(tmp_path: Path):
157 cache_dir = tmp_path / "models"
158 _write_model_files(cache_dir, parakeet_readiness.MAC_MODEL_FILES)
159
160 assert parakeet_readiness._verify_mac_cache(cache_dir) is False
161
162
163def test_helper_path_env_override_wins(
164 monkeypatch: pytest.MonkeyPatch,
165 tmp_path: Path,
166):
167 fake = tmp_path / "custom" / "parakeet-helper"
168 monkeypatch.setenv(install_models.HELPER_ENV_KEY, str(fake))
169 monkeypatch.setattr(install_models, "_package_root", lambda: tmp_path)
170 assert install_models._helper_path() == fake.expanduser().resolve()
171
172
173def test_helper_path_prefers_bundled_bin(
174 monkeypatch: pytest.MonkeyPatch,
175 tmp_path: Path,
176):
177 monkeypatch.delenv(install_models.HELPER_ENV_KEY, raising=False)
178 monkeypatch.setattr(install_models, "_package_root", lambda: tmp_path)
179 bundled = (
180 tmp_path
181 / "observe"
182 / "transcribe"
183 / "parakeet_helper"
184 / "_bin"
185 / "parakeet-helper"
186 )
187 bundled.parent.mkdir(parents=True)
188 bundled.write_text("")
189 assert install_models._helper_path() == bundled
190
191
192def test_helper_path_falls_back_to_swift_build(
193 monkeypatch: pytest.MonkeyPatch,
194 tmp_path: Path,
195):
196 monkeypatch.delenv(install_models.HELPER_ENV_KEY, raising=False)
197 monkeypatch.setattr(install_models, "_package_root", lambda: tmp_path)
198 expected = (
199 tmp_path
200 / "observe"
201 / "transcribe"
202 / "parakeet_helper"
203 / ".build"
204 / "release"
205 / "parakeet-helper"
206 )
207 assert install_models._helper_path() == expected
208
209
210def _ready_paths(tmp_path: Path) -> dict[str, Path]:
211 return {
212 "binary_cpu": tmp_path / "bin" / "cpu" / "parakeet-server",
213 "binary_vulkan": tmp_path / "bin" / "vulkan" / "parakeet-server",
214 "model": tmp_path / "models" / "model.gguf",
215 }
216
217
218def _prepare_check_main(monkeypatch: pytest.MonkeyPatch) -> None:
219 monkeypatch.setattr(sys, "argv", ["sol install-models", "--check"])
220 monkeypatch.delenv(install_models.JOURNAL_VARIANT_ENV, raising=False)
221 monkeypatch.setattr(install_models, "_platform_info", lambda: ("linux", "x86_64"))
222 monkeypatch.setattr(install_models, "_detect_linux_variant", lambda: "cpu")
223 monkeypatch.setattr(install_models, "_verify_bundled_assets", lambda: None)
224 monkeypatch.setattr(
225 install_models, "_install_rerank_model", lambda *, check, force: 0
226 )
227 monkeypatch.setattr(
228 install_models, "_install_ced_assets", lambda *, check, force: 0
229 )
230 monkeypatch.setattr(
231 install_models, "_install_rfdetr_model", lambda *, check, force: 0
232 )
233
234
235def test_main_check_missing_cpp_artifacts_returns_nonzero(
236 monkeypatch: pytest.MonkeyPatch,
237 capsys: pytest.CaptureFixture[str],
238):
239 _prepare_check_main(monkeypatch)
240 monkeypatch.setattr(
241 install_models,
242 "_check_linux_cpp_ready",
243 lambda: (_ for _ in ()).throw(RuntimeError("model missing")),
244 )
245
246 assert install_models.main() == 1
247 assert "model missing" in capsys.readouterr().err
248
249
250def test_main_check_ready_cpp_artifacts_returns_zero(
251 monkeypatch: pytest.MonkeyPatch,
252 tmp_path: Path,
253 capsys: pytest.CaptureFixture[str],
254):
255 paths = _ready_paths(tmp_path)
256 _prepare_check_main(monkeypatch)
257 monkeypatch.setattr(install_models, "_check_linux_cpp_ready", lambda: paths)
258
259 assert install_models.main() == 0
260 assert f"model ready: {paths['model']}" in capsys.readouterr().out
261
262
263def test_main_rerank_failure_short_circuits_before_parakeet(
264 monkeypatch: pytest.MonkeyPatch,
265):
266 calls = []
267 monkeypatch.setattr(sys, "argv", ["sol install-models", "--check"])
268 monkeypatch.delenv(install_models.JOURNAL_VARIANT_ENV, raising=False)
269 monkeypatch.setattr(install_models, "_platform_info", lambda: ("linux", "x86_64"))
270 monkeypatch.setattr(install_models, "_detect_linux_variant", lambda: "cpu")
271 monkeypatch.setattr(install_models, "_verify_bundled_assets", lambda: None)
272 monkeypatch.setattr(
273 install_models,
274 "_install_rerank_model",
275 lambda *, check, force: calls.append((check, force)) or 7,
276 )
277 monkeypatch.setattr(
278 install_models,
279 "_install_ced_assets",
280 lambda *, check, force: pytest.fail("ced install should not start"),
281 )
282 monkeypatch.setattr(
283 install_models,
284 "_install_rfdetr_model",
285 lambda *, check, force: pytest.fail("rf-detr check should not start"),
286 )
287 monkeypatch.setattr(
288 install_models,
289 "_check_linux_cpp_ready",
290 lambda: pytest.fail("parakeet check should not start"),
291 )
292 monkeypatch.setattr(
293 install_models,
294 "_install_models",
295 lambda *_args, **_kwargs: pytest.fail("parakeet install should not start"),
296 )
297
298 assert install_models.main() == 7
299 assert calls == [(True, False)]
300
301
302def test_main_runs_ced_after_rerank_before_parakeet(
303 monkeypatch: pytest.MonkeyPatch,
304 tmp_path: Path,
305):
306 calls = []
307 paths = _ready_paths(tmp_path)
308 monkeypatch.setattr(sys, "argv", ["sol install-models", "--check"])
309 monkeypatch.delenv(install_models.JOURNAL_VARIANT_ENV, raising=False)
310 monkeypatch.setattr(install_models, "_platform_info", lambda: ("linux", "x86_64"))
311 monkeypatch.setattr(install_models, "_detect_linux_variant", lambda: "cpu")
312 monkeypatch.setattr(install_models, "_verify_bundled_assets", lambda: None)
313 monkeypatch.setattr(
314 install_models,
315 "_install_rerank_model",
316 lambda *, check, force: calls.append(("rerank", check, force)) or 0,
317 )
318 monkeypatch.setattr(
319 install_models,
320 "_install_ced_assets",
321 lambda *, check, force: calls.append(("ced", check, force)) or 0,
322 )
323 monkeypatch.setattr(
324 install_models,
325 "_check_linux_cpp_ready",
326 lambda: calls.append(("parakeet",)) or paths,
327 )
328 monkeypatch.setattr(
329 install_models, "_install_rfdetr_model", lambda *, check, force: 0
330 )
331
332 assert install_models.main() == 0
333 assert calls == [
334 ("rerank", True, False),
335 ("ced", True, False),
336 ("parakeet",),
337 ]
338
339
340def test_main_ced_failure_short_circuits_before_parakeet(
341 monkeypatch: pytest.MonkeyPatch,
342):
343 monkeypatch.setattr(sys, "argv", ["sol install-models", "--check"])
344 monkeypatch.delenv(install_models.JOURNAL_VARIANT_ENV, raising=False)
345 monkeypatch.setattr(install_models, "_platform_info", lambda: ("linux", "x86_64"))
346 monkeypatch.setattr(install_models, "_detect_linux_variant", lambda: "cpu")
347 monkeypatch.setattr(install_models, "_verify_bundled_assets", lambda: None)
348 monkeypatch.setattr(
349 install_models, "_install_rerank_model", lambda *, check, force: 0
350 )
351 monkeypatch.setattr(
352 install_models, "_install_ced_assets", lambda *, check, force: 8
353 )
354 monkeypatch.setattr(
355 install_models,
356 "_check_linux_cpp_ready",
357 lambda: pytest.fail("parakeet check should not start"),
358 )
359
360 assert install_models.main() == 8
361
362
363def test_install_ced_assets_unsupported_platform_prints_skip(
364 monkeypatch: pytest.MonkeyPatch,
365 capsys: pytest.CaptureFixture[str],
366):
367 from solstone.think.providers import ced_install
368
369 monkeypatch.setattr(install_models, "_platform_info", lambda: ("windows", "amd64"))
370 monkeypatch.setattr(
371 ced_install, "ced_engine_artifact_key", lambda os_name=None, arch=None: None
372 )
373 monkeypatch.setattr(
374 ced_install,
375 "install_ced_assets",
376 lambda **_kwargs: pytest.fail("ced install should not start"),
377 )
378
379 assert install_models._install_ced_assets(check=False, force=False) == 0
380 assert (
381 "ced install: unsupported platform windows/amd64; skipping ced sound-tag assets"
382 ) in capsys.readouterr().out
383
384
385def test_install_ced_assets_threads_check_and_force(
386 monkeypatch: pytest.MonkeyPatch,
387 tmp_path: Path,
388 capsys: pytest.CaptureFixture[str],
389):
390 from solstone.think.providers import ced_install
391
392 calls = []
393 record = object()
394 monkeypatch.setattr(install_models, "_platform_info", lambda: ("linux", "x86_64"))
395 monkeypatch.setattr(
396 ced_install,
397 "ced_engine_artifact_key",
398 lambda os_name=None, arch=None: "linux-cpu-x64",
399 )
400 monkeypatch.setattr(ced_install, "model_path", lambda: tmp_path / "ced.gguf")
401 monkeypatch.setattr(
402 ced_install, "check_ced_assets", lambda: calls.append(("check",)) or record
403 )
404 monkeypatch.setattr(
405 ced_install,
406 "install_ced_assets",
407 lambda *, force: calls.append(("install", force)) or record,
408 )
409
410 assert install_models._install_ced_assets(check=True, force=False) == 0
411 assert f"model ready: {tmp_path / 'ced.gguf'}" in capsys.readouterr().out
412
413 assert install_models._install_ced_assets(check=False, force=True) == 0
414 stdout = capsys.readouterr().out
415 assert install_models.CED_DOWNLOAD_DISCLOSURE in stdout
416 assert f"model ready: {tmp_path / 'ced.gguf'}" in stdout
417 assert calls == [("check",), ("install", True)]
418
419
420def test_install_ced_assets_downloads_when_missing(
421 monkeypatch: pytest.MonkeyPatch,
422 tmp_path: Path,
423 capsys: pytest.CaptureFixture[str],
424):
425 from solstone.think.providers import ced_install
426
427 calls = []
428 record = object()
429 monkeypatch.setattr(install_models, "_platform_info", lambda: ("linux", "x86_64"))
430 monkeypatch.setattr(
431 ced_install,
432 "ced_engine_artifact_key",
433 lambda os_name=None, arch=None: "linux-cpu-x64",
434 )
435 monkeypatch.setattr(ced_install, "model_path", lambda: tmp_path / "ced.gguf")
436
437 def missing_check():
438 calls.append(("check",))
439 raise ced_install.CedInstallError("sidecar_missing", "missing")
440
441 monkeypatch.setattr(ced_install, "check_ced_assets", missing_check)
442 monkeypatch.setattr(
443 ced_install,
444 "install_ced_assets",
445 lambda *, force: calls.append(("install", force)) or record,
446 )
447
448 assert install_models._install_ced_assets(check=False, force=False) == 0
449 stdout = capsys.readouterr().out
450 assert install_models.CED_DOWNLOAD_DISCLOSURE in stdout
451 assert f"model ready: {tmp_path / 'ced.gguf'}" in stdout
452 assert calls == [("check",), ("install", False)]
453
454
455def test_main_rfdetr_failure_short_circuits_before_parakeet(
456 monkeypatch: pytest.MonkeyPatch,
457):
458 calls = []
459 monkeypatch.setattr(sys, "argv", ["sol install-models", "--check"])
460 monkeypatch.delenv(install_models.JOURNAL_VARIANT_ENV, raising=False)
461 monkeypatch.setattr(install_models, "_platform_info", lambda: ("linux", "x86_64"))
462 monkeypatch.setattr(install_models, "_detect_linux_variant", lambda: "cpu")
463 monkeypatch.setattr(install_models, "_verify_bundled_assets", lambda: None)
464 monkeypatch.setattr(
465 install_models, "_install_rerank_model", lambda *, check, force: 0
466 )
467 monkeypatch.setattr(
468 install_models, "_install_ced_assets", lambda *, check, force: 0
469 )
470 monkeypatch.setattr(
471 install_models,
472 "_install_rfdetr_model",
473 lambda *, check, force: calls.append((check, force)) or 7,
474 )
475 monkeypatch.setattr(
476 install_models,
477 "_check_linux_cpp_ready",
478 lambda: pytest.fail("parakeet check should not start"),
479 )
480 monkeypatch.setattr(
481 install_models,
482 "_install_models",
483 lambda *_args, **_kwargs: pytest.fail("parakeet install should not start"),
484 )
485
486 assert install_models.main() == 7
487 assert calls == [(True, False)]
488
489
490def test_main_rfdetr_success_continues_to_parakeet(
491 monkeypatch: pytest.MonkeyPatch,
492 tmp_path: Path,
493):
494 calls = []
495 paths = _ready_paths(tmp_path)
496 monkeypatch.setattr(sys, "argv", ["sol install-models", "--check"])
497 monkeypatch.delenv(install_models.JOURNAL_VARIANT_ENV, raising=False)
498 monkeypatch.setattr(install_models, "_platform_info", lambda: ("linux", "x86_64"))
499 monkeypatch.setattr(install_models, "_detect_linux_variant", lambda: "cpu")
500 monkeypatch.setattr(install_models, "_verify_bundled_assets", lambda: None)
501 monkeypatch.setattr(
502 install_models,
503 "_install_rerank_model",
504 lambda *, check, force: calls.append(("rerank", check, force)) or 0,
505 )
506 monkeypatch.setattr(
507 install_models, "_install_ced_assets", lambda *, check, force: 0
508 )
509 monkeypatch.setattr(
510 install_models,
511 "_install_rfdetr_model",
512 lambda *, check, force: calls.append(("rfdetr", check, force)) or 0,
513 )
514
515 def ready_paths() -> dict[str, Path]:
516 calls.append(("parakeet", True, False))
517 return paths
518
519 monkeypatch.setattr(install_models, "_check_linux_cpp_ready", ready_paths)
520
521 assert install_models.main() == 0
522 assert calls == [
523 ("rerank", True, False),
524 ("rfdetr", True, False),
525 ("parakeet", True, False),
526 ]
527
528
529def test_run_mac_helper_soft_fails_on_packaged_install(
530 monkeypatch: pytest.MonkeyPatch,
531 tmp_path: Path,
532 capsys: pytest.CaptureFixture[str],
533):
534 cache_dir = tmp_path / "cache"
535 sentinel_path = tmp_path / "sentinel.json"
536 missing_helper = tmp_path / "missing" / "parakeet-helper"
537
538 monkeypatch.delenv(install_models.HELPER_ENV_KEY, raising=False)
539 monkeypatch.setattr(install_models, "_helper_path", lambda: missing_helper)
540 monkeypatch.setattr(install_models, "is_packaged_install", lambda: True)
541 monkeypatch.setattr(install_models, "_sentinel_path", lambda variant: sentinel_path)
542 monkeypatch.setattr(install_models, "_cache_dir", lambda variant: cache_dir)
543
544 assert install_models._run_mac_helper(cache_dir) is None
545 stderr = capsys.readouterr().err
546 assert "Apple Silicon Macs running macOS 14" in stderr
547 assert "Intel Mac" in stderr
548 assert "source checkout" in stderr
549
550 assert install_models._install_models("darwin", "arm64", "coreml") == 0
551
552
553def test_install_models_linux_routes_through_parakeet_provider(
554 monkeypatch: pytest.MonkeyPatch,
555 tmp_path: Path,
556 capsys: pytest.CaptureFixture[str],
557):
558 from solstone.think.providers import parakeet_install
559
560 calls = []
561 paths = _ready_paths(tmp_path)
562 journal = tmp_path / "journal"
563 (journal / "config").mkdir(parents=True)
564 (journal / "config" / "journal.json").write_text(
565 '{"providers": {}}\n',
566 encoding="utf-8",
567 )
568 monkeypatch.setenv("SOLSTONE_JOURNAL", str(journal))
569 import solstone.think.utils as think_utils
570
571 think_utils._journal_path_cache = None
572
573 monkeypatch.setattr(
574 parakeet_install,
575 "install_parakeet",
576 lambda **_kwargs: calls.append("install"),
577 )
578 monkeypatch.setattr(install_models, "_check_linux_cpp_ready", lambda: paths)
579
580 assert install_models._install_models("linux", "x86_64", "cpu") == 0
581 assert calls == ["install"]
582 assert f"model ready: {paths['model']}" in capsys.readouterr().out
583
584
585def test_main_force_reinstalls_linux_cpp(
586 monkeypatch: pytest.MonkeyPatch,
587 tmp_path: Path,
588):
589 calls = []
590 monkeypatch.setattr(sys, "argv", ["sol install-models", "--force"])
591 monkeypatch.setattr(install_models, "_platform_info", lambda: ("linux", "x86_64"))
592 monkeypatch.setattr(install_models, "_detect_linux_variant", lambda: "cpu")
593 monkeypatch.setattr(install_models, "_verify_bundled_assets", lambda: None)
594 monkeypatch.setattr(
595 install_models, "_install_rerank_model", lambda *, check, force: 0
596 )
597 monkeypatch.setattr(
598 install_models, "_install_ced_assets", lambda *, check, force: 0
599 )
600 monkeypatch.setattr(
601 install_models, "_install_rfdetr_model", lambda *, check, force: 0
602 )
603 monkeypatch.setattr(
604 install_models, "_check_linux_cpp_ready", lambda: _ready_paths(tmp_path)
605 )
606 monkeypatch.setattr(fit_report, "build_parakeet_fit_report", lambda: _fit("ok"))
607 monkeypatch.setattr(
608 install_models,
609 "_install_models",
610 lambda os_name, arch, variant, **kwargs: (
611 calls.append((os_name, arch, variant, kwargs)) or 0
612 ),
613 )
614
615 assert install_models.main() == 0
616 assert calls == [("linux", "x86_64", "cpu", {"force": True})]
617
618
619def test_main_linux_blocks_before_install_models(
620 monkeypatch: pytest.MonkeyPatch,
621 capsys: pytest.CaptureFixture[str],
622):
623 monkeypatch.setattr(sys, "argv", ["sol install-models", "--force"])
624 monkeypatch.setattr(install_models, "_platform_info", lambda: ("linux", "x86_64"))
625 monkeypatch.setattr(install_models, "_detect_linux_variant", lambda: "cpu")
626 monkeypatch.setattr(install_models, "_verify_bundled_assets", lambda: None)
627 monkeypatch.setattr(
628 install_models, "_install_rerank_model", lambda *, check, force: 0
629 )
630 monkeypatch.setattr(
631 install_models, "_install_ced_assets", lambda *, check, force: 0
632 )
633 monkeypatch.setattr(
634 install_models, "_install_rfdetr_model", lambda *, check, force: 0
635 )
636 monkeypatch.setattr(
637 fit_report, "build_parakeet_fit_report", lambda: _fit("blocked")
638 )
639 monkeypatch.setattr(
640 install_models,
641 "_install_models",
642 lambda *_args, **_kwargs: pytest.fail("install should not start"),
643 )
644
645 assert install_models.main() == 1
646 assert "blocked detail" in capsys.readouterr().err
647
648
649def test_main_linux_warning_continues_to_install_models(
650 monkeypatch: pytest.MonkeyPatch,
651 capsys: pytest.CaptureFixture[str],
652):
653 calls = []
654 monkeypatch.setattr(sys, "argv", ["sol install-models", "--force"])
655 monkeypatch.setattr(install_models, "_platform_info", lambda: ("linux", "x86_64"))
656 monkeypatch.setattr(install_models, "_detect_linux_variant", lambda: "cpu")
657 monkeypatch.setattr(install_models, "_verify_bundled_assets", lambda: None)
658 monkeypatch.setattr(
659 install_models, "_install_rerank_model", lambda *, check, force: 0
660 )
661 monkeypatch.setattr(
662 install_models, "_install_ced_assets", lambda *, check, force: 0
663 )
664 monkeypatch.setattr(
665 install_models, "_install_rfdetr_model", lambda *, check, force: 0
666 )
667 monkeypatch.setattr(
668 fit_report, "build_parakeet_fit_report", lambda: _fit("warning")
669 )
670 monkeypatch.setattr(
671 install_models,
672 "_install_models",
673 lambda os_name, arch, variant, **kwargs: (
674 calls.append((os_name, arch, variant, kwargs)) or 0
675 ),
676 )
677
678 assert install_models.main() == 0
679 assert calls == [("linux", "x86_64", "cpu", {"force": True})]
680 assert "warning detail" in capsys.readouterr().err
681
682
683def test_main_coreml_blocks_before_install_models(
684 monkeypatch: pytest.MonkeyPatch,
685 capsys: pytest.CaptureFixture[str],
686):
687 monkeypatch.setattr(sys, "argv", ["sol install-models", "--force"])
688 monkeypatch.setattr(install_models, "_platform_info", lambda: ("darwin", "arm64"))
689 monkeypatch.setattr(install_models, "_verify_bundled_assets", lambda: None)
690 monkeypatch.setattr(
691 install_models, "_install_rerank_model", lambda *, check, force: 0
692 )
693 monkeypatch.setattr(
694 install_models, "_install_ced_assets", lambda *, check, force: 0
695 )
696 monkeypatch.setattr(
697 install_models, "_install_rfdetr_model", lambda *, check, force: 0
698 )
699 monkeypatch.setattr(
700 fit_report,
701 "build_coreml_parakeet_fit_report",
702 lambda os_name, arch, cache_dir: _fit("blocked"),
703 )
704 monkeypatch.setattr(
705 install_models,
706 "_install_models",
707 lambda *_args, **_kwargs: pytest.fail("install should not start"),
708 )
709
710 assert install_models.main() == 1
711 assert "blocked detail" in capsys.readouterr().err
712
713
714def test_main_skips_install_when_linux_cpp_ready(
715 monkeypatch: pytest.MonkeyPatch,
716 tmp_path: Path,
717):
718 monkeypatch.setattr(sys, "argv", ["sol install-models"])
719 monkeypatch.setattr(install_models, "_platform_info", lambda: ("linux", "x86_64"))
720 monkeypatch.setattr(install_models, "_detect_linux_variant", lambda: "cpu")
721 monkeypatch.setattr(install_models, "_verify_bundled_assets", lambda: None)
722 monkeypatch.setattr(
723 install_models, "_install_rerank_model", lambda *, check, force: 0
724 )
725 monkeypatch.setattr(
726 install_models, "_install_ced_assets", lambda *, check, force: 0
727 )
728 monkeypatch.setattr(
729 install_models, "_install_rfdetr_model", lambda *, check, force: 0
730 )
731 monkeypatch.setattr(
732 install_models, "_check_linux_cpp_ready", lambda: _ready_paths(tmp_path)
733 )
734 monkeypatch.setattr(
735 install_models,
736 "_install_models",
737 lambda *_args: pytest.fail("ready artifacts should not reinstall"),
738 )
739
740 assert install_models.main() == 0