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:
@@ -425,6 +425,7 @@ def test_discord_auto_thread_config_bridge(monkeypatch, tmp_path):
|
|||||||
}))
|
}))
|
||||||
|
|
||||||
monkeypatch.delenv("DISCORD_AUTO_THREAD", raising=False)
|
monkeypatch.delenv("DISCORD_AUTO_THREAD", raising=False)
|
||||||
|
monkeypatch.setenv("HERMES_HOME", str(hermes_dir))
|
||||||
monkeypatch.setattr(Path, "home", lambda: tmp_path)
|
monkeypatch.setattr(Path, "home", lambda: tmp_path)
|
||||||
|
|
||||||
from gateway.config import load_gateway_config
|
from gateway.config import load_gateway_config
|
||||||
|
|||||||
@@ -1,11 +1,10 @@
|
|||||||
"""
|
"""
|
||||||
Tests for ManagedServer tool_call_parser integration.
|
Tests for ManagedServer / tool-parser integration.
|
||||||
|
|
||||||
Validates that:
|
Validates that:
|
||||||
1. ManagedServer accepts tool_call_parser parameter (tool_call_support branch)
|
1. The installed atroposlib API still matches Hermes's expectations
|
||||||
2. ServerManager.managed_server() passes tool_call_parser through
|
2. Hermes's parser registry remains compatible with ManagedServer parsing
|
||||||
3. The parser's parse() output is correctly attached to ChatCompletion responses
|
3. HermesAgentBaseEnv wires the selected parser into ServerManager correctly
|
||||||
4. hermes-agent's tool_call_parsers are compatible with ManagedServer's expectations
|
|
||||||
|
|
||||||
These tests verify the contract between hermes-agent's environments/ code
|
These tests verify the contract between hermes-agent's environments/ code
|
||||||
and atroposlib's ManagedServer. They detect API incompatibilities early.
|
and atroposlib's ManagedServer. They detect API incompatibilities early.
|
||||||
@@ -142,37 +141,38 @@ class TestParserCompatibility:
|
|||||||
|
|
||||||
|
|
||||||
class TestBaseEnvCompatibility:
|
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):
|
def test_hermes_base_env_sets_server_manager_tool_parser(self):
|
||||||
"""
|
"""Hermes wires parser selection through ServerManager.tool_parser."""
|
||||||
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.
|
|
||||||
"""
|
|
||||||
import ast
|
import ast
|
||||||
|
|
||||||
base_env_path = Path(__file__).parent.parent / "environments" / "hermes_base_env.py"
|
base_env_path = Path(__file__).parent.parent / "environments" / "hermes_base_env.py"
|
||||||
source = base_env_path.read_text()
|
source = base_env_path.read_text()
|
||||||
tree = ast.parse(source)
|
tree = ast.parse(source)
|
||||||
|
|
||||||
# Find the managed_server() call
|
found_assignment = False
|
||||||
found_tool_call_parser_kwarg = False
|
|
||||||
for node in ast.walk(tree):
|
for node in ast.walk(tree):
|
||||||
if isinstance(node, ast.Call):
|
if isinstance(node, ast.Assign):
|
||||||
# Look for self.server.managed_server(...)
|
for target in node.targets:
|
||||||
if isinstance(node.func, ast.Attribute) and node.func.attr == "managed_server":
|
if isinstance(target, ast.Attribute) and target.attr == "tool_parser":
|
||||||
for kw in node.keywords:
|
parent = target.value
|
||||||
if kw.arg == "tool_call_parser":
|
if (
|
||||||
found_tool_call_parser_kwarg = True
|
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, (
|
assert found_assignment, (
|
||||||
"hermes_base_env.py should pass tool_call_parser= to managed_server()"
|
"hermes_base_env.py should set self.server.tool_parser from config.tool_call_parser"
|
||||||
)
|
)
|
||||||
|
|
||||||
def test_hermes_base_env_uses_get_parser(self):
|
def test_hermes_base_env_uses_config_tool_call_parser(self):
|
||||||
"""Verify hermes_base_env imports and uses get_parser from tool_call_parsers."""
|
"""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"
|
base_env_path = Path(__file__).parent.parent / "environments" / "hermes_base_env.py"
|
||||||
source = base_env_path.read_text()
|
source = base_env_path.read_text()
|
||||||
|
|
||||||
assert "from environments.tool_call_parsers import get_parser" in source
|
assert 'tool_call_parser: str = Field(' in source
|
||||||
assert "get_parser(" in source
|
assert 'self.server.tool_parser = config.tool_call_parser' in source
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
"""Tests for user-defined quick commands that bypass the agent loop."""
|
"""Tests for user-defined quick commands that bypass the agent loop."""
|
||||||
import subprocess
|
import subprocess
|
||||||
from unittest.mock import MagicMock, patch, AsyncMock
|
from unittest.mock import MagicMock, patch, AsyncMock
|
||||||
|
from rich.text import Text
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
|
|
||||||
@@ -9,6 +10,12 @@ import pytest
|
|||||||
class TestCLIQuickCommands:
|
class TestCLIQuickCommands:
|
||||||
"""Test quick command dispatch in HermesCLI.process_command."""
|
"""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):
|
def _make_cli(self, quick_commands):
|
||||||
from cli import HermesCLI
|
from cli import HermesCLI
|
||||||
cli = HermesCLI.__new__(HermesCLI)
|
cli = HermesCLI.__new__(HermesCLI)
|
||||||
@@ -22,7 +29,9 @@ class TestCLIQuickCommands:
|
|||||||
cli = self._make_cli({"dn": {"type": "exec", "command": "echo daily-note"}})
|
cli = self._make_cli({"dn": {"type": "exec", "command": "echo daily-note"}})
|
||||||
result = cli.process_command("/dn")
|
result = cli.process_command("/dn")
|
||||||
assert result is True
|
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):
|
def test_exec_command_stderr_shown_on_no_stdout(self):
|
||||||
cli = self._make_cli({"err": {"type": "exec", "command": "echo error >&2"}})
|
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"}})
|
cli = self._make_cli({"mygif": {"type": "exec", "command": "echo overridden"}})
|
||||||
with patch("cli._skill_commands", {"/mygif": {"name": "gif-search"}}):
|
with patch("cli._skill_commands", {"/mygif": {"name": "gif-search"}}):
|
||||||
cli.process_command("/mygif")
|
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):
|
def test_unknown_command_still_shows_error(self):
|
||||||
cli = self._make_cli({})
|
cli = self._make_cli({})
|
||||||
|
|||||||
Reference in New Issue
Block a user