"""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 # ── _stream_download ────────────────────────────────────────────────────────── 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 # ── install_gitleaks ────────────────────────────────────────────────────────── 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() # Result is "installed" even without gitleaks (chmod line will fail gracefully in except) # or succeeds if chmod doesn't raise assert isinstance(result, str) # ── install_hadolint ────────────────────────────────────────────────────────── 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"] # ── bootstrap_binaries ──────────────────────────────────────────────────────── 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()