Clean rebase of ruizanthony's #5412 (rebase-first). Co-authored-by: ruizanthony <ruizanthony@users.noreply.github.com>
121 lines
4.0 KiB
Python
121 lines
4.0 KiB
Python
from __future__ import annotations
|
|
|
|
import sys
|
|
import types
|
|
from pathlib import Path
|
|
from types import SimpleNamespace
|
|
|
|
import pytest
|
|
|
|
|
|
REPO_ROOT = Path(__file__).resolve().parents[1]
|
|
|
|
|
|
def _profile_row(name: str, path: Path, *, is_default: bool = False):
|
|
return SimpleNamespace(
|
|
name=name,
|
|
path=path,
|
|
is_default=is_default,
|
|
gateway_running=False,
|
|
model=None,
|
|
provider=None,
|
|
has_env=False,
|
|
)
|
|
|
|
|
|
@pytest.fixture(autouse=True)
|
|
def _clear_profile_rows_cache():
|
|
import api.profiles as profiles
|
|
|
|
profiles._LIST_PROFILES_CACHE = None
|
|
yield
|
|
profiles._LIST_PROFILES_CACHE = None
|
|
|
|
|
|
def _install_fake_hermes_profiles(monkeypatch, rows):
|
|
hermes_cli = types.ModuleType("hermes_cli")
|
|
profiles_mod = types.ModuleType("hermes_cli.profiles")
|
|
profiles_mod.list_profiles = lambda: rows
|
|
monkeypatch.setitem(sys.modules, "hermes_cli", hermes_cli)
|
|
monkeypatch.setitem(sys.modules, "hermes_cli.profiles", profiles_mod)
|
|
|
|
|
|
def test_profile_yaml_visible_false_is_exposed_as_hidden(monkeypatch, tmp_path):
|
|
import api.profiles as profiles
|
|
|
|
hidden = tmp_path / "profiles" / "worker-coder"
|
|
visible = tmp_path / "profiles" / "human"
|
|
missing = tmp_path / "profiles" / "missing-meta"
|
|
malformed = tmp_path / "profiles" / "malformed"
|
|
string_false = tmp_path / "profiles" / "string-false"
|
|
for path in (hidden, visible, missing, malformed, string_false):
|
|
path.mkdir(parents=True)
|
|
(hidden / "profile.yaml").write_text("visible: false\n", encoding="utf-8")
|
|
(visible / "profile.yaml").write_text("visible: true\n", encoding="utf-8")
|
|
(malformed / "profile.yaml").write_text("visible: [\n", encoding="utf-8")
|
|
(string_false / "profile.yaml").write_text('visible: "false"\n', encoding="utf-8")
|
|
|
|
rows = [
|
|
_profile_row("worker-coder", hidden),
|
|
_profile_row("human", visible),
|
|
_profile_row("missing-meta", missing),
|
|
_profile_row("malformed", malformed),
|
|
_profile_row("string-false", string_false),
|
|
]
|
|
_install_fake_hermes_profiles(monkeypatch, rows)
|
|
monkeypatch.setattr(profiles, "_get_profile_skills_stats", lambda _path: (0, 0))
|
|
monkeypatch.setattr(profiles, "get_active_profile_name", lambda: "human")
|
|
|
|
result = {row["name"]: row["visible"] for row in profiles.list_profiles_api()}
|
|
|
|
assert result == {
|
|
"worker-coder": False,
|
|
"human": True,
|
|
"missing-meta": True,
|
|
"malformed": True,
|
|
"string-false": True,
|
|
}
|
|
|
|
|
|
def test_default_profile_fallback_stays_visible(monkeypatch):
|
|
import api.profiles as profiles
|
|
|
|
monkeypatch.setattr(profiles, "_get_profile_skills_stats", lambda _path: (0, 0))
|
|
|
|
assert profiles._default_profile_dict()["visible"] is True
|
|
|
|
|
|
def _panels_js() -> str:
|
|
return (REPO_ROOT / "static" / "panels.js").read_text(encoding="utf-8")
|
|
|
|
|
|
def _function_body(src: str, signature: str) -> str:
|
|
start = src.find(signature)
|
|
assert start != -1, f"{signature} not found"
|
|
i = src.find("{", start)
|
|
depth = 0
|
|
for j in range(i, len(src)):
|
|
if src[j] == "{":
|
|
depth += 1
|
|
elif src[j] == "}":
|
|
depth -= 1
|
|
if depth == 0:
|
|
return src[start : j + 1]
|
|
raise AssertionError(f"could not find end of {signature}")
|
|
|
|
|
|
def test_profile_dropdown_filters_hidden_profiles_but_preserves_active():
|
|
body = _function_body(_panels_js(), "function renderProfileDropdown(data)")
|
|
|
|
assert "data = data || {};" in body
|
|
assert "const allProfiles = (Array.isArray(data.profiles) ? data.profiles : []).filter(p => p && typeof p.name === 'string');" in body
|
|
assert "allProfiles.some(p => p.name === S.activeProfile)" in body
|
|
assert "const profiles = allProfiles.filter(p => p && (p.visible !== false || p.name === active));" in body
|
|
|
|
|
|
def test_profiles_management_panel_still_renders_all_profiles():
|
|
body = _function_body(_panels_js(), "async function loadProfilesPanel()")
|
|
|
|
assert "for (const p of data.profiles)" in body
|
|
assert "visible !== false" not in body
|