mirror of
https://github.com/NousResearch/hermes-agent.git
synced 2026-04-25 00:51:20 +00:00
fix(tests): resolve 10 CI failures across hooks, tiktoken, plugins (#3848)
test_hooks.py (7 failures): Built-in boot-md hook was always loaded
by _register_builtin_hooks(), adding +1 to every expected hook count.
Mock out built-in registration in TestDiscoverAndLoad so tests isolate
user-hook discovery logic.
test_tool_token_estimation.py (2 failures): tiktoken is not in
core/[all] dependencies. The estimation function gracefully returns {}
when tiktoken is missing, but tests expected non-empty results. Added
skipif markers for tests that need tiktoken.
test_plugins_cmd.py (1 failure): bare 'hermes plugins' now dispatches
to cmd_toggle() (interactive curses UI) instead of cmd_list(). Updated
test to match the new behavior.
This commit is contained in:
parent
3e2c8c529b
commit
2d264a4562
3 changed files with 27 additions and 10 deletions
|
|
@ -29,13 +29,18 @@ class TestHookRegistryInit:
|
||||||
assert reg._handlers == {}
|
assert reg._handlers == {}
|
||||||
|
|
||||||
|
|
||||||
|
def _patch_no_builtins(reg):
|
||||||
|
"""Suppress built-in hook registration so tests only exercise user-hook discovery."""
|
||||||
|
return patch.object(reg, "_register_builtin_hooks")
|
||||||
|
|
||||||
|
|
||||||
class TestDiscoverAndLoad:
|
class TestDiscoverAndLoad:
|
||||||
def test_loads_valid_hook(self, tmp_path):
|
def test_loads_valid_hook(self, tmp_path):
|
||||||
_create_hook(tmp_path, "my-hook", '["agent:start"]',
|
_create_hook(tmp_path, "my-hook", '["agent:start"]',
|
||||||
"def handle(event_type, context):\n pass\n")
|
"def handle(event_type, context):\n pass\n")
|
||||||
|
|
||||||
reg = HookRegistry()
|
reg = HookRegistry()
|
||||||
with patch("gateway.hooks.HOOKS_DIR", tmp_path):
|
with patch("gateway.hooks.HOOKS_DIR", tmp_path), _patch_no_builtins(reg):
|
||||||
reg.discover_and_load()
|
reg.discover_and_load()
|
||||||
|
|
||||||
assert len(reg.loaded_hooks) == 1
|
assert len(reg.loaded_hooks) == 1
|
||||||
|
|
@ -48,7 +53,7 @@ class TestDiscoverAndLoad:
|
||||||
(hook_dir / "handler.py").write_text("def handle(e, c): pass\n")
|
(hook_dir / "handler.py").write_text("def handle(e, c): pass\n")
|
||||||
|
|
||||||
reg = HookRegistry()
|
reg = HookRegistry()
|
||||||
with patch("gateway.hooks.HOOKS_DIR", tmp_path):
|
with patch("gateway.hooks.HOOKS_DIR", tmp_path), _patch_no_builtins(reg):
|
||||||
reg.discover_and_load()
|
reg.discover_and_load()
|
||||||
|
|
||||||
assert len(reg.loaded_hooks) == 0
|
assert len(reg.loaded_hooks) == 0
|
||||||
|
|
@ -59,7 +64,7 @@ class TestDiscoverAndLoad:
|
||||||
(hook_dir / "HOOK.yaml").write_text("name: bad\nevents: ['agent:start']\n")
|
(hook_dir / "HOOK.yaml").write_text("name: bad\nevents: ['agent:start']\n")
|
||||||
|
|
||||||
reg = HookRegistry()
|
reg = HookRegistry()
|
||||||
with patch("gateway.hooks.HOOKS_DIR", tmp_path):
|
with patch("gateway.hooks.HOOKS_DIR", tmp_path), _patch_no_builtins(reg):
|
||||||
reg.discover_and_load()
|
reg.discover_and_load()
|
||||||
|
|
||||||
assert len(reg.loaded_hooks) == 0
|
assert len(reg.loaded_hooks) == 0
|
||||||
|
|
@ -71,7 +76,7 @@ class TestDiscoverAndLoad:
|
||||||
(hook_dir / "handler.py").write_text("def handle(e, c): pass\n")
|
(hook_dir / "handler.py").write_text("def handle(e, c): pass\n")
|
||||||
|
|
||||||
reg = HookRegistry()
|
reg = HookRegistry()
|
||||||
with patch("gateway.hooks.HOOKS_DIR", tmp_path):
|
with patch("gateway.hooks.HOOKS_DIR", tmp_path), _patch_no_builtins(reg):
|
||||||
reg.discover_and_load()
|
reg.discover_and_load()
|
||||||
|
|
||||||
assert len(reg.loaded_hooks) == 0
|
assert len(reg.loaded_hooks) == 0
|
||||||
|
|
@ -83,14 +88,14 @@ class TestDiscoverAndLoad:
|
||||||
(hook_dir / "handler.py").write_text("def something_else(): pass\n")
|
(hook_dir / "handler.py").write_text("def something_else(): pass\n")
|
||||||
|
|
||||||
reg = HookRegistry()
|
reg = HookRegistry()
|
||||||
with patch("gateway.hooks.HOOKS_DIR", tmp_path):
|
with patch("gateway.hooks.HOOKS_DIR", tmp_path), _patch_no_builtins(reg):
|
||||||
reg.discover_and_load()
|
reg.discover_and_load()
|
||||||
|
|
||||||
assert len(reg.loaded_hooks) == 0
|
assert len(reg.loaded_hooks) == 0
|
||||||
|
|
||||||
def test_nonexistent_hooks_dir(self, tmp_path):
|
def test_nonexistent_hooks_dir(self, tmp_path):
|
||||||
reg = HookRegistry()
|
reg = HookRegistry()
|
||||||
with patch("gateway.hooks.HOOKS_DIR", tmp_path / "nonexistent"):
|
with patch("gateway.hooks.HOOKS_DIR", tmp_path / "nonexistent"), _patch_no_builtins(reg):
|
||||||
reg.discover_and_load()
|
reg.discover_and_load()
|
||||||
|
|
||||||
assert len(reg.loaded_hooks) == 0
|
assert len(reg.loaded_hooks) == 0
|
||||||
|
|
@ -102,7 +107,7 @@ class TestDiscoverAndLoad:
|
||||||
"def handle(e, c): pass\n")
|
"def handle(e, c): pass\n")
|
||||||
|
|
||||||
reg = HookRegistry()
|
reg = HookRegistry()
|
||||||
with patch("gateway.hooks.HOOKS_DIR", tmp_path):
|
with patch("gateway.hooks.HOOKS_DIR", tmp_path), _patch_no_builtins(reg):
|
||||||
reg.discover_and_load()
|
reg.discover_and_load()
|
||||||
|
|
||||||
assert len(reg.loaded_hooks) == 2
|
assert len(reg.loaded_hooks) == 2
|
||||||
|
|
|
||||||
|
|
@ -4,10 +4,20 @@ from unittest.mock import patch
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
|
# tiktoken is not in core/[all] deps — skip estimation tests when unavailable
|
||||||
|
_has_tiktoken = True
|
||||||
|
try:
|
||||||
|
import tiktoken # noqa: F401
|
||||||
|
except ImportError:
|
||||||
|
_has_tiktoken = False
|
||||||
|
|
||||||
|
_needs_tiktoken = pytest.mark.skipif(not _has_tiktoken, reason="tiktoken not installed")
|
||||||
|
|
||||||
|
|
||||||
# ─── Token Estimation Tests ──────────────────────────────────────────────────
|
# ─── Token Estimation Tests ──────────────────────────────────────────────────
|
||||||
|
|
||||||
|
|
||||||
|
@_needs_tiktoken
|
||||||
def test_estimate_tool_tokens_returns_positive_counts():
|
def test_estimate_tool_tokens_returns_positive_counts():
|
||||||
"""_estimate_tool_tokens should return a non-empty dict with positive values."""
|
"""_estimate_tool_tokens should return a non-empty dict with positive values."""
|
||||||
from hermes_cli.tools_config import _estimate_tool_tokens, _tool_token_cache
|
from hermes_cli.tools_config import _estimate_tool_tokens, _tool_token_cache
|
||||||
|
|
@ -26,6 +36,7 @@ def test_estimate_tool_tokens_returns_positive_counts():
|
||||||
assert count > 0, f"Tool {name} has non-positive token count: {count}"
|
assert count > 0, f"Tool {name} has non-positive token count: {count}"
|
||||||
|
|
||||||
|
|
||||||
|
@_needs_tiktoken
|
||||||
def test_estimate_tool_tokens_is_cached():
|
def test_estimate_tool_tokens_is_cached():
|
||||||
"""Second call should return the same cached dict object."""
|
"""Second call should return the same cached dict object."""
|
||||||
import hermes_cli.tools_config as tc
|
import hermes_cli.tools_config as tc
|
||||||
|
|
@ -60,6 +71,7 @@ def test_estimate_tool_tokens_returns_empty_when_tiktoken_unavailable(monkeypatc
|
||||||
tc._tool_token_cache = None
|
tc._tool_token_cache = None
|
||||||
|
|
||||||
|
|
||||||
|
@_needs_tiktoken
|
||||||
def test_estimate_tool_tokens_covers_known_tools():
|
def test_estimate_tool_tokens_covers_known_tools():
|
||||||
"""Should include schemas for well-known tools like terminal, web_search."""
|
"""Should include schemas for well-known tools like terminal, web_search."""
|
||||||
import hermes_cli.tools_config as tc
|
import hermes_cli.tools_config as tc
|
||||||
|
|
|
||||||
|
|
@ -150,11 +150,11 @@ class TestPluginsCommandDispatch:
|
||||||
plugins_command(args)
|
plugins_command(args)
|
||||||
mock_list.assert_called_once()
|
mock_list.assert_called_once()
|
||||||
|
|
||||||
@patch("hermes_cli.plugins_cmd.cmd_list")
|
@patch("hermes_cli.plugins_cmd.cmd_toggle")
|
||||||
def test_none_falls_through_to_list(self, mock_list):
|
def test_none_falls_through_to_toggle(self, mock_toggle):
|
||||||
args = self._make_args(None)
|
args = self._make_args(None)
|
||||||
plugins_command(args)
|
plugins_command(args)
|
||||||
mock_list.assert_called_once()
|
mock_toggle.assert_called_once()
|
||||||
|
|
||||||
@patch("hermes_cli.plugins_cmd.cmd_install")
|
@patch("hermes_cli.plugins_cmd.cmd_install")
|
||||||
def test_install_dispatches(self, mock_install):
|
def test_install_dispatches(self, mock_install):
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue