test: pin /api/model/options to _config_profile_scope for selected profiles

Regression for #58576: _profile_scope holds _SKILLS_PROFILE_LOCK across
the payload build, which can block up to 15s on a models.dev cache miss
and starve concurrent /api/config on the same lock. The test records
which scope the handler enters for a selected profile and asserts only
the config-only (contextvar) scope is used.
This commit is contained in:
David Metcalfe
2026-08-05 20:03:34 -07:00
committed by Teknium
parent 6545812c86
commit e4c35e397c
@@ -7,6 +7,7 @@ reads/writes land in the REQUESTED profile, the dashboard's own profile
stays untouched, and the chat PTY env is scoped via HERMES_HOME.
"""
import json
from contextlib import contextmanager
import pytest
import yaml
@@ -353,6 +354,47 @@ class TestProfileScopedModel:
def test_model_options_uses_config_only_scope_for_selected_profile(
self, client, monkeypatch
):
"""Regression (#58576): _profile_scope holds _SKILLS_PROFILE_LOCK
across its body, and the payload build can block up to 15s on a
models.dev cache miss — a cold request would starve concurrent
/api/config on the same lock. The handler must scope the worker
through _config_profile_scope (contextvar only, no lock) for the
selected profile."""
import hermes_cli.web_server as web_server
scopes = []
@contextmanager
def _recording_config_scope(profile):
scopes.append(("config", profile))
yield object()
@contextmanager
def _recording_profile_scope(profile):
scopes.append(("full", profile))
yield object()
monkeypatch.setattr(
web_server, "_config_profile_scope", _recording_config_scope
)
monkeypatch.setattr(web_server, "_profile_scope", _recording_profile_scope)
monkeypatch.setattr(
"hermes_cli.inventory.load_picker_context", lambda: object()
)
monkeypatch.setattr(
"hermes_cli.inventory.build_model_options_payload",
lambda _ctx, **kwargs: {"providers": [], "model": "", "provider": ""},
)
resp = client.get("/api/model/options", params={"profile": "worker_beta"})
assert resp.status_code == 200
# Only the config-only scope may wrap the payload build; entering
# _profile_scope would hold _SKILLS_PROFILE_LOCK across it (#58576).
assert scopes == [("config", "worker_beta")]
def test_model_info_unknown_profile_404(self, client, isolated_profiles):
"""Regression: the broad except used to convert the 404 into a 200
with empty model info ("no model set" — silently wrong)."""