Files
hermes-agent/tests/gateway/test_multiplex_residue_parity.py
Teknium fc71fb63e5 test(multiplex): invariants for the residue fixes; retire allowlist tests
- tests/gateway/test_multiplex_residue_parity.py: served profile reads its own
  sessions.*; per-turn bridge skips secondary scope; resolve_proxy_url reads the
  routed scope (and does not borrow the default's on a miss); a stale served
  turn never recreates an archived profile; MCP discovery runs per profile home.
- test_config.py: v43 migration removes multiplex_profile_allowlist.
- Allowlist tests deleted (feature removed) or rewritten to "served set = all
  live profiles"; the unserved cases now use a tombstone / missing dir.
- MCP discovery fixtures converted to the per-home set/dict slot.
2026-09-11 19:50:46 -07:00

119 lines
4.9 KiB
Python

"""Multiplex parity: a served profile reads ITS ``sessions.*`` / proxy env, never the launch
profile's; a stale served route never re-scaffolds an archived profile; MCP discovery runs once
per served profile home."""
import logging
import threading
import pytest
from agent import secret_scope as ss
from hermes_constants import reset_hermes_home_override, set_hermes_home_override
@pytest.fixture
def two_homes(tmp_path, monkeypatch):
root = tmp_path / ".hermes"
alpha = root / "profiles" / "alpha"
for home in (root, alpha):
(home / "sessions").mkdir(parents=True)
(home / ".env").write_text("", encoding="utf-8")
(root / "config.yaml").write_text(
"sessions:\n cjk_fts: true\n search_slow_ms: 1000\n", encoding="utf-8")
(alpha / "config.yaml").write_text(
"sessions:\n cjk_fts: false\n search_slow_ms: 50\n", encoding="utf-8")
monkeypatch.setattr("pathlib.Path.home", lambda: tmp_path)
monkeypatch.setenv("HERMES_HOME", str(root))
ss.set_multiplex_active(False)
yield root, alpha
ss.set_multiplex_active(False)
def test_served_profile_reads_its_own_sessions_settings(two_homes, monkeypatch):
root, alpha = two_homes
from hermes_state_fts import _cjk_fts_config_enabled
from hermes_state_search import _search_slow_ms
# The multiplexer bridged the LAUNCH (default) profile's sessions.* into env at import.
monkeypatch.setenv("HERMES_CJK_FTS", "true")
monkeypatch.setenv("HERMES_SEARCH_SLOW_MS", "1000")
assert _cjk_fts_config_enabled() is True and _search_slow_ms() == 1000.0 # unscoped = env bridge
token = set_hermes_home_override(str(alpha))
try:
assert _cjk_fts_config_enabled() is False
assert _search_slow_ms() == 50.0
finally:
reset_hermes_home_override(token)
def test_per_turn_sessions_bridge_skips_secondary_scope(two_homes, monkeypatch):
root, alpha = two_homes
from gateway import run as gw_run
monkeypatch.delenv("HERMES_CJK_FTS", raising=False)
ss.set_multiplex_active(True)
with gw_run._profile_runtime_scope(alpha):
gw_run._bridge_max_turns_from_config(alpha)
assert "HERMES_CJK_FTS" not in __import__("os").environ, "secondary scope must not write the process env"
def test_resolve_proxy_url_reads_routed_profile_scope(two_homes, monkeypatch):
root, alpha = two_homes
from gateway.platforms.base import resolve_proxy_url
monkeypatch.setenv("TELEGRAM_PROXY", "socks5://default-proxy:1080") # launch profile's .env
ss.set_multiplex_active(True)
token = ss.set_secret_scope({"TELEGRAM_PROXY": "socks5://alpha-proxy:1080"})
try:
assert resolve_proxy_url("TELEGRAM_PROXY") == "socks5://alpha-proxy:1080"
finally:
ss.reset_secret_scope(token)
token = ss.set_secret_scope({}) # a served profile with NO proxy must not borrow the default's
try:
monkeypatch.delenv("HTTPS_PROXY", raising=False)
assert resolve_proxy_url("TELEGRAM_PROXY") is None
finally:
ss.reset_secret_scope(token)
ss.set_multiplex_active(False)
assert resolve_proxy_url("TELEGRAM_PROXY") == "socks5://default-proxy:1080" # standalone: env
def test_stale_served_turn_never_recreates_archived_profile(two_homes):
"""#94590: a multiplexer still holding an archived profile's route must not re-scaffold it."""
import shutil
root, alpha = two_homes
from gateway import run as gw_run
shutil.rmtree(alpha)
ss.set_multiplex_active(True)
with gw_run._profile_runtime_scope(alpha):
from hermes_state import SessionDB
with pytest.raises(FileNotFoundError):
SessionDB()
assert not alpha.exists()
def test_mcp_discovery_slot_is_per_profile_home(two_homes, monkeypatch):
"""#67605: alpha building an agent after default must still get ITS discovery run."""
root, alpha = two_homes
import hermes_cli.mcp_startup as ms
from hermes_constants import get_hermes_home
for home in (root, alpha):
(home / "config.yaml").write_text("mcp_servers:\n demo:\n command: /bin/true\n", encoding="utf-8")
monkeypatch.setattr(ms, "_mcp_discovery_started", set())
monkeypatch.setattr(ms, "_mcp_discovery_thread", {})
seen, done = [], threading.Event()
def fake_discover(**_kw):
seen.append(get_hermes_home().name)
done.set()
monkeypatch.setattr("tools.mcp_tool_discovery.discover_mcp_tools", fake_discover)
monkeypatch.setattr("tools.mcp_tool_discovery.get_mcp_status", lambda *a, **k: [{"connected": True}])
for home in (root, alpha):
token = set_hermes_home_override(str(home))
try:
done.clear()
ms.start_background_mcp_discovery(logger=logging.getLogger("t"), thread_name=f"mcp-{home.name}")
assert done.wait(5)
ms.wait_for_mcp_discovery(timeout=5)
finally:
reset_hermes_home_override(token)
assert seen == [root.name, "alpha"]