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

105 lines
4.8 KiB
Python

"""Phase 4: lifecycle guard + per-profile observability."""
import pytest
class TestServedProfilesStatus:
def test_write_and_read_served_profiles(self, tmp_path, monkeypatch):
monkeypatch.setenv("HERMES_HOME", str(tmp_path))
import importlib
import gateway.status as status
importlib.reload(status)
try:
status.write_runtime_status(
gateway_state="running", served_profiles=["default", "coder"]
)
rec = status.read_runtime_status()
assert rec.get("served_profiles") == ["default", "coder"]
finally:
importlib.reload(status)
def test_served_profiles_absent_by_default(self, tmp_path, monkeypatch):
monkeypatch.setenv("HERMES_HOME", str(tmp_path))
import importlib
import gateway.status as status
importlib.reload(status)
try:
status.write_runtime_status(gateway_state="running")
rec = status.read_runtime_status()
assert "served_profiles" not in rec
finally:
importlib.reload(status)
class TestNamedProfileMultiplexerGuard:
"""_guard_named_profile_under_multiplexer is inert unless all conditions hold."""
def test_inert_for_default_profile(self, monkeypatch):
from hermes_cli import gateway as gw
monkeypatch.setattr(gw, "_profile_suffix", lambda: "")
# Should return without raising (default profile => guard N/A).
gw._guard_named_profile_under_multiplexer(force=False)
def test_force_bypasses(self, monkeypatch):
from hermes_cli import gateway as gw
# Even if it looks like a named profile, force returns immediately.
monkeypatch.setattr(gw, "_profile_suffix", lambda: "coder")
gw._guard_named_profile_under_multiplexer(force=True)
def test_inert_when_no_default_gateway_running(self, monkeypatch, tmp_path):
from hermes_cli import gateway as gw
monkeypatch.setattr(gw, "_profile_suffix", lambda: "coder")
monkeypatch.setattr(
"hermes_constants.get_default_hermes_root", lambda: tmp_path
)
# No gateway.pid in tmp_path => no running default gateway => no raise.
gw._guard_named_profile_under_multiplexer(force=False)
def _fake_running_default_gateway(self, monkeypatch, tmp_path):
"""Make the guard believe a live default gateway exists at tmp_path."""
from hermes_cli import gateway as gw
import gateway.status as status
monkeypatch.setattr(gw, "_profile_suffix", lambda: "coder")
monkeypatch.setattr(
"hermes_constants.get_default_hermes_root", lambda: tmp_path
)
(tmp_path / "gateway.pid").write_text("12345", encoding="utf-8")
monkeypatch.setattr(status, "_read_pid_record", lambda p: {"pid": 12345})
monkeypatch.setattr(status, "_pid_from_record", lambda rec: 12345)
monkeypatch.setattr(status, "_pid_exists", lambda pid: True)
def test_env_forces_guard_even_without_config(self, monkeypatch, tmp_path):
"""GATEWAY_MULTIPLEX_PROFILES=true must trip the guard even when the
default profile's config.yaml has no multiplex_profiles key — the hosted
case where multiplex is forced purely by the env stamp."""
from hermes_cli import gateway as gw
self._fake_running_default_gateway(monkeypatch, tmp_path)
# No config.yaml written → the only signal is the env override.
monkeypatch.setenv("GATEWAY_MULTIPLEX_PROFILES", "true")
with pytest.raises(SystemExit):
gw._guard_named_profile_under_multiplexer(force=False)
def test_env_false_disables_guard_over_config_true(self, monkeypatch, tmp_path):
"""GATEWAY_MULTIPLEX_PROFILES=false wins over a config.yaml opt-in, so
the guard stays inert (symmetric with the config precedence)."""
from hermes_cli import gateway as gw
self._fake_running_default_gateway(monkeypatch, tmp_path)
(tmp_path / "config.yaml").write_text(
"multiplex_profiles: true\n", encoding="utf-8"
)
monkeypatch.setenv("GATEWAY_MULTIPLEX_PROFILES", "false")
# Env forces OFF → guard must NOT raise.
gw._guard_named_profile_under_multiplexer(force=False)
def test_blank_env_falls_through_to_config_and_raises(self, monkeypatch, tmp_path):
"""A blank env value must not shadow a config.yaml opt-in: the guard
still trips on the config value."""
from hermes_cli import gateway as gw
self._fake_running_default_gateway(monkeypatch, tmp_path)
(tmp_path / "config.yaml").write_text(
"multiplex_profiles: true\n", encoding="utf-8"
)
monkeypatch.setenv("GATEWAY_MULTIPLEX_PROFILES", "")
with pytest.raises(SystemExit):
gw._guard_named_profile_under_multiplexer(force=False)