Files
hermes-agent/tests/gateway/test_model_command_async_offload.py
teknium1 61fa0f47ec test: purge low-value tests, lane py07 (216 removed)
Change-detectors, tautologies, source-reading tests, redundant duplicates,
mock-echo tests and dead/unrunnable tests. Per-test rationale in the lane
ledger (category + reason for every removal).
2026-09-23 03:15:26 -07:00

97 lines
3.8 KiB
Python

"""Gateway ``/model`` picker listing is a read path (#41289, #74003): it must ask for
cache-only catalogs and live-probe only the currently selected custom endpoint, so a
stale provider cache cannot freeze the gateway on blocking HTTP fetches.
"""
import pytest
from gateway.config import Platform
from gateway.platforms.event import MessageEvent, MessageType
from gateway.run import GatewayRunner
from gateway.session import SessionSource
# --------------------------------------------------------------------------- #
# Harness
# --------------------------------------------------------------------------- #
def _make_runner():
runner = object.__new__(GatewayRunner)
runner.adapters = {}
runner._voice_mode = {}
runner._session_model_overrides = {}
runner._running_agents = {}
return runner
def _make_event():
"""A bare ``/model`` (no args) — triggers the listing branch."""
return MessageEvent(
text="/model",
message_type=MessageType.TEXT,
source=SessionSource(platform=Platform.TELEGRAM, chat_id="12345", chat_type="dm"),
)
@pytest.fixture
def _isolated_config(tmp_path, monkeypatch):
"""Point the handler at an empty isolated home so config loading is cheap
and deterministic (no real provider creds / network)."""
import gateway.run as gateway_run
hermes_home = tmp_path / ".hermes"
hermes_home.mkdir()
(hermes_home / "config.yaml").write_text("model:\n default: gpt-x\n provider: openrouter\nproviders: {}\n", encoding="utf-8")
monkeypatch.setattr(gateway_run, "_hermes_home", hermes_home)
monkeypatch.setattr("agent.models_dev.fetch_models_dev", lambda: {})
return hermes_home
# --------------------------------------------------------------------------- #
# Text-fallback path -> list_authenticated_providers
# --------------------------------------------------------------------------- #
# --------------------------------------------------------------------------- #
# Picker path -> list_picker_providers
# --------------------------------------------------------------------------- #
class _FakePickerResult:
success = True
class _FakePickerAdapter:
"""Adapter whose *type* exposes ``send_model_picker`` (the gate the handler
checks via ``getattr(type(adapter), 'send_model_picker', None)``)."""
async def send_model_picker(self, **kwargs):
return _FakePickerResult()
def _thread_metadata(self, *a, **k): # pragma: no cover - not exercised
return None
@pytest.mark.asyncio
async def test_picker_path_lists_cache_only_and_probes_only_the_current_custom_endpoint(_isolated_config, monkeypatch):
"""#74003: the chat ``/model`` reply is a read path. The listing must ask for cache-only catalogs
and must not live-probe every saved custom endpoint (only the selected one), matching the GUI."""
seen: list[dict] = []
def _fake_list_picker_providers(**kwargs):
seen.append(kwargs)
return [{"slug": "openrouter", "name": "OpenRouter", "is_current": True,
"models": ["gpt-x"], "total_models": 1}]
monkeypatch.setattr("hermes_cli.model_switch_providers.list_picker_providers", _fake_list_picker_providers)
runner = _make_runner()
runner.adapters = {Platform.TELEGRAM: _FakePickerAdapter()}
monkeypatch.setattr(runner, "_thread_metadata_for_source", lambda *a, **k: None, raising=False)
monkeypatch.setattr(runner, "_reply_anchor_for_event", lambda *a, **k: None, raising=False)
assert await runner._handle_model_command(_make_event()) is None
assert seen, "listing never ran"
flags = {k: seen[0].get(k) for k in ("non_blocking_catalogs", "probe_custom_providers", "probe_current_custom_provider")}
assert flags == {"non_blocking_catalogs": True, "probe_custom_providers": False,
"probe_current_custom_provider": True}, flags