mirror of
				https://github.com/ansible-collections/community.general.git
				synced 2025-10-26 05:50:36 -07:00 
			
		
		
		
	
		
			Some checks failed
		
		
	
	EOL CI / EOL Sanity (Ⓐ2.15) (push) Has been cancelled
				
			EOL CI / EOL Units (Ⓐ2.15+py2.7) (push) Has been cancelled
				
			EOL CI / EOL Units (Ⓐ2.15+py3.10) (push) Has been cancelled
				
			EOL CI / EOL Units (Ⓐ2.15+py3.5) (push) Has been cancelled
				
			EOL CI / EOL I (Ⓐ2.15+alpine3+py:azp/posix/1/) (push) Has been cancelled
				
			EOL CI / EOL I (Ⓐ2.15+alpine3+py:azp/posix/2/) (push) Has been cancelled
				
			EOL CI / EOL I (Ⓐ2.15+alpine3+py:azp/posix/3/) (push) Has been cancelled
				
			EOL CI / EOL I (Ⓐ2.15+fedora37+py:azp/posix/1/) (push) Has been cancelled
				
			EOL CI / EOL I (Ⓐ2.15+fedora37+py:azp/posix/2/) (push) Has been cancelled
				
			EOL CI / EOL I (Ⓐ2.15+fedora37+py:azp/posix/3/) (push) Has been cancelled
				
			import-galaxy / Test to import built collection artifact with Galaxy importer (push) Has been cancelled
				
			Verify REUSE / check (push) Has been cancelled
				
			Unit tests: replace mock and compat with code from community.internal_test_tools (#9921)
* Replace compat with equivalent from community.internal_test_tools.
* Replace mock with equivalent from community.internal_test_tools.
(cherry picked from commit 410cf72aec)
Co-authored-by: Felix Fontein <felix@fontein.de>
		
	
			
		
			
				
	
	
		
			1099 lines
		
	
	
	
		
			40 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
			
		
		
	
	
			1099 lines
		
	
	
	
		
			40 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
| # Copyright (c) Ansible project
 | |
| # GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
 | |
| # SPDX-License-Identifier: GPL-3.0-or-later
 | |
| 
 | |
| from __future__ import absolute_import, division, print_function
 | |
| 
 | |
| __metaclass__ = type
 | |
| 
 | |
| 
 | |
| from ansible.module_utils import basic
 | |
| from ansible_collections.community.internal_test_tools.tests.unit.compat import mock
 | |
| from ansible_collections.community.internal_test_tools.tests.unit.plugins.modules.utils import (
 | |
|     AnsibleExitJson,
 | |
|     AnsibleFailJson,
 | |
|     set_module_args,
 | |
|     exit_json,
 | |
|     fail_json,
 | |
| )
 | |
| 
 | |
| from ansible_collections.community.general.plugins.modules import pacman
 | |
| from ansible_collections.community.general.plugins.modules.pacman import (
 | |
|     Package,
 | |
|     VersionTuple,
 | |
| )
 | |
| 
 | |
| import pytest
 | |
| 
 | |
| 
 | |
| def get_bin_path(self, arg, required=False):
 | |
|     """Mock AnsibleModule.get_bin_path"""
 | |
|     return arg
 | |
| 
 | |
| 
 | |
| # This inventory data is tightly coupled with the inventory test and the mock_valid_inventory fixture
 | |
| valid_inventory = {
 | |
|     "installed_pkgs": {
 | |
|         "file": "5.41-1",
 | |
|         "filesystem": "2021.11.11-1",
 | |
|         "findutils": "4.8.0-1",
 | |
|         "gawk": "5.1.1-1",
 | |
|         "gettext": "0.21-1",
 | |
|         "grep": "3.7-1",
 | |
|         "gzip": "1.11-1",
 | |
|         "pacman": "6.0.1-2",
 | |
|         "pacman-mirrorlist": "20211114-1",
 | |
|         "sed": "4.8-1",
 | |
|         "sqlite": "3.36.0-1",
 | |
|     },
 | |
|     "installed_groups": {
 | |
|         "base-devel": set(["gawk", "grep", "file", "findutils", "pacman", "sed", "gzip", "gettext"])
 | |
|     },
 | |
|     "available_pkgs": {
 | |
|         "acl": "2.3.1-1",
 | |
|         "amd-ucode": "20211027.1d00989-1",
 | |
|         "archlinux-keyring": "20211028-1",
 | |
|         "argon2": "20190702-3",
 | |
|         "attr": "2.5.1-1",
 | |
|         "audit": "3.0.6-5",
 | |
|         "autoconf": "2.71-1",
 | |
|         "automake": "1.16.5-1",
 | |
|         "b43-fwcutter": "019-3",
 | |
|         "gawk": "5.1.1-1",
 | |
|         "grep": "3.7-1",
 | |
|         "sqlite": "3.37.0-1",
 | |
|         "sudo": "1.9.8.p2-3",
 | |
|     },
 | |
|     "available_groups": {
 | |
|         "base-devel": set(
 | |
|             [
 | |
|                 "libtool",
 | |
|                 "gawk",
 | |
|                 "which",
 | |
|                 "texinfo",
 | |
|                 "fakeroot",
 | |
|                 "grep",
 | |
|                 "findutils",
 | |
|                 "autoconf",
 | |
|                 "gzip",
 | |
|                 "pkgconf",
 | |
|                 "flex",
 | |
|                 "patch",
 | |
|                 "groff",
 | |
|                 "m4",
 | |
|                 "bison",
 | |
|                 "gcc",
 | |
|                 "gettext",
 | |
|                 "make",
 | |
|                 "file",
 | |
|                 "pacman",
 | |
|                 "sed",
 | |
|                 "automake",
 | |
|                 "sudo",
 | |
|                 "binutils",
 | |
|             ]
 | |
|         ),
 | |
|         "some-group": set(["libtool", "sudo", "binutils"]),
 | |
|     },
 | |
|     "upgradable_pkgs": {
 | |
|         "sqlite": VersionTuple(current="3.36.0-1", latest="3.37.0-1"),
 | |
|     },
 | |
|     "pkg_reasons": {
 | |
|         "file": "explicit",
 | |
|         "filesystem": "explicit",
 | |
|         "findutils": "explicit",
 | |
|         "gawk": "explicit",
 | |
|         "gettext": "explicit",
 | |
|         "grep": "explicit",
 | |
|         "gzip": "explicit",
 | |
|         "pacman": "explicit",
 | |
|         "pacman-mirrorlist": "dependency",
 | |
|         "sed": "explicit",
 | |
|         "sqlite": "explicit",
 | |
|     },
 | |
| }
 | |
| 
 | |
| empty_inventory = {
 | |
|     "installed_pkgs": {},
 | |
|     "available_pkgs": {},
 | |
|     "installed_groups": {},
 | |
|     "available_groups": {},
 | |
|     "upgradable_pkgs": {},
 | |
|     "pkg_reasons": {},
 | |
| }
 | |
| 
 | |
| 
 | |
| class TestPacman:
 | |
|     @pytest.fixture(autouse=True)
 | |
|     def run_command(self, mocker):
 | |
|         self.mock_run_command = mocker.patch.object(basic.AnsibleModule, "run_command", autospec=True)
 | |
| 
 | |
|     @pytest.fixture
 | |
|     def mock_package_list(self, mocker):
 | |
|         return mocker.patch.object(pacman.Pacman, "package_list", autospec=True)
 | |
| 
 | |
|     @pytest.fixture(autouse=True)
 | |
|     def common(self, mocker):
 | |
|         self.mock_module = mocker.patch.multiple(
 | |
|             basic.AnsibleModule,
 | |
|             exit_json=exit_json,
 | |
|             fail_json=fail_json,
 | |
|             get_bin_path=get_bin_path,
 | |
|         )
 | |
| 
 | |
|     @pytest.fixture
 | |
|     def mock_empty_inventory(self, mocker):
 | |
|         inv = empty_inventory
 | |
|         return mocker.patch.object(pacman.Pacman, "_build_inventory", return_value=inv)
 | |
| 
 | |
|     @pytest.fixture
 | |
|     def mock_valid_inventory(self, mocker):
 | |
|         return mocker.patch.object(pacman.Pacman, "_build_inventory", return_value=valid_inventory)
 | |
| 
 | |
|     def test_fail_without_required_args(self):
 | |
|         with pytest.raises(AnsibleFailJson) as e:
 | |
|             with set_module_args({}):
 | |
|                 pacman.main()
 | |
|         assert e.match(r"one of the following is required")
 | |
| 
 | |
|     def test_success(self, mock_empty_inventory):
 | |
|         with set_module_args({"update_cache": True}):  # Simplest args to let init go through
 | |
|             P = pacman.Pacman(pacman.setup_module())
 | |
|             with pytest.raises(AnsibleExitJson) as e:
 | |
|                 P.success()
 | |
| 
 | |
|     def test_fail(self, mock_empty_inventory):
 | |
|         with set_module_args({"update_cache": True}):
 | |
|             P = pacman.Pacman(pacman.setup_module())
 | |
| 
 | |
|             args = dict(
 | |
|                 msg="msg", stdout="something", stderr="somethingelse", cmd=["command", "with", "args"], rc=1
 | |
|             )
 | |
|             with pytest.raises(AnsibleFailJson) as e:
 | |
|                 P.fail(**args)
 | |
| 
 | |
|         assert all(item in e.value.args[0] for item in args)
 | |
| 
 | |
|     @pytest.mark.parametrize(
 | |
|         "expected, run_command_side_effect, raises",
 | |
|         [
 | |
|             (
 | |
|                 # Regular run
 | |
|                 valid_inventory,
 | |
|                 [
 | |
|                     [  # pacman --query
 | |
|                         0,
 | |
|                         """file 5.41-1
 | |
|                         filesystem 2021.11.11-1
 | |
|                         findutils 4.8.0-1
 | |
|                         gawk 5.1.1-1
 | |
|                         gettext 0.21-1
 | |
|                         grep 3.7-1
 | |
|                         gzip 1.11-1
 | |
|                         pacman 6.0.1-2
 | |
|                         pacman-mirrorlist 20211114-1
 | |
|                         sed 4.8-1
 | |
|                         sqlite 3.36.0-1
 | |
|                         """,
 | |
|                         "",
 | |
|                     ],
 | |
|                     (  # pacman --query --group
 | |
|                         0,
 | |
|                         """base-devel file
 | |
|                         base-devel findutils
 | |
|                         base-devel gawk
 | |
|                         base-devel gettext
 | |
|                         base-devel grep
 | |
|                         base-devel gzip
 | |
|                         base-devel pacman
 | |
|                         base-devel sed
 | |
|                         """,
 | |
|                         "",
 | |
|                     ),
 | |
|                     (  # pacman --sync --list
 | |
|                         0,
 | |
|                         """core acl 2.3.1-1 [installed]
 | |
|                         core amd-ucode 20211027.1d00989-1
 | |
|                         core archlinux-keyring 20211028-1 [installed]
 | |
|                         core argon2 20190702-3 [installed]
 | |
|                         core attr 2.5.1-1 [installed]
 | |
|                         core audit 3.0.6-5 [installed: 3.0.6-2]
 | |
|                         core autoconf 2.71-1
 | |
|                         core automake 1.16.5-1
 | |
|                         core b43-fwcutter 019-3
 | |
|                         core gawk 5.1.1-1 [installed]
 | |
|                         core grep 3.7-1 [installed]
 | |
|                         core sqlite 3.37.0-1 [installed: 3.36.0-1]
 | |
|                         code sudo 1.9.8.p2-3
 | |
|                         """,
 | |
|                         "",
 | |
|                     ),
 | |
|                     (  # pacman --sync --group --group
 | |
|                         0,
 | |
|                         """base-devel autoconf
 | |
|                         base-devel automake
 | |
|                         base-devel binutils
 | |
|                         base-devel bison
 | |
|                         base-devel fakeroot
 | |
|                         base-devel file
 | |
|                         base-devel findutils
 | |
|                         base-devel flex
 | |
|                         base-devel gawk
 | |
|                         base-devel gcc
 | |
|                         base-devel gettext
 | |
|                         base-devel grep
 | |
|                         base-devel groff
 | |
|                         base-devel gzip
 | |
|                         base-devel libtool
 | |
|                         base-devel m4
 | |
|                         base-devel make
 | |
|                         base-devel pacman
 | |
|                         base-devel patch
 | |
|                         base-devel pkgconf
 | |
|                         base-devel sed
 | |
|                         base-devel sudo
 | |
|                         base-devel texinfo
 | |
|                         base-devel which
 | |
|                         some-group libtool
 | |
|                         some-group sudo
 | |
|                         some-group binutils
 | |
|                         """,
 | |
|                         "",
 | |
|                     ),
 | |
|                     (  # pacman --query --upgrades
 | |
|                         0,
 | |
|                         """sqlite 3.36.0-1 -> 3.37.0-1
 | |
|                         systemd 249.6-3 -> 249.7-2 [ignored]
 | |
|                         """,
 | |
|                         "",
 | |
|                     ),
 | |
|                     (  # pacman --query --explicit
 | |
|                         0,
 | |
|                         """file 5.41-1
 | |
|                         filesystem 2021.11.11-1
 | |
|                         findutils 4.8.0-1
 | |
|                         gawk 5.1.1-1
 | |
|                         gettext 0.21-1
 | |
|                         grep 3.7-1
 | |
|                         gzip 1.11-1
 | |
|                         pacman 6.0.1-2
 | |
|                         sed 4.8-1
 | |
|                         sqlite 3.36.0-1
 | |
|                         """,
 | |
|                         "",
 | |
|                     ),
 | |
|                     (  # pacman --query --deps
 | |
|                         0,
 | |
|                         """pacman-mirrorlist 20211114-1
 | |
|                         """,
 | |
|                         "",
 | |
|                     ),
 | |
|                 ],
 | |
|                 None,
 | |
|             ),
 | |
|             (
 | |
|                 # All good, but call to --query --upgrades return 1. aka nothing to upgrade
 | |
|                 # with a pacman warning
 | |
|                 empty_inventory,
 | |
|                 [
 | |
|                     (0, "", ""),
 | |
|                     (0, "", ""),
 | |
|                     (0, "", ""),
 | |
|                     (0, "", ""),
 | |
|                     (
 | |
|                         1,
 | |
|                         "",
 | |
|                         "warning: config file /etc/pacman.conf, line 34: directive 'TotalDownload' in section 'options' not recognized.",
 | |
|                     ),
 | |
|                     (0, "", ""),
 | |
|                     (0, "", ""),
 | |
|                 ],
 | |
|                 None,
 | |
|             ),
 | |
|             (
 | |
|                 # failure
 | |
|                 empty_inventory,
 | |
|                 [
 | |
|                     (0, "", ""),
 | |
|                     (0, "", ""),
 | |
|                     (0, "", ""),
 | |
|                     (0, "", ""),
 | |
|                     (
 | |
|                         1,
 | |
|                         "partial\npkg\\nlist",
 | |
|                         "some warning",
 | |
|                     ),
 | |
|                     (0, "", ""),
 | |
|                     (0, "", ""),
 | |
|                 ],
 | |
|                 AnsibleFailJson,
 | |
|             ),
 | |
|         ],
 | |
|     )
 | |
|     def test_build_inventory(self, expected, run_command_side_effect, raises):
 | |
|         self.mock_run_command.side_effect = run_command_side_effect
 | |
| 
 | |
|         with set_module_args({"update_cache": True}):
 | |
|             if raises:
 | |
|                 with pytest.raises(raises):
 | |
|                     P = pacman.Pacman(pacman.setup_module())
 | |
|                     P._build_inventory()
 | |
|             else:
 | |
|                 P = pacman.Pacman(pacman.setup_module())
 | |
|                 assert P._build_inventory() == expected
 | |
| 
 | |
|     @pytest.mark.parametrize("check_mode_value", [True, False])
 | |
|     def test_upgrade_check_empty_inventory(self, mock_empty_inventory, check_mode_value):
 | |
|         with set_module_args({"upgrade": True, "_ansible_check_mode": check_mode_value}):
 | |
|             P = pacman.Pacman(pacman.setup_module())
 | |
|             with pytest.raises(AnsibleExitJson) as e:
 | |
|                 P.run()
 | |
|         assert self.mock_run_command.call_count == 0
 | |
|         out = e.value.args[0]
 | |
|         assert "packages" not in out
 | |
|         assert not out["changed"]
 | |
|         assert "diff" not in out
 | |
| 
 | |
|     def test_update_db_check(self, mock_empty_inventory):
 | |
|         with set_module_args({"update_cache": True, "_ansible_check_mode": True}):
 | |
|             P = pacman.Pacman(pacman.setup_module())
 | |
| 
 | |
|             with pytest.raises(AnsibleExitJson) as e:
 | |
|                 P.run()
 | |
|         self.mock_run_command.call_count == 0
 | |
|         out = e.value.args[0]
 | |
|         assert "packages" not in out
 | |
|         assert out["changed"]
 | |
| 
 | |
|     @pytest.mark.parametrize(
 | |
|         "module_args,expected_calls,changed",
 | |
|         [
 | |
|             (
 | |
|                 {},
 | |
|                 [
 | |
|                     (["pacman", "--sync", "--list"], {'check_rc': True}, 0, 'a\nb\nc', ''),
 | |
|                     (["pacman", "--sync", "--refresh"], {'check_rc': False}, 0, 'stdout', 'stderr'),
 | |
|                     (["pacman", "--sync", "--list"], {'check_rc': True}, 0, 'b\na\nc', ''),
 | |
|                 ],
 | |
|                 False,
 | |
|             ),
 | |
|             (
 | |
|                 {"force": True},
 | |
|                 [
 | |
|                     (["pacman", "--sync", "--refresh", "--refresh"], {'check_rc': False}, 0, 'stdout', 'stderr'),
 | |
|                 ],
 | |
|                 True,
 | |
|             ),
 | |
|             (
 | |
|                 {"update_cache_extra_args": "--some-extra args"},  # shlex test
 | |
|                 [
 | |
|                     (["pacman", "--sync", "--list"], {'check_rc': True}, 0, 'a\nb\nc', ''),
 | |
|                     (["pacman", "--sync", "--refresh", "--some-extra", "args"], {'check_rc': False}, 0, 'stdout', 'stderr'),
 | |
|                     (["pacman", "--sync", "--list"], {'check_rc': True}, 0, 'a changed\nb\nc', ''),
 | |
|                 ],
 | |
|                 True,
 | |
|             ),
 | |
|             (
 | |
|                 {"force": True, "update_cache_extra_args": "--some-extra args"},
 | |
|                 [
 | |
|                     (["pacman", "--sync", "--refresh", "--some-extra", "args", "--refresh"], {'check_rc': False}, 0, 'stdout', 'stderr'),
 | |
|                 ],
 | |
|                 True,
 | |
|             ),
 | |
|             (
 | |
|                 # Test whether pacman --sync --list is not called more than twice
 | |
|                 {"upgrade": True},
 | |
|                 [
 | |
|                     (["pacman", "--sync", "--list"], {'check_rc': True}, 0, 'core foo 1.0.0-1 [installed]', ''),
 | |
|                     (["pacman", "--sync", "--refresh"], {'check_rc': False}, 0, 'stdout', 'stderr'),
 | |
|                     (["pacman", "--sync", "--list"], {'check_rc': True}, 0, 'core foo 1.0.0-1 [installed]', ''),
 | |
|                     # The following is _build_inventory:
 | |
|                     (["pacman", "--query"], {'check_rc': True}, 0, 'foo 1.0.0-1', ''),
 | |
|                     (["pacman", "--query", "--groups"], {'check_rc': True}, 0, '', ''),
 | |
|                     (["pacman", "--sync", "--groups", "--groups"], {'check_rc': True}, 0, '', ''),
 | |
|                     (["pacman", "--query", "--upgrades"], {'check_rc': False}, 0, '', ''),
 | |
|                     (["pacman", "--query", "--explicit"], {'check_rc': True}, 0, 'foo 1.0.0-1', ''),
 | |
|                     (["pacman", "--query", "--deps"], {'check_rc': True}, 0, '', ''),
 | |
|                 ],
 | |
|                 False,
 | |
|             ),
 | |
|         ],
 | |
|     )
 | |
|     def test_update_db(self, module_args, expected_calls, changed):
 | |
|         args = {"update_cache": True}
 | |
|         args.update(module_args)
 | |
|         with set_module_args(args):
 | |
| 
 | |
|             self.mock_run_command.side_effect = [
 | |
|                 (rc, stdout, stderr) for expected_call, kwargs, rc, stdout, stderr in expected_calls
 | |
|             ]
 | |
|             with pytest.raises(AnsibleExitJson) as e:
 | |
|                 P = pacman.Pacman(pacman.setup_module())
 | |
|                 P.run()
 | |
| 
 | |
|         self.mock_run_command.assert_has_calls([
 | |
|             mock.call(mock.ANY, expected_call, **kwargs) for expected_call, kwargs, rc, stdout, stderr in expected_calls
 | |
|         ])
 | |
|         out = e.value.args[0]
 | |
|         assert out["cache_updated"] == changed
 | |
|         assert out["changed"] == changed
 | |
| 
 | |
|     @pytest.mark.parametrize(
 | |
|         "check_mode_value, run_command_data, upgrade_extra_args",
 | |
|         [
 | |
|             # just check
 | |
|             (True, None, None),
 | |
|             (
 | |
|                 # for real
 | |
|                 False,
 | |
|                 {
 | |
|                     "args": ["pacman", "--sync", "--sysupgrade", "--quiet", "--noconfirm"],
 | |
|                     "return_value": [0, "stdout", "stderr"],
 | |
|                 },
 | |
|                 None,
 | |
|             ),
 | |
|             (
 | |
|                 # with extra args
 | |
|                 False,
 | |
|                 {
 | |
|                     "args": [
 | |
|                         "pacman",
 | |
|                         "--sync",
 | |
|                         "--sysupgrade",
 | |
|                         "--quiet",
 | |
|                         "--noconfirm",
 | |
|                         "--some",
 | |
|                         "value",
 | |
|                     ],
 | |
|                     "return_value": [0, "stdout", "stderr"],
 | |
|                 },
 | |
|                 "--some value",
 | |
|             ),
 | |
|         ],
 | |
|     )
 | |
|     def test_upgrade(self, mock_valid_inventory, check_mode_value, run_command_data, upgrade_extra_args):
 | |
|         args = {"upgrade": True, "_ansible_check_mode": check_mode_value}
 | |
|         if upgrade_extra_args:
 | |
|             args["upgrade_extra_args"] = upgrade_extra_args
 | |
|         with set_module_args(args):
 | |
| 
 | |
|             if run_command_data and "return_value" in run_command_data:
 | |
|                 self.mock_run_command.return_value = run_command_data["return_value"]
 | |
| 
 | |
|             P = pacman.Pacman(pacman.setup_module())
 | |
| 
 | |
|             with pytest.raises(AnsibleExitJson) as e:
 | |
|                 P.run()
 | |
|             out = e.value.args[0]
 | |
| 
 | |
|         if check_mode_value:
 | |
|             self.mock_run_command.call_count == 0
 | |
| 
 | |
|         if run_command_data and "args" in run_command_data:
 | |
|             self.mock_run_command.assert_called_with(mock.ANY, run_command_data["args"], check_rc=False)
 | |
|             assert out["stdout"] == "stdout"
 | |
|             assert out["stderr"] == "stderr"
 | |
| 
 | |
|         assert len(out["packages"]) == 1 and "sqlite" in out["packages"]
 | |
|         assert out["changed"]
 | |
|         assert out["diff"]["before"] and out["diff"]["after"]
 | |
| 
 | |
|     def test_upgrade_fail(self, mock_valid_inventory):
 | |
|         with set_module_args({"upgrade": True}):
 | |
|             self.mock_run_command.return_value = [1, "stdout", "stderr"]
 | |
|             P = pacman.Pacman(pacman.setup_module())
 | |
| 
 | |
|             with pytest.raises(AnsibleFailJson) as e:
 | |
|                 P.run()
 | |
|         assert self.mock_run_command.call_count == 1
 | |
|         out = e.value.args[0]
 | |
|         assert out["failed"]
 | |
|         assert out["stdout"] == "stdout"
 | |
|         assert out["stderr"] == "stderr"
 | |
| 
 | |
|     @pytest.mark.parametrize(
 | |
|         "state, pkg_names, expected, run_command_data, raises",
 | |
|         [
 | |
|             # regular packages, no resolving required
 | |
|             (
 | |
|                 "present",
 | |
|                 ["acl", "attr"],
 | |
|                 [Package(name="acl", source="acl"), Package(name="attr", source="attr")],
 | |
|                 None,
 | |
|                 None,
 | |
|             ),
 | |
|             (
 | |
|                 # group expansion
 | |
|                 "present",
 | |
|                 ["acl", "some-group", "attr"],
 | |
|                 [
 | |
|                     Package(name="acl", source="acl"),
 | |
|                     Package(name="binutils", source="binutils"),
 | |
|                     Package(name="libtool", source="libtool"),
 | |
|                     Package(name="sudo", source="sudo"),
 | |
|                     Package(name="attr", source="attr"),
 | |
|                 ],
 | |
|                 None,
 | |
|                 None,
 | |
|             ),
 | |
|             (
 | |
|                 # <repo>/<pkgname> format -> call to pacman to resolve
 | |
|                 "present",
 | |
|                 ["community/elixir"],
 | |
|                 [Package(name="elixir", source="community/elixir")],
 | |
|                 {
 | |
|                     "calls": [
 | |
|                         mock.call(
 | |
|                             mock.ANY,
 | |
|                             ["pacman", "--sync", "--print-format", "%n", "community/elixir"],
 | |
|                             check_rc=False,
 | |
|                         )
 | |
|                     ],
 | |
|                     "side_effect": [(0, "elixir", "")],
 | |
|                 },
 | |
|                 None,
 | |
|             ),
 | |
|             (
 | |
|                 # catch all -> call to pacman to resolve (--sync and --upgrade)
 | |
|                 "present",
 | |
|                 ["somepackage-12.3-x86_64.pkg.tar.zst"],
 | |
|                 [
 | |
|                     Package(
 | |
|                         name="somepackage",
 | |
|                         source="somepackage-12.3-x86_64.pkg.tar.zst",
 | |
|                         source_is_URL=True,
 | |
|                     )
 | |
|                 ],
 | |
|                 {
 | |
|                     "calls": [
 | |
|                         mock.call(
 | |
|                             mock.ANY,
 | |
|                             [
 | |
|                                 "pacman",
 | |
|                                 "--sync",
 | |
|                                 "--print-format",
 | |
|                                 "%n",
 | |
|                                 "somepackage-12.3-x86_64.pkg.tar.zst",
 | |
|                             ],
 | |
|                             check_rc=False,
 | |
|                         ),
 | |
|                         mock.call(
 | |
|                             mock.ANY,
 | |
|                             [
 | |
|                                 "pacman",
 | |
|                                 "--upgrade",
 | |
|                                 "--print-format",
 | |
|                                 "%n",
 | |
|                                 "somepackage-12.3-x86_64.pkg.tar.zst",
 | |
|                             ],
 | |
|                             check_rc=False,
 | |
|                         ),
 | |
|                     ],
 | |
|                     "side_effect": [(1, "", "nope"), (0, "somepackage", "")],
 | |
|                 },
 | |
|                 None,
 | |
|             ),
 | |
|             (
 | |
|                 # install a package that doesn't exist. call pacman twice and give up
 | |
|                 "present",
 | |
|                 ["unknown-package"],
 | |
|                 [],
 | |
|                 {
 | |
|                     # no call validation, since it will fail
 | |
|                     "side_effect": [(1, "", "nope"), (1, "", "stillnope")],
 | |
|                 },
 | |
|                 AnsibleFailJson,
 | |
|             ),
 | |
|             (
 | |
|                 # Edge case: resolve a pkg that doesn't exist when trying to remove it (state == absent).
 | |
|                 # will fallback to file + url format but not complain since it is already not there
 | |
|                 # Can happen if a pkg is removed for the repos  (or if a repo is disabled/removed)
 | |
|                 "absent",
 | |
|                 ["unknown-package-to-remove"],
 | |
|                 [],
 | |
|                 {
 | |
|                     "calls": [
 | |
|                         mock.call(
 | |
|                             mock.ANY,
 | |
|                             ["pacman", "--sync", "--print-format", "%n", "unknown-package-to-remove"],
 | |
|                             check_rc=False,
 | |
|                         ),
 | |
|                         mock.call(
 | |
|                             mock.ANY,
 | |
|                             ["pacman", "--upgrade", "--print-format", "%n", "unknown-package-to-remove"],
 | |
|                             check_rc=False,
 | |
|                         ),
 | |
|                     ],
 | |
|                     "side_effect": [(1, "", "nope"), (1, "", "stillnope")],
 | |
|                 },
 | |
|                 None,  # Doesn't fail
 | |
|             ),
 | |
|         ],
 | |
|     )
 | |
|     def test_package_list(
 | |
|         self, mock_valid_inventory, state, pkg_names, expected, run_command_data, raises
 | |
|     ):
 | |
|         with set_module_args({"name": pkg_names, "state": state}):
 | |
|             P = pacman.Pacman(pacman.setup_module())
 | |
|             P.inventory = P._build_inventory()
 | |
|             if run_command_data:
 | |
|                 self.mock_run_command.side_effect = run_command_data["side_effect"]
 | |
| 
 | |
|             if raises:
 | |
|                 with pytest.raises(raises):
 | |
|                     P.package_list()
 | |
|             else:
 | |
|                 assert sorted(P.package_list()) == sorted(expected)
 | |
|                 if run_command_data:
 | |
|                     assert self.mock_run_command.mock_calls == run_command_data["calls"]
 | |
| 
 | |
|     @pytest.mark.parametrize("check_mode_value", [True, False])
 | |
|     @pytest.mark.parametrize(
 | |
|         "name, state, package_list",
 | |
|         [
 | |
|             (["already-absent"], "absent", [Package("already-absent", "already-absent")]),
 | |
|             (["grep"], "present", [Package("grep", "grep")]),
 | |
|         ],
 | |
|     )
 | |
|     def test_op_packages_nothing_to_do(
 | |
|         self, mock_valid_inventory, mock_package_list, check_mode_value, name, state, package_list
 | |
|     ):
 | |
|         with set_module_args({"name": name, "state": state, "_ansible_check_mode": check_mode_value}):
 | |
|             mock_package_list.return_value = package_list
 | |
|             P = pacman.Pacman(pacman.setup_module())
 | |
|             with pytest.raises(AnsibleExitJson) as e:
 | |
|                 P.run()
 | |
|         out = e.value.args[0]
 | |
|         assert not out["changed"]
 | |
|         assert "packages" in out
 | |
|         assert "diff" not in out
 | |
|         self.mock_run_command.call_count == 0
 | |
| 
 | |
|     @pytest.mark.parametrize(
 | |
|         "module_args, expected_packages, package_list_out, run_command_data, raises",
 | |
|         [
 | |
|             (
 | |
|                 # remove pkg: Check mode -- call to print format but that's it
 | |
|                 {"_ansible_check_mode": True, "name": ["grep"], "state": "absent"},
 | |
|                 ["grep-version"],
 | |
|                 [Package("grep", "grep")],
 | |
|                 {
 | |
|                     "calls": [
 | |
|                         mock.call(
 | |
|                             mock.ANY,
 | |
|                             [
 | |
|                                 "pacman",
 | |
|                                 "--remove",
 | |
|                                 "--noconfirm",
 | |
|                                 "--noprogressbar",
 | |
|                                 "--print-format",
 | |
|                                 "%n-%v",
 | |
|                                 "grep",
 | |
|                             ],
 | |
|                             check_rc=False,
 | |
|                         ),
 | |
|                     ],
 | |
|                     "side_effect": [(0, "grep-version", "")],
 | |
|                 },
 | |
|                 AnsibleExitJson,
 | |
|             ),
 | |
|             (
 | |
|                 # remove pkg for real now -- with 2 packages
 | |
|                 {"name": ["grep", "gawk"], "state": "absent"},
 | |
|                 ["grep-version", "gawk-anotherversion"],
 | |
|                 [Package("grep", "grep"), Package("gawk", "gawk")],
 | |
|                 {
 | |
|                     "calls": [
 | |
|                         mock.call(
 | |
|                             mock.ANY,
 | |
|                             [
 | |
|                                 "pacman",
 | |
|                                 "--remove",
 | |
|                                 "--noconfirm",
 | |
|                                 "--noprogressbar",
 | |
|                                 "--print-format",
 | |
|                                 "%n-%v",
 | |
|                                 "grep",
 | |
|                                 "gawk",
 | |
|                             ],
 | |
|                             check_rc=False,
 | |
|                         ),
 | |
|                         mock.call(
 | |
|                             mock.ANY,
 | |
|                             ["pacman", "--remove", "--noconfirm", "--noprogressbar", "grep", "gawk"],
 | |
|                             check_rc=False,
 | |
|                         ),
 | |
|                     ],
 | |
|                     "side_effect": [
 | |
|                         (0, "grep-version\ngawk-anotherversion", ""),
 | |
|                         (0, "stdout", "stderr"),
 | |
|                     ],
 | |
|                 },
 | |
|                 AnsibleExitJson,
 | |
|             ),
 | |
|             (
 | |
|                 # remove pkg   force + extra_args
 | |
|                 {
 | |
|                     "name": ["grep"],
 | |
|                     "state": "absent",
 | |
|                     "force": True,
 | |
|                     "extra_args": "--some --extra arg",
 | |
|                 },
 | |
|                 ["grep-version"],
 | |
|                 [Package("grep", "grep")],
 | |
|                 {
 | |
|                     "calls": [
 | |
|                         mock.call(
 | |
|                             mock.ANY,
 | |
|                             [
 | |
|                                 "pacman",
 | |
|                                 "--remove",
 | |
|                                 "--noconfirm",
 | |
|                                 "--noprogressbar",
 | |
|                                 "--some",
 | |
|                                 "--extra",
 | |
|                                 "arg",
 | |
|                                 "--nodeps",
 | |
|                                 "--nodeps",
 | |
|                                 "--print-format",
 | |
|                                 "%n-%v",
 | |
|                                 "grep",
 | |
|                             ],
 | |
|                             check_rc=False,
 | |
|                         ),
 | |
|                         mock.call(
 | |
|                             mock.ANY,
 | |
|                             [
 | |
|                                 "pacman",
 | |
|                                 "--remove",
 | |
|                                 "--noconfirm",
 | |
|                                 "--noprogressbar",
 | |
|                                 "--some",
 | |
|                                 "--extra",
 | |
|                                 "arg",
 | |
|                                 "--nodeps",
 | |
|                                 "--nodeps",
 | |
|                                 "grep",
 | |
|                             ],
 | |
|                             check_rc=False,
 | |
|                         ),
 | |
|                     ],
 | |
|                     "side_effect": [
 | |
|                         (0, "grep-version", ""),
 | |
|                         (0, "stdout", "stderr"),
 | |
|                     ],
 | |
|                 },
 | |
|                 AnsibleExitJson,
 | |
|             ),
 | |
|             (
 | |
|                 # remove pkg -- Failure to list
 | |
|                 {"name": ["grep"], "state": "absent"},
 | |
|                 ["grep-3.7-1"],
 | |
|                 [Package("grep", "grep")],
 | |
|                 {
 | |
|                     "calls": [
 | |
|                         mock.call(
 | |
|                             mock.ANY,
 | |
|                             [
 | |
|                                 "pacman",
 | |
|                                 "--remove",
 | |
|                                 "--noconfirm",
 | |
|                                 "--noprogressbar",
 | |
|                                 "--print-format",
 | |
|                                 "%n-%v",
 | |
|                                 "grep",
 | |
|                             ],
 | |
|                             check_rc=False,
 | |
|                         )
 | |
|                     ],
 | |
|                     "side_effect": [
 | |
|                         (1, "stdout", "stderr"),
 | |
|                     ],
 | |
|                 },
 | |
|                 AnsibleFailJson,
 | |
|             ),
 | |
|             (
 | |
|                 # remove pkg -- Failure to remove
 | |
|                 {"name": ["grep"], "state": "absent"},
 | |
|                 ["grep-3.7-1"],
 | |
|                 [Package("grep", "grep")],
 | |
|                 {
 | |
|                     "calls": [
 | |
|                         mock.call(
 | |
|                             mock.ANY,
 | |
|                             [
 | |
|                                 "pacman",
 | |
|                                 "--remove",
 | |
|                                 "--noconfirm",
 | |
|                                 "--noprogressbar",
 | |
|                                 "--print-format",
 | |
|                                 "%n-%v",
 | |
|                                 "grep",
 | |
|                             ],
 | |
|                             check_rc=False,
 | |
|                         ),
 | |
|                         mock.call(
 | |
|                             mock.ANY,
 | |
|                             ["pacman", "--remove", "--noconfirm", "--noprogressbar", "grep"],
 | |
|                             check_rc=False,
 | |
|                         ),
 | |
|                     ],
 | |
|                     "side_effect": [
 | |
|                         (0, "grep", ""),
 | |
|                         (1, "stdout", "stderr"),
 | |
|                     ],
 | |
|                 },
 | |
|                 AnsibleFailJson,
 | |
|             ),
 | |
|             (
 | |
|                 # install pkg: Check mode
 | |
|                 {"_ansible_check_mode": True, "name": ["sudo"], "state": "present"},
 | |
|                 ["sudo"],
 | |
|                 [Package("sudo", "sudo")],
 | |
|                 {
 | |
|                     "calls": [
 | |
|                         mock.call(
 | |
|                             mock.ANY,
 | |
|                             [
 | |
|                                 "pacman",
 | |
|                                 "--noconfirm",
 | |
|                                 "--noprogressbar",
 | |
|                                 "--needed",
 | |
|                                 "--sync",
 | |
|                                 "--print-format",
 | |
|                                 "%n %v",
 | |
|                                 "sudo",
 | |
|                             ],
 | |
|                             check_rc=False,
 | |
|                         ),
 | |
|                     ],
 | |
|                     "side_effect": [(0, "sudo version", "")],
 | |
|                 },
 | |
|                 AnsibleExitJson,
 | |
|             ),
 | |
|             (
 | |
|                 # Install pkgs: one regular, one already installed, one file URL and one https URL
 | |
|                 {
 | |
|                     "name": [
 | |
|                         "sudo",
 | |
|                         "grep",
 | |
|                         "./somepackage-12.3-x86_64.pkg.tar.zst",
 | |
|                         "http://example.com/otherpkg-1.2-x86_64.pkg.tar.zst",
 | |
|                     ],
 | |
|                     "state": "present",
 | |
|                 },
 | |
|                 ["otherpkg", "somepackage", "sudo"],
 | |
|                 [
 | |
|                     Package("sudo", "sudo"),
 | |
|                     Package("grep", "grep"),
 | |
|                     Package("somepackage", "./somepackage-12.3-x86_64.pkg.tar.zst", source_is_URL=True),
 | |
|                     Package(
 | |
|                         "otherpkg",
 | |
|                         "http://example.com/otherpkg-1.2-x86_64.pkg.tar.zst",
 | |
|                         source_is_URL=True,
 | |
|                     ),
 | |
|                 ],
 | |
|                 {
 | |
|                     "calls": [
 | |
|                         mock.call(
 | |
|                             mock.ANY,
 | |
|                             [
 | |
|                                 "pacman",
 | |
|                                 "--noconfirm",
 | |
|                                 "--noprogressbar",
 | |
|                                 "--needed",
 | |
|                                 "--sync",
 | |
|                                 "--print-format",
 | |
|                                 "%n %v",
 | |
|                                 "sudo",
 | |
|                             ],
 | |
|                             check_rc=False,
 | |
|                         ),
 | |
|                         mock.call(
 | |
|                             mock.ANY,
 | |
|                             [
 | |
|                                 "pacman",
 | |
|                                 "--noconfirm",
 | |
|                                 "--noprogressbar",
 | |
|                                 "--needed",
 | |
|                                 "--upgrade",
 | |
|                                 "--print-format",
 | |
|                                 "%n %v",
 | |
|                                 "./somepackage-12.3-x86_64.pkg.tar.zst",
 | |
|                                 "http://example.com/otherpkg-1.2-x86_64.pkg.tar.zst",
 | |
|                             ],
 | |
|                             check_rc=False,
 | |
|                         ),
 | |
|                         mock.call(
 | |
|                             mock.ANY,
 | |
|                             [
 | |
|                                 "pacman",
 | |
|                                 "--noconfirm",
 | |
|                                 "--noprogressbar",
 | |
|                                 "--needed",
 | |
|                                 "--sync",
 | |
|                                 "sudo",
 | |
|                             ],
 | |
|                             check_rc=False,
 | |
|                         ),
 | |
|                         mock.call(
 | |
|                             mock.ANY,
 | |
|                             [
 | |
|                                 "pacman",
 | |
|                                 "--noconfirm",
 | |
|                                 "--noprogressbar",
 | |
|                                 "--needed",
 | |
|                                 "--upgrade",
 | |
|                                 "./somepackage-12.3-x86_64.pkg.tar.zst",
 | |
|                                 "http://example.com/otherpkg-1.2-x86_64.pkg.tar.zst",
 | |
|                             ],
 | |
|                             check_rc=False,
 | |
|                         ),
 | |
|                     ],
 | |
|                     "side_effect": [
 | |
|                         (0, "sudo version", ""),
 | |
|                         (0, "somepackage 12.3\notherpkg 1.2", ""),
 | |
|                         (0, "", ""),
 | |
|                         (0, "", ""),
 | |
|                     ],
 | |
|                 },
 | |
|                 AnsibleExitJson,
 | |
|             ),
 | |
|             (
 | |
|                 # install pkg, extra_args
 | |
|                 {"name": ["sudo"], "state": "present", "extra_args": "--some --thing else"},
 | |
|                 ["sudo"],
 | |
|                 [Package("sudo", "sudo")],
 | |
|                 {
 | |
|                     "calls": [
 | |
|                         mock.call(
 | |
|                             mock.ANY,
 | |
|                             [
 | |
|                                 "pacman",
 | |
|                                 "--noconfirm",
 | |
|                                 "--noprogressbar",
 | |
|                                 "--needed",
 | |
|                                 "--some",
 | |
|                                 "--thing",
 | |
|                                 "else",
 | |
|                                 "--sync",
 | |
|                                 "--print-format",
 | |
|                                 "%n %v",
 | |
|                                 "sudo",
 | |
|                             ],
 | |
|                             check_rc=False,
 | |
|                         ),
 | |
|                         mock.call(
 | |
|                             mock.ANY,
 | |
|                             [
 | |
|                                 "pacman",
 | |
|                                 "--noconfirm",
 | |
|                                 "--noprogressbar",
 | |
|                                 "--needed",
 | |
|                                 "--some",
 | |
|                                 "--thing",
 | |
|                                 "else",
 | |
|                                 "--sync",
 | |
|                                 "sudo",
 | |
|                             ],
 | |
|                             check_rc=False,
 | |
|                         ),
 | |
|                     ],
 | |
|                     "side_effect": [(0, "sudo version", ""), (0, "", "")],
 | |
|                 },
 | |
|                 AnsibleExitJson,
 | |
|             ),
 | |
|             (
 | |
|                 # latest pkg: Check mode
 | |
|                 {"_ansible_check_mode": True, "name": ["sqlite"], "state": "latest"},
 | |
|                 ["sqlite"],
 | |
|                 [Package("sqlite", "sqlite")],
 | |
|                 {
 | |
|                     "calls": [
 | |
|                         mock.call(
 | |
|                             mock.ANY,
 | |
|                             [
 | |
|                                 "pacman",
 | |
|                                 "--noconfirm",
 | |
|                                 "--noprogressbar",
 | |
|                                 "--needed",
 | |
|                                 "--sync",
 | |
|                                 "--print-format",
 | |
|                                 "%n %v",
 | |
|                                 "sqlite",
 | |
|                             ],
 | |
|                             check_rc=False,
 | |
|                         ),
 | |
|                     ],
 | |
|                     "side_effect": [(0, "sqlite new-version", "")],
 | |
|                 },
 | |
|                 AnsibleExitJson,
 | |
|             ),
 | |
|             (
 | |
|                 # latest pkg -- one already latest
 | |
|                 {"name": ["sqlite", "grep"], "state": "latest"},
 | |
|                 ["sqlite"],
 | |
|                 [Package("sqlite", "sqlite")],
 | |
|                 {
 | |
|                     "calls": [
 | |
|                         mock.call(
 | |
|                             mock.ANY,
 | |
|                             [
 | |
|                                 "pacman",
 | |
|                                 "--noconfirm",
 | |
|                                 "--noprogressbar",
 | |
|                                 "--needed",
 | |
|                                 "--sync",
 | |
|                                 "--print-format",
 | |
|                                 "%n %v",
 | |
|                                 "sqlite",
 | |
|                             ],
 | |
|                             check_rc=False,
 | |
|                         ),
 | |
|                         mock.call(
 | |
|                             mock.ANY,
 | |
|                             [
 | |
|                                 "pacman",
 | |
|                                 "--noconfirm",
 | |
|                                 "--noprogressbar",
 | |
|                                 "--needed",
 | |
|                                 "--sync",
 | |
|                                 "sqlite",
 | |
|                             ],
 | |
|                             check_rc=False,
 | |
|                         ),
 | |
|                     ],
 | |
|                     "side_effect": [(0, "sqlite new-version", ""), (0, "", "")],
 | |
|                 },
 | |
|                 AnsibleExitJson,
 | |
|             ),
 | |
|         ],
 | |
|     )
 | |
|     def test_op_packages(
 | |
|         self,
 | |
|         mock_valid_inventory,
 | |
|         mock_package_list,
 | |
|         module_args,
 | |
|         expected_packages,
 | |
|         package_list_out,
 | |
|         run_command_data,
 | |
|         raises,
 | |
|     ):
 | |
|         with set_module_args(module_args):
 | |
|             self.mock_run_command.side_effect = run_command_data["side_effect"]
 | |
|             mock_package_list.return_value = package_list_out
 | |
| 
 | |
|             P = pacman.Pacman(pacman.setup_module())
 | |
|             with pytest.raises(raises) as e:
 | |
|                 P.run()
 | |
|         out = e.value.args[0]
 | |
| 
 | |
|         assert self.mock_run_command.mock_calls == run_command_data["calls"]
 | |
|         if raises == AnsibleExitJson:
 | |
|             assert out["packages"] == expected_packages
 | |
|             assert out["changed"]
 | |
|             assert "packages" in out
 | |
|             assert "diff" in out
 | |
|         else:
 | |
|             assert out["stdout"] == "stdout"
 | |
|             assert out["stderr"] == "stderr"
 |