Files
hermes-agent/tests/pm/test_node_libatomic_install_boundary.py
kshitijk4poor af90026aa0 fix(update): run the libatomic pre-install spawn only on Linux
The extra interpreter spawn ran on every platform before run_completion's
Popen. On Windows it went through subprocess.run first, which the taskkill
interrupt tests patch to assert only the completion child exists, so all
three failed. install_before_lock is a no-op off Linux anyway; skip the
spawn there.
2026-10-04 19:19:41 +05:30

120 lines
4.4 KiB
Python

"""Regression tests for #124926: libatomic repair belongs to install, not verify."""
from pathlib import Path
import pytest
from pm.packages import Nodejs
_LOADER = (
"node: error while loading shared libraries: libatomic.so.1: "
"cannot open shared object file: No such file or directory"
)
def _node_script(path: Path, body: str) -> None:
path.parent.mkdir(parents=True, exist_ok=True)
path.write_text(body, encoding="utf-8")
path.chmod(0o755)
@pytest.mark.platforms("posix")
def test_staged_repair_installs_libatomic_then_reprobes(tmp_path, monkeypatch):
node = tmp_path / "bin" / "node"
_node_script(node, f'#!/bin/sh\necho "{_LOADER}" >&2\nexit 127\n')
monkeypatch.setattr("pm.packages.current_target", lambda: "linux-x64")
calls = {"n": 0}
def install():
calls["n"] += 1
_node_script(node, "#!/bin/sh\necho v26.7.0\n")
return True, "install with sudo dnf install -y libatomic"
monkeypatch.setattr("pm.libatomic.try_install_libatomic", install)
package = Nodejs()
first = package.verify(tmp_path, "linux-x64")
repaired = package.repair_staged_verification(tmp_path, "linux-x64", first)
assert calls["n"] == 1
assert repaired == ("", "")
def test_auto_repair_never_reads_a_tty(monkeypatch):
from pm import libatomic
monkeypatch.setattr(libatomic, "_ATTEMPT", None)
monkeypatch.setattr(libatomic, "_is_root", lambda: False)
monkeypatch.setattr(libatomic, "_host_install_command", lambda: ("dnf", "install", "-y", "libatomic"))
monkeypatch.setattr(libatomic.shutil, "which", lambda name: "/usr/bin/sudo" if name == "sudo" else None)
captured = {}
def run(argv, **kwargs):
captured["argv"] = argv
captured.update(kwargs)
return type("Result", (), {"returncode": 1})()
monkeypatch.setattr(libatomic.subprocess, "run", run)
attempted, _remedy = libatomic.try_install_libatomic()
assert attempted is False
assert captured["argv"][:2] == ["/usr/bin/sudo", "-n"]
assert captured["stdin"] is libatomic.subprocess.DEVNULL
@pytest.mark.platforms("linux")
@pytest.mark.parametrize("flags", ["", "NON_INTERACTIVE=true"])
def test_installer_sudo_warmup_honours_non_interactive(tmp_path, flags):
"""The prerequisites warm-up may prompt for sudo, but never under --non-interactive."""
import os
import pty
script = Path(__file__).resolve().parents[2] / "scripts" / "install.sh"
bin_dir = tmp_path / "bin"
log = tmp_path / "sudo.log"
_node_script(bin_dir / "ldconfig", "#!/bin/sh\nexit 0\n")
_node_script(bin_dir / "id", "#!/bin/sh\necho 1000\n")
_node_script(bin_dir / "sudo", f'#!/bin/sh\necho "$*" >> "{log}"\nexit 0\n')
probe = f'source "$1" --manifest\n{flags}\nuv_bootstrap_target() {{ echo linux-x64; }}\nstage_prerequisites\n'
env = dict(os.environ, PATH=f"{bin_dir}{os.pathsep}{os.environ['PATH']}", HOME=str(tmp_path))
# A real controlling terminal: has_terminal() and `sudo -v </dev/tty` open it.
pid, fd = pty.fork()
if pid == 0:
os.execvpe("bash", ["bash", "-c", probe, "probe", str(script)], env)
while True:
try:
if not os.read(fd, 4096):
break
except OSError:
break
os.waitpid(pid, 0)
prompted = log.is_file() and "-v" in log.read_text().split()
assert prompted is (flags == "")
def test_update_completion_installs_before_detaching(monkeypatch):
"""run_completion's child has no controlling terminal; the pre-install runs first, in-session."""
from hermes_cli import update_completion
monkeypatch.setattr(update_completion.sys, "platform", "linux")
calls = []
monkeypatch.setattr(update_completion.subprocess, "run",
lambda argv, **kw: calls.append(("run", argv, kw)))
def popen(argv, **kw):
calls.append(("popen", argv, kw))
raise RuntimeError("stop after spawn")
monkeypatch.setattr(update_completion.subprocess, "Popen", popen)
with pytest.raises(RuntimeError, match="stop after spawn"):
update_completion.run_completion({"source": "/nonexistent", "home": "/nonexistent",
"receipt": {"update_id": "u"}})
assert [kind for kind, _argv, _kw in calls] == ["run", "popen"]
_kind, argv, kw = calls[0]
assert "install_before_lock" in " ".join(argv)
assert "start_new_session" not in kw and kw.get("check") is False