Refactor pkgmgr into modular installer pipeline with Nix flake support, PKGBUILD build workflow, local Nix cache, and full test suite restructuring.
See conversation: https://chatgpt.com/share/69332519-7ff4-800f-bc21-7fcd24a66c10
This commit is contained in:
0
tests/unit/pkgmgr/__init__.py
Normal file
0
tests/unit/pkgmgr/__init__.py
Normal file
0
tests/unit/pkgmgr/installers/__init__.py
Normal file
0
tests/unit/pkgmgr/installers/__init__.py
Normal file
71
tests/unit/pkgmgr/installers/test_ansible_requirements.py
Normal file
71
tests/unit/pkgmgr/installers/test_ansible_requirements.py
Normal file
@@ -0,0 +1,71 @@
|
||||
# tests/unit/pkgmgr/installers/test_ansible_requirements.py
|
||||
|
||||
import os
|
||||
import unittest
|
||||
from unittest.mock import patch, mock_open
|
||||
|
||||
from pkgmgr.context import RepoContext
|
||||
from pkgmgr.installers.ansible_requirements import AnsibleRequirementsInstaller
|
||||
|
||||
|
||||
class TestAnsibleRequirementsInstaller(unittest.TestCase):
|
||||
def setUp(self):
|
||||
self.repo = {"name": "test-repo"}
|
||||
self.ctx = RepoContext(
|
||||
repo=self.repo,
|
||||
identifier="test-id",
|
||||
repo_dir="/tmp/repo",
|
||||
repositories_base_dir="/tmp",
|
||||
bin_dir="/bin",
|
||||
all_repos=[self.repo],
|
||||
no_verification=False,
|
||||
preview=False,
|
||||
quiet=False,
|
||||
clone_mode="ssh",
|
||||
update_dependencies=False,
|
||||
)
|
||||
self.installer = AnsibleRequirementsInstaller()
|
||||
|
||||
@patch("os.path.exists", return_value=True)
|
||||
def test_supports_true_when_requirements_exist(self, mock_exists):
|
||||
self.assertTrue(self.installer.supports(self.ctx))
|
||||
mock_exists.assert_called_with(os.path.join(self.ctx.repo_dir, "requirements.yml"))
|
||||
|
||||
@patch("os.path.exists", return_value=False)
|
||||
def test_supports_false_when_requirements_missing(self, mock_exists):
|
||||
self.assertFalse(self.installer.supports(self.ctx))
|
||||
|
||||
@patch("pkgmgr.installers.ansible_requirements.run_command")
|
||||
@patch("tempfile.NamedTemporaryFile")
|
||||
@patch(
|
||||
"builtins.open",
|
||||
new_callable=mock_open,
|
||||
read_data="""
|
||||
collections:
|
||||
- name: community.docker
|
||||
roles:
|
||||
- src: geerlingguy.docker
|
||||
""",
|
||||
)
|
||||
@patch("os.path.exists", return_value=True)
|
||||
def test_run_installs_collections_and_roles(
|
||||
self, mock_exists, mock_file, mock_tmp, mock_run_command
|
||||
):
|
||||
# Fake temp file name
|
||||
mock_tmp().__enter__().name = "/tmp/req.yml"
|
||||
|
||||
self.installer.run(self.ctx)
|
||||
|
||||
cmds = [call[0][0] for call in mock_run_command.call_args_list]
|
||||
self.assertIn(
|
||||
"ansible-galaxy collection install -r /tmp/req.yml",
|
||||
cmds,
|
||||
)
|
||||
self.assertIn(
|
||||
"ansible-galaxy role install -r /tmp/req.yml",
|
||||
cmds,
|
||||
)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
97
tests/unit/pkgmgr/installers/test_aur.py
Normal file
97
tests/unit/pkgmgr/installers/test_aur.py
Normal file
@@ -0,0 +1,97 @@
|
||||
# tests/unit/pkgmgr/installers/test_aur.py
|
||||
|
||||
import os
|
||||
import unittest
|
||||
from unittest.mock import patch, mock_open
|
||||
|
||||
from pkgmgr.context import RepoContext
|
||||
from pkgmgr.installers.aur import AurInstaller, AUR_CONFIG_FILENAME
|
||||
|
||||
|
||||
class TestAurInstaller(unittest.TestCase):
|
||||
def setUp(self):
|
||||
self.repo = {"name": "test-repo"}
|
||||
self.ctx = RepoContext(
|
||||
repo=self.repo,
|
||||
identifier="test-id",
|
||||
repo_dir="/tmp/repo",
|
||||
repositories_base_dir="/tmp",
|
||||
bin_dir="/bin",
|
||||
all_repos=[self.repo],
|
||||
no_verification=False,
|
||||
preview=False,
|
||||
quiet=False,
|
||||
clone_mode="ssh",
|
||||
update_dependencies=False,
|
||||
)
|
||||
self.installer = AurInstaller()
|
||||
|
||||
@patch("shutil.which", return_value="/usr/bin/pacman")
|
||||
@patch("os.path.exists", return_value=True)
|
||||
@patch(
|
||||
"builtins.open",
|
||||
new_callable=mock_open,
|
||||
read_data="""
|
||||
helper: yay
|
||||
packages:
|
||||
- aurutils
|
||||
- name: some-aur-only-tool
|
||||
reason: "Test tool"
|
||||
""",
|
||||
)
|
||||
def test_supports_true_when_arch_and_aur_config_present(
|
||||
self, mock_file, mock_exists, mock_which
|
||||
):
|
||||
self.assertTrue(self.installer.supports(self.ctx))
|
||||
mock_which.assert_called_with("pacman")
|
||||
mock_exists.assert_called_with(os.path.join(self.ctx.repo_dir, AUR_CONFIG_FILENAME))
|
||||
|
||||
@patch("shutil.which", return_value=None)
|
||||
def test_supports_false_when_not_arch(self, mock_which):
|
||||
self.assertFalse(self.installer.supports(self.ctx))
|
||||
|
||||
@patch("shutil.which", return_value="/usr/bin/pacman")
|
||||
@patch("os.path.exists", return_value=False)
|
||||
def test_supports_false_when_no_config(self, mock_exists, mock_which):
|
||||
self.assertFalse(self.installer.supports(self.ctx))
|
||||
|
||||
@patch("shutil.which", side_effect=lambda name: "/usr/bin/pacman" if name == "pacman" else "/usr/bin/yay")
|
||||
@patch("pkgmgr.installers.aur.run_command")
|
||||
@patch(
|
||||
"builtins.open",
|
||||
new_callable=mock_open,
|
||||
read_data="""
|
||||
helper: yay
|
||||
packages:
|
||||
- aurutils
|
||||
- some-aur-only-tool
|
||||
""",
|
||||
)
|
||||
@patch("os.path.exists", return_value=True)
|
||||
def test_run_installs_packages_with_helper(
|
||||
self, mock_exists, mock_file, mock_run_command, mock_which
|
||||
):
|
||||
self.installer.run(self.ctx)
|
||||
|
||||
cmd = mock_run_command.call_args[0][0]
|
||||
self.assertTrue(cmd.startswith("yay -S --noconfirm "))
|
||||
self.assertIn("aurutils", cmd)
|
||||
self.assertIn("some-aur-only-tool", cmd)
|
||||
|
||||
@patch("shutil.which", return_value="/usr/bin/pacman")
|
||||
@patch(
|
||||
"builtins.open",
|
||||
new_callable=mock_open,
|
||||
read_data="packages: []",
|
||||
)
|
||||
@patch("os.path.exists", return_value=True)
|
||||
def test_run_skips_when_no_packages(
|
||||
self, mock_exists, mock_file, mock_which
|
||||
):
|
||||
with patch("pkgmgr.installers.aur.run_command") as mock_run_command:
|
||||
self.installer.run(self.ctx)
|
||||
mock_run_command.assert_not_called()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
43
tests/unit/pkgmgr/installers/test_base.py
Normal file
43
tests/unit/pkgmgr/installers/test_base.py
Normal file
@@ -0,0 +1,43 @@
|
||||
# tests/unit/pkgmgr/installers/test_base.py
|
||||
|
||||
import unittest
|
||||
from pkgmgr.installers.base import BaseInstaller
|
||||
from pkgmgr.context import RepoContext
|
||||
|
||||
|
||||
class DummyInstaller(BaseInstaller):
|
||||
def __init__(self, supports_value: bool = True):
|
||||
self._supports_value = supports_value
|
||||
self.ran_with = None
|
||||
|
||||
def supports(self, ctx: RepoContext) -> bool:
|
||||
return self._supports_value
|
||||
|
||||
def run(self, ctx: RepoContext) -> None:
|
||||
self.ran_with = ctx
|
||||
|
||||
|
||||
class TestBaseInstaller(unittest.TestCase):
|
||||
def test_dummy_installer_supports_and_run(self):
|
||||
ctx = RepoContext(
|
||||
repo={},
|
||||
identifier="id",
|
||||
repo_dir="/tmp/repo",
|
||||
repositories_base_dir="/tmp",
|
||||
bin_dir="/bin",
|
||||
all_repos=[],
|
||||
no_verification=False,
|
||||
preview=False,
|
||||
quiet=False,
|
||||
clone_mode="ssh",
|
||||
update_dependencies=False,
|
||||
)
|
||||
inst = DummyInstaller(supports_value=True)
|
||||
self.assertTrue(inst.supports(ctx))
|
||||
self.assertIsNone(inst.ran_with)
|
||||
inst.run(ctx)
|
||||
self.assertIs(inst.ran_with, ctx)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
51
tests/unit/pkgmgr/installers/test_makefile_installer.py
Normal file
51
tests/unit/pkgmgr/installers/test_makefile_installer.py
Normal file
@@ -0,0 +1,51 @@
|
||||
# tests/unit/pkgmgr/installers/test_makefile_installer.py
|
||||
|
||||
import os
|
||||
import unittest
|
||||
from unittest.mock import patch
|
||||
|
||||
from pkgmgr.context import RepoContext
|
||||
from pkgmgr.installers.makefile import MakefileInstaller
|
||||
|
||||
|
||||
class TestMakefileInstaller(unittest.TestCase):
|
||||
def setUp(self):
|
||||
self.repo = {"name": "test-repo"}
|
||||
self.ctx = RepoContext(
|
||||
repo=self.repo,
|
||||
identifier="test-id",
|
||||
repo_dir="/tmp/repo",
|
||||
repositories_base_dir="/tmp",
|
||||
bin_dir="/bin",
|
||||
all_repos=[self.repo],
|
||||
no_verification=False,
|
||||
preview=False,
|
||||
quiet=False,
|
||||
clone_mode="ssh",
|
||||
update_dependencies=False,
|
||||
)
|
||||
self.installer = MakefileInstaller()
|
||||
|
||||
@patch("os.path.exists", return_value=True)
|
||||
def test_supports_true_when_makefile_exists(self, mock_exists):
|
||||
self.assertTrue(self.installer.supports(self.ctx))
|
||||
mock_exists.assert_called_with(os.path.join(self.ctx.repo_dir, "Makefile"))
|
||||
|
||||
@patch("os.path.exists", return_value=False)
|
||||
def test_supports_false_when_makefile_missing(self, mock_exists):
|
||||
self.assertFalse(self.installer.supports(self.ctx))
|
||||
|
||||
@patch("pkgmgr.installers.makefile.run_command")
|
||||
@patch("os.path.exists", return_value=True)
|
||||
def test_run_executes_make_install(self, mock_exists, mock_run_command):
|
||||
self.installer.run(self.ctx)
|
||||
cmd = mock_run_command.call_args[0][0]
|
||||
self.assertEqual(cmd, "make install")
|
||||
self.assertEqual(
|
||||
mock_run_command.call_args[1].get("cwd"),
|
||||
self.ctx.repo_dir,
|
||||
)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
64
tests/unit/pkgmgr/installers/test_nix_flake.py
Normal file
64
tests/unit/pkgmgr/installers/test_nix_flake.py
Normal file
@@ -0,0 +1,64 @@
|
||||
import os
|
||||
import unittest
|
||||
from unittest import mock
|
||||
from unittest.mock import patch
|
||||
|
||||
from pkgmgr.context import RepoContext
|
||||
from pkgmgr.installers.nix_flake import NixFlakeInstaller
|
||||
|
||||
|
||||
class TestNixFlakeInstaller(unittest.TestCase):
|
||||
def setUp(self):
|
||||
self.repo = {"name": "test-repo"}
|
||||
self.ctx = RepoContext(
|
||||
repo=self.repo,
|
||||
identifier="test-id",
|
||||
repo_dir="/tmp/repo",
|
||||
repositories_base_dir="/tmp",
|
||||
bin_dir="/bin",
|
||||
all_repos=[self.repo],
|
||||
no_verification=False,
|
||||
preview=False,
|
||||
quiet=False,
|
||||
clone_mode="ssh",
|
||||
update_dependencies=False,
|
||||
)
|
||||
self.installer = NixFlakeInstaller()
|
||||
|
||||
@patch("shutil.which", return_value="/usr/bin/nix")
|
||||
@patch("os.path.exists", return_value=True)
|
||||
def test_supports_true_when_nix_and_flake_exist(self, mock_exists, mock_which):
|
||||
self.assertTrue(self.installer.supports(self.ctx))
|
||||
mock_which.assert_called_with("nix")
|
||||
mock_exists.assert_called_with(os.path.join(self.ctx.repo_dir, "flake.nix"))
|
||||
|
||||
@patch("shutil.which", return_value=None)
|
||||
@patch("os.path.exists", return_value=True)
|
||||
def test_supports_false_when_nix_missing(self, mock_exists, mock_which):
|
||||
self.assertFalse(self.installer.supports(self.ctx))
|
||||
|
||||
@patch("os.path.exists", return_value=True)
|
||||
@patch("shutil.which", return_value="/usr/bin/nix")
|
||||
@mock.patch("pkgmgr.installers.nix_flake.run_command")
|
||||
def test_run_tries_pkgmgr_then_default(self, mock_run_command, mock_which, mock_exists):
|
||||
cmds = []
|
||||
|
||||
def side_effect(cmd, cwd=None, preview=False, *args, **kwargs):
|
||||
cmds.append(cmd)
|
||||
return None
|
||||
|
||||
mock_run_command.side_effect = side_effect
|
||||
|
||||
self.installer.run(self.ctx)
|
||||
|
||||
self.assertIn(
|
||||
f"nix profile install {self.ctx.repo_dir}#pkgmgr",
|
||||
cmds,
|
||||
)
|
||||
self.assertIn(
|
||||
f"nix profile install {self.ctx.repo_dir}#default",
|
||||
cmds,
|
||||
)
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
65
tests/unit/pkgmgr/installers/test_pkgbuild.py
Normal file
65
tests/unit/pkgmgr/installers/test_pkgbuild.py
Normal file
@@ -0,0 +1,65 @@
|
||||
# tests/unit/pkgmgr/installers/test_pkgbuild.py
|
||||
|
||||
import os
|
||||
import unittest
|
||||
from unittest.mock import patch
|
||||
|
||||
from pkgmgr.context import RepoContext
|
||||
from pkgmgr.installers.pkgbuild import PkgbuildInstaller
|
||||
|
||||
|
||||
class TestPkgbuildInstaller(unittest.TestCase):
|
||||
def setUp(self):
|
||||
self.repo = {"name": "test-repo"}
|
||||
self.ctx = RepoContext(
|
||||
repo=self.repo,
|
||||
identifier="test-id",
|
||||
repo_dir="/tmp/repo",
|
||||
repositories_base_dir="/tmp",
|
||||
bin_dir="/bin",
|
||||
all_repos=[self.repo],
|
||||
no_verification=False,
|
||||
preview=False,
|
||||
quiet=False,
|
||||
clone_mode="ssh",
|
||||
update_dependencies=False,
|
||||
)
|
||||
self.installer = PkgbuildInstaller()
|
||||
|
||||
@patch("os.path.exists", return_value=True)
|
||||
@patch("shutil.which", return_value="/usr/bin/pacman")
|
||||
def test_supports_true_when_pacman_and_pkgbuild_exist(self, mock_which, mock_exists):
|
||||
self.assertTrue(self.installer.supports(self.ctx))
|
||||
mock_which.assert_called_with("pacman")
|
||||
mock_exists.assert_called_with(os.path.join(self.ctx.repo_dir, "PKGBUILD"))
|
||||
|
||||
@patch("os.path.exists", return_value=False)
|
||||
@patch("shutil.which", return_value="/usr/bin/pacman")
|
||||
def test_supports_false_when_pkgbuild_missing(self, mock_which, mock_exists):
|
||||
self.assertFalse(self.installer.supports(self.ctx))
|
||||
|
||||
@patch("pkgmgr.installers.pkgbuild.run_command")
|
||||
@patch("subprocess.check_output", return_value="python\ngit\n")
|
||||
@patch("os.path.exists", return_value=True)
|
||||
@patch("shutil.which", return_value="/usr/bin/pacman")
|
||||
def test_run_installs_all_packages_and_uses_clean_bash(
|
||||
self, mock_which, mock_exists, mock_check_output, mock_run_command
|
||||
):
|
||||
self.installer.run(self.ctx)
|
||||
|
||||
# Check subprocess.check_output arguments (clean shell)
|
||||
args, kwargs = mock_check_output.call_args
|
||||
cmd_list = args[0]
|
||||
self.assertEqual(cmd_list[0], "bash")
|
||||
self.assertIn("--noprofile", cmd_list)
|
||||
self.assertIn("--norc", cmd_list)
|
||||
|
||||
# Check that pacman is called with the extracted packages
|
||||
cmd = mock_run_command.call_args[0][0]
|
||||
self.assertTrue(cmd.startswith("sudo pacman -S --noconfirm "))
|
||||
self.assertIn("python", cmd)
|
||||
self.assertIn("git", cmd)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
87
tests/unit/pkgmgr/installers/test_pkgmgr_manifest.py
Normal file
87
tests/unit/pkgmgr/installers/test_pkgmgr_manifest.py
Normal file
@@ -0,0 +1,87 @@
|
||||
# tests/unit/pkgmgr/installers/test_pkgmgr_manifest.py
|
||||
|
||||
import os
|
||||
import unittest
|
||||
from unittest.mock import patch, mock_open
|
||||
|
||||
from pkgmgr.context import RepoContext
|
||||
from pkgmgr.installers.pkgmgr_manifest import PkgmgrManifestInstaller
|
||||
|
||||
|
||||
class TestPkgmgrManifestInstaller(unittest.TestCase):
|
||||
def setUp(self):
|
||||
self.repo = {"name": "test-repo"}
|
||||
self.ctx = RepoContext(
|
||||
repo=self.repo,
|
||||
identifier="test-id",
|
||||
repo_dir="/tmp/repo",
|
||||
repositories_base_dir="/tmp",
|
||||
bin_dir="/bin",
|
||||
all_repos=[self.repo],
|
||||
no_verification=False,
|
||||
preview=False,
|
||||
quiet=False,
|
||||
clone_mode="ssh",
|
||||
update_dependencies=True,
|
||||
)
|
||||
self.installer = PkgmgrManifestInstaller()
|
||||
|
||||
@patch("os.path.exists", return_value=True)
|
||||
def test_supports_true_when_manifest_exists(self, mock_exists):
|
||||
self.assertTrue(self.installer.supports(self.ctx))
|
||||
manifest_path = os.path.join(self.ctx.repo_dir, "pkgmgr.yml")
|
||||
mock_exists.assert_called_with(manifest_path)
|
||||
|
||||
@patch("os.path.exists", return_value=False)
|
||||
def test_supports_false_when_manifest_missing(self, mock_exists):
|
||||
self.assertFalse(self.installer.supports(self.ctx))
|
||||
|
||||
@patch("pkgmgr.installers.pkgmgr_manifest.run_command")
|
||||
@patch("builtins.open", new_callable=mock_open, read_data="""
|
||||
version: 1
|
||||
author: "Kevin"
|
||||
url: "https://example.com"
|
||||
description: "Test repo"
|
||||
dependencies:
|
||||
- repository: github:user/repo1
|
||||
version: main
|
||||
reason: "Core dependency"
|
||||
- repository: github:user/repo2
|
||||
""")
|
||||
@patch("os.path.exists", return_value=True)
|
||||
def test_run_installs_dependencies_and_pulls_when_update_enabled(
|
||||
self, mock_exists, mock_file, mock_run_command
|
||||
):
|
||||
self.installer.run(self.ctx)
|
||||
|
||||
# First call: pkgmgr pull github:user/repo1 github:user/repo2
|
||||
# Then calls to pkgmgr install ...
|
||||
cmds = [call_args[0][0] for call_args in mock_run_command.call_args_list]
|
||||
|
||||
self.assertIn(
|
||||
"pkgmgr pull github:user/repo1 github:user/repo2",
|
||||
cmds,
|
||||
)
|
||||
self.assertIn(
|
||||
"pkgmgr install github:user/repo1 --version main --dependencies --clone-mode ssh",
|
||||
cmds,
|
||||
)
|
||||
# For repo2: no version but dependencies + clone_mode
|
||||
self.assertIn(
|
||||
"pkgmgr install github:user/repo2 --dependencies --clone-mode ssh",
|
||||
cmds,
|
||||
)
|
||||
|
||||
@patch("pkgmgr.installers.pkgmgr_manifest.run_command")
|
||||
@patch("builtins.open", new_callable=mock_open, read_data="{}")
|
||||
@patch("os.path.exists", return_value=True)
|
||||
def test_run_no_dependencies_no_command_called(
|
||||
self, mock_exists, mock_file, mock_run_command
|
||||
):
|
||||
self.ctx.update_dependencies = True
|
||||
self.installer.run(self.ctx)
|
||||
mock_run_command.assert_not_called()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
71
tests/unit/pkgmgr/installers/test_python_installer.py
Normal file
71
tests/unit/pkgmgr/installers/test_python_installer.py
Normal file
@@ -0,0 +1,71 @@
|
||||
# tests/unit/pkgmgr/installers/test_python_installer.py
|
||||
|
||||
import os
|
||||
import unittest
|
||||
from unittest.mock import patch
|
||||
|
||||
from pkgmgr.context import RepoContext
|
||||
from pkgmgr.installers.python import PythonInstaller
|
||||
|
||||
|
||||
class TestPythonInstaller(unittest.TestCase):
|
||||
def setUp(self):
|
||||
self.repo = {"name": "test-repo"}
|
||||
self.ctx = RepoContext(
|
||||
repo=self.repo,
|
||||
identifier="test-id",
|
||||
repo_dir="/tmp/repo",
|
||||
repositories_base_dir="/tmp",
|
||||
bin_dir="/bin",
|
||||
all_repos=[self.repo],
|
||||
no_verification=False,
|
||||
preview=False,
|
||||
quiet=False,
|
||||
clone_mode="ssh",
|
||||
update_dependencies=False,
|
||||
)
|
||||
self.installer = PythonInstaller()
|
||||
|
||||
@patch("os.path.exists", side_effect=lambda path: path.endswith("pyproject.toml"))
|
||||
def test_supports_true_when_pyproject_exists(self, mock_exists):
|
||||
self.assertTrue(self.installer.supports(self.ctx))
|
||||
|
||||
@patch("os.path.exists", side_effect=lambda path: path.endswith("requirements.txt"))
|
||||
def test_supports_true_when_requirements_exists(self, mock_exists):
|
||||
self.assertTrue(self.installer.supports(self.ctx))
|
||||
|
||||
@patch("os.path.exists", return_value=False)
|
||||
def test_supports_false_when_no_python_files(self, mock_exists):
|
||||
self.assertFalse(self.installer.supports(self.ctx))
|
||||
|
||||
@patch("pkgmgr.installers.python.run_command")
|
||||
@patch(
|
||||
"os.path.exists",
|
||||
side_effect=lambda path: path.endswith("pyproject.toml")
|
||||
)
|
||||
def test_run_installs_project_from_pyproject(self, mock_exists, mock_run_command):
|
||||
self.installer.run(self.ctx)
|
||||
cmd = mock_run_command.call_args[0][0]
|
||||
self.assertIn("pip install .", cmd)
|
||||
self.assertEqual(
|
||||
mock_run_command.call_args[1].get("cwd"),
|
||||
self.ctx.repo_dir,
|
||||
)
|
||||
|
||||
@patch("pkgmgr.installers.python.run_command")
|
||||
@patch(
|
||||
"os.path.exists",
|
||||
side_effect=lambda path: path.endswith("requirements.txt")
|
||||
)
|
||||
def test_run_installs_dependencies_from_requirements(self, mock_exists, mock_run_command):
|
||||
self.installer.run(self.ctx)
|
||||
cmd = mock_run_command.call_args[0][0]
|
||||
self.assertIn("pip install -r requirements.txt", cmd)
|
||||
self.assertEqual(
|
||||
mock_run_command.call_args[1].get("cwd"),
|
||||
self.ctx.repo_dir,
|
||||
)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
168
tests/unit/pkgmgr/test_clone_repos.py
Normal file
168
tests/unit/pkgmgr/test_clone_repos.py
Normal file
@@ -0,0 +1,168 @@
|
||||
# tests/test_clone_repos.py
|
||||
import unittest
|
||||
from unittest.mock import patch, MagicMock
|
||||
|
||||
from pkgmgr.clone_repos import clone_repos
|
||||
|
||||
|
||||
class TestCloneRepos(unittest.TestCase):
|
||||
def setUp(self):
|
||||
self.repo = {
|
||||
"provider": "github.com",
|
||||
"account": "user",
|
||||
"repository": "repo",
|
||||
}
|
||||
self.selected = [self.repo]
|
||||
self.base_dir = "/tmp/repos"
|
||||
self.all_repos = self.selected
|
||||
|
||||
@patch("pkgmgr.clone_repos.verify_repository")
|
||||
@patch("pkgmgr.clone_repos.subprocess.run")
|
||||
@patch("pkgmgr.clone_repos.os.makedirs")
|
||||
@patch("pkgmgr.clone_repos.os.path.exists")
|
||||
@patch("pkgmgr.clone_repos.get_repo_dir")
|
||||
@patch("pkgmgr.clone_repos.get_repo_identifier")
|
||||
def test_clone_ssh_mode_uses_ssh_url(
|
||||
self,
|
||||
mock_get_repo_identifier,
|
||||
mock_get_repo_dir,
|
||||
mock_exists,
|
||||
mock_makedirs,
|
||||
mock_run,
|
||||
mock_verify,
|
||||
):
|
||||
mock_get_repo_identifier.return_value = "github.com/user/repo"
|
||||
mock_get_repo_dir.return_value = "/tmp/repos/user/repo"
|
||||
mock_exists.return_value = False
|
||||
mock_run.return_value = MagicMock(returncode=0)
|
||||
mock_verify.return_value = (True, [], "hash", "key")
|
||||
|
||||
clone_repos(
|
||||
self.selected,
|
||||
self.base_dir,
|
||||
self.all_repos,
|
||||
preview=False,
|
||||
no_verification=True,
|
||||
clone_mode="ssh",
|
||||
)
|
||||
|
||||
mock_run.assert_called_once()
|
||||
# subprocess.run wird mit positional args aufgerufen
|
||||
cmd = mock_run.call_args[0][0]
|
||||
cwd = mock_run.call_args[1]["cwd"]
|
||||
|
||||
self.assertIn("git clone", cmd)
|
||||
self.assertIn("git@github.com:user/repo.git", cmd)
|
||||
self.assertEqual(cwd, "/tmp/repos/user")
|
||||
|
||||
@patch("pkgmgr.clone_repos.verify_repository")
|
||||
@patch("pkgmgr.clone_repos.subprocess.run")
|
||||
@patch("pkgmgr.clone_repos.os.makedirs")
|
||||
@patch("pkgmgr.clone_repos.os.path.exists")
|
||||
@patch("pkgmgr.clone_repos.get_repo_dir")
|
||||
@patch("pkgmgr.clone_repos.get_repo_identifier")
|
||||
def test_clone_https_mode_uses_https_url(
|
||||
self,
|
||||
mock_get_repo_identifier,
|
||||
mock_get_repo_dir,
|
||||
mock_exists,
|
||||
mock_makedirs,
|
||||
mock_run,
|
||||
mock_verify,
|
||||
):
|
||||
mock_get_repo_identifier.return_value = "github.com/user/repo"
|
||||
mock_get_repo_dir.return_value = "/tmp/repos/user/repo"
|
||||
mock_exists.return_value = False
|
||||
mock_run.return_value = MagicMock(returncode=0)
|
||||
mock_verify.return_value = (True, [], "hash", "key")
|
||||
|
||||
clone_repos(
|
||||
self.selected,
|
||||
self.base_dir,
|
||||
self.all_repos,
|
||||
preview=False,
|
||||
no_verification=True,
|
||||
clone_mode="https",
|
||||
)
|
||||
|
||||
mock_run.assert_called_once()
|
||||
cmd = mock_run.call_args[0][0]
|
||||
cwd = mock_run.call_args[1]["cwd"]
|
||||
|
||||
self.assertIn("git clone", cmd)
|
||||
self.assertIn("https://github.com/user/repo.git", cmd)
|
||||
self.assertEqual(cwd, "/tmp/repos/user")
|
||||
|
||||
@patch("pkgmgr.clone_repos.verify_repository")
|
||||
@patch("pkgmgr.clone_repos.subprocess.run")
|
||||
@patch("pkgmgr.clone_repos.os.makedirs")
|
||||
@patch("pkgmgr.clone_repos.os.path.exists")
|
||||
@patch("pkgmgr.clone_repos.get_repo_dir")
|
||||
@patch("pkgmgr.clone_repos.get_repo_identifier")
|
||||
def test_clone_shallow_mode_uses_https_with_depth(
|
||||
self,
|
||||
mock_get_repo_identifier,
|
||||
mock_get_repo_dir,
|
||||
mock_exists,
|
||||
mock_makedirs,
|
||||
mock_run,
|
||||
mock_verify,
|
||||
):
|
||||
mock_get_repo_identifier.return_value = "github.com/user/repo"
|
||||
mock_get_repo_dir.return_value = "/tmp/repos/user/repo"
|
||||
mock_exists.return_value = False
|
||||
mock_run.return_value = MagicMock(returncode=0)
|
||||
mock_verify.return_value = (True, [], "hash", "key")
|
||||
|
||||
clone_repos(
|
||||
self.selected,
|
||||
self.base_dir,
|
||||
self.all_repos,
|
||||
preview=False,
|
||||
no_verification=True,
|
||||
clone_mode="shallow",
|
||||
)
|
||||
|
||||
mock_run.assert_called_once()
|
||||
cmd = mock_run.call_args[0][0]
|
||||
cwd = mock_run.call_args[1]["cwd"]
|
||||
|
||||
self.assertIn("git clone --depth 1 --single-branch", cmd)
|
||||
self.assertIn("https://github.com/user/repo.git", cmd)
|
||||
self.assertEqual(cwd, "/tmp/repos/user")
|
||||
|
||||
@patch("pkgmgr.clone_repos.verify_repository")
|
||||
@patch("pkgmgr.clone_repos.subprocess.run")
|
||||
@patch("pkgmgr.clone_repos.os.makedirs")
|
||||
@patch("pkgmgr.clone_repos.os.path.exists")
|
||||
@patch("pkgmgr.clone_repos.get_repo_dir")
|
||||
@patch("pkgmgr.clone_repos.get_repo_identifier")
|
||||
def test_preview_mode_does_not_call_subprocess_run(
|
||||
self,
|
||||
mock_get_repo_identifier,
|
||||
mock_get_repo_dir,
|
||||
mock_exists,
|
||||
mock_makedirs,
|
||||
mock_run,
|
||||
mock_verify,
|
||||
):
|
||||
mock_get_repo_identifier.return_value = "github.com/user/repo"
|
||||
mock_get_repo_dir.return_value = "/tmp/repos/user/repo"
|
||||
mock_exists.return_value = False
|
||||
mock_verify.return_value = (True, [], "hash", "key")
|
||||
|
||||
clone_repos(
|
||||
self.selected,
|
||||
self.base_dir,
|
||||
self.all_repos,
|
||||
preview=True,
|
||||
no_verification=True,
|
||||
clone_mode="shallow",
|
||||
)
|
||||
|
||||
# Im Preview-Modus sollte subprocess.run nicht aufgerufen werden
|
||||
mock_run.assert_not_called()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
36
tests/unit/pkgmgr/test_context.py
Normal file
36
tests/unit/pkgmgr/test_context.py
Normal file
@@ -0,0 +1,36 @@
|
||||
import unittest
|
||||
from pkgmgr.context import RepoContext
|
||||
|
||||
|
||||
class TestRepoContext(unittest.TestCase):
|
||||
def test_repo_context_fields_are_stored(self):
|
||||
repo = {"name": "test-repo"}
|
||||
ctx = RepoContext(
|
||||
repo=repo,
|
||||
identifier="test-id",
|
||||
repo_dir="/tmp/test",
|
||||
repositories_base_dir="/tmp",
|
||||
bin_dir="/usr/local/bin",
|
||||
all_repos=[repo],
|
||||
no_verification=True,
|
||||
preview=False,
|
||||
quiet=True,
|
||||
clone_mode="ssh",
|
||||
update_dependencies=True,
|
||||
)
|
||||
|
||||
self.assertEqual(ctx.repo, repo)
|
||||
self.assertEqual(ctx.identifier, "test-id")
|
||||
self.assertEqual(ctx.repo_dir, "/tmp/test")
|
||||
self.assertEqual(ctx.repositories_base_dir, "/tmp")
|
||||
self.assertEqual(ctx.bin_dir, "/usr/local/bin")
|
||||
self.assertEqual(ctx.all_repos, [repo])
|
||||
self.assertTrue(ctx.no_verification)
|
||||
self.assertFalse(ctx.preview)
|
||||
self.assertTrue(ctx.quiet)
|
||||
self.assertEqual(ctx.clone_mode, "ssh")
|
||||
self.assertTrue(ctx.update_dependencies)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
122
tests/unit/pkgmgr/test_install_repos.py
Normal file
122
tests/unit/pkgmgr/test_install_repos.py
Normal file
@@ -0,0 +1,122 @@
|
||||
from pkgmgr.run_command import run_command
|
||||
import unittest
|
||||
from unittest.mock import patch, MagicMock
|
||||
|
||||
from pkgmgr.context import RepoContext
|
||||
import pkgmgr.install_repos as install_module
|
||||
|
||||
|
||||
class DummyInstaller:
|
||||
"""Simple installer for testing orchestration."""
|
||||
def __init__(self):
|
||||
self.calls = []
|
||||
|
||||
def supports(self, ctx: RepoContext) -> bool:
|
||||
# Always support to verify that the pipeline runs
|
||||
return True
|
||||
|
||||
def run(self, ctx: RepoContext) -> None:
|
||||
self.calls.append(ctx.identifier)
|
||||
|
||||
|
||||
class TestInstallReposOrchestration(unittest.TestCase):
|
||||
@patch("pkgmgr.install_repos.create_ink")
|
||||
@patch("pkgmgr.install_repos.verify_repository")
|
||||
@patch("pkgmgr.install_repos.get_repo_dir")
|
||||
@patch("pkgmgr.install_repos.get_repo_identifier")
|
||||
@patch("pkgmgr.install_repos.clone_repos")
|
||||
def test_install_repos_runs_pipeline_for_each_repo(
|
||||
self,
|
||||
mock_clone_repos,
|
||||
mock_get_repo_identifier,
|
||||
mock_get_repo_dir,
|
||||
mock_verify_repository,
|
||||
mock_create_ink,
|
||||
):
|
||||
repo1 = {"name": "repo1"}
|
||||
repo2 = {"name": "repo2"}
|
||||
selected_repos = [repo1, repo2]
|
||||
all_repos = selected_repos
|
||||
|
||||
# Return identifiers and directories
|
||||
mock_get_repo_identifier.side_effect = ["id1", "id2"]
|
||||
mock_get_repo_dir.side_effect = ["/tmp/repo1", "/tmp/repo2"]
|
||||
|
||||
# Simulate verification success: (ok, errors, commit, key)
|
||||
mock_verify_repository.return_value = (True, [], "commit", "key")
|
||||
|
||||
# Ensure directories exist (no cloning)
|
||||
with patch("os.path.exists", return_value=True):
|
||||
dummy_installer = DummyInstaller()
|
||||
# Monkeypatch INSTALLERS for this test
|
||||
old_installers = install_module.INSTALLERS
|
||||
install_module.INSTALLERS = [dummy_installer]
|
||||
try:
|
||||
install_module.install_repos(
|
||||
selected_repos=selected_repos,
|
||||
repositories_base_dir="/tmp",
|
||||
bin_dir="/bin",
|
||||
all_repos=all_repos,
|
||||
no_verification=False,
|
||||
preview=False,
|
||||
quiet=False,
|
||||
clone_mode="ssh",
|
||||
update_dependencies=False,
|
||||
)
|
||||
finally:
|
||||
install_module.INSTALLERS = old_installers
|
||||
|
||||
# Check that installers ran with both identifiers
|
||||
self.assertEqual(dummy_installer.calls, ["id1", "id2"])
|
||||
self.assertEqual(mock_create_ink.call_count, 2)
|
||||
self.assertEqual(mock_verify_repository.call_count, 2)
|
||||
|
||||
@patch("pkgmgr.install_repos.verify_repository")
|
||||
@patch("pkgmgr.install_repos.get_repo_dir")
|
||||
@patch("pkgmgr.install_repos.get_repo_identifier")
|
||||
@patch("pkgmgr.install_repos.clone_repos")
|
||||
def test_install_repos_skips_on_failed_verification(
|
||||
self,
|
||||
mock_clone_repos,
|
||||
mock_get_repo_identifier,
|
||||
mock_get_repo_dir,
|
||||
mock_verify_repository,
|
||||
):
|
||||
repo = {"name": "repo1", "verified": True}
|
||||
selected_repos = [repo]
|
||||
all_repos = selected_repos
|
||||
|
||||
mock_get_repo_identifier.return_value = "id1"
|
||||
mock_get_repo_dir.return_value = "/tmp/repo1"
|
||||
|
||||
# Verification fails: ok=False, with error list
|
||||
mock_verify_repository.return_value = (False, ["sig error"], None, None)
|
||||
|
||||
dummy_installer = DummyInstaller()
|
||||
with patch("os.path.exists", return_value=True), \
|
||||
patch("pkgmgr.install_repos.create_ink") as mock_create_ink, \
|
||||
patch("builtins.input", return_value="n"):
|
||||
old_installers = install_module.INSTALLERS
|
||||
install_module.INSTALLERS = [dummy_installer]
|
||||
try:
|
||||
install_module.install_repos(
|
||||
selected_repos=selected_repos,
|
||||
repositories_base_dir="/tmp",
|
||||
bin_dir="/bin",
|
||||
all_repos=all_repos,
|
||||
no_verification=False,
|
||||
preview=False,
|
||||
quiet=False,
|
||||
clone_mode="ssh",
|
||||
update_dependencies=False,
|
||||
)
|
||||
finally:
|
||||
install_module.INSTALLERS = old_installers
|
||||
|
||||
# No installer run and no create_ink when user declines
|
||||
self.assertEqual(dummy_installer.calls, [])
|
||||
mock_create_ink.assert_not_called()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
Reference in New Issue
Block a user