feat: add abillity to check broken dependencies (#122)

* implement elf dynamic linking check

* load local database too in pacman wrapper
This commit is contained in:
2024-02-13 11:35:38 +02:00
parent 7bbe3242d4
commit 50a045434d
62 changed files with 2589 additions and 208 deletions

View File

@ -1,13 +1,14 @@
import pytest
import tarfile
from pathlib import Path
from pyalpm import error as PyalpmError
from pytest_mock import MockerFixture
from tempfile import TemporaryDirectory
from unittest.mock import MagicMock
from unittest.mock import MagicMock, call as MockCall
from ahriman.core.alpm.pacman import Pacman
from ahriman.core.configuration import Configuration
from ahriman.models.package import Package
from ahriman.models.pacman_synchronization import PacmanSynchronization
from ahriman.models.repository_paths import RepositoryPaths
@ -48,7 +49,7 @@ def test_init_with_local_cache_forced(configuration: Configuration, mocker: Mock
sync_mock.assert_called_once_with(pytest.helpers.anyvar(int), force=True)
def test_database_copy(pacman: Pacman, repository_paths: RepositoryPaths, mocker: MockerFixture) -> None:
def test_database_copy(pacman: Pacman, mocker: MockerFixture) -> None:
"""
must copy database from root
"""
@ -62,13 +63,13 @@ def test_database_copy(pacman: Pacman, repository_paths: RepositoryPaths, mocker
copy_mock = mocker.patch("shutil.copy")
chown_mock = mocker.patch("ahriman.models.repository_paths.RepositoryPaths.chown")
pacman.database_copy(pacman.handle, database, path, repository_paths, use_ahriman_cache=True)
pacman.database_copy(pacman.handle, database, path, use_ahriman_cache=True)
mkdir_mock.assert_called_once_with(mode=0o755, exist_ok=True)
copy_mock.assert_called_once_with(path / "sync" / "core.db", dst_path)
chown_mock.assert_called_once_with(dst_path)
def test_database_copy_skip(pacman: Pacman, repository_paths: RepositoryPaths, mocker: MockerFixture) -> None:
def test_database_copy_skip(pacman: Pacman, mocker: MockerFixture) -> None:
"""
must do not copy database from root if local cache is disabled
"""
@ -79,11 +80,11 @@ def test_database_copy_skip(pacman: Pacman, repository_paths: RepositoryPaths, m
mocker.patch("pathlib.Path.is_file", autospec=True, side_effect=lambda p: p.is_relative_to(path))
copy_mock = mocker.patch("shutil.copy")
pacman.database_copy(pacman.handle, database, path, repository_paths, use_ahriman_cache=False)
pacman.database_copy(pacman.handle, database, path, use_ahriman_cache=False)
copy_mock.assert_not_called()
def test_database_copy_no_directory(pacman: Pacman, repository_paths: RepositoryPaths, mocker: MockerFixture) -> None:
def test_database_copy_no_directory(pacman: Pacman, mocker: MockerFixture) -> None:
"""
must do not copy database if local cache already exists
"""
@ -94,11 +95,11 @@ def test_database_copy_no_directory(pacman: Pacman, repository_paths: Repository
mocker.patch("pathlib.Path.is_file", autospec=True, side_effect=lambda p: p.is_relative_to(path))
copy_mock = mocker.patch("shutil.copy")
pacman.database_copy(pacman.handle, database, path, repository_paths, use_ahriman_cache=True)
pacman.database_copy(pacman.handle, database, path, use_ahriman_cache=True)
copy_mock.assert_not_called()
def test_database_copy_no_root_file(pacman: Pacman, repository_paths: RepositoryPaths, mocker: MockerFixture) -> None:
def test_database_copy_no_root_file(pacman: Pacman, mocker: MockerFixture) -> None:
"""
must do not copy database if no repository file exists in filesystem
"""
@ -109,11 +110,11 @@ def test_database_copy_no_root_file(pacman: Pacman, repository_paths: Repository
mocker.patch("pathlib.Path.is_file", return_value=False)
copy_mock = mocker.patch("shutil.copy")
pacman.database_copy(pacman.handle, database, path, repository_paths, use_ahriman_cache=True)
pacman.database_copy(pacman.handle, database, path, use_ahriman_cache=True)
copy_mock.assert_not_called()
def test_database_copy_database_exist(pacman: Pacman, repository_paths: RepositoryPaths, mocker: MockerFixture) -> None:
def test_database_copy_database_exist(pacman: Pacman, mocker: MockerFixture) -> None:
"""
must do not copy database if local cache already exists
"""
@ -123,7 +124,7 @@ def test_database_copy_database_exist(pacman: Pacman, repository_paths: Reposito
mocker.patch("pathlib.Path.is_file", return_value=True)
copy_mock = mocker.patch("shutil.copy")
pacman.database_copy(pacman.handle, database, Path("root"), repository_paths, use_ahriman_cache=True)
pacman.database_copy(pacman.handle, database, Path("root"), use_ahriman_cache=True)
copy_mock.assert_not_called()
@ -131,71 +132,133 @@ def test_database_init(pacman: Pacman, configuration: Configuration) -> None:
"""
must init database with settings
"""
mirror = configuration.get("alpm", "mirror")
database = pacman.database_init(pacman.handle, "testing", mirror, "x86_64")
database = pacman.database_init(pacman.handle, "testing", "x86_64")
assert database.servers == ["https://geo.mirror.pkgbuild.com/testing/os/x86_64"]
def test_database_sync(pacman: Pacman) -> None:
def test_database_init_local(pacman: Pacman, configuration: Configuration) -> None:
"""
must set file protocol for local databases
"""
_, repository_id = configuration.check_loaded()
database = pacman.database_init(MagicMock(), repository_id.name, repository_id.architecture)
assert database.servers == [f"file://{configuration.repository_paths.repository}"]
def test_database_sync(pacman: Pacman, mocker: MockerFixture) -> None:
"""
must sync databases
"""
handle_mock = MagicMock()
core_mock = MagicMock()
extra_mock = MagicMock()
transaction_mock = MagicMock()
handle_mock.get_syncdbs.return_value = [core_mock, extra_mock]
handle_mock.get_syncdbs.return_value = [1, 2]
handle_mock.init_transaction.return_value = transaction_mock
pacman.handle = handle_mock
pacman.database_sync(pacman.handle, force=False)
sync_mock = mocker.patch("ahriman.core.alpm.pacman_database.PacmanDatabase.sync")
pacman.database_sync(handle_mock, force=False)
handle_mock.init_transaction.assert_called_once_with()
core_mock.update.assert_called_once_with(False)
extra_mock.update.assert_called_once_with(False)
sync_mock.assert_has_calls([MockCall(force=False), MockCall(force=False)])
transaction_mock.release.assert_called_once_with()
def test_database_sync_failed(pacman: Pacman) -> None:
"""
must sync databases even if there was exception
"""
handle_mock = MagicMock()
core_mock = MagicMock()
core_mock.update.side_effect = PyalpmError()
extra_mock = MagicMock()
handle_mock.get_syncdbs.return_value = [core_mock, extra_mock]
pacman.handle = handle_mock
pacman.database_sync(pacman.handle, force=False)
extra_mock.update.assert_called_once_with(False)
def test_database_sync_forced(pacman: Pacman) -> None:
def test_database_sync_forced(pacman: Pacman, mocker: MockerFixture) -> None:
"""
must sync databases with force flag
"""
handle_mock = MagicMock()
core_mock = MagicMock()
handle_mock.get_syncdbs.return_value = [core_mock]
handle_mock.get_syncdbs.return_value = [1]
sync_mock = mocker.patch("ahriman.core.alpm.pacman_database.PacmanDatabase.sync")
pacman.database_sync(handle_mock, force=True)
sync_mock.assert_called_once_with(force=True)
def test_files(pacman: Pacman, package_ahriman: Package, mocker: MockerFixture, resource_path_root: Path) -> None:
"""
must load files from databases
"""
handle_mock = MagicMock()
handle_mock.get_syncdbs.return_value = [MagicMock()]
pacman.handle = handle_mock
tarball = resource_path_root / "core" / "arcanisrepo.files.tar.gz"
mocker.patch("pathlib.Path.is_file", return_value=True)
open_mock = mocker.patch("ahriman.core.alpm.pacman.tarfile.open", return_value=tarfile.open(tarball, "r:gz"))
files = pacman.files()
assert len(files) == 2
assert package_ahriman.base in files
assert Path("usr/bin/ahriman") in files[package_ahriman.base]
open_mock.assert_called_once_with(pytest.helpers.anyvar(int), "r:gz")
def test_files_package(pacman: Pacman, package_ahriman: Package, mocker: MockerFixture,
resource_path_root: Path) -> None:
"""
must load files only for the specified package
"""
handle_mock = MagicMock()
handle_mock.get_syncdbs.return_value = [MagicMock()]
pacman.handle = handle_mock
pacman.database_sync(pacman.handle, force=True)
handle_mock.init_transaction.assert_called_once_with()
core_mock.update.assert_called_once_with(True)
tarball = resource_path_root / "core" / "arcanisrepo.files.tar.gz"
mocker.patch("pathlib.Path.is_file", return_value=True)
mocker.patch("ahriman.core.alpm.pacman.tarfile.open", return_value=tarfile.open(tarball, "r:gz"))
files = pacman.files(package_ahriman.base)
assert len(files) == 1
assert package_ahriman.base in files
def test_package_get(pacman: Pacman) -> None:
def test_files_skip(pacman: Pacman, mocker: MockerFixture) -> None:
"""
must return empty list if no database found
"""
handle_mock = MagicMock()
handle_mock.get_syncdbs.return_value = [MagicMock()]
pacman.handle = handle_mock
mocker.patch("pathlib.Path.is_file", return_value=False)
assert not pacman.files()
def test_files_no_content(pacman: Pacman, mocker: MockerFixture) -> None:
"""
must skip package if no content can be loaded
"""
handle_mock = MagicMock()
handle_mock.get_syncdbs.return_value = [MagicMock()]
pacman.handle = handle_mock
tar_mock = MagicMock()
tar_mock.getmembers.return_value = [MagicMock()]
tar_mock.extractfile.return_value = None
open_mock = MagicMock()
open_mock.__enter__.return_value = tar_mock
mocker.patch("pathlib.Path.is_file", return_value=True)
mocker.patch("ahriman.core.alpm.pacman.tarfile.open", return_value=open_mock)
assert not pacman.files()
def test_package(pacman: Pacman) -> None:
"""
must retrieve package
"""
assert list(pacman.package_get("pacman"))
assert list(pacman.package("pacman"))
def test_package_get_empty(pacman: Pacman) -> None:
def test_package_empty(pacman: Pacman) -> None:
"""
must return empty packages list without exception
"""
assert not list(pacman.package_get("some-random-name"))
assert not list(pacman.package("some-random-name"))
def test_packages(pacman: Pacman) -> None: