mirror of
https://github.com/NousResearch/hermes-agent.git
synced 2026-05-29 06:31:32 +00:00
4 tests: session-not-found in quiet mode -> stderr; in full mode -> stdout (unchanged); resumed banner in quiet mode -> stderr; has-no-messages in quiet mode -> stderr.
121 lines
4.8 KiB
Python
121 lines
4.8 KiB
Python
"""Tests for /resume status lines going to stderr in quiet mode (#11793).
|
|
|
|
The fix in cli._init_agent routes three messages to stderr when
|
|
``tool_progress_mode == "off"`` (set by ``hermes chat --quiet``):
|
|
|
|
* "Session not found: ..."
|
|
* "↻ Resumed session ... (N user messages, M total messages)"
|
|
* "Session ... found but has no messages. Starting fresh."
|
|
|
|
Interactive mode (tool_progress_mode == "full") still uses ChatConsole.
|
|
"""
|
|
|
|
from datetime import datetime
|
|
from unittest.mock import MagicMock, patch
|
|
|
|
import pytest
|
|
|
|
from cli import HermesCLI
|
|
|
|
|
|
def _make_cli(quiet=False, session_id="20260524_111111_xyz", db=None):
|
|
"""Build a minimal HermesCLI bound to only what _init_agent needs for
|
|
the resume code path: _resumed, _session_db, conversation_history,
|
|
session_id, and tool_progress_mode."""
|
|
cli = HermesCLI.__new__(HermesCLI)
|
|
cli.session_id = session_id
|
|
cli._resumed = True
|
|
cli.conversation_history = []
|
|
cli._session_db = db
|
|
cli.tool_progress_mode = "off" if quiet else "full"
|
|
cli.session_start = datetime.now()
|
|
cli.agent = None
|
|
# We need _init_agent to reach the resume block (line ~4757) but not
|
|
# proceed into actual AIAgent construction. _ensure_runtime_credentials
|
|
# must return True (False returns early at line 4743). _install_tool_callbacks,
|
|
# _ensure_tirith_security are stubbed; the resume block will either return
|
|
# False (session-not-found) or reach the eventual AIAgent() call which
|
|
# we'll let raise — we only check stdout/stderr printed BEFORE that.
|
|
cli._install_tool_callbacks = lambda: None
|
|
cli._ensure_tirith_security = lambda: None
|
|
cli._ensure_runtime_credentials = lambda: True
|
|
return cli
|
|
|
|
|
|
class TestResumeQuietStderr:
|
|
def test_session_not_found_goes_to_stderr_in_quiet_mode(self, capsys):
|
|
db = MagicMock()
|
|
db.get_session.return_value = None
|
|
cli = _make_cli(quiet=True, db=db)
|
|
|
|
with patch("cli._prepare_deferred_agent_startup"):
|
|
result = cli._init_agent()
|
|
|
|
captured = capsys.readouterr()
|
|
assert result is False
|
|
# stdout must stay clean
|
|
assert "Session not found" not in captured.out
|
|
# the resume status goes to stderr
|
|
assert "Session not found" in captured.err
|
|
assert "hermes sessions list" in captured.err
|
|
|
|
def test_session_not_found_goes_to_stdout_in_full_mode(self, capsys):
|
|
db = MagicMock()
|
|
db.get_session.return_value = None
|
|
cli = _make_cli(quiet=False, db=db)
|
|
|
|
with patch("cli._prepare_deferred_agent_startup"):
|
|
result = cli._init_agent()
|
|
|
|
captured = capsys.readouterr()
|
|
assert result is False
|
|
# Interactive mode keeps the existing _cprint path → stdout.
|
|
assert "Session not found" in captured.out
|
|
|
|
def test_resumed_banner_goes_to_stderr_in_quiet_mode(self, capsys):
|
|
db = MagicMock()
|
|
db.get_session.return_value = {"id": "20260524_111111_xyz", "title": "demo"}
|
|
db.resolve_resume_session_id.return_value = "20260524_111111_xyz"
|
|
db.get_messages_as_conversation.return_value = [
|
|
{"role": "user", "content": "hi"},
|
|
{"role": "assistant", "content": "hey"},
|
|
]
|
|
db._conn = MagicMock() # for the reopen execute() call
|
|
|
|
cli = _make_cli(quiet=True, db=db)
|
|
# Stop _init_agent right after the resume banner: prevent it from
|
|
# constructing a real AIAgent (the next code path).
|
|
with patch("cli._prepare_deferred_agent_startup"):
|
|
try:
|
|
cli._init_agent()
|
|
except Exception:
|
|
# The post-resume agent-init machinery may fail in this
|
|
# stubbed context (no API key, no real config) — we only
|
|
# care about the printed banner that comes earlier.
|
|
pass
|
|
|
|
captured = capsys.readouterr()
|
|
# Banner on stderr — stdout stays clean for automation.
|
|
assert "↻ Resumed session" not in captured.out
|
|
assert "↻ Resumed session" in captured.err
|
|
assert "20260524_111111_xyz" in captured.err
|
|
assert "demo" in captured.err
|
|
|
|
def test_no_messages_goes_to_stderr_in_quiet_mode(self, capsys):
|
|
db = MagicMock()
|
|
db.get_session.return_value = {"id": "20260524_111111_xyz"}
|
|
db.resolve_resume_session_id.return_value = "20260524_111111_xyz"
|
|
db.get_messages_as_conversation.return_value = []
|
|
db._conn = MagicMock()
|
|
|
|
cli = _make_cli(quiet=True, db=db)
|
|
with patch("cli._prepare_deferred_agent_startup"):
|
|
try:
|
|
cli._init_agent()
|
|
except Exception:
|
|
pass
|
|
|
|
captured = capsys.readouterr()
|
|
assert "has no messages" not in captured.out
|
|
assert "has no messages" in captured.err
|
|
assert "Starting fresh" in captured.err
|