From af8791a49d3d07c9f193a36995fa115e372ced5f Mon Sep 17 00:00:00 2001 From: Teknium <127238744+teknium1@users.noreply.github.com> Date: Fri, 13 Mar 2026 21:56:12 -0700 Subject: [PATCH] test: fix stale CI assumptions in parser and quick-command coverage (#1236) - update managed-server compatibility tests to match the current ServerManager.tool_parser wiring used by hermes_base_env - make quick-command CLI assertions accept Rich Text objects, which is how ANSI-safe output is rendered now - set HERMES_HOME explicitly in the Discord auto-thread config bridge test so it loads the intended temporary config file Validated with the targeted test set and the full pytest suite. --- tests/gateway/test_discord_slash_commands.py | 1 + tests/test_managed_server_tool_support.py | 50 ++++++++++---------- tests/test_quick_commands.py | 15 +++++- 3 files changed, 39 insertions(+), 27 deletions(-) diff --git a/tests/gateway/test_discord_slash_commands.py b/tests/gateway/test_discord_slash_commands.py index 0b8cb04c7..78141a639 100644 --- a/tests/gateway/test_discord_slash_commands.py +++ b/tests/gateway/test_discord_slash_commands.py @@ -425,6 +425,7 @@ def test_discord_auto_thread_config_bridge(monkeypatch, tmp_path): })) monkeypatch.delenv("DISCORD_AUTO_THREAD", raising=False) + monkeypatch.setenv("HERMES_HOME", str(hermes_dir)) monkeypatch.setattr(Path, "home", lambda: tmp_path) from gateway.config import load_gateway_config diff --git a/tests/test_managed_server_tool_support.py b/tests/test_managed_server_tool_support.py index 2ab6abb08..92cf83f5c 100644 --- a/tests/test_managed_server_tool_support.py +++ b/tests/test_managed_server_tool_support.py @@ -1,11 +1,10 @@ """ -Tests for ManagedServer tool_call_parser integration. +Tests for ManagedServer / tool-parser integration. Validates that: -1. ManagedServer accepts tool_call_parser parameter (tool_call_support branch) -2. ServerManager.managed_server() passes tool_call_parser through -3. The parser's parse() output is correctly attached to ChatCompletion responses -4. hermes-agent's tool_call_parsers are compatible with ManagedServer's expectations +1. The installed atroposlib API still matches Hermes's expectations +2. Hermes's parser registry remains compatible with ManagedServer parsing +3. HermesAgentBaseEnv wires the selected parser into ServerManager correctly These tests verify the contract between hermes-agent's environments/ code and atroposlib's ManagedServer. They detect API incompatibilities early. @@ -142,37 +141,38 @@ class TestParserCompatibility: class TestBaseEnvCompatibility: - """Test that hermes_base_env.py's managed_server() call matches the API.""" + """Test that hermes_base_env.py's tool-parser wiring matches the current API.""" - def test_hermes_base_env_managed_server_call_pattern(self): - """ - Verify that hermes_base_env.py passes tool_call_parser to managed_server(). - This is a source-level check — the actual managed_server() call must match. - """ + def test_hermes_base_env_sets_server_manager_tool_parser(self): + """Hermes wires parser selection through ServerManager.tool_parser.""" import ast base_env_path = Path(__file__).parent.parent / "environments" / "hermes_base_env.py" source = base_env_path.read_text() tree = ast.parse(source) - # Find the managed_server() call - found_tool_call_parser_kwarg = False + found_assignment = False for node in ast.walk(tree): - if isinstance(node, ast.Call): - # Look for self.server.managed_server(...) - if isinstance(node.func, ast.Attribute) and node.func.attr == "managed_server": - for kw in node.keywords: - if kw.arg == "tool_call_parser": - found_tool_call_parser_kwarg = True + if isinstance(node, ast.Assign): + for target in node.targets: + if isinstance(target, ast.Attribute) and target.attr == "tool_parser": + parent = target.value + if ( + isinstance(parent, ast.Attribute) + and parent.attr == "server" + and isinstance(parent.value, ast.Name) + and parent.value.id == "self" + ): + found_assignment = True - assert found_tool_call_parser_kwarg, ( - "hermes_base_env.py should pass tool_call_parser= to managed_server()" + assert found_assignment, ( + "hermes_base_env.py should set self.server.tool_parser from config.tool_call_parser" ) - def test_hermes_base_env_uses_get_parser(self): - """Verify hermes_base_env imports and uses get_parser from tool_call_parsers.""" + def test_hermes_base_env_uses_config_tool_call_parser(self): + """Verify hermes_base_env uses the config field rather than a local parser instance.""" base_env_path = Path(__file__).parent.parent / "environments" / "hermes_base_env.py" source = base_env_path.read_text() - assert "from environments.tool_call_parsers import get_parser" in source - assert "get_parser(" in source + assert 'tool_call_parser: str = Field(' in source + assert 'self.server.tool_parser = config.tool_call_parser' in source diff --git a/tests/test_quick_commands.py b/tests/test_quick_commands.py index c34a3d052..67e93c1d0 100644 --- a/tests/test_quick_commands.py +++ b/tests/test_quick_commands.py @@ -1,6 +1,7 @@ """Tests for user-defined quick commands that bypass the agent loop.""" import subprocess from unittest.mock import MagicMock, patch, AsyncMock +from rich.text import Text import pytest @@ -9,6 +10,12 @@ import pytest class TestCLIQuickCommands: """Test quick command dispatch in HermesCLI.process_command.""" + @staticmethod + def _printed_plain(call_arg): + if isinstance(call_arg, Text): + return call_arg.plain + return str(call_arg) + def _make_cli(self, quick_commands): from cli import HermesCLI cli = HermesCLI.__new__(HermesCLI) @@ -22,7 +29,9 @@ class TestCLIQuickCommands: cli = self._make_cli({"dn": {"type": "exec", "command": "echo daily-note"}}) result = cli.process_command("/dn") assert result is True - cli.console.print.assert_called_once_with("daily-note") + cli.console.print.assert_called_once() + printed = self._printed_plain(cli.console.print.call_args[0][0]) + assert printed == "daily-note" def test_exec_command_stderr_shown_on_no_stdout(self): cli = self._make_cli({"err": {"type": "exec", "command": "echo error >&2"}}) @@ -57,7 +66,9 @@ class TestCLIQuickCommands: cli = self._make_cli({"mygif": {"type": "exec", "command": "echo overridden"}}) with patch("cli._skill_commands", {"/mygif": {"name": "gif-search"}}): cli.process_command("/mygif") - cli.console.print.assert_called_once_with("overridden") + cli.console.print.assert_called_once() + printed = self._printed_plain(cli.console.print.call_args[0][0]) + assert printed == "overridden" def test_unknown_command_still_shows_error(self): cli = self._make_cli({})