File size: 4,236 Bytes
674d56a | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 | """Behavioral coverage for required Node dependency installation."""
from __future__ import annotations
import json
import os
import subprocess
from pathlib import Path
REPO_ROOT = Path(__file__).resolve().parent.parent.parent.parent
INSTALL_SH = REPO_ROOT / "scripts" / "install.sh"
def _write_executable(path: Path, body: str) -> None:
path.write_text(body, encoding="utf-8")
path.chmod(0o755)
def _run_node_deps_stage(
tmp_path: Path,
*,
fail_directory: str | None,
) -> tuple[subprocess.CompletedProcess[str], Path, list[str]]:
install_dir = tmp_path / "install"
tui_dir = install_dir / "ui-tui"
bin_dir = tmp_path / "bin"
hermes_home = tmp_path / "home"
managed_bin = hermes_home / "bin"
npm_calls = tmp_path / "npm-calls"
tui_dir.mkdir(parents=True)
bin_dir.mkdir()
managed_bin.mkdir(parents=True)
(install_dir / "package.json").write_text(
'{"name":"installer-regression-probe","private":true}\n',
encoding="utf-8",
)
(tui_dir / "package.json").write_text(
'{"name":"tui-regression-probe","private":true}\n',
encoding="utf-8",
)
_write_executable(bin_dir / "node", "#!/bin/sh\necho v26.0.0\n")
_write_executable(
bin_dir / "npm",
"""#!/bin/sh
if [ "${1:-}" = "--version" ]; then
echo 12.0.0
exit 0
fi
printf '%s\\n' "$PWD" >> "$NPM_CALLS"
if [ -n "${NPM_FAIL_DIRECTORY:-}" ] && [ "$PWD" = "$NPM_FAIL_DIRECTORY" ]; then
echo "simulated npm lifecycle failure" >&2
exit 37
fi
exit 0
""",
)
_write_executable(managed_bin / "uv", "#!/bin/sh\necho 'uv probe'\n")
env = os.environ.copy()
env.update(
{
"HERMES_HOME": str(hermes_home),
"HERMES_INSTALL_DIR": str(install_dir),
"NPM_CALLS": str(npm_calls),
"NPM_FAIL_DIRECTORY": fail_directory or "",
"PATH": f"{bin_dir}:{env['PATH']}",
}
)
proc = subprocess.run(
[
"bash",
str(INSTALL_SH),
"--stage",
"node-deps",
"--json",
"--skip-browser",
"--skip-computer-use",
],
cwd=REPO_ROOT,
env=env,
capture_output=True,
text=True,
check=False,
)
calls = npm_calls.read_text(encoding="utf-8").splitlines()
return proc, install_dir, calls
def _stage_result(proc: subprocess.CompletedProcess[str]) -> dict[str, object]:
return json.loads(proc.stdout.splitlines()[-1])
def test_root_node_dependency_failure_is_fatal(tmp_path: Path) -> None:
install_dir = tmp_path / "install"
proc, actual_install_dir, calls = _run_node_deps_stage(
tmp_path,
fail_directory=str(install_dir),
)
assert actual_install_dir == install_dir
assert proc.returncode != 0
assert _stage_result(proc) == {
"ok": False,
"stage": "node-deps",
"skipped": False,
"reason": "exit code 1",
}
assert calls == [str(install_dir)]
assert "Node.js dependencies installed" not in proc.stdout
assert "TUI dependencies installed" not in proc.stdout
assert not (install_dir / "node_modules").exists()
def test_tui_node_dependency_failure_is_fatal(tmp_path: Path) -> None:
install_dir = tmp_path / "install"
tui_dir = install_dir / "ui-tui"
proc, _, calls = _run_node_deps_stage(
tmp_path,
fail_directory=str(tui_dir),
)
assert proc.returncode != 0
assert _stage_result(proc)["ok"] is False
assert calls == [str(install_dir), str(tui_dir)]
assert "Node.js dependencies installed" in proc.stdout
assert "TUI dependencies installed" not in proc.stdout
def test_node_dependency_success_remains_successful(tmp_path: Path) -> None:
proc, install_dir, calls = _run_node_deps_stage(
tmp_path,
fail_directory=None,
)
assert proc.returncode == 0, proc.stderr
assert _stage_result(proc) == {
"ok": True,
"stage": "node-deps",
"skipped": False,
}
assert calls == [str(install_dir), str(install_dir / "ui-tui")]
assert "Node.js dependencies installed" in proc.stdout
assert "TUI dependencies installed" in proc.stdout
|