test: add unit tests for wizard model-discovery helpers
Cover _fetch_local_models (LM Studio and Ollama parsing, error paths, missing base_url), _fetch_lmstudio_available_models (happy path and errors), and _load_lmstudio_model (success, API failure, exception). All mocked via monkeypatch/MagicMock — no real HTTP calls. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -1,4 +1,6 @@
|
||||
"""Tests for setup wizard personalization helpers."""
|
||||
"""Tests for setup wizard personalization and model-discovery helpers."""
|
||||
|
||||
from unittest.mock import MagicMock
|
||||
|
||||
import pytest
|
||||
|
||||
@@ -90,3 +92,92 @@ def test_suggest_plugins_multiple_categories(monkeypatch):
|
||||
combined = " ".join(str(p) for p in panels)
|
||||
assert "email" in combined
|
||||
assert "ssh_tool" in combined
|
||||
|
||||
|
||||
# ── _fetch_local_models ────────────────────────────────────────────────────────
|
||||
|
||||
def test_fetch_local_models_lmstudio_returns_model_ids(monkeypatch):
|
||||
import pyra.setup.wizard as wiz
|
||||
mock_resp = MagicMock()
|
||||
mock_resp.json.return_value = {"data": [{"id": "gemma-4b"}, {"id": "llama3"}]}
|
||||
mock_resp.raise_for_status = lambda: None
|
||||
monkeypatch.setattr(wiz.httpx, "get", lambda *a, **kw: mock_resp)
|
||||
from pyra.setup.providers import get_provider
|
||||
assert wiz._fetch_local_models(get_provider("lmstudio")) == ["gemma-4b", "llama3"]
|
||||
|
||||
|
||||
def test_fetch_local_models_ollama_returns_model_names(monkeypatch):
|
||||
import pyra.setup.wizard as wiz
|
||||
mock_resp = MagicMock()
|
||||
mock_resp.json.return_value = {"models": [{"name": "llama3:latest"}, {"name": "mistral"}]}
|
||||
mock_resp.raise_for_status = lambda: None
|
||||
monkeypatch.setattr(wiz.httpx, "get", lambda *a, **kw: mock_resp)
|
||||
from pyra.setup.providers import get_provider
|
||||
assert wiz._fetch_local_models(get_provider("ollama")) == ["llama3:latest", "mistral"]
|
||||
|
||||
|
||||
def test_fetch_local_models_returns_empty_on_connection_error(monkeypatch):
|
||||
import pyra.setup.wizard as wiz
|
||||
monkeypatch.setattr(wiz.httpx, "get", MagicMock(side_effect=Exception("conn refused")))
|
||||
from pyra.setup.providers import get_provider
|
||||
assert wiz._fetch_local_models(get_provider("lmstudio")) == []
|
||||
|
||||
|
||||
def test_fetch_local_models_returns_empty_when_no_base_url():
|
||||
import pyra.setup.wizard as wiz
|
||||
from pyra.setup.providers import Provider
|
||||
provider = Provider(
|
||||
id="test", display_name="Test", requires_key=False,
|
||||
default_model="x", litellm_prefix="openai/", group="Local",
|
||||
)
|
||||
assert wiz._fetch_local_models(provider) == []
|
||||
|
||||
|
||||
# ── _fetch_lmstudio_available_models ──────────────────────────────────────────
|
||||
|
||||
def test_fetch_lmstudio_available_models_returns_ids(monkeypatch):
|
||||
import pyra.setup.wizard as wiz
|
||||
mock_resp = MagicMock()
|
||||
mock_resp.json.return_value = {"data": [{"id": "model-a"}, {"id": "model-b"}]}
|
||||
mock_resp.raise_for_status = lambda: None
|
||||
monkeypatch.setattr(wiz.httpx, "get", lambda *a, **kw: mock_resp)
|
||||
assert wiz._fetch_lmstudio_available_models() == ["model-a", "model-b"]
|
||||
|
||||
|
||||
def test_fetch_lmstudio_available_models_returns_empty_on_error(monkeypatch):
|
||||
import pyra.setup.wizard as wiz
|
||||
monkeypatch.setattr(wiz.httpx, "get", MagicMock(side_effect=Exception("not found")))
|
||||
assert wiz._fetch_lmstudio_available_models() == []
|
||||
|
||||
|
||||
def test_fetch_lmstudio_available_models_empty_data(monkeypatch):
|
||||
import pyra.setup.wizard as wiz
|
||||
mock_resp = MagicMock()
|
||||
mock_resp.json.return_value = {"data": []}
|
||||
mock_resp.raise_for_status = lambda: None
|
||||
monkeypatch.setattr(wiz.httpx, "get", lambda *a, **kw: mock_resp)
|
||||
assert wiz._fetch_lmstudio_available_models() == []
|
||||
|
||||
|
||||
# ── _load_lmstudio_model ──────────────────────────────────────────────────────
|
||||
|
||||
def test_load_lmstudio_model_returns_true_on_success(monkeypatch):
|
||||
import pyra.setup.wizard as wiz
|
||||
mock_resp = MagicMock()
|
||||
mock_resp.is_success = True
|
||||
monkeypatch.setattr(wiz.httpx, "post", lambda *a, **kw: mock_resp)
|
||||
assert wiz._load_lmstudio_model("gemma-4b") is True
|
||||
|
||||
|
||||
def test_load_lmstudio_model_returns_false_on_api_failure(monkeypatch):
|
||||
import pyra.setup.wizard as wiz
|
||||
mock_resp = MagicMock()
|
||||
mock_resp.is_success = False
|
||||
monkeypatch.setattr(wiz.httpx, "post", lambda *a, **kw: mock_resp)
|
||||
assert wiz._load_lmstudio_model("gemma-4b") is False
|
||||
|
||||
|
||||
def test_load_lmstudio_model_returns_false_on_exception(monkeypatch):
|
||||
import pyra.setup.wizard as wiz
|
||||
monkeypatch.setattr(wiz.httpx, "post", MagicMock(side_effect=Exception("timeout")))
|
||||
assert wiz._load_lmstudio_model("gemma-4b") is False
|
||||
|
||||
Reference in New Issue
Block a user