Files
hermes-webui/tests/test_live_models_ttl_cache.py
T
2026-05-01 04:46:15 +00:00

115 lines
4.0 KiB
Python

"""Regression tests for /api/models/live backend TTL caching."""
import sys
import types
from urllib.parse import urlparse
def _install_provider_model_ids(monkeypatch, fn):
hermes_cli = types.ModuleType("hermes_cli")
hermes_cli.__path__ = []
models = types.ModuleType("hermes_cli.models")
models.provider_model_ids = fn
monkeypatch.setitem(sys.modules, "hermes_cli", hermes_cli)
monkeypatch.setitem(sys.modules, "hermes_cli.models", models)
def _patch_live_models_basics(monkeypatch, routes, profile="default"):
import api.config as config
import api.profiles as profiles
routes._clear_live_models_cache()
monkeypatch.setattr(routes, "j", lambda _handler, payload, status=200, extra_headers=None: payload)
monkeypatch.setattr(config, "get_config", lambda: {"model": {"provider": "openai"}})
monkeypatch.setattr(config, "_resolve_provider_alias", lambda provider: provider)
monkeypatch.setattr(profiles, "get_active_profile_name", lambda: profile)
def test_live_models_cache_hits_within_ttl(monkeypatch):
import api.routes as routes
calls = []
def provider_model_ids(provider):
calls.append(provider)
return ["openai/gpt-test"]
_install_provider_model_ids(monkeypatch, provider_model_ids)
_patch_live_models_basics(monkeypatch, routes)
parsed = urlparse("/api/models/live?provider=openai")
first = routes._handle_live_models(object(), parsed)
second = routes._handle_live_models(object(), parsed)
assert calls == ["openai"]
assert first == second
assert first["models"] == [{"id": "openai/gpt-test", "label": "GPT Test"}]
def test_live_models_cache_expires(monkeypatch):
import api.routes as routes
now = [1000.0]
calls = []
def provider_model_ids(provider):
calls.append(provider)
return [f"{provider}/model-{len(calls)}"]
_install_provider_model_ids(monkeypatch, provider_model_ids)
_patch_live_models_basics(monkeypatch, routes)
monkeypatch.setattr(routes.time, "monotonic", lambda: now[0])
parsed = urlparse("/api/models/live?provider=openai")
first = routes._handle_live_models(object(), parsed)
now[0] += routes._LIVE_MODELS_CACHE_TTL + 1
second = routes._handle_live_models(object(), parsed)
assert calls == ["openai", "openai"]
assert first["models"][0]["id"] == "openai/model-1"
assert second["models"][0]["id"] == "openai/model-2"
def test_live_models_cache_is_profile_scoped(monkeypatch):
import api.routes as routes
import api.profiles as profiles
active_profile = ["default"]
calls = []
def provider_model_ids(provider):
calls.append((active_profile[0], provider))
return [f"{provider}/{active_profile[0]}-model"]
_install_provider_model_ids(monkeypatch, provider_model_ids)
_patch_live_models_basics(monkeypatch, routes)
monkeypatch.setattr(profiles, "get_active_profile_name", lambda: active_profile[0])
parsed = urlparse("/api/models/live?provider=openai")
default_payload = routes._handle_live_models(object(), parsed)
active_profile[0] = "research"
research_payload = routes._handle_live_models(object(), parsed)
again_payload = routes._handle_live_models(object(), parsed)
assert calls == [("default", "openai"), ("research", "openai")]
assert default_payload["models"][0]["id"] == "openai/default-model"
assert research_payload["models"][0]["id"] == "openai/research-model"
assert again_payload == research_payload
def test_live_models_cache_returns_deep_copies(monkeypatch):
import api.routes as routes
_install_provider_model_ids(monkeypatch, lambda provider: ["openai/gpt-test"])
_patch_live_models_basics(monkeypatch, routes)
parsed = urlparse("/api/models/live?provider=openai")
first = routes._handle_live_models(object(), parsed)
first["models"].clear()
first["provider"] = "mutated"
second = routes._handle_live_models(object(), parsed)
assert second["provider"] == "openai"
assert second["models"] == [{"id": "openai/gpt-test", "label": "GPT Test"}]