personal memory agent
0

Configure Feed

Select the types of activity you want to include in your feed.

solstone / tests / test_install_models.py
26 kB 740 lines
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