/ tests / cli / test_cli_tools_command.py
test_cli_tools_command.py
  1  """Tests for /tools slash command handler in the interactive CLI."""
  2  
  3  from unittest.mock import MagicMock, patch, call
  4  
  5  from cli import HermesCLI
  6  
  7  
  8  def _make_cli(enabled_toolsets=None):
  9      """Build a minimal HermesCLI stub without running __init__."""
 10      cli_obj = HermesCLI.__new__(HermesCLI)
 11      cli_obj.enabled_toolsets = set(enabled_toolsets or ["web", "memory"])
 12      cli_obj._command_running = False
 13      cli_obj.console = MagicMock()
 14      return cli_obj
 15  
 16  
 17  # ── /tools (no subcommand) ──────────────────────────────────────────────────
 18  
 19  
 20  class TestToolsSlashNoSubcommand:
 21  
 22      def test_bare_tools_shows_tool_list(self):
 23          cli_obj = _make_cli()
 24          with patch.object(cli_obj, "show_tools") as mock_show:
 25              cli_obj._handle_tools_command("/tools")
 26          mock_show.assert_called_once()
 27  
 28      def test_unknown_subcommand_falls_back_to_show_tools(self):
 29          cli_obj = _make_cli()
 30          with patch.object(cli_obj, "show_tools") as mock_show:
 31              cli_obj._handle_tools_command("/tools foobar")
 32          mock_show.assert_called_once()
 33  
 34  
 35  # ── /tools list ─────────────────────────────────────────────────────────────
 36  
 37  
 38  class TestToolsSlashList:
 39  
 40      def test_list_calls_backend(self, capsys):
 41          cli_obj = _make_cli()
 42          with patch("hermes_cli.tools_config.load_config",
 43                     return_value={"platform_toolsets": {"cli": ["web"]}}), \
 44               patch("hermes_cli.tools_config.save_config"):
 45              cli_obj._handle_tools_command("/tools list")
 46          out = capsys.readouterr().out
 47          assert "web" in out
 48  
 49      def test_list_does_not_modify_enabled_toolsets(self):
 50          """List is read-only — self.enabled_toolsets must not change."""
 51          cli_obj = _make_cli(["web", "memory"])
 52          with patch("hermes_cli.tools_config.load_config",
 53                     return_value={"platform_toolsets": {"cli": ["web"]}}):
 54              cli_obj._handle_tools_command("/tools list")
 55          assert cli_obj.enabled_toolsets == {"web", "memory"}
 56  
 57  
 58  # ── /tools disable (session reset) ──────────────────────────────────────────
 59  
 60  
 61  class TestToolsSlashDisableWithReset:
 62  
 63      def test_disable_applies_directly_and_resets_session(self):
 64          """Disable applies immediately (no confirmation prompt) and resets session."""
 65          cli_obj = _make_cli(["web", "memory"])
 66          with patch("hermes_cli.tools_config.load_config",
 67                     return_value={"platform_toolsets": {"cli": ["web", "memory"]}}), \
 68               patch("hermes_cli.tools_config.save_config"), \
 69               patch("hermes_cli.tools_config._get_platform_tools", return_value={"memory"}), \
 70               patch("hermes_cli.config.load_config", return_value={}), \
 71               patch.object(cli_obj, "new_session") as mock_reset:
 72              cli_obj._handle_tools_command("/tools disable web")
 73          mock_reset.assert_called_once()
 74          assert "web" not in cli_obj.enabled_toolsets
 75  
 76      def test_disable_does_not_prompt_for_confirmation(self):
 77          """Disable no longer uses input() — it applies directly."""
 78          cli_obj = _make_cli(["web", "memory"])
 79          with patch("hermes_cli.tools_config.load_config",
 80                     return_value={"platform_toolsets": {"cli": ["web", "memory"]}}), \
 81               patch("hermes_cli.tools_config.save_config"), \
 82               patch("hermes_cli.tools_config._get_platform_tools", return_value={"memory"}), \
 83               patch("hermes_cli.config.load_config", return_value={}), \
 84               patch.object(cli_obj, "new_session"), \
 85               patch("builtins.input") as mock_input:
 86              cli_obj._handle_tools_command("/tools disable web")
 87          mock_input.assert_not_called()
 88  
 89      def test_disable_always_resets_session(self):
 90          """Even without a confirmation prompt, disable always resets the session."""
 91          cli_obj = _make_cli(["web", "memory"])
 92          with patch("hermes_cli.tools_config.load_config",
 93                     return_value={"platform_toolsets": {"cli": ["web", "memory"]}}), \
 94               patch("hermes_cli.tools_config.save_config"), \
 95               patch("hermes_cli.tools_config._get_platform_tools", return_value={"memory"}), \
 96               patch("hermes_cli.config.load_config", return_value={}), \
 97               patch.object(cli_obj, "new_session") as mock_reset:
 98              cli_obj._handle_tools_command("/tools disable web")
 99          mock_reset.assert_called_once()
100  
101      def test_disable_missing_name_prints_usage(self, capsys):
102          cli_obj = _make_cli()
103          cli_obj._handle_tools_command("/tools disable")
104          out = capsys.readouterr().out
105          assert "Usage" in out
106  
107  
108  # ── /tools enable (session reset) ───────────────────────────────────────────
109  
110  
111  class TestToolsSlashEnableWithReset:
112  
113      def test_enable_applies_directly_and_resets_session(self):
114          """Enable applies immediately (no confirmation prompt) and resets session."""
115          cli_obj = _make_cli(["memory"])
116          with patch("hermes_cli.tools_config.load_config",
117                     return_value={"platform_toolsets": {"cli": ["memory"]}}), \
118               patch("hermes_cli.tools_config.save_config"), \
119               patch("hermes_cli.tools_config._get_platform_tools", return_value={"memory", "web"}), \
120               patch("hermes_cli.config.load_config", return_value={}), \
121               patch.object(cli_obj, "new_session") as mock_reset:
122              cli_obj._handle_tools_command("/tools enable web")
123          mock_reset.assert_called_once()
124          assert "web" in cli_obj.enabled_toolsets
125  
126      def test_enable_missing_name_prints_usage(self, capsys):
127          cli_obj = _make_cli()
128          cli_obj._handle_tools_command("/tools enable")
129          out = capsys.readouterr().out
130          assert "Usage" in out