| """Tests for core.bootstrap β binary download and installation (all I/O mocked)."""
|
| import io
|
| import os
|
| import tarfile as tarfile_mod
|
| import zipfile as zipfile_mod
|
| from pathlib import Path
|
| from unittest.mock import MagicMock, patch
|
|
|
| import pytest
|
| import requests as req
|
|
|
|
|
|
|
|
|
| class TestStreamDownload:
|
| def test_writes_chunks_to_file(self, tmp_path):
|
| dest = tmp_path / "output.bin"
|
| mock_resp = MagicMock()
|
| mock_resp.iter_content.return_value = iter([b"hello", b"", b"world"])
|
|
|
| with patch("core.bootstrap.requests.get", return_value=mock_resp):
|
| from core.bootstrap import _stream_download
|
| _stream_download("https://github.com/owner/repo/releases/download/v1/file", dest)
|
|
|
| assert dest.exists()
|
| assert dest.read_bytes() == b"helloworld"
|
|
|
| def test_skips_empty_chunks(self, tmp_path):
|
| dest = tmp_path / "empty_chunk.bin"
|
| mock_resp = MagicMock()
|
| mock_resp.iter_content.return_value = iter([b"", b"", b"data"])
|
|
|
| with patch("core.bootstrap.requests.get", return_value=mock_resp):
|
| from core.bootstrap import _stream_download
|
| _stream_download("https://github.com/owner/repo/releases/download/v1/file", dest)
|
|
|
| assert dest.read_bytes() == b"data"
|
|
|
| def test_raises_on_http_error(self, tmp_path):
|
| dest = tmp_path / "out.bin"
|
| mock_resp = MagicMock()
|
| mock_resp.raise_for_status.side_effect = req.HTTPError("404 Not Found")
|
|
|
| with patch("core.bootstrap.requests.get", return_value=mock_resp):
|
| from core.bootstrap import _stream_download
|
| with pytest.raises(req.HTTPError):
|
| _stream_download("https://github.com/owner/repo/releases/download/v1/missing", dest)
|
|
|
| def test_passes_timeout_to_get(self, tmp_path):
|
| dest = tmp_path / "out.bin"
|
| mock_resp = MagicMock()
|
| mock_resp.iter_content.return_value = iter([])
|
|
|
| with patch("core.bootstrap.requests.get", return_value=mock_resp) as mock_get:
|
| from core.bootstrap import _stream_download
|
| _stream_download("https://github.com/owner/repo/releases/download/v1/file", dest, timeout=42)
|
|
|
| assert mock_get.call_args[1]["timeout"] == 42
|
|
|
|
|
|
|
|
|
| class TestInstallGitleaks:
|
| def test_already_installed_skips_download(self):
|
| with patch("core.bootstrap.have_binary", return_value=True):
|
| from core.bootstrap import install_gitleaks
|
| result = install_gitleaks()
|
| assert result == "already installed"
|
|
|
| def test_download_failure_returns_error(self, tmp_path):
|
| with patch("core.bootstrap.have_binary", return_value=False), \
|
| patch("core.bootstrap.BIN_DIR", tmp_path), \
|
| patch("core.bootstrap._stream_download",
|
| side_effect=ConnectionError("network down")):
|
| from core.bootstrap import install_gitleaks
|
| result = install_gitleaks()
|
| assert result.startswith("install failed:")
|
|
|
| def test_windows_zip_install(self, tmp_path):
|
| """Windows path: download zip, extract gitleaks.exe."""
|
| buf = io.BytesIO()
|
| with zipfile_mod.ZipFile(buf, "w") as z:
|
| z.writestr("gitleaks.exe", b"fake gitleaks binary")
|
| zip_bytes = buf.getvalue()
|
|
|
| def fake_download(url, dest_path, timeout=120):
|
| dest_path.write_bytes(zip_bytes)
|
|
|
| with patch("core.bootstrap.have_binary", return_value=False), \
|
| patch("core.bootstrap.BIN_DIR", tmp_path), \
|
| patch("core.bootstrap._IS_WINDOWS", True), \
|
| patch("core.bootstrap._stream_download", side_effect=fake_download):
|
| from core.bootstrap import install_gitleaks
|
| result = install_gitleaks()
|
|
|
| assert result == "installed"
|
| assert (tmp_path / "gitleaks.exe").exists()
|
|
|
| def test_windows_zip_with_lowercase_gitleaks_name(self, tmp_path):
|
| """Zip contains 'gitleaks' (not '.exe') β still extracted and renamed."""
|
| buf = io.BytesIO()
|
| with zipfile_mod.ZipFile(buf, "w") as z:
|
| z.writestr("gitleaks", b"fake binary")
|
| zip_bytes = buf.getvalue()
|
|
|
| def fake_download(url, dest_path, timeout=120):
|
| dest_path.write_bytes(zip_bytes)
|
|
|
| with patch("core.bootstrap.have_binary", return_value=False), \
|
| patch("core.bootstrap.BIN_DIR", tmp_path), \
|
| patch("core.bootstrap._IS_WINDOWS", True), \
|
| patch("core.bootstrap._stream_download", side_effect=fake_download):
|
| from core.bootstrap import install_gitleaks
|
| result = install_gitleaks()
|
|
|
| assert result == "installed"
|
| assert (tmp_path / "gitleaks.exe").exists()
|
|
|
| def test_linux_tarball_install(self, tmp_path):
|
| """Linux path: download tar.gz, extract and chmod gitleaks binary."""
|
| buf = io.BytesIO()
|
| with tarfile_mod.open(fileobj=buf, mode="w:gz") as t:
|
| content = b"fake gitleaks"
|
| info = tarfile_mod.TarInfo(name="gitleaks")
|
| info.size = len(content)
|
| t.addfile(info, io.BytesIO(content))
|
| tar_bytes = buf.getvalue()
|
|
|
| def fake_download(url, dest_path, timeout=120):
|
| dest_path.write_bytes(tar_bytes)
|
|
|
| with patch("core.bootstrap.have_binary", return_value=False), \
|
| patch("core.bootstrap.BIN_DIR", tmp_path), \
|
| patch("core.bootstrap._IS_WINDOWS", False), \
|
| patch("core.bootstrap._stream_download", side_effect=fake_download):
|
| from core.bootstrap import install_gitleaks
|
| result = install_gitleaks()
|
|
|
| assert result == "installed"
|
| assert (tmp_path / "gitleaks").exists()
|
|
|
| def test_linux_tarball_no_matching_member(self, tmp_path):
|
| """Tar has no 'gitleaks' member β loop completes without extraction."""
|
| buf = io.BytesIO()
|
| with tarfile_mod.open(fileobj=buf, mode="w:gz") as t:
|
| content = b"something else"
|
| info = tarfile_mod.TarInfo(name="README.md")
|
| info.size = len(content)
|
| t.addfile(info, io.BytesIO(content))
|
| tar_bytes = buf.getvalue()
|
|
|
| def fake_download(url, dest_path, timeout=120):
|
| dest_path.write_bytes(tar_bytes)
|
|
|
| with patch("core.bootstrap.have_binary", return_value=False), \
|
| patch("core.bootstrap.BIN_DIR", tmp_path), \
|
| patch("core.bootstrap._IS_WINDOWS", False), \
|
| patch("core.bootstrap._stream_download", side_effect=fake_download):
|
| from core.bootstrap import install_gitleaks
|
| result = install_gitleaks()
|
|
|
|
|
|
|
| assert isinstance(result, str)
|
|
|
|
|
|
|
|
|
| class TestInstallHadolint:
|
| def test_already_installed_skips_download(self):
|
| with patch("core.bootstrap.have_binary", return_value=True):
|
| from core.bootstrap import install_hadolint
|
| result = install_hadolint()
|
| assert result == "already installed"
|
|
|
| def test_download_failure_returns_error(self, tmp_path):
|
| with patch("core.bootstrap.have_binary", return_value=False), \
|
| patch("core.bootstrap.BIN_DIR", tmp_path), \
|
| patch("core.bootstrap._stream_download",
|
| side_effect=OSError("disk full")):
|
| from core.bootstrap import install_hadolint
|
| result = install_hadolint()
|
| assert result.startswith("install failed:")
|
|
|
| def test_windows_download_installs_exe(self, tmp_path):
|
| def fake_download(url, dest_path, timeout=120):
|
| dest_path.write_bytes(b"fake hadolint")
|
|
|
| with patch("core.bootstrap.have_binary", return_value=False), \
|
| patch("core.bootstrap.BIN_DIR", tmp_path), \
|
| patch("core.bootstrap._IS_WINDOWS", True), \
|
| patch("core.bootstrap._stream_download", side_effect=fake_download):
|
| from core.bootstrap import install_hadolint
|
| result = install_hadolint()
|
|
|
| assert result == "installed"
|
| assert (tmp_path / "hadolint.exe").exists()
|
|
|
| def test_linux_download_installs_and_chmods(self, tmp_path):
|
| def fake_download(url, dest_path, timeout=120):
|
| dest_path.write_bytes(b"fake hadolint")
|
|
|
| with patch("core.bootstrap.have_binary", return_value=False), \
|
| patch("core.bootstrap.BIN_DIR", tmp_path), \
|
| patch("core.bootstrap._IS_WINDOWS", False), \
|
| patch("core.bootstrap._stream_download", side_effect=fake_download):
|
| from core.bootstrap import install_hadolint
|
| result = install_hadolint()
|
|
|
| assert result == "installed"
|
| assert (tmp_path / "hadolint").exists()
|
|
|
| def test_windows_url_contains_windows_in_path(self, tmp_path):
|
| captured = {}
|
|
|
| def fake_download(url, dest_path, timeout=120):
|
| captured["url"] = url
|
| dest_path.write_bytes(b"x")
|
|
|
| with patch("core.bootstrap.have_binary", return_value=False), \
|
| patch("core.bootstrap.BIN_DIR", tmp_path), \
|
| patch("core.bootstrap._IS_WINDOWS", True), \
|
| patch("core.bootstrap._stream_download", side_effect=fake_download):
|
| from core.bootstrap import install_hadolint
|
| install_hadolint()
|
|
|
| assert "Windows" in captured["url"] or "windows" in captured["url"]
|
|
|
| def test_linux_url_contains_linux_in_path(self, tmp_path):
|
| captured = {}
|
|
|
| def fake_download(url, dest_path, timeout=120):
|
| captured["url"] = url
|
| dest_path.write_bytes(b"x")
|
|
|
| with patch("core.bootstrap.have_binary", return_value=False), \
|
| patch("core.bootstrap.BIN_DIR", tmp_path), \
|
| patch("core.bootstrap._IS_WINDOWS", False), \
|
| patch("core.bootstrap._stream_download", side_effect=fake_download):
|
| from core.bootstrap import install_hadolint
|
| install_hadolint()
|
|
|
| assert "Linux" in captured["url"] or "linux" in captured["url"]
|
|
|
|
|
|
|
|
|
| class TestBootstrapBinaries:
|
| def test_returns_dict_with_gitleaks_and_hadolint(self, tmp_path):
|
| with patch("core.bootstrap.BIN_DIR", tmp_path), \
|
| patch("core.bootstrap.install_gitleaks", return_value="already installed"), \
|
| patch("core.bootstrap.install_hadolint", return_value="ok"):
|
| from core.bootstrap import bootstrap_binaries
|
| result = bootstrap_binaries()
|
|
|
| assert "gitleaks" in result
|
| assert "hadolint" in result
|
|
|
| def test_adds_bin_dir_to_path_if_missing(self, tmp_path, monkeypatch):
|
| monkeypatch.setenv("PATH", "/usr/bin")
|
| with patch("core.bootstrap.BIN_DIR", tmp_path), \
|
| patch("core.bootstrap.install_gitleaks", return_value="ok"), \
|
| patch("core.bootstrap.install_hadolint", return_value="ok"):
|
| from core.bootstrap import bootstrap_binaries
|
| bootstrap_binaries()
|
|
|
| assert str(tmp_path) in os.environ["PATH"]
|
|
|
| def test_does_not_duplicate_bin_dir_in_path(self, tmp_path, monkeypatch):
|
| monkeypatch.setenv("PATH", str(tmp_path) + os.pathsep + "/usr/bin")
|
| with patch("core.bootstrap.BIN_DIR", tmp_path), \
|
| patch("core.bootstrap.install_gitleaks", return_value="ok"), \
|
| patch("core.bootstrap.install_hadolint", return_value="ok"):
|
| from core.bootstrap import bootstrap_binaries
|
| bootstrap_binaries()
|
|
|
| path_parts = os.environ["PATH"].split(os.pathsep)
|
| assert path_parts.count(str(tmp_path)) == 1
|
|
|
| def test_creates_bin_dir_if_missing(self, tmp_path):
|
| new_dir = tmp_path / "new_bin_dir"
|
| with patch("core.bootstrap.BIN_DIR", new_dir), \
|
| patch("core.bootstrap.install_gitleaks", return_value="ok"), \
|
| patch("core.bootstrap.install_hadolint", return_value="ok"):
|
| from core.bootstrap import bootstrap_binaries
|
| bootstrap_binaries()
|
|
|
| assert new_dir.exists()
|
|
|