diff --git a/tests/unit/pkgmgr/actions/install/installers/os_packages/test_debian_control.py b/tests/unit/pkgmgr/actions/install/installers/os_packages/test_debian_control.py index 9efc8d4..f1370d4 100644 --- a/tests/unit/pkgmgr/actions/install/installers/os_packages/test_debian_control.py +++ b/tests/unit/pkgmgr/actions/install/installers/os_packages/test_debian_control.py @@ -1,11 +1,10 @@ -# tests/unit/pkgmgr/installers/os_packages/test_debian_control.py - -import os import unittest from unittest.mock import patch from pkgmgr.actions.repository.install.context import RepoContext -from pkgmgr.actions.repository.install.installers.os_packages.debian_control import DebianControlInstaller +from pkgmgr.actions.repository.install.installers.os_packages.debian_control import ( + DebianControlInstaller, +) class TestDebianControlInstaller(unittest.TestCase): @@ -29,14 +28,24 @@ class TestDebianControlInstaller(unittest.TestCase): @patch("os.path.exists", return_value=True) @patch("shutil.which", return_value="/usr/bin/dpkg-buildpackage") def test_supports_true(self, mock_which, mock_exists): + """ + supports() should return True when dpkg-buildpackage is available + and a debian/control file exists in the repository. + """ self.assertTrue(self.installer.supports(self.ctx)) @patch("os.path.exists", return_value=True) @patch("shutil.which", return_value=None) def test_supports_false_without_dpkg_buildpackage(self, mock_which, mock_exists): + """ + supports() should return False when dpkg-buildpackage is not available, + even if a debian/control file exists. + """ self.assertFalse(self.installer.supports(self.ctx)) - @patch("pkgmgr.actions.repository.install.installers.os_packages.debian_control.run_command") + @patch( + "pkgmgr.actions.repository.install.installers.os_packages.debian_control.run_command" + ) @patch("glob.glob", return_value=["/tmp/package-manager_0.1.1_all.deb"]) @patch("os.path.exists", return_value=True) @patch("shutil.which") @@ -47,7 +56,19 @@ class TestDebianControlInstaller(unittest.TestCase): mock_glob, mock_run_command, ): - # dpkg-buildpackage + apt-get vorhanden + """ + run() should: + + 1. Install build dependencies (apt-get build-dep). + 2. Build the package using dpkg-buildpackage -b -us -uc. + 3. Discover built .deb files via glob. + 4. Install the resulting .deb packages using a suitable tool: + - dpkg -i + - sudo dpkg -i + - or sudo apt-get install -y + """ + + # Simulate dpkg-buildpackage and apt-get being available. def which_side_effect(name): if name == "dpkg-buildpackage": return "/usr/bin/dpkg-buildpackage" @@ -64,16 +85,35 @@ class TestDebianControlInstaller(unittest.TestCase): # 1) apt-get update self.assertTrue(any("apt-get update" in cmd for cmd in cmds)) - # 2) apt-get build-dep ./ - self.assertTrue(any("apt-get build-dep -y ./ " in cmd or - "apt-get build-dep -y ./" - in cmd for cmd in cmds)) + # 2) apt-get build-dep -y ./ (with or without trailing space) + self.assertTrue( + any( + "apt-get build-dep -y ./ " in cmd + or "apt-get build-dep -y ./" + in cmd + for cmd in cmds + ) + ) # 3) dpkg-buildpackage -b -us -uc self.assertTrue(any("dpkg-buildpackage -b -us -uc" in cmd for cmd in cmds)) - # 4) dpkg -i ../*.deb - self.assertTrue(any(cmd.startswith("sudo dpkg -i ") for cmd in cmds)) + # 4) final installation of .deb packages: + # accept dpkg -i, sudo dpkg -i, or sudo apt-get install -y + has_plain_dpkg_install = any(cmd.startswith("dpkg -i ") for cmd in cmds) + has_sudo_dpkg_install = any(cmd.startswith("sudo dpkg -i ") for cmd in cmds) + has_apt_install = any( + cmd.startswith("sudo apt-get install -y ") for cmd in cmds + ) + + self.assertTrue( + has_plain_dpkg_install or has_sudo_dpkg_install or has_apt_install, + msg=( + "Expected one of 'dpkg -i', 'sudo dpkg -i' or " + "'sudo apt-get install -y', but got commands: " + f"{cmds}" + ), + ) if __name__ == "__main__": diff --git a/tests/unit/pkgmgr/actions/install/installers/os_packages/test_rpm_spec.py b/tests/unit/pkgmgr/actions/install/installers/os_packages/test_rpm_spec.py index 76acb22..8214da6 100644 --- a/tests/unit/pkgmgr/actions/install/installers/os_packages/test_rpm_spec.py +++ b/tests/unit/pkgmgr/actions/install/installers/os_packages/test_rpm_spec.py @@ -1,10 +1,10 @@ -# tests/unit/pkgmgr/installers/os_packages/test_rpm_spec.py - import unittest from unittest.mock import patch from pkgmgr.actions.repository.install.context import RepoContext -from pkgmgr.actions.repository.install.installers.os_packages.rpm_spec import RpmSpecInstaller +from pkgmgr.actions.repository.install.installers.os_packages.rpm_spec import ( + RpmSpecInstaller, +) class TestRpmSpecInstaller(unittest.TestCase): @@ -28,6 +28,13 @@ class TestRpmSpecInstaller(unittest.TestCase): @patch("glob.glob", return_value=["/tmp/repo/test.spec"]) @patch("shutil.which") def test_supports_true(self, mock_which, mock_glob): + """ + supports() should return True when: + - rpmbuild is available, and + - at least one of dnf/yum/yum-builddep is available, and + - a *.spec file is present in the repo. + """ + def which_side_effect(name): if name == "rpmbuild": return "/usr/bin/rpmbuild" @@ -42,9 +49,14 @@ class TestRpmSpecInstaller(unittest.TestCase): @patch("glob.glob", return_value=[]) @patch("shutil.which") def test_supports_false_missing_spec(self, mock_which, mock_glob): + """ + supports() should return False if no *.spec file is found, + even if rpmbuild is present. + """ mock_which.return_value = "/usr/bin/rpmbuild" self.assertFalse(self.installer.supports(self.ctx)) + @patch.object(RpmSpecInstaller, "_prepare_source_tarball") @patch("pkgmgr.actions.repository.install.installers.os_packages.rpm_spec.run_command") @patch("glob.glob") @patch("shutil.which") @@ -53,8 +65,20 @@ class TestRpmSpecInstaller(unittest.TestCase): mock_which, mock_glob, mock_run_command, + mock_prepare_source_tarball, ): - # glob.glob wird zweimal benutzt: einmal für *.spec, einmal für gebaute RPMs + """ + run() should: + + 1. Determine the .spec file in the repo. + 2. Call _prepare_source_tarball() once with ctx and spec path. + 3. Install build dependencies via dnf/yum-builddep/yum. + 4. Call rpmbuild -ba . + 5. Find built RPMs via glob. + 6. Install built RPMs via dnf/yum/rpm (here: dnf). + """ + + # glob.glob is used twice: once for *.spec, once for built RPMs. def glob_side_effect(pattern, recursive=False): if pattern.endswith("*.spec"): return ["/tmp/repo/package-manager.spec"] @@ -77,16 +101,23 @@ class TestRpmSpecInstaller(unittest.TestCase): self.installer.run(self.ctx) + # _prepare_source_tarball must have been called with the resolved spec path. + mock_prepare_source_tarball.assert_called_once_with( + self.ctx, + "/tmp/repo/package-manager.spec", + ) + + # Collect all command strings passed to run_command. cmds = [c[0][0] for c in mock_run_command.call_args_list] - # 1) builddep + # 1) build dependencies (dnf builddep) self.assertTrue(any("builddep -y" in cmd for cmd in cmds)) - # 2) rpmbuild -ba + # 2) rpmbuild -ba self.assertTrue(any(cmd.startswith("rpmbuild -ba ") for cmd in cmds)) - # 3) rpm -i … - self.assertTrue(any(cmd.startswith("sudo rpm -i ") for cmd in cmds)) + # 3) installation via dnf: "sudo dnf install -y " + self.assertTrue(any(cmd.startswith("sudo dnf install -y ") for cmd in cmds)) if __name__ == "__main__": diff --git a/tests/unit/pkgmgr/actions/install/installers/test_nix_flake.py b/tests/unit/pkgmgr/actions/install/installers/test_nix_flake.py index 62d3b0f..96329c9 100644 --- a/tests/unit/pkgmgr/actions/install/installers/test_nix_flake.py +++ b/tests/unit/pkgmgr/actions/install/installers/test_nix_flake.py @@ -28,14 +28,27 @@ class TestNixFlakeInstaller(unittest.TestCase): @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)) + """ + supports() should return True when: + - nix is available, + - flake.nix exists in the repo, + - and we are not inside a Nix dev shell. + """ + with patch.dict(os.environ, {"IN_NIX_SHELL": ""}, clear=False): + 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)) + """ + supports() should return False if nix is not available, + even if a flake.nix file exists. + """ + with patch.dict(os.environ, {"IN_NIX_SHELL": ""}, clear=False): + self.assertFalse(self.installer.supports(self.ctx)) @patch("os.path.exists", return_value=True) @patch("shutil.which", return_value="/usr/bin/nix") @@ -47,10 +60,12 @@ class TestNixFlakeInstaller(unittest.TestCase): mock_exists, ): """ - Ensure that run(): - - first tries to remove the old 'package-manager' profile entry - - then installs both 'pkgmgr' and 'default' outputs. + run() should: + + 1. attempt to remove the old 'package-manager' profile entry, and + 2. install both 'pkgmgr' and 'default' flake outputs. """ + cmds = [] def side_effect(cmd, cwd=None, preview=False, *args, **kwargs): @@ -59,18 +74,24 @@ class TestNixFlakeInstaller(unittest.TestCase): mock_run_command.side_effect = side_effect - self.installer.run(self.ctx) + # Simulate a normal environment (not inside nix develop, installer enabled). + with patch.dict( + os.environ, + {"IN_NIX_SHELL": "", "PKGMGR_DISABLE_NIX_FLAKE_INSTALLER": ""}, + clear=False, + ): + self.installer.run(self.ctx) remove_cmd = f"nix profile remove {self.installer.PROFILE_NAME} || true" install_pkgmgr_cmd = f"nix profile install {self.ctx.repo_dir}#pkgmgr" install_default_cmd = f"nix profile install {self.ctx.repo_dir}#default" - # Mindestens diese drei Kommandos müssen aufgerufen worden sein + # At least these three commands must have been issued. self.assertIn(remove_cmd, cmds) self.assertIn(install_pkgmgr_cmd, cmds) self.assertIn(install_default_cmd, cmds) - # Optional: sicherstellen, dass der remove-Aufruf zuerst kam + # Optional: ensure the remove call came first. self.assertEqual(cmds[0], remove_cmd) @patch("shutil.which", return_value="/usr/bin/nix") @@ -90,8 +111,13 @@ class TestNixFlakeInstaller(unittest.TestCase): mock_run_command.side_effect = side_effect - # Should not raise, SystemExit is swallowed internally. - self.installer._ensure_old_profile_removed(self.ctx) + with patch.dict( + os.environ, + {"IN_NIX_SHELL": "", "PKGMGR_DISABLE_NIX_FLAKE_INSTALLER": ""}, + clear=False, + ): + # Should not raise, SystemExit is swallowed internally. + self.installer._ensure_old_profile_removed(self.ctx) remove_cmd = f"nix profile remove {self.installer.PROFILE_NAME} || true" mock_run_command.assert_called_with( diff --git a/tests/unit/pkgmgr/actions/install/installers/test_python_installer.py b/tests/unit/pkgmgr/actions/install/installers/test_python_installer.py index 713230b..3169667 100644 --- a/tests/unit/pkgmgr/actions/install/installers/test_python_installer.py +++ b/tests/unit/pkgmgr/actions/install/installers/test_python_installer.py @@ -1,5 +1,3 @@ -# tests/unit/pkgmgr/installers/test_python_installer.py - import os import unittest from unittest.mock import patch @@ -28,18 +26,40 @@ class TestPythonInstaller(unittest.TestCase): @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)) + """ + supports() should return True when a pyproject.toml exists in the repo + and we are not inside a Nix dev shell. + """ + with patch.dict(os.environ, {"IN_NIX_SHELL": ""}, clear=False): + self.assertTrue(self.installer.supports(self.ctx)) @patch("os.path.exists", return_value=False) def test_supports_false_when_no_pyproject(self, mock_exists): - self.assertFalse(self.installer.supports(self.ctx)) + """ + supports() should return False when no pyproject.toml exists. + """ + with patch.dict(os.environ, {"IN_NIX_SHELL": ""}, clear=False): + self.assertFalse(self.installer.supports(self.ctx)) @patch("pkgmgr.actions.repository.install.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) + """ + run() should invoke pip to install the project from pyproject.toml + when we are not inside a Nix dev shell. + """ + # Simulate a normal environment (not inside nix develop). + with patch.dict(os.environ, {"IN_NIX_SHELL": ""}, clear=False): + self.installer.run(self.ctx) + + # Ensure run_command was actually called. + mock_run_command.assert_called() + + # Extract the command string. cmd = mock_run_command.call_args[0][0] self.assertIn("pip install .", cmd) + + # Ensure the working directory is the repo dir. self.assertEqual( mock_run_command.call_args[1].get("cwd"), self.ctx.repo_dir,