mirror of
https://github.com/NousResearch/hermes-agent.git
synced 2026-06-11 08:42:11 +00:00
144 lines
5.2 KiB
Python
144 lines
5.2 KiB
Python
"""Tests for ``hermes gui`` desktop launcher wiring."""
|
|
|
|
from __future__ import annotations
|
|
|
|
import argparse
|
|
import subprocess
|
|
import sys
|
|
from pathlib import Path
|
|
from unittest.mock import patch
|
|
|
|
import pytest
|
|
|
|
from hermes_cli import main as cli_main
|
|
|
|
|
|
def _ns(**kw):
|
|
defaults = dict(
|
|
skip_build=False,
|
|
fake_boot=False,
|
|
ignore_existing=False,
|
|
hermes_root=None,
|
|
cwd=None,
|
|
)
|
|
defaults.update(kw)
|
|
return argparse.Namespace(**defaults)
|
|
|
|
|
|
def _make_desktop_tree(tmp_path: Path) -> Path:
|
|
root = tmp_path / "hermes-agent"
|
|
desktop_dir = root / "apps" / "desktop"
|
|
desktop_dir.mkdir(parents=True)
|
|
(desktop_dir / "package.json").write_text("{}", encoding="utf-8")
|
|
return root
|
|
|
|
|
|
def test_gui_installs_builds_and_launches_desktop(tmp_path, monkeypatch):
|
|
root = _make_desktop_tree(tmp_path)
|
|
desktop_dir = root / "apps" / "desktop"
|
|
monkeypatch.setattr(cli_main, "PROJECT_ROOT", root)
|
|
|
|
install_ok = subprocess.CompletedProcess(["npm", "ci"], 0)
|
|
build_ok = subprocess.CompletedProcess(["npm", "run", "build"], 0)
|
|
launch_ok = subprocess.CompletedProcess(["npm", "exec", "--", "electron", "."], 0)
|
|
|
|
with patch("hermes_cli.main.shutil.which", return_value="/usr/bin/npm"), \
|
|
patch("hermes_cli.main._run_npm_install_deterministic", return_value=install_ok) as mock_install, \
|
|
patch("hermes_cli.main.subprocess.run", side_effect=[build_ok, launch_ok]) as mock_run, \
|
|
pytest.raises(SystemExit) as exc:
|
|
cli_main.cmd_gui(_ns())
|
|
|
|
assert exc.value.code == 0
|
|
mock_install.assert_called_once_with("/usr/bin/npm", root, capture_output=False)
|
|
assert mock_run.call_args_list[0].args[0] == ["/usr/bin/npm", "run", "build"]
|
|
assert mock_run.call_args_list[0].kwargs["cwd"] == desktop_dir
|
|
assert mock_run.call_args_list[1].args[0] == ["/usr/bin/npm", "exec", "--", "electron", "."]
|
|
assert mock_run.call_args_list[1].kwargs["cwd"] == desktop_dir
|
|
|
|
|
|
def test_gui_forwards_desktop_environment_overrides(tmp_path, monkeypatch):
|
|
root = _make_desktop_tree(tmp_path)
|
|
hermes_root = tmp_path / "custom-hermes"
|
|
cwd = tmp_path / "project"
|
|
hermes_root.mkdir()
|
|
cwd.mkdir()
|
|
monkeypatch.setattr(cli_main, "PROJECT_ROOT", root)
|
|
|
|
ok = subprocess.CompletedProcess([], 0)
|
|
|
|
with patch("hermes_cli.main.shutil.which", return_value="/usr/bin/npm"), \
|
|
patch("hermes_cli.main._run_npm_install_deterministic", return_value=ok), \
|
|
patch("hermes_cli.main.subprocess.run", side_effect=[ok, ok]) as mock_run, \
|
|
pytest.raises(SystemExit):
|
|
cli_main.cmd_gui(_ns(
|
|
fake_boot=True,
|
|
ignore_existing=True,
|
|
hermes_root=str(hermes_root),
|
|
cwd=str(cwd),
|
|
))
|
|
|
|
launch_env = mock_run.call_args_list[1].kwargs["env"]
|
|
assert launch_env["HERMES_DESKTOP_BOOT_FAKE"] == "1"
|
|
assert launch_env["HERMES_DESKTOP_IGNORE_EXISTING"] == "1"
|
|
assert launch_env["HERMES_DESKTOP_HERMES_ROOT"] == str(hermes_root)
|
|
assert launch_env["HERMES_DESKTOP_CWD"] == str(cwd)
|
|
|
|
|
|
def test_gui_exits_when_npm_missing(tmp_path, monkeypatch, capsys):
|
|
root = _make_desktop_tree(tmp_path)
|
|
monkeypatch.setattr(cli_main, "PROJECT_ROOT", root)
|
|
|
|
with patch("hermes_cli.main.shutil.which", return_value=None), \
|
|
pytest.raises(SystemExit) as exc:
|
|
cli_main.cmd_gui(_ns())
|
|
|
|
assert exc.value.code == 1
|
|
assert "npm was not found" in capsys.readouterr().out
|
|
|
|
|
|
def test_gui_skip_build_requires_existing_dist(tmp_path, monkeypatch, capsys):
|
|
root = _make_desktop_tree(tmp_path)
|
|
monkeypatch.setattr(cli_main, "PROJECT_ROOT", root)
|
|
|
|
with patch("hermes_cli.main.shutil.which", return_value="/usr/bin/npm"), \
|
|
pytest.raises(SystemExit) as exc:
|
|
cli_main.cmd_gui(_ns(skip_build=True))
|
|
|
|
assert exc.value.code == 1
|
|
assert "no desktop dist found" in capsys.readouterr().out
|
|
|
|
|
|
def test_gui_skip_build_launches_existing_dist_without_install_or_build(tmp_path, monkeypatch):
|
|
root = _make_desktop_tree(tmp_path)
|
|
desktop_dir = root / "apps" / "desktop"
|
|
(desktop_dir / "dist").mkdir()
|
|
(desktop_dir / "dist" / "index.html").write_text("<div></div>", encoding="utf-8")
|
|
electron_pkg = root / "node_modules" / "electron"
|
|
electron_pkg.mkdir(parents=True)
|
|
(electron_pkg / "package.json").write_text("{}", encoding="utf-8")
|
|
monkeypatch.setattr(cli_main, "PROJECT_ROOT", root)
|
|
|
|
launch_ok = subprocess.CompletedProcess(["npm", "exec", "--", "electron", "."], 0)
|
|
|
|
with patch("hermes_cli.main.shutil.which", return_value="/usr/bin/npm"), \
|
|
patch("hermes_cli.main._run_npm_install_deterministic") as mock_install, \
|
|
patch("hermes_cli.main.subprocess.run", return_value=launch_ok) as mock_run, \
|
|
pytest.raises(SystemExit) as exc:
|
|
cli_main.cmd_gui(_ns(skip_build=True))
|
|
|
|
assert exc.value.code == 0
|
|
mock_install.assert_not_called()
|
|
mock_run.assert_called_once()
|
|
assert mock_run.call_args.args[0] == ["/usr/bin/npm", "exec", "--", "electron", "."]
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
"argv",
|
|
[
|
|
["hermes", "gui"],
|
|
["hermes", "-m", "gpt5", "gui"],
|
|
],
|
|
)
|
|
def test_gui_is_known_builtin_for_plugin_gating(argv):
|
|
with patch.object(sys, "argv", argv):
|
|
assert cli_main._plugin_cli_discovery_needed() is False
|