Source code for tests.test_manager_microdnf

# Copyright Kevin Deldycke <[email protected]> and contributors.
#
# This program is Free Software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
"""microdnf parser tests.

Every payload below is captured output, from AlmaLinux 10.2 aarch64 running
microdnf `3.10.1` and libdnf `0.73.1`. Trailing spaces are kept: the transaction
table pads its heading and `replacing` rows to the width of the table.
"""

from __future__ import annotations

import pytest

from meta_package_manager.managers.microdnf import MicroDNF
from meta_package_manager.version import VersionRange

UPGRADE_PREVIEW = (
    "Package                                             Repository     Size\n"
    "Installing:                                                            \n"
    " kernel-6.12.0-211.53.1.el10_2.aarch64              baseos       1.7 MB\n"
    " kernel-core-6.12.0-211.53.1.el10_2.aarch64         baseos      19.9 MB\n"
    " kernel-modules-6.12.0-211.53.1.el10_2.aarch64      baseos      28.3 MB\n"
    " kernel-modules-core-6.12.0-211.53.1.el10_2.aarch64 baseos      26.6 MB\n"
    "Upgrading:                                                             \n"
    " rsync-3.5.0-3.el10_2.aarch64                       baseos     474.3 kB\n"
    "  replacing rsync-3.4.4-1.el10_2.aarch64                               \n"
    " tar-2:1.35-13.el10_2.aarch64                       baseos     884.5 kB\n"
    "   replacing tar-2:1.35-11.el10.aarch64                                \n"
    "Transaction Summary:\n"
    " Installing:        4 packages\n"
    " Reinstalling:      0 packages\n"
    " Upgrading:         2 packages\n"
    " Obsoleting:        0 packages\n"
    " Removing:          0 packages\n"
    " Downgrading:       0 packages\n"
    "Operation aborted.\n"
)
"""`microdnf --assumeno upgrade`, with two upgrades and one new kernel.

The kernel packages are install-only: RPM installs their new version beside the
old one, so they sit under `Installing:` with no `replacing` row.
"""

INSTALLED_KERNELS = (
    "kernel-6.12.0-211.47.1.el10_2.aarch64\n"
    "kernel-core-6.12.0-211.47.1.el10_2.aarch64\n"
    "kernel-modules-6.12.0-211.47.1.el10_2.aarch64\n"
    "kernel-modules-core-6.12.0-211.47.1.el10_2.aarch64\n"
)
"""The installed kernels, read by the second query of `outdated`."""

INSTALL_PREVIEW = (
    "Package                    Repository    Size\n"
    "Installing:                                  \n"
    " tree-2.1.0-8.el10.aarch64 baseos     56.6 kB\n"
    "Transaction Summary:\n"
    " Installing:        1 packages\n"
    " Reinstalling:      0 packages\n"
    " Upgrading:         0 packages\n"
    " Obsoleting:        0 packages\n"
    " Removing:          0 packages\n"
    " Downgrading:       0 packages\n"
    "Operation aborted.\n"
)
"""`microdnf --assumeno install tree`, on a host where `tree` is not installed.

The same table as an upgrade preview, with an `Installing:` row for a package
the host does not have: the shape of a new dependency an upgrade pulls in.
"""

SEARCH_BUILDS = (
    "xxd-2:9.1.083-9.el10_2.2.aarch64\n"
    "xxd-2:9.1.083-9.el10_2.3.aarch64\n"
    "xxd-2:9.1.083-9.el10_2.4.aarch64\n"
    "xxd-2:9.1.083-9.el10_2.7.aarch64\n"
    "xxd-2:9.1.083-9.el10_2.12.aarch64\n"
    "xxd-2:9.1.083-9.el10_2.20.aarch64\n"
)
"""`microdnf repoquery *xxd*`: the repository keeps six builds of one package."""

SEARCH_COLD_CACHE = (
    "Downloading metadata...\n"
    "Downloading metadata...\n"
    "Downloading metadata...\n"
    "Downloading metadata...\n"
    "bash-5.2.26-6.el10.aarch64\n"
)
"""`microdnf repoquery bash` on a cold cache, which reports each download too."""


[docs] @pytest.mark.parametrize( ("output", "expected"), ( pytest.param("3.10.1", "3.10.1", id="package-installed"), # The answer on a host with no `microdnf` package, like a Fedora where # the name is a symlink to dnf5. pytest.param("package microdnf is not installed\n", None, id="no-package"), ), ) def test_version_probe(output, expected): """Only a bare version is read, and it clears the requirement.""" version = MicroDNF()._parse_version(output) if expected is None: assert version is None return assert version is not None assert str(version) == expected assert version in VersionRange(MicroDNF.requirement)
[docs] def replay( monkeypatch, responses: dict[tuple[str, ...], str] ) -> tuple[MicroDNF, list[tuple]]: """Answer each call with the output of the first matching argument prefix.""" manager = MicroDNF() calls: list[tuple] = [] def fake_run_cli(*args, **kwargs) -> str: calls.append(args) for prefix, output in responses.items(): if args[: len(prefix)] == prefix: return output raise AssertionError(f"Unexpected call: {args}") monkeypatch.setattr(manager, "run_cli", fake_run_cli) return manager, calls
[docs] def test_outdated_reads_the_upgrade_preview(monkeypatch): """Upgrades pair with their `replacing` row, kernels with the installed list.""" manager, calls = replay( monkeypatch, { ("--assumeno", "upgrade"): UPGRADE_PREVIEW, ("repoquery", "--installed"): INSTALLED_KERNELS, }, ) found = [ (package.id, str(package.installed_version), str(package.latest_version)) for package in manager.outdated ] assert found == [ ("kernel", "6.12.0-211.47.1.el10_2", "6.12.0-211.53.1.el10_2"), ("kernel-core", "6.12.0-211.47.1.el10_2", "6.12.0-211.53.1.el10_2"), ("kernel-modules", "6.12.0-211.47.1.el10_2", "6.12.0-211.53.1.el10_2"), ("kernel-modules-core", "6.12.0-211.47.1.el10_2", "6.12.0-211.53.1.el10_2"), ("rsync", "3.4.4-1.el10_2", "3.5.0-3.el10_2"), # The epoch stays in both versions. ("tar", "2:1.35-11.el10", "2:1.35-13.el10_2"), ] # The rows paired by a `replacing` row are not queried again. assert calls == [ ("--assumeno", "upgrade"), ( "repoquery", "--installed", "kernel", "kernel-core", "kernel-modules", "kernel-modules-core", ), ]
[docs] def test_outdated_skips_packages_not_installed(monkeypatch): """An `Installing:` row is only an update when its package is installed.""" manager, calls = replay( monkeypatch, { ("--assumeno", "upgrade"): INSTALL_PREVIEW, ("repoquery", "--installed"): "", }, ) assert list(manager.outdated) == [] assert calls[-1] == ("repoquery", "--installed", "tree")
[docs] def test_outdated_with_nothing_to_do(monkeypatch): """An up-to-date host prints one line and needs no second query.""" manager, calls = replay( monkeypatch, {("--assumeno", "upgrade"): "Nothing to do.\n"} ) assert list(manager.outdated) == [] assert calls == [("--assumeno", "upgrade")]