diff --git a/changelogs/fragments/fix-dnf5-in-dnf_config_manager.yml b/changelogs/fragments/fix-dnf5-in-dnf_config_manager.yml new file mode 100644 index 0000000000..9973fc8afe --- /dev/null +++ b/changelogs/fragments/fix-dnf5-in-dnf_config_manager.yml @@ -0,0 +1,2 @@ +bugfixes: + - dnf_config_manager - fix incompatibility with DNF5. The module was crashing on systems with DNF5 due to CLI changes since DNF4 (https://github.com/ansible-collections/community.general/issues/9127). diff --git a/plugins/modules/dnf_config_manager.py b/plugins/modules/dnf_config_manager.py index 7063adae4c..30ccf63014 100644 --- a/plugins/modules/dnf_config_manager.py +++ b/plugins/modules/dnf_config_manager.py @@ -125,12 +125,27 @@ import re from ansible.module_utils.basic import AnsibleModule DNF_BIN = "/usr/bin/dnf" -REPO_ID_RE = re.compile(r"^Repo-id\s*:\s*(\S+)$") -REPO_STATUS_RE = re.compile(r"^Repo-status\s*:\s*(disabled|enabled)$") +REPO_ID_RE = re.compile(r"^Repo[-\s]id\s*:\s*(\S+)$", re.IGNORECASE) +REPO_STATUS_RE = re.compile(r"^(?:Repo-)?status\s*:\s*(disabled|enabled)$", re.IGNORECASE) -def get_repo_states(module): - rc, out, err = module.run_command([DNF_BIN, "repolist", "--all", "--verbose"], check_rc=True) +def get_dnf_version(module) -> 4 | 5: + rc, out, err = module.run_command([DNF_BIN, "--version"], check_rc=True) + line, separator, rest = out.partition("\n") + if re.compile(r"^dnf5\s*").match(line): + return 5 + else: + return 4 + + +def get_repo_states(module, dnf_v): + command = [DNF_BIN] + if dnf_v == 4: + command.extend(["repolist", "--all", "--verbose"]) + else: + command.extend(["repo", "info", "--all"]) + + rc, out, err = module.run_command(command, check_rc=True) repos = dict() last_repo = "" @@ -150,8 +165,13 @@ def get_repo_states(module): return repos -def set_repo_states(module, repo_ids, state): - module.run_command([DNF_BIN, "config-manager", "--assumeyes", f"--set-{state}"] + repo_ids, check_rc=True) +def set_repo_states(module, dnf_v, repo_ids, state): + if dnf_v == 4: + module.run_command([DNF_BIN, "config-manager", "--assumeyes", f"--set-{state}"] + repo_ids, check_rc=True) + else: + state = "1" if state == "enabled" else "0" + opts = map(lambda v: f"{v}.enabled={state}", repo_ids) + module.run_command([DNF_BIN, "config-manager", "setopt"] + list(opts), check_rc=True) def pack_repo_states_for_return(states): @@ -184,7 +204,9 @@ def main(): if not os.path.exists(DNF_BIN): module.fail_json(msg=f"{DNF_BIN} was not found") - repo_states = get_repo_states(module) + dnf_v = get_dnf_version(module) + + repo_states = get_repo_states(module, dnf_v) result["repo_states_pre"] = pack_repo_states_for_return(repo_states) desired_repo_state = module.params["state"] @@ -203,9 +225,9 @@ def main(): module.exit_json(**result) if len(to_change) > 0: - set_repo_states(module, to_change, desired_repo_state) + set_repo_states(module, dnf_v, to_change, desired_repo_state) - repo_states_post = get_repo_states(module) + repo_states_post = get_repo_states(module, dnf_v) result["repo_states_post"] = pack_repo_states_for_return(repo_states_post) for repo_id in to_change: diff --git a/tests/unit/plugins/modules/dnf_config_manager/fixtures/mock_dnf4_repolist_crb_disabled.txt b/tests/unit/plugins/modules/dnf_config_manager/fixtures/mock_dnf4_states_repo_disabled.txt similarity index 100% rename from tests/unit/plugins/modules/dnf_config_manager/fixtures/mock_dnf4_repolist_crb_disabled.txt rename to tests/unit/plugins/modules/dnf_config_manager/fixtures/mock_dnf4_states_repo_disabled.txt diff --git a/tests/unit/plugins/modules/dnf_config_manager/fixtures/mock_dnf4_repolist_crb_enabled.txt b/tests/unit/plugins/modules/dnf_config_manager/fixtures/mock_dnf4_states_repo_enabled.txt similarity index 100% rename from tests/unit/plugins/modules/dnf_config_manager/fixtures/mock_dnf4_repolist_crb_enabled.txt rename to tests/unit/plugins/modules/dnf_config_manager/fixtures/mock_dnf4_states_repo_enabled.txt diff --git a/tests/unit/plugins/modules/dnf_config_manager/fixtures/mock_dnf4_version.txt b/tests/unit/plugins/modules/dnf_config_manager/fixtures/mock_dnf4_version.txt new file mode 100644 index 0000000000..a66e5a549f --- /dev/null +++ b/tests/unit/plugins/modules/dnf_config_manager/fixtures/mock_dnf4_version.txt @@ -0,0 +1,6 @@ +4.23.0 + Installed: dnf-0:4.23.0-1.fc40.1.noarch at Tue May 13 07:48:45 2025 + Built : Fedora Project at Fri Apr 11 12:21:43 2025 + + Installed: rpm-0:4.19.1.1-1.fc40.x86_64 at Tue May 13 07:48:45 2025 + Built : Fedora Project at Wed Feb 7 15:55:53 2024 diff --git a/tests/unit/plugins/modules/dnf_config_manager/fixtures/mock_dnf5_states_repo_disabled.txt b/tests/unit/plugins/modules/dnf_config_manager/fixtures/mock_dnf5_states_repo_disabled.txt new file mode 100644 index 0000000000..6e731910b4 --- /dev/null +++ b/tests/unit/plugins/modules/dnf_config_manager/fixtures/mock_dnf5_states_repo_disabled.txt @@ -0,0 +1,299 @@ +Repo ID : copr:copr.fedorainfracloud.org:phracek:PyCharm +Name : Copr repo for PyCharm owned by phracek +Status : disabled +Priority : 99 +Cost : 1000 +Type : available +Metadata expire : 172800 seconds (last: unknown) +Skip if unavailable : true +Config file : /etc/yum.repos.d/_copr:copr.fedorainfracloud.org:phracek:PyCharm.repo +URLs : + Base URL : https://download.copr.fedorainfracloud.org/results/phracek/PyCharm/fedora-44-x86_64/ +OpenPGP : + Keys : https://download.copr.fedorainfracloud.org/results/phracek/PyCharm/pubkey.gpg + Verify repodata : false + Verify packages : true + +Repo ID : fedora +Name : Fedora 44 - x86_64 +Status : enabled +Priority : 99 +Cost : 1000 +Type : available +Metadata expire : 604800 seconds (last: 2026-06-08 03:33:15) +Skip if unavailable : false +Config file : /etc/yum.repos.d/fedora.repo +URLs : + Base URL : https://distrohub.kyiv.ua/fedora/fedora/linux/releases/44/Everything/x86_64/os/ (103 more) + Metalink : https://mirrors.fedoraproject.org/metalink?repo=fedora-44&arch=x86_64 +OpenPGP : + Keys : file:///etc/pki/rpm-gpg/RPM-GPG-KEY-fedora-44-x86_64 + Verify repodata : false + Verify packages : true +Repodata info : + Available packages : 76354 + Total packages : 76354 + Size : 113.1 GiB + Revision : 1776864872 + Updated : 2026-04-22 13:34:32 + +Repo ID : fedora-cisco-openh264 +Name : Fedora 44 openh264 (From Cisco) - x86_64 +Status : enabled +Priority : 99 +Cost : 1000 +Type : available +Metadata expire : 1209600 seconds (last: 2026-06-08 03:33:15) +Skip if unavailable : true +Config file : /etc/yum.repos.d/fedora-cisco-openh264.repo +URLs : + Base URL : https://codecs.fedoraproject.org/openh264/44/x86_64/ (1 more) + Metalink : https://mirrors.fedoraproject.org/metalink?repo=fedora-cisco-openh264-44&arch=x86_64 +OpenPGP : + Keys : file:///etc/pki/rpm-gpg/RPM-GPG-KEY-fedora-44-x86_64 + Verify repodata : false + Verify packages : true +Repodata info : + Available packages : 3 + Total packages : 3 + Size : 901.3 KiB + Revision : 1774348624 + Updated : 2026-03-24 10:37:04 + +Repo ID : fedora-cisco-openh264-debuginfo +Name : Fedora 44 openh264 (From Cisco) - x86_64 - Debug +Status : disabled +Priority : 99 +Cost : 1000 +Type : available +Metadata expire : 1209600 seconds (last: unknown) +Skip if unavailable : true +Config file : /etc/yum.repos.d/fedora-cisco-openh264.repo +URLs : + Metalink : https://mirrors.fedoraproject.org/metalink?repo=fedora-cisco-openh264-debug-44&arch=x86_64 +OpenPGP : + Keys : file:///etc/pki/rpm-gpg/RPM-GPG-KEY-fedora-44-x86_64 + Verify repodata : false + Verify packages : true + +Repo ID : fedora-cisco-openh264-source +Name : Fedora 44 openh264 (From Cisco) - x86_64 - Source +Status : disabled +Priority : 99 +Cost : 1000 +Type : available +Metadata expire : 1209600 seconds (last: unknown) +Skip if unavailable : true +Config file : /etc/yum.repos.d/fedora-cisco-openh264.repo +URLs : + Metalink : https://mirrors.fedoraproject.org/metalink?repo=fedora-cisco-openh264-source-44&arch=x86_64 +OpenPGP : + Keys : file:///etc/pki/rpm-gpg/RPM-GPG-KEY-fedora-44-x86_64 + Verify repodata : false + Verify packages : true + +Repo ID : fedora-debuginfo +Name : Fedora 44 - x86_64 - Debug +Status : disabled +Priority : 99 +Cost : 1000 +Type : available +Metadata expire : 604800 seconds (last: unknown) +Skip if unavailable : false +Config file : /etc/yum.repos.d/fedora.repo +URLs : + Metalink : https://mirrors.fedoraproject.org/metalink?repo=fedora-debug-44&arch=x86_64 +OpenPGP : + Keys : file:///etc/pki/rpm-gpg/RPM-GPG-KEY-fedora-44-x86_64 + Verify repodata : false + Verify packages : true + +Repo ID : fedora-source +Name : Fedora 44 - Source +Status : disabled +Priority : 99 +Cost : 1000 +Type : available +Metadata expire : 604800 seconds (last: unknown) +Skip if unavailable : false +Config file : /etc/yum.repos.d/fedora.repo +URLs : + Metalink : https://mirrors.fedoraproject.org/metalink?repo=fedora-source-44&arch=x86_64 +OpenPGP : + Keys : file:///etc/pki/rpm-gpg/RPM-GPG-KEY-fedora-44-x86_64 + Verify repodata : false + Verify packages : true + +Repo ID : google-chrome +Name : google-chrome +Status : enabled +Priority : 99 +Cost : 1000 +Type : available +Metadata expire : 172800 seconds (last: 2026-06-08 03:33:15) +Skip if unavailable : true +Config file : /etc/yum.repos.d/google-chrome.repo +URLs : + Base URL : https://dl.google.com/linux/chrome/rpm/stable/x86_64 +OpenPGP : + Keys : https://dl.google.com/linux/linux_signing_key.pub + Verify repodata : false + Verify packages : true +Repodata info : + Available packages : 4 + Total packages : 4 + Size : 519.9 MiB + Revision : 1780771125 + Updated : 2026-06-06 18:38:45 + +Repo ID : rawhide +Name : Fedora - Rawhide - Developmental packages for the next Fedora release +Status : disabled +Priority : 99 +Cost : 1000 +Type : available +Metadata expire : 21600 seconds (last: unknown) +Skip if unavailable : false +Config file : /etc/yum.repos.d/fedora-rawhide.repo +URLs : + Metalink : https://mirrors.fedoraproject.org/metalink?repo=rawhide&arch=x86_64 +OpenPGP : + Keys : file:///etc/pki/rpm-gpg/RPM-GPG-KEY-fedora-rawhide-x86_64 file:///etc/pki/rpm-gpg/RPM-GPG-KEY-fedora-46-x86_64 + Verify repodata : false + Verify packages : true + +Repo ID : rawhide-debuginfo +Name : Fedora - Rawhide - Debug +Status : disabled +Priority : 99 +Cost : 1000 +Type : available +Metadata expire : 21600 seconds (last: unknown) +Skip if unavailable : false +Config file : /etc/yum.repos.d/fedora-rawhide.repo +URLs : + Metalink : https://mirrors.fedoraproject.org/metalink?repo=rawhide-debug&arch=x86_64 +OpenPGP : + Keys : file:///etc/pki/rpm-gpg/RPM-GPG-KEY-fedora-rawhide-x86_64 file:///etc/pki/rpm-gpg/RPM-GPG-KEY-fedora-46-x86_64 + Verify repodata : false + Verify packages : true + +Repo ID : rawhide-source +Name : Fedora - Rawhide - Source +Status : disabled +Priority : 99 +Cost : 1000 +Type : available +Metadata expire : 21600 seconds (last: unknown) +Skip if unavailable : false +Config file : /etc/yum.repos.d/fedora-rawhide.repo +URLs : + Metalink : https://mirrors.fedoraproject.org/metalink?repo=rawhide-source&arch=x86_64 +OpenPGP : + Keys : file:///etc/pki/rpm-gpg/RPM-GPG-KEY-fedora-rawhide-x86_64 file:///etc/pki/rpm-gpg/RPM-GPG-KEY-fedora-46-x86_64 + Verify repodata : false + Verify packages : true + +Repo ID : updates +Name : Fedora 44 - x86_64 - Updates +Status : enabled +Priority : 99 +Cost : 1000 +Type : available +Metadata expire : 21600 seconds (last: 2026-06-08 23:37:59) +Skip if unavailable : false +Config file : /etc/yum.repos.d/fedora-updates.repo +URLs : + Base URL : https://distrohub.kyiv.ua/fedora/fedora/linux/updates/44/Everything/x86_64/ (30 more) + Metalink : https://mirrors.fedoraproject.org/metalink?repo=updates-released-f44&arch=x86_64 +OpenPGP : + Keys : file:///etc/pki/rpm-gpg/RPM-GPG-KEY-fedora-44-x86_64 + Verify repodata : false + Verify packages : true +Repodata info : + Available packages : 17879 + Total packages : 17879 + Size : 42.9 GiB + Revision : 1780878040 + Updated : 2026-06-08 01:19:51 + +Repo ID : updates-debuginfo +Name : Fedora 44 - x86_64 - Updates - Debug +Status : disabled +Priority : 99 +Cost : 1000 +Type : available +Metadata expire : 21600 seconds (last: unknown) +Skip if unavailable : false +Config file : /etc/yum.repos.d/fedora-updates.repo +URLs : + Metalink : https://mirrors.fedoraproject.org/metalink?repo=updates-released-debug-f44&arch=x86_64 +OpenPGP : + Keys : file:///etc/pki/rpm-gpg/RPM-GPG-KEY-fedora-44-x86_64 + Verify repodata : false + Verify packages : true + +Repo ID : updates-source +Name : Fedora 44 - Updates Source +Status : disabled +Priority : 99 +Cost : 1000 +Type : available +Metadata expire : 21600 seconds (last: unknown) +Skip if unavailable : false +Config file : /etc/yum.repos.d/fedora-updates.repo +URLs : + Metalink : https://mirrors.fedoraproject.org/metalink?repo=updates-released-source-f44&arch=x86_64 +OpenPGP : + Keys : file:///etc/pki/rpm-gpg/RPM-GPG-KEY-fedora-44-x86_64 + Verify repodata : false + Verify packages : true + +Repo ID : updates-testing +Name : Fedora 44 - x86_64 - Test Updates +Status : disabled +Priority : 99 +Cost : 1000 +Type : available +Metadata expire : 21600 seconds (last: unknown) +Skip if unavailable : false +Config file : /etc/yum.repos.d/fedora-updates-testing.repo +URLs : + Metalink : https://mirrors.fedoraproject.org/metalink?repo=updates-testing-f44&arch=x86_64 +OpenPGP : + Keys : file:///etc/pki/rpm-gpg/RPM-GPG-KEY-fedora-44-x86_64 + Verify repodata : false + Verify packages : true + +Repo ID : updates-testing-debuginfo +Name : Fedora 44 - x86_64 - Test Updates Debug +Status : disabled +Priority : 99 +Cost : 1000 +Type : available +Metadata expire : 21600 seconds (last: unknown) +Skip if unavailable : false +Config file : /etc/yum.repos.d/fedora-updates-testing.repo +URLs : + Metalink : https://mirrors.fedoraproject.org/metalink?repo=updates-testing-debug-f44&arch=x86_64 +OpenPGP : + Keys : file:///etc/pki/rpm-gpg/RPM-GPG-KEY-fedora-44-x86_64 + Verify repodata : false + Verify packages : true + +Repo ID : updates-testing-source +Name : Fedora 44 - Test Updates Source +Status : disabled +Priority : 99 +Cost : 1000 +Type : available +Metadata expire : 21600 seconds (last: unknown) +Skip if unavailable : false +Config file : /etc/yum.repos.d/fedora-updates-testing.repo +URLs : + Metalink : https://mirrors.fedoraproject.org/metalink?repo=updates-testing-source-f44&arch=x86_64 +OpenPGP : + Keys : file:///etc/pki/rpm-gpg/RPM-GPG-KEY-fedora-44-x86_64 + Verify repodata : false + Verify packages : true + diff --git a/tests/unit/plugins/modules/dnf_config_manager/fixtures/mock_dnf5_states_repo_enabled.txt b/tests/unit/plugins/modules/dnf_config_manager/fixtures/mock_dnf5_states_repo_enabled.txt new file mode 100644 index 0000000000..a60d47c79e --- /dev/null +++ b/tests/unit/plugins/modules/dnf_config_manager/fixtures/mock_dnf5_states_repo_enabled.txt @@ -0,0 +1,304 @@ +Repo ID : copr:copr.fedorainfracloud.org:phracek:PyCharm +Name : Copr repo for PyCharm owned by phracek +Status : enabled +Priority : 99 +Cost : 1000 +Type : available +Metadata expire : 172800 seconds (last: 2026-06-08 03:33:15) +Skip if unavailable : true +Config file : /etc/yum.repos.d/_copr:copr.fedorainfracloud.org:phracek:PyCharm.repo +URLs : + Base URL : https://download.copr.fedorainfracloud.org/results/phracek/PyCharm/fedora-44-x86_64/ +OpenPGP : + Keys : https://download.copr.fedorainfracloud.org/results/phracek/PyCharm/pubkey.gpg + Verify repodata : false + Verify packages : true +Repodata info : + Available packages : 5 + Total packages : 5 + Size : 1.8 GiB + Revision : 1771912250 + Updated : 2026-02-24 05:51:20 + +Repo ID : fedora +Name : Fedora 44 - x86_64 +Status : enabled +Priority : 99 +Cost : 1000 +Type : available +Metadata expire : 604800 seconds (last: 2026-06-08 03:33:15) +Skip if unavailable : false +Config file : /etc/yum.repos.d/fedora.repo +URLs : + Base URL : https://distrohub.kyiv.ua/fedora/fedora/linux/releases/44/Everything/x86_64/os/ (103 more) + Metalink : https://mirrors.fedoraproject.org/metalink?repo=fedora-44&arch=x86_64 +OpenPGP : + Keys : file:///etc/pki/rpm-gpg/RPM-GPG-KEY-fedora-44-x86_64 + Verify repodata : false + Verify packages : true +Repodata info : + Available packages : 76354 + Total packages : 76354 + Size : 113.1 GiB + Revision : 1776864872 + Updated : 2026-04-22 13:34:32 + +Repo ID : fedora-cisco-openh264 +Name : Fedora 44 openh264 (From Cisco) - x86_64 +Status : enabled +Priority : 99 +Cost : 1000 +Type : available +Metadata expire : 1209600 seconds (last: 2026-06-08 03:33:15) +Skip if unavailable : true +Config file : /etc/yum.repos.d/fedora-cisco-openh264.repo +URLs : + Base URL : https://codecs.fedoraproject.org/openh264/44/x86_64/ (1 more) + Metalink : https://mirrors.fedoraproject.org/metalink?repo=fedora-cisco-openh264-44&arch=x86_64 +OpenPGP : + Keys : file:///etc/pki/rpm-gpg/RPM-GPG-KEY-fedora-44-x86_64 + Verify repodata : false + Verify packages : true +Repodata info : + Available packages : 3 + Total packages : 3 + Size : 901.3 KiB + Revision : 1774348624 + Updated : 2026-03-24 10:37:04 + +Repo ID : fedora-cisco-openh264-debuginfo +Name : Fedora 44 openh264 (From Cisco) - x86_64 - Debug +Status : disabled +Priority : 99 +Cost : 1000 +Type : available +Metadata expire : 1209600 seconds (last: unknown) +Skip if unavailable : true +Config file : /etc/yum.repos.d/fedora-cisco-openh264.repo +URLs : + Metalink : https://mirrors.fedoraproject.org/metalink?repo=fedora-cisco-openh264-debug-44&arch=x86_64 +OpenPGP : + Keys : file:///etc/pki/rpm-gpg/RPM-GPG-KEY-fedora-44-x86_64 + Verify repodata : false + Verify packages : true + +Repo ID : fedora-cisco-openh264-source +Name : Fedora 44 openh264 (From Cisco) - x86_64 - Source +Status : disabled +Priority : 99 +Cost : 1000 +Type : available +Metadata expire : 1209600 seconds (last: unknown) +Skip if unavailable : true +Config file : /etc/yum.repos.d/fedora-cisco-openh264.repo +URLs : + Metalink : https://mirrors.fedoraproject.org/metalink?repo=fedora-cisco-openh264-source-44&arch=x86_64 +OpenPGP : + Keys : file:///etc/pki/rpm-gpg/RPM-GPG-KEY-fedora-44-x86_64 + Verify repodata : false + Verify packages : true + +Repo ID : fedora-debuginfo +Name : Fedora 44 - x86_64 - Debug +Status : disabled +Priority : 99 +Cost : 1000 +Type : available +Metadata expire : 604800 seconds (last: unknown) +Skip if unavailable : false +Config file : /etc/yum.repos.d/fedora.repo +URLs : + Metalink : https://mirrors.fedoraproject.org/metalink?repo=fedora-debug-44&arch=x86_64 +OpenPGP : + Keys : file:///etc/pki/rpm-gpg/RPM-GPG-KEY-fedora-44-x86_64 + Verify repodata : false + Verify packages : true + +Repo ID : fedora-source +Name : Fedora 44 - Source +Status : disabled +Priority : 99 +Cost : 1000 +Type : available +Metadata expire : 604800 seconds (last: unknown) +Skip if unavailable : false +Config file : /etc/yum.repos.d/fedora.repo +URLs : + Metalink : https://mirrors.fedoraproject.org/metalink?repo=fedora-source-44&arch=x86_64 +OpenPGP : + Keys : file:///etc/pki/rpm-gpg/RPM-GPG-KEY-fedora-44-x86_64 + Verify repodata : false + Verify packages : true + +Repo ID : google-chrome +Name : google-chrome +Status : enabled +Priority : 99 +Cost : 1000 +Type : available +Metadata expire : 172800 seconds (last: 2026-06-08 03:33:15) +Skip if unavailable : true +Config file : /etc/yum.repos.d/google-chrome.repo +URLs : + Base URL : https://dl.google.com/linux/chrome/rpm/stable/x86_64 +OpenPGP : + Keys : https://dl.google.com/linux/linux_signing_key.pub + Verify repodata : false + Verify packages : true +Repodata info : + Available packages : 4 + Total packages : 4 + Size : 519.9 MiB + Revision : 1780771125 + Updated : 2026-06-06 18:38:45 + +Repo ID : rawhide +Name : Fedora - Rawhide - Developmental packages for the next Fedora release +Status : disabled +Priority : 99 +Cost : 1000 +Type : available +Metadata expire : 21600 seconds (last: unknown) +Skip if unavailable : false +Config file : /etc/yum.repos.d/fedora-rawhide.repo +URLs : + Metalink : https://mirrors.fedoraproject.org/metalink?repo=rawhide&arch=x86_64 +OpenPGP : + Keys : file:///etc/pki/rpm-gpg/RPM-GPG-KEY-fedora-rawhide-x86_64 file:///etc/pki/rpm-gpg/RPM-GPG-KEY-fedora-46-x86_64 + Verify repodata : false + Verify packages : true + +Repo ID : rawhide-debuginfo +Name : Fedora - Rawhide - Debug +Status : disabled +Priority : 99 +Cost : 1000 +Type : available +Metadata expire : 21600 seconds (last: unknown) +Skip if unavailable : false +Config file : /etc/yum.repos.d/fedora-rawhide.repo +URLs : + Metalink : https://mirrors.fedoraproject.org/metalink?repo=rawhide-debug&arch=x86_64 +OpenPGP : + Keys : file:///etc/pki/rpm-gpg/RPM-GPG-KEY-fedora-rawhide-x86_64 file:///etc/pki/rpm-gpg/RPM-GPG-KEY-fedora-46-x86_64 + Verify repodata : false + Verify packages : true + +Repo ID : rawhide-source +Name : Fedora - Rawhide - Source +Status : disabled +Priority : 99 +Cost : 1000 +Type : available +Metadata expire : 21600 seconds (last: unknown) +Skip if unavailable : false +Config file : /etc/yum.repos.d/fedora-rawhide.repo +URLs : + Metalink : https://mirrors.fedoraproject.org/metalink?repo=rawhide-source&arch=x86_64 +OpenPGP : + Keys : file:///etc/pki/rpm-gpg/RPM-GPG-KEY-fedora-rawhide-x86_64 file:///etc/pki/rpm-gpg/RPM-GPG-KEY-fedora-46-x86_64 + Verify repodata : false + Verify packages : true + +Repo ID : updates +Name : Fedora 44 - x86_64 - Updates +Status : enabled +Priority : 99 +Cost : 1000 +Type : available +Metadata expire : 21600 seconds (last: 2026-06-08 23:37:59) +Skip if unavailable : false +Config file : /etc/yum.repos.d/fedora-updates.repo +URLs : + Base URL : https://distrohub.kyiv.ua/fedora/fedora/linux/updates/44/Everything/x86_64/ (30 more) + Metalink : https://mirrors.fedoraproject.org/metalink?repo=updates-released-f44&arch=x86_64 +OpenPGP : + Keys : file:///etc/pki/rpm-gpg/RPM-GPG-KEY-fedora-44-x86_64 + Verify repodata : false + Verify packages : true +Repodata info : + Available packages : 17879 + Total packages : 17879 + Size : 42.9 GiB + Revision : 1780878040 + Updated : 2026-06-08 01:19:51 + +Repo ID : updates-debuginfo +Name : Fedora 44 - x86_64 - Updates - Debug +Status : disabled +Priority : 99 +Cost : 1000 +Type : available +Metadata expire : 21600 seconds (last: unknown) +Skip if unavailable : false +Config file : /etc/yum.repos.d/fedora-updates.repo +URLs : + Metalink : https://mirrors.fedoraproject.org/metalink?repo=updates-released-debug-f44&arch=x86_64 +OpenPGP : + Keys : file:///etc/pki/rpm-gpg/RPM-GPG-KEY-fedora-44-x86_64 + Verify repodata : false + Verify packages : true + +Repo ID : updates-source +Name : Fedora 44 - Updates Source +Status : disabled +Priority : 99 +Cost : 1000 +Type : available +Metadata expire : 21600 seconds (last: unknown) +Skip if unavailable : false +Config file : /etc/yum.repos.d/fedora-updates.repo +URLs : + Metalink : https://mirrors.fedoraproject.org/metalink?repo=updates-released-source-f44&arch=x86_64 +OpenPGP : + Keys : file:///etc/pki/rpm-gpg/RPM-GPG-KEY-fedora-44-x86_64 + Verify repodata : false + Verify packages : true + +Repo ID : updates-testing +Name : Fedora 44 - x86_64 - Test Updates +Status : disabled +Priority : 99 +Cost : 1000 +Type : available +Metadata expire : 21600 seconds (last: unknown) +Skip if unavailable : false +Config file : /etc/yum.repos.d/fedora-updates-testing.repo +URLs : + Metalink : https://mirrors.fedoraproject.org/metalink?repo=updates-testing-f44&arch=x86_64 +OpenPGP : + Keys : file:///etc/pki/rpm-gpg/RPM-GPG-KEY-fedora-44-x86_64 + Verify repodata : false + Verify packages : true + +Repo ID : updates-testing-debuginfo +Name : Fedora 44 - x86_64 - Test Updates Debug +Status : disabled +Priority : 99 +Cost : 1000 +Type : available +Metadata expire : 21600 seconds (last: unknown) +Skip if unavailable : false +Config file : /etc/yum.repos.d/fedora-updates-testing.repo +URLs : + Metalink : https://mirrors.fedoraproject.org/metalink?repo=updates-testing-debug-f44&arch=x86_64 +OpenPGP : + Keys : file:///etc/pki/rpm-gpg/RPM-GPG-KEY-fedora-44-x86_64 + Verify repodata : false + Verify packages : true + +Repo ID : updates-testing-source +Name : Fedora 44 - Test Updates Source +Status : disabled +Priority : 99 +Cost : 1000 +Type : available +Metadata expire : 21600 seconds (last: unknown) +Skip if unavailable : false +Config file : /etc/yum.repos.d/fedora-updates-testing.repo +URLs : + Metalink : https://mirrors.fedoraproject.org/metalink?repo=updates-testing-source-f44&arch=x86_64 +OpenPGP : + Keys : file:///etc/pki/rpm-gpg/RPM-GPG-KEY-fedora-44-x86_64 + Verify repodata : false + Verify packages : true diff --git a/tests/unit/plugins/modules/dnf_config_manager/fixtures/mock_dnf5_version.txt b/tests/unit/plugins/modules/dnf_config_manager/fixtures/mock_dnf5_version.txt new file mode 100644 index 0000000000..51a28a8b12 --- /dev/null +++ b/tests/unit/plugins/modules/dnf_config_manager/fixtures/mock_dnf5_version.txt @@ -0,0 +1,46 @@ +dnf5 version 5.4.2.1 +dnf5 plugin API version 2.0 +libdnf5 version 5.4.2.1 +libdnf5 plugin API version 2.2 + +Loaded dnf5 plugins: + name: builddep + version: 1.0.0 + API version: 2.0 + + name: changelog + version: 1.0.0 + API version: 2.0 + + name: config-manager + version: 0.1.0 + API version: 2.0 + + name: copr + version: 0.1.0 + API version: 2.0 + + name: needs_restarting + version: 1.0.0 + API version: 2.0 + + name: repoclosure + version: 1.0.0 + API version: 2.0 + + name: repomanage + version: 1.0.0 + API version: 2.0 + + name: reposync + version: 1.0.0 + API version: 2.0 + +Loaded libdnf5 plugins: + name: appstream + version: 1.0.0 + API version: 2.0 + + name: expired-pgp-keys + version: 1.0.0 + API version: 2.1 diff --git a/tests/unit/plugins/modules/dnf_config_manager/test_dnf_config_manager.py b/tests/unit/plugins/modules/dnf_config_manager/test_dnf_config_manager.py index 94a380e72b..ea8c6d6a33 100644 --- a/tests/unit/plugins/modules/dnf_config_manager/test_dnf_config_manager.py +++ b/tests/unit/plugins/modules/dnf_config_manager/test_dnf_config_manager.py @@ -22,7 +22,7 @@ def fixture(name: str) -> str: return file.read() -expected_repo_states_crb_enabled = { +expected_states_enabled_repo_dnf4 = { "disabled": ["appstream-debuginfo", "appstream-source", "baseos-debuginfo", "baseos-source"], "enabled": [ "appstream", @@ -33,7 +33,7 @@ expected_repo_states_crb_enabled = { ], } -expected_repo_states_crb_disabled = { +expected_states_disabled_repo_dnf4 = { "disabled": ["appstream-debuginfo", "appstream-source", "baseos-debuginfo", "baseos-source", "crb"], "enabled": [ "appstream", @@ -43,9 +43,61 @@ expected_repo_states_crb_disabled = { ], } -call_get_repo_states = call(["/usr/bin/dnf", "repolist", "--all", "--verbose"], check_rc=True) -call_disable_crb = call(["/usr/bin/dnf", "config-manager", "--assumeyes", "--set-disabled", "crb"], check_rc=True) -call_enable_crb = call(["/usr/bin/dnf", "config-manager", "--assumeyes", "--set-enabled", "crb"], check_rc=True) +expected_states_enabled_repo_dnf5 = { + "disabled": [ + "fedora-cisco-openh264-debuginfo", + "fedora-cisco-openh264-source", + "fedora-debuginfo", + "fedora-source", + "rawhide", + "rawhide-debuginfo", + "rawhide-source", + "updates-debuginfo", + "updates-source", + "updates-testing", + "updates-testing-debuginfo", + "updates-testing-source", + ], + "enabled": [ + "copr:copr.fedorainfracloud.org:phracek:PyCharm", + "fedora", + "fedora-cisco-openh264", + "google-chrome", + "updates", + ], +} + +expected_states_disabled_repo_dnf5 = { + "disabled": [ + "copr:copr.fedorainfracloud.org:phracek:PyCharm", + "fedora-cisco-openh264-debuginfo", + "fedora-cisco-openh264-source", + "fedora-debuginfo", + "fedora-source", + "rawhide", + "rawhide-debuginfo", + "rawhide-source", + "updates-debuginfo", + "updates-source", + "updates-testing", + "updates-testing-debuginfo", + "updates-testing-source", + ], + "enabled": [ + "fedora", + "fedora-cisco-openh264", + "google-chrome", + "updates", + ], +} + +call_get_dnf_version = call(["/usr/bin/dnf", "--version"], check_rc=True) +call_get_repo_states_dnf4 = call(["/usr/bin/dnf", "repolist", "--all", "--verbose"], check_rc=True) +call_get_repo_states_dnf5 = call(["/usr/bin/dnf", "repo", "info", "--all"], check_rc=True) +call_disable_dnf4 = call(["/usr/bin/dnf", "config-manager", "--assumeyes", "--set-disabled", "crb"], check_rc=True) +call_enable_repo_dnf4 = call(["/usr/bin/dnf", "config-manager", "--assumeyes", "--set-enabled", "crb"], check_rc=True) +call_disable_dnf5 = call(["/usr/bin/dnf", "config-manager", "setopt", "copr:copr.fedorainfracloud.org:phracek:PyCharm.enabled=0"], check_rc=True) +call_enable_repo_dnf5 = call(["/usr/bin/dnf", "config-manager", "setopt", "copr:copr.fedorainfracloud.org:phracek:PyCharm.enabled=1"], check_rc=True) class TestDNFConfigManager(ModuleTestCase): @@ -57,8 +109,12 @@ class TestDNFConfigManager(ModuleTestCase): self.path_exists = self.mock_path_exists.start() self.path_exists.return_value = True self.module = dnf_config_manager_module - self.mock_dnf4_repolist_crb_enabled = fixture("mock_dnf4_repolist_crb_enabled.txt") - self.mock_dnf4_repolist_crb_disabled = fixture("mock_dnf4_repolist_crb_disabled.txt") + self.mock_dnf4_version = fixture("mock_dnf4_version.txt") + self.mock_dnf5_version = fixture("mock_dnf5_version.txt") + self.mock_dnf4_states_repo_enabled = fixture("mock_dnf4_states_repo_enabled.txt") + self.mock_dnf4_states_repo_disabled = fixture("mock_dnf4_states_repo_disabled.txt") + self.mock_dnf5_states_repo_enabled = fixture("mock_dnf5_states_repo_enabled.txt") + self.mock_dnf5_states_repo_disabled = fixture("mock_dnf5_states_repo_disabled.txt") self.mock_dnf4_repolist_no_status = fixture("mock_dnf4_repolist_no_status.txt") self.mock_dnf4_repolist_status_before_id = fixture("mock_dnf4_repolist_status_before_id.txt") @@ -98,81 +154,156 @@ class TestDNFConfigManager(ModuleTestCase): self.assertEqual(result["changed"], changed) return result - def test_get_repo_states(self): + def test_get_repo_states_dnf4(self): with set_module_args({}): - self.set_command_mock(execute_return=(0, self.mock_dnf4_repolist_crb_enabled, "")) + side_effects = [(0, self.mock_dnf4_version, ""), (0, self.mock_dnf4_states_repo_enabled, ""), + (0, self.mock_dnf4_states_repo_enabled, "")] + self.set_command_mock( + execute_side_effect=side_effects, + execute_return=(0, self.mock_dnf4_states_repo_enabled, "")) result = self.execute_module(changed=False) - self.assertEqual(result["repo_states_pre"], expected_repo_states_crb_enabled) - self.assertEqual(result["repo_states_post"], expected_repo_states_crb_enabled) + self.assertEqual(result["repo_states_pre"], expected_states_enabled_repo_dnf4) + self.assertEqual(result["repo_states_post"], expected_states_enabled_repo_dnf4) self.assertEqual(result["changed_repos"], []) - self.run_command.assert_has_calls(calls=[call_get_repo_states, call_get_repo_states], any_order=False) - def test_enable_disabled_repo(self): - with set_module_args({"name": ["crb"], "state": "enabled"}): - side_effects = [(0, self.mock_dnf4_repolist_crb_disabled, ""), (0, "", ""), (0, self.mock_dnf4_repolist_crb_enabled, "")] - self.set_command_mock(execute_side_effect=side_effects) - result = self.execute_module(changed=True) - self.assertEqual(result["repo_states_pre"], expected_repo_states_crb_disabled) - self.assertEqual(result["repo_states_post"], expected_repo_states_crb_enabled) - self.assertEqual(result["changed_repos"], ["crb"]) - expected_calls = [call_get_repo_states, call_enable_crb, call_get_repo_states] + expected_calls = [call_get_dnf_version, call_get_repo_states_dnf4, call_get_repo_states_dnf4] self.run_command.assert_has_calls(calls=expected_calls, any_order=False) - def test_enable_disabled_repo_check_mode(self): + def test_get_repo_states_dnf5(self): + with set_module_args({}): + side_effects = [(0, self.mock_dnf5_version, ""), (0, self.mock_dnf5_states_repo_enabled, ""), + (0, self.mock_dnf5_states_repo_enabled, "")] + self.set_command_mock( + execute_side_effect=side_effects, + execute_return=(0, self.mock_dnf5_states_repo_enabled, "")) + result = self.execute_module(changed=False) + self.assertEqual(result["repo_states_pre"], expected_states_enabled_repo_dnf5) + self.assertEqual(result["repo_states_post"], expected_states_enabled_repo_dnf5) + self.assertEqual(result["changed_repos"], []) + + expected_calls = [call_get_dnf_version, call_get_repo_states_dnf5, call_get_repo_states_dnf5] + self.run_command.assert_has_calls(calls=expected_calls, any_order=False) + + def test_enable_disabled_repo_dnf4(self): + with set_module_args({"name": ["crb"], "state": "enabled"}): + side_effects = [(0, self.mock_dnf4_version, ""), (0, self.mock_dnf4_states_repo_disabled, ""), + (0, "", ""), (0, self.mock_dnf4_states_repo_enabled, "")] + self.set_command_mock(execute_side_effect=side_effects) + result = self.execute_module(changed=True) + self.assertEqual(result["repo_states_pre"], expected_states_disabled_repo_dnf4) + self.assertEqual(result["repo_states_post"], expected_states_enabled_repo_dnf4) + self.assertEqual(result["changed_repos"], ["crb"]) + + expected_calls = [call_get_dnf_version, call_get_repo_states_dnf4, call_enable_repo_dnf4, call_get_repo_states_dnf4] + self.run_command.assert_has_calls(calls=expected_calls, any_order=False) + + def test_enable_disabled_repo_dnf5(self): + with set_module_args({"name": ["copr:copr.fedorainfracloud.org:phracek:PyCharm"], "state": "enabled"}): + side_effects = [(0, self.mock_dnf5_version, ""), (0, self.mock_dnf5_states_repo_disabled, ""), + (0, "", ""), (0, self.mock_dnf5_states_repo_enabled, "")] + self.set_command_mock(execute_side_effect=side_effects) + result = self.execute_module(changed=True) + self.assertEqual(result["repo_states_pre"], expected_states_disabled_repo_dnf5) + self.assertEqual(result["repo_states_post"], expected_states_enabled_repo_dnf5) + self.assertEqual(result["changed_repos"], ["copr:copr.fedorainfracloud.org:phracek:PyCharm"]) + + expected_calls = [call_get_dnf_version, call_get_repo_states_dnf5, call_enable_repo_dnf5, call_get_repo_states_dnf5] + self.run_command.assert_has_calls(calls=expected_calls, any_order=False) + + def test_enable_disabled_repo_check_mode_dnf4(self): with set_module_args({"name": ["crb"], "state": "enabled", "_ansible_check_mode": True}): - side_effects = [(0, self.mock_dnf4_repolist_crb_disabled, ""), (0, self.mock_dnf4_repolist_crb_disabled, "")] + side_effects = [(0, self.mock_dnf4_version, ""), (0, self.mock_dnf4_states_repo_disabled, ""), + (0, self.mock_dnf4_states_repo_disabled, "")] self.set_command_mock(execute_side_effect=side_effects) result = self.execute_module(changed=True) self.assertEqual(result["changed_repos"], ["crb"]) - self.run_command.assert_has_calls(calls=[call_get_repo_states], any_order=False) - def test_disable_enabled_repo(self): - with set_module_args({"name": ["crb"], "state": "disabled"}): - side_effects = [(0, self.mock_dnf4_repolist_crb_enabled, ""), (0, "", ""), (0, self.mock_dnf4_repolist_crb_disabled, "")] - self.set_command_mock(execute_side_effect=side_effects) - result = self.execute_module(changed=True) - self.assertEqual(result["repo_states_pre"], expected_repo_states_crb_enabled) - self.assertEqual(result["repo_states_post"], expected_repo_states_crb_disabled) - self.assertEqual(result["changed_repos"], ["crb"]) - expected_calls = [call_get_repo_states, call_disable_crb, call_get_repo_states] + expected_calls = [call_get_dnf_version, call_get_repo_states_dnf4] self.run_command.assert_has_calls(calls=expected_calls, any_order=False) - def test_crb_already_enabled(self): + def test_enable_disabled_repo_check_mode_dnf5(self): + with set_module_args({"name": ["copr:copr.fedorainfracloud.org:phracek:PyCharm"], "state": "enabled", "_ansible_check_mode": True}): + side_effects = [(0, self.mock_dnf5_version, ""), (0, self.mock_dnf5_states_repo_disabled, ""), (0, self.mock_dnf5_states_repo_enabled, "")] + self.set_command_mock(execute_side_effect=side_effects) + result = self.execute_module(changed=True) + self.assertEqual(result["changed_repos"], ["copr:copr.fedorainfracloud.org:phracek:PyCharm"]) + + expected_calls = [call_get_dnf_version, call_get_repo_states_dnf5] + self.run_command.assert_has_calls(calls=expected_calls, any_order=False) + + def test_disable_enabled_repo_dnf4(self): + with set_module_args({"name": ["crb"], "state": "disabled"}): + side_effects = [(0, self.mock_dnf4_version, ""), (0, self.mock_dnf4_states_repo_enabled, ""), + (0, "", ""), (0, self.mock_dnf4_states_repo_disabled, "")] + self.set_command_mock(execute_side_effect=side_effects) + result = self.execute_module(changed=True) + self.assertEqual(result["repo_states_pre"], expected_states_enabled_repo_dnf4) + self.assertEqual(result["repo_states_post"], expected_states_disabled_repo_dnf4) + self.assertEqual(result["changed_repos"], ["crb"]) + expected_calls = [call_get_dnf_version, call_get_repo_states_dnf4, call_disable_dnf4, call_get_repo_states_dnf4] + self.run_command.assert_has_calls(calls=expected_calls, any_order=False) + + def test_disable_enabled_repo_dnf5(self): + with set_module_args({"name": ["copr:copr.fedorainfracloud.org:phracek:PyCharm"], "state": "disabled"}): + side_effects = [(0, self.mock_dnf5_version, ""), (0, self.mock_dnf5_states_repo_enabled, ""), + (0, "", ""), (0, self.mock_dnf5_states_repo_disabled, "")] + self.set_command_mock(execute_side_effect=side_effects) + result = self.execute_module(changed=True) + self.assertEqual(result["repo_states_pre"], expected_states_enabled_repo_dnf5) + self.assertEqual(result["repo_states_post"], expected_states_disabled_repo_dnf5) + self.assertEqual(result["changed_repos"], ["copr:copr.fedorainfracloud.org:phracek:PyCharm"]) + expected_calls = [call_get_dnf_version, call_get_repo_states_dnf5, call_disable_dnf5, call_get_repo_states_dnf5] + self.run_command.assert_has_calls(calls=expected_calls, any_order=False) + + def test_crb_already_enabled_dnf4(self): with set_module_args({"name": ["crb"], "state": "enabled"}): - side_effects = [(0, self.mock_dnf4_repolist_crb_enabled, ""), (0, self.mock_dnf4_repolist_crb_enabled, "")] + side_effects = [(0, self.mock_dnf4_version, ""), (0, self.mock_dnf4_states_repo_enabled, ""), + (0, self.mock_dnf4_states_repo_enabled, "")] self.set_command_mock(execute_side_effect=side_effects) result = self.execute_module(changed=False) - self.assertEqual(result["repo_states_pre"], expected_repo_states_crb_enabled) - self.assertEqual(result["repo_states_post"], expected_repo_states_crb_enabled) + self.assertEqual(result["repo_states_pre"], expected_states_enabled_repo_dnf4) + self.assertEqual(result["repo_states_post"], expected_states_enabled_repo_dnf4) self.assertEqual(result["changed_repos"], []) - self.run_command.assert_has_calls(calls=[call_get_repo_states, call_get_repo_states], any_order=False) + self.run_command.assert_has_calls(calls=[call_get_dnf_version, call_get_repo_states_dnf4, call_get_repo_states_dnf4], any_order=False) + + def test_crb_already_enabled_dnf5(self): + with set_module_args({"name": ["copr:copr.fedorainfracloud.org:phracek:PyCharm"], "state": "enabled"}): + side_effects = [(0, self.mock_dnf5_version, ""), (1, self.mock_dnf5_states_repo_enabled, ""), + (0, self.mock_dnf5_states_repo_enabled, "")] + self.set_command_mock(execute_side_effect=side_effects) + result = self.execute_module(changed=False) + self.assertEqual(result["repo_states_pre"], expected_states_enabled_repo_dnf5) + self.assertEqual(result["repo_states_post"], expected_states_enabled_repo_dnf5) + self.assertEqual(result["changed_repos"], []) + self.run_command.assert_has_calls(calls=[call_get_dnf_version, call_get_repo_states_dnf5, call_get_repo_states_dnf5], any_order=False) def test_get_repo_states_fail_no_status(self): with set_module_args({}): self.set_command_mock(execute_return=(0, self.mock_dnf4_repolist_no_status, "")) result = self.execute_module(failed=True) self.assertEqual(result["msg"], "dnf repolist parse failure: parsed another repo id before next status") - self.run_command.assert_has_calls(calls=[call_get_repo_states], any_order=False) + self.run_command.assert_has_calls(calls=[call_get_dnf_version, call_get_repo_states_dnf4], any_order=False) def test_get_repo_states_fail_status_before_id(self): with set_module_args({}): self.set_command_mock(execute_return=(0, self.mock_dnf4_repolist_status_before_id, "")) result = self.execute_module(failed=True) self.assertEqual(result["msg"], "dnf repolist parse failure: parsed status before repo id") - self.run_command.assert_has_calls(calls=[call_get_repo_states], any_order=False) + self.run_command.assert_has_calls(calls=[call_get_dnf_version, call_get_repo_states_dnf4], any_order=False) def test_failed__unknown_repo_id(self): with set_module_args({"name": ["fake"]}): - self.set_command_mock(execute_return=(0, self.mock_dnf4_repolist_crb_disabled, "")) + self.set_command_mock(execute_return=(0, self.mock_dnf4_states_repo_disabled, "")) result = self.execute_module(failed=True) self.assertEqual(result["msg"], "did not find repo with ID 'fake' in dnf repolist --all --verbose") - self.run_command.assert_has_calls(calls=[call_get_repo_states], any_order=False) + self.run_command.assert_has_calls(calls=[call_get_dnf_version, call_get_repo_states_dnf4], any_order=False) def test_failed_state_change_ineffective(self): with set_module_args({"name": ["crb"], "state": "enabled"}): - side_effects = [(0, self.mock_dnf4_repolist_crb_disabled, ""), (0, "", ""), (0, self.mock_dnf4_repolist_crb_disabled, "")] + side_effects = [(0, self.mock_dnf4_version, ""), (0, self.mock_dnf4_states_repo_disabled, ""), + (0, "", ""), (0, self.mock_dnf4_states_repo_disabled, "")] self.set_command_mock(execute_side_effect=side_effects) result = self.execute_module(failed=True) self.assertEqual(result["msg"], "dnf config-manager failed to make 'crb' enabled") - expected_calls = [call_get_repo_states, call_enable_crb, call_get_repo_states] + expected_calls = [call_get_dnf_version, call_get_repo_states_dnf4, call_enable_repo_dnf4, call_get_repo_states_dnf4] self.run_command.assert_has_calls(calls=expected_calls, any_order=False)