mirror of
https://github.com/NousResearch/hermes-agent.git
synced 2026-07-30 19:09:28 +00:00
Second, deeper pass over tools/gateway/hermes_cli plus first pass over the trees wave 1 missed (acp, acp_adapter, skills, computer_use, docker, dashboard, conformance, monitoring, secret_sources, hermes_state, providers). Same rubric as wave 1 (AGENTS.md test policy); security, alternation/caching invariants, issue-number regressions, and E2E kept. Real test-quality fixes found and rooted out along the way: - tests/tools/test_command_guards.py made real auxiliary-LLM HTTPS calls (DEFAULT_CONFIG smart-approval leaked in) — pinned approval mode=manual via autouse fixture: 17.4s → 0.4s. - test_model_switch_custom_providers.py / test_user_providers_model_switch.py silently probed live provider catalogs (~2s/test) — stubbed cached_provider_model_ids/provider_model_ids/fetch_api_models. - test_telegram_noise_filter.py: 15-platform copy-paste matrix over shared gateway.run logic → 3 representative platforms (55s → 3.9s). - test_gateway_shutdown.py: stop()'s 5s interrupt-deadline loop spun on MagicMock agents — interrupt.side_effect now clears _running_agents (22s → 1.0s). - test_gateway_inactivity_timeout.py poll-harness timings shrunk 3-5x (24s → 1.1s); test_mcp_stability.py backoff/SIGTERM-grace sleeps patched (15.4s → 2.5s); test_async_delegation.py negative-drain wait 5s → 0.5s. - test_telegram_init_deadline.py: loop-block margin restored to 1.0s with rationale comment — the watchdog-dump assertion needs the loop blocked well past deadline+grace under parallel load (flaked once in the 40-worker verification run at a 0.2s margin). Verification: full hermetic suite via scripts/run_tests.sh — 2,438 files, 21,718 tests passed, 0 failed, 293.9s wall. Suite totals vs original baseline: 46,820 → 19,757 test functions (−57.8%), wall 583.5s → 293.9s (−50%), subprocess CPU 13,564s → 11,623s.
82 lines
2.9 KiB
Python
82 lines
2.9 KiB
Python
"""Tests for non-interactive setup and first-run headless behavior."""
|
|
|
|
from argparse import Namespace
|
|
from unittest.mock import patch
|
|
|
|
import pytest
|
|
from hermes_cli.config import DEFAULT_CONFIG, load_config, save_config
|
|
|
|
|
|
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 paths exit cleanly in headless/non-interactive environments."""
|
|
|
|
|
|
|
|
|
|
|
|
def test_reset_flag_rewrites_config_before_noninteractive_exit(self, tmp_path, monkeypatch, capsys):
|
|
"""--reset should rewrite config.yaml even when the wizard cannot run interactively."""
|
|
from hermes_cli.setup import run_setup_wizard
|
|
|
|
monkeypatch.setenv("HERMES_HOME", str(tmp_path))
|
|
cfg = load_config()
|
|
cfg["model"] = {"provider": "custom", "base_url": "http://localhost:8080/v1", "default": "llama3"}
|
|
cfg["agent"]["max_turns"] = 12
|
|
save_config(cfg)
|
|
|
|
args = _make_setup_args(non_interactive=True, reset=True)
|
|
|
|
run_setup_wizard(args)
|
|
|
|
reloaded = load_config()
|
|
assert reloaded["model"] == DEFAULT_CONFIG["model"]
|
|
assert reloaded["agent"]["max_turns"] == DEFAULT_CONFIG["agent"]["max_turns"]
|
|
out = capsys.readouterr().out
|
|
assert "Configuration reset to defaults." 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
|
|
|