Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
46 changes: 44 additions & 2 deletions tests/unit/install/test_install_safety.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,42 @@
SENTINEL_END = re.compile(r"^# INSTALL_SAFETY_END", re.MULTILINE)


def _usable_bash() -> bool:
"""Return True only when a real POSIX bash is invocable.

install.sh is the Unix installer (Windows uses install.ps1), and these
tests shell out to ``bash`` to exercise its safety validator. On
``windows-latest`` GitHub runners the ambient ``bash`` on PATH is the WSL
stub (``C:\\Windows\\System32\\bash.exe``); with no distribution installed
it exits non-zero for every invocation, which would otherwise make every
bash-dependent test here fail with a uniform exit code rather than skip.
Probe with a trivial POSIX command and treat anything unexpected as "no
usable bash" so the suite skips instead of failing on such platforms.
"""
try:
proc = subprocess.run(
["bash", "-c", "printf ok"],
capture_output=True,
text=True,
timeout=10,
)
Comment on lines +50 to +55
except (OSError, subprocess.SubprocessError):
return False
return proc.returncode == 0 and proc.stdout.strip() == "ok"


_BASH_USABLE = _usable_bash()

requires_bash = pytest.mark.skipif(
not _BASH_USABLE,
reason="POSIX bash unavailable (e.g. Windows WSL stub); install.sh is the Unix installer",
)

# Prevent Git Bash (MSYS2) from rewriting POSIX-looking arguments such as
# /usr/local/lib/apm into Windows paths when a real bash is present.
_BASH_ENV_EXTRA = {"MSYS_NO_PATHCONV": "1", "MSYS2_ARG_CONV_EXCL": "*"}


def _read_install_sh() -> str:
"""Read install.sh in a shell-safe form across platforms.

Expand Down Expand Up @@ -81,7 +117,7 @@ def _run_validator(lib_dir: str, home: str | None = None) -> int:
input="",
capture_output=True,
text=True,
env={**os.environ, "HOME": home},
env={**os.environ, "HOME": home, **_BASH_ENV_EXTRA},
cwd=tmp,
timeout=10,
)
Expand All @@ -102,7 +138,7 @@ def _run_prepare_parent(lib_dir: str, home: str | None = None) -> int:
input="",
capture_output=True,
text=True,
env={**os.environ, "HOME": home},
env={**os.environ, "HOME": home, **_BASH_ENV_EXTRA},
cwd=tmp,
timeout=10,
)
Expand All @@ -114,6 +150,7 @@ def _run_prepare_parent(lib_dir: str, home: str | None = None) -> int:
# ---------------------------------------------------------------------------


@requires_bash
class TestAbsolutePathGuard:
def test_accepts_unix_absolute(self):
assert _run_validator("/usr/local/lib/apm") == 0
Expand All @@ -133,6 +170,7 @@ def test_rejects_dot_relative(self):
# ---------------------------------------------------------------------------


@requires_bash
class TestSuffixGuard:
def test_accepts_apm_suffix(self):
assert _run_validator("/opt/apm") == 0
Expand Down Expand Up @@ -172,6 +210,7 @@ def test_rejects_partial_match(self):
# ---------------------------------------------------------------------------


@requires_bash
class TestBlocklistGuard:
@pytest.mark.parametrize(
"broad_path",
Expand Down Expand Up @@ -244,6 +283,7 @@ def test_rejects_local_share(self):
# ---------------------------------------------------------------------------


@requires_bash
class TestMarkerFileGuard:
"""Guard 4 only fires when the directory exists and is non-empty. The
Python harness stages directories under a tempdir and calls the validator
Expand Down Expand Up @@ -306,6 +346,7 @@ def test_accepts_nonexistent_dir(self):
# ---------------------------------------------------------------------------


@requires_bash
class TestUserLocalInstall:
def test_prepare_parent_creates_missing_user_local_lib_without_sudo(self):
with tempfile.TemporaryDirectory() as tmp:
Expand All @@ -328,6 +369,7 @@ def test_prepare_parent_falls_back_when_parent_unwritable(self):
protected.chmod(0o755)


@requires_bash
class TestReportedIncident:
"""The exact command from issue #1690's reproduction must be blocked."""

Expand Down
Loading