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.
This commit is contained in:
Teknium
2026-03-13 21:56:12 -07:00
committed by GitHub
parent 7c3cb9bb31
commit af8791a49d
3 changed files with 39 additions and 27 deletions

View File

@@ -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

View File

@@ -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

View File

@@ -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({})