"""Tests for ``jarvis model pull`` multi-engine support.""" from __future__ import annotations from unittest import mock from click.testing import CliRunner from rich.console import Console from openjarvis.cli.model import ollama_pull class TestOllamaPull: """Test the extracted ollama_pull helper.""" def test_ollama_pull_success(self) -> None: import io console = Console(file=io.StringIO()) mock_lines = [ '{"status": "pulling manifest"}', '{"status": "downloading", "total": 100, "completed": 100}', '{"status": "success"}', ] mock_resp = mock.MagicMock() mock_resp.raise_for_status = mock.MagicMock() mock_resp.iter_lines.return_value = iter(mock_lines) mock_resp.__enter__ = mock.MagicMock(return_value=mock_resp) mock_resp.__exit__ = mock.MagicMock(return_value=False) with mock.patch("httpx.stream", return_value=mock_resp): result = ollama_pull("http://localhost:11434", "qwen3.5:2b", console) assert result is True def test_ollama_pull_connect_error(self) -> None: import io import httpx console = Console(file=io.StringIO()) with mock.patch("httpx.stream", side_effect=httpx.ConnectError("refused")): result = ollama_pull("http://localhost:11434", "qwen3.5:2b", console) assert result is False class TestPullCliMultiEngine: """Test the pull CLI command dispatches to correct engine.""" def test_pull_llamacpp_uses_huggingface_cli(self) -> None: from openjarvis.cli import cli runner = CliRunner() with ( mock.patch("openjarvis.cli.model.load_config") as mock_cfg, mock.patch("subprocess.run") as mock_run, ): mock_cfg.return_value.engine.default = "llamacpp" mock_cfg.return_value.engine.ollama_host = None mock_run.return_value = mock.MagicMock(returncode=0) result = runner.invoke( cli, ["model", "pull", "qwen3.5:9b", "--engine", "llamacpp"] ) assert result.exit_code == 0 mock_run.assert_called_once() call_args = mock_run.call_args[0][0] assert "huggingface-cli" in call_args assert "qwen3.5-9b-q4_k_m.gguf" in call_args def test_pull_mlx_uses_huggingface_cli(self) -> None: from openjarvis.cli import cli runner = CliRunner() with ( mock.patch("openjarvis.cli.model.load_config") as mock_cfg, mock.patch("subprocess.run") as mock_run, ): mock_cfg.return_value.engine.default = "mlx" mock_cfg.return_value.engine.ollama_host = None mock_run.return_value = mock.MagicMock(returncode=0) result = runner.invoke( cli, ["model", "pull", "qwen3.5:9b", "--engine", "mlx"] ) assert result.exit_code == 0 mock_run.assert_called_once() call_args = mock_run.call_args[0][0] assert "huggingface-cli" in call_args assert "mlx-community/Qwen3.5-9B-MLX-4bit" in call_args def test_pull_llamacpp_huggingface_cli_not_found(self) -> None: from openjarvis.cli import cli runner = CliRunner() with ( mock.patch("openjarvis.cli.model.load_config") as mock_cfg, mock.patch("subprocess.run", side_effect=FileNotFoundError), ): mock_cfg.return_value.engine.default = "llamacpp" mock_cfg.return_value.engine.ollama_host = None result = runner.invoke( cli, ["model", "pull", "qwen3.5:9b", "--engine", "llamacpp"] ) assert result.exit_code != 0 assert "huggingface_hub" in result.output or "pip install" in result.output