mirror of
https://github.com/kevinveenbirkenbach/computer-playbook.git
synced 2026-11-08 11:46:35 +00:00
The apt module loads its whole cache before it finds that every name is installed, about 2.4 s per call; an update deploy spent ~28 s on twelve such no-op calls from roles outside the batch. package_install now asks dpkg-query, pacman --query or rpm --query under LC_ALL=C first and returns unchanged only when every plain name is proven to be in the requested state; anything it cannot read in full runs the module as before. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
208 lines
8.2 KiB
Python
208 lines
8.2 KiB
Python
import unittest
|
|
|
|
from utils.packages.calls import (
|
|
GENERIC_PACKAGE,
|
|
STATE_ABSENT,
|
|
STATE_PRESENT,
|
|
ModuleCall,
|
|
package_call,
|
|
)
|
|
from utils.packages.installed import query_argv, state_holds
|
|
|
|
|
|
def _holds(pkg_mgr, names, state, rc, stdout="", stderr="", **extra):
|
|
result = {"rc": rc, "stdout": stdout, "stderr": stderr, **extra}
|
|
return state_holds(package_call(names, state), pkg_mgr, result)
|
|
|
|
|
|
def _present(pkg_mgr, names, rc, stdout="", stderr=""):
|
|
return _holds(pkg_mgr, names, STATE_PRESENT, rc, stdout, stderr)
|
|
|
|
|
|
def _absent(pkg_mgr, names, rc, stdout="", stderr=""):
|
|
return _holds(pkg_mgr, names, STATE_ABSENT, rc, stdout, stderr)
|
|
|
|
|
|
class TestQueryArgv(unittest.TestCase):
|
|
def test_apt_asks_dpkg_for_every_name_in_the_c_locale(self):
|
|
argv = query_argv(package_call(["git", "curl"], STATE_PRESENT), "apt")
|
|
self.assertEqual(argv[:3], ["env", "LC_ALL=C", "dpkg-query"])
|
|
self.assertEqual(argv[-3:], ["--", "git", "curl"])
|
|
|
|
def test_pacman_asks_the_local_database(self):
|
|
argv = query_argv(package_call(["git"], STATE_ABSENT), "pacman")
|
|
self.assertEqual(argv[2:5], ["pacman", "--query", "--"])
|
|
|
|
def test_every_rpm_manager_asks_rpm(self):
|
|
for pkg_mgr in ("dnf", "dnf5", "yum"):
|
|
with self.subTest(pkg_mgr=pkg_mgr):
|
|
argv = query_argv(package_call(["git"], STATE_PRESENT), pkg_mgr)
|
|
self.assertEqual(argv[2], "rpm")
|
|
self.assertEqual(argv[-1], "git")
|
|
|
|
def test_an_unknown_manager_is_not_queried(self):
|
|
self.assertIsNone(query_argv(package_call(["git"], STATE_PRESENT), "zypper"))
|
|
|
|
def test_a_name_that_is_not_plain_is_not_queried(self):
|
|
for name in (
|
|
"nginx=1.24",
|
|
"libc6:i386",
|
|
"/tmp/pkg.deb",
|
|
"https://example.org/pkg.rpm",
|
|
"python3-*",
|
|
"-x",
|
|
"",
|
|
):
|
|
with self.subTest(name=name):
|
|
call = package_call(["git", name], STATE_PRESENT)
|
|
self.assertIsNone(query_argv(call, "apt"))
|
|
|
|
def test_a_concrete_module_is_not_queried(self):
|
|
call = ModuleCall(
|
|
"community.general.pacman", {"name": ["git"], "state": "present"}
|
|
)
|
|
self.assertIsNone(query_argv(call, "pacman"))
|
|
|
|
def test_a_call_under_another_user_is_not_queried(self):
|
|
call = ModuleCall(
|
|
GENERIC_PACKAGE, {"name": ["git"], "state": "present"}, become_user="b"
|
|
)
|
|
self.assertIsNone(query_argv(call, "pacman"))
|
|
|
|
def test_a_call_with_further_arguments_is_not_queried(self):
|
|
call = ModuleCall(
|
|
GENERIC_PACKAGE,
|
|
{"name": ["git"], "state": "present", "update_cache": True},
|
|
)
|
|
self.assertIsNone(query_argv(call, "apt"))
|
|
|
|
def test_an_empty_name_list_is_not_queried(self):
|
|
self.assertIsNone(query_argv(package_call([], STATE_PRESENT), "apt"))
|
|
|
|
|
|
class TestDpkg(unittest.TestCase):
|
|
def test_every_name_installed_holds_present(self):
|
|
self.assertTrue(_present("apt", ["git", "curl"], 0, "git\tii \ncurl\tii "))
|
|
|
|
def test_a_held_package_counts_as_installed(self):
|
|
self.assertTrue(_present("apt", ["git"], 0, "git\thi "))
|
|
|
|
def test_a_trimmed_last_line_still_parses(self):
|
|
self.assertTrue(_present("apt", ["git"], 0, "git\tii"))
|
|
|
|
def test_one_unknown_name_breaks_present(self):
|
|
stderr = "dpkg-query: no packages found matching curl"
|
|
self.assertFalse(_present("apt", ["git", "curl"], 1, "git\tii ", stderr))
|
|
|
|
def test_one_installed_name_breaks_absent(self):
|
|
stderr = "dpkg-query: no packages found matching curl"
|
|
self.assertFalse(_absent("apt", ["git", "curl"], 1, "git\tii ", stderr))
|
|
|
|
def test_unknown_and_config_files_only_hold_absent(self):
|
|
stderr = "dpkg-query: no packages found matching postfix-lmdb"
|
|
self.assertTrue(
|
|
_absent("apt", ["postfix", "postfix-lmdb"], 1, "postfix\trc ", stderr)
|
|
)
|
|
|
|
def test_config_files_only_breaks_present(self):
|
|
self.assertFalse(_present("apt", ["postfix"], 0, "postfix\trc "))
|
|
|
|
def test_a_half_installed_package_holds_neither_state(self):
|
|
self.assertFalse(_present("apt", ["git"], 0, "git\tiU "))
|
|
self.assertFalse(_absent("apt", ["git"], 0, "git\tiU "))
|
|
|
|
def test_a_package_that_needs_reinstalling_is_not_installed(self):
|
|
self.assertFalse(_present("apt", ["git"], 0, "git\tiiR"))
|
|
|
|
def test_every_architecture_must_be_installed(self):
|
|
self.assertTrue(_present("apt", ["libc6"], 0, "libc6\tii \nlibc6\tii "))
|
|
self.assertFalse(_present("apt", ["libc6"], 0, "libc6\tii \nlibc6\trc "))
|
|
self.assertFalse(_absent("apt", ["libc6"], 0, "libc6\tii \nlibc6\trc "))
|
|
|
|
|
|
class TestPacman(unittest.TestCase):
|
|
def test_every_name_installed_holds_present(self):
|
|
self.assertTrue(
|
|
_present("pacman", ["git", "curl"], 0, "git 2.51-1\ncurl 8.16-1")
|
|
)
|
|
|
|
def test_one_missing_name_breaks_present(self):
|
|
stderr = "error: package 'curl' was not found"
|
|
self.assertFalse(_present("pacman", ["git", "curl"], 1, "git 2.51-1", stderr))
|
|
|
|
def test_no_name_installed_holds_absent(self):
|
|
stderr = (
|
|
"error: package 'postfix' was not found\n"
|
|
"error: package 'postfix-lmdb' was not found"
|
|
)
|
|
self.assertTrue(_absent("pacman", ["postfix", "postfix-lmdb"], 1, "", stderr))
|
|
|
|
def test_one_installed_name_breaks_absent(self):
|
|
stderr = "error: package 'postfix-lmdb' was not found"
|
|
self.assertFalse(
|
|
_absent("pacman", ["postfix", "postfix-lmdb"], 1, "postfix 3.11-2", stderr)
|
|
)
|
|
|
|
def test_a_name_answered_by_its_provider_proves_nothing(self):
|
|
self.assertFalse(_present("pacman", ["sh"], 0, "bash 5.3-1"))
|
|
self.assertFalse(_absent("pacman", ["sh"], 0, "bash 5.3-1"))
|
|
|
|
|
|
class TestRpm(unittest.TestCase):
|
|
def test_every_name_installed_holds_present(self):
|
|
self.assertTrue(_present("dnf", ["git", "curl"], 0, "git\ncurl"))
|
|
|
|
def test_one_missing_name_holds_neither_state(self):
|
|
stdout = "git\npackage curl is not installed"
|
|
self.assertFalse(_present("dnf5", ["git", "curl"], 1, stdout))
|
|
self.assertFalse(_absent("dnf5", ["git", "curl"], 1, stdout))
|
|
|
|
def test_no_name_installed_holds_absent(self):
|
|
stdout = (
|
|
"package postfix is not installed\npackage postfix-lmdb is not installed"
|
|
)
|
|
self.assertTrue(_absent("yum", ["postfix", "postfix-lmdb"], 2, stdout))
|
|
|
|
def test_the_not_installed_message_may_come_on_stderr(self):
|
|
stderr = "package postfix is not installed"
|
|
self.assertTrue(_absent("dnf", ["postfix"], 1, "", stderr))
|
|
|
|
def test_a_version_match_under_another_name_proves_nothing(self):
|
|
self.assertFalse(_present("dnf", ["foo-1.0"], 0, "foo"))
|
|
|
|
def test_a_translated_message_proves_nothing(self):
|
|
stdout = "Paket postfix ist nicht installiert"
|
|
self.assertFalse(_absent("dnf", ["postfix"], 1, stdout))
|
|
|
|
|
|
class TestDoubtRunsTheModule(unittest.TestCase):
|
|
def test_a_check_mode_skip_proves_nothing(self):
|
|
self.assertFalse(_holds("apt", ["git"], STATE_ABSENT, 0, skipped=True))
|
|
|
|
def test_a_failure_without_a_not_found_message_proves_nothing(self):
|
|
self.assertFalse(_absent("apt", ["git"], 1))
|
|
self.assertFalse(_absent("pacman", ["git"], 1))
|
|
self.assertFalse(_absent("dnf", ["git"], 1))
|
|
|
|
def test_success_with_a_not_found_message_proves_nothing(self):
|
|
stderr = "dpkg-query: no packages found matching git"
|
|
self.assertFalse(_absent("apt", ["git"], 0, "", stderr))
|
|
|
|
def test_a_name_left_unanswered_proves_nothing(self):
|
|
self.assertFalse(_present("apt", ["git", "curl"], 0, "git\tii "))
|
|
|
|
def test_a_foreign_stderr_line_proves_nothing(self):
|
|
stderr = "dpkg-query: error: parsing file '/var/lib/dpkg/status'"
|
|
self.assertFalse(_present("apt", ["git"], 0, "git\tii ", stderr))
|
|
|
|
def test_a_foreign_stdout_line_proves_nothing(self):
|
|
self.assertFalse(_present("apt", ["git"], 0, "git\tii \ncurl\tii "))
|
|
|
|
def test_a_result_without_output_proves_nothing(self):
|
|
call = package_call(["git"], STATE_ABSENT)
|
|
self.assertFalse(state_holds(call, "apt", {"failed": True, "msg": "boom"}))
|
|
|
|
|
|
if __name__ == "__main__":
|
|
unittest.main()
|