emma-hermes/tests/acp/test_entry.py

197 lines
5.5 KiB
Python

"""Tests for acp_adapter.entry startup wiring."""
import sys
import acp
import pytest
from acp_adapter import entry
def test_main_enables_unstable_protocol(monkeypatch):
calls = {}
async def fake_run_agent(agent, **kwargs):
calls["kwargs"] = kwargs
monkeypatch.setattr(entry, "_setup_logging", lambda: None)
monkeypatch.setattr(entry, "_load_env", lambda: None)
monkeypatch.setattr(acp, "run_agent", fake_run_agent)
entry.main([])
assert calls["kwargs"]["use_unstable_protocol"] is True
def test_main_version_prints_without_starting_server(monkeypatch, capsys):
monkeypatch.setattr(entry, "_setup_logging", lambda: (_ for _ in ()).throw(AssertionError("started server")))
entry.main(["--version"])
output = capsys.readouterr().out.strip()
assert output
assert "Starting hermes-agent ACP adapter" not in output
def test_main_check_prints_ok_without_starting_server(monkeypatch, capsys):
monkeypatch.setattr(entry, "_setup_logging", lambda: (_ for _ in ()).throw(AssertionError("started server")))
entry.main(["--check"])
assert capsys.readouterr().out.strip() == "Hermes ACP check OK"
def test_main_setup_runs_model_configuration(monkeypatch):
calls = {}
def fake_hermes_main():
calls["argv"] = sys.argv[:]
monkeypatch.setattr("hermes_cli.main.main", fake_hermes_main)
# Pretend stdin is not a TTY so the follow-up browser prompt is skipped.
# That keeps this test focused on the model-setup wiring; the
# browser-prompt path has its own test below.
monkeypatch.setattr("sys.stdin.isatty", lambda: False)
entry.main(["--setup"])
assert calls["argv"][1:] == ["model"]
def test_main_setup_offers_browser_install_when_tty(monkeypatch):
"""When stdin is a TTY and the user answers yes, model setup is followed
by a browser-tools bootstrap call."""
monkeypatch.setattr("hermes_cli.main.main", lambda: None)
monkeypatch.setattr("sys.stdin.isatty", lambda: True)
monkeypatch.setattr("builtins.input", lambda *_args, **_kwargs: "y")
bootstrap_calls = []
monkeypatch.setattr(
entry,
"_run_setup_browser",
lambda assume_yes=False: bootstrap_calls.append(assume_yes) or 0,
)
entry.main(["--setup"])
assert bootstrap_calls == [False]
def test_main_setup_skips_browser_prompt_on_no(monkeypatch):
monkeypatch.setattr("hermes_cli.main.main", lambda: None)
monkeypatch.setattr("sys.stdin.isatty", lambda: True)
monkeypatch.setattr("builtins.input", lambda *_args, **_kwargs: "")
called = []
monkeypatch.setattr(
entry,
"_run_setup_browser",
lambda assume_yes=False: called.append(assume_yes) or 0,
)
entry.main(["--setup"])
assert called == []
def test_main_setup_browser_invokes_bundled_script(monkeypatch):
"""`hermes-acp --setup-browser` must shell out to the bundled bootstrap
script — never reimplement the install logic inline."""
monkeypatch.setattr("platform.system", lambda: "Linux")
captured = {}
def fake_run(cmd, check=False):
captured["cmd"] = cmd
class _R:
returncode = 0
return _R()
monkeypatch.setattr("subprocess.run", fake_run)
entry.main(["--setup-browser"])
assert captured["cmd"][0] == "bash"
assert captured["cmd"][1].endswith("bootstrap_browser_tools.sh")
# --yes is NOT passed when the flag is absent.
assert "--yes" not in captured["cmd"]
def test_main_setup_browser_forwards_yes_flag(monkeypatch):
monkeypatch.setattr("platform.system", lambda: "Linux")
captured = {}
def fake_run(cmd, check=False):
captured["cmd"] = cmd
class _R:
returncode = 0
return _R()
monkeypatch.setattr("subprocess.run", fake_run)
entry.main(["--setup-browser", "--yes"])
assert "--yes" in captured["cmd"]
def test_main_setup_browser_uses_powershell_on_windows(monkeypatch):
monkeypatch.setattr("platform.system", lambda: "Windows")
captured = {}
def fake_run(cmd, check=False):
captured["cmd"] = cmd
class _R:
returncode = 0
return _R()
monkeypatch.setattr("subprocess.run", fake_run)
entry.main(["--setup-browser", "--yes"])
assert captured["cmd"][0] == "powershell.exe"
assert any(part.endswith("bootstrap_browser_tools.ps1") for part in captured["cmd"])
assert "-Yes" in captured["cmd"]
def test_main_setup_browser_propagates_failure(monkeypatch):
monkeypatch.setattr("platform.system", lambda: "Linux")
class _R:
returncode = 7
monkeypatch.setattr("subprocess.run", lambda cmd, check=False: _R())
with pytest.raises(SystemExit) as excinfo:
entry.main(["--setup-browser"])
assert excinfo.value.code == 7
def test_bootstrap_scripts_ship_with_package():
"""The package-data wiring (pyproject.toml) must include the bootstrap
scripts — otherwise `--setup-browser` 404s at runtime."""
from pathlib import Path
bootstrap_dir = Path(entry.__file__).resolve().parent / "bootstrap"
sh = bootstrap_dir / "bootstrap_browser_tools.sh"
ps1 = bootstrap_dir / "bootstrap_browser_tools.ps1"
assert sh.is_file(), f"missing bundled script: {sh}"
assert ps1.is_file(), f"missing bundled script: {ps1}"
sh_text = sh.read_text(encoding="utf-8")
ps1_text = ps1.read_text(encoding="utf-8")
# Sanity: scripts know how to find the Hermes-managed Node prefix.
assert "HERMES_HOME" in sh_text
assert "agent-browser" in sh_text
assert "HermesHome" in ps1_text
assert "agent-browser" in ps1_text