"""Tests for the installer: pure planning logic and the run_step executor.""" from __future__ import annotations from pathlib import Path import pytest from sjaur.installer import ( InstallStep, check_environment, plan_install, run_step, ) from sjaur.errors import SjaurError # -- plan_install (pure logic) --------------------------------------------------- def test_plan_install_without_existing_clone(tmp_path): steps = plan_install("yay", tmp_path) assert steps == [ InstallStep("clone", "yay"), InstallStep("makepkg", "yay"), ] def test_plan_install_reuses_existing_clone(tmp_path): (tmp_path / "yay").mkdir() steps = plan_install("yay", tmp_path) assert steps == [ InstallStep("reuse", "yay"), InstallStep("makepkg", "yay"), ] def test_plan_install_refresh_updates_existing_clone(tmp_path): (tmp_path / "yay").mkdir() steps = plan_install("yay", tmp_path, refresh=True) assert steps == [ InstallStep("update", "yay"), InstallStep("makepkg", "yay"), ] def test_plan_install_refresh_clones_when_missing(tmp_path): steps = plan_install("yay", tmp_path, refresh=True) assert [step.kind for step in steps] == ["clone", "makepkg"] def test_plan_install_repo_package_uses_pacman(tmp_path): for repository in ("core", "extra", "multilib"): steps = plan_install("firefox", tmp_path, repository=repository) assert steps == [InstallStep("pacman", "firefox")] def test_run_step_pacman_uses_sudo_for_non_root(tmp_path, monkeypatch): import sjaur.installer as installer recorded = {} def fake_run(cmd, cwd=None, check=False): recorded["cmd"] = cmd class Result: returncode = 0 return Result() monkeypatch.setattr(installer.subprocess, "run", fake_run) monkeypatch.setattr(installer.os, "geteuid", lambda: 1000) run_step(InstallStep("pacman", "firefox"), tmp_path) assert recorded["cmd"] == ["sudo", "pacman", "-S", "--needed", "firefox"] def test_run_step_pacman_as_root_needs_no_sudo(tmp_path, monkeypatch): import sjaur.installer as installer recorded = {} def fake_run(cmd, cwd=None, check=False): recorded["cmd"] = cmd class Result: returncode = 0 return Result() monkeypatch.setattr(installer.subprocess, "run", fake_run) monkeypatch.setattr(installer.os, "geteuid", lambda: 0) run_step(InstallStep("pacman", "firefox"), tmp_path) assert recorded["cmd"] == ["pacman", "-S", "--needed", "firefox"] def test_check_environment_repo_packages_only_need_pacman(monkeypatch): import sjaur.installer as installer monkeypatch.setattr( installer.shutil, "which", lambda name: None if name in ("git", "makepkg") else "/usr/bin/pacman", ) assert check_environment(include_build_tools=False) == [] assert len(check_environment(include_build_tools=True)) == 2 # -- run_step (subprocess execution) ---------------------------------------------- def test_run_step_clone_creates_directory(tmp_path, monkeypatch): import subprocess import sjaur.installer as installer # Point the AUR URL template at a local bare repository: no network. source = tmp_path / "aur-mock" source.mkdir() subprocess.run(["git", "init", "-q", "--bare", str(source / "yay.git")], check=True) monkeypatch.setattr(installer, "AUR_GIT_URL", str(source / "{name}.git")) target_root = tmp_path / "builds" target_root.mkdir() run_step(InstallStep("clone", "yay"), target_root) assert (target_root / "yay" / ".git").is_dir() def test_run_step_reuse_is_a_noop(tmp_path): (tmp_path / "whatever").mkdir() run_step(InstallStep("reuse", "whatever"), tmp_path) # must not raise def test_run_step_makepkg_without_pkgbuild_raises(tmp_path): (tmp_path / "pkg").mkdir() with pytest.raises(SjaurError) as excinfo: run_step(InstallStep("makepkg", "pkg"), tmp_path) assert "PKGBUILD" in str(excinfo.value) def test_run_step_makepkg_failure_raises(tmp_path): pkg = tmp_path / "pkg" pkg.mkdir() (pkg / "PKGBUILD").write_text("pkgname=pkg\n") with pytest.raises(SjaurError) as excinfo: run_step(InstallStep("makepkg", "pkg"), tmp_path) assert "makepkg" in str(excinfo.value) def test_run_step_unknown_kind_raises(tmp_path): with pytest.raises(SjaurError): run_step(InstallStep("explode", "pkg"), tmp_path) # -- check_environment ------------------------------------------------------------- def test_check_environment_finds_basic_tools(): # git/makepkg/pacman exist in CI-like Linux environments; if any is # missing the message names the tool. problems = check_environment() assert isinstance(problems, list) for problem in problems: assert "not found in PATH" in problem def test_check_environment_reports_missing_tool(monkeypatch): import sjaur.installer as installer monkeypatch.setattr(installer.shutil, "which", lambda name: None) problems = check_environment() assert len(problems) == 3 assert any("git" in problem for problem in problems) def test_missing_binary_in_run_step_raises(tmp_path, monkeypatch): import sjaur.installer as installer def raise_missing(*args, **kwargs): raise FileNotFoundError("git") monkeypatch.setattr(installer.subprocess, "run", raise_missing) with pytest.raises(SjaurError) as excinfo: run_step(InstallStep("clone", "x"), tmp_path) assert "git" in str(excinfo.value)