mirror of
https://github.com/NousResearch/hermes-agent.git
synced 2026-04-25 00:51:20 +00:00
fix: cover headless first-run setup flow
This commit is contained in:
parent
4aa94ae7cc
commit
9492f42aa7
4 changed files with 119 additions and 37 deletions
|
|
@ -1,43 +1,94 @@
|
|||
"""Tests for non-interactive setup wizard behavior."""
|
||||
"""Tests for non-interactive setup and first-run headless behavior."""
|
||||
|
||||
from argparse import Namespace
|
||||
from unittest.mock import patch
|
||||
|
||||
import pytest
|
||||
from unittest.mock import patch, MagicMock
|
||||
|
||||
|
||||
def _make_args(**kwargs):
|
||||
args = MagicMock()
|
||||
args.non_interactive = kwargs.get("non_interactive", False)
|
||||
args.section = kwargs.get("section", None)
|
||||
args.reset = kwargs.get("reset", False)
|
||||
return args
|
||||
def _make_setup_args(**overrides):
|
||||
return Namespace(
|
||||
non_interactive=overrides.get("non_interactive", False),
|
||||
section=overrides.get("section", None),
|
||||
reset=overrides.get("reset", False),
|
||||
)
|
||||
|
||||
|
||||
def _make_chat_args(**overrides):
|
||||
return Namespace(
|
||||
continue_last=overrides.get("continue_last", None),
|
||||
resume=overrides.get("resume", None),
|
||||
model=overrides.get("model", None),
|
||||
provider=overrides.get("provider", None),
|
||||
toolsets=overrides.get("toolsets", None),
|
||||
verbose=overrides.get("verbose", False),
|
||||
query=overrides.get("query", None),
|
||||
worktree=overrides.get("worktree", False),
|
||||
yolo=overrides.get("yolo", False),
|
||||
pass_session_id=overrides.get("pass_session_id", False),
|
||||
quiet=overrides.get("quiet", False),
|
||||
checkpoints=overrides.get("checkpoints", False),
|
||||
)
|
||||
|
||||
|
||||
class TestNonInteractiveSetup:
|
||||
"""Verify setup wizard exits cleanly in non-interactive environments."""
|
||||
"""Verify setup paths exit cleanly in headless/non-interactive environments."""
|
||||
|
||||
def test_non_interactive_flag_skips_wizard(self, capsys):
|
||||
"""--non-interactive flag should print help and return without hanging."""
|
||||
"""--non-interactive should print guidance and not enter the wizard."""
|
||||
from hermes_cli.setup import run_setup_wizard
|
||||
args = _make_args(non_interactive=True)
|
||||
|
||||
with patch("hermes_cli.setup.ensure_hermes_home"), \
|
||||
patch("hermes_cli.setup.load_config", return_value={}), \
|
||||
patch("hermes_cli.setup.get_hermes_home", return_value="/tmp/.hermes"):
|
||||
args = _make_setup_args(non_interactive=True)
|
||||
|
||||
with (
|
||||
patch("hermes_cli.setup.ensure_hermes_home"),
|
||||
patch("hermes_cli.setup.load_config", return_value={}),
|
||||
patch("hermes_cli.setup.get_hermes_home", return_value="/tmp/.hermes"),
|
||||
patch("hermes_cli.auth.get_active_provider", side_effect=AssertionError("wizard continued")),
|
||||
patch("builtins.input", side_effect=AssertionError("input should not be called")),
|
||||
):
|
||||
run_setup_wizard(args)
|
||||
|
||||
out = capsys.readouterr().out
|
||||
assert "hermes config set" in out
|
||||
assert "hermes config set model.provider custom" in out
|
||||
|
||||
def test_no_tty_skips_wizard(self, capsys):
|
||||
"""When stdin has no TTY, wizard should exit with helpful message."""
|
||||
"""When stdin has no TTY, the setup wizard should print guidance and return."""
|
||||
from hermes_cli.setup import run_setup_wizard
|
||||
args = _make_args(non_interactive=False)
|
||||
|
||||
with patch("hermes_cli.setup.ensure_hermes_home"), \
|
||||
patch("hermes_cli.setup.load_config", return_value={}), \
|
||||
patch("hermes_cli.setup.get_hermes_home", return_value="/tmp/.hermes"), \
|
||||
patch("sys.stdin") as mock_stdin:
|
||||
args = _make_setup_args(non_interactive=False)
|
||||
|
||||
with (
|
||||
patch("hermes_cli.setup.ensure_hermes_home"),
|
||||
patch("hermes_cli.setup.load_config", return_value={}),
|
||||
patch("hermes_cli.setup.get_hermes_home", return_value="/tmp/.hermes"),
|
||||
patch("hermes_cli.auth.get_active_provider", side_effect=AssertionError("wizard continued")),
|
||||
patch("sys.stdin") as mock_stdin,
|
||||
patch("builtins.input", side_effect=AssertionError("input should not be called")),
|
||||
):
|
||||
mock_stdin.isatty.return_value = False
|
||||
run_setup_wizard(args)
|
||||
|
||||
out = capsys.readouterr().out
|
||||
assert "hermes config set" in out
|
||||
assert "hermes config set model.provider custom" in out
|
||||
|
||||
def test_chat_first_run_headless_skips_setup_prompt(self, capsys):
|
||||
"""Bare `hermes` should not prompt for input when no provider exists and stdin is headless."""
|
||||
from hermes_cli.main import cmd_chat
|
||||
|
||||
args = _make_chat_args()
|
||||
|
||||
with (
|
||||
patch("hermes_cli.main._has_any_provider_configured", return_value=False),
|
||||
patch("hermes_cli.main.cmd_setup") as mock_setup,
|
||||
patch("sys.stdin") as mock_stdin,
|
||||
patch("builtins.input", side_effect=AssertionError("input should not be called")),
|
||||
):
|
||||
mock_stdin.isatty.return_value = False
|
||||
with pytest.raises(SystemExit) as exc:
|
||||
cmd_chat(args)
|
||||
|
||||
assert exc.value.code == 1
|
||||
mock_setup.assert_not_called()
|
||||
out = capsys.readouterr().out
|
||||
assert "hermes config set model.provider custom" in out
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue