Files
wehub-resource-sync b4fbd6fe9f
Deploy Site / deploy-vercel (push) Has been skipped
Deploy Site / deploy-docs (push) Has been skipped
Build Skills Index / build-index (push) Has been skipped
CI / Deny unrelated histories (push) Has been skipped
CI / Detect affected areas (push) Successful in 27m35s
CI / OSV scan (push) Failing after 4s
CI / Build&Test Docker image (push) Successful in 9s
CI / Supply-chain scan (push) Has been skipped
CI / Lint Docker scripts (push) Failing after 5m13s
CI / Check contributors (push) Failing after 12m8s
CI / Docs Site (push) Failing after 12m8s
CI / TypeScript (push) Failing after 12m8s
CI / Python lints (push) Failing after 12m9s
CI / Python tests (push) Failing after 12m9s
CI / Check uv.lock (push) Failing after 23m22s
CI / CI timing report (push) Has been cancelled
Build Skills Index / trigger-deploy (push) Has been cancelled
CI / All required checks pass (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 11:56:03 +08:00

61 lines
2.3 KiB
Python

"""Tests for the /model picker background cache prewarm.
``prewarm_picker_cache_async()`` warms the provider-models disk cache off the
user's critical path so the first ``/model`` open in a session is fast instead
of blocking ~1-2s on serial /v1/models fetches. These pin the two contracts
that matter: it runs the warm path exactly once per process (no thread leak),
and it delegates to ``list_authenticated_providers`` to do the warming.
"""
from __future__ import annotations
from unittest.mock import patch
import hermes_cli.model_switch as ms
def _reset_guard():
ms._picker_prewarm_done.clear()
def test_prewarm_runs_list_authenticated_providers_once():
"""First call spawns a thread that calls list_authenticated_providers;
the warm side effect is delegated there (which disk-caches per provider)."""
_reset_guard()
with patch.object(ms, "list_authenticated_providers", return_value=[]) as mock_list:
t = ms.prewarm_picker_cache_async()
assert t is not None, "first call must spawn a prewarm thread"
t.join(timeout=10)
assert not t.is_alive(), "prewarm thread should finish promptly"
mock_list.assert_called_once()
_reset_guard()
def test_prewarm_guard_is_once_per_process():
"""The process-level Event guard must make repeat calls no-ops so a
long-lived process never leaks one OS thread per call."""
_reset_guard()
with patch.object(ms, "list_authenticated_providers", return_value=[]):
t1 = ms.prewarm_picker_cache_async()
assert t1 is not None
t1.join(timeout=10)
# Subsequent calls return None (guard set) — no new thread.
assert ms.prewarm_picker_cache_async() is None
assert ms.prewarm_picker_cache_async() is None
_reset_guard()
def test_prewarm_never_raises_on_failure():
"""A failing/offline provider path must be fully swallowed — the prewarm
is best-effort and must never surface errors into the session."""
_reset_guard()
with patch.object(
ms, "list_authenticated_providers", side_effect=RuntimeError("boom")
):
t = ms.prewarm_picker_cache_async()
assert t is not None
# join must not raise; the worker swallows the exception internally.
t.join(timeout=10)
assert not t.is_alive()
_reset_guard()