feat: slash command parity + skill autocomplete — v0.50.91 (PR #711)
Combines PR #618 (@renheqiang) slash command parity (/retry /undo /stop /title /status /voice) with PR #701 (@franksong2702) skill autocomplete. 1469 tests pass. Closes #460. Co-authored-by: renheqiang <renheqiang@users.noreply.github.com> Co-authored-by: franksong2702 <franksong2702@users.noreply.github.com>
This commit is contained in:
84
tests/test_commands_endpoint.py
Normal file
84
tests/test_commands_endpoint.py
Normal file
@@ -0,0 +1,84 @@
|
||||
"""Tests for GET /api/commands -- exposes hermes-agent COMMAND_REGISTRY."""
|
||||
import json
|
||||
import urllib.request
|
||||
|
||||
import pytest
|
||||
|
||||
from tests.conftest import TEST_BASE, requires_agent_modules
|
||||
|
||||
|
||||
def _get(path):
|
||||
"""GET helper -- returns parsed JSON or raises HTTPError."""
|
||||
with urllib.request.urlopen(TEST_BASE + path, timeout=10) as r:
|
||||
return json.loads(r.read())
|
||||
|
||||
|
||||
@requires_agent_modules
|
||||
def test_commands_endpoint_returns_list():
|
||||
"""GET /api/commands returns a JSON object with a 'commands' list."""
|
||||
body = _get('/api/commands')
|
||||
assert 'commands' in body
|
||||
assert isinstance(body['commands'], list)
|
||||
assert len(body['commands']) > 0
|
||||
|
||||
|
||||
@requires_agent_modules
|
||||
def test_commands_endpoint_includes_help():
|
||||
"""The 'help' command must always be present (it's not cli_only)."""
|
||||
body = _get('/api/commands')
|
||||
names = {c['name'] for c in body['commands']}
|
||||
assert 'help' in names
|
||||
|
||||
|
||||
@requires_agent_modules
|
||||
def test_commands_endpoint_command_shape():
|
||||
"""Each command entry has the required fields."""
|
||||
body = _get('/api/commands')
|
||||
cmd = next(c for c in body['commands'] if c['name'] == 'help')
|
||||
required = {
|
||||
'name', 'description', 'category', 'aliases',
|
||||
'args_hint', 'subcommands', 'cli_only', 'gateway_only',
|
||||
}
|
||||
assert set(cmd.keys()) >= required
|
||||
assert isinstance(cmd['aliases'], list)
|
||||
assert isinstance(cmd['subcommands'], list)
|
||||
assert isinstance(cmd['cli_only'], bool)
|
||||
assert isinstance(cmd['gateway_only'], bool)
|
||||
|
||||
|
||||
@requires_agent_modules
|
||||
def test_commands_endpoint_excludes_gateway_only_and_never_expose():
|
||||
"""gateway_only commands and the _NEVER_EXPOSE set are filtered out."""
|
||||
body = _get('/api/commands')
|
||||
names = {c['name'] for c in body['commands']}
|
||||
# /sethome, /restart, /update are gateway_only; /commands is in _NEVER_EXPOSE
|
||||
for name in ('sethome', 'restart', 'update', 'commands'):
|
||||
assert name not in names, f"{name} must be excluded from /api/commands"
|
||||
|
||||
|
||||
@requires_agent_modules
|
||||
def test_commands_endpoint_keeps_new_with_reset_alias():
|
||||
"""The 'new' command stays exposed and carries its 'reset' alias."""
|
||||
body = _get('/api/commands')
|
||||
new_cmd = next(c for c in body['commands'] if c['name'] == 'new')
|
||||
assert 'reset' in new_cmd['aliases']
|
||||
|
||||
|
||||
def test_list_commands_returns_empty_for_empty_registry():
|
||||
"""list_commands(_registry=[]) returns [] -- the same path as when
|
||||
hermes_cli is missing (the empty-or-missing case)."""
|
||||
from api.commands import list_commands
|
||||
assert list_commands(_registry=[]) == []
|
||||
|
||||
|
||||
def test_list_commands_degrades_when_agent_missing(monkeypatch):
|
||||
"""If hermes_cli.commands is not importable, list_commands() returns []
|
||||
via the ImportError path. Verified by stubbing sys.modules; test cleanup
|
||||
is handled by monkeypatch + the fact that we don't reload api.commands."""
|
||||
import sys
|
||||
monkeypatch.setitem(sys.modules, 'hermes_cli.commands', None)
|
||||
# NOTE: we do NOT reload api.commands. The lazy import inside
|
||||
# list_commands() will re-attempt the import on each call and hit
|
||||
# the stubbed-None module, raising ImportError, taking the fallback path.
|
||||
from api.commands import list_commands
|
||||
assert list_commands() == []
|
||||
Reference in New Issue
Block a user