mirror of
https://github.com/NousResearch/hermes-agent.git
synced 2026-05-29 06:31:32 +00:00
test(cli): cover quiet-mode resume status lines routed to stderr
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.
This commit is contained in:
parent
25295e7ac9
commit
ffe11c14ec
1 changed files with 121 additions and 0 deletions
121
tests/cli/test_resume_quiet_stderr.py
Normal file
121
tests/cli/test_resume_quiet_stderr.py
Normal file
|
|
@ -0,0 +1,121 @@
|
|||
"""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
|
||||
Loading…
Add table
Add a link
Reference in a new issue