test_mcp_reload_confirm_gate.py
1 """Tests for the approvals.mcp_reload_confirm config gate. 2 3 When the user runs /reload-mcp, the MCP tool set is rebuilt which 4 invalidates the provider prompt cache for the active session. That's 5 expensive on long-context / high-reasoning models. The config gate 6 adds a three-option confirmation (Approve Once / Always Approve / 7 Cancel); "Always Approve" flips this key to false so subsequent reloads 8 run silently. 9 """ 10 11 from __future__ import annotations 12 13 from copy import deepcopy 14 15 from hermes_cli.config import DEFAULT_CONFIG 16 17 18 class TestMcpReloadConfirmDefault: 19 def test_default_config_has_the_key(self): 20 approvals = DEFAULT_CONFIG.get("approvals") 21 assert isinstance(approvals, dict) 22 assert "mcp_reload_confirm" in approvals 23 24 def test_default_is_true(self): 25 # New installs confirm by default — this is the safe behavior. 26 assert DEFAULT_CONFIG["approvals"]["mcp_reload_confirm"] is True 27 28 def test_shape_matches_other_approval_keys(self): 29 # Same flat dict level as `mode` / `timeout` / `cron_mode`. 30 approvals = DEFAULT_CONFIG["approvals"] 31 assert isinstance(approvals.get("mode"), str) 32 assert isinstance(approvals.get("timeout"), int) 33 assert isinstance(approvals.get("cron_mode"), str) 34 assert isinstance(approvals.get("mcp_reload_confirm"), bool) 35 36 37 class TestUserConfigMerge: 38 """If a user has a pre-existing config without this key, load_config 39 should fill it in from DEFAULT_CONFIG (deep merge preserves keys the 40 user didn't override). 41 """ 42 43 def test_existing_user_config_without_key_gets_default(self, tmp_path, monkeypatch): 44 import yaml 45 46 # Simulate a legacy user config without the new key. 47 home = tmp_path / ".hermes" 48 home.mkdir() 49 cfg_path = home / "config.yaml" 50 legacy = { 51 "approvals": {"mode": "manual", "timeout": 60, "cron_mode": "deny"}, 52 } 53 cfg_path.write_text(yaml.safe_dump(legacy)) 54 55 monkeypatch.setenv("HERMES_HOME", str(home)) 56 # Force a fresh reimport of config.py so the HERMES_HOME is honored. 57 import importlib 58 import hermes_cli.config as cfg_mod 59 importlib.reload(cfg_mod) 60 61 cfg = cfg_mod.load_config() 62 assert cfg["approvals"]["mcp_reload_confirm"] is True 63 64 def test_existing_user_config_with_false_key_survives_merge( 65 self, tmp_path, monkeypatch, 66 ): 67 """A user who has clicked "Always Approve" (key=false) must keep 68 that setting across reloads — the default_true value must not win. 69 """ 70 import yaml 71 72 home = tmp_path / ".hermes" 73 home.mkdir() 74 cfg_path = home / "config.yaml" 75 user_cfg = { 76 "approvals": { 77 "mode": "manual", 78 "timeout": 60, 79 "cron_mode": "deny", 80 "mcp_reload_confirm": False, 81 }, 82 } 83 cfg_path.write_text(yaml.safe_dump(user_cfg)) 84 85 monkeypatch.setenv("HERMES_HOME", str(home)) 86 import importlib 87 import hermes_cli.config as cfg_mod 88 importlib.reload(cfg_mod) 89 90 cfg = cfg_mod.load_config() 91 assert cfg["approvals"]["mcp_reload_confirm"] is False