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

@ -0,0 +1,21 @@
import pytest
from ahriman.core.alpm.pacman_database import PacmanDatabase
from ahriman.core.alpm.pacman import Pacman
from ahriman.core.configuration import Configuration
@pytest.fixture
def pacman_database(configuration: Configuration, pacman: Pacman) -> PacmanDatabase:
"""
database sync fixture
Args:
configuration(Configuration): configuration test instance
pacman(Pacman): pacman test instance
Returns:
DatabaseSync: database sync test instance
"""
database = next(iter(pacman.handle.get_syncdbs()))
return PacmanDatabase(database, configuration)

View File

@ -14,7 +14,7 @@ def test_package_info(official_syncdb: OfficialSyncdb, aur_package_akonadi: AURP
must return package info from the database
"""
mocker.patch("ahriman.models.aur_package.AURPackage.from_pacman", return_value=aur_package_akonadi)
get_mock = mocker.patch("ahriman.core.alpm.pacman.Pacman.package_get", return_value=[aur_package_akonadi])
get_mock = mocker.patch("ahriman.core.alpm.pacman.Pacman.package", return_value=[aur_package_akonadi])
package = official_syncdb.package_info(aur_package_akonadi.name, pacman=pacman)
get_mock.assert_called_once_with(aur_package_akonadi.name)
@ -26,7 +26,7 @@ def test_package_info_no_pacman(official_syncdb: OfficialSyncdb, aur_package_ako
"""
must raise UnknownPackageError if no pacman set
"""
mocker.patch("ahriman.core.alpm.pacman.Pacman.package_get", return_value=[aur_package_akonadi])
mocker.patch("ahriman.core.alpm.pacman.Pacman.package", return_value=[aur_package_akonadi])
with pytest.raises(UnknownPackageError, match=aur_package_akonadi.name):
official_syncdb.package_info(aur_package_akonadi.name, pacman=None)
@ -37,6 +37,6 @@ def test_package_info_not_found(official_syncdb: OfficialSyncdb, aur_package_ako
"""
must raise UnknownPackage exception in case if no package was found
"""
mocker.patch("ahriman.core.alpm.pacman.Pacman.package_get", return_value=[])
mocker.patch("ahriman.core.alpm.pacman.Pacman.package", return_value=[])
with pytest.raises(UnknownPackageError, match=aur_package_akonadi.name):
assert official_syncdb.package_info(aur_package_akonadi.name, pacman=pacman)

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:

View File

@ -0,0 +1,203 @@
import pytest
from pathlib import Path
from pytest_mock import MockerFixture
from unittest.mock import MagicMock, call as MockCall
from ahriman.core.alpm.pacman_database import PacmanDatabase
from ahriman.core.exceptions import PacmanError
def test_copy(pacman_database: PacmanDatabase, mocker: MockerFixture) -> None:
"""
must copy loca database file
"""
copy_mock = mocker.patch("shutil.copy")
pacman_database.copy(Path("remote"), Path("local"))
copy_mock.assert_called_once_with(Path("remote"), Path("local"))
def test_download(pacman_database: PacmanDatabase, mocker: MockerFixture) -> None:
"""
must download database by remote url
"""
response_obj = MagicMock()
response_obj.headers = {pacman_database.LAST_MODIFIED_HEADER: "Fri, 09 Feb 2024 00:25:55 GMT"}
response_obj.iter_content.return_value = ["chunk".encode("utf8")]
path = Path("local")
url = "url"
file_mock = MagicMock()
request_mock = mocker.patch("ahriman.core.alpm.pacman_database.PacmanDatabase.make_request",
return_value=response_obj)
open_mock = mocker.patch("pathlib.Path.open")
open_mock.return_value.__enter__.return_value = file_mock
mtime_mock = mocker.patch("os.utime")
pacman_database.download(url, path)
request_mock.assert_called_once_with("GET", url, stream=True)
open_mock.assert_called_once_with("wb")
file_mock.write.assert_called_once_with("chunk".encode("utf8"))
mtime_mock.assert_called_once_with(path, (1707438355.0, 1707438355.0))
def test_download_no_header(pacman_database: PacmanDatabase, mocker: MockerFixture) -> None:
"""
must raise exception in case if no last modified head found
"""
response_obj = MagicMock()
response_obj.headers = {}
mocker.patch("ahriman.core.alpm.pacman_database.PacmanDatabase.make_request", return_value=response_obj)
with pytest.raises(PacmanError):
pacman_database.download("url", Path("local"))
def test_is_outdated(pacman_database: PacmanDatabase, mocker: MockerFixture) -> None:
"""
must correctly check if file is outdated
"""
response_obj = MagicMock()
response_obj.headers = {pacman_database.LAST_MODIFIED_HEADER: "Fri, 09 Feb 2024 00:25:55 GMT"}
stat_mock = MagicMock()
stat_mock.st_mtime = 1707438354
path = Path("local")
url = "url"
mocker.patch("pathlib.Path.is_file", return_value=True)
mocker.patch("ahriman.core.alpm.pacman_database.PacmanDatabase.make_request", return_value=response_obj)
mocker.patch("pathlib.Path.stat", return_value=stat_mock)
assert pacman_database.is_outdated(url, path)
stat_mock.st_mtime += 1
assert not pacman_database.is_outdated(url, path)
stat_mock.st_mtime += 1
assert not pacman_database.is_outdated(url, path)
def test_is_outdated_not_a_file(pacman_database: PacmanDatabase, mocker: MockerFixture) -> None:
"""
must mark as outdated if no file was found
"""
mocker.patch("pathlib.Path.is_file", return_value=False)
assert pacman_database.is_outdated("url", Path("local"))
def test_is_outdated_no_header(pacman_database: PacmanDatabase, mocker: MockerFixture) -> None:
"""
must raise exception in case if no last modified head found during timestamp check
"""
response_obj = MagicMock()
response_obj.headers = {}
mocker.patch("ahriman.core.alpm.pacman_database.PacmanDatabase.make_request", return_value=response_obj)
mocker.patch("pathlib.Path.is_file", return_value=True)
with pytest.raises(PacmanError):
pacman_database.is_outdated("url", Path("local"))
def test_sync(pacman_database: PacmanDatabase, mocker: MockerFixture) -> None:
"""
must sync database
"""
sync_db_mock = mocker.patch("ahriman.core.alpm.pacman_database.PacmanDatabase.sync_packages")
sync_files_mock = mocker.patch("ahriman.core.alpm.pacman_database.PacmanDatabase.sync_files")
pacman_database.sync(force=True)
pacman_database.sync(force=False)
sync_db_mock.assert_has_calls([MockCall(force=True), MockCall(force=False)])
sync_files_mock.assert_not_called()
def test_sync_with_files(pacman_database: PacmanDatabase, mocker: MockerFixture) -> None:
"""
must sync database and files
"""
sync_db_mock = mocker.patch("ahriman.core.alpm.pacman_database.PacmanDatabase.sync_packages")
sync_files_mock = mocker.patch("ahriman.core.alpm.pacman_database.PacmanDatabase.sync_files")
pacman_database.sync_files_database = True
pacman_database.sync(force=True)
pacman_database.sync(force=False)
sync_db_mock.assert_has_calls([MockCall(force=True), MockCall(force=False)])
sync_files_mock.assert_has_calls([MockCall(force=True), MockCall(force=False)])
def test_sync_exception(pacman_database: PacmanDatabase, mocker: MockerFixture) -> None:
"""
must suppress all exceptions on failure
"""
mocker.patch("ahriman.core.alpm.pacman_database.PacmanDatabase.sync_packages", side_effect=Exception())
pacman_database.sync(force=True)
def test_sync_files(pacman_database: PacmanDatabase, mocker: MockerFixture) -> None:
"""
must sync files database
"""
outdated_mock = mocker.patch("ahriman.core.alpm.pacman_database.PacmanDatabase.is_outdated", return_value=True)
download_mock = mocker.patch("ahriman.core.alpm.pacman_database.PacmanDatabase.download")
pacman_database.sync_files(force=False)
outdated_mock.assert_called_once_with(
"https://geo.mirror.pkgbuild.com/core/os/x86_64/core.files.tar.gz", pytest.helpers.anyvar(int))
download_mock.assert_called_once_with(
"https://geo.mirror.pkgbuild.com/core/os/x86_64/core.files.tar.gz", pytest.helpers.anyvar(int))
def test_sync_files_not_outdated(pacman_database: PacmanDatabase, mocker: MockerFixture) -> None:
"""
must skip files sync if up-to-date
"""
mocker.patch("ahriman.core.alpm.pacman_database.PacmanDatabase.is_outdated", return_value=False)
download_mock = mocker.patch("ahriman.core.alpm.pacman_database.PacmanDatabase.download")
pacman_database.sync_files(force=False)
download_mock.assert_not_called()
def test_sync_files_force(pacman_database: PacmanDatabase, mocker: MockerFixture) -> None:
"""
must sync up-to-date files if force flag is set
"""
mocker.patch("ahriman.core.alpm.pacman_database.PacmanDatabase.is_outdated", return_value=False)
download_mock = mocker.patch("ahriman.core.alpm.pacman_database.PacmanDatabase.download")
pacman_database.sync_files(force=True)
download_mock.assert_called_once_with(
"https://geo.mirror.pkgbuild.com/core/os/x86_64/core.files.tar.gz", pytest.helpers.anyvar(int))
def test_sync_files_local(pacman_database: PacmanDatabase, mocker: MockerFixture) -> None:
"""
must copy local files instead of downloading them
"""
pacman_database.database.servers = ["file:///var"]
copy_mock = mocker.patch("ahriman.core.alpm.pacman_database.PacmanDatabase.copy")
pacman_database.sync_files(force=False)
copy_mock.assert_called_once_with(Path("/var/core.files.tar.gz"), pytest.helpers.anyvar(int))
def test_sync_files_unknown_source(pacman_database: PacmanDatabase) -> None:
"""
must raise an exception in case if server scheme is unsupported
"""
pacman_database.database.servers = ["some random string"]
with pytest.raises(PacmanError):
pacman_database.sync_files(force=False)
def test_sync_packages(pacman_database: PacmanDatabase) -> None:
"""
must sync packages by using pyalpm method
"""
pacman_database.database = MagicMock()
pacman_database.sync_packages(force=True)
pacman_database.sync_packages(force=False)
pacman_database.database.update.assert_has_calls([MockCall(True), MockCall(False)])

View File

@ -0,0 +1,8 @@
from ahriman.core.database.migrations.m013_dependencies import steps
def test_migration_dependencies() -> None:
"""
migration must not be empty
"""
assert steps

View File

@ -0,0 +1,61 @@
from pathlib import Path
from ahriman.core.database import SQLite
from ahriman.models.dependencies import Dependencies
from ahriman.models.package import Package
from ahriman.models.repository_id import RepositoryId
def test_dependencies_insert_get(database: SQLite, package_ahriman: Package) -> None:
"""
must insert and get dependencies
"""
dependencies = Dependencies(package_ahriman.base, {Path("usr/lib/python3.11/site-packages"): ["python"]})
database.dependencies_insert(dependencies)
assert database.dependencies_get(package_ahriman.base) == [dependencies]
dependencies2 = Dependencies(package_ahriman.base, {Path("usr/lib/python3.11/site-packages"): ["python3"]})
database.dependencies_insert(dependencies2, RepositoryId("i686", database._repository_id.name))
assert database.dependencies_get() == [dependencies]
assert database.dependencies_get(package_ahriman.base) == [dependencies]
assert database.dependencies_get(
package_ahriman.base, RepositoryId("i686", database._repository_id.name)) == [dependencies2]
def test_dependencies_insert_remove(database: SQLite, package_ahriman: Package,
package_python_schedule: Package) -> None:
"""
must remove dependencies for the package
"""
dependencies1 = Dependencies(package_ahriman.base, {Path("usr"): ["python"]})
database.dependencies_insert(dependencies1)
dependencies2 = Dependencies(package_python_schedule.base, {Path("usr"): ["filesystem"]})
database.dependencies_insert(dependencies2)
dependencies3 = Dependencies(package_ahriman.base, {Path("usr"): ["python3"]})
database.dependencies_insert(dependencies3, RepositoryId("i686", database._repository_id.name))
assert database.dependencies_get() == [dependencies1, dependencies2]
database.dependencies_remove(package_ahriman.base)
assert database.dependencies_get(package_ahriman.base) == []
assert database.dependencies_get(package_python_schedule.base) == [dependencies2]
# insert null
database.dependencies_remove(package_ahriman.base, RepositoryId("i686", database._repository_id.name))
assert database.dependencies_get(package_ahriman.base, RepositoryId("i686", database._repository_id.name)) == []
assert database.dependencies_get(package_python_schedule.base) == [dependencies2]
def test_dependencies_insert_remove_full(database: SQLite, package_ahriman: Package,
package_python_schedule: Package) -> None:
"""
must remove all dependencies for the repository
"""
database.dependencies_insert(Dependencies(package_ahriman.base, {Path("usr"): ["python"]}))
database.dependencies_insert(Dependencies(package_python_schedule.base, {Path("usr"): ["filesystem"]}))
database.dependencies_insert(Dependencies(package_ahriman.base, {Path("usr"): ["python3"]}),
RepositoryId("i686", database._repository_id.name))
database.dependencies_remove(None)
assert database.dependencies_get() == []
assert database.dependencies_get(package_ahriman.base, RepositoryId("i686", database._repository_id.name))

View File

@ -33,3 +33,21 @@ def test_init_skip_migration(database: SQLite, configuration: Configuration, moc
database.init(configuration)
migrate_schema_mock.assert_not_called()
def test_package_clear(database: SQLite, mocker: MockerFixture) -> None:
"""
must clear package data
"""
build_queue_mock = mocker.patch("ahriman.core.database.SQLite.build_queue_clear")
patches_mock = mocker.patch("ahriman.core.database.SQLite.patches_remove")
logs_mock = mocker.patch("ahriman.core.database.SQLite.logs_remove")
changes_mock = mocker.patch("ahriman.core.database.SQLite.changes_remove")
dependencies_mock = mocker.patch("ahriman.core.database.SQLite.dependencies_remove")
database.package_clear("package")
build_queue_mock.assert_called_once_with("package")
patches_mock.assert_called_once_with("package", [])
logs_mock.assert_called_once_with("package", None)
changes_mock.assert_called_once_with("package")
dependencies_mock.assert_called_once_with("package")

View File

@ -82,34 +82,40 @@ def test_make_request(mocker: MockerFixture) -> None:
auth = client.auth = ("username", "password")
assert client.make_request("GET", "url9") is not None
client.auth = None
assert client.make_request("GET", "url10", stream=True) is not None
request_mock.assert_has_calls([
MockCall("GET", "url1", params=None, data=None, headers=None, files=None, json=None,
auth=None, timeout=client.timeout),
stream=None, auth=None, timeout=client.timeout),
MockCall().raise_for_status(),
MockCall("GET", "url2", params=[("param", "value")], data=None, headers=None, files=None, json=None,
auth=None, timeout=client.timeout),
stream=None, auth=None, timeout=client.timeout),
MockCall().raise_for_status(),
MockCall("POST", "url3", params=None, data=None, headers=None, files=None, json=None,
auth=None, timeout=client.timeout),
stream=None, auth=None, timeout=client.timeout),
MockCall().raise_for_status(),
MockCall("POST", "url4", params=None, data=None, headers=None, files=None, json={"param": "value"},
auth=None, timeout=client.timeout),
stream=None, auth=None, timeout=client.timeout),
MockCall().raise_for_status(),
MockCall("POST", "url5", params=None, data={"param": "value"}, headers=None, files=None, json=None,
auth=None, timeout=client.timeout),
stream=None, auth=None, timeout=client.timeout),
MockCall().raise_for_status(),
MockCall("POST", "url6", params=None, data=None, headers=None, files={"file": "tuple"}, json=None,
auth=None, timeout=client.timeout),
stream=None, auth=None, timeout=client.timeout),
MockCall().raise_for_status(),
MockCall("DELETE", "url7", params=None, data=None, headers=None, files=None, json=None,
auth=None, timeout=client.timeout),
stream=None, auth=None, timeout=client.timeout),
MockCall().raise_for_status(),
MockCall("GET", "url8", params=None, data=None, headers={"user-agent": "ua"}, files=None, json=None,
auth=None, timeout=client.timeout),
stream=None, auth=None, timeout=client.timeout),
MockCall().raise_for_status(),
MockCall("GET", "url9", params=None, data=None, headers=None, files=None, json=None,
auth=auth, timeout=client.timeout),
stream=None, auth=auth, timeout=client.timeout),
MockCall().raise_for_status(),
MockCall("GET", "url10", params=None, data=None, headers=None, files=None, json=None,
stream=True, auth=None, timeout=client.timeout),
MockCall().raise_for_status(),
])
@ -151,4 +157,4 @@ def test_make_request_session() -> None:
client.make_request("GET", "url", session=session_mock)
session_mock.request.assert_called_once_with(
"GET", "url", params=None, data=None, headers=None, files=None, json=None,
auth=None, timeout=client.timeout)
stream=None, auth=None, timeout=client.timeout)

View File

@ -2,28 +2,37 @@ import pytest
from pathlib import Path
from pytest_mock import MockerFixture
from typing import Any
from unittest.mock import call as MockCall
from ahriman.core.repository.executor import Executor
from ahriman.models.changes import Changes
from ahriman.models.dependencies import Dependencies
from ahriman.models.package import Package
from ahriman.models.packagers import Packagers
from ahriman.models.user import User
def test_process_build(executor: Executor, package_ahriman: Package, mocker: MockerFixture) -> None:
def test_process_build(executor: Executor, package_ahriman: Package, passwd: Any, mocker: MockerFixture) -> None:
"""
must run build process
"""
dependencies = Dependencies(package_ahriman.base)
mocker.patch("ahriman.models.repository_paths.getpwuid", return_value=passwd)
mocker.patch("ahriman.core.repository.executor.Executor.packages", return_value=[package_ahriman])
mocker.patch("ahriman.core.build_tools.task.Task.build", return_value=[Path(package_ahriman.base)])
init_mock = mocker.patch("ahriman.core.build_tools.task.Task.init", return_value="sha")
move_mock = mocker.patch("shutil.move")
status_client_mock = mocker.patch("ahriman.core.status.client.Client.set_building")
commit_sha_mock = mocker.patch("ahriman.core.status.client.Client.package_changes_set")
depends_on_mock = mocker.patch("ahriman.models.package_archive.PackageArchive.depends_on",
return_value=dependencies)
dependencies_mock = mocker.patch("ahriman.core.database.SQLite.dependencies_insert")
executor.process_build([package_ahriman], Packagers("packager"), bump_pkgrel=False)
init_mock.assert_called_once_with(pytest.helpers.anyvar(int), pytest.helpers.anyvar(int), None)
depends_on_mock.assert_called_once_with()
dependencies_mock.assert_called_once_with(dependencies)
# must move files (once)
move_mock.assert_called_once_with(Path(package_ahriman.base), executor.paths.packages / package_ahriman.base)
# must update status
@ -70,10 +79,7 @@ def test_process_remove_base(executor: Executor, package_ahriman: Package, mocke
mocker.patch("ahriman.core.repository.executor.Executor.packages", return_value=[package_ahriman])
tree_clear_mock = mocker.patch("ahriman.models.repository_paths.RepositoryPaths.tree_clear")
repo_remove_mock = mocker.patch("ahriman.core.alpm.repo.Repo.remove")
build_queue_mock = mocker.patch("ahriman.core.database.SQLite.build_queue_clear")
patches_mock = mocker.patch("ahriman.core.database.SQLite.patches_remove")
logs_mock = mocker.patch("ahriman.core.database.SQLite.logs_remove")
commit_sha_mock = mocker.patch("ahriman.core.database.SQLite.changes_remove")
database_mock = mocker.patch("ahriman.core.database.SQLite.package_clear")
status_client_mock = mocker.patch("ahriman.core.status.client.Client.package_remove")
executor.process_remove([package_ahriman.base])
@ -82,11 +88,8 @@ def test_process_remove_base(executor: Executor, package_ahriman: Package, mocke
package_ahriman.base, package_ahriman.packages[package_ahriman.base].filepath)
# must update status and remove package files
tree_clear_mock.assert_called_once_with(package_ahriman.base)
build_queue_mock.assert_called_once_with(package_ahriman.base)
patches_mock.assert_called_once_with(package_ahriman.base, [])
logs_mock.assert_called_once_with(package_ahriman.base, None)
database_mock.assert_called_once_with(package_ahriman.base)
status_client_mock.assert_called_once_with(package_ahriman.base)
commit_sha_mock.assert_called_once_with(package_ahriman.base)
def test_process_remove_with_debug(executor: Executor, package_ahriman: Package, mocker: MockerFixture) -> None:

View File

@ -96,17 +96,30 @@ def test_package_changes_skip(package_info: PackageInfo, package_ahriman: Packag
changes_mock.assert_not_called()
def test_packages(package_info: PackageInfo, package_ahriman: Package, mocker: MockerFixture) -> None:
def test_packages(package_info: PackageInfo, package_ahriman: Package, package_python_schedule: Package,
mocker: MockerFixture) -> None:
"""
must return repository packages
"""
mocker.patch("pathlib.Path.iterdir", return_value=[package_ahriman])
load_mock = mocker.patch("ahriman.core.repository.package_info.PackageInfo.load_archives")
package_info.packages()
mocker.patch("pathlib.Path.iterdir")
load_mock = mocker.patch("ahriman.core.repository.package_info.PackageInfo.load_archives",
return_value=[package_ahriman, package_python_schedule])
assert package_info.packages() == [package_ahriman, package_python_schedule]
# it uses filter object, so we cannot verify argument list =/
load_mock.assert_called_once_with(pytest.helpers.anyvar(int))
def test_packages_filter(package_info: PackageInfo, package_ahriman: Package, package_python_schedule: Package,
mocker: MockerFixture) -> None:
"""
must filter result by bases
"""
mocker.patch("pathlib.Path.iterdir")
mocker.patch("ahriman.core.repository.package_info.PackageInfo.load_archives",
return_value=[package_ahriman, package_python_schedule])
assert package_info.packages([package_ahriman.base]) == [package_ahriman]
def test_packages_built(package_info: PackageInfo, mocker: MockerFixture) -> None:
"""
must return build packages

View File

@ -6,6 +6,7 @@ from typing import Any
from ahriman.core.exceptions import UnknownPackageError
from ahriman.core.repository.update_handler import UpdateHandler
from ahriman.models.dependencies import Dependencies
from ahriman.models.package import Package
from ahriman.models.package_source import PackageSource
from ahriman.models.remote_source import RemoteSource
@ -16,12 +17,14 @@ def test_updates_aur(update_handler: UpdateHandler, package_ahriman: Package,
"""
must provide updates with status updates
"""
mocker.patch("ahriman.core.repository.update_handler.UpdateHandler.packages", return_value=[package_ahriman])
packages_mock = mocker.patch("ahriman.core.repository.update_handler.UpdateHandler.packages",
return_value=[package_ahriman])
mocker.patch("ahriman.models.package.Package.from_aur", return_value=package_ahriman)
status_client_mock = mocker.patch("ahriman.core.status.client.Client.set_pending")
package_is_outdated_mock = mocker.patch("ahriman.models.package.Package.is_outdated", return_value=True)
assert update_handler.updates_aur([], vcs=True) == [package_ahriman]
packages_mock.assert_called_once_with([])
status_client_mock.assert_called_once_with(package_ahriman.base)
package_is_outdated_mock.assert_called_once_with(
package_ahriman, update_handler.paths,
@ -70,17 +73,17 @@ def test_updates_aur_local(update_handler: UpdateHandler, package_ahriman: Packa
package_load_mock.assert_not_called()
def test_updates_aur_filter(update_handler: UpdateHandler, package_ahriman: Package, package_python_schedule: Package,
mocker: MockerFixture) -> None:
def test_updates_aur_filter(update_handler: UpdateHandler, package_ahriman: Package, mocker: MockerFixture) -> None:
"""
must provide updates only for filtered packages
"""
mocker.patch("ahriman.core.repository.update_handler.UpdateHandler.packages",
return_value=[package_ahriman, package_python_schedule])
packages_mock = mocker.patch("ahriman.core.repository.update_handler.UpdateHandler.packages",
return_value=[package_ahriman])
mocker.patch("ahriman.models.package.Package.is_outdated", return_value=True)
package_load_mock = mocker.patch("ahriman.models.package.Package.from_aur", return_value=package_ahriman)
assert update_handler.updates_aur([package_ahriman.base], vcs=True) == [package_ahriman]
packages_mock.assert_called_once_with([package_ahriman.base])
package_load_mock.assert_called_once_with(package_ahriman.base, None)
@ -131,7 +134,7 @@ def test_updates_aur_load_by_package(update_handler: UpdateHandler, package_pyth
assert update_handler.updates_aur([], vcs=True) == [package_python_schedule]
def test_updates_load_by_package_aur_failed(update_handler: UpdateHandler, package_ahriman: Package,
def test_updates_aur_load_by_package_failed(update_handler: UpdateHandler, package_ahriman: Package,
mocker: MockerFixture) -> None:
"""
must update status via client for failed load
@ -143,6 +146,56 @@ def test_updates_load_by_package_aur_failed(update_handler: UpdateHandler, packa
update_handler.updates_aur([], vcs=True)
def test_updates_dependencies(update_handler: UpdateHandler, package_ahriman: Package, package_python_schedule: Package,
mocker: MockerFixture) -> None:
"""
must define updates with broken dependencies
"""
packages_mock = mocker.patch("ahriman.core.repository.update_handler.UpdateHandler.packages",
return_value=[package_ahriman, package_python_schedule])
dependencies = [
Dependencies(package_ahriman.base, {Path("usr/lib/python3.11/site-packages"): ["python"]}),
Dependencies(package_python_schedule.base, {Path("usr/lib/python3.12/site-packages"): ["python"]}),
]
mocker.patch("ahriman.core.database.SQLite.dependencies_get", return_value=dependencies)
mocker.patch("ahriman.core.alpm.pacman.Pacman.files",
return_value={"python": {Path("usr/lib/python3.12/site-packages")}})
assert update_handler.updates_dependencies(["filter"]) == [package_ahriman]
packages_mock.assert_called_once_with(["filter"])
def test_updates_dependencies_skip_unknown(update_handler: UpdateHandler, package_ahriman: Package,
mocker: MockerFixture) -> None:
"""
must skip unknown package dependencies
"""
mocker.patch("ahriman.core.repository.update_handler.UpdateHandler.packages", return_value=[package_ahriman])
mocker.patch("ahriman.core.database.SQLite.dependencies_get", return_value=[])
mocker.patch("ahriman.core.alpm.pacman.Pacman.files",
return_value={"python": {Path("usr/lib/python3.12/site-packages")}})
assert update_handler.updates_dependencies(["filter"]) == []
def test_updates_dependencies_partial(update_handler: UpdateHandler, package_ahriman: Package,
mocker: MockerFixture) -> None:
"""
must skip broken dependencies update if at least one package provides file
"""
mocker.patch("ahriman.core.repository.update_handler.UpdateHandler.packages", return_value=[package_ahriman])
dependencies = [
Dependencies(package_ahriman.base, {Path("usr"): ["filesystem", "python"]}),
]
mocker.patch("ahriman.core.database.SQLite.dependencies_get", return_value=dependencies)
mocker.patch("ahriman.core.alpm.pacman.Pacman.files", return_value={
"filesystem": {Path("usr")},
"python": {Path("usr")},
})
assert update_handler.updates_dependencies(["filter"]) == []
def test_updates_local(update_handler: UpdateHandler, package_ahriman: Package, mocker: MockerFixture) -> None:
"""
must check for updates for locally stored packages

View File

@ -460,6 +460,7 @@ def test_walk(resource_path_root: Path) -> None:
"""
expected = sorted([
resource_path_root / "core" / "ahriman.ini",
resource_path_root / "core" / "arcanisrepo.files.tar.gz",
resource_path_root / "core" / "logging.ini",
resource_path_root / "models" / "aur_error",
resource_path_root / "models" / "big_file_checksum",
@ -467,6 +468,7 @@ def test_walk(resource_path_root: Path) -> None:
resource_path_root / "models" / "official_error",
resource_path_root / "models" / "package_ahriman_aur",
resource_path_root / "models" / "package_akonadi_aur",
resource_path_root / "models" / "package_ahriman_files",
resource_path_root / "models" / "package_ahriman_srcinfo",
resource_path_root / "models" / "package_gcc10_srcinfo",
resource_path_root / "models" / "package_jellyfin-ffmpeg5-bin_srcinfo",