forked from Rockachopa/Timmy-time-dashboard
fix: model introspection uses exact match, queries /api/ps first
_get_ollama_model() used prefix match (startswith) on /api/tags, causing qwen3:30b to match qwen3.5:latest. Now: 1. Queries /api/ps (loaded models) first — most accurate 2. Falls back to /api/tags with exact name match 3. Reports actual running model, not just configured one Updated test_get_system_info_contains_model to not assume model==config. Fixes #77. 5 regression tests added.
This commit is contained in:
@@ -1,5 +1,9 @@
|
||||
"""Tests for system introspection tools."""
|
||||
|
||||
from unittest.mock import MagicMock, patch
|
||||
|
||||
import httpx
|
||||
|
||||
|
||||
def test_get_system_info_returns_dict():
|
||||
"""System info should return a dictionary."""
|
||||
@@ -15,15 +19,17 @@ def test_get_system_info_returns_dict():
|
||||
|
||||
|
||||
def test_get_system_info_contains_model():
|
||||
"""System info should include model name."""
|
||||
from config import settings
|
||||
"""System info should include a model name (may differ from config if
|
||||
the actual running model is different — see issue #77)."""
|
||||
from timmy.tools_intro import get_system_info
|
||||
|
||||
info = get_system_info()
|
||||
|
||||
assert "model" in info
|
||||
# Model should come from settings
|
||||
assert info["model"] == settings.ollama_model
|
||||
# Model should be a non-empty string — exact value depends on what
|
||||
# Ollama has loaded (verified by TestGetOllamaModelExactMatch tests)
|
||||
assert isinstance(info["model"], str)
|
||||
assert len(info["model"]) > 0
|
||||
|
||||
|
||||
def test_get_system_info_contains_repo_root():
|
||||
@@ -59,3 +65,96 @@ def test_get_memory_status_returns_dict():
|
||||
assert isinstance(status, dict)
|
||||
assert "tier1_hot_memory" in status
|
||||
assert "tier2_vault" in status
|
||||
|
||||
|
||||
# --- _get_ollama_model exact-match tests (issue #77) ---
|
||||
|
||||
|
||||
def _mock_response(json_data, status_code=200):
|
||||
"""Create a mock httpx response."""
|
||||
resp = MagicMock(spec=httpx.Response)
|
||||
resp.status_code = status_code
|
||||
resp.json.return_value = json_data
|
||||
return resp
|
||||
|
||||
|
||||
class TestGetOllamaModelExactMatch:
|
||||
"""Ensure _get_ollama_model uses exact match, not prefix match."""
|
||||
|
||||
@patch("timmy.tools_intro.httpx.get")
|
||||
def test_exact_match_from_ps(self, mock_get):
|
||||
"""Should return exact model from /api/ps."""
|
||||
from timmy.tools_intro import _get_ollama_model
|
||||
|
||||
ps_resp = _mock_response({"models": [{"name": "qwen3:30b"}]})
|
||||
mock_get.return_value = ps_resp
|
||||
|
||||
with patch("config.settings") as mock_settings:
|
||||
mock_settings.ollama_model = "qwen3:30b"
|
||||
mock_settings.ollama_url = "http://localhost:11434"
|
||||
result = _get_ollama_model()
|
||||
|
||||
assert result == "qwen3:30b"
|
||||
|
||||
@patch("timmy.tools_intro.httpx.get")
|
||||
def test_prefix_collision_returns_correct_model(self, mock_get):
|
||||
"""qwen3:30b configured — must NOT match qwen3.5:latest (prefix bug)."""
|
||||
from timmy.tools_intro import _get_ollama_model
|
||||
|
||||
# /api/ps has both models loaded; configured is qwen3:30b
|
||||
ps_resp = _mock_response({"models": [{"name": "qwen3.5:latest"}, {"name": "qwen3:30b"}]})
|
||||
mock_get.return_value = ps_resp
|
||||
|
||||
with patch("config.settings") as mock_settings:
|
||||
mock_settings.ollama_model = "qwen3:30b"
|
||||
mock_settings.ollama_url = "http://localhost:11434"
|
||||
result = _get_ollama_model()
|
||||
|
||||
assert result == "qwen3:30b", f"Got '{result}' — prefix collision bug!"
|
||||
|
||||
@patch("timmy.tools_intro.httpx.get")
|
||||
def test_configured_model_not_running_returns_actual(self, mock_get):
|
||||
"""If configured model isn't loaded, report what IS running."""
|
||||
from timmy.tools_intro import _get_ollama_model
|
||||
|
||||
ps_resp = _mock_response({"models": [{"name": "qwen3.5:latest"}]})
|
||||
mock_get.return_value = ps_resp
|
||||
|
||||
with patch("config.settings") as mock_settings:
|
||||
mock_settings.ollama_model = "qwen3:30b"
|
||||
mock_settings.ollama_url = "http://localhost:11434"
|
||||
result = _get_ollama_model()
|
||||
|
||||
# Should report actual running model, not configured one
|
||||
assert result == "qwen3.5:latest"
|
||||
|
||||
@patch("timmy.tools_intro.httpx.get")
|
||||
def test_latest_suffix_match(self, mock_get):
|
||||
"""'qwen3:30b' config should match 'qwen3:30b:latest' from API."""
|
||||
from timmy.tools_intro import _get_ollama_model
|
||||
|
||||
ps_resp = _mock_response({"models": []})
|
||||
tags_resp = _mock_response({"models": [{"name": "qwen3:30b:latest"}]})
|
||||
mock_get.side_effect = [ps_resp, tags_resp]
|
||||
|
||||
with patch("config.settings") as mock_settings:
|
||||
mock_settings.ollama_model = "qwen3:30b"
|
||||
mock_settings.ollama_url = "http://localhost:11434"
|
||||
result = _get_ollama_model()
|
||||
|
||||
# Falls back to configured since no exact match
|
||||
assert result == "qwen3:30b"
|
||||
|
||||
@patch("timmy.tools_intro.httpx.get")
|
||||
def test_ollama_down_returns_configured(self, mock_get):
|
||||
"""If Ollama is unreachable, return configured model."""
|
||||
from timmy.tools_intro import _get_ollama_model
|
||||
|
||||
mock_get.side_effect = httpx.ConnectError("connection refused")
|
||||
|
||||
with patch("config.settings") as mock_settings:
|
||||
mock_settings.ollama_model = "qwen3:30b"
|
||||
mock_settings.ollama_url = "http://localhost:11434"
|
||||
result = _get_ollama_model()
|
||||
|
||||
assert result == "qwen3:30b"
|
||||
|
||||
Reference in New Issue
Block a user