reorder tests

This commit is contained in:
2026-07-13 13:00:13 +03:00
parent 92ac036f78
commit 7db38ff9e4
800 changed files with 307 additions and 996 deletions
@@ -0,0 +1,103 @@
import pytest
from pytest_mock import MockerFixture
from ahriman.application.application import Application
from ahriman.application.application.application_packages import ApplicationPackages
from ahriman.application.application.application_properties import ApplicationProperties
from ahriman.application.application.application_repository import ApplicationRepository
from ahriman.application.application.updates_iterator import FixedUpdatesIterator, UpdatesIterator
from ahriman.core.configuration import Configuration
from ahriman.core.database import SQLite
from ahriman.core.repository import Repository
@pytest.fixture
def application_packages(configuration: Configuration, database: SQLite, repository: Repository,
mocker: MockerFixture) -> ApplicationPackages:
"""
fixture for application with package functions
Args:
configuration(Configuration): configuration fixture
database(SQLite): database fixture
repository(Repository): repository fixture
mocker(MockerFixture): mocker object
Returns:
ApplicationPackages: application test instance
"""
mocker.patch("ahriman.core.repository.Repository.load", return_value=repository)
mocker.patch("ahriman.core.database.SQLite.load", return_value=database)
_, repository_id = configuration.check_loaded()
return ApplicationPackages(repository_id, configuration, report=False)
@pytest.fixture
def application_properties(configuration: Configuration, database: SQLite, repository: Repository,
mocker: MockerFixture) -> ApplicationProperties:
"""
fixture for application with properties only
Args:
configuration(Configuration): configuration fixture
database(SQLite): database fixture
repository(Repository): repository fixture
mocker(MockerFixture): mocker object
Returns:
ApplicationProperties: application test instance
"""
mocker.patch("ahriman.core.repository.Repository.load", return_value=repository)
mocker.patch("ahriman.core.database.SQLite.load", return_value=database)
_, repository_id = configuration.check_loaded()
return ApplicationProperties(repository_id, configuration, report=False)
@pytest.fixture
def application_repository(configuration: Configuration, database: SQLite, repository: Repository,
mocker: MockerFixture) -> ApplicationRepository:
"""
fixture for application with repository functions
Args:
configuration(Configuration): configuration fixture
database(SQLite): database fixture
repository(Repository): repository fixture
mocker(MockerFixture): mocker object
Returns:
ApplicationRepository: application test instance
"""
mocker.patch("ahriman.core.repository.Repository.load", return_value=repository)
mocker.patch("ahriman.core.database.SQLite.load", return_value=database)
_, repository_id = configuration.check_loaded()
return ApplicationRepository(repository_id, configuration, report=False)
@pytest.fixture
def fixed_updates_iterator(application: Application) -> FixedUpdatesIterator:
"""
fixture for fixed updates iterator
Args:
application(Application): application fixture
Returns:
FixedUpdatesIterator: fixed updates iterator test instance
"""
return FixedUpdatesIterator(application, 1)
@pytest.fixture
def updates_iterator(application: Application) -> UpdatesIterator:
"""
fixture for chunk bases updates iterator
Args:
application(Application): application fixture
Returns:
UpdatesIterator: updates iterator test instance
"""
return UpdatesIterator(application, 1)
@@ -0,0 +1,164 @@
from pathlib import Path
from pytest_mock import MockerFixture
from typing import Any
from unittest.mock import MagicMock, call as MockCall
from ahriman.application.application import Application
from ahriman.core.tree import Leaf, Tree
from ahriman.models.package import Package
from ahriman.models.result import Result
def test_known_packages(application: Application, package_ahriman: Package, mocker: MockerFixture) -> None:
"""
must return not empty list of known packages
"""
mocker.patch("ahriman.core.repository.repository.Repository.packages", return_value=[package_ahriman])
packages = application._known_packages()
assert len(packages) > 1
assert package_ahriman.base in packages
def test_on_result(application: Application, package_ahriman: Package, mocker: MockerFixture) -> None:
"""
must call on_result trigger function
"""
mocker.patch("ahriman.core.repository.Repository.packages", return_value=[package_ahriman])
triggers_mock = mocker.patch("ahriman.core.triggers.TriggerLoader.on_result")
application.on_result(Result())
triggers_mock.assert_called_once_with(Result(), [package_ahriman])
def test_on_start(application: Application, mocker: MockerFixture) -> None:
"""
must call on_start trigger function
"""
triggers_mock = mocker.patch("ahriman.core.triggers.TriggerLoader.on_start")
application.on_start()
triggers_mock.assert_called_once_with()
def test_on_stop(application: Application, mocker: MockerFixture) -> None:
"""
must call on_stop trigger function
"""
triggers_mock = mocker.patch("ahriman.core.triggers.TriggerLoader.on_stop")
application.on_stop()
triggers_mock.assert_called_once_with()
def test_print_updates(application: Application, package_ahriman: Package, mocker: MockerFixture) -> None:
"""
must print updates
"""
tree = Tree([Leaf(package_ahriman)])
mocker.patch("ahriman.core.repository.repository.Repository.packages", return_value=[package_ahriman])
mocker.patch("ahriman.core.tree.Tree.resolve", return_value=tree.levels())
print_mock = mocker.patch("ahriman.core.formatters.Printer.print")
application.print_updates([package_ahriman], log_fn=print)
print_mock.assert_called_once_with(verbose=True, log_fn=print, separator=" -> ")
def test_with_dependencies(application: Application, package_ahriman: Package, package_python_schedule: Package,
mocker: MockerFixture) -> None:
"""
must append list of missing dependencies
"""
def create_package_mock(package_base) -> MagicMock:
mock = MagicMock()
mock.base = package_base
mock.depends_build = []
mock.packages_full = [package_base]
return mock
def get_package(name: str | Path, *args: Any, **kwargs: Any) -> Package:
name = name if isinstance(name, str) else name.name
return packages[name]
package_python_schedule.packages = {
package_python_schedule.base: package_python_schedule.packages[package_python_schedule.base]
}
package_ahriman.packages[package_ahriman.base].depends = ["devtools", "python", package_python_schedule.base]
package_ahriman.packages[package_ahriman.base].make_depends = ["python-build", "python-installer"]
packages = {
package_ahriman.base: package_ahriman,
package_python_schedule.base: package_python_schedule,
"python": create_package_mock("python"),
"python-installer": create_package_mock("python-installer"),
}
mocker.patch("pathlib.Path.is_dir", autospec=True, side_effect=lambda p: p.name == "python")
package_aur_mock = mocker.patch("ahriman.models.package.Package.from_aur", side_effect=get_package)
package_local_mock = mocker.patch("ahriman.models.package.Package.from_build", side_effect=get_package)
packages_mock = mocker.patch("ahriman.application.application.Application._known_packages",
return_value={"devtools", "python-build", "python-pytest"})
status_client_mock = mocker.patch("ahriman.core.status.Client.set_unknown")
result = application.with_dependencies([package_ahriman], process_dependencies=True)
assert {package.base: package for package in result} == packages
package_aur_mock.assert_has_calls([
MockCall(package_python_schedule.base, package_ahriman.packager, include_provides=True),
MockCall("python-installer", package_ahriman.packager, include_provides=True),
], any_order=True)
package_local_mock.assert_has_calls([
MockCall(application.repository.paths.cache_for("python"), "x86_64", package_ahriman.packager),
], any_order=True)
packages_mock.assert_called_once_with()
status_client_mock.assert_has_calls([
MockCall(package_python_schedule),
MockCall(packages["python"]),
MockCall(packages["python-installer"]),
], any_order=True)
def test_with_dependencies_exception(application: Application, package_ahriman: Package,
package_python_schedule: Package, mocker: MockerFixture) -> None:
"""
must skip packages if exception occurs
"""
def create_package_mock(package_base) -> MagicMock:
mock = MagicMock()
mock.base = package_base
mock.depends_build = []
mock.packages_full = [package_base]
return mock
package_python_schedule.packages = {
package_python_schedule.base: package_python_schedule.packages[package_python_schedule.base]
}
package_ahriman.packages[package_ahriman.base].depends = ["devtools", "python", package_python_schedule.base]
package_ahriman.packages[package_ahriman.base].make_depends = ["python-build", "python-installer"]
packages = {
package_ahriman.base: package_ahriman,
package_python_schedule.base: package_python_schedule,
"python": create_package_mock("python"),
"python-installer": create_package_mock("python-installer"),
}
mocker.patch("pathlib.Path.is_dir", autospec=True, side_effect=lambda p: p.name == "python")
mocker.patch("ahriman.models.package.Package.from_aur", side_effect=lambda *args: packages[args[0]])
mocker.patch("ahriman.models.package.Package.from_build", side_effect=Exception)
mocker.patch("ahriman.application.application.Application._known_packages",
return_value={"devtools", "python-build", "python-pytest"})
assert not application.with_dependencies([package_ahriman], process_dependencies=True)
def test_with_dependencies_skip(application: Application, package_ahriman: Package, mocker: MockerFixture) -> None:
"""
must skip processing of dependencies
"""
packages_mock = mocker.patch("ahriman.application.application.Application._known_packages")
assert application.with_dependencies([package_ahriman], process_dependencies=False) == [package_ahriman]
packages_mock.assert_not_called()
assert application.with_dependencies([], process_dependencies=True) == []
packages_mock.assert_not_called()
@@ -0,0 +1,236 @@
import pytest
from pathlib import Path
from pytest_mock import MockerFixture
from unittest.mock import MagicMock
from ahriman.application.application.application_packages import ApplicationPackages
from ahriman.core.exceptions import UnknownPackageError
from ahriman.models.package import Package
from ahriman.models.package_description import PackageDescription
from ahriman.models.package_source import PackageSource
from ahriman.models.result import Result
def test_add_archive(application_packages: ApplicationPackages, package_ahriman: Package,
mocker: MockerFixture) -> None:
"""
must add package from archive
"""
is_file_mock = mocker.patch("pathlib.Path.is_file", return_value=True)
copy_mock = mocker.patch("shutil.copy")
application_packages._add_archive(package_ahriman.base)
is_file_mock.assert_called_once_with()
copy_mock.assert_called_once_with(
Path(package_ahriman.base), application_packages.repository.paths.packages / package_ahriman.base)
def test_add_archive_missing(application_packages: ApplicationPackages, mocker: MockerFixture) -> None:
"""
must raise UnknownPackageError on unknown path
"""
mocker.patch("pathlib.Path.is_file", return_value=False)
with pytest.raises(UnknownPackageError):
application_packages._add_archive("package")
def test_add_aur(application_packages: ApplicationPackages, package_ahriman: Package, mocker: MockerFixture) -> None:
"""
must add package from AUR
"""
mocker.patch("ahriman.models.package.Package.from_aur", return_value=package_ahriman)
build_queue_mock = mocker.patch("ahriman.core.database.SQLite.build_queue_insert")
status_client_mock = mocker.patch("ahriman.core.status.Client.set_unknown")
application_packages._add_aur(package_ahriman.base, "packager")
build_queue_mock.assert_called_once_with(package_ahriman)
status_client_mock.assert_called_once_with(package_ahriman)
def test_add_directory(application_packages: ApplicationPackages, package_ahriman: Package,
mocker: MockerFixture) -> None:
"""
must add packages from directory
"""
is_dir_mock = mocker.patch("pathlib.Path.is_dir", return_value=True)
filename = package_ahriman.packages[package_ahriman.base].filepath
iterdir_mock = mocker.patch("pathlib.Path.iterdir", return_value=[filename])
add_mock = mocker.patch("ahriman.application.application.application_packages.ApplicationPackages._add_archive")
application_packages._add_directory(package_ahriman.base)
is_dir_mock.assert_called_once_with()
iterdir_mock.assert_called_once_with()
add_mock.assert_called_once_with(str(filename))
def test_add_directory_missing(application_packages: ApplicationPackages, mocker: MockerFixture) -> None:
"""
must raise UnknownPackageError on unknown directory path
"""
mocker.patch("pathlib.Path.is_dir", return_value=False)
with pytest.raises(UnknownPackageError):
application_packages._add_directory("package")
def test_add_local(application_packages: ApplicationPackages, package_ahriman: Package, mocker: MockerFixture) -> None:
"""
must add package from local sources
"""
mocker.patch("ahriman.models.package.Package.from_build", return_value=package_ahriman)
is_dir_mock = mocker.patch("pathlib.Path.is_dir", return_value=True)
init_mock = mocker.patch("ahriman.core.build_tools.sources.Sources.init")
copytree_mock = mocker.patch("shutil.copytree")
build_queue_mock = mocker.patch("ahriman.core.database.SQLite.build_queue_insert")
application_packages._add_local(package_ahriman.base, "packager")
is_dir_mock.assert_called_once_with()
copytree_mock.assert_called_once_with(
Path(package_ahriman.base),
application_packages.repository.paths.cache_for(package_ahriman.base),
dirs_exist_ok=True)
init_mock.assert_called_once_with(application_packages.repository.paths.cache_for(package_ahriman.base))
build_queue_mock.assert_called_once_with(package_ahriman)
def test_add_local_cache(application_packages: ApplicationPackages, package_ahriman: Package,
mocker: MockerFixture) -> None:
"""
must add package from local source if there is cache
"""
mocker.patch("ahriman.models.package.Package.from_build", return_value=package_ahriman)
mocker.patch("pathlib.Path.is_dir", autospec=True,
side_effect=lambda p: p.is_relative_to(application_packages.repository.paths.cache))
init_mock = mocker.patch("ahriman.core.build_tools.sources.Sources.init")
copytree_mock = mocker.patch("shutil.copytree")
build_queue_mock = mocker.patch("ahriman.core.database.SQLite.build_queue_insert")
application_packages._add_local(package_ahriman.base, "packager")
copytree_mock.assert_not_called()
init_mock.assert_not_called()
build_queue_mock.assert_called_once_with(package_ahriman)
def test_add_local_missing(application_packages: ApplicationPackages, mocker: MockerFixture) -> None:
"""
must raise UnknownPackageError if package wasn't found
"""
mocker.patch("pathlib.Path.is_dir", return_value=False)
with pytest.raises(UnknownPackageError):
application_packages._add_local("package", "packager")
def test_add_remote(application_packages: ApplicationPackages, package_description_ahriman: PackageDescription,
mocker: MockerFixture) -> None:
"""
must add package from remote source
"""
response_mock = MagicMock()
response_mock.iter_content.return_value = ["chunk"]
open_mock = mocker.patch("pathlib.Path.open")
request_mock = mocker.patch("requests.get", return_value=response_mock)
url = f"https://host/{package_description_ahriman.filename}"
application_packages._add_remote(url)
open_mock.assert_called_once_with("wb")
request_mock.assert_called_once_with(url, stream=True, timeout=None)
response_mock.raise_for_status.assert_called_once_with()
def test_add_remote_missing(application_packages: ApplicationPackages, mocker: MockerFixture) -> None:
"""
must raise UnknownPackageError if remote package wasn't found
"""
mocker.patch("requests.get", side_effect=Exception)
with pytest.raises(UnknownPackageError):
application_packages._add_remote("url")
def test_add_repository(application_packages: ApplicationPackages, package_ahriman: Package,
mocker: MockerFixture) -> None:
"""
must add package from official repository
"""
mocker.patch("ahriman.models.package.Package.from_official", return_value=package_ahriman)
build_queue_mock = mocker.patch("ahriman.core.database.SQLite.build_queue_insert")
status_client_mock = mocker.patch("ahriman.core.status.Client.set_unknown")
application_packages._add_repository(package_ahriman.base, "packager")
build_queue_mock.assert_called_once_with(package_ahriman)
status_client_mock.assert_called_once_with(package_ahriman)
def test_add_add_archive(application_packages: ApplicationPackages, package_ahriman: Package,
mocker: MockerFixture) -> None:
"""
must add package from archive via add function
"""
add_mock = mocker.patch("ahriman.application.application.application_packages.ApplicationPackages._add_archive")
application_packages.add([package_ahriman.base], PackageSource.Archive, "packager")
add_mock.assert_called_once_with(package_ahriman.base, "packager")
def test_add_add_aur(application_packages: ApplicationPackages, package_ahriman: Package,
mocker: MockerFixture) -> None:
"""
must add package from AUR via add function
"""
add_mock = mocker.patch("ahriman.application.application.application_packages.ApplicationPackages._add_aur")
application_packages.add([package_ahriman.base], PackageSource.AUR, "packager")
add_mock.assert_called_once_with(package_ahriman.base, "packager")
def test_add_add_directory(application_packages: ApplicationPackages, package_ahriman: Package,
mocker: MockerFixture) -> None:
"""
must add packages from directory via add function
"""
add_mock = mocker.patch("ahriman.application.application.application_packages.ApplicationPackages._add_directory")
application_packages.add([package_ahriman.base], PackageSource.Directory, "packager")
add_mock.assert_called_once_with(package_ahriman.base, "packager")
def test_add_add_local(application_packages: ApplicationPackages, package_ahriman: Package,
mocker: MockerFixture) -> None:
"""
must add package from local sources via add function
"""
add_mock = mocker.patch("ahriman.application.application.application_packages.ApplicationPackages._add_local")
application_packages.add([package_ahriman.base], PackageSource.Local, "packager")
add_mock.assert_called_once_with(package_ahriman.base, "packager")
def test_add_add_remote(application_packages: ApplicationPackages, package_description_ahriman: PackageDescription,
mocker: MockerFixture) -> None:
"""
must add package from remote source via add function
"""
add_mock = mocker.patch("ahriman.application.application.application_packages.ApplicationPackages._add_remote")
url = f"https://host/{package_description_ahriman.filename}"
application_packages.add([url], PackageSource.Remote, "packager")
add_mock.assert_called_once_with(url, "packager")
def test_on_result(application_packages: ApplicationPackages) -> None:
"""
must raise NotImplemented for missing finalize method
"""
with pytest.raises(NotImplementedError):
application_packages.on_result(Result())
def test_remove(application_packages: ApplicationPackages, mocker: MockerFixture) -> None:
"""
must remove package
"""
executor_mock = mocker.patch("ahriman.core.repository.executor.Executor.process_remove", return_value=Result())
on_result_mock = mocker.patch("ahriman.application.application.application_packages.ApplicationPackages.on_result")
application_packages.remove([])
executor_mock.assert_called_once_with([])
on_result_mock.assert_called_once_with(Result())
@@ -0,0 +1,15 @@
from ahriman.application.application.application_properties import ApplicationProperties
def test_architecture(application_properties: ApplicationProperties) -> None:
"""
must return repository architecture
"""
assert application_properties.architecture == application_properties.repository_id.architecture
def test_reporter(application_properties: ApplicationProperties) -> None:
"""
must have reporter attribute
"""
assert application_properties.reporter
@@ -0,0 +1,404 @@
import pytest
from pathlib import Path
from pytest_mock import MockerFixture
from unittest.mock import call as MockCall
from ahriman.application.application.application_repository import ApplicationRepository
from ahriman.core.exceptions import UnknownPackageError
from ahriman.core.tree import Leaf, Tree
from ahriman.models.changes import Changes
from ahriman.models.package import Package
from ahriman.models.packagers import Packagers
from ahriman.models.result import Result
def test_changes(application_repository: ApplicationRepository, package_ahriman: Package,
mocker: MockerFixture) -> None:
"""
must generate changes for the packages
"""
changes = Changes("sha", "change")
hashes_mock = mocker.patch("ahriman.core.status.local_client.LocalClient.package_changes_get", return_value=changes)
changes_mock = mocker.patch("ahriman.core.repository.Repository.package_changes", return_value=changes)
report_mock = mocker.patch("ahriman.core.status.local_client.LocalClient.package_changes_update")
application_repository.changes([package_ahriman])
hashes_mock.assert_called_once_with(package_ahriman.base)
changes_mock.assert_called_once_with(package_ahriman, changes.last_commit_sha)
report_mock.assert_called_once_with(package_ahriman.base, changes)
def test_changes_skip(application_repository: ApplicationRepository, package_ahriman: Package,
mocker: MockerFixture) -> None:
"""
must skip change generation if no last commit sha has been found
"""
changes_mock = mocker.patch("ahriman.core.repository.Repository.package_changes")
report_mock = mocker.patch("ahriman.core.status.local_client.LocalClient.package_changes_update")
application_repository.changes([package_ahriman])
changes_mock.assert_not_called()
report_mock.assert_not_called()
def test_changes_no_update(application_repository: ApplicationRepository, package_ahriman: Package,
mocker: MockerFixture) -> None:
"""
must skip update if package_changes returns None (no new commits)
"""
changes = Changes("sha", "change")
mocker.patch("ahriman.core.status.local_client.LocalClient.package_changes_get", return_value=changes)
mocker.patch("ahriman.core.repository.Repository.package_changes", return_value=None)
report_mock = mocker.patch("ahriman.core.status.local_client.LocalClient.package_changes_update")
application_repository.changes([package_ahriman])
report_mock.assert_not_called()
def test_clean_cache(application_repository: ApplicationRepository, mocker: MockerFixture) -> None:
"""
must clean cache directory
"""
clear_mock = mocker.patch("ahriman.core.repository.Repository.clear_cache")
application_repository.clean(cache=True, chroot=False, manual=False, packages=False, pacman=False)
clear_mock.assert_called_once_with()
def test_clean_chroot(application_repository: ApplicationRepository, mocker: MockerFixture) -> None:
"""
must clean chroot directory
"""
clear_mock = mocker.patch("ahriman.core.repository.Repository.clear_chroot")
application_repository.clean(cache=False, chroot=True, manual=False, packages=False, pacman=False)
clear_mock.assert_called_once_with()
def test_clean_manual(application_repository: ApplicationRepository, mocker: MockerFixture) -> None:
"""
must clean manual directory
"""
clear_mock = mocker.patch("ahriman.core.repository.Repository.clear_queue")
application_repository.clean(cache=False, chroot=False, manual=True, packages=False, pacman=False)
clear_mock.assert_called_once_with()
def test_clean_packages(application_repository: ApplicationRepository, mocker: MockerFixture) -> None:
"""
must clean packages directory
"""
clear_mock = mocker.patch("ahriman.core.repository.Repository.clear_packages")
application_repository.clean(cache=False, chroot=False, manual=False, packages=True, pacman=False)
clear_mock.assert_called_once_with()
def test_clean_pacman(application_repository: ApplicationRepository, mocker: MockerFixture) -> None:
"""
must clean pacman directory
"""
clear_mock = mocker.patch("ahriman.core.repository.Repository.clear_pacman")
application_repository.clean(cache=False, chroot=False, manual=False, packages=False, pacman=True)
clear_mock.assert_called_once_with()
def test_on_result(application_repository: ApplicationRepository) -> None:
"""
must raise NotImplemented for missing finalize method
"""
with pytest.raises(NotImplementedError):
application_repository.on_result(Result())
def test_sign(application_repository: ApplicationRepository, package_ahriman: Package, package_python_schedule: Package,
mocker: MockerFixture) -> None:
"""
must sign world
"""
mocker.patch("ahriman.core.repository.repository.Repository.packages",
return_value=[package_ahriman, package_python_schedule])
sign_package_mock = mocker.patch("ahriman.core.sign.gpg.GPG.process_sign_package")
sign_repository_mock = mocker.patch("ahriman.core.sign.gpg.GPG.process_sign_repository")
on_result_mock = mocker.patch(
"ahriman.application.application.application_repository.ApplicationRepository.on_result")
application_repository.sign([])
sign_package_mock.assert_has_calls([
MockCall(pytest.helpers.anyvar(int), None),
MockCall(pytest.helpers.anyvar(int), None),
MockCall(pytest.helpers.anyvar(int), None),
])
sign_repository_mock.assert_called_once_with(application_repository.repository.repo.repo_path)
on_result_mock.assert_called_once_with(Result())
def test_sign_skip(application_repository: ApplicationRepository, package_ahriman: Package,
mocker: MockerFixture) -> None:
"""
must skip sign packages with empty filename
"""
package_ahriman.packages[package_ahriman.base].filename = None
mocker.patch("ahriman.core.repository.repository.Repository.packages", return_value=[package_ahriman])
mocker.patch("ahriman.application.application.application_repository.ApplicationRepository.update")
mocker.patch("ahriman.application.application.application_repository.ApplicationRepository.on_result")
application_repository.sign([])
def test_unknown_no_aur(application_repository: ApplicationRepository, package_ahriman: Package,
mocker: MockerFixture) -> None:
"""
must return empty list in case if there is locally stored PKGBUILD
"""
mocker.patch("ahriman.core.repository.repository.Repository.packages", return_value=[package_ahriman])
mocker.patch("ahriman.models.package.Package.from_aur", side_effect=UnknownPackageError(package_ahriman.base))
mocker.patch("ahriman.models.package.Package.from_build", return_value=package_ahriman)
mocker.patch("pathlib.Path.is_dir", return_value=True)
mocker.patch("ahriman.core.build_tools.sources.Sources.has_remotes", return_value=False)
assert not application_repository.unknown()
def test_unknown_no_aur_no_local(application_repository: ApplicationRepository, package_ahriman: Package,
mocker: MockerFixture) -> None:
"""
must return list of packages missing in aur and in local storage
"""
mocker.patch("ahriman.core.repository.repository.Repository.packages", return_value=[package_ahriman])
mocker.patch("ahriman.models.package.Package.from_aur", side_effect=UnknownPackageError(package_ahriman.base))
mocker.patch("pathlib.Path.is_dir", return_value=False)
packages = application_repository.unknown()
assert packages == list(package_ahriman.packages.keys())
def test_unknown_no_local(application_repository: ApplicationRepository, package_ahriman: Package,
mocker: MockerFixture) -> None:
"""
must return empty list in case if there is package in AUR
"""
mocker.patch("ahriman.core.repository.repository.Repository.packages", return_value=[package_ahriman])
mocker.patch("ahriman.models.package.Package.from_aur")
mocker.patch("pathlib.Path.is_dir", return_value=False)
assert not application_repository.unknown()
def test_update(application_repository: ApplicationRepository, package_ahriman: Package, result: Result,
mocker: MockerFixture) -> None:
"""
must process package updates
"""
paths = [package.filepath for package in package_ahriman.packages.values()]
tree = Tree([Leaf(package_ahriman)])
prebuilt_result = Result()
resolve_mock = mocker.patch("ahriman.application.application.workers.local_updater.LocalUpdater.partition",
return_value=tree.levels())
mocker.patch("ahriman.core.repository.repository.Repository.packages_built", return_value=paths)
build_mock = mocker.patch("ahriman.application.application.workers.local_updater.LocalUpdater.update",
return_value=result)
update_mock = mocker.patch("ahriman.core.repository.Repository.process_update", return_value=prebuilt_result)
on_result_mock = mocker.patch(
"ahriman.application.application.application_repository.ApplicationRepository.on_result")
application_repository.update([package_ahriman], Packagers("username"), bump_pkgrel=True)
resolve_mock.assert_called_once_with([package_ahriman])
build_mock.assert_called_once_with([package_ahriman], Packagers("username"), bump_pkgrel=True)
update_mock.assert_called_once_with(paths, Packagers("username"))
on_result_mock.assert_has_calls([MockCall(prebuilt_result), MockCall(result)])
def test_update_prebuilt_filter(application_repository: ApplicationRepository, package_ahriman: Package, result: Result,
mocker: MockerFixture) -> None:
"""
must filter out packages which were successfully prebuilt
"""
paths = [package.filepath for package in package_ahriman.packages.values()]
resolve_mock = mocker.patch("ahriman.application.application.workers.local_updater.LocalUpdater.partition",
return_value=[])
mocker.patch("ahriman.core.repository.repository.Repository.packages_built", return_value=paths)
mocker.patch("ahriman.core.repository.Repository.process_update", return_value=result)
mocker.patch("ahriman.application.application.application_repository.ApplicationRepository.on_result")
application_repository.update([package_ahriman], Packagers("username"), bump_pkgrel=True)
resolve_mock.assert_called_once_with([])
def test_update_empty(application_repository: ApplicationRepository, package_ahriman: Package, result: Result,
mocker: MockerFixture) -> None:
"""
must skip updating repository if no packages supplied
"""
tree = Tree([Leaf(package_ahriman)])
mocker.patch("ahriman.application.application.workers.Updater.partition", return_value=tree.levels())
mocker.patch("ahriman.core.repository.Repository.packages_built", return_value=[])
mocker.patch("ahriman.application.application.workers.local_updater.LocalUpdater.update", return_value=result)
mocker.patch("ahriman.application.application.application_repository.ApplicationRepository.on_result")
update_mock = mocker.patch("ahriman.core.repository.Repository.process_update")
application_repository.update([package_ahriman])
update_mock.assert_not_called()
def test_updates_all(application_repository: ApplicationRepository, package_ahriman: Package,
mocker: MockerFixture) -> None:
"""
must get updates for all
"""
path = Path("local")
mocker.patch("ahriman.core.repository.package_info.PackageInfo.packages_built", return_value=[path])
updates_built_mock = mocker.patch("ahriman.core.repository.package_info.PackageInfo.load_archives")
updates_aur_mock = mocker.patch("ahriman.core.repository.update_handler.UpdateHandler.updates_aur",
return_value=[package_ahriman])
updates_local_mock = mocker.patch("ahriman.core.repository.update_handler.UpdateHandler.updates_local")
updates_manual_mock = mocker.patch("ahriman.core.repository.update_handler.UpdateHandler.updates_manual")
updates_deps_mock = mocker.patch("ahriman.core.repository.update_handler.UpdateHandler.updates_dependencies")
application_repository.updates([], aur=True, local=True, manual=True, vcs=True, check_files=True)
updates_built_mock.assert_called_once_with([path])
updates_aur_mock.assert_called_once_with([], vcs=True)
updates_local_mock.assert_called_once_with(vcs=True)
updates_manual_mock.assert_called_once_with()
updates_deps_mock.assert_called_once_with([])
def test_updates_disabled(application_repository: ApplicationRepository, mocker: MockerFixture) -> None:
"""
must get updates without anything
"""
path = Path("local")
mocker.patch("ahriman.core.repository.package_info.PackageInfo.packages_built", return_value=[path])
updates_built_mock = mocker.patch("ahriman.core.repository.package_info.PackageInfo.load_archives")
updates_aur_mock = mocker.patch("ahriman.core.repository.update_handler.UpdateHandler.updates_aur")
updates_local_mock = mocker.patch("ahriman.core.repository.update_handler.UpdateHandler.updates_local")
updates_manual_mock = mocker.patch("ahriman.core.repository.update_handler.UpdateHandler.updates_manual")
updates_deps_mock = mocker.patch("ahriman.core.repository.update_handler.UpdateHandler.updates_dependencies")
application_repository.updates([], aur=False, local=False, manual=False, vcs=True, check_files=False)
updates_built_mock.assert_called_once_with([path])
updates_aur_mock.assert_not_called()
updates_local_mock.assert_not_called()
updates_manual_mock.assert_not_called()
updates_deps_mock.assert_not_called()
def test_updates_no_aur(application_repository: ApplicationRepository, mocker: MockerFixture) -> None:
"""
must get updates without aur
"""
path = Path("local")
mocker.patch("ahriman.core.repository.package_info.PackageInfo.packages_built", return_value=[path])
updates_built_mock = mocker.patch("ahriman.core.repository.package_info.PackageInfo.load_archives")
updates_aur_mock = mocker.patch("ahriman.core.repository.update_handler.UpdateHandler.updates_aur")
updates_local_mock = mocker.patch("ahriman.core.repository.update_handler.UpdateHandler.updates_local")
updates_manual_mock = mocker.patch("ahriman.core.repository.update_handler.UpdateHandler.updates_manual")
updates_deps_mock = mocker.patch("ahriman.core.repository.update_handler.UpdateHandler.updates_dependencies")
application_repository.updates([], aur=False, local=True, manual=True, vcs=True, check_files=True)
updates_built_mock.assert_called_once_with([path])
updates_aur_mock.assert_not_called()
updates_local_mock.assert_called_once_with(vcs=True)
updates_manual_mock.assert_called_once_with()
updates_deps_mock.assert_called_once_with([])
def test_updates_no_local(application_repository: ApplicationRepository, mocker: MockerFixture) -> None:
"""
must get updates without local packages
"""
path = Path("local")
mocker.patch("ahriman.core.repository.package_info.PackageInfo.packages_built", return_value=[path])
updates_built_mock = mocker.patch("ahriman.core.repository.package_info.PackageInfo.load_archives")
updates_aur_mock = mocker.patch("ahriman.core.repository.update_handler.UpdateHandler.updates_aur")
updates_local_mock = mocker.patch("ahriman.core.repository.update_handler.UpdateHandler.updates_local")
updates_manual_mock = mocker.patch("ahriman.core.repository.update_handler.UpdateHandler.updates_manual")
updates_deps_mock = mocker.patch("ahriman.core.repository.update_handler.UpdateHandler.updates_dependencies")
application_repository.updates([], aur=True, local=False, manual=True, vcs=True, check_files=True)
updates_built_mock.assert_called_once_with([path])
updates_aur_mock.assert_called_once_with([], vcs=True)
updates_local_mock.assert_not_called()
updates_manual_mock.assert_called_once_with()
updates_deps_mock.assert_called_once_with([])
def test_updates_no_manual(application_repository: ApplicationRepository, mocker: MockerFixture) -> None:
"""
must get updates without manual
"""
path = Path("local")
mocker.patch("ahriman.core.repository.package_info.PackageInfo.packages_built", return_value=[path])
updates_built_mock = mocker.patch("ahriman.core.repository.package_info.PackageInfo.load_archives")
updates_aur_mock = mocker.patch("ahriman.core.repository.update_handler.UpdateHandler.updates_aur")
updates_local_mock = mocker.patch("ahriman.core.repository.update_handler.UpdateHandler.updates_local")
updates_manual_mock = mocker.patch("ahriman.core.repository.update_handler.UpdateHandler.updates_manual")
updates_deps_mock = mocker.patch("ahriman.core.repository.update_handler.UpdateHandler.updates_dependencies")
application_repository.updates([], aur=True, local=True, manual=False, vcs=True, check_files=True)
updates_built_mock.assert_called_once_with([path])
updates_aur_mock.assert_called_once_with([], vcs=True)
updates_local_mock.assert_called_once_with(vcs=True)
updates_manual_mock.assert_not_called()
updates_deps_mock.assert_called_once_with([])
def test_updates_no_vcs(application_repository: ApplicationRepository, mocker: MockerFixture) -> None:
"""
must get updates without VCS
"""
path = Path("local")
mocker.patch("ahriman.core.repository.package_info.PackageInfo.packages_built", return_value=[path])
updates_built_mock = mocker.patch("ahriman.core.repository.package_info.PackageInfo.load_archives")
updates_aur_mock = mocker.patch("ahriman.core.repository.update_handler.UpdateHandler.updates_aur")
updates_local_mock = mocker.patch("ahriman.core.repository.update_handler.UpdateHandler.updates_local")
updates_manual_mock = mocker.patch("ahriman.core.repository.update_handler.UpdateHandler.updates_manual")
updates_deps_mock = mocker.patch("ahriman.core.repository.update_handler.UpdateHandler.updates_dependencies")
application_repository.updates([], aur=True, local=True, manual=True, vcs=False, check_files=True)
updates_built_mock.assert_called_once_with([path])
updates_aur_mock.assert_called_once_with([], vcs=False)
updates_local_mock.assert_called_once_with(vcs=False)
updates_manual_mock.assert_called_once_with()
updates_deps_mock.assert_called_once_with([])
def test_updates_no_check_files(application_repository: ApplicationRepository, mocker: MockerFixture) -> None:
"""
must get updates without checking broken links
"""
path = Path("local")
mocker.patch("ahriman.core.repository.package_info.PackageInfo.packages_built", return_value=[path])
updates_built_mock = mocker.patch("ahriman.core.repository.package_info.PackageInfo.load_archives")
updates_aur_mock = mocker.patch("ahriman.core.repository.update_handler.UpdateHandler.updates_aur")
updates_local_mock = mocker.patch("ahriman.core.repository.update_handler.UpdateHandler.updates_local")
updates_manual_mock = mocker.patch("ahriman.core.repository.update_handler.UpdateHandler.updates_manual")
updates_deps_mock = mocker.patch("ahriman.core.repository.update_handler.UpdateHandler.updates_dependencies")
application_repository.updates([], aur=True, local=True, manual=True, vcs=True, check_files=False)
updates_built_mock.assert_called_once_with([path])
updates_aur_mock.assert_called_once_with([], vcs=True)
updates_local_mock.assert_called_once_with(vcs=True)
updates_manual_mock.assert_called_once_with()
updates_deps_mock.assert_not_called()
def test_updates_with_filter(application_repository: ApplicationRepository, mocker: MockerFixture) -> None:
"""
must get updates with filter
"""
path = Path("local")
mocker.patch("ahriman.core.repository.package_info.PackageInfo.packages_built", return_value=[path])
updates_built_mock = mocker.patch("ahriman.core.repository.package_info.PackageInfo.load_archives")
updates_aur_mock = mocker.patch("ahriman.core.repository.update_handler.UpdateHandler.updates_aur")
updates_local_mock = mocker.patch("ahriman.core.repository.update_handler.UpdateHandler.updates_local")
updates_manual_mock = mocker.patch("ahriman.core.repository.update_handler.UpdateHandler.updates_manual")
updates_deps_mock = mocker.patch("ahriman.core.repository.update_handler.UpdateHandler.updates_dependencies")
application_repository.updates(["filter"], aur=True, local=True, manual=True, vcs=True, check_files=True)
updates_built_mock.assert_called_once_with([path])
updates_aur_mock.assert_called_once_with(["filter"], vcs=True)
updates_local_mock.assert_called_once_with(vcs=True)
updates_manual_mock.assert_called_once_with()
updates_deps_mock.assert_called_once_with(["filter"])
@@ -0,0 +1,74 @@
from pytest_mock import MockerFixture
from unittest.mock import call as MockCall
from ahriman.application.application.updates_iterator import FixedUpdatesIterator, UpdatesIterator
from ahriman.models.package import Package
def test_select_packages(updates_iterator: UpdatesIterator, package_ahriman: Package,
package_python_schedule: Package, mocker: MockerFixture) -> None:
"""
must return next partition
"""
mocker.patch("ahriman.core.repository.Repository.packages",
return_value=[package_ahriman, package_python_schedule])
assert updates_iterator.select_packages() == ([package_python_schedule.base], 2)
assert updates_iterator.select_packages() == ([package_python_schedule.base], 2)
def test_select_packages_empty(updates_iterator: UpdatesIterator, mocker: MockerFixture) -> None:
"""
must return None for empty repository
"""
mocker.patch("ahriman.core.repository.Repository.packages", return_value=[])
assert updates_iterator.select_packages() == (None, 1)
def test_select_packages_cycle(updates_iterator: UpdatesIterator, package_ahriman: Package,
package_python_schedule: Package, mocker: MockerFixture) -> None:
"""
must cycle over partitions
"""
mocker.patch("ahriman.core.repository.Repository.packages",
return_value=[package_ahriman, package_python_schedule])
assert updates_iterator.select_packages() == ([package_python_schedule.base], 2)
updates_iterator.updated_packages.add(package_python_schedule.base)
assert updates_iterator.select_packages() == ([package_ahriman.base], 2)
updates_iterator.updated_packages.add(package_ahriman.base)
assert updates_iterator.select_packages() == ([package_python_schedule.base], 2)
assert not updates_iterator.updated_packages
def test_iter(updates_iterator: UpdatesIterator) -> None:
"""
must return self as iterator
"""
assert iter(updates_iterator) == updates_iterator
def test_next(updates_iterator: UpdatesIterator, package_ahriman: Package, mocker: MockerFixture) -> None:
"""
must return next chunk to update
"""
mocker.patch("ahriman.application.application.updates_iterator.UpdatesIterator.select_packages",
side_effect=[([package_ahriman.base], 2), (None, 2), StopIteration])
sleep_mock = mocker.patch("time.sleep")
updates = list(updates_iterator)
assert updates == [[package_ahriman.base], None]
sleep_mock.assert_has_calls([MockCall(0.5), MockCall(0.5)])
def test_fixed_updates_iterator(fixed_updates_iterator: FixedUpdatesIterator, package_ahriman: Package,
mocker: MockerFixture) -> None:
"""
must always return empty package list
"""
assert fixed_updates_iterator.select_packages() == ([], 1)
mocker.patch("ahriman.core.repository.Repository.packages", return_value=[package_ahriman])
assert fixed_updates_iterator.select_packages() == ([], 1)
@@ -0,0 +1,48 @@
import pytest
from ahriman.application.application.workers import Updater
from ahriman.application.application.workers.local_updater import LocalUpdater
from ahriman.application.application.workers.remote_updater import RemoteUpdater
from ahriman.core.configuration import Configuration
from ahriman.core.repository import Repository
from ahriman.models.worker import Worker
@pytest.fixture
def local_updater(repository: Repository) -> LocalUpdater:
"""
local updater fixture
Args:
repository(Repository): repository fixture
Returns:
LocalUpdater: local updater test instance
"""
return LocalUpdater(repository)
@pytest.fixture
def remote_updater(configuration: Configuration) -> RemoteUpdater:
"""
local updater fixture
Args:
configuration(Configuration): configuration fixture
Returns:
RemoteUpdater: remote updater test instance
"""
_, repository_id = configuration.check_loaded()
return RemoteUpdater([Worker("remote1"), Worker("remote2")], repository_id, configuration)
@pytest.fixture
def updater() -> Updater:
"""
empty updater fixture
Returns:
Updater: empty updater test instance
"""
return Updater()
@@ -0,0 +1,30 @@
from pytest_mock import MockerFixture
from ahriman.application.application.workers.local_updater import LocalUpdater
from ahriman.models.package import Package
from ahriman.models.packagers import Packagers
from ahriman.models.result import Result
def test_partition(local_updater: LocalUpdater, mocker: MockerFixture) -> None:
"""
must partition as tree resolution
"""
resolve_mock = mocker.patch("ahriman.core.tree.Tree.resolve")
local_updater.partition([])
resolve_mock.assert_called_once_with([])
def test_update(local_updater: LocalUpdater, package_ahriman: Package, result: Result,
mocker: MockerFixture) -> None:
"""
must process package updates
"""
paths = [package.filepath for package in package_ahriman.packages.values()]
mocker.patch("ahriman.core.repository.Repository.packages_built", return_value=paths)
build_mock = mocker.patch("ahriman.core.repository.Repository.process_build", return_value=result)
update_mock = mocker.patch("ahriman.core.repository.Repository.process_update", return_value=result)
assert local_updater.update([package_ahriman], Packagers("username"), bump_pkgrel=True) == result
build_mock.assert_called_once_with([package_ahriman], Packagers("username"), bump_pkgrel=True)
update_mock.assert_called_once_with(paths, Packagers("username"))
@@ -0,0 +1,83 @@
from pytest_mock import MockerFixture
from ahriman.application.application.workers.remote_updater import RemoteUpdater
from ahriman.core.http import SyncAhrimanClient
from ahriman.models.package import Package
from ahriman.models.packagers import Packagers
from ahriman.models.result import Result
def test_clients(remote_updater: RemoteUpdater) -> None:
"""
must return map of clients
"""
worker = remote_updater.workers[0]
client = SyncAhrimanClient()
remote_updater._clients.append((worker, client))
assert remote_updater.clients == {worker: client}
def test_update_url(remote_updater: RemoteUpdater) -> None:
"""
must generate update url correctly
"""
worker = remote_updater.workers[0]
assert remote_updater._update_url(worker).startswith(worker.address)
assert remote_updater._update_url(worker).endswith("/api/v1/service/add")
def test_next_worker(remote_updater: RemoteUpdater) -> None:
"""
must return next not used worker
"""
assert remote_updater.next_worker()[0] == remote_updater.workers[0]
assert len(remote_updater.clients) == 1
assert remote_updater.workers[0] in remote_updater.clients
assert remote_updater.next_worker()[0] == remote_updater.workers[1]
assert remote_updater.workers[1] in remote_updater.clients
assert len(remote_updater.clients) == 2
def test_next_worker_cycle(remote_updater: RemoteUpdater) -> None:
"""
must return first used worker if no free workers left
"""
worker1, client1 = remote_updater.next_worker()
worker2, client2 = remote_updater.next_worker()
assert remote_updater.next_worker() == (worker1, client1)
assert remote_updater.next_worker() == (worker2, client2)
assert remote_updater.next_worker() == (worker1, client1)
def test_partition(remote_updater: RemoteUpdater, mocker: MockerFixture) -> None:
"""
must partition as tree partition
"""
resolve_mock = mocker.patch("ahriman.core.tree.Tree.partition")
remote_updater.partition([])
resolve_mock.assert_called_once_with([], count=2)
def test_update(remote_updater: RemoteUpdater, package_ahriman: Package, mocker: MockerFixture) -> None:
"""
must process remote package updates
"""
worker, client = remote_updater.next_worker()
worker_mock = mocker.patch("ahriman.application.application.workers.remote_updater.RemoteUpdater.next_worker",
return_value=(worker, client))
request_mock = mocker.patch("ahriman.core.http.SyncAhrimanClient.make_request")
assert remote_updater.update([package_ahriman], Packagers("username"), bump_pkgrel=True) == Result()
worker_mock.assert_called_once_with()
request_mock.assert_called_once_with("POST", remote_updater._update_url(worker),
params=remote_updater.repository_id.query(),
json={
"increment": False,
"packager": "username",
"packages": [package_ahriman.base],
"patches": [],
"refresh": True,
})
@@ -0,0 +1,50 @@
import pytest
from ahriman.application.application.workers import Updater
from ahriman.application.application.workers.local_updater import LocalUpdater
from ahriman.application.application.workers.remote_updater import RemoteUpdater
from ahriman.core.configuration import Configuration
from ahriman.core.repository import Repository
from ahriman.models.worker import Worker
def test_load(configuration: Configuration, repository: Repository) -> None:
"""
must load local updater if empty worker list is set
"""
_, repository_id = configuration.check_loaded()
assert isinstance(Updater.load(repository_id, configuration, repository), LocalUpdater)
assert isinstance(Updater.load(repository_id, configuration, repository, []), LocalUpdater)
def test_load_from_option(configuration: Configuration, repository: Repository) -> None:
"""
must load remote updater if nonempty worker list is set
"""
_, repository_id = configuration.check_loaded()
assert isinstance(Updater.load(repository_id, configuration, repository, [Worker("remote")]), RemoteUpdater)
def test_load_from_configuration(configuration: Configuration, repository: Repository) -> None:
"""
must load remote updater from settings
"""
configuration.set_option("build", "workers", "remote")
_, repository_id = configuration.check_loaded()
assert isinstance(Updater.load(repository_id, configuration, repository), RemoteUpdater)
def test_partition(updater: Updater) -> None:
"""
must raise not implemented error for missing partition method
"""
with pytest.raises(NotImplementedError):
updater.partition([])
def test_update(updater: Updater) -> None:
"""
must raise not implemented error for missing update method
"""
with pytest.raises(NotImplementedError):
updater.update([])
@@ -0,0 +1,83 @@
import argparse
import pytest
from pytest_mock import MockerFixture
from ahriman.application.ahriman import _parser
from ahriman.application.application import Application
from ahriman.application.help_formatter import _HelpFormatter
from ahriman.application.lock import Lock
from ahriman.core.configuration import Configuration
from ahriman.core.database import SQLite
from ahriman.core.repository import Repository
@pytest.fixture
def application(configuration: Configuration, repository: Repository, database: SQLite,
mocker: MockerFixture) -> Application:
"""
fixture for application
Args:
configuration(Configuration): configuration fixture
database(SQLite): database fixture
repository(Repository): repository fixture
mocker(MockerFixture): mocker object
Returns:
Application: application test instance
"""
mocker.patch("ahriman.core.database.SQLite.load", return_value=database)
mocker.patch("ahriman.core.repository.Repository.load", return_value=repository)
_, repository_id = configuration.check_loaded()
return Application(repository_id, configuration, report=False)
@pytest.fixture
def args() -> argparse.Namespace:
"""
fixture for command line arguments
Returns:
argparse.Namespace: command line arguments test instance
"""
return argparse.Namespace(architecture=None, lock=None, force=False, unsafe=False, report=False,
repository=None, repository_id=None, wait_timeout=-1)
@pytest.fixture
def formatter() -> _HelpFormatter:
"""
fixture for help message formatter
Returns:
_HelpFormatter: help message formatter test instance
"""
return _HelpFormatter("ahriman")
@pytest.fixture
def lock(args: argparse.Namespace, configuration: Configuration) -> Lock:
"""
fixture for file lock
Args:
args(argparse.Namespace): command line arguments fixture
configuration(Configuration): configuration fixture
Returns:
Lock: file lock test instance
"""
_, repository_id = configuration.check_loaded()
return Lock(args, repository_id, configuration)
@pytest.fixture
def parser() -> argparse.ArgumentParser:
"""
fixture for command line arguments parser
Returns:
argparse.ArgumentParser: command line arguments parser test instance
"""
return _parser()
@@ -0,0 +1,211 @@
import argparse
import pytest
from pathlib import Path
from pytest_mock import MockerFixture
from ahriman.application.handlers.handler import Handler
from ahriman.core.configuration import Configuration
from ahriman.core.exceptions import ExitCode, MissingArchitectureError, MultipleArchitecturesError
from ahriman.models.log_handler import LogHandler
from ahriman.models.repository_id import RepositoryId
def test_call(args: argparse.Namespace, configuration: Configuration, mocker: MockerFixture) -> None:
"""
must call inside lock
"""
args.configuration = Path("")
args.log_handler = LogHandler.Console
args.quiet = False
args.report = False
mocker.patch("ahriman.application.handlers.handler.Handler.run")
configuration_mock = mocker.patch("ahriman.core.configuration.Configuration.from_path", return_value=configuration)
log_handler_mock = mocker.patch("ahriman.core.log.log_loader.LogLoader.handler", return_value=args.log_handler)
log_load_mock = mocker.patch("ahriman.core.log.log_loader.LogLoader.load")
enter_mock = mocker.patch("ahriman.application.lock.Lock.__enter__")
exit_mock = mocker.patch("ahriman.application.lock.Lock.__exit__")
_, repository_id = configuration.check_loaded()
assert Handler.call(args, repository_id)
configuration_mock.assert_called_once_with(args.configuration, repository_id)
log_handler_mock.assert_called_once_with(args.log_handler)
log_load_mock.assert_called_once_with(
repository_id,
configuration,
args.log_handler,
quiet=args.quiet,
report=args.report)
enter_mock.assert_called_once_with()
exit_mock.assert_called_once_with(None, None, None)
def test_call_exception(args: argparse.Namespace, configuration: Configuration, mocker: MockerFixture) -> None:
"""
must process exception
"""
args.configuration = Path("")
args.quiet = False
mocker.patch("ahriman.core.configuration.Configuration.from_path", side_effect=Exception)
logging_mock = mocker.patch("logging.Logger.exception")
_, repository_id = configuration.check_loaded()
assert not Handler.call(args, repository_id)
logging_mock.assert_called_once_with(pytest.helpers.anyvar(str, strict=True))
def test_call_exit_code(args: argparse.Namespace, configuration: Configuration, mocker: MockerFixture) -> None:
"""
must process exitcode exception
"""
args.configuration = Path("")
args.quiet = False
mocker.patch("ahriman.core.configuration.Configuration.from_path", side_effect=ExitCode)
logging_mock = mocker.patch("logging.Logger.exception")
_, repository_id = configuration.check_loaded()
assert not Handler.call(args, repository_id)
logging_mock.assert_not_called()
def test_execute(args: argparse.Namespace, mocker: MockerFixture) -> None:
"""
must run execution in multiple processes
"""
ids = [
RepositoryId("i686", "aur"),
RepositoryId("x86_64", "aur"),
]
mocker.patch("ahriman.application.handlers.handler.Handler.repositories_extract", return_value=ids)
starmap_mock = mocker.patch("multiprocessing.pool.Pool.starmap")
Handler.execute(args)
starmap_mock.assert_called_once_with(Handler.call, [(args, repository_id) for repository_id in ids])
def test_execute_multiple_not_supported(args: argparse.Namespace, mocker: MockerFixture) -> None:
"""
must raise an exception if multiple architectures are not supported by the handler
"""
args.command = "web"
mocker.patch("ahriman.application.handlers.handler.Handler.repositories_extract", return_value=[
RepositoryId("i686", "aur"),
RepositoryId("x86_64", "aur"),
])
mocker.patch.object(Handler, "ALLOW_MULTI_ARCHITECTURE_RUN", False)
with pytest.raises(MultipleArchitecturesError):
Handler.execute(args)
def test_execute_single(args: argparse.Namespace, mocker: MockerFixture) -> None:
"""
must run execution in current process if only one architecture supplied
"""
mocker.patch("ahriman.application.handlers.handler.Handler.repositories_extract", return_value=[
RepositoryId("x86_64", "aur"),
])
starmap_mock = mocker.patch("multiprocessing.pool.Pool.starmap")
Handler.execute(args)
starmap_mock.assert_not_called()
def test_run(args: argparse.Namespace, configuration: Configuration) -> None:
"""
must raise NotImplemented for missing method
"""
_, repository_id = configuration.check_loaded()
with pytest.raises(NotImplementedError):
Handler.run(args, repository_id, configuration, report=True)
def test_check_status() -> None:
"""
must raise exception in case if predicate is True and enabled
"""
Handler.check_status(False, True)
Handler.check_status(False, False)
Handler.check_status(False, lambda: True)
Handler.check_status(False, lambda: False)
Handler.check_status(True, True)
with pytest.raises(ExitCode):
Handler.check_status(True, False)
Handler.check_status(True, lambda: True)
with pytest.raises(ExitCode):
Handler.check_status(True, lambda: False)
def test_repositories_extract(args: argparse.Namespace, configuration: Configuration, mocker: MockerFixture) -> None:
"""
must generate list of available repositories based on flags
"""
args.architecture = "arch"
args.configuration = configuration.path
args.repository = "repo"
mocker.patch("ahriman.core.configuration.Configuration.load", new=lambda self, _: self.copy_from(configuration))
extract_mock = mocker.patch("ahriman.core.repository.Explorer.repositories_extract",
return_value=[RepositoryId("arch", "repo")])
assert Handler.repositories_extract(args) == [RepositoryId("arch", "repo")]
extract_mock.assert_called_once_with(pytest.helpers.anyvar(Configuration, True), args.repository, args.architecture)
def test_repositories_extract_empty(args: argparse.Namespace, configuration: Configuration,
mocker: MockerFixture) -> None:
"""
must raise exception if no available architectures found
"""
args.command = "config"
args.configuration = configuration.path
mocker.patch("ahriman.core.configuration.Configuration.load", new=lambda self, _: self.copy_from(configuration))
mocker.patch("ahriman.core.repository.Explorer.repositories_extract", return_value=[])
with pytest.raises(MissingArchitectureError):
Handler.repositories_extract(args)
def test_repositories_extract_systemd(args: argparse.Namespace, configuration: Configuration,
mocker: MockerFixture) -> None:
"""
must extract repository list for systemd units
"""
args.configuration = configuration.path
args.repository_id = "i686/some/repo/name"
mocker.patch("ahriman.core.configuration.Configuration.load", new=lambda self, _: self.copy_from(configuration))
extract_mock = mocker.patch("ahriman.core.repository.Explorer.repositories_extract",
return_value=[RepositoryId("i686", "some-repo-name")])
assert Handler.repositories_extract(args) == [RepositoryId("i686", "some-repo-name")]
extract_mock.assert_called_once_with(pytest.helpers.anyvar(Configuration, True), "some-repo-name", "i686")
def test_repositories_extract_systemd_with_dash(args: argparse.Namespace, configuration: Configuration,
mocker: MockerFixture) -> None:
"""
must extract repository list by using dash separated identifier
"""
args.configuration = configuration.path
args.repository_id = "i686-some-repo-name"
mocker.patch("ahriman.core.configuration.Configuration.load", new=lambda self, _: self.copy_from(configuration))
extract_mock = mocker.patch("ahriman.core.repository.Explorer.repositories_extract",
return_value=[RepositoryId("i686", "some-repo-name")])
assert Handler.repositories_extract(args) == [RepositoryId("i686", "some-repo-name")]
extract_mock.assert_called_once_with(pytest.helpers.anyvar(Configuration, True), "some-repo-name", "i686")
def test_repositories_extract_systemd_legacy(args: argparse.Namespace, configuration: Configuration,
mocker: MockerFixture) -> None:
"""
must extract repository list for systemd units in legacy format
"""
args.configuration = configuration.path
args.repository_id = "i686"
mocker.patch("ahriman.core.configuration.Configuration.load", new=lambda self, _: self.copy_from(configuration))
extract_mock = mocker.patch("ahriman.core.repository.Explorer.repositories_extract",
return_value=[RepositoryId("i686", "aur")])
assert Handler.repositories_extract(args) == [RepositoryId("i686", "aur")]
extract_mock.assert_called_once_with(pytest.helpers.anyvar(Configuration, True), None, "i686")
@@ -0,0 +1,85 @@
import argparse
import pytest
from pytest_mock import MockerFixture
from ahriman.application.application import Application
from ahriman.application.handlers.add import Add
from ahriman.core.configuration import Configuration
from ahriman.core.repository import Repository
from ahriman.models.package_source import PackageSource
from ahriman.models.pkgbuild_patch import PkgbuildPatch
def _default_args(args: argparse.Namespace) -> argparse.Namespace:
"""
default arguments for these test cases
Args:
args(argparse.Namespace): command line arguments fixture
Returns:
argparse.Namespace: generated arguments for these test cases
"""
args.package = ["ahriman"]
args.now = False
args.refresh = 0
args.source = PackageSource.Auto
args.username = "username"
args.variable = None
return args
def test_run(args: argparse.Namespace, configuration: Configuration, repository: Repository,
mocker: MockerFixture) -> None:
"""
must run command
"""
args = _default_args(args)
mocker.patch("ahriman.core.repository.Repository.load", return_value=repository)
on_start_mock = mocker.patch("ahriman.application.application.Application.on_start")
perform_mock = mocker.patch("ahriman.application.handlers.add.Add.perform_action")
_, repository_id = configuration.check_loaded()
Add.run(args, repository_id, configuration, report=False)
on_start_mock.assert_called_once_with()
perform_mock.assert_called_once_with(pytest.helpers.anyvar(int), args)
def test_perform_action(args: argparse.Namespace, application: Application, mocker: MockerFixture) -> None:
"""
must perform add action
"""
args = _default_args(args)
application_mock = mocker.patch("ahriman.application.application.Application.add")
update_mock = mocker.patch("ahriman.application.handlers.update.Update.perform_action")
Add.perform_action(application, args)
application_mock.assert_called_once_with(args.package, args.source, args.username)
update_mock.assert_not_called()
def test_perform_action_with_patches(args: argparse.Namespace, application: Application, mocker: MockerFixture) -> None:
"""
must perform add action and insert temporary patches
"""
args = _default_args(args)
args.variable = ["KEY=VALUE"]
mocker.patch("ahriman.application.application.Application.add")
patches_mock = mocker.patch("ahriman.core.status.local_client.LocalClient.package_patches_update")
Add.perform_action(application, args)
patches_mock.assert_called_once_with(args.package[0], PkgbuildPatch("KEY", "VALUE"))
def test_perform_action_with_updates(args: argparse.Namespace, application: Application, mocker: MockerFixture) -> None:
"""
must perform add action with updates after
"""
args = _default_args(args)
args.now = True
mocker.patch("ahriman.application.application.Application.add")
update_mock = mocker.patch("ahriman.application.handlers.update.Update.perform_action")
Add.perform_action(application, args)
update_mock.assert_called_once_with(application, args)
@@ -0,0 +1,84 @@
import argparse
import pytest
from pytest_mock import MockerFixture
from ahriman.application.handlers.archives import Archives
from ahriman.core.configuration import Configuration
from ahriman.core.database import SQLite
from ahriman.core.repository import Repository
from ahriman.models.action import Action
from ahriman.models.package import Package
def _default_args(args: argparse.Namespace) -> argparse.Namespace:
"""
default arguments for these test cases
Args:
args(argparse.Namespace): command line arguments fixture
Returns:
argparse.Namespace: generated arguments for these test cases
"""
args.action = Action.List
args.exit_code = False
args.info = False
args.package = "package"
return args
def test_run(args: argparse.Namespace, configuration: Configuration, repository: Repository,
package_ahriman: Package, mocker: MockerFixture) -> None:
"""
must run command
"""
args = _default_args(args)
mocker.patch("ahriman.core.repository.Repository.load", return_value=repository)
application_mock = mocker.patch("ahriman.core.repository.package_info.PackageInfo.package_archives",
return_value=[package_ahriman])
check_mock = mocker.patch("ahriman.application.handlers.handler.Handler.check_status")
print_mock = mocker.patch("ahriman.core.formatters.Printer.print")
_, repository_id = configuration.check_loaded()
Archives.run(args, repository_id, configuration, report=False)
application_mock.assert_called_once_with(args.package)
check_mock.assert_called_once_with(False, True)
print_mock.assert_called_once_with(verbose=False, log_fn=pytest.helpers.anyvar(int), separator=": ")
def test_run_empty_exception(args: argparse.Namespace, configuration: Configuration, repository: Repository,
mocker: MockerFixture) -> None:
"""
must raise ExitCode exception on empty archives result
"""
args = _default_args(args)
args.exit_code = True
mocker.patch("ahriman.core.repository.Repository.load", return_value=repository)
mocker.patch("ahriman.core.repository.package_info.PackageInfo.package_archives", return_value=[])
check_mock = mocker.patch("ahriman.application.handlers.handler.Handler.check_status")
_, repository_id = configuration.check_loaded()
Archives.run(args, repository_id, configuration, report=False)
check_mock.assert_called_once_with(True, False)
def test_imply_with_report(args: argparse.Namespace, configuration: Configuration, database: SQLite,
mocker: MockerFixture) -> None:
"""
must create application object with native reporting
"""
args = _default_args(args)
mocker.patch("ahriman.core.database.SQLite.load", return_value=database)
load_mock = mocker.patch("ahriman.core.repository.Repository.load")
_, repository_id = configuration.check_loaded()
Archives.run(args, repository_id, configuration, report=False)
load_mock.assert_called_once_with(repository_id, configuration, database, report=True, refresh_pacman_database=0)
def test_disallow_multi_architecture_run() -> None:
"""
must not allow multi architecture run
"""
assert not Archives.ALLOW_MULTI_ARCHITECTURE_RUN
@@ -0,0 +1,63 @@
import argparse
from pathlib import Path
from pytest_mock import MockerFixture
from unittest.mock import MagicMock
from ahriman.application.handlers.backup import Backup
from ahriman.core.configuration import Configuration
from ahriman.models.repository_paths import RepositoryPaths
def _default_args(args: argparse.Namespace) -> argparse.Namespace:
"""
default arguments for these test cases
Args:
args(argparse.Namespace): command line arguments fixture
Returns:
argparse.Namespace: generated arguments for these test cases
"""
args.path = Path("result.tar.gz")
return args
def test_run(args: argparse.Namespace, configuration: Configuration, mocker: MockerFixture) -> None:
"""
must run command
"""
args = _default_args(args)
mocker.patch("ahriman.application.handlers.backup.Backup.get_paths", return_value=[Path("path")])
tarfile = MagicMock()
add_mock = tarfile.__enter__.return_value = MagicMock()
mocker.patch("ahriman.application.handlers.backup.tarfile.open", return_value=tarfile)
_, repository_id = configuration.check_loaded()
Backup.run(args, repository_id, configuration, report=False)
add_mock.add.assert_called_once_with(Path("path"))
def test_get_paths(configuration: Configuration, mocker: MockerFixture) -> None:
"""
must get paths to be archived
"""
# gnupg export mock
mocker.patch("pathlib.Path.is_dir", return_value=True)
mocker.patch.object(RepositoryPaths, "root_owner", (42, 42))
getpwuid_mock = mocker.patch("ahriman.application.handlers.backup.getpwuid", return_value=MagicMock())
# well database does not exist so we override it
database_mock = mocker.patch("ahriman.core.database.SQLite.database_path", return_value=configuration.path)
paths = Backup.get_paths(configuration)
getpwuid_mock.assert_called_once_with(42)
database_mock.assert_called_once_with(configuration)
assert configuration.path in paths
assert all(path.exists() for path in paths if path.name not in (".gnupg", "cache"))
def test_disallow_multi_architecture_run() -> None:
"""
must not allow multi architecture run
"""
assert not Backup.ALLOW_MULTI_ARCHITECTURE_RUN
@@ -0,0 +1,98 @@
import argparse
import pytest
from pytest_mock import MockerFixture
from ahriman.application.handlers.change import Change
from ahriman.core.configuration import Configuration
from ahriman.core.database import SQLite
from ahriman.core.repository import Repository
from ahriman.models.action import Action
from ahriman.models.changes import Changes
def _default_args(args: argparse.Namespace) -> argparse.Namespace:
"""
default arguments for these test cases
Args:
args(argparse.Namespace): command line arguments fixture
Returns:
argparse.Namespace: generated arguments for these test cases
"""
args.action = Action.List
args.exit_code = False
args.package = "package"
return args
def test_run(args: argparse.Namespace, configuration: Configuration, repository: Repository,
mocker: MockerFixture) -> None:
"""
must run command
"""
args = _default_args(args)
mocker.patch("ahriman.core.repository.Repository.load", return_value=repository)
application_mock = mocker.patch("ahriman.core.status.local_client.LocalClient.package_changes_get",
return_value=Changes("sha", "change"))
check_mock = mocker.patch("ahriman.application.handlers.handler.Handler.check_status")
print_mock = mocker.patch("ahriman.core.formatters.Printer.print")
_, repository_id = configuration.check_loaded()
Change.run(args, repository_id, configuration, report=False)
application_mock.assert_called_once_with(args.package)
check_mock.assert_called_once_with(False, True)
print_mock.assert_called_once_with(verbose=True, log_fn=pytest.helpers.anyvar(int), separator="")
def test_run_empty_exception(args: argparse.Namespace, configuration: Configuration, repository: Repository,
mocker: MockerFixture) -> None:
"""
must raise ExitCode exception on empty changes result
"""
args = _default_args(args)
args.exit_code = True
mocker.patch("ahriman.core.repository.Repository.load", return_value=repository)
mocker.patch("ahriman.core.status.local_client.LocalClient.package_changes_get", return_value=Changes())
check_mock = mocker.patch("ahriman.application.handlers.handler.Handler.check_status")
_, repository_id = configuration.check_loaded()
Change.run(args, repository_id, configuration, report=False)
check_mock.assert_called_once_with(True, False)
def test_run_remove(args: argparse.Namespace, configuration: Configuration, repository: Repository,
mocker: MockerFixture) -> None:
"""
must remove package changes
"""
args = _default_args(args)
args.action = Action.Remove
mocker.patch("ahriman.core.repository.Repository.load", return_value=repository)
update_mock = mocker.patch("ahriman.core.status.local_client.LocalClient.package_changes_update")
_, repository_id = configuration.check_loaded()
Change.run(args, repository_id, configuration, report=False)
update_mock.assert_called_once_with(args.package, Changes())
def test_imply_with_report(args: argparse.Namespace, configuration: Configuration, database: SQLite,
mocker: MockerFixture) -> None:
"""
must create application object with native reporting
"""
args = _default_args(args)
mocker.patch("ahriman.core.database.SQLite.load", return_value=database)
load_mock = mocker.patch("ahriman.core.repository.Repository.load")
_, repository_id = configuration.check_loaded()
Change.run(args, repository_id, configuration, report=False)
load_mock.assert_called_once_with(repository_id, configuration, database, report=True, refresh_pacman_database=0)
def test_disallow_multi_architecture_run() -> None:
"""
must not allow multi architecture run
"""
assert not Change.ALLOW_MULTI_ARCHITECTURE_RUN
@@ -0,0 +1,41 @@
import argparse
from pytest_mock import MockerFixture
from ahriman.application.handlers.clean import Clean
from ahriman.core.configuration import Configuration
from ahriman.core.repository import Repository
def _default_args(args: argparse.Namespace) -> argparse.Namespace:
"""
default arguments for these test cases
Args:
args(argparse.Namespace): command line arguments fixture
Returns:
argparse.Namespace: generated arguments for these test cases
"""
args.cache = False
args.chroot = False
args.manual = False
args.packages = False
args.pacman = False
return args
def test_run(args: argparse.Namespace, configuration: Configuration, repository: Repository,
mocker: MockerFixture) -> None:
"""
must run command
"""
args = _default_args(args)
mocker.patch("ahriman.core.repository.Repository.load", return_value=repository)
application_mock = mocker.patch("ahriman.application.application.Application.clean")
on_start_mock = mocker.patch("ahriman.application.application.Application.on_start")
_, repository_id = configuration.check_loaded()
Clean.run(args, repository_id, configuration, report=False)
application_mock.assert_called_once_with(cache=False, chroot=False, manual=False, packages=False, pacman=False)
on_start_mock.assert_called_once_with()
@@ -0,0 +1,110 @@
import argparse
import pytest
from pytest_mock import MockerFixture
from ahriman.application.application import Application
from ahriman.application.handlers.copy import Copy
from ahriman.core.configuration import Configuration
from ahriman.core.database import SQLite
from ahriman.core.repository import Repository
from ahriman.models.build_status import BuildStatusEnum
from ahriman.models.package import Package
from ahriman.models.package_source import PackageSource
def _default_args(args: argparse.Namespace) -> argparse.Namespace:
"""
default arguments for these test cases
Args:
args(argparse.Namespace): command line arguments fixture
Returns:
argparse.Namespace: generated arguments for these test cases
"""
args.source = "source"
args.package = ["ahriman"]
args.exit_code = False
args.remove = False
return args
def test_run(args: argparse.Namespace, configuration: Configuration, repository: Repository,
database: SQLite, package_ahriman: Package, mocker: MockerFixture) -> None:
"""
must run command
"""
args = _default_args(args)
mocker.patch("ahriman.core.database.SQLite.load", return_value=database)
mocker.patch("ahriman.core.repository.Repository.load", return_value=repository)
mocker.patch("ahriman.core.repository.Repository.packages", return_value=[package_ahriman])
application_mock = mocker.patch("ahriman.application.handlers.copy.Copy.copy_package")
update_mock = mocker.patch("ahriman.application.application.Application.update")
remove_mock = mocker.patch("ahriman.application.application.Application.remove")
on_start_mock = mocker.patch("ahriman.application.application.Application.on_start")
_, repository_id = configuration.check_loaded()
Copy.run(args, repository_id, configuration, report=False)
application_mock.assert_called_once_with(package_ahriman, pytest.helpers.anyvar(int), pytest.helpers.anyvar(int))
update_mock.assert_called_once_with([])
remove_mock.assert_not_called()
on_start_mock.assert_called_once_with()
def test_run_remove(args: argparse.Namespace, configuration: Configuration, repository: Repository,
database: SQLite, package_ahriman: Package, mocker: MockerFixture) -> None:
"""
must run command and remove packages afterward
"""
args = _default_args(args)
args.remove = True
mocker.patch("ahriman.core.database.SQLite.load", return_value=database)
mocker.patch("ahriman.core.repository.Repository.load", return_value=repository)
mocker.patch("ahriman.core.repository.Repository.packages", return_value=[package_ahriman])
mocker.patch("ahriman.application.handlers.copy.Copy.copy_package")
mocker.patch("ahriman.application.application.Application.update")
remove_mock = mocker.patch("ahriman.application.application.Application.remove")
_, repository_id = configuration.check_loaded()
Copy.run(args, repository_id, configuration, report=False)
remove_mock.assert_called_once_with(args.package)
def test_run_empty_exception(args: argparse.Namespace, configuration: Configuration, repository: Repository,
database: SQLite, mocker: MockerFixture) -> None:
"""
must raise ExitCode exception on empty result
"""
args = _default_args(args)
args.exit_code = True
mocker.patch("ahriman.core.database.SQLite.load", return_value=database)
mocker.patch("ahriman.core.repository.Repository.load", return_value=repository)
mocker.patch("ahriman.core.repository.Repository.packages", return_value=[])
mocker.patch("ahriman.application.application.Application.update")
check_mock = mocker.patch("ahriman.application.handlers.handler.Handler.check_status")
_, repository_id = configuration.check_loaded()
Copy.run(args, repository_id, configuration, report=False)
check_mock.assert_called_once_with(True, [])
def test_copy_package(package_ahriman: Package, application: Application, mocker: MockerFixture) -> None:
"""
must copy package between repositories and its metadata
"""
add_mock = mocker.patch("ahriman.application.application.Application.add")
changes_get_mock = mocker.patch("ahriman.core.status.local_client.LocalClient.package_changes_get")
changes_update_mock = mocker.patch("ahriman.core.status.local_client.LocalClient.package_changes_update")
deps_get_mock = mocker.patch("ahriman.core.status.local_client.LocalClient.package_dependencies_get")
deps_update_mock = mocker.patch("ahriman.core.status.local_client.LocalClient.package_dependencies_update")
package_update_mock = mocker.patch("ahriman.core.status.local_client.LocalClient.package_update")
path = application.repository.paths.repository / package_ahriman.packages[package_ahriman.base].filename
Copy.copy_package(package_ahriman, application, application)
add_mock.assert_called_once_with([str(path)], PackageSource.Archive)
changes_get_mock.assert_called_once_with(package_ahriman.base)
changes_update_mock.assert_called_once_with(package_ahriman.base, changes_get_mock.return_value)
deps_get_mock.assert_called_once_with(package_ahriman.base)
deps_update_mock.assert_called_once_with(package_ahriman.base, deps_get_mock.return_value)
package_update_mock.assert_called_once_with(package_ahriman, BuildStatusEnum.Pending)
@@ -0,0 +1,78 @@
import argparse
from pytest_mock import MockerFixture
from ahriman.application.handlers.daemon import Daemon
from ahriman.core.configuration import Configuration
from ahriman.core.repository import Repository
from ahriman.models.package import Package
def _default_args(args: argparse.Namespace) -> argparse.Namespace:
"""
default arguments for these test cases
Args:
args(argparse.Namespace): command line arguments fixture
Returns:
argparse.Namespace: generated arguments for these test cases
"""
args.interval = 60 * 60 * 12
args.partitions = True
args.refresh = 0
return args
def test_run(args: argparse.Namespace, configuration: Configuration, package_ahriman: Package, repository: Repository,
mocker: MockerFixture) -> None:
"""
must run command
"""
args = _default_args(args)
mocker.patch("ahriman.core.repository.Repository.load", return_value=repository)
run_mock = mocker.patch("ahriman.application.handlers.update.Update.run")
iter_mock = mocker.patch("ahriman.application.application.updates_iterator.UpdatesIterator.__iter__",
return_value=iter([[package_ahriman.base]]))
_, repository_id = configuration.check_loaded()
Daemon.run(args, repository_id, configuration, report=True)
args.package = [package_ahriman.base]
run_mock.assert_called_once_with(args, repository_id, configuration, report=True)
iter_mock.assert_called_once_with()
def test_run_no_partitions(args: argparse.Namespace, configuration: Configuration, repository: Repository,
mocker: MockerFixture) -> None:
"""
must run command without partitioning
"""
args = _default_args(args)
args.partitions = False
mocker.patch("ahriman.core.repository.Repository.load", return_value=repository)
run_mock = mocker.patch("ahriman.application.handlers.update.Update.run")
iter_mock = mocker.patch("ahriman.application.application.updates_iterator.UpdatesIterator.__iter__",
return_value=iter([[]]))
_, repository_id = configuration.check_loaded()
Daemon.run(args, repository_id, configuration, report=True)
run_mock.assert_called_once_with(args, repository_id, configuration, report=True)
iter_mock.assert_called_once_with()
def test_run_no_updates(args: argparse.Namespace, configuration: Configuration, package_ahriman: Package,
repository: Repository, mocker: MockerFixture) -> None:
"""
must skip empty update list
"""
args = _default_args(args)
mocker.patch("ahriman.core.repository.Repository.load", return_value=repository)
run_mock = mocker.patch("ahriman.application.handlers.update.Update.run")
iter_mock = mocker.patch("ahriman.application.application.updates_iterator.UpdatesIterator.__iter__",
return_value=iter([[package_ahriman.base], None]))
_, repository_id = configuration.check_loaded()
Daemon.run(args, repository_id, configuration, report=True)
args.package = [package_ahriman.base]
run_mock.assert_called_once_with(args, repository_id, configuration, report=True)
iter_mock.assert_called_once_with()
@@ -0,0 +1,88 @@
import argparse
import pytest
from pytest_mock import MockerFixture
from ahriman.application.handlers.dump import Dump
from ahriman.core.configuration import Configuration
def _default_args(args: argparse.Namespace) -> argparse.Namespace:
"""
default arguments for these test cases
Args:
args(argparse.Namespace): command line arguments fixture
Returns:
argparse.Namespace: generated arguments for these test cases
"""
args.info = False
args.key = None
args.section = None
args.secure = True
return args
def test_run(args: argparse.Namespace, configuration: Configuration, mocker: MockerFixture) -> None:
"""
must run command
"""
args = _default_args(args)
print_mock = mocker.patch("ahriman.core.formatters.Printer.print")
application_mock = mocker.patch("ahriman.core.configuration.Configuration.dump",
return_value=configuration.dump())
_, repository_id = configuration.check_loaded()
Dump.run(args, repository_id, configuration, report=False)
application_mock.assert_called_once_with()
print_mock.assert_called()
def test_run_info(args: argparse.Namespace, configuration: Configuration, mocker: MockerFixture) -> None:
"""
must run command with info
"""
args = _default_args(args)
args.info = True
print_mock = mocker.patch("ahriman.core.formatters.Printer.print")
_, repository_id = configuration.check_loaded()
Dump.run(args, repository_id, configuration, report=False)
print_mock.assert_called()
def test_run_section(args: argparse.Namespace, configuration: Configuration, mocker: MockerFixture) -> None:
"""
must run command with filter by section
"""
args = _default_args(args)
args.section = "settings"
print_mock = mocker.patch("ahriman.core.formatters.Printer.print")
_, repository_id = configuration.check_loaded()
Dump.run(args, repository_id, configuration, report=False)
print_mock.assert_called_once_with(verbose=False, log_fn=pytest.helpers.anyvar(int), separator=" = ")
def test_run_section_key(args: argparse.Namespace, configuration: Configuration, mocker: MockerFixture) -> None:
"""
must run command with filter by section and key
"""
args = _default_args(args)
args.section = "settings"
args.key = "include"
print_mock = mocker.patch("ahriman.core.formatters.Printer.print")
application_mock = mocker.patch("ahriman.core.configuration.Configuration.dump")
_, repository_id = configuration.check_loaded()
Dump.run(args, repository_id, configuration, report=False)
application_mock.assert_not_called()
print_mock.assert_called_once_with(verbose=False, log_fn=pytest.helpers.anyvar(int), separator=": ")
def test_disallow_multi_architecture_run() -> None:
"""
must not allow multi architecture run
"""
assert not Dump.ALLOW_MULTI_ARCHITECTURE_RUN
@@ -0,0 +1,54 @@
import argparse
from pytest_mock import MockerFixture
from ahriman.application.ahriman import _parser
from ahriman.application.handlers.help import Help
from ahriman.core.configuration import Configuration
def _default_args(args: argparse.Namespace) -> argparse.Namespace:
"""
default arguments for these test cases
Args:
args(argparse.Namespace): command line arguments fixture
Returns:
argparse.Namespace: generated arguments for these test cases
"""
args.parser = _parser
args.subcommand = None
return args
def test_run(args: argparse.Namespace, configuration: Configuration, mocker: MockerFixture) -> None:
"""
must run command
"""
args = _default_args(args)
parse_mock = mocker.patch("argparse.ArgumentParser.parse_args")
_, repository_id = configuration.check_loaded()
Help.run(args, repository_id, configuration, report=False)
parse_mock.assert_called_once_with(["--help"])
def test_run_command(args: argparse.Namespace, configuration: Configuration, mocker: MockerFixture) -> None:
"""
must run command for specific subcommand
"""
args = _default_args(args)
args.subcommand = "aur-search"
parse_mock = mocker.patch("argparse.ArgumentParser.parse_args")
_, repository_id = configuration.check_loaded()
Help.run(args, repository_id, configuration, report=False)
parse_mock.assert_called_once_with(["aur-search", "--help"])
def test_disallow_multi_architecture_run() -> None:
"""
must not allow multi architecture run
"""
assert not Help.ALLOW_MULTI_ARCHITECTURE_RUN
@@ -0,0 +1,53 @@
import argparse
import pytest
from pytest_mock import MockerFixture
from ahriman.application.handlers.hold import Hold
from ahriman.core.configuration import Configuration
from ahriman.core.repository import Repository
from ahriman.models.action import Action
def _default_args(args: argparse.Namespace) -> argparse.Namespace:
"""
default arguments for these test cases
Args:
args(argparse.Namespace): command line arguments fixture
Returns:
argparse.Namespace: generated arguments for these test cases
"""
args.package = ["ahriman"]
return args
def test_run(args: argparse.Namespace, configuration: Configuration, repository: Repository,
mocker: MockerFixture) -> None:
"""
must run command
"""
args = _default_args(args)
args.action = Action.Update
mocker.patch("ahriman.core.repository.Repository.load", return_value=repository)
hold_mock = mocker.patch("ahriman.core.status.local_client.LocalClient.package_hold_update")
_, repository_id = configuration.check_loaded()
Hold.run(args, repository_id, configuration, report=False)
hold_mock.assert_called_once_with("ahriman", enabled=True)
def test_run_remove(args: argparse.Namespace, configuration: Configuration, repository: Repository,
mocker: MockerFixture) -> None:
"""
must remove held status
"""
args = _default_args(args)
args.action = Action.Remove
mocker.patch("ahriman.core.repository.Repository.load", return_value=repository)
hold_mock = mocker.patch("ahriman.core.status.local_client.LocalClient.package_hold_update")
_, repository_id = configuration.check_loaded()
Hold.run(args, repository_id, configuration, report=False)
hold_mock.assert_called_once_with("ahriman", enabled=False)
@@ -0,0 +1,43 @@
import argparse
from pytest_mock import MockerFixture
from ahriman.application.handlers.key_import import KeyImport
from ahriman.core.configuration import Configuration
from ahriman.core.repository import Repository
def _default_args(args: argparse.Namespace) -> argparse.Namespace:
"""
default arguments for these test cases
Args:
args(argparse.Namespace): command line arguments fixture
Returns:
argparse.Namespace: generated arguments for these test cases
"""
args.key = "0xE989490C"
args.key_server = "keyserver.ubuntu.com"
return args
def test_run(args: argparse.Namespace, configuration: Configuration, repository: Repository,
mocker: MockerFixture) -> None:
"""
must run command
"""
args = _default_args(args)
mocker.patch("ahriman.core.repository.Repository.load", return_value=repository)
application_mock = mocker.patch("ahriman.core.sign.gpg.GPG.key_import")
_, repository_id = configuration.check_loaded()
KeyImport.run(args, repository_id, configuration, report=False)
application_mock.assert_called_once_with(args.key_server, args.key)
def test_disallow_multi_architecture_run() -> None:
"""
must not allow multi architecture run
"""
assert not KeyImport.ALLOW_MULTI_ARCHITECTURE_RUN
@@ -0,0 +1,232 @@
import argparse
import pytest
import sys
from pathlib import Path
from pytest_mock import MockerFixture
from ahriman.application.application import Application
from ahriman.application.handlers.patch import Patch
from ahriman.core.configuration import Configuration
from ahriman.core.repository import Repository
from ahriman.models.action import Action
from ahriman.models.package import Package
from ahriman.models.pkgbuild_patch import PkgbuildPatch
def _default_args(args: argparse.Namespace) -> argparse.Namespace:
"""
default arguments for these test cases
Args:
args(argparse.Namespace): command line arguments fixture
Returns:
argparse.Namespace: generated arguments for these test cases
"""
args.package = "ahriman"
args.exit_code = False
args.remove = False
args.track = ["*.diff", "*.patch"]
args.variable = None
return args
def test_run(args: argparse.Namespace, configuration: Configuration, repository: Repository,
mocker: MockerFixture) -> None:
"""
must run command
"""
args = _default_args(args)
args.action = Action.Update
mocker.patch("ahriman.core.repository.Repository.load", return_value=repository)
patch_mock = mocker.patch("ahriman.application.handlers.patch.Patch.patch_create_from_diff",
return_value=(args.package, PkgbuildPatch(None, "patch")))
application_mock = mocker.patch("ahriman.application.handlers.patch.Patch.patch_set_create")
_, repository_id = configuration.check_loaded()
Patch.run(args, repository_id, configuration, report=False)
patch_mock.assert_called_once_with(args.package, repository_id.architecture, args.track)
application_mock.assert_called_once_with(pytest.helpers.anyvar(int), args.package, PkgbuildPatch(None, "patch"))
def test_run_function(args: argparse.Namespace, configuration: Configuration, repository: Repository,
mocker: MockerFixture) -> None:
"""
must run command with patch function flag
"""
args = _default_args(args)
args.action = Action.Update
args.patch = "patch"
args.variable = "version"
patch = PkgbuildPatch(args.variable, args.patch)
mocker.patch("ahriman.core.repository.Repository.load", return_value=repository)
patch_mock = mocker.patch("ahriman.application.handlers.patch.Patch.patch_create_from_function",
return_value=patch)
application_mock = mocker.patch("ahriman.application.handlers.patch.Patch.patch_set_create")
_, repository_id = configuration.check_loaded()
Patch.run(args, repository_id, configuration, report=False)
patch_mock.assert_called_once_with(args.variable, args.patch)
application_mock.assert_called_once_with(pytest.helpers.anyvar(int), args.package, patch)
def test_run_list(args: argparse.Namespace, configuration: Configuration, repository: Repository,
mocker: MockerFixture) -> None:
"""
must run command with list flag
"""
args = _default_args(args)
args.action = Action.List
args.variable = ["version"]
mocker.patch("ahriman.core.repository.Repository.load", return_value=repository)
application_mock = mocker.patch("ahriman.application.handlers.patch.Patch.patch_set_list")
_, repository_id = configuration.check_loaded()
Patch.run(args, repository_id, configuration, report=False)
application_mock.assert_called_once_with(pytest.helpers.anyvar(int), args.package, ["version"], False)
def test_run_remove(args: argparse.Namespace, configuration: Configuration, repository: Repository,
mocker: MockerFixture) -> None:
"""
must run command with remove flag
"""
args = _default_args(args)
args.action = Action.Remove
args.variable = ["version"]
mocker.patch("ahriman.core.repository.Repository.load", return_value=repository)
application_mock = mocker.patch("ahriman.application.handlers.patch.Patch.patch_set_remove")
_, repository_id = configuration.check_loaded()
Patch.run(args, repository_id, configuration, report=False)
application_mock.assert_called_once_with(pytest.helpers.anyvar(int), args.package, ["version"])
def test_patch_create_from_diff(package_ahriman: Package, configuration: Configuration, mocker: MockerFixture) -> None:
"""
must create patch from directory tree diff
"""
patch = PkgbuildPatch(None, "patch")
path = Path("local")
mocker.patch("pathlib.Path.mkdir")
package_mock = mocker.patch("ahriman.models.package.Package.from_build", return_value=package_ahriman)
sources_mock = mocker.patch("ahriman.core.build_tools.sources.Sources.patch_create", return_value=patch.value)
_, repository_id = configuration.check_loaded()
assert Patch.patch_create_from_diff(path, repository_id.architecture, ["*.diff"]) == (package_ahriman.base, patch)
package_mock.assert_called_once_with(path, repository_id.architecture, None)
sources_mock.assert_called_once_with(path, "*.diff")
def test_patch_create_from_function(mocker: MockerFixture) -> None:
"""
must create function patch from file
"""
patch = PkgbuildPatch("version", "patch")
read_mock = mocker.patch("pathlib.Path.read_text", return_value=patch.value)
assert Patch.patch_create_from_function(patch.key, Path("local")) == patch
read_mock.assert_called_once_with(encoding="utf8")
def test_patch_create_from_function_stdin(mocker: MockerFixture) -> None:
"""
must create function patch from stdin
"""
patch = PkgbuildPatch("version", "This is a patch")
mocker.patch.object(sys, "stdin", patch.value.splitlines())
assert Patch.patch_create_from_function(patch.key, None) == patch
def test_patch_create_from_function_strip(mocker: MockerFixture) -> None:
"""
must remove spaces at the beginning and at the end of the line
"""
patch = PkgbuildPatch("version", "This is a patch")
mocker.patch.object(sys, "stdin", ["\n"] + patch.value.splitlines() + ["\n"])
assert Patch.patch_create_from_function(patch.key, None) == patch
def test_patch_create_from_function_array(mocker: MockerFixture) -> None:
"""
must correctly read array variable
"""
patch = PkgbuildPatch("version", ["array", "patch"])
mocker.patch("pathlib.Path.read_text", return_value=f"({" ".join(patch.value)})")
assert Patch.patch_create_from_function(patch.key, Path("local")) == patch
def test_patch_set_list(application: Application, mocker: MockerFixture) -> None:
"""
must list available patches for the command
"""
get_mock = mocker.patch("ahriman.core.status.local_client.LocalClient.package_patches_get",
return_value=[PkgbuildPatch(None, "patch"), PkgbuildPatch("version", "value")])
print_mock = mocker.patch("ahriman.core.formatters.Printer.print")
check_mock = mocker.patch("ahriman.application.handlers.handler.Handler.check_status")
Patch.patch_set_list(application, "ahriman", ["version"], False)
get_mock.assert_called_once_with("ahriman", None)
print_mock.assert_called_once_with(verbose=True, log_fn=pytest.helpers.anyvar(int), separator=" = ")
check_mock.assert_called_once_with(False, [PkgbuildPatch(key='version', value='value')])
def test_patch_set_list_all(application: Application, mocker: MockerFixture) -> None:
"""
must list all available patches for the command
"""
get_mock = mocker.patch("ahriman.core.status.local_client.LocalClient.package_patches_get",
return_value=[PkgbuildPatch(None, "patch")])
print_mock = mocker.patch("ahriman.core.formatters.Printer.print")
check_mock = mocker.patch("ahriman.application.handlers.handler.Handler.check_status")
Patch.patch_set_list(application, "ahriman", None, False)
get_mock.assert_called_once_with("ahriman", None)
print_mock.assert_called_once_with(verbose=True, log_fn=pytest.helpers.anyvar(int), separator=" = ")
check_mock.assert_called_once_with(False, [PkgbuildPatch(key=None, value='patch')])
def test_patch_set_list_empty_exception(application: Application, mocker: MockerFixture) -> None:
"""
must raise ExitCode exception on empty patch list
"""
mocker.patch("ahriman.core.status.local_client.LocalClient.package_patches_get", return_value={})
check_mock = mocker.patch("ahriman.application.handlers.handler.Handler.check_status")
Patch.patch_set_list(application, "ahriman", [], True)
check_mock.assert_called_once_with(True, [])
def test_patch_set_create(application: Application, package_ahriman: Package, mocker: MockerFixture) -> None:
"""
must create patch set for the package
"""
create_mock = mocker.patch("ahriman.core.status.local_client.LocalClient.package_patches_update")
Patch.patch_set_create(application, package_ahriman.base, PkgbuildPatch("version", package_ahriman.version))
create_mock.assert_called_once_with(package_ahriman.base, PkgbuildPatch("version", package_ahriman.version))
def test_patch_set_remove(application: Application, package_ahriman: Package, mocker: MockerFixture) -> None:
"""
must remove patch set for the package
"""
remove_mock = mocker.patch("ahriman.core.status.local_client.LocalClient.package_patches_remove")
Patch.patch_set_remove(application, package_ahriman.base, ["version"])
remove_mock.assert_called_once_with(package_ahriman.base, "version")
def test_patch_set_remove_all(application: Application, package_ahriman: Package, mocker: MockerFixture) -> None:
"""
must remove all patches for the package
"""
remove_mock = mocker.patch("ahriman.core.status.local_client.LocalClient.package_patches_remove")
Patch.patch_set_remove(application, package_ahriman.base, None)
remove_mock.assert_called_once_with(package_ahriman.base, None)
def test_disallow_multi_architecture_run() -> None:
"""
must not allow multi architecture run
"""
assert not Patch.ALLOW_MULTI_ARCHITECTURE_RUN
@@ -0,0 +1,100 @@
import argparse
import pytest
from pytest_mock import MockerFixture
from ahriman.application.handlers.pkgbuild import Pkgbuild
from ahriman.core.configuration import Configuration
from ahriman.core.database import SQLite
from ahriman.core.repository import Repository
from ahriman.models.action import Action
from ahriman.models.changes import Changes
def _default_args(args: argparse.Namespace) -> argparse.Namespace:
"""
default arguments for these test cases
Args:
args(argparse.Namespace): command line arguments fixture
Returns:
argparse.Namespace: generated arguments for these test cases
"""
args.action = Action.List
args.exit_code = False
args.package = "package"
return args
def test_run(args: argparse.Namespace, configuration: Configuration, repository: Repository,
mocker: MockerFixture) -> None:
"""
must run command
"""
args = _default_args(args)
mocker.patch("ahriman.core.repository.Repository.load", return_value=repository)
application_mock = mocker.patch("ahriman.core.status.local_client.LocalClient.package_changes_get",
return_value=Changes("sha", "change", "pkgbuild content"))
check_mock = mocker.patch("ahriman.application.handlers.handler.Handler.check_status")
print_mock = mocker.patch("ahriman.core.formatters.Printer.print")
_, repository_id = configuration.check_loaded()
Pkgbuild.run(args, repository_id, configuration, report=False)
application_mock.assert_called_once_with(args.package)
check_mock.assert_called_once_with(False, True)
print_mock.assert_called_once_with(verbose=True, log_fn=pytest.helpers.anyvar(int), separator="")
def test_run_empty_exception(args: argparse.Namespace, configuration: Configuration, repository: Repository,
mocker: MockerFixture) -> None:
"""
must raise ExitCode exception on empty pkgbuild result
"""
args = _default_args(args)
args.exit_code = True
mocker.patch("ahriman.core.repository.Repository.load", return_value=repository)
mocker.patch("ahriman.core.status.local_client.LocalClient.package_changes_get", return_value=Changes())
check_mock = mocker.patch("ahriman.application.handlers.handler.Handler.check_status")
_, repository_id = configuration.check_loaded()
Pkgbuild.run(args, repository_id, configuration, report=False)
check_mock.assert_called_once_with(True, False)
def test_run_remove(args: argparse.Namespace, configuration: Configuration, repository: Repository,
mocker: MockerFixture) -> None:
"""
must remove package pkgbuild
"""
args = _default_args(args)
args.action = Action.Remove
mocker.patch("ahriman.core.repository.Repository.load", return_value=repository)
changes = Changes("sha", "change", "pkgbuild content")
mocker.patch("ahriman.core.status.local_client.LocalClient.package_changes_get", return_value=changes)
update_mock = mocker.patch("ahriman.core.status.local_client.LocalClient.package_changes_update")
_, repository_id = configuration.check_loaded()
Pkgbuild.run(args, repository_id, configuration, report=False)
update_mock.assert_called_once_with(args.package, Changes("sha", "change", None))
def test_imply_with_report(args: argparse.Namespace, configuration: Configuration, database: SQLite,
mocker: MockerFixture) -> None:
"""
must create application object with native reporting
"""
args = _default_args(args)
mocker.patch("ahriman.core.database.SQLite.load", return_value=database)
load_mock = mocker.patch("ahriman.core.repository.Repository.load")
_, repository_id = configuration.check_loaded()
Pkgbuild.run(args, repository_id, configuration, report=False)
load_mock.assert_called_once_with(repository_id, configuration, database, report=True, refresh_pacman_database=0)
def test_disallow_multi_architecture_run() -> None:
"""
must not allow multi architecture run
"""
assert not Pkgbuild.ALLOW_MULTI_ARCHITECTURE_RUN
@@ -0,0 +1,198 @@
import argparse
import pytest
from pytest_mock import MockerFixture
from unittest.mock import call as MockCall
from ahriman.application.application import Application
from ahriman.application.handlers.rebuild import Rebuild
from ahriman.core.configuration import Configuration
from ahriman.core.repository import Repository
from ahriman.models.build_status import BuildStatus, BuildStatusEnum
from ahriman.models.package import Package
from ahriman.models.packagers import Packagers
from ahriman.models.result import Result
def _default_args(args: argparse.Namespace) -> argparse.Namespace:
"""
default arguments for these test cases
Args:
args(argparse.Namespace): command line arguments fixture
Returns:
argparse.Namespace: generated arguments for these test cases
"""
args.depends_on = None
args.dry_run = False
args.from_database = False
args.exit_code = False
args.increment = True
args.status = None
args.username = "username"
return args
def test_run(args: argparse.Namespace, package_ahriman: Package, configuration: Configuration,
repository: Repository, mocker: MockerFixture) -> None:
"""
must run command
"""
args = _default_args(args)
result = Result()
result.add_updated(package_ahriman)
mocker.patch("ahriman.core.repository.Repository.load", return_value=repository)
extract_mock = mocker.patch("ahriman.application.handlers.rebuild.Rebuild.extract_packages",
return_value=[package_ahriman])
application_packages_mock = mocker.patch("ahriman.core.repository.repository.Repository.packages_depend_on",
return_value=[package_ahriman])
application_mock = mocker.patch("ahriman.application.application.Application.update", return_value=result)
check_mock = mocker.patch("ahriman.application.handlers.handler.Handler.check_status")
on_start_mock = mocker.patch("ahriman.application.application.Application.on_start")
_, repository_id = configuration.check_loaded()
Rebuild.run(args, repository_id, configuration, report=False)
extract_mock.assert_called_once_with(pytest.helpers.anyvar(int), args.status, from_database=args.from_database)
application_packages_mock.assert_called_once_with([package_ahriman], None)
application_mock.assert_called_once_with([package_ahriman], Packagers(args.username), bump_pkgrel=args.increment)
check_mock.assert_has_calls([MockCall(False, [package_ahriman]), MockCall(False, True)])
on_start_mock.assert_called_once_with()
def test_run_extract_packages(args: argparse.Namespace, configuration: Configuration, repository: Repository,
mocker: MockerFixture) -> None:
"""
must run command from database
"""
args = _default_args(args)
args.from_database = True
args.dry_run = True
mocker.patch("ahriman.core.repository.Repository.load", return_value=repository)
mocker.patch("ahriman.application.application.Application.add")
mocker.patch("ahriman.application.application.Application.print_updates")
extract_mock = mocker.patch("ahriman.application.handlers.rebuild.Rebuild.extract_packages", return_value=[])
_, repository_id = configuration.check_loaded()
Rebuild.run(args, repository_id, configuration, report=False)
extract_mock.assert_called_once_with(pytest.helpers.anyvar(int), args.status, from_database=args.from_database)
def test_run_dry_run(args: argparse.Namespace, configuration: Configuration, repository: Repository,
package_ahriman: Package, mocker: MockerFixture) -> None:
"""
must run command without update itself
"""
args = _default_args(args)
args.dry_run = True
mocker.patch("ahriman.core.repository.Repository.load", return_value=repository)
mocker.patch("ahriman.application.handlers.rebuild.Rebuild.extract_packages", return_value=[package_ahriman])
application_mock = mocker.patch("ahriman.application.application.Application.update")
check_mock = mocker.patch("ahriman.application.handlers.handler.Handler.check_status")
print_mock = mocker.patch("ahriman.application.application.Application.print_updates")
_, repository_id = configuration.check_loaded()
Rebuild.run(args, repository_id, configuration, report=False)
application_mock.assert_not_called()
check_mock.assert_called_once_with(False, [package_ahriman])
print_mock.assert_called_once_with([package_ahriman], log_fn=pytest.helpers.anyvar(int))
def test_run_filter(args: argparse.Namespace, configuration: Configuration, repository: Repository,
mocker: MockerFixture) -> None:
"""
must run command with depends on filter
"""
args = _default_args(args)
args.depends_on = ["python-aur"]
mocker.patch("ahriman.application.application.Application.update")
mocker.patch("ahriman.core.repository.Repository.load", return_value=repository)
mocker.patch("ahriman.application.handlers.rebuild.Rebuild.extract_packages", return_value=[])
application_packages_mock = mocker.patch("ahriman.core.repository.repository.Repository.packages_depend_on")
_, repository_id = configuration.check_loaded()
Rebuild.run(args, repository_id, configuration, report=False)
application_packages_mock.assert_called_once_with([], ["python-aur"])
def test_run_without_filter(args: argparse.Namespace, configuration: Configuration, repository: Repository,
mocker: MockerFixture) -> None:
"""
must run command for all packages if no filter supplied
"""
args = _default_args(args)
mocker.patch("ahriman.application.application.Application.update")
mocker.patch("ahriman.core.repository.Repository.load", return_value=repository)
mocker.patch("ahriman.application.handlers.rebuild.Rebuild.extract_packages", return_value=[])
application_packages_mock = mocker.patch("ahriman.core.repository.repository.Repository.packages_depend_on")
_, repository_id = configuration.check_loaded()
Rebuild.run(args, repository_id, configuration, report=False)
application_packages_mock.assert_called_once_with([], None)
def test_run_update_empty_exception(args: argparse.Namespace, configuration: Configuration, repository: Repository,
mocker: MockerFixture) -> None:
"""
must raise ExitCode exception on empty update list
"""
args = _default_args(args)
args.exit_code = True
args.dry_run = True
mocker.patch("ahriman.core.repository.Repository.load", return_value=repository)
mocker.patch("ahriman.application.handlers.rebuild.Rebuild.extract_packages")
mocker.patch("ahriman.core.repository.repository.Repository.packages_depend_on", return_value=[])
mocker.patch("ahriman.application.application.Application.print_updates")
check_mock = mocker.patch("ahriman.application.handlers.handler.Handler.check_status")
_, repository_id = configuration.check_loaded()
Rebuild.run(args, repository_id, configuration, report=False)
check_mock.assert_called_once_with(True, [])
def test_run_build_empty_exception(args: argparse.Namespace, configuration: Configuration, repository: Repository,
package_ahriman: Package, mocker: MockerFixture) -> None:
"""
must raise ExitCode exception on empty update result
"""
args = _default_args(args)
args.exit_code = True
mocker.patch("ahriman.core.repository.Repository.load", return_value=repository)
mocker.patch("ahriman.application.handlers.rebuild.Rebuild.extract_packages")
mocker.patch("ahriman.core.repository.repository.Repository.packages_depend_on", return_value=[package_ahriman])
mocker.patch("ahriman.application.application.Application.update", return_value=Result())
check_mock = mocker.patch("ahriman.application.handlers.handler.Handler.check_status")
_, repository_id = configuration.check_loaded()
Rebuild.run(args, repository_id, configuration, report=False)
check_mock.assert_has_calls([MockCall(True, [package_ahriman]), MockCall(True, False)])
def test_extract_packages(application: Application, mocker: MockerFixture) -> None:
"""
must extract packages from database
"""
packages_mock = mocker.patch("ahriman.core.repository.repository.Repository.packages")
Rebuild.extract_packages(application, None, from_database=False)
packages_mock.assert_called_once_with()
def test_extract_packages_by_status(application: Application, mocker: MockerFixture) -> None:
"""
must extract packages from database and filter them by status
"""
packages_mock = mocker.patch("ahriman.core.database.SQLite.packages_get", return_value=[
("package1", BuildStatus(BuildStatusEnum.Success)),
("package2", BuildStatus(BuildStatusEnum.Failed)),
])
assert Rebuild.extract_packages(application, BuildStatusEnum.Failed, from_database=True) == ["package2"]
packages_mock.assert_called_once_with(application.repository_id)
def test_extract_packages_from_database(application: Application, mocker: MockerFixture) -> None:
"""
must extract packages from database
"""
packages_mock = mocker.patch("ahriman.core.database.SQLite.packages_get")
Rebuild.extract_packages(application, None, from_database=True)
packages_mock.assert_called_once_with(application.repository_id)
@@ -0,0 +1,27 @@
import argparse
from pytest_mock import MockerFixture
from ahriman.application.handlers.reload import Reload
from ahriman.core.configuration import Configuration
from ahriman.core.repository import Repository
def test_run(args: argparse.Namespace, configuration: Configuration, repository: Repository,
mocker: MockerFixture) -> None:
"""
must run command
"""
mocker.patch("ahriman.core.repository.Repository.load", return_value=repository)
run_mock = mocker.patch("ahriman.core.status.local_client.LocalClient.configuration_reload")
_, repository_id = configuration.check_loaded()
Reload.run(args, repository_id, configuration, report=False)
run_mock.assert_called_once_with()
def test_disallow_multi_architecture_run() -> None:
"""
must not allow multi architecture run
"""
assert not Reload.ALLOW_MULTI_ARCHITECTURE_RUN
@@ -0,0 +1,37 @@
import argparse
from pytest_mock import MockerFixture
from ahriman.application.handlers.remove import Remove
from ahriman.core.configuration import Configuration
from ahriman.core.repository import Repository
def _default_args(args: argparse.Namespace) -> argparse.Namespace:
"""
default arguments for these test cases
Args:
args(argparse.Namespace): command line arguments fixture
Returns:
argparse.Namespace: generated arguments for these test cases
"""
args.package = []
return args
def test_run(args: argparse.Namespace, configuration: Configuration, repository: Repository,
mocker: MockerFixture) -> None:
"""
must run command
"""
args = _default_args(args)
mocker.patch("ahriman.core.repository.Repository.load", return_value=repository)
application_mock = mocker.patch("ahriman.application.application.Application.remove")
on_start_mock = mocker.patch("ahriman.application.application.Application.on_start")
_, repository_id = configuration.check_loaded()
Remove.run(args, repository_id, configuration, report=False)
application_mock.assert_called_once_with([])
on_start_mock.assert_called_once_with()
@@ -0,0 +1,62 @@
import argparse
import pytest
from pytest_mock import MockerFixture
from ahriman.application.handlers.remove_unknown import RemoveUnknown
from ahriman.core.configuration import Configuration
from ahriman.core.repository import Repository
from ahriman.models.package import Package
def _default_args(args: argparse.Namespace) -> argparse.Namespace:
"""
default arguments for these test cases
Args:
args(argparse.Namespace): command line arguments fixture
Returns:
argparse.Namespace: generated arguments for these test cases
"""
args.dry_run = False
return args
def test_run(args: argparse.Namespace, package_ahriman: Package, configuration: Configuration,
repository: Repository, mocker: MockerFixture) -> None:
"""
must run command
"""
args = _default_args(args)
mocker.patch("ahriman.core.repository.Repository.load", return_value=repository)
application_mock = mocker.patch("ahriman.application.application.Application.unknown",
return_value=[package_ahriman])
remove_mock = mocker.patch("ahriman.application.application.Application.remove")
on_start_mock = mocker.patch("ahriman.application.application.Application.on_start")
_, repository_id = configuration.check_loaded()
RemoveUnknown.run(args, repository_id, configuration, report=False)
application_mock.assert_called_once_with()
remove_mock.assert_called_once_with([package_ahriman])
on_start_mock.assert_called_once_with()
def test_run_dry_run(args: argparse.Namespace, configuration: Configuration, repository: Repository,
package_ahriman: Package, mocker: MockerFixture) -> None:
"""
must run simplified command
"""
args = _default_args(args)
args.dry_run = True
mocker.patch("ahriman.core.repository.Repository.load", return_value=repository)
application_mock = mocker.patch("ahriman.application.application.Application.unknown",
return_value=[package_ahriman])
remove_mock = mocker.patch("ahriman.application.application.Application.remove")
print_mock = mocker.patch("ahriman.core.formatters.Printer.print")
_, repository_id = configuration.check_loaded()
RemoveUnknown.run(args, repository_id, configuration, report=False)
application_mock.assert_called_once_with()
remove_mock.assert_not_called()
print_mock.assert_called_once_with(verbose=False, log_fn=pytest.helpers.anyvar(int), separator=": ")
@@ -0,0 +1,37 @@
import argparse
import pytest
from pytest_mock import MockerFixture
from ahriman.application.handlers.repositories import Repositories
from ahriman.core.configuration import Configuration
def _default_args(args: argparse.Namespace) -> argparse.Namespace:
"""
default arguments for these test cases
Args:
args(argparse.Namespace): command line arguments fixture
Returns:
argparse.Namespace: generated arguments for these test cases
"""
args.configuration = None # doesn't matter actually
args.id_only = False
return args
def test_run(args: argparse.Namespace, configuration: Configuration, mocker: MockerFixture) -> None:
"""
must run command
"""
args = _default_args(args)
print_mock = mocker.patch("ahriman.core.formatters.Printer.print")
_, repository_id = configuration.check_loaded()
application_mock = mocker.patch("ahriman.application.handlers.handler.Handler.repositories_extract",
return_value=[repository_id])
Repositories.run(args, repository_id, configuration, report=False)
application_mock.assert_called_once_with(pytest.helpers.anyvar(int))
print_mock.assert_called_once_with(verbose=not args.id_only, log_fn=pytest.helpers.anyvar(int), separator=": ")
@@ -0,0 +1,44 @@
import argparse
from pathlib import Path
from pytest_mock import MockerFixture
from unittest.mock import MagicMock
from ahriman.application.handlers.restore import Restore
from ahriman.core.configuration import Configuration
def _default_args(args: argparse.Namespace) -> argparse.Namespace:
"""
default arguments for these test cases
Args:
args(argparse.Namespace): command line arguments fixture
Returns:
argparse.Namespace: generated arguments for these test cases
"""
args.path = Path("result.tar.gz")
args.output = Path.cwd()
return args
def test_run(args: argparse.Namespace, configuration: Configuration, mocker: MockerFixture) -> None:
"""
must run command
"""
args = _default_args(args)
tarfile = MagicMock()
extract_mock = tarfile.__enter__.return_value = MagicMock()
mocker.patch("ahriman.application.handlers.restore.tarfile.open", return_value=tarfile)
_, repository_id = configuration.check_loaded()
Restore.run(args, repository_id, configuration, report=False)
extract_mock.extractall.assert_called_once_with(path=args.output, filter="data")
def test_disallow_multi_architecture_run() -> None:
"""
must not allow multi architecture run
"""
assert not Restore.ALLOW_MULTI_ARCHITECTURE_RUN
@@ -0,0 +1,113 @@
import argparse
import pytest
from pytest_mock import MockerFixture
from ahriman.application.application import Application
from ahriman.application.handlers.rollback import Rollback
from ahriman.core.configuration import Configuration
from ahriman.core.exceptions import UnknownPackageError
from ahriman.core.repository import Repository
from ahriman.models.package import Package
def _default_args(args: argparse.Namespace) -> argparse.Namespace:
"""
default arguments for these test cases
Args:
args(argparse.Namespace): command line arguments fixture
Returns:
argparse.Namespace: generated arguments for these test cases
"""
args.package = "ahriman"
args.version = "1.0.0-1"
args.hold = False
return args
def test_run(args: argparse.Namespace, configuration: Configuration, repository: Repository, package_ahriman: Package,
mocker: MockerFixture) -> None:
"""
must run command
"""
args = _default_args(args)
artifacts = [package.filepath for package in package_ahriman.packages.values()]
mocker.patch("ahriman.core.repository.Repository.load", return_value=repository)
on_start_mock = mocker.patch("ahriman.application.application.Application.on_start")
load_mock = mocker.patch("ahriman.application.handlers.rollback.Rollback.package_load",
return_value=package_ahriman)
artifacts_mock = mocker.patch("ahriman.application.handlers.rollback.Rollback.package_artifacts",
return_value=artifacts)
perform_mock = mocker.patch("ahriman.application.handlers.add.Add.perform_action")
hold_mock = mocker.patch("ahriman.core.status.local_client.LocalClient.package_hold_update")
_, repository_id = configuration.check_loaded()
Rollback.run(args, repository_id, configuration, report=False)
on_start_mock.assert_called_once_with()
load_mock.assert_called_once_with(pytest.helpers.anyvar(int), package_ahriman.base, args.version)
artifacts_mock.assert_called_once_with(pytest.helpers.anyvar(int), package_ahriman)
perform_mock.assert_called_once_with(pytest.helpers.anyvar(int), args)
hold_mock.assert_not_called()
def test_run_hold(args: argparse.Namespace, configuration: Configuration, repository: Repository,
package_ahriman: Package, mocker: MockerFixture) -> None:
"""
must hold package after rollback
"""
args = _default_args(args)
args.hold = True
mocker.patch("ahriman.core.repository.Repository.load", return_value=repository)
mocker.patch("ahriman.application.application.Application.on_start")
mocker.patch("ahriman.application.handlers.rollback.Rollback.package_load", return_value=package_ahriman)
mocker.patch("ahriman.application.handlers.rollback.Rollback.package_artifacts", return_value=[])
mocker.patch("ahriman.application.handlers.add.Add.perform_action")
hold_mock = mocker.patch("ahriman.core.status.local_client.LocalClient.package_hold_update")
_, repository_id = configuration.check_loaded()
Rollback.run(args, repository_id, configuration, report=False)
hold_mock.assert_called_once_with(package_ahriman.base, enabled=True)
def test_package_artifacts(application: Application, package_ahriman: Package, mocker: MockerFixture) -> None:
"""
must return package artifacts
"""
artifacts = [package.filepath for package in package_ahriman.packages.values()]
lookup_mock = mocker.patch("ahriman.core.repository.Repository.package_archives_lookup", return_value=artifacts)
assert Rollback.package_artifacts(application, package_ahriman) == artifacts
lookup_mock.assert_called_once_with(package_ahriman)
def test_package_artifacts_empty(application: Application, package_ahriman: Package,
mocker: MockerFixture) -> None:
"""
must raise UnknownPackageError if no artifacts found
"""
mocker.patch("ahriman.core.repository.Repository.package_archives_lookup", return_value=[])
with pytest.raises(UnknownPackageError):
Rollback.package_artifacts(application, package_ahriman)
def test_package_load(application: Application, package_ahriman: Package, mocker: MockerFixture) -> None:
"""
must load package from reporter
"""
package_mock = mocker.patch("ahriman.core.status.local_client.LocalClient.package_get",
return_value=[(package_ahriman, None)])
result = Rollback.package_load(application, package_ahriman.base, "2.0.0-1")
assert result.version == "2.0.0-1"
package_mock.assert_called_once_with(package_ahriman.base)
def test_package_load_unknown(application: Application, package_ahriman: Package, mocker: MockerFixture) -> None:
"""
must raise UnknownPackageError if package not found
"""
mocker.patch("ahriman.core.status.local_client.LocalClient.package_get", return_value=[])
with pytest.raises(UnknownPackageError):
Rollback.package_load(application, package_ahriman.base, package_ahriman.version)
@@ -0,0 +1,71 @@
import argparse
import pytest
from pytest_mock import MockerFixture
from ahriman.application.ahriman import _parser
from ahriman.application.handlers.run import Run
from ahriman.core.configuration import Configuration
from ahriman.core.exceptions import ExitCode
def _default_args(args: argparse.Namespace) -> argparse.Namespace:
"""
default arguments for these test cases
Args:
args(argparse.Namespace): command line arguments fixture
Returns:
argparse.Namespace: generated arguments for these test cases
"""
args.command = ["help"]
args.parser = _parser
return args
def test_run(args: argparse.Namespace, configuration: Configuration, mocker: MockerFixture) -> None:
"""
must run command
"""
args = _default_args(args)
application_mock = mocker.patch("ahriman.application.handlers.run.Run.run_command")
_, repository_id = configuration.check_loaded()
Run.run(args, repository_id, configuration, report=False)
application_mock.assert_called_once_with(["help"], pytest.helpers.anyvar(int))
def test_run_failed(args: argparse.Namespace, configuration: Configuration, mocker: MockerFixture) -> None:
"""
must run commands until success
"""
args = _default_args(args)
args.command = ["help", "config"]
application_mock = mocker.patch("ahriman.application.handlers.run.Run.run_command", return_value=False)
_, repository_id = configuration.check_loaded()
with pytest.raises(ExitCode):
Run.run(args, repository_id, configuration, report=False)
application_mock.assert_called_once_with(["help"], pytest.helpers.anyvar(int))
def test_run_command(mocker: MockerFixture) -> None:
"""
must correctly run external command
"""
# because of dynamic load we need to patch exact instance of the object
parser = _parser()
subparser = next((action for action in parser._actions if isinstance(action, argparse._SubParsersAction)), None)
action = subparser.choices["help"]
execute_mock = mocker.patch.object(action.get_default("handler"), "execute")
Run.run_command(["help"], parser)
execute_mock.assert_called_once_with(pytest.helpers.anyvar(int))
def test_disallow_multi_architecture_run() -> None:
"""
must not allow multi architecture run
"""
assert not Run.ALLOW_MULTI_ARCHITECTURE_RUN
@@ -0,0 +1,147 @@
import argparse
import dataclasses
import pytest
from pytest_mock import MockerFixture
from unittest.mock import call as MockCall
from ahriman.application.handlers.search import Search
from ahriman.core.configuration import Configuration
from ahriman.core.exceptions import OptionError
from ahriman.core.repository import Repository
from ahriman.models.aur_package import AURPackage
def _default_args(args: argparse.Namespace) -> argparse.Namespace:
"""
default arguments for these test cases
Args:
args(argparse.Namespace): command line arguments fixture
Returns:
argparse.Namespace: generated arguments for these test cases
"""
args.search = ["ahriman"]
args.exit_code = False
args.info = False
args.sort_by = "name"
return args
def test_run(args: argparse.Namespace, configuration: Configuration, repository: Repository,
aur_package_ahriman: AURPackage, mocker: MockerFixture) -> None:
"""
must run command
"""
args = _default_args(args)
mocker.patch("ahriman.core.repository.Repository.load", return_value=repository)
aur_search_mock = mocker.patch("ahriman.core.alpm.remote.AUR.multisearch", return_value=[aur_package_ahriman])
official_search_mock = mocker.patch("ahriman.core.alpm.remote.Official.multisearch",
return_value=[aur_package_ahriman])
check_mock = mocker.patch("ahriman.application.handlers.handler.Handler.check_status")
print_mock = mocker.patch("ahriman.core.formatters.Printer.print")
_, repository_id = configuration.check_loaded()
Search.run(args, repository_id, configuration, report=False)
aur_search_mock.assert_called_once_with("ahriman")
official_search_mock.assert_called_once_with("ahriman")
check_mock.assert_called_once_with(False, True)
print_mock.assert_has_calls([
MockCall(verbose=False, log_fn=pytest.helpers.anyvar(int), separator=": "),
MockCall(verbose=False, log_fn=pytest.helpers.anyvar(int), separator=": "),
])
def test_run_empty_exception(args: argparse.Namespace, configuration: Configuration, repository: Repository,
mocker: MockerFixture) -> None:
"""
must raise ExitCode exception on empty result list
"""
args = _default_args(args)
args.exit_code = True
mocker.patch("ahriman.core.alpm.remote.AUR.multisearch", return_value=[])
mocker.patch("ahriman.core.alpm.remote.Official.multisearch", return_value=[])
mocker.patch("ahriman.core.formatters.Printer.print")
mocker.patch("ahriman.core.repository.Repository.load", return_value=repository)
check_mock = mocker.patch("ahriman.application.handlers.handler.Handler.check_status")
_, repository_id = configuration.check_loaded()
Search.run(args, repository_id, configuration, report=False)
check_mock.assert_called_once_with(True, False)
def test_run_sort(args: argparse.Namespace, configuration: Configuration, repository: Repository,
aur_package_ahriman: AURPackage, mocker: MockerFixture) -> None:
"""
must run command with sorting
"""
args = _default_args(args)
mocker.patch("ahriman.core.alpm.remote.AUR.multisearch", return_value=[aur_package_ahriman])
mocker.patch("ahriman.core.alpm.remote.Official.multisearch", return_value=[])
mocker.patch("ahriman.core.repository.Repository.load", return_value=repository)
sort_mock = mocker.patch("ahriman.application.handlers.search.Search.sort")
_, repository_id = configuration.check_loaded()
Search.run(args, repository_id, configuration, report=False)
sort_mock.assert_has_calls([
MockCall([], "name"), MockCall().__iter__(),
MockCall([aur_package_ahriman], "name"), MockCall().__iter__()
])
def test_run_sort_by(args: argparse.Namespace, configuration: Configuration, repository: Repository,
aur_package_ahriman: AURPackage, mocker: MockerFixture) -> None:
"""
must run command with sorting by specified field
"""
args = _default_args(args)
args.sort_by = "field"
mocker.patch("ahriman.core.alpm.remote.AUR.multisearch", return_value=[aur_package_ahriman])
mocker.patch("ahriman.core.alpm.remote.Official.multisearch", return_value=[])
mocker.patch("ahriman.core.repository.Repository.load", return_value=repository)
sort_mock = mocker.patch("ahriman.application.handlers.search.Search.sort")
_, repository_id = configuration.check_loaded()
Search.run(args, repository_id, configuration, report=False)
sort_mock.assert_has_calls([
MockCall([], "field"), MockCall().__iter__(),
MockCall([aur_package_ahriman], "field"), MockCall().__iter__()
])
def test_sort(aur_package_ahriman: AURPackage) -> None:
"""
must sort package list
"""
another = dataclasses.replace(aur_package_ahriman, name="1", package_base="base")
# sort by name
assert Search.sort([aur_package_ahriman, another], "name") == [another, aur_package_ahriman]
# sort by another field
assert Search.sort([aur_package_ahriman, another], "package_base") == [aur_package_ahriman, another]
# sort by field with the same values
assert Search.sort([aur_package_ahriman, another], "version") == [another, aur_package_ahriman]
def test_sort_exception(aur_package_ahriman: AURPackage) -> None:
"""
must raise an exception on unknown sorting field
"""
with pytest.raises(OptionError):
Search.sort([aur_package_ahriman], "random_field")
def test_disallow_multi_architecture_run() -> None:
"""
must not allow multi architecture run
"""
assert not Search.ALLOW_MULTI_ARCHITECTURE_RUN
def test_sort_fields(aur_package_ahriman: AURPackage) -> None:
"""
must store valid field list which are allowed to be used for sorting
"""
expected = {field.name for field in dataclasses.fields(AURPackage)}
assert all(field in expected for field in Search.SORT_FIELDS)
assert all(not isinstance(getattr(aur_package_ahriman, field), list) for field in Search.SORT_FIELDS)
@@ -0,0 +1,68 @@
import argparse
import pytest
from pytest_mock import MockerFixture
from ahriman import __version__
from ahriman.application.handlers.service_updates import ServiceUpdates
from ahriman.core.configuration import Configuration
from ahriman.core.repository import Repository
from ahriman.models.package import Package
def _default_args(args: argparse.Namespace) -> argparse.Namespace:
"""
default arguments for these test cases
Args:
args(argparse.Namespace): command line arguments fixture
Returns:
argparse.Namespace: generated arguments for these test cases
"""
args.exit_code = False
return args
def test_run(args: argparse.Namespace, configuration: Configuration, repository: Repository,
package_ahriman: Package, mocker: MockerFixture) -> None:
"""
must run command
"""
package_ahriman.version = "0.0.0-1"
args = _default_args(args)
mocker.patch("ahriman.core.repository.Repository.load", return_value=repository)
package_mock = mocker.patch("ahriman.models.package.Package.from_aur", return_value=package_ahriman)
application_mock = mocker.patch("ahriman.core.formatters.Printer.print")
check_mock = mocker.patch("ahriman.application.handlers.handler.Handler.check_status")
_, repository_id = configuration.check_loaded()
ServiceUpdates.run(args, repository_id, configuration, report=False)
package_mock.assert_called_once_with(package_ahriman.base, None)
application_mock.assert_called_once_with(verbose=True, log_fn=pytest.helpers.anyvar(int), separator=" -> ")
check_mock.assert_called_once_with(args.exit_code, False)
def test_run_skip(args: argparse.Namespace, configuration: Configuration, repository: Repository,
package_ahriman: Package, mocker: MockerFixture) -> None:
"""
must do not perform any actions if package is up-to-date
"""
package_ahriman.version = f"{__version__}-1"
args = _default_args(args)
mocker.patch("ahriman.core.repository.Repository.load", return_value=repository)
mocker.patch("ahriman.models.package.Package.from_aur", return_value=package_ahriman)
application_mock = mocker.patch("ahriman.core.formatters.Printer.print")
check_mock = mocker.patch("ahriman.application.handlers.handler.Handler.check_status")
_, repository_id = configuration.check_loaded()
ServiceUpdates.run(args, repository_id, configuration, report=False)
application_mock.assert_not_called()
check_mock.assert_not_called()
def test_disallow_multi_architecture_run() -> None:
"""
must not allow multi architecture run
"""
assert not ServiceUpdates.ALLOW_MULTI_ARCHITECTURE_RUN
@@ -0,0 +1,286 @@
import argparse
import pytest
from pathlib import Path
from pytest_mock import MockerFixture
from typing import Any
from unittest.mock import call as MockCall
from urllib.parse import quote_plus as url_encode
from ahriman.application.handlers.setup import Setup
from ahriman.core.configuration import Configuration
from ahriman.core.database import SQLite
from ahriman.core.exceptions import MissingArchitectureError
from ahriman.core.repository import Repository
from ahriman.models.repository_id import RepositoryId
from ahriman.models.repository_paths import RepositoryPaths
from ahriman.models.sign_settings import SignSettings
def _default_args(args: argparse.Namespace) -> argparse.Namespace:
"""
default arguments for these test cases
Args:
args(argparse.Namespace): command line arguments fixture
Returns:
argparse.Namespace: generated arguments for these test cases
"""
args.architecture = "x86_64"
args.build_as_user = "ahriman"
args.from_configuration = Path("/usr/share/devtools/pacman.conf.d/extra.conf")
args.generate_salt = True
args.makeflags_jobs = True
args.mirror = "mirror"
args.multilib = True
args.packager = "ahriman bot <ahriman@example.com>"
args.repository = "aur"
args.server = None
args.sign_key = "key"
args.sign_target = [SignSettings.Packages]
args.web_port = 8080
args.web_unix_socket = Path("/var/lib/ahriman/ahriman-web.sock")
return args
def test_run(args: argparse.Namespace, configuration: Configuration, repository: Repository,
database: SQLite, repository_paths: RepositoryPaths, mocker: MockerFixture) -> None:
"""
must run command
"""
args = _default_args(args)
mocker.patch("ahriman.core.database.SQLite.load", return_value=database)
mocker.patch("ahriman.core.repository.Repository.load", return_value=repository)
ahriman_configuration_mock = mocker.patch("ahriman.application.handlers.setup.Setup.configuration_create_ahriman")
devtools_configuration_mock = mocker.patch("ahriman.application.handlers.setup.Setup.configuration_create_devtools")
makepkg_configuration_mock = mocker.patch("ahriman.application.handlers.setup.Setup.configuration_create_makepkg")
sudo_configuration_mock = mocker.patch("ahriman.application.handlers.setup.Setup.configuration_create_sudo")
executable_mock = mocker.patch("ahriman.application.handlers.setup.Setup.executable_create")
init_mock = mocker.patch("ahriman.core.alpm.repo.Repo.init")
owner_guard_mock = mocker.patch("ahriman.models.repository_paths.RepositoryPaths.preserve_owner")
_, repository_id = configuration.check_loaded()
Setup.run(args, repository_id, configuration, report=False)
owner_guard_mock.assert_called_once_with()
ahriman_configuration_mock.assert_called_once_with(args, repository_id, configuration)
devtools_configuration_mock.assert_called_once_with(
repository_id, args.from_configuration, args.mirror, args.multilib, f"file://{repository_paths.repository}")
makepkg_configuration_mock.assert_called_once_with(args.packager, args.makeflags_jobs, repository_paths)
sudo_configuration_mock.assert_called_once_with(repository_paths, repository_id)
executable_mock.assert_called_once_with(repository_paths, repository_id)
init_mock.assert_called_once_with()
def test_run_no_architecture_or_repository(configuration: Configuration) -> None:
"""
must raise MissingArchitectureError if either architecture or repository are not supplied
"""
_, repository_id = configuration.check_loaded()
args = argparse.Namespace(architecture=None, command="service-setup", repository=None)
with pytest.raises(MissingArchitectureError):
Setup.run(args, repository_id, configuration, report=False)
args = argparse.Namespace(architecture=[repository_id.architecture], command="service-setup", repository=None)
with pytest.raises(MissingArchitectureError):
Setup.run(args, repository_id, configuration, report=False)
args = argparse.Namespace(architecture=None, command="service-setup", repository=[repository_id.name])
with pytest.raises(MissingArchitectureError):
Setup.run(args, repository_id, configuration, report=False)
def test_run_with_server(args: argparse.Namespace, configuration: Configuration, repository: Repository,
database: SQLite, mocker: MockerFixture) -> None:
"""
must run command with server specified
"""
args = _default_args(args)
args.server = "server"
mocker.patch("ahriman.core.database.SQLite.load", return_value=database)
mocker.patch("ahriman.core.repository.Repository.load", return_value=repository)
mocker.patch("ahriman.application.handlers.setup.Setup.configuration_create_ahriman")
mocker.patch("ahriman.application.handlers.setup.Setup.configuration_create_makepkg")
mocker.patch("ahriman.application.handlers.setup.Setup.configuration_create_sudo")
mocker.patch("ahriman.application.handlers.setup.Setup.executable_create")
mocker.patch("ahriman.core.alpm.repo.Repo.init")
devtools_configuration_mock = mocker.patch("ahriman.application.handlers.setup.Setup.configuration_create_devtools")
_, repository_id = configuration.check_loaded()
Setup.run(args, repository_id, configuration, report=False)
devtools_configuration_mock.assert_called_once_with(
repository_id, args.from_configuration, args.mirror, args.multilib, "server")
def test_build_command(repository_id: RepositoryId) -> None:
"""
must generate correct build command name
"""
path = Path("local")
build_command = Setup.build_command(path, repository_id)
assert build_command.name == f"{repository_id.name}-{repository_id.architecture}-build"
assert build_command.parent == path
def test_configuration_create_ahriman(args: argparse.Namespace, configuration: Configuration,
repository_paths: RepositoryPaths, mocker: MockerFixture) -> None:
"""
must create configuration for the service
"""
args = _default_args(args)
mocker.patch("pathlib.Path.open")
set_option_mock = mocker.patch("ahriman.core.configuration.Configuration.set_option")
write_mock = mocker.patch("ahriman.core.configuration.Configuration.write")
remove_mock = mocker.patch("pathlib.Path.unlink", autospec=True)
_, repository_id = configuration.check_loaded()
command = Setup.build_command(repository_paths.root, repository_id)
Setup.configuration_create_ahriman(args, repository_id, configuration)
set_option_mock.assert_has_calls([
MockCall(Configuration.section_name("build", repository_id.name, repository_id.architecture), "build_command",
str(command)),
MockCall("repository", "name", repository_id.name),
MockCall(Configuration.section_name("build", repository_id.name, repository_id.architecture),
"makechrootpkg_flags", f"-U {args.build_as_user}"),
MockCall(Configuration.section_name(
"alpm", repository_id.name, repository_id.architecture), "mirror", args.mirror),
MockCall(Configuration.section_name("sign", repository_id.name, repository_id.architecture), "target",
" ".join([target.name.lower() for target in args.sign_target])),
MockCall(Configuration.section_name("sign", repository_id.name, repository_id.architecture), "key",
args.sign_key),
MockCall("web", "port", str(args.web_port)),
MockCall("status", "address", f"http://127.0.0.1:{str(args.web_port)}"),
MockCall("web", "unix_socket", str(args.web_unix_socket)),
MockCall("status", "address", f"http+unix://{url_encode(str(args.web_unix_socket))}"),
MockCall("auth", "salt", pytest.helpers.anyvar(str, strict=True)),
])
write_mock.assert_called_once_with(pytest.helpers.anyvar(int))
remove_mock.assert_called_once_with(configuration.include / "00-setup-overrides.ini", missing_ok=True)
def test_configuration_create_ahriman_no_multilib(args: argparse.Namespace, configuration: Configuration,
mocker: MockerFixture) -> None:
"""
must create configuration for the service without multilib repository
"""
args = _default_args(args)
args.multilib = False
mocker.patch("pathlib.Path.open")
mocker.patch("ahriman.core.configuration.Configuration.write")
set_option_mock = mocker.patch("ahriman.core.configuration.Configuration.set_option")
_, repository_id = configuration.check_loaded()
Setup.configuration_create_ahriman(args, repository_id, configuration)
set_option_mock.assert_has_calls([
MockCall(Configuration.section_name("alpm", repository_id.name, repository_id.architecture), "mirror",
args.mirror),
]) # non-strict check called intentionally
def test_configuration_create_devtools(args: argparse.Namespace, configuration: Configuration,
mocker: MockerFixture) -> None:
"""
must create configuration for the devtools
"""
args = _default_args(args)
mocker.patch("pathlib.Path.open")
mocker.patch("ahriman.core.configuration.Configuration.set")
add_section_mock = mocker.patch("ahriman.core.configuration.Configuration.add_section")
write_mock = mocker.patch("ahriman.core.configuration.Configuration.write")
_, repository_id = configuration.check_loaded()
Setup.configuration_create_devtools(repository_id, args.from_configuration, None, args.multilib, "server")
add_section_mock.assert_has_calls([MockCall("multilib"), MockCall(repository_id.name)])
write_mock.assert_called_once_with(pytest.helpers.anyvar(int))
def test_configuration_create_devtools_mirror(args: argparse.Namespace, configuration: Configuration,
mocker: MockerFixture) -> None:
"""
must create configuration for the devtools with mirror set explicitly
"""
def get(section: str, key: str, **kwargs: Any) -> Any:
if section == "core" and key == "Include":
return str(Setup.MIRRORLIST_PATH)
return kwargs["fallback"]
args = _default_args(args)
mocker.patch("pathlib.Path.open")
mocker.patch("ahriman.core.configuration.Configuration.set")
mocker.patch("ahriman.core.configuration.Configuration.write")
mocker.patch("ahriman.core.configuration.Configuration.sections", return_value=["core", "extra"])
get_mock = mocker.patch("ahriman.core.configuration.Configuration.get", side_effect=get)
remove_option_mock = mocker.patch("ahriman.core.configuration.Configuration.remove_option")
set_option_mock = mocker.patch("ahriman.core.configuration.Configuration.set_option")
_, repository_id = configuration.check_loaded()
Setup.configuration_create_devtools(repository_id, args.from_configuration, args.mirror, args.multilib, "server")
get_mock.assert_has_calls([MockCall("core", "Include", fallback=None), MockCall("extra", "Include", fallback=None)])
remove_option_mock.assert_called_once_with("core", "Include")
set_option_mock.assert_has_calls([MockCall("core", "Server", args.mirror)]) # non-strict check called intentionally
def test_configuration_create_devtools_no_multilib(args: argparse.Namespace, configuration: Configuration,
mocker: MockerFixture) -> None:
"""
must create configuration for the devtools without multilib
"""
args = _default_args(args)
mocker.patch("pathlib.Path.open")
mocker.patch("ahriman.core.configuration.Configuration.set")
write_mock = mocker.patch("ahriman.core.configuration.Configuration.write")
_, repository_id = configuration.check_loaded()
Setup.configuration_create_devtools(repository_id, args.from_configuration, args.mirror, False, "server")
write_mock.assert_called_once_with(pytest.helpers.anyvar(int))
def test_configuration_create_makepkg(args: argparse.Namespace, repository_paths: RepositoryPaths,
passwd: Any, mocker: MockerFixture) -> None:
"""
must create makepkg configuration
"""
args = _default_args(args)
mocker.patch("ahriman.application.handlers.setup.getpwuid", return_value=passwd)
write_text_mock = mocker.patch("pathlib.Path.write_text", autospec=True)
Setup.configuration_create_makepkg(args.packager, args.makeflags_jobs, repository_paths)
write_text_mock.assert_called_once_with(
Path("home") / ".makepkg.conf", pytest.helpers.anyvar(str, True), encoding="utf8")
def test_configuration_create_sudo(configuration: Configuration, repository_paths: RepositoryPaths,
mocker: MockerFixture) -> None:
"""
must create sudo configuration
"""
chmod_text_mock = mocker.patch("pathlib.Path.chmod")
write_text_mock = mocker.patch("pathlib.Path.write_text")
_, repository_id = configuration.check_loaded()
Setup.configuration_create_sudo(repository_paths, repository_id)
chmod_text_mock.assert_called_once_with(0o400)
write_text_mock.assert_called_once_with(pytest.helpers.anyvar(str, True), encoding="utf8")
def test_executable_create(configuration: Configuration, repository_paths: RepositoryPaths,
mocker: MockerFixture) -> None:
"""
must create executable
"""
symlink_mock = mocker.patch("pathlib.Path.symlink_to")
unlink_mock = mocker.patch("pathlib.Path.unlink")
_, repository_id = configuration.check_loaded()
Setup.executable_create(repository_paths, repository_id)
symlink_mock.assert_called_once_with(Setup.ARCHBUILD_COMMAND_PATH)
unlink_mock.assert_called_once_with(missing_ok=True)
def test_disallow_multi_architecture_run() -> None:
"""
must not allow multi architecture run
"""
assert not Setup.ALLOW_MULTI_ARCHITECTURE_RUN
@@ -0,0 +1,78 @@
import argparse
import pytest
from pytest_mock import MockerFixture
from ahriman.application.handlers.shell import Shell
from ahriman.core.configuration import Configuration
from ahriman.core.repository import Repository
def _default_args(args: argparse.Namespace) -> argparse.Namespace:
"""
default arguments for these test cases
Args:
args(argparse.Namespace): command line arguments fixture
Returns:
argparse.Namespace: generated arguments for these test cases
"""
args.code = None
args.verbose = False
return args
def test_run(args: argparse.Namespace, configuration: Configuration, repository: Repository,
mocker: MockerFixture) -> None:
"""
must run command
"""
args = _default_args(args)
mocker.patch("ahriman.core.repository.Repository.load", return_value=repository)
application_mock = mocker.patch("ahriman.application.interactive_shell.InteractiveShell.interact")
_, repository_id = configuration.check_loaded()
Shell.run(args, repository_id, configuration, report=False)
application_mock.assert_called_once_with()
def test_run_eval(args: argparse.Namespace, configuration: Configuration, repository: Repository,
mocker: MockerFixture) -> None:
"""
must run command via eval
"""
args = _default_args(args)
args.code = """print("hello world")"""
mocker.patch("ahriman.core.repository.Repository.load", return_value=repository)
application_mock = mocker.patch("ahriman.application.interactive_shell.InteractiveShell.runcode")
_, repository_id = configuration.check_loaded()
Shell.run(args, repository_id, configuration, report=False)
application_mock.assert_called_once_with(args.code)
def test_run_verbose(args: argparse.Namespace, configuration: Configuration, repository: Repository,
mocker: MockerFixture) -> None:
"""
must run command with verbose option
"""
args = _default_args(args)
args.verbose = True
mocker.patch("ahriman.core.repository.Repository.load", return_value=repository)
read_mock = mocker.patch("pathlib.Path.read_text", return_value="")
print_mock = mocker.patch("ahriman.core.formatters.Printer.print")
application_mock = mocker.patch("ahriman.application.interactive_shell.InteractiveShell.interact")
_, repository_id = configuration.check_loaded()
Shell.run(args, repository_id, configuration, report=False)
application_mock.assert_called_once_with()
read_mock.assert_called_once_with(encoding="utf8")
print_mock.assert_called_once_with(verbose=False, log_fn=pytest.helpers.anyvar(int), separator=": ")
def test_disallow_multi_architecture_run() -> None:
"""
must not allow multi architecture run
"""
assert not Shell.ALLOW_MULTI_ARCHITECTURE_RUN
@@ -0,0 +1,35 @@
import argparse
from pytest_mock import MockerFixture
from ahriman.application.handlers.sign import Sign
from ahriman.core.configuration import Configuration
from ahriman.core.repository import Repository
def _default_args(args: argparse.Namespace) -> argparse.Namespace:
"""
default arguments for these test cases
Args:
args(argparse.Namespace): command line arguments fixture
Returns:
argparse.Namespace: generated arguments for these test cases
"""
args.package = []
return args
def test_run(args: argparse.Namespace, configuration: Configuration, repository: Repository,
mocker: MockerFixture) -> None:
"""
must run command
"""
args = _default_args(args)
mocker.patch("ahriman.core.repository.Repository.load", return_value=repository)
application_mock = mocker.patch("ahriman.application.application.Application.sign")
_, repository_id = configuration.check_loaded()
Sign.run(args, repository_id, configuration, report=False)
application_mock.assert_called_once_with([])
@@ -0,0 +1,203 @@
import argparse
import pytest
from pathlib import Path
from pytest_mock import MockerFixture
from unittest.mock import call as MockCall
from ahriman.application.handlers.statistics import Statistics
from ahriman.core.configuration import Configuration
from ahriman.core.repository import Repository
from ahriman.core.utils import pretty_datetime, utcnow
from ahriman.models.event import Event, EventType
from ahriman.models.package import Package
from ahriman.models.repository_stats import RepositoryStats
def _default_args(args: argparse.Namespace) -> argparse.Namespace:
"""
default arguments for these test cases
Args:
args(argparse.Namespace): command line arguments fixture
Returns:
argparse.Namespace: generated arguments for these test cases
"""
args.chart = None
args.event = EventType.PackageUpdated
args.from_date = None
args.limit = -1
args.offset = 0
args.package = None
args.to_date = None
return args
def test_run(args: argparse.Namespace, configuration: Configuration, repository: Repository,
mocker: MockerFixture) -> None:
"""
must run command
"""
args = _default_args(args)
events = [Event("1", "1"), Event("2", "2")]
stats = RepositoryStats(bases=1, packages=2, archive_size=3, installed_size=4)
mocker.patch("ahriman.core.repository.Repository.load", return_value=repository)
events_mock = mocker.patch("ahriman.core.status.local_client.LocalClient.event_get", return_value=events)
stats_mock = mocker.patch("ahriman.core.status.client.Client.statistics", return_value=stats)
application_mock = mocker.patch("ahriman.application.handlers.statistics.Statistics.stats_per_package")
_, repository_id = configuration.check_loaded()
Statistics.run(args, repository_id, configuration, report=False)
events_mock.assert_called_once_with(args.event, args.package, None, None, args.limit, args.offset)
stats_mock.assert_called_once_with()
application_mock.assert_called_once_with(args.event, events, args.chart)
def test_run_for_package(args: argparse.Namespace, configuration: Configuration, repository: Repository,
package_ahriman: Package, mocker: MockerFixture) -> None:
"""
must run command for specific package
"""
args = _default_args(args)
args.package = package_ahriman.base
events = [Event("1", "1"), Event("2", "2")]
mocker.patch("ahriman.core.repository.Repository.load", return_value=repository)
events_mock = mocker.patch("ahriman.core.status.local_client.LocalClient.event_get", return_value=events)
application_mock = mocker.patch("ahriman.application.handlers.statistics.Statistics.stats_for_package")
_, repository_id = configuration.check_loaded()
Statistics.run(args, repository_id, configuration, report=False)
events_mock.assert_called_once_with(args.event, args.package, None, None, args.limit, args.offset)
application_mock.assert_called_once_with(args.event, events, args.chart)
def test_run_convert_from_date(args: argparse.Namespace, configuration: Configuration, repository: Repository,
mocker: MockerFixture) -> None:
"""
must convert from date
"""
args = _default_args(args)
date = utcnow()
args.from_date = date.isoformat()
mocker.patch("ahriman.core.repository.Repository.load", return_value=repository)
mocker.patch("ahriman.application.handlers.statistics.Statistics.stats_per_package")
events_mock = mocker.patch("ahriman.core.status.local_client.LocalClient.event_get", return_value=[])
_, repository_id = configuration.check_loaded()
Statistics.run(args, repository_id, configuration, report=False)
events_mock.assert_called_once_with(args.event, args.package, date.timestamp(), None, args.limit, args.offset)
def test_run_convert_to_date(args: argparse.Namespace, configuration: Configuration, repository: Repository,
mocker: MockerFixture) -> None:
"""
must convert to date
"""
args = _default_args(args)
date = utcnow()
args.to_date = date.isoformat()
mocker.patch("ahriman.core.repository.Repository.load", return_value=repository)
mocker.patch("ahriman.application.handlers.statistics.Statistics.stats_per_package")
events_mock = mocker.patch("ahriman.core.status.local_client.LocalClient.event_get", return_value=[])
_, repository_id = configuration.check_loaded()
Statistics.run(args, repository_id, configuration, report=False)
events_mock.assert_called_once_with(args.event, args.package, None, date.timestamp(), args.limit, args.offset)
def test_event_stats(mocker: MockerFixture) -> None:
"""
must print event stats
"""
print_mock = mocker.patch("ahriman.core.formatters.Printer.print")
events = [Event("event", "1"), Event("event", "2", took=42.0)]
Statistics.event_stats("event", events)
print_mock.assert_called_once_with(verbose=True, log_fn=pytest.helpers.anyvar(int), separator=": ")
def test_plot_packages(mocker: MockerFixture) -> None:
"""
must plot chart for packages
"""
plot_mock = mocker.patch("matplotlib.pyplot.bar")
save_mock = mocker.patch("matplotlib.pyplot.savefig")
local = Path("local")
Statistics.plot_packages("event", {"1": 1, "2": 2}, local)
plot_mock.assert_called_once_with(["1", "2"], [1, 2])
save_mock.assert_called_once_with(local)
def test_plot_times(mocker: MockerFixture) -> None:
"""
must plot chart for durations
"""
plot_mock = mocker.patch("matplotlib.pyplot.plot")
save_mock = mocker.patch("matplotlib.pyplot.savefig")
local = Path("local")
Statistics.plot_times("event", [
Event("", "", created=1, took=2),
Event("", "", created=3, took=4),
], local)
plot_mock.assert_called_once_with((pretty_datetime(1), pretty_datetime(3)), (2, 4))
save_mock.assert_called_once_with(local)
def test_stats_for_package(mocker: MockerFixture) -> None:
"""
must print statistics for the package
"""
events = [Event("event", "1"), Event("event", "1")]
events_mock = mocker.patch("ahriman.application.handlers.statistics.Statistics.event_stats")
chart_plot = mocker.patch("ahriman.application.handlers.statistics.Statistics.plot_times")
Statistics.stats_for_package("event", events, None)
events_mock.assert_called_once_with("event", events)
chart_plot.assert_not_called()
def test_stats_for_package_with_chart(mocker: MockerFixture) -> None:
"""
must generate chart for package stats
"""
local = Path("local")
events = [Event("event", "1"), Event("event", "1")]
mocker.patch("ahriman.application.handlers.statistics.Statistics.event_stats")
chart_plot = mocker.patch("ahriman.application.handlers.statistics.Statistics.plot_times")
Statistics.stats_for_package("event", events, local)
chart_plot.assert_called_once_with("event", events, local)
def test_stats_per_package(mocker: MockerFixture) -> None:
"""
must print statistics per package
"""
events = [Event("event", "1"), Event("event", "2"), Event("event", "1")]
print_mock = mocker.patch("ahriman.core.formatters.Printer.print")
events_mock = mocker.patch("ahriman.application.handlers.statistics.Statistics.event_stats")
chart_plot = mocker.patch("ahriman.application.handlers.statistics.Statistics.plot_packages")
Statistics.stats_per_package("event", events, None)
print_mock.assert_has_calls([
MockCall(verbose=True, log_fn=pytest.helpers.anyvar(int), separator=": "),
MockCall(verbose=True, log_fn=pytest.helpers.anyvar(int), separator=": "),
])
events_mock.assert_called_once_with("event", events)
chart_plot.assert_not_called()
def test_stats_per_package_with_chart(mocker: MockerFixture) -> None:
"""
must print statistics per package with chart
"""
local = Path("local")
events = [Event("event", "1"), Event("event", "2"), Event("event", "1")]
mocker.patch("ahriman.application.handlers.statistics.Statistics.event_stats")
chart_plot = mocker.patch("ahriman.application.handlers.statistics.Statistics.plot_packages")
Statistics.stats_per_package("event", events, local)
chart_plot.assert_called_once_with("event", {"1": 2, "2": 1}, local)
@@ -0,0 +1,152 @@
import argparse
import pytest
from pytest_mock import MockerFixture
from unittest.mock import call as MockCall
from ahriman.application.handlers.status import Status
from ahriman.core.configuration import Configuration
from ahriman.core.database import SQLite
from ahriman.core.repository import Repository
from ahriman.models.build_status import BuildStatus, BuildStatusEnum
from ahriman.models.package import Package
def _default_args(args: argparse.Namespace) -> argparse.Namespace:
"""
default arguments for these test cases
Args:
args(argparse.Namespace): command line arguments fixture
Returns:
argparse.Namespace: generated arguments for these test cases
"""
args.ahriman = True
args.exit_code = False
args.info = False
args.package = []
args.status = None
return args
def test_run(args: argparse.Namespace, configuration: Configuration, repository: Repository,
package_ahriman: Package, package_python_schedule: Package, mocker: MockerFixture) -> None:
"""
must run command
"""
args = _default_args(args)
packages = [
(package_ahriman, BuildStatus(BuildStatusEnum.Success)),
(package_python_schedule, BuildStatus(BuildStatusEnum.Failed)),
]
mocker.patch("ahriman.core.repository.Repository.load", return_value=repository)
application_mock = mocker.patch("ahriman.core.status.Client.status_get")
packages_mock = mocker.patch("ahriman.core.status.local_client.LocalClient.package_get", return_value=packages)
check_mock = mocker.patch("ahriman.application.handlers.handler.Handler.check_status")
print_mock = mocker.patch("ahriman.core.formatters.Printer.print")
_, repository_id = configuration.check_loaded()
Status.run(args, repository_id, configuration, report=False)
application_mock.assert_called_once_with()
packages_mock.assert_called_once_with(None)
check_mock.assert_called_once_with(False, packages)
print_mock.assert_has_calls([
MockCall(verbose=False, log_fn=pytest.helpers.anyvar(int), separator=": ")
for _ in range(3)
])
def test_run_empty_exception(args: argparse.Namespace, configuration: Configuration, repository: Repository,
mocker: MockerFixture) -> None:
"""
must raise ExitCode exception on empty status result
"""
args = _default_args(args)
args.exit_code = True
mocker.patch("ahriman.core.repository.Repository.load", return_value=repository)
mocker.patch("ahriman.core.status.Client.status_get")
mocker.patch("ahriman.core.status.local_client.LocalClient.package_get", return_value=[])
check_mock = mocker.patch("ahriman.application.handlers.handler.Handler.check_status")
_, repository_id = configuration.check_loaded()
Status.run(args, repository_id, configuration, report=False)
check_mock.assert_called_once_with(True, [])
def test_run_verbose(args: argparse.Namespace, configuration: Configuration, repository: Repository,
package_ahriman: Package, mocker: MockerFixture) -> None:
"""
must run command with detailed info
"""
args = _default_args(args)
args.info = True
mocker.patch("ahriman.core.repository.Repository.load", return_value=repository)
mocker.patch("ahriman.core.status.local_client.LocalClient.package_get",
return_value=[(package_ahriman, BuildStatus(BuildStatusEnum.Success))])
print_mock = mocker.patch("ahriman.core.formatters.Printer.print")
_, repository_id = configuration.check_loaded()
Status.run(args, repository_id, configuration, report=False)
print_mock.assert_has_calls([
MockCall(verbose=True, log_fn=pytest.helpers.anyvar(int), separator=": ")
for _ in range(2)
])
def test_run_with_package_filter(args: argparse.Namespace, configuration: Configuration, repository: Repository,
package_ahriman: Package, mocker: MockerFixture) -> None:
"""
must run command with package filter
"""
args = _default_args(args)
args.package = [package_ahriman.base]
mocker.patch("ahriman.core.repository.Repository.load", return_value=repository)
packages_mock = mocker.patch("ahriman.core.status.local_client.LocalClient.package_get",
return_value=[(package_ahriman, BuildStatus(BuildStatusEnum.Success))])
_, repository_id = configuration.check_loaded()
Status.run(args, repository_id, configuration, report=False)
packages_mock.assert_called_once_with(package_ahriman.base)
def test_run_by_status(args: argparse.Namespace, configuration: Configuration, repository: Repository,
package_ahriman: Package, package_python_schedule: Package, mocker: MockerFixture) -> None:
"""
must filter packages by status
"""
args = _default_args(args)
args.status = BuildStatusEnum.Failed
mocker.patch("ahriman.core.status.local_client.LocalClient.package_get",
return_value=[(package_ahriman, BuildStatus(BuildStatusEnum.Success)),
(package_python_schedule, BuildStatus(BuildStatusEnum.Failed))])
mocker.patch("ahriman.core.repository.Repository.load", return_value=repository)
print_mock = mocker.patch("ahriman.core.formatters.Printer.print")
_, repository_id = configuration.check_loaded()
Status.run(args, repository_id, configuration, report=False)
print_mock.assert_has_calls([
MockCall(verbose=False, log_fn=pytest.helpers.anyvar(int), separator=": ")
for _ in range(2)
])
def test_imply_with_report(args: argparse.Namespace, configuration: Configuration, database: SQLite,
mocker: MockerFixture) -> None:
"""
must create application object with native reporting
"""
args = _default_args(args)
mocker.patch("ahriman.core.database.SQLite.load", return_value=database)
load_mock = mocker.patch("ahriman.core.repository.Repository.load")
_, repository_id = configuration.check_loaded()
Status.run(args, repository_id, configuration, report=False)
load_mock.assert_called_once_with(repository_id, configuration, database, report=True, refresh_pacman_database=0)
def test_disallow_multi_architecture_run() -> None:
"""
must not allow multi architecture run
"""
assert not Status.ALLOW_MULTI_ARCHITECTURE_RUN
@@ -0,0 +1,93 @@
import argparse
from pytest_mock import MockerFixture
from ahriman.application.handlers.status_update import StatusUpdate
from ahriman.core.configuration import Configuration
from ahriman.core.database import SQLite
from ahriman.core.repository import Repository
from ahriman.models.action import Action
from ahriman.models.build_status import BuildStatusEnum
from ahriman.models.package import Package
def _default_args(args: argparse.Namespace) -> argparse.Namespace:
"""
default arguments for these test cases
Args:
args(argparse.Namespace): command line arguments fixture
Returns:
argparse.Namespace: generated arguments for these test cases
"""
args.package = None
args.action = Action.Update
args.status = BuildStatusEnum.Success
return args
def test_run(args: argparse.Namespace, configuration: Configuration, repository: Repository,
mocker: MockerFixture) -> None:
"""
must run command
"""
args = _default_args(args)
mocker.patch("ahriman.core.repository.Repository.load", return_value=repository)
update_self_mock = mocker.patch("ahriman.core.status.local_client.LocalClient.status_update")
_, repository_id = configuration.check_loaded()
StatusUpdate.run(args, repository_id, configuration, report=False)
update_self_mock.assert_called_once_with(args.status)
def test_run_packages(args: argparse.Namespace, configuration: Configuration, repository: Repository,
mocker: MockerFixture) -> None:
"""
must run command with specified packages
"""
args = _default_args(args)
args.package = ["package"]
mocker.patch("ahriman.core.repository.Repository.load", return_value=repository)
update_mock = mocker.patch("ahriman.core.status.local_client.LocalClient.package_status_update")
_, repository_id = configuration.check_loaded()
StatusUpdate.run(args, repository_id, configuration, report=False)
update_mock.assert_called_once_with("package", args.status)
def test_run_remove(args: argparse.Namespace, configuration: Configuration, repository: Repository,
package_ahriman: Package, mocker: MockerFixture) -> None:
"""
must remove package from status page
"""
args = _default_args(args)
args.package = [package_ahriman.base]
args.action = Action.Remove
mocker.patch("ahriman.core.repository.Repository.load", return_value=repository)
update_mock = mocker.patch("ahriman.core.status.local_client.LocalClient.package_remove")
_, repository_id = configuration.check_loaded()
StatusUpdate.run(args, repository_id, configuration, report=False)
update_mock.assert_called_once_with(package_ahriman.base)
def test_imply_with_report(args: argparse.Namespace, configuration: Configuration, database: SQLite,
mocker: MockerFixture) -> None:
"""
must create application object with native reporting
"""
args = _default_args(args)
mocker.patch("ahriman.core.database.SQLite.load", return_value=database)
load_mock = mocker.patch("ahriman.core.repository.Repository.load")
_, repository_id = configuration.check_loaded()
StatusUpdate.run(args, repository_id, configuration, report=False)
load_mock.assert_called_once_with(repository_id, configuration, database, report=True, refresh_pacman_database=0)
def test_disallow_multi_architecture_run() -> None:
"""
must not allow multi architecture run
"""
assert not StatusUpdate.ALLOW_MULTI_ARCHITECTURE_RUN
@@ -0,0 +1,54 @@
import argparse
import pytest
from pytest_mock import MockerFixture
from unittest.mock import call as MockCall
from ahriman.application.handlers.structure import Structure
from ahriman.core.configuration import Configuration
from ahriman.core.repository import Repository
from ahriman.models.package import Package
def _default_args(args: argparse.Namespace) -> argparse.Namespace:
"""
default arguments for these test cases
Args:
args(argparse.Namespace): command line arguments fixture
Returns:
argparse.Namespace: generated arguments for these test cases
"""
args.partitions = 1
return args
def test_run(args: argparse.Namespace, configuration: Configuration, repository: Repository,
package_ahriman: Package, mocker: MockerFixture) -> None:
"""
must run command
"""
args = _default_args(args)
mocker.patch("ahriman.core.repository.Repository.load", return_value=repository)
mocker.patch("ahriman.core.repository.Repository.packages", return_value=[package_ahriman])
packages_mock = mocker.patch("ahriman.core.tree.Tree.partition", return_value=[[package_ahriman]])
application_mock = mocker.patch("ahriman.core.tree.Tree.resolve", return_value=[[package_ahriman]])
print_mock = mocker.patch("ahriman.core.formatters.Printer.print")
_, repository_id = configuration.check_loaded()
Structure.run(args, repository_id, configuration, report=False)
packages_mock.assert_called_once_with([package_ahriman], count=args.partitions)
application_mock.assert_called_once_with([package_ahriman])
print_mock.assert_has_calls([
MockCall(verbose=False, log_fn=pytest.helpers.anyvar(int), separator=": "),
MockCall(verbose=True, log_fn=pytest.helpers.anyvar(int), separator=" "),
MockCall(verbose=False, log_fn=pytest.helpers.anyvar(int), separator=": "),
])
def test_disallow_multi_architecture_run() -> None:
"""
must not allow multi architecture run
"""
assert not Structure.ALLOW_MULTI_ARCHITECTURE_RUN
@@ -0,0 +1,74 @@
import argparse
from pathlib import Path
from pytest_mock import MockerFixture
from unittest.mock import call as MockCall
from ahriman.application.handlers.tree_migrate import TreeMigrate
from ahriman.core.configuration import Configuration
from ahriman.models.package import Package
from ahriman.models.repository_id import RepositoryId
from ahriman.models.repository_paths import RepositoryPaths
def test_run(args: argparse.Namespace, configuration: Configuration, mocker: MockerFixture) -> None:
"""
must run command
"""
tree_create_mock = mocker.patch("ahriman.models.repository_paths.RepositoryPaths.tree_create")
application_mock = mocker.patch("ahriman.application.handlers.tree_migrate.TreeMigrate.tree_move")
symlinks_mock = mocker.patch("ahriman.application.handlers.tree_migrate.TreeMigrate.symlinks_fix")
_, repository_id = configuration.check_loaded()
old_paths = configuration.repository_paths
new_paths = RepositoryPaths(old_paths.root, old_paths.repository_id, _force_current_tree=True)
TreeMigrate.run(args, repository_id, configuration, report=False)
tree_create_mock.assert_called_once_with()
application_mock.assert_called_once_with(old_paths, new_paths)
symlinks_mock.assert_called_once_with(new_paths)
def test_symlinks_fix(repository_paths: RepositoryPaths, package_ahriman: Package, mocker: MockerFixture) -> None:
"""
must replace symlinks during migration
"""
mocker.patch("ahriman.application.handlers.tree_migrate.walk", side_effect=[
[
repository_paths.archive_for(package_ahriman.base) / "file",
repository_paths.archive_for(package_ahriman.base) / "symlink",
],
[
repository_paths.repository / "file",
repository_paths.repository / "symlink",
],
])
mocker.patch("pathlib.Path.exists", autospec=True, side_effect=lambda p: p.name == "file")
unlink_mock = mocker.patch("pathlib.Path.unlink")
symlink_mock = mocker.patch("pathlib.Path.symlink_to")
TreeMigrate.symlinks_fix(repository_paths)
unlink_mock.assert_called_once_with()
symlink_mock.assert_called_once_with(
Path("..") /
".." /
".." /
repository_paths.archive_for(package_ahriman.base).relative_to(repository_paths.root) /
"symlink"
)
def test_move_tree(mocker: MockerFixture) -> None:
"""
must move tree
"""
rename_mock = mocker.patch("pathlib.Path.rename", autospec=True)
root = Path("local")
from_paths = RepositoryPaths(root, RepositoryId("arch", ""))
to_paths = RepositoryPaths(root, RepositoryId("arch", "repo"))
TreeMigrate.tree_move(from_paths, to_paths)
rename_mock.assert_has_calls([
MockCall(from_paths.packages, to_paths.packages),
MockCall(from_paths.pacman, to_paths.pacman),
MockCall(from_paths.repository, to_paths.repository),
])
@@ -0,0 +1,57 @@
import argparse
from pytest_mock import MockerFixture
from ahriman.application.handlers.triggers import Triggers
from ahriman.core.configuration import Configuration
from ahriman.core.repository import Repository
from ahriman.models.package import Package
from ahriman.models.result import Result
def _default_args(args: argparse.Namespace) -> argparse.Namespace:
"""
default arguments for these test cases
Args:
args(argparse.Namespace): command line arguments fixture
Returns:
argparse.Namespace: generated arguments for these test cases
"""
args.trigger = []
return args
def test_run(args: argparse.Namespace, configuration: Configuration, repository: Repository,
mocker: MockerFixture) -> None:
"""
must run command
"""
args = _default_args(args)
mocker.patch("ahriman.core.repository.Repository.load", return_value=repository)
application_mock = mocker.patch("ahriman.application.application.Application.on_result")
on_start_mock = mocker.patch("ahriman.application.application.Application.on_start")
_, repository_id = configuration.check_loaded()
Triggers.run(args, repository_id, configuration, report=False)
application_mock.assert_called_once_with(Result())
on_start_mock.assert_called_once_with()
def test_run_trigger(args: argparse.Namespace, configuration: Configuration, repository: Repository,
package_ahriman: Package, mocker: MockerFixture) -> None:
"""
must run triggers specified by command line
"""
args = _default_args(args)
args.trigger = ["ahriman.core.report.ReportTrigger"]
mocker.patch("ahriman.core.repository.Repository.packages", return_value=[package_ahriman])
mocker.patch("ahriman.core.repository.Repository.load", return_value=repository)
report_mock = mocker.patch("ahriman.core.report.ReportTrigger.on_result")
upload_mock = mocker.patch("ahriman.core.upload.UploadTrigger.on_result")
_, repository_id = configuration.check_loaded()
Triggers.run(args, repository_id, configuration, report=False)
report_mock.assert_called_once_with(Result(), [package_ahriman])
upload_mock.assert_not_called()
@@ -0,0 +1,90 @@
import argparse
import pytest
from pytest_mock import MockerFixture
from ahriman.application.ahriman import _parser
from ahriman.application.handlers.unsafe_commands import UnsafeCommands
from ahriman.core.configuration import Configuration
def _default_args(args: argparse.Namespace) -> argparse.Namespace:
"""
default arguments for these test cases
Args:
args(argparse.Namespace): command line arguments fixture
Returns:
argparse.Namespace: generated arguments for these test cases
"""
args.parser = _parser
args.subcommand = []
return args
def test_run(args: argparse.Namespace, configuration: Configuration, mocker: MockerFixture) -> None:
"""
must run command
"""
args = _default_args(args)
commands_mock = mocker.patch("ahriman.application.handlers.unsafe_commands.UnsafeCommands.get_unsafe_commands",
return_value=["command"])
print_mock = mocker.patch("ahriman.core.formatters.Printer.print")
_, repository_id = configuration.check_loaded()
UnsafeCommands.run(args, repository_id, configuration, report=False)
commands_mock.assert_called_once_with(pytest.helpers.anyvar(int))
print_mock.assert_called_once_with(verbose=True, log_fn=pytest.helpers.anyvar(int), separator=": ")
def test_run_check(args: argparse.Namespace, configuration: Configuration, mocker: MockerFixture) -> None:
"""
must run command and check if command is unsafe
"""
args = _default_args(args)
args.subcommand = ["clean"]
commands_mock = mocker.patch("ahriman.application.handlers.unsafe_commands.UnsafeCommands.get_unsafe_commands",
return_value=["command"])
check_mock = mocker.patch("ahriman.application.handlers.unsafe_commands.UnsafeCommands.check_unsafe")
_, repository_id = configuration.check_loaded()
UnsafeCommands.run(args, repository_id, configuration, report=False)
commands_mock.assert_called_once_with(pytest.helpers.anyvar(int))
check_mock.assert_called_once_with(["clean"], ["command"], pytest.helpers.anyvar(int))
def test_check_unsafe(mocker: MockerFixture) -> None:
"""
must check if command is unsafe
"""
check_mock = mocker.patch("ahriman.application.handlers.handler.Handler.check_status")
UnsafeCommands.check_unsafe(["service-clean"], ["service-clean"], _parser())
check_mock.assert_called_once_with(True, False)
def test_check_unsafe_safe(mocker: MockerFixture) -> None:
"""
must check if command is safe
"""
check_mock = mocker.patch("ahriman.application.handlers.handler.Handler.check_status")
UnsafeCommands.check_unsafe(["package-status"], ["service-clean"], _parser())
check_mock.assert_called_once_with(True, True)
def test_get_unsafe_commands() -> None:
"""
must return unsafe commands
"""
parser = _parser()
subparser = next(action for action in parser._actions if isinstance(action, argparse._SubParsersAction))
commands = UnsafeCommands.get_unsafe_commands(parser)
for command in commands:
assert subparser.choices[command].get_default("unsafe")
def test_disallow_multi_architecture_run() -> None:
"""
must not allow multi architecture run
"""
assert not UnsafeCommands.ALLOW_MULTI_ARCHITECTURE_RUN
@@ -0,0 +1,161 @@
import argparse
import pytest
from pytest_mock import MockerFixture
from unittest.mock import call as MockCall
from ahriman.application.application import Application
from ahriman.application.handlers.update import Update
from ahriman.core.configuration import Configuration
from ahriman.core.repository import Repository
from ahriman.models.package import Package
from ahriman.models.packagers import Packagers
from ahriman.models.result import Result
def _default_args(args: argparse.Namespace) -> argparse.Namespace:
"""
default arguments for these test cases
Args:
args(argparse.Namespace): command line arguments fixture
Returns:
argparse.Namespace: generated arguments for these test cases
"""
args.aur = True
args.changes = True
args.check_files = True
args.package = []
args.dependencies = True
args.dry_run = False
args.exit_code = False
args.increment = True
args.local = True
args.manual = True
args.refresh = 0
args.username = "username"
args.vcs = True
return args
def test_run(args: argparse.Namespace, configuration: Configuration, repository: Repository,
mocker: MockerFixture) -> None:
"""
must run command
"""
args = _default_args(args)
mocker.patch("ahriman.core.repository.Repository.load", return_value=repository)
on_start_mock = mocker.patch("ahriman.application.application.Application.on_start")
perform_mock = mocker.patch("ahriman.application.handlers.update.Update.perform_action")
_, repository_id = configuration.check_loaded()
Update.run(args, repository_id, configuration, report=False)
on_start_mock.assert_called_once_with()
perform_mock.assert_called_once_with(pytest.helpers.anyvar(int), args)
def test_perform_action(args: argparse.Namespace, application: Application, package_ahriman: Package,
mocker: MockerFixture) -> None:
"""
must perform update action
"""
args = _default_args(args)
result = Result()
result.add_updated(package_ahriman)
application_mock = mocker.patch("ahriman.application.application.Application.update", return_value=result)
check_mock = mocker.patch("ahriman.application.handlers.handler.Handler.check_status")
dependencies_mock = mocker.patch("ahriman.application.application.Application.with_dependencies",
return_value=[package_ahriman])
updates_mock = mocker.patch("ahriman.application.application.Application.updates", return_value=[package_ahriman])
changes_mock = mocker.patch("ahriman.application.application.Application.changes")
print_mock = mocker.patch("ahriman.application.application.Application.print_updates")
Update.perform_action(application, args)
application_mock.assert_called_once_with([package_ahriman],
Packagers(args.username, {package_ahriman.base: "packager"}),
bump_pkgrel=args.increment)
updates_mock.assert_called_once_with(
args.package, aur=args.aur, local=args.local, manual=args.manual, vcs=args.vcs, check_files=args.check_files)
changes_mock.assert_called_once_with([package_ahriman])
dependencies_mock.assert_called_once_with([package_ahriman], process_dependencies=args.dependencies)
check_mock.assert_called_once_with(False, True)
print_mock.assert_called_once_with([package_ahriman], log_fn=pytest.helpers.anyvar(int))
def test_perform_action_empty_exception(args: argparse.Namespace, application: Application,
mocker: MockerFixture) -> None:
"""
must raise ExitCode exception on empty update list
"""
args = _default_args(args)
args.exit_code = True
args.dry_run = True
mocker.patch("ahriman.application.application.Application.updates", return_value=[])
check_mock = mocker.patch("ahriman.application.handlers.handler.Handler.check_status")
Update.perform_action(application, args)
check_mock.assert_called_once_with(True, [])
def test_perform_action_update_empty_exception(args: argparse.Namespace, application: Application,
package_ahriman: Package, mocker: MockerFixture) -> None:
"""
must raise ExitCode exception on empty build result
"""
args = _default_args(args)
args.exit_code = True
mocker.patch("ahriman.application.application.Application.update", return_value=Result())
mocker.patch("ahriman.application.application.Application.updates", return_value=[package_ahriman])
mocker.patch("ahriman.application.application.Application.with_dependencies", return_value=[package_ahriman])
mocker.patch("ahriman.application.application.Application.print_updates")
mocker.patch("ahriman.application.application.Application.changes")
check_mock = mocker.patch("ahriman.application.handlers.handler.Handler.check_status")
Update.perform_action(application, args)
check_mock.assert_called_once_with(True, False)
def test_perform_action_dry_run(args: argparse.Namespace, application: Application, package_ahriman: Package,
mocker: MockerFixture) -> None:
"""
must run simplified command
"""
args = _default_args(args)
args.dry_run = True
application_mock = mocker.patch("ahriman.application.application.Application.update")
check_mock = mocker.patch("ahriman.application.handlers.handler.Handler.check_status")
updates_mock = mocker.patch("ahriman.application.application.Application.updates", return_value=[package_ahriman])
changes_mock = mocker.patch("ahriman.application.application.Application.changes")
Update.perform_action(application, args)
updates_mock.assert_called_once_with(
args.package, aur=args.aur, local=args.local, manual=args.manual, vcs=args.vcs, check_files=args.check_files)
application_mock.assert_not_called()
changes_mock.assert_called_once_with([package_ahriman])
check_mock.assert_called_once_with(False, [package_ahriman])
def test_perform_action_no_changes(args: argparse.Namespace, application: Application, mocker: MockerFixture) -> None:
"""
must skip changes calculation
"""
args = _default_args(args)
args.dry_run = True
args.changes = False
mocker.patch("ahriman.application.application.Application.update")
mocker.patch("ahriman.application.handlers.handler.Handler.check_status")
mocker.patch("ahriman.application.application.Application.updates")
changes_mock = mocker.patch("ahriman.application.application.Application.changes")
Update.perform_action(application, args)
changes_mock.assert_not_called()
def test_log_fn(application: Application, mocker: MockerFixture) -> None:
"""
must print package updates
"""
logger_mock = mocker.patch("logging.Logger.info")
Update.log_fn(application, False)("hello")
logger_mock.assert_has_calls([MockCall("hello")])
@@ -0,0 +1,182 @@
import argparse
import pytest
from pytest_mock import MockerFixture
from unittest.mock import call as MockCall
from ahriman.application.handlers.users import Users
from ahriman.core.configuration import Configuration
from ahriman.core.database import SQLite
from ahriman.core.exceptions import PasswordError
from ahriman.models.action import Action
from ahriman.models.user import User
from ahriman.models.user_access import UserAccess
def _default_args(args: argparse.Namespace) -> argparse.Namespace:
"""
default arguments for these test cases
Args:
args(argparse.Namespace): command line arguments fixture
Returns:
argparse.Namespace: generated arguments for these test cases
"""
args.username = "user"
args.action = Action.Update
args.exit_code = False
args.key = "key"
args.packager = "packager"
args.password = "pa55w0rd"
args.role = UserAccess.Reporter
return args
def test_run(args: argparse.Namespace, configuration: Configuration, database: SQLite, mocker: MockerFixture) -> None:
"""
must run command
"""
args = _default_args(args)
user = User(username=args.username, password=args.password, access=args.role,
packager_id=args.packager, key=args.key)
mocker.patch("ahriman.core.database.SQLite.load", return_value=database)
mocker.patch("ahriman.models.user.User.hash_password", return_value=user)
create_user_mock = mocker.patch("ahriman.application.handlers.users.Users.user_create", return_value=user)
update_mock = mocker.patch("ahriman.core.database.SQLite.user_update")
_, repository_id = configuration.check_loaded()
Users.run(args, repository_id, configuration, report=False)
create_user_mock.assert_called_once_with(args)
update_mock.assert_called_once_with(user)
def test_run_empty_salt(args: argparse.Namespace, configuration: Configuration, database: SQLite,
mocker: MockerFixture) -> None:
"""
must process users with empty password salt
"""
configuration.remove_option("auth", "salt")
args = _default_args(args)
user = User(username=args.username, password=args.password, access=args.role,
packager_id=args.packager, key=args.key)
mocker.patch("ahriman.core.database.SQLite.load", return_value=database)
mocker.patch("ahriman.models.user.User.hash_password", return_value=user)
create_user_mock = mocker.patch("ahriman.application.handlers.users.Users.user_create", return_value=user)
update_mock = mocker.patch("ahriman.core.database.SQLite.user_update")
_, repository_id = configuration.check_loaded()
Users.run(args, repository_id, configuration, report=False)
create_user_mock.assert_called_once_with(args)
update_mock.assert_called_once_with(user)
def test_run_empty_salt_without_password(args: argparse.Namespace, configuration: Configuration, database: SQLite,
mocker: MockerFixture) -> None:
"""
must skip salt option in case if password was empty
"""
configuration.remove_option("auth", "salt")
args = _default_args(args)
user = User(username=args.username, password="", access=args.role,
packager_id=args.packager, key=args.key)
mocker.patch("ahriman.core.database.SQLite.load", return_value=database)
mocker.patch("ahriman.models.user.User.hash_password", return_value=user)
create_user_mock = mocker.patch("ahriman.application.handlers.users.Users.user_create", return_value=user)
update_mock = mocker.patch("ahriman.core.database.SQLite.user_update")
_, repository_id = configuration.check_loaded()
Users.run(args, repository_id, configuration, report=False)
create_user_mock.assert_called_once_with(args)
update_mock.assert_called_once_with(user)
def test_run_list(args: argparse.Namespace, configuration: Configuration, database: SQLite, user: User,
mocker: MockerFixture) -> None:
"""
must list available users
"""
args = _default_args(args)
args.action = Action.List
mocker.patch("ahriman.core.database.SQLite.load", return_value=database)
check_mock = mocker.patch("ahriman.application.handlers.handler.Handler.check_status")
list_mock = mocker.patch("ahriman.core.database.SQLite.user_list", return_value=[user])
_, repository_id = configuration.check_loaded()
Users.run(args, repository_id, configuration, report=False)
list_mock.assert_called_once_with("user", args.role)
check_mock.assert_called_once_with(False, [user])
def test_run_empty_exception(args: argparse.Namespace, configuration: Configuration, database: SQLite,
mocker: MockerFixture) -> None:
"""
must raise ExitCode exception on empty user list
"""
args = _default_args(args)
args.action = Action.List
args.exit_code = True
mocker.patch("ahriman.core.database.SQLite.load", return_value=database)
mocker.patch("ahriman.core.database.SQLite.user_list", return_value=[])
check_mock = mocker.patch("ahriman.application.handlers.handler.Handler.check_status")
_, repository_id = configuration.check_loaded()
Users.run(args, repository_id, configuration, report=False)
check_mock.assert_called_once_with(True, [])
def test_run_remove(args: argparse.Namespace, configuration: Configuration, database: SQLite,
mocker: MockerFixture) -> None:
"""
must remove user if remove flag supplied
"""
args = _default_args(args)
args.action = Action.Remove
mocker.patch("ahriman.core.database.SQLite.load", return_value=database)
remove_mock = mocker.patch("ahriman.core.database.SQLite.user_remove")
_, repository_id = configuration.check_loaded()
Users.run(args, repository_id, configuration, report=False)
remove_mock.assert_called_once_with(args.username)
def test_user_create(args: argparse.Namespace, user: User) -> None:
"""
must create user
"""
args = _default_args(args)
generated = Users.user_create(args)
assert generated.username == user.username
assert generated.access == user.access
def test_user_create_getpass(args: argparse.Namespace, mocker: MockerFixture) -> None:
"""
must create user and get password from command line
"""
args = _default_args(args)
args.password = None
getpass_mock = mocker.patch("getpass.getpass", return_value="password")
generated = Users.user_create(args)
getpass_mock.assert_has_calls([MockCall(), MockCall("Repeat password: ")])
assert generated.password == "password"
def test_user_create_getpass_exception(args: argparse.Namespace, mocker: MockerFixture) -> None:
"""
must raise password error in case if password doesn't match
"""
args = _default_args(args)
args.password = None
mocker.patch("getpass.getpass", side_effect=lambda *_: User.generate_password(10))
with pytest.raises(PasswordError):
Users.user_create(args)
def test_disallow_multi_architecture_run() -> None:
"""
must not allow multi architecture run
"""
assert not Users.ALLOW_MULTI_ARCHITECTURE_RUN
@@ -0,0 +1,162 @@
import argparse
import json
import pytest
from pathlib import Path
from pytest_mock import MockerFixture
from ahriman.application.handlers.validate import Validate
from ahriman.core.configuration import Configuration
from ahriman.core.configuration.schema import CONFIGURATION_SCHEMA
from ahriman.core.configuration.validator import Validator
from ahriman.core.gitremote import RemotePullTrigger, RemotePushTrigger
def _default_args(args: argparse.Namespace) -> argparse.Namespace:
"""
default arguments for these test cases
Args:
args(argparse.Namespace): command line arguments fixture
Returns:
argparse.Namespace: generated arguments for these test cases
"""
args.exit_code = False
return args
def test_run(args: argparse.Namespace, configuration: Configuration, mocker: MockerFixture) -> None:
"""
must run command
"""
args = _default_args(args)
mocker.patch.object(Validator, "errors", {"node": ["error"]})
print_mock = mocker.patch("ahriman.core.formatters.Printer.print")
application_mock = mocker.patch("ahriman.core.configuration.validator.Validator.validate", return_value=False)
_, repository_id = configuration.check_loaded()
Validate.run(args, repository_id, configuration, report=False)
application_mock.assert_called_once_with(configuration.dump())
print_mock.assert_called_once_with(verbose=True, log_fn=pytest.helpers.anyvar(int), separator=": ")
def test_run_skip(args: argparse.Namespace, configuration: Configuration, mocker: MockerFixture) -> None:
"""
must skip print if no errors found
"""
args = _default_args(args)
mocker.patch("ahriman.core.configuration.validator.Validator.validate", return_value=True)
print_mock = mocker.patch("ahriman.core.formatters.Printer.print")
_, repository_id = configuration.check_loaded()
Validate.run(args, repository_id, configuration, report=False)
print_mock.assert_not_called()
def test_run_default(args: argparse.Namespace, configuration: Configuration) -> None:
"""
must run on default configuration without errors
"""
args.exit_code = True
_, repository_id = configuration.check_loaded()
default = Configuration.from_path(Configuration.SYSTEM_CONFIGURATION_PATH, repository_id)
# copy autogenerated values
for section, key in (("build", "build_command"), ("repository", "root")):
value = configuration.get(section, key)
default.set_option(section, key, value)
Validate.run(args, repository_id, default, report=False)
def test_run_repo_specific_triggers(args: argparse.Namespace, configuration: Configuration,
resource_path_root: Path) -> None:
"""
must correctly insert repo specific triggers
"""
args.exit_code = True
_, repository_id = configuration.check_loaded()
# remove unused sections
for section in ("archive", "customs3", "github:x86_64", "logs-rotation", "mirrorlist"):
configuration.remove_section(section)
configuration.set_option("report", "target", "test")
for section in ("test", "test:i686", "test:another-repo:x86_64"):
configuration.set_option(section, "type", "html")
configuration.set_option(section, "link_path", "http://link_path")
configuration.set_option(section, "path", "path")
configuration.set_option(section, "template", "template")
configuration.set_option(section, "templates", str(resource_path_root))
Validate.run(args, repository_id, configuration, report=False)
def test_schema(configuration: Configuration) -> None:
"""
must generate full schema correctly
"""
schema = Validate.schema(configuration)
# defaults
assert schema.pop("console")
assert schema.pop("email")
assert schema.pop("github")
assert schema.pop("gitremote")
assert schema.pop("html")
assert schema.pop("keyring")
assert schema.pop("keyring-generator")
assert schema.pop("mirrorlist")
assert schema.pop("mirrorlist-generator")
assert schema.pop("remote-call")
assert schema.pop("remote-pull")
assert schema.pop("remote-push")
assert schema.pop("remote-service")
assert schema.pop("report")
assert schema.pop("rss")
assert schema.pop("rsync")
assert schema.pop("s3")
assert schema.pop("telegram")
assert schema.pop("upload")
assert schema.pop("worker")
assert schema == CONFIGURATION_SCHEMA
def test_schema_invalid_trigger(configuration: Configuration) -> None:
"""
must skip trigger if it caused exception on load
"""
configuration.set_option("build", "triggers", "some.invalid.trigger.path.Trigger")
configuration.remove_option("build", "triggers_known")
assert Validate.schema(configuration) == CONFIGURATION_SCHEMA
def test_schema_erase_required() -> None:
"""
must remove required field from dictionaries recursively
"""
# the easiest way is to just dump to string and check
assert "required" not in json.dumps(Validate.schema_erase_required(CONFIGURATION_SCHEMA))
def test_schema_merge() -> None:
"""
must merge schemas correctly
"""
erased = Validate.schema_erase_required(CONFIGURATION_SCHEMA)
assert Validate.schema_merge(erased, CONFIGURATION_SCHEMA) == CONFIGURATION_SCHEMA
merged = Validate.schema_merge(RemotePullTrigger.CONFIGURATION_SCHEMA, RemotePushTrigger.CONFIGURATION_SCHEMA)
for key in RemotePullTrigger.CONFIGURATION_SCHEMA["gitremote"]["schema"]:
assert key in merged["gitremote"]["schema"]
for key in RemotePushTrigger.CONFIGURATION_SCHEMA["gitremote"]["schema"]:
assert key in merged["gitremote"]["schema"]
def test_disallow_multi_architecture_run() -> None:
"""
must not allow multi architecture run
"""
assert not Validate.ALLOW_MULTI_ARCHITECTURE_RUN
@@ -0,0 +1,50 @@
import argparse
import pytest
from pytest_mock import MockerFixture
from unittest.mock import call as MockCall
from ahriman.application.handlers.versions import Versions
from ahriman.core.configuration import Configuration
def test_run(args: argparse.Namespace, configuration: Configuration, mocker: MockerFixture) -> None:
"""
must run command
"""
application_mock = mocker.patch("ahriman.application.handlers.versions.Versions.package_dependencies")
print_mock = mocker.patch("ahriman.core.formatters.Printer.print")
_, repository_id = configuration.check_loaded()
Versions.run(args, repository_id, configuration, report=False)
application_mock.assert_called_once_with("ahriman")
print_mock.assert_has_calls([
MockCall(verbose=False, log_fn=pytest.helpers.anyvar(int), separator=" "),
MockCall(verbose=False, log_fn=pytest.helpers.anyvar(int), separator=" "),
])
def test_package_dependencies() -> None:
"""
must extract package dependencies
"""
packages = dict(Versions.package_dependencies("requests"))
assert packages
assert packages.get("urllib3") is not None
def test_package_dependencies_missing() -> None:
"""
must extract package dependencies even if some of them are missing
"""
packages = dict(Versions.package_dependencies("ahriman"))
assert packages
assert packages.get("pyalpm") is not None
assert packages.get("Sphinx") is None
def test_disallow_multi_architecture_run() -> None:
"""
must not allow multi architecture run
"""
assert not Versions.ALLOW_MULTI_ARCHITECTURE_RUN
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,19 @@
from ahriman.application.help_formatter import _HelpFormatter
def test_whitespace_matcher(formatter: _HelpFormatter) -> None:
"""
must only match spaces or tabs
"""
assert formatter._whitespace_matcher.match(" ")
assert formatter._whitespace_matcher.match("\t")
assert formatter._whitespace_matcher.match("\n") is None
assert formatter._whitespace_matcher.match("\r") is None
def test_fill_text(formatter: _HelpFormatter) -> None:
"""
must wrap text keeping new lines
"""
assert formatter._fill_text("first\n 1 longwordhere", 10, "") == "first\n 1 longwor\ndhere"
@@ -0,0 +1,46 @@
from pytest_mock import MockerFixture
from ahriman.application.interactive_shell import InteractiveShell
def test_has_ipython(mocker: MockerFixture) -> None:
"""
must correctly check if IPython is installed
"""
find_spec_mock = mocker.patch("ahriman.application.interactive_shell.find_spec")
assert InteractiveShell.has_ipython()
find_spec_mock.assert_called_once_with("IPython.terminal.embed")
def test_has_ipython_module_not_found(mocker: MockerFixture) -> None:
"""
must return False if IPython is not installed
"""
mocker.patch("ahriman.application.interactive_shell.find_spec", side_effect=ModuleNotFoundError)
assert not InteractiveShell.has_ipython()
def test_interact(mocker: MockerFixture) -> None:
"""
must call IPython shell
"""
mocker.patch("ahriman.application.interactive_shell.InteractiveShell.has_ipython", return_value=True)
banner_mock = mocker.patch("IPython.terminal.embed.InteractiveShellEmbed.show_banner")
interact_mock = mocker.patch("IPython.terminal.embed.InteractiveShellEmbed.interact")
shell = InteractiveShell()
shell.interact()
banner_mock.assert_called_once_with()
interact_mock.assert_called_once_with()
def test_interact_no_ipython(mocker: MockerFixture) -> None:
"""
must call builtin shell if no IPython available
"""
mocker.patch("ahriman.application.interactive_shell.InteractiveShell.has_ipython", return_value=None)
interact_mock = mocker.patch("code.InteractiveConsole.interact")
shell = InteractiveShell()
shell.interact()
interact_mock.assert_called_once_with(shell)
@@ -0,0 +1,296 @@
import argparse
import fcntl
import os
import pytest
from pathlib import Path
from pytest_mock import MockerFixture
from tempfile import NamedTemporaryFile
from unittest.mock import MagicMock, call as MockCall
from ahriman import __version__
from ahriman.application.lock import Lock
from ahriman.core.configuration import Configuration
from ahriman.core.exceptions import DuplicateRunError, UnsafeRunError
from ahriman.models.build_status import BuildStatus, BuildStatusEnum
from ahriman.models.internal_status import InternalStatus
from ahriman.models.repository_id import RepositoryId
def test_path(args: argparse.Namespace, configuration: Configuration) -> None:
"""
must create path variable correctly
"""
_, repository_id = configuration.check_loaded()
assert Lock(args, repository_id, configuration).path is None
args.lock = Path("/run/ahriman.pid")
assert Lock(args, repository_id, configuration).path == Path("/run/ahriman_x86_64-aur.pid")
args.lock = Path("ahriman.pid")
assert Lock(args, repository_id, configuration).path == Path("/run/ahriman/ahriman_x86_64-aur.pid")
assert Lock(args, RepositoryId("", ""), configuration).path == Path("/run/ahriman/ahriman.pid")
with pytest.raises(ValueError):
args.lock = Path("/")
assert Lock(args, repository_id, configuration).path # special case
def test_perform_lock(mocker: MockerFixture) -> None:
"""
must lock file with fcntl
"""
flock_mock = mocker.patch("fcntl.flock")
assert Lock.perform_lock(1)
flock_mock.assert_called_once_with(1, fcntl.LOCK_EX | fcntl.LOCK_NB)
def test_perform_lock_exception(mocker: MockerFixture) -> None:
"""
must return False on OSError
"""
mocker.patch("fcntl.flock", side_effect=OSError)
assert not Lock.perform_lock(1)
def test_open(lock: Lock, mocker: MockerFixture) -> None:
"""
must open file
"""
open_mock = mocker.patch("pathlib.Path.open")
lock.path = Path("ahriman.pid")
lock._open()
open_mock.assert_called_once_with("a+", encoding="utf8")
def test_open_skip(lock: Lock, mocker: MockerFixture) -> None:
"""
must skip file opening if path is not set
"""
open_mock = mocker.patch("pathlib.Path.open")
lock._open()
open_mock.assert_not_called()
def test_watch(lock: Lock, mocker: MockerFixture) -> None:
"""
must check if lock file exists
"""
lock._pid_file = MagicMock()
lock._pid_file.fileno.return_value = 1
wait_mock = mocker.patch("ahriman.models.waiter.Waiter.wait")
lock._watch()
wait_mock.assert_called_once_with(pytest.helpers.anyvar(int), 1)
def test_watch_skip(lock: Lock, mocker: MockerFixture) -> None:
"""
must skip watch on empty path
"""
mocker.patch("ahriman.application.lock.Lock.perform_lock", return_value=True)
lock._watch()
def test_write(lock: Lock) -> None:
"""
must write PID to lock file
"""
with NamedTemporaryFile("a+") as pid_file:
lock._pid_file = pid_file
lock._write(is_locked=False)
assert int(lock._pid_file.readline()) == os.getpid()
def test_write_skip(lock: Lock) -> None:
"""
must skip write to file if no path set
"""
lock._write(is_locked=False)
def test_write_locked(lock: Lock, mocker: MockerFixture) -> None:
"""
must raise DuplicateRunError if it cannot lock file
"""
mocker.patch("ahriman.application.lock.Lock.perform_lock", return_value=False)
with pytest.raises(DuplicateRunError):
lock._pid_file = MagicMock()
lock._write(is_locked=False)
def test_write_locked_before(lock: Lock, mocker: MockerFixture) -> None:
"""
must skip lock in case if file was locked before
"""
lock_mock = mocker.patch("ahriman.application.lock.Lock.perform_lock")
lock._pid_file = MagicMock()
lock._write(is_locked=True)
lock_mock.assert_not_called()
def test_check_user(lock: Lock, mocker: MockerFixture) -> None:
"""
must check user correctly
"""
check_user_patch = mocker.patch("ahriman.application.lock.check_user")
tree_create = mocker.patch("ahriman.models.repository_paths.RepositoryPaths.tree_create")
lock.check_user()
check_user_patch.assert_called_once_with(lock.paths.root, unsafe=False)
tree_create.assert_called_once_with()
def test_check_user_exception(lock: Lock, mocker: MockerFixture) -> None:
"""
must raise exception if user differs
"""
mocker.patch("ahriman.application.lock.check_user", side_effect=UnsafeRunError(0, 1))
with pytest.raises(UnsafeRunError):
lock.check_user()
def test_check_user_unsafe(lock: Lock, mocker: MockerFixture) -> None:
"""
must skip user check if unsafe flag set
"""
mocker.patch("ahriman.models.repository_paths.RepositoryPaths.tree_create")
lock.unsafe = True
lock.check_user()
def test_check_version(lock: Lock, mocker: MockerFixture) -> None:
"""
must check version correctly
"""
mocker.patch("ahriman.core.status.Client.status_get",
return_value=InternalStatus(status=BuildStatus(), version=__version__))
logging_mock = mocker.patch("logging.Logger.warning")
lock.check_version()
logging_mock.assert_not_called()
def test_check_version_mismatch(lock: Lock, mocker: MockerFixture) -> None:
"""
must check mismatched version correctly
"""
mocker.patch("ahriman.core.status.Client.status_get",
return_value=InternalStatus(status=BuildStatus(), version="version"))
logging_mock = mocker.patch("logging.Logger.warning")
lock.check_version()
logging_mock.assert_called_once() # we do not check logging arguments
def test_clear(lock: Lock) -> None:
"""
must remove lock file
"""
lock.path = Path("ahriman-test.pid")
lock.path.touch()
lock.clear()
assert not lock.path.is_file()
def test_clear_missing(lock: Lock) -> None:
"""
must not fail on lock removal if file is missing
"""
lock.path = Path("ahriman-test.pid")
lock.clear()
def test_clear_skip(lock: Lock, mocker: MockerFixture) -> None:
"""
must skip removal if no file set
"""
unlink_mock = mocker.patch("pathlib.Path.unlink")
lock.clear()
unlink_mock.assert_not_called()
def test_clear_close(lock: Lock) -> None:
"""
must close pid file if opened
"""
close_mock = lock._pid_file = MagicMock()
lock.clear()
close_mock.close.assert_called_once_with()
def test_clear_close_exception(lock: Lock) -> None:
"""
must suppress IO exception on file closure
"""
close_mock = lock._pid_file = MagicMock()
close_mock.close.side_effect = IOError
lock.clear()
def test_lock(lock: Lock, mocker: MockerFixture) -> None:
"""
must perform lock correctly
"""
clear_mock = mocker.patch("ahriman.application.lock.Lock.clear")
open_mock = mocker.patch("ahriman.application.lock.Lock._open")
watch_mock = mocker.patch("ahriman.application.lock.Lock._watch", return_value=True)
write_mock = mocker.patch("ahriman.application.lock.Lock._write")
lock.lock()
clear_mock.assert_not_called()
open_mock.assert_called_once_with()
watch_mock.assert_called_once_with()
write_mock.assert_called_once_with(is_locked=True)
def test_lock_clear(lock: Lock, mocker: MockerFixture) -> None:
"""
must clear lock file before lock if force flag is set
"""
mocker.patch("ahriman.application.lock.Lock._open")
mocker.patch("ahriman.application.lock.Lock._watch")
mocker.patch("ahriman.application.lock.Lock._write")
clear_mock = mocker.patch("ahriman.application.lock.Lock.clear")
lock.force = True
lock.lock()
clear_mock.assert_called_once_with()
def test_enter(lock: Lock, mocker: MockerFixture) -> None:
"""
must process with context manager
"""
check_user_mock = mocker.patch("ahriman.application.lock.Lock.check_user")
check_version_mock = mocker.patch("ahriman.application.lock.Lock.check_version")
lock_mock = mocker.patch("ahriman.application.lock.Lock.lock")
update_status_mock = mocker.patch("ahriman.core.status.Client.status_update")
with lock:
pass
check_user_mock.assert_called_once_with()
check_version_mock.assert_called_once_with()
lock_mock.assert_called_once_with()
update_status_mock.assert_has_calls([MockCall(BuildStatusEnum.Building), MockCall(BuildStatusEnum.Success)])
def test_exit_with_exception(lock: Lock, mocker: MockerFixture) -> None:
"""
must process with context manager in case if exception raised
"""
mocker.patch("ahriman.application.lock.Lock.check_user")
mocker.patch("ahriman.application.lock.Lock.clear")
mocker.patch("ahriman.application.lock.Lock.lock")
update_status_mock = mocker.patch("ahriman.core.status.Client.status_update")
with pytest.raises(ValueError):
with lock:
raise ValueError()
update_status_mock.assert_has_calls([MockCall(BuildStatusEnum.Building), MockCall(BuildStatusEnum.Failed)])
@@ -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)
@@ -0,0 +1,47 @@
import pytest
from ahriman.core.alpm.remote import AUR, Official, OfficialSyncdb, Remote
@pytest.fixture
def aur() -> AUR:
"""
aur helper fixture
Returns:
AUR: aur helper instance
"""
return AUR()
@pytest.fixture
def official() -> Official:
"""
official repository fixture
Returns:
Official: official repository helper instance
"""
return Official()
@pytest.fixture
def official_syncdb() -> OfficialSyncdb:
"""
official repository fixture with database processing
Returns:
OfficialSyncdb: official repository with database processing helper instance
"""
return OfficialSyncdb()
@pytest.fixture
def remote() -> Remote:
"""
official repository fixture
Returns:
Remote: official repository helper instance
"""
return Remote()
@@ -0,0 +1,177 @@
import json
import pytest
import requests
from pathlib import Path
from pytest_mock import MockerFixture
from unittest.mock import MagicMock, call as MockCall
from ahriman.core.alpm.remote import AUR
from ahriman.core.exceptions import PackageInfoError, UnknownPackageError
from ahriman.models.aur_package import AURPackage
def _get_response(resource_path_root: Path) -> str:
"""
load response from resource file
Args:
resource_path_root(Path): path to resource root
Returns:
str: response text
"""
return (resource_path_root / "models" / "package_ahriman_aur").read_text()
def test_parse_response(aur_package_ahriman: AURPackage, resource_path_root: Path) -> None:
"""
must parse success response
"""
response = _get_response(resource_path_root)
assert AUR.parse_response(json.loads(response)) == [aur_package_ahriman]
def test_parse_response_error(resource_path_root: Path) -> None:
"""
must raise exception on invalid response
"""
response = (resource_path_root / "models" / "aur_error").read_text()
with pytest.raises(PackageInfoError, match="Incorrect request type specified."):
AUR.parse_response(json.loads(response))
def test_parse_response_unknown_error() -> None:
"""
must raise exception on invalid response with empty error message
"""
with pytest.raises(PackageInfoError, match="Unknown API error"):
AUR.parse_response({"type": "error"})
def test_remote_git_url(aur_package_ahriman: AURPackage) -> None:
"""
must generate package git url
"""
git_url = AUR.remote_git_url(aur_package_ahriman.package_base, aur_package_ahriman.repository)
assert git_url.endswith(".git")
assert git_url.startswith(AUR.DEFAULT_AUR_URL)
def test_remote_web_url(aur_package_ahriman: AURPackage) -> None:
"""
must generate package web url
"""
web_url = AUR.remote_web_url(aur_package_ahriman.package_base)
assert web_url.startswith(AUR.DEFAULT_AUR_URL)
def test_aur_request(aur: AUR, aur_package_ahriman: AURPackage,
mocker: MockerFixture, resource_path_root: Path) -> None:
"""
must perform request to AUR
"""
response_mock = MagicMock()
response_mock.json.return_value = json.loads(_get_response(resource_path_root))
request_mock = mocker.patch("ahriman.core.alpm.remote.AUR.make_request", return_value=response_mock)
assert aur.aur_request("info", "ahriman") == [aur_package_ahriman]
request_mock.assert_called_once_with("GET", "https://aur.archlinux.org/rpc/v5/info/ahriman", params=[])
def test_aur_request_multi_arg(aur: AUR) -> None:
"""
must raise PackageInfoError if invalid amount of arguments supplied
"""
with pytest.raises(PackageInfoError):
aur.aur_request("search", "ahriman", "is", "cool")
with pytest.raises(PackageInfoError):
aur.aur_request("search")
def test_aur_request_with_kwargs(aur: AUR, aur_package_ahriman: AURPackage,
mocker: MockerFixture, resource_path_root: Path) -> None:
"""
must perform request to AUR with named parameters
"""
response_mock = MagicMock()
response_mock.json.return_value = json.loads(_get_response(resource_path_root))
request_mock = mocker.patch("ahriman.core.alpm.remote.AUR.make_request", return_value=response_mock)
assert aur.aur_request("search", "ahriman", by="name") == [aur_package_ahriman]
request_mock.assert_called_once_with("GET", "https://aur.archlinux.org/rpc/v5/search/ahriman",
params=[("by", "name")])
def test_aur_request_failed(aur: AUR, mocker: MockerFixture) -> None:
"""
must reraise generic exception
"""
mocker.patch("requests.Session.request", side_effect=Exception)
with pytest.raises(Exception):
aur.aur_request("info", "ahriman")
def test_aur_request_failed_http_error(aur: AUR, mocker: MockerFixture) -> None:
""" must reraise http exception
"""
mocker.patch("requests.Session.request", side_effect=requests.HTTPError)
with pytest.raises(requests.HTTPError):
aur.aur_request("info", "ahriman")
def test_package_info(aur: AUR, aur_package_ahriman: AURPackage, mocker: MockerFixture) -> None:
"""
must make request for info
"""
request_mock = mocker.patch("ahriman.core.alpm.remote.AUR.aur_request", return_value=[aur_package_ahriman])
assert aur.package_info(aur_package_ahriman.name, pacman=None) == aur_package_ahriman
request_mock.assert_called_once_with("info", aur_package_ahriman.name)
def test_package_info_not_found(aur: AUR, aur_package_ahriman: AURPackage, mocker: MockerFixture) -> None:
"""
must raise UnknownPackageError in case if no package was found
"""
mocker.patch("ahriman.core.alpm.remote.AUR.aur_request", return_value=[])
with pytest.raises(UnknownPackageError, match=aur_package_ahriman.name):
assert aur.package_info(aur_package_ahriman.name, pacman=None)
def test_package_provided_by(aur: AUR, aur_package_ahriman: AURPackage, aur_package_akonadi: AURPackage,
mocker: MockerFixture) -> None:
"""
must search for packages which provide required one
"""
aur_package_ahriman.provides.append(aur_package_ahriman.name)
search_mock = mocker.patch("ahriman.core.alpm.remote.AUR.package_search", return_value=[
aur_package_ahriman, aur_package_akonadi
])
info_mock = mocker.patch("ahriman.core.alpm.remote.AUR.package_info", side_effect=[
aur_package_ahriman, aur_package_akonadi
])
assert aur.package_provided_by(aur_package_ahriman.name, pacman=None) == [aur_package_ahriman]
search_mock.assert_called_once_with(aur_package_ahriman.name, pacman=None, search_by="provides")
info_mock.assert_has_calls([
MockCall(aur_package_ahriman.name, pacman=None), MockCall(aur_package_akonadi.name, pacman=None)
])
def test_package_search(aur: AUR, aur_package_ahriman: AURPackage, mocker: MockerFixture) -> None:
"""
must make request for search
"""
request_mock = mocker.patch("ahriman.core.alpm.remote.AUR.aur_request", return_value=[aur_package_ahriman])
assert aur.package_search(aur_package_ahriman.name, pacman=None, search_by=None) == [aur_package_ahriman]
request_mock.assert_called_once_with("search", aur_package_ahriman.name, by="name-desc")
def test_package_search_provides(aur: AUR, aur_package_ahriman: AURPackage, mocker: MockerFixture) -> None:
"""
must make request for search with custom field
"""
request_mock = mocker.patch("ahriman.core.alpm.remote.AUR.aur_request")
aur.package_search(aur_package_ahriman.name, pacman=None, search_by="provides")
request_mock.assert_called_once_with("search", aur_package_ahriman.name, by="provides")
@@ -0,0 +1,134 @@
import json
import pytest
import requests
from pathlib import Path
from pytest_mock import MockerFixture
from unittest.mock import MagicMock
from ahriman.core.alpm.remote import Official
from ahriman.core.exceptions import PackageInfoError, UnknownPackageError
from ahriman.models.aur_package import AURPackage
def _get_response(resource_path_root: Path) -> str:
"""
load response from resource file
Args:
resource_path_root(Path): path to resource root
Returns:
str: response text
"""
return (resource_path_root / "models" / "package_akonadi_aur").read_text()
def test_parse_response(aur_package_akonadi: AURPackage, resource_path_root: Path) -> None:
"""
must parse success response
"""
response = _get_response(resource_path_root)
assert Official.parse_response(json.loads(response)) == [aur_package_akonadi]
def test_parse_response_unknown_error(resource_path_root: Path) -> None:
"""
must raise exception on invalid response with empty error message
"""
response = (resource_path_root / "models" / "official_error").read_text()
with pytest.raises(PackageInfoError, match="API validation error"):
Official.parse_response(json.loads(response))
def test_remote_git_url(aur_package_akonadi: AURPackage) -> None:
"""
must generate package git url for core packages
"""
git_urls = [
Official.remote_git_url(aur_package_akonadi.package_base, repository)
for repository in ("core", "extra", "Core", "Extra")
]
assert all(git_url.endswith(f"{aur_package_akonadi.package_base}.git") for git_url in git_urls)
assert len(set(git_urls)) == 1
def test_remote_web_url(aur_package_akonadi: AURPackage) -> None:
"""
must generate package git url
"""
web_url = Official.remote_web_url(aur_package_akonadi.package_base)
assert web_url.startswith(Official.DEFAULT_ARCHLINUX_URL)
def test_arch_request(official: Official, aur_package_akonadi: AURPackage,
mocker: MockerFixture, resource_path_root: Path) -> None:
"""
must perform request to official repositories
"""
response_mock = MagicMock()
response_mock.json.return_value = json.loads(_get_response(resource_path_root))
request_mock = mocker.patch("ahriman.core.alpm.remote.Official.make_request", return_value=response_mock)
assert official.arch_request("akonadi", by="q") == [aur_package_akonadi]
request_mock.assert_called_once_with(
"GET", "https://archlinux.org/packages/search/json",
params=[("repo", repository) for repository in Official.DEFAULT_SEARCH_REPOSITORIES] + [("q", "akonadi")])
def test_arch_request_failed(official: Official, mocker: MockerFixture) -> None:
"""
must reraise generic exception
"""
mocker.patch("requests.Session.request", side_effect=Exception)
with pytest.raises(Exception):
official.arch_request("akonadi", by="q")
def test_arch_request_failed_http_error(official: Official, mocker: MockerFixture) -> None:
"""
must reraise http exception
"""
mocker.patch("requests.Session.request", side_effect=requests.HTTPError)
with pytest.raises(requests.HTTPError):
official.arch_request("akonadi", by="q")
def test_package_info(official: Official, aur_package_akonadi: AURPackage, mocker: MockerFixture) -> None:
"""
must make request for info
"""
request_mock = mocker.patch("ahriman.core.alpm.remote.Official.arch_request",
return_value=[aur_package_akonadi])
assert official.package_info(aur_package_akonadi.name, pacman=None) == aur_package_akonadi
request_mock.assert_called_once_with(aur_package_akonadi.name, by="name")
def test_package_info_not_found(official: Official, aur_package_ahriman: AURPackage, mocker: MockerFixture) -> None:
"""
must raise UnknownPackageError in case if no package was found
"""
mocker.patch("ahriman.core.alpm.remote.Official.arch_request", return_value=[])
with pytest.raises(UnknownPackageError, match=aur_package_ahriman.name):
assert official.package_info(aur_package_ahriman.name, pacman=None)
def test_package_search(official: Official, aur_package_akonadi: AURPackage, mocker: MockerFixture) -> None:
"""
must make request for search
"""
request_mock = mocker.patch("ahriman.core.alpm.remote.Official.arch_request",
return_value=[aur_package_akonadi])
assert official.package_search(aur_package_akonadi.name, pacman=None, search_by=None) == [
aur_package_akonadi,
]
request_mock.assert_called_once_with(aur_package_akonadi.name, by="q")
def test_package_search_name(official: Official, aur_package_akonadi: AURPackage, mocker: MockerFixture) -> None:
"""
must make request for search with custom field
"""
request_mock = mocker.patch("ahriman.core.alpm.remote.Official.arch_request")
official.package_search(aur_package_akonadi.name, pacman=None, search_by="name")
request_mock.assert_called_once_with(aur_package_akonadi.name, by="name")
@@ -0,0 +1,57 @@
import pytest
from pytest_mock import MockerFixture
from ahriman.core.alpm.pacman import Pacman
from ahriman.core.alpm.remote import OfficialSyncdb
from ahriman.core.exceptions import UnknownPackageError
from ahriman.models.aur_package import AURPackage
def test_package_info(official_syncdb: OfficialSyncdb, aur_package_akonadi: AURPackage, pacman: Pacman,
mocker: MockerFixture) -> None:
"""
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", return_value=[aur_package_akonadi])
assert official_syncdb.package_info(aur_package_akonadi.name, pacman=pacman) == aur_package_akonadi
get_mock.assert_called_once_with(aur_package_akonadi.name)
def test_package_info_no_pacman(official_syncdb: OfficialSyncdb, aur_package_akonadi: AURPackage) -> None:
"""
must raise UnknownPackageError if no pacman set
"""
with pytest.raises(UnknownPackageError, match=aur_package_akonadi.name):
official_syncdb.package_info(aur_package_akonadi.name, pacman=None)
def test_package_info_not_found(official_syncdb: OfficialSyncdb, aur_package_akonadi: AURPackage, pacman: Pacman,
mocker: MockerFixture) -> None:
"""
must raise UnknownPackage exception in case if no package was found
"""
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)
def test_package_provided_by(official_syncdb: OfficialSyncdb, aur_package_akonadi: AURPackage, pacman: Pacman,
mocker: MockerFixture) -> None:
"""
must search by provides in database
"""
mocker.patch("ahriman.models.aur_package.AURPackage.from_pacman", return_value=aur_package_akonadi)
get_mock = mocker.patch("ahriman.core.alpm.pacman.Pacman.provided_by", return_value=[aur_package_akonadi])
assert official_syncdb.package_provided_by(aur_package_akonadi.name, pacman=pacman) == [aur_package_akonadi]
get_mock.assert_called_once_with(aur_package_akonadi.name)
def test_package_provided_by_no_pacman(official_syncdb: OfficialSyncdb, aur_package_akonadi: AURPackage) -> None:
"""
must return empty list if no pacman set
"""
assert official_syncdb.package_provided_by(aur_package_akonadi.name, pacman=None) == []
@@ -0,0 +1,148 @@
import pytest
from dataclasses import replace
from pytest_mock import MockerFixture
from unittest.mock import call as MockCall
from ahriman.core.alpm.pacman import Pacman
from ahriman.core.alpm.remote import Remote
from ahriman.core.exceptions import UnknownPackageError
from ahriman.models.aur_package import AURPackage
def test_info(aur_package_ahriman: AURPackage, pacman: Pacman, mocker: MockerFixture) -> None:
"""
must call info method
"""
info_mock = mocker.patch("ahriman.core.alpm.remote.Remote.package_info", return_value=aur_package_ahriman)
assert Remote.info(aur_package_ahriman.name, pacman=pacman) == aur_package_ahriman
info_mock.assert_called_once_with(aur_package_ahriman.name, pacman=pacman)
def test_info_not_found(aur_package_ahriman: AURPackage, pacman: Pacman, mocker: MockerFixture) -> None:
"""
must raise UnknownPackageError if no package found and search by provides is disabled
"""
mocker.patch("ahriman.core.alpm.remote.Remote.package_info",
side_effect=UnknownPackageError(aur_package_ahriman.name))
with pytest.raises(UnknownPackageError):
Remote.info(aur_package_ahriman.name, pacman=pacman)
def test_info_include_provides(aur_package_ahriman: AURPackage, pacman: Pacman, mocker: MockerFixture) -> None:
"""
must perform search through provides list is set
"""
mocker.patch("ahriman.core.alpm.remote.Remote.package_info",
side_effect=UnknownPackageError(aur_package_ahriman.name))
provided_mock = mocker.patch("ahriman.core.alpm.remote.Remote.package_provided_by",
return_value=[aur_package_ahriman])
assert Remote.info(aur_package_ahriman.name, pacman=pacman, include_provides=True) == aur_package_ahriman
provided_mock.assert_called_once_with(aur_package_ahriman.name, pacman=pacman)
def test_info_include_provides_not_found(aur_package_ahriman: AURPackage, pacman: Pacman,
mocker: MockerFixture) -> None:
"""
must raise UnknownPackageError if no package found and search by provides returns empty list
"""
mocker.patch("ahriman.core.alpm.remote.Remote.package_info",
side_effect=UnknownPackageError(aur_package_ahriman.name))
mocker.patch("ahriman.core.alpm.remote.Remote.package_provided_by", return_value=[])
with pytest.raises(UnknownPackageError):
Remote.info("ahriman", pacman=pacman, include_provides=True)
def test_multisearch(aur_package_ahriman: AURPackage, pacman: Pacman, mocker: MockerFixture) -> None:
"""
must search in AUR with multiple words
"""
terms = ["ahriman", "is", "cool"]
search_mock = mocker.patch("ahriman.core.alpm.remote.Remote.package_search", return_value=[aur_package_ahriman])
assert Remote.multisearch(*terms, pacman=pacman, search_by="name") == [aur_package_ahriman]
search_mock.assert_has_calls([
MockCall("ahriman", pacman=pacman, search_by="name"),
MockCall("cool", pacman=pacman, search_by="name"),
])
def test_multisearch_empty(pacman: Pacman, mocker: MockerFixture) -> None:
"""
must return empty list if no long terms supplied
"""
terms = ["it", "is"]
search_mock = mocker.patch("ahriman.core.alpm.remote.Remote.package_search")
assert Remote.multisearch(*terms, pacman=pacman) == []
search_mock.assert_not_called()
def test_multisearch_single(aur_package_ahriman: AURPackage, pacman: Pacman, mocker: MockerFixture) -> None:
"""
must search in AUR with one word
"""
search_mock = mocker.patch("ahriman.core.alpm.remote.Remote.package_search", return_value=[aur_package_ahriman])
assert Remote.multisearch("ahriman", pacman=pacman) == [aur_package_ahriman]
search_mock.assert_called_once_with("ahriman", pacman=pacman, search_by=None)
def test_multisearch_remove_duplicates(aur_package_ahriman: AURPackage, pacman: Pacman, mocker: MockerFixture) -> None:
"""
must remove duplicates from search result
"""
package1 = replace(aur_package_ahriman)
package2 = replace(aur_package_ahriman, name="ahriman-triggers")
mocker.patch("ahriman.core.alpm.remote.Remote.package_search", return_value=[package1, package2])
assert Remote.multisearch("ahriman", pacman=pacman) == [package1]
def test_remote_git_url(remote: Remote) -> None:
"""
must raise NotImplemented for missing remote git url
"""
with pytest.raises(NotImplementedError):
remote.remote_git_url("package", "repositories")
def test_remote_web_url(remote: Remote) -> None:
"""
must raise NotImplemented for missing remote web url
"""
with pytest.raises(NotImplementedError):
remote.remote_web_url("package")
def test_search(pacman: Pacman, mocker: MockerFixture) -> None:
"""
must call search method
"""
search_mock = mocker.patch("ahriman.core.alpm.remote.Remote.package_search")
Remote.search("ahriman", pacman=pacman, search_by="name")
search_mock.assert_called_once_with("ahriman", pacman=pacman, search_by="name")
def test_package_info(remote: Remote, pacman: Pacman) -> None:
"""
must raise NotImplemented for missing package info method
"""
with pytest.raises(NotImplementedError):
remote.package_info("package", pacman=pacman)
def test_package_provided_by(remote: Remote, pacman: Pacman) -> None:
"""
must return empty list for provides method
"""
assert remote.package_provided_by("package", pacman=pacman) == []
def test_package_search(remote: Remote, pacman: Pacman) -> None:
"""
must raise NotImplemented for missing package search method
"""
with pytest.raises(NotImplementedError):
remote.package_search("package", pacman=pacman, search_by=None)
@@ -0,0 +1,292 @@
import pyalpm
import pytest
import tarfile
from pathlib import Path
from pytest_mock import MockerFixture
from tempfile import TemporaryDirectory
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
def test_init_with_local_cache(configuration: Configuration, mocker: MockerFixture) -> None:
"""
must sync repositories at the start if set
"""
mocker.patch("ahriman.core.alpm.pacman.Pacman.database_copy")
sync_mock = mocker.patch("ahriman.core.alpm.pacman.Pacman.database_sync")
configuration.set_option("alpm", "use_ahriman_cache", "yes")
_, repository_id = configuration.check_loaded()
# pyalpm.Handle is trying to reach the directory we've asked, thus we need to patch it a bit
with TemporaryDirectory(ignore_cleanup_errors=True) as pacman_root:
mocker.patch.object(RepositoryPaths, "pacman", Path(pacman_root))
# during the creation pyalpm.Handle will create also version file which we would like to remove later
pacman = Pacman(repository_id, configuration, refresh_database=PacmanSynchronization.Enabled)
assert pacman.handle
sync_mock.assert_called_once_with(pytest.helpers.anyvar(int), force=False)
def test_init_with_local_cache_forced(configuration: Configuration, mocker: MockerFixture) -> None:
"""
must sync repositories at the start if set with force flag
"""
mocker.patch("ahriman.core.alpm.pacman.Pacman.database_copy")
sync_mock = mocker.patch("ahriman.core.alpm.pacman.Pacman.database_sync")
configuration.set_option("alpm", "use_ahriman_cache", "yes")
_, repository_id = configuration.check_loaded()
# pyalpm.Handle is trying to reach the directory we've asked, thus we need to patch it a bit
with TemporaryDirectory(ignore_cleanup_errors=True) as pacman_root:
mocker.patch.object(RepositoryPaths, "pacman", Path(pacman_root))
# during the creation pyalpm.Handle will create also version file which we would like to remove later
pacman = Pacman(repository_id, configuration, refresh_database=PacmanSynchronization.Force)
assert pacman.handle
sync_mock.assert_called_once_with(pytest.helpers.anyvar(int), force=True)
def test_database_copy(pacman: Pacman, mocker: MockerFixture) -> None:
"""
must copy database from root
"""
database = next(db for db in pacman.handle.get_syncdbs() if db.name == "core")
path = Path("randomname")
dst_path = Path("/var/lib/pacman/sync/core.db")
mocker.patch("pathlib.Path.is_dir", return_value=True)
# root database exists, local database does not
mocker.patch("pathlib.Path.is_file", autospec=True, side_effect=lambda p: p.is_relative_to(path))
mkdir_mock = mocker.patch("pathlib.Path.mkdir")
copy_mock = mocker.patch("shutil.copy")
owner_guard_mock = mocker.patch("ahriman.models.repository_paths.RepositoryPaths.preserve_owner")
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)
owner_guard_mock.assert_called_once_with()
def test_database_copy_skip(pacman: Pacman, mocker: MockerFixture) -> None:
"""
must do not copy database from root if local cache is disabled
"""
database = next(db for db in pacman.handle.get_syncdbs() if db.name == "core")
path = Path("randomname")
mocker.patch("pathlib.Path.is_dir", return_value=True)
# root database exists, local database does not
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, use_ahriman_cache=False)
copy_mock.assert_not_called()
def test_database_copy_no_directory(pacman: Pacman, mocker: MockerFixture) -> None:
"""
must do not copy database if directory does not exist
"""
database = next(db for db in pacman.handle.get_syncdbs() if db.name == "core")
path = Path("randomname")
mocker.patch("pathlib.Path.is_dir", return_value=False)
# root database exists, local database does not
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, use_ahriman_cache=True)
copy_mock.assert_not_called()
def test_database_copy_no_root_file(pacman: Pacman, mocker: MockerFixture) -> None:
"""
must do not copy database if no repository file exists in filesystem
"""
database = next(db for db in pacman.handle.get_syncdbs() if db.name == "core")
path = Path("randomname")
mocker.patch("pathlib.Path.is_dir", return_value=True)
# root database does not exist, local database does not either
mocker.patch("pathlib.Path.is_file", return_value=False)
copy_mock = mocker.patch("shutil.copy")
pacman.database_copy(pacman.handle, database, path, use_ahriman_cache=True)
copy_mock.assert_not_called()
def test_database_copy_database_exist(pacman: Pacman, mocker: MockerFixture) -> None:
"""
must do not copy database if local cache already exists
"""
database = next(db for db in pacman.handle.get_syncdbs() if db.name == "core")
mocker.patch("pathlib.Path.is_dir", return_value=True)
# root database exists, local database does either
mocker.patch("pathlib.Path.is_file", return_value=True)
copy_mock = mocker.patch("shutil.copy")
pacman.database_copy(pacman.handle, database, Path("root"), use_ahriman_cache=True)
copy_mock.assert_not_called()
def test_database_init(pacman: Pacman) -> None:
"""
must init database with settings
"""
database = pacman.database_init(pacman.handle, "testing", "x86_64")
assert database.servers == ["https://geo.mirror.pkgbuild.com/testing/os/x86_64"]
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()
transaction_mock = MagicMock()
handle_mock.get_syncdbs.return_value = [1, 2]
handle_mock.init_transaction.return_value = transaction_mock
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()
sync_mock.assert_has_calls([MockCall(force=False), MockCall(force=False)])
transaction_mock.release.assert_called_once_with()
def test_database_sync_forced(pacman: Pacman, mocker: MockerFixture) -> None:
"""
must sync databases with force flag
"""
handle_mock = MagicMock()
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_package(pacman: Pacman, package_ahriman: Package, pyalpm_package_ahriman: pyalpm.Package,
mocker: MockerFixture, resource_path_root: Path) -> None:
"""
must load files only for the specified package
"""
mocker.patch("ahriman.core.alpm.pacman.Pacman.package", return_value=[pyalpm_package_ahriman])
handle_mock = MagicMock()
handle_mock.get_syncdbs.return_value = [MagicMock()]
pacman.handle = handle_mock
tarball = resource_path_root / "core" / "arcanisrepo.files.tar.gz"
with tarfile.open(tarball, "r:gz") as fd:
mocker.patch("pathlib.Path.is_file", return_value=True)
mocker.patch("ahriman.core.alpm.pacman.tarfile.open", return_value=fd)
files = pacman.files([package_ahriman.base])
assert len(files) == 1
assert package_ahriman.base in files
def test_files_skip(pacman: Pacman, pyalpm_package_ahriman: pyalpm.Package, mocker: MockerFixture) -> None:
"""
must return empty list if no database found
"""
mocker.patch("ahriman.core.alpm.pacman.Pacman.package", return_value=[pyalpm_package_ahriman])
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([pyalpm_package_ahriman.name])
def test_files_no_content(pacman: Pacman, pyalpm_package_ahriman: pyalpm.Package, mocker: MockerFixture) -> None:
"""
must skip package if no content can be loaded
"""
mocker.patch("ahriman.core.alpm.pacman.Pacman.package", return_value=[pyalpm_package_ahriman])
handle_mock = MagicMock()
handle_mock.get_syncdbs.return_value = [MagicMock()]
pacman.handle = handle_mock
tar_mock = 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([pyalpm_package_ahriman.name])
def test_files_no_entry(pacman: Pacman, pyalpm_package_ahriman: pyalpm.Package, mocker: MockerFixture) -> None:
"""
must skip package if it wasn't found in the archive
"""
mocker.patch("ahriman.core.alpm.pacman.Pacman.package", return_value=[pyalpm_package_ahriman])
handle_mock = MagicMock()
handle_mock.get_syncdbs.return_value = [MagicMock()]
pacman.handle = handle_mock
tar_mock = MagicMock()
tar_mock.extractfile.side_effect = KeyError
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([pyalpm_package_ahriman.name])
def test_package(pacman: Pacman) -> None:
"""
must retrieve package
"""
assert list(pacman.package("pacman"))
def test_package_empty(pacman: Pacman) -> None:
"""
must return empty packages list without exception
"""
assert not list(pacman.package("some-random-name"))
def test_packages(pacman: Pacman) -> None:
"""
package list must not be empty
"""
packages = pacman.packages()
assert packages
assert "pacman" in packages
def test_packages_with_provides(pacman: Pacman) -> None:
"""
package list must contain provides packages
"""
assert "sh" in pacman.packages()
assert "mysql" in pacman.packages() # mariadb
def test_package_provided_by(pacman: Pacman) -> None:
"""
must search through the provides lists
"""
assert list(pacman.provided_by("sh"))
assert list(pacman.provided_by("libacl.so")) # case with exact version
@@ -0,0 +1,212 @@
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(mocker: MockerFixture) -> None:
"""
must copy local database file
"""
copy_mock = mocker.patch("shutil.copy")
PacmanDatabase.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_no_servers(pacman_database: PacmanDatabase) -> None:
"""
must raise PacmanError if no servers are configured
"""
pacman_database.database.servers = []
with pytest.raises(PacmanError):
pacman_database.sync_files(force=False)
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)])
@@ -0,0 +1,37 @@
import pytest
from pathlib import Path
from unittest.mock import MagicMock
from ahriman.core.alpm.pacman_handle import PacmanHandle
def test_package_load() -> None:
"""
must load package from archive path
"""
local = Path("local")
instance = PacmanHandle.ephemeral()
handle_mock = instance.handle = MagicMock()
instance.package_load(local)
handle_mock.load_pkg.assert_called_once_with(str(local))
PacmanHandle._ephemeral = None
def test_getattr() -> None:
"""
must proxy attribute access to underlying handle
"""
instance = PacmanHandle.ephemeral()
assert instance.dbpath
def test_getattr_not_found() -> None:
"""
must raise AttributeError for missing handle attributes
"""
instance = PacmanHandle.ephemeral()
with pytest.raises(AttributeError):
assert instance.random_attribute
@@ -0,0 +1,329 @@
import pytest
from io import StringIO
from pathlib import Path
from ahriman.core.alpm.pkgbuild_parser import PkgbuildParser
from ahriman.core.exceptions import PkgbuildParserError
from ahriman.models.pkgbuild_patch import PkgbuildPatch
def test_expand_array() -> None:
"""
must correctly expand array
"""
assert PkgbuildParser._expand_array(["${pkgbase}{", ",", "-libs", ",", "-fortran}"]) == [
"${pkgbase}", "${pkgbase}-libs", "${pkgbase}-fortran"
]
assert PkgbuildParser._expand_array(["first", "prefix{1", ",", "2", ",", "3}suffix", "last"]) == [
"first", "prefix1suffix", "prefix2suffix", "prefix3suffix", "last"
]
def test_expand_array_no_comma() -> None:
"""
must skip array extraction if there is no comma
"""
assert PkgbuildParser._expand_array(["${pkgbase}{", "-libs", "-fortran}"]) == ["${pkgbase}{", "-libs", "-fortran}"]
def test_expand_array_short() -> None:
"""
must skip array extraction if it is short
"""
assert PkgbuildParser._expand_array(["${pkgbase}{", ","]) == ["${pkgbase}{", ","]
def test_expand_array_exception() -> None:
"""
must raise exception if there is unclosed element
"""
with pytest.raises(PkgbuildParserError):
assert PkgbuildParser._expand_array(["${pkgbase}{", ",", "-libs"])
def test_is_escaped_exception(resource_path_root: Path) -> None:
"""
must raise PkgbuildParserError if no valid utf symbols found
"""
utf8 = resource_path_root / "models" / "utf8"
with utf8.open(encoding="utf8") as content:
content.seek(2)
with pytest.raises(PkgbuildParserError):
assert not PkgbuildParser(content)._is_escaped()
def test_parse_array() -> None:
"""
must parse array
"""
parser = PkgbuildParser(StringIO("var=(first second)"))
assert list(parser.parse()) == [PkgbuildPatch("var", ["first", "second"])]
def test_parse_array_comment() -> None:
"""
must parse array with comments inside
"""
parser = PkgbuildParser(StringIO("""validpgpkeys=(
'F3691687D867B81B51CE07D9BBE43771487328A9' # bpiotrowski@archlinux.org
'86CFFCA918CF3AF47147588051E8B148A9999C34' # evangelos@foutrelis.com
'13975A70E63C361C73AE69EF6EEB81F8981C74C7' # richard.guenther@gmail.com
'D3A93CAD751C2AF4F8C7AD516C35B99309B5FA62' # Jakub Jelinek <jakub@redhat.com>
)"""))
assert list(parser.parse()) == [PkgbuildPatch("validpgpkeys", [
"F3691687D867B81B51CE07D9BBE43771487328A9",
"86CFFCA918CF3AF47147588051E8B148A9999C34",
"13975A70E63C361C73AE69EF6EEB81F8981C74C7",
"D3A93CAD751C2AF4F8C7AD516C35B99309B5FA62",
])]
def test_parse_array_escaped() -> None:
"""
must correctly process quoted brackets
"""
parser = PkgbuildParser(StringIO("""var=(first "(" second)"""))
assert list(parser.parse()) == [PkgbuildPatch("var", ["first", "(", "second"])]
parser = PkgbuildParser(StringIO("""var=(first ")" second)"""))
assert list(parser.parse()) == [PkgbuildPatch("var", ["first", ")", "second"])]
parser = PkgbuildParser(StringIO("""var=(first ')' second)"""))
assert list(parser.parse()) == [PkgbuildPatch("var", ["first", ")", "second"])]
parser = PkgbuildParser(StringIO("""var=(first \\) second)"""))
assert list(parser.parse()) == [PkgbuildPatch("var", ["first", ")", "second"])]
def test_parse_array_exception() -> None:
"""
must raise exception if there is no closing bracket
"""
parser = PkgbuildParser(StringIO("var=(first second"))
with pytest.raises(PkgbuildParserError):
assert list(parser.parse())
def test_parse_function() -> None:
"""
must parse function
"""
parser = PkgbuildParser(StringIO("var() { echo hello world } "))
assert list(parser.parse()) == [PkgbuildPatch("var()", "{ echo hello world }")]
def test_parse_function_eof() -> None:
"""
must parse function with "}" at the end of the file
"""
parser = PkgbuildParser(StringIO("var() { echo hello world }"))
assert list(parser.parse()) == [PkgbuildPatch("var()", "{ echo hello world }")]
def test_parse_function_spaces() -> None:
"""
must parse function with spaces in declaration
"""
parser = PkgbuildParser(StringIO("var ( ) { echo hello world } "))
assert list(parser.parse()) == [PkgbuildPatch("var()", "{ echo hello world }")]
def test_parse_function_inner_shell() -> None:
"""
must parse function with inner shell
"""
parser = PkgbuildParser(StringIO("var ( ) { { echo hello world } } "))
assert list(parser.parse()) == [PkgbuildPatch("var()", "{ { echo hello world } }")]
def test_parse_function_escaped() -> None:
"""
must parse function with bracket in quotes
"""
parser = PkgbuildParser(StringIO("""var ( ) { echo "hello world {" } """))
assert list(parser.parse()) == [PkgbuildPatch("var()", """{ echo "hello world {" }""")]
parser = PkgbuildParser(StringIO("""var ( ) { echo hello world "{" } """))
assert list(parser.parse()) == [PkgbuildPatch("var()", """{ echo hello world "{" }""")]
parser = PkgbuildParser(StringIO("""var ( ) { echo "hello world }" } """))
assert list(parser.parse()) == [PkgbuildPatch("var()", """{ echo "hello world }" }""")]
parser = PkgbuildParser(StringIO("""var ( ) { echo hello world "}" } """))
assert list(parser.parse()) == [PkgbuildPatch("var()", """{ echo hello world "}" }""")]
parser = PkgbuildParser(StringIO("""var ( ) { echo hello world '}' } """))
assert list(parser.parse()) == [PkgbuildPatch("var()", """{ echo hello world '}' }""")]
parser = PkgbuildParser(StringIO("""var ( ) { echo hello world \\} } """))
assert list(parser.parse()) == [PkgbuildPatch("var()", """{ echo hello world \\} }""")]
def test_parse_function_exception() -> None:
"""
must raise exception if no bracket found
"""
parser = PkgbuildParser(StringIO("var() echo hello world } "))
with pytest.raises(PkgbuildParserError):
assert list(parser.parse())
parser = PkgbuildParser(StringIO("var() { echo hello world"))
with pytest.raises(PkgbuildParserError):
assert list(parser.parse())
def test_parse_token_assignment() -> None:
"""
must parse simple assignment
"""
parser = PkgbuildParser(StringIO())
assert next(parser._parse_token("var=value")) == PkgbuildPatch("var", "value")
assert next(parser._parse_token("var=$value")) == PkgbuildPatch("var", "$value")
assert next(parser._parse_token("var=${value}")) == PkgbuildPatch("var", "${value}")
assert next(parser._parse_token("var=${value/-/_}")) == PkgbuildPatch("var", "${value/-/_}")
def test_parse_token_comment() -> None:
"""
must correctly parse comment
"""
parser = PkgbuildParser(StringIO("""first=1 # comment
# comment line
second=2
#third=3
"""))
assert list(parser.parse()) == [
PkgbuildPatch("first", "1"),
PkgbuildPatch("second", "2"),
]
def test_read_comment() -> None:
"""
must read comment correctly
"""
io = StringIO("# comment\nnew line")
io.seek(2)
PkgbuildParser(io)._read_comment()
assert io.tell() == 10
def test_read_comment_skip() -> None:
"""
must skip reading new line if comment ends with new line
"""
io = StringIO("#comment\nnew line")
io.seek(7)
PkgbuildParser(io)._read_comment()
assert io.tell() == 9
def test_read_last() -> None:
"""
must read last symbol from current position
"""
io = StringIO("mock")
io.seek(2)
assert PkgbuildParser(io)._read_last() == (1, "o")
def test_read_last_starting() -> None:
"""
must raise exception if it reads from starting position
"""
with pytest.raises(PkgbuildParserError):
assert PkgbuildParser(StringIO("mock"))._read_last()
def test_read_last_from_position() -> None:
"""
must read last symbol from the specified position
"""
assert PkgbuildParser(StringIO("mock"))._read_last(2) == (2, "c")
def test_parse(resource_path_root: Path) -> None:
"""
must parse complex file
"""
pkgbuild = resource_path_root / "models" / "pkgbuild"
with pkgbuild.open(encoding="utf8") as content:
parser = PkgbuildParser(content)
assert list(parser.parse()) == [
PkgbuildPatch("var", "value"),
PkgbuildPatch("var", "value"),
PkgbuildPatch("var", "value with space"),
PkgbuildPatch("var", "value"),
PkgbuildPatch("var", "$ref"),
PkgbuildPatch("var", "${ref}"),
PkgbuildPatch("var", "$ref value"),
PkgbuildPatch("var", "${ref}value"),
PkgbuildPatch("var", "${ref/-/_}"),
PkgbuildPatch("var", "${ref##.*}"),
PkgbuildPatch("var", "${ref%%.*}"),
PkgbuildPatch("array", ["first", "second", "third", "with space"]),
PkgbuildPatch("array", ["single"]),
PkgbuildPatch("array", ["$ref"]),
PkgbuildPatch("array", ["first", "second", "third"]),
PkgbuildPatch("array", ["first", "second", "third"]),
PkgbuildPatch("array", ["first", "last"]),
PkgbuildPatch("array", ["first", "1suffix", "2suffix", "last"]),
PkgbuildPatch("array", ["first", "prefix1", "prefix2", "last"]),
PkgbuildPatch("array", ["first", "prefix1suffix", "prefix2suffix", "last"]),
PkgbuildPatch("array", ["first", "(", "second"]),
PkgbuildPatch("array", ["first", ")", "second"]),
PkgbuildPatch("array", ["first", "(", "second"]),
PkgbuildPatch("array", ["first", ")", "second"]),
PkgbuildPatch("function()", """{ single line }"""),
PkgbuildPatch("function()", """{
multi
line
}"""),
PkgbuildPatch("function()", """{
c
multi
line
}"""),
PkgbuildPatch("function()", """{
# comment
multi
line
}"""),
PkgbuildPatch("function()", """{
body
}"""),
PkgbuildPatch("function()", """{
body
}"""),
PkgbuildPatch("function_with-package-name()", """{ body }"""),
PkgbuildPatch("function()", """{
first
{ inner shell }
last
}"""),
PkgbuildPatch("function()", """{
body "{" argument
}"""),
PkgbuildPatch("function()", """{
body "}" argument
}"""),
PkgbuildPatch("function()", """{
body '{' argument
}"""),
PkgbuildPatch("function()", """{
body '}' argument
}"""),
PkgbuildPatch("function()", """{
# we don't care about unclosed quotation in comments
body # no, I said we really don't care
}"""),
PkgbuildPatch("function()", """{
mv "$pkgdir"/usr/share/fonts/站酷小薇体 "$pkgdir"/usr/share/fonts/zcool-xiaowei-regular
mv "$pkgdir"/usr/share/licenses/"$pkgname"/LICENSE.站酷小薇体 "$pkgdir"/usr/share/licenses/"$pkgname"/LICENSE.zcool-xiaowei-regular
}"""),
PkgbuildPatch("var", "value"),
PkgbuildPatch("array", ["first", "second", "third"]),
]
@@ -0,0 +1,71 @@
import pytest
from pathlib import Path
from pytest_mock import MockerFixture
from ahriman.core.alpm.repo import Repo
from ahriman.models.package import Package
from ahriman.models.repository_paths import RepositoryPaths
def test_root(repository_paths: RepositoryPaths) -> None:
"""
must correctly define repository root
"""
assert Repo(repository_paths.repository_id.name, repository_paths, []).root == repository_paths.repository
assert Repo(repository_paths.repository_id.name, repository_paths, [], Path("path")).root == Path("path")
def test_repo_path(repo: Repo) -> None:
"""
name must be something like archive name
"""
assert repo.repo_path.name.endswith("db.tar.gz")
def test_repo_add(repo: Repo, mocker: MockerFixture) -> None:
"""
must call repo-add on package addition
"""
check_output_mock = mocker.patch("ahriman.core.alpm.repo.check_output")
repo.add(Path("path"))
check_output_mock.assert_called_once() # it will be checked later
assert check_output_mock.call_args[0][0] == "repo-add"
assert "--remove" in check_output_mock.call_args[0]
def test_repo_init(repo: Repo, mocker: MockerFixture) -> None:
"""
must call repo-add with empty package list on repo initializing
"""
check_output_mock = mocker.patch("ahriman.core.alpm.repo.check_output")
repo.init()
check_output_mock.assert_called_once() # it will be checked later
assert check_output_mock.call_args[0][0] == "repo-add"
def test_repo_remove(repo: Repo, package_ahriman: Package, mocker: MockerFixture) -> None:
"""
must call repo-remove on package removal
"""
filepath = package_ahriman.packages[package_ahriman.base].filepath
mocker.patch("pathlib.Path.glob", return_value=[])
check_output_mock = mocker.patch("ahriman.core.alpm.repo.check_output")
repo.remove(package_ahriman.base, filepath)
check_output_mock.assert_called_once() # it will be checked later
assert check_output_mock.call_args[0][0] == "repo-remove"
assert package_ahriman.base in check_output_mock.call_args[0]
def test_repo_remove_fail_no_file(repo: Repo, mocker: MockerFixture) -> None:
"""
must fail removal on missing file
"""
mocker.patch("pathlib.Path.glob", return_value=[Path("package.pkg.tar.xz")])
mocker.patch("pathlib.Path.unlink", side_effect=FileNotFoundError)
with pytest.raises(FileNotFoundError):
repo.remove("package", Path("package.pkg.tar.xz"))
@@ -0,0 +1,43 @@
import pytest
from pytest_mock import MockerFixture
from typing import Any
from ahriman.core.alpm.pacman import Pacman
from ahriman.core.build_tools.package_archive import PackageArchive
from ahriman.core.build_tools.sources import Sources
from ahriman.models.package import Package
from ahriman.models.repository_paths import RepositoryPaths
from ahriman.models.scan_paths import ScanPaths
@pytest.fixture
def package_archive_ahriman(package_ahriman: Package, repository_paths: RepositoryPaths, pacman: Pacman,
scan_paths: ScanPaths, passwd: Any, mocker: MockerFixture) -> PackageArchive:
"""
package archive fixture
Args:
package_ahriman(Package): package test instance
repository_paths(RepositoryPaths): repository paths test instance
pacman(Pacman): pacman test instance
scan_paths(ScanPaths): scan paths test instance
passwd(Any): passwd structure test instance
mocker(MockerFixture): mocker object
Returns:
PackageArchive: package archive test instance
"""
mocker.patch("ahriman.models.repository_paths.getpwuid", return_value=passwd)
return PackageArchive(repository_paths.build_root, package_ahriman, pacman, scan_paths)
@pytest.fixture
def sources() -> Sources:
"""
sources fixture
Returns:
Sources: sources instance
"""
return Sources()
@@ -0,0 +1,231 @@
from io import BytesIO
from pathlib import Path
from pytest_mock import MockerFixture
from unittest.mock import MagicMock, PropertyMock
from ahriman.core.build_tools.package_archive import PackageArchive
from ahriman.core.exceptions import UnknownPackageError
from ahriman.models.filesystem_package import FilesystemPackage
def test_dynamic_needed(mocker: MockerFixture) -> None:
"""
must correctly define list of dynamically linked libraries
"""
mocker.patch("ahriman.core.build_tools.package_archive.PackageArchive.is_elf", return_value=True)
linked = PackageArchive.dynamic_needed(Path(".tox") / "tests" / "bin" / "python")
assert linked
assert next(library for library in linked if library.startswith("libpython"))
assert next(library for library in linked if library.startswith("libc"))
def test_dynamic_needed_not_elf(mocker: MockerFixture) -> None:
"""
must skip checking if not an elf file
"""
mocker.patch("ahriman.core.build_tools.package_archive.PackageArchive.is_elf", return_value=False)
assert not PackageArchive.dynamic_needed(Path(".tox") / "tests" / "bin" / "python")
def test_dynamic_needed_no_section(mocker: MockerFixture) -> None:
"""
must skip checking if there was no dynamic section found
"""
mocker.patch("ahriman.core.build_tools.package_archive.PackageArchive.is_elf", return_value=True)
mocker.patch("elftools.elf.elffile.ELFFile.iter_sections", return_value=[])
assert not PackageArchive.dynamic_needed(Path(".tox") / "tests" / "bin" / "python")
def test_is_elf() -> None:
"""
must correctly define elf file
"""
assert not PackageArchive.is_elf(BytesIO())
assert not PackageArchive.is_elf(BytesIO(b"random string"))
assert PackageArchive.is_elf(BytesIO(b"\x7fELF"))
assert PackageArchive.is_elf(BytesIO(b"\x7fELF\nrandom string"))
def test_load_pacman_package(package_archive_ahriman: PackageArchive, mocker: MockerFixture) -> None:
"""
must correctly load filesystem package from pacman
"""
directory = f"{package_archive_ahriman.package.base}-{package_archive_ahriman.package.version}"
path = Path("/") / "var" / "lib" / "pacman" / "local" / directory / "files"
package = MagicMock()
type(package).depends = PropertyMock(return_value=["depends"])
type(package).opt_depends = PropertyMock(return_value=["opt_depends"])
info_mock = mocker.patch("ahriman.core.alpm.remote.OfficialSyncdb.info", return_value=package)
assert package_archive_ahriman._load_pacman_package(path) == FilesystemPackage(
package_name=package_archive_ahriman.package.base,
depends={"depends"},
opt_depends={"opt_depends"},
)
info_mock.assert_called_once_with(package_archive_ahriman.package.base, pacman=package_archive_ahriman.pacman)
def test_load_pacman_package_exception(package_archive_ahriman: PackageArchive, mocker: MockerFixture) -> None:
"""
must return empty package if no package found
"""
directory = f"{package_archive_ahriman.package.base}-{package_archive_ahriman.package.version}"
path = Path("/") / "var" / "lib" / "pacman" / "local" / directory / "files"
mocker.patch("ahriman.core.alpm.remote.OfficialSyncdb.info",
side_effect=UnknownPackageError(package_archive_ahriman.package.base))
assert package_archive_ahriman._load_pacman_package(path) == FilesystemPackage(
package_name=package_archive_ahriman.package.base,
depends=set(),
opt_depends=set(),
)
def test_raw_dependencies_packages(package_archive_ahriman: PackageArchive, mocker: MockerFixture) -> None:
"""
must correctly extract raw dependencies list
"""
packages = {
package_archive_ahriman.package.base: FilesystemPackage(
package_name=package_archive_ahriman.package.base,
depends=set(),
opt_depends=set(),
directories=[Path("usr") / "dir2"],
files=[Path("file1")],
),
"package1": FilesystemPackage(
package_name="package1",
depends=set(),
opt_depends=set(),
directories=[Path("package1") / "dir1", Path("usr") / "dir2"],
files=[Path("package1") / "file1", Path("package1") / "file2"],
),
"package2": FilesystemPackage(
package_name="package2",
depends=set(),
opt_depends=set(),
directories=[Path("usr") / "dir2", Path("package2") / "dir3", Path("package2") / "dir4"],
files=[Path("package2") / "file4", Path("package2") / "file3"],
),
}
mocker.patch("ahriman.core.build_tools.package_archive.PackageArchive.installed_packages", return_value=packages)
mocker.patch("ahriman.core.build_tools.package_archive.PackageArchive.depends_on_paths", return_value=(
{"file1", "file3"},
{Path("usr") / "dir2", Path("dir3"), Path("package2") / "dir4"},
))
result = package_archive_ahriman._raw_dependencies_packages()
assert result == {
Path("package1") / "file1": [packages["package1"]],
Path("package2") / "file3": [packages["package2"]],
Path("package2") / "dir4": [packages["package2"]],
Path("usr") / "dir2": [packages["package1"], packages["package2"]],
}
def test_refine_dependencies(package_archive_ahriman: PackageArchive, mocker: MockerFixture) -> None:
"""
must correctly refine dependencies list
"""
base_package = MagicMock()
type(base_package).depends = PropertyMock(return_value=["base"])
info_mock = mocker.patch("ahriman.core.alpm.remote.OfficialSyncdb.info", return_value=base_package)
path1 = Path("usr") / "lib" / "python3.12"
path2 = path1 / "site-packages"
path3 = Path("usr") / "lib" / "path"
path4 = Path("usr") / "lib" / "whatever"
path5 = Path("usr") / "share" / "applications"
path6 = Path("etc")
package1 = FilesystemPackage(package_name="package1", depends={"package5"}, opt_depends={"package2"})
package2 = FilesystemPackage(package_name="package2", depends={"package1"}, opt_depends=set())
package3 = FilesystemPackage(package_name="package3", depends=set(), opt_depends={"package1"})
package4 = FilesystemPackage(package_name="base", depends=set(), opt_depends=set())
package5 = FilesystemPackage(package_name="package5", depends={"package1"}, opt_depends=set())
package6 = FilesystemPackage(package_name="package6", depends=set(), opt_depends=set())
assert package_archive_ahriman._refine_dependencies({
path1: [package1, package2, package3, package5, package6],
path2: [package1, package2, package3, package5],
path3: [package1, package4],
path4: [package1],
path5: [package1],
path6: [package1],
}) == {
path1: [package6],
path2: [package1, package5],
path4: [package1],
}
info_mock.assert_called_once_with("base", pacman=package_archive_ahriman.pacman)
def test_depends_on(package_archive_ahriman: PackageArchive, mocker: MockerFixture) -> None:
"""
must extract packages and files which are dependencies for the package
"""
raw_mock = mocker.patch("ahriman.core.build_tools.package_archive.PackageArchive._raw_dependencies_packages",
return_value="1")
refined_mock = mocker.patch(
"ahriman.core.build_tools.package_archive.PackageArchive._refine_dependencies", return_value={
Path("package1") / "file1": [FilesystemPackage(package_name="package1", depends=set(), opt_depends=set())],
Path("package2") / "file3": [FilesystemPackage(package_name="package2", depends=set(), opt_depends=set())],
Path("package2") / "dir4": [FilesystemPackage(package_name="package2", depends=set(), opt_depends=set())],
Path("usr") / "dir2": [
FilesystemPackage(package_name="package1", depends=set(), opt_depends=set()),
FilesystemPackage(package_name="package2", depends=set(), opt_depends=set()),
],
}
)
result = package_archive_ahriman.depends_on()
raw_mock.assert_called_once_with()
refined_mock.assert_called_once_with("1")
assert result.paths == {
"package1/file1": ["package1"],
"package2/file3": ["package2"],
"package2/dir4": ["package2"],
"usr/dir2": ["package1", "package2"]
}
def test_depends_on_paths(package_archive_ahriman: PackageArchive, mocker: MockerFixture) -> None:
"""
must correctly extract dependencies
"""
package_dir = package_archive_ahriman.root / "build" / \
package_archive_ahriman.package.base / "pkg" / package_archive_ahriman.package.base
dynamic_mock = mocker.patch("ahriman.core.build_tools.package_archive.PackageArchive.dynamic_needed",
return_value=["lib"])
walk_mock = mocker.patch("ahriman.core.build_tools.package_archive.walk", return_value=[
package_dir / "root" / "file",
Path("directory"),
])
mocker.patch("pathlib.Path.is_file", autospec=True, side_effect=lambda path: path != Path("directory"))
dependencies, roots = package_archive_ahriman.depends_on_paths()
assert dependencies == {"lib"}
assert roots == {Path("root")}
dynamic_mock.assert_called_once_with(package_dir / "root" / "file")
walk_mock.assert_called_once_with(package_dir)
def test_installed_packages(package_archive_ahriman: PackageArchive, mocker: MockerFixture,
resource_path_root: Path) -> None:
"""
must load list of installed packages and their files
"""
walk_mock = mocker.patch("ahriman.core.build_tools.package_archive.walk", return_value=[
Path("ahriman-2.13.3-1") / "desc",
Path("ahriman-2.13.3-1") / "files",
])
files = (resource_path_root / "models" / "package_ahriman_files").read_text(encoding="utf8")
read_mock = mocker.patch("pathlib.Path.read_text", return_value=files)
result = package_archive_ahriman.installed_packages()
assert result
assert Path("usr") in result[package_archive_ahriman.package.base].directories
assert Path("usr/bin/ahriman") in result[package_archive_ahriman.package.base].files
walk_mock.assert_called_once_with(package_archive_ahriman.root / "var" / "lib" / "pacman" / "local")
read_mock.assert_called_once_with(encoding="utf8")
@@ -0,0 +1,111 @@
from pathlib import Path
from pytest_mock import MockerFixture
from ahriman.core.build_tools.package_version import PackageVersion
from ahriman.core.configuration import Configuration
from ahriman.core.utils import utcnow
from ahriman.models.package import Package
from ahriman.models.pkgbuild import Pkgbuild
def test_actual_version(package_ahriman: Package, configuration: Configuration) -> None:
"""
must return same actual_version as version is
"""
assert PackageVersion(package_ahriman).actual_version(configuration) == package_ahriman.version
def test_actual_version_vcs(package_tpacpi_bat_git: Package, configuration: Configuration,
mocker: MockerFixture, resource_path_root: Path) -> None:
"""
must return valid actual_version for VCS package
"""
pkgbuild = resource_path_root / "models" / "package_tpacpi-bat-git_pkgbuild"
mocker.patch("ahriman.models.pkgbuild.Pkgbuild.from_file", return_value=Pkgbuild.from_file(pkgbuild))
mocker.patch("pathlib.Path.glob", return_value=[Path("local")])
init_mock = mocker.patch("ahriman.core.build_tools.task.Task.init")
unlink_mock = mocker.patch("pathlib.Path.unlink")
assert PackageVersion(package_tpacpi_bat_git).actual_version(configuration) == "3.1.r13.g4959b52-1"
init_mock.assert_called_once_with(configuration.repository_paths.cache_for(package_tpacpi_bat_git.base), [], None)
unlink_mock.assert_called_once_with()
def test_actual_version_failed(package_tpacpi_bat_git: Package, configuration: Configuration,
mocker: MockerFixture) -> None:
"""
must return same version in case if exception occurred
"""
mocker.patch("ahriman.core.build_tools.task.Task.init", side_effect=Exception)
mocker.patch("pathlib.Path.glob", return_value=[Path("local")])
unlink_mock = mocker.patch("pathlib.Path.unlink")
assert PackageVersion(package_tpacpi_bat_git).actual_version(configuration) == package_tpacpi_bat_git.version
unlink_mock.assert_called_once_with()
def test_is_newer_than(package_ahriman: Package, package_python_schedule: Package) -> None:
"""
must correctly check if package is newer than specified timestamp
"""
# base checks, true/false
older = package_ahriman.packages[package_ahriman.base].build_date - 1
assert PackageVersion(package_ahriman).is_newer_than(older)
newer = package_ahriman.packages[package_ahriman.base].build_date + 1
assert not PackageVersion(package_ahriman).is_newer_than(newer)
# list check
min_date = min(package.build_date for package in package_python_schedule.packages.values())
assert PackageVersion(package_python_schedule).is_newer_than(min_date)
# null list check
package_python_schedule.packages["python-schedule"].build_date = None
assert PackageVersion(package_python_schedule).is_newer_than(min_date)
package_python_schedule.packages["python2-schedule"].build_date = None
assert not PackageVersion(package_python_schedule).is_newer_than(min_date)
def test_is_outdated_false(package_ahriman: Package, configuration: Configuration, mocker: MockerFixture) -> None:
"""
must be not outdated for the same package
"""
actual_version_mock = mocker.patch("ahriman.core.build_tools.package_version.PackageVersion.actual_version",
return_value=package_ahriman.version)
assert not PackageVersion(package_ahriman).is_outdated(package_ahriman, configuration)
actual_version_mock.assert_called_once_with(configuration)
def test_is_outdated_true(package_ahriman: Package, configuration: Configuration, mocker: MockerFixture) -> None:
"""
must be outdated for the new version
"""
other = Package.from_json(package_ahriman.view())
other.version = other.version.replace("-1", "-2")
actual_version_mock = mocker.patch("ahriman.core.build_tools.package_version.PackageVersion.actual_version",
return_value=other.version)
assert PackageVersion(package_ahriman).is_outdated(other, configuration)
actual_version_mock.assert_called_once_with(configuration)
def test_is_outdated_no_version_calculation(package_ahriman: Package, configuration: Configuration,
mocker: MockerFixture) -> None:
"""
must not call actual version if calculation is disabled
"""
actual_version_mock = mocker.patch("ahriman.core.build_tools.package_version.PackageVersion.actual_version")
assert not PackageVersion(package_ahriman).is_outdated(package_ahriman, configuration, calculate_version=False)
actual_version_mock.assert_not_called()
def test_is_outdated_fresh_package(package_ahriman: Package, configuration: Configuration,
mocker: MockerFixture) -> None:
"""
must not call actual version if package is never than specified time
"""
configuration.set_option("build", "vcs_allowed_age", str(int(utcnow().timestamp())))
actual_version_mock = mocker.patch("ahriman.core.build_tools.package_version.PackageVersion.actual_version")
assert not PackageVersion(package_ahriman).is_outdated(package_ahriman, configuration)
actual_version_mock.assert_not_called()
@@ -0,0 +1,616 @@
import pytest
from pathlib import Path
from pytest_mock import MockerFixture
from unittest.mock import call as MockCall
from ahriman.core.build_tools.sources import Sources
from ahriman.core.exceptions import CalledProcessError
from ahriman.models.changes import Changes
from ahriman.models.package import Package
from ahriman.models.package_source import PackageSource
from ahriman.models.pkgbuild_patch import PkgbuildPatch
from ahriman.models.remote_source import RemoteSource
from ahriman.models.repository_paths import RepositoryPaths
def test_changes(mocker: MockerFixture) -> None:
"""
must calculate changes from the last known commit
"""
fetch_mock = mocker.patch("ahriman.core.build_tools.sources.Sources.fetch_until")
diff_mock = mocker.patch("ahriman.core.build_tools.sources.Sources.diff", return_value="diff")
read_mock = mocker.patch("ahriman.core.build_tools.sources.Sources.read", return_value="pkgbuild")
local = Path("local")
last_commit_sha = "sha"
assert Sources.changes(local, last_commit_sha) == Changes(last_commit_sha, "diff", "pkgbuild")
fetch_mock.assert_called_once_with(local, commit_sha=last_commit_sha)
diff_mock.assert_called_once_with(local, last_commit_sha)
read_mock.assert_called_once_with(local, "HEAD", Path("PKGBUILD"))
def test_changes_unknown_commit(mocker: MockerFixture) -> None:
"""
must return changes without diff in case if commit sha wasn't found at the required depth
"""
mocker.patch("ahriman.core.build_tools.sources.Sources.fetch_until", return_value=None)
diff_mock = mocker.patch("ahriman.core.build_tools.sources.Sources.diff")
read_mock = mocker.patch("ahriman.core.build_tools.sources.Sources.read", return_value="pkgbuild")
assert Sources.changes(Path("local"), "sha") == Changes("sha", None, "pkgbuild")
diff_mock.assert_not_called()
read_mock.assert_called_once_with(Path("local"), "HEAD", Path("PKGBUILD"))
def test_extend_architectures(mocker: MockerFixture) -> None:
"""
must update available architecture list
"""
mocker.patch("pathlib.Path.is_file", return_value=True)
architectures_mock = mocker.patch("ahriman.models.pkgbuild.Pkgbuild.supported_architectures",
return_value={"x86_64"})
assert Sources.extend_architectures(Path("local"), "i686") == [PkgbuildPatch("arch", list({"x86_64", "i686"}))]
architectures_mock.assert_called_once_with(Path("local"))
def test_extend_architectures_any(mocker: MockerFixture) -> None:
"""
must skip architecture patching in case if there is any architecture
"""
mocker.patch("pathlib.Path.is_file", return_value=True)
mocker.patch("ahriman.models.pkgbuild.Pkgbuild.supported_architectures", return_value={"any"})
assert Sources.extend_architectures(Path("local"), "i686") == []
def test_fetch_empty(remote_source: RemoteSource, mocker: MockerFixture) -> None:
"""
must do nothing in case if no branches available
"""
mocker.patch("pathlib.Path.is_dir", return_value=True)
mocker.patch("ahriman.core.build_tools.sources.Sources.has_remotes", return_value=False)
head_mock = mocker.patch("ahriman.core.build_tools.sources.Sources.head", return_value="sha")
check_output_mock = mocker.patch("ahriman.core.build_tools.sources.check_output")
local = Path("local")
assert Sources.fetch(local, remote_source) == "sha"
head_mock.assert_called_once_with(local)
check_output_mock.assert_not_called()
def test_fetch_existing(sources: Sources, remote_source: RemoteSource, mocker: MockerFixture) -> None:
"""
must fetch new package via fetch command
"""
mocker.patch("pathlib.Path.is_dir", return_value=True)
mocker.patch("ahriman.core.build_tools.sources.Sources.has_remotes", return_value=True)
check_output_mock = mocker.patch("ahriman.core.build_tools.sources.check_output")
fetch_mock = mocker.patch("ahriman.core.build_tools.sources.Sources.fetch_until")
move_mock = mocker.patch("ahriman.core.build_tools.sources.Sources.move")
head_mock = mocker.patch("ahriman.core.build_tools.sources.Sources.head", return_value="sha")
local = Path("local")
assert sources.fetch(local, remote_source) == "sha"
fetch_mock.assert_called_once_with(local, branch=remote_source.branch)
check_output_mock.assert_has_calls([
MockCall(*sources.git(), "checkout", "--force", remote_source.branch,
cwd=local, logger=pytest.helpers.anyvar(int)),
MockCall(*sources.git(), "reset", "--quiet", "--hard", f"origin/{remote_source.branch}",
cwd=local, logger=pytest.helpers.anyvar(int)),
])
move_mock.assert_called_once_with(local.resolve(), local)
head_mock.assert_called_once_with(local)
def test_fetch_new(sources: Sources, remote_source: RemoteSource, mocker: MockerFixture) -> None:
"""
must fetch new package via clone command
"""
mocker.patch("pathlib.Path.is_dir", return_value=False)
check_output_mock = mocker.patch("ahriman.core.build_tools.sources.check_output")
move_mock = mocker.patch("ahriman.core.build_tools.sources.Sources.move")
head_mock = mocker.patch("ahriman.core.build_tools.sources.Sources.head", return_value="sha")
local = Path("local")
assert sources.fetch(local, remote_source) == "sha"
check_output_mock.assert_has_calls([
MockCall(*sources.git(), "clone", "--quiet", "--depth", "1", "--branch", remote_source.branch,
"--single-branch", remote_source.git_url, str(local),
cwd=local.parent, logger=pytest.helpers.anyvar(int)),
MockCall(*sources.git(), "checkout", "--force", remote_source.branch,
cwd=local, logger=pytest.helpers.anyvar(int)),
MockCall(*sources.git(), "reset", "--quiet", "--hard", f"origin/{remote_source.branch}",
cwd=local, logger=pytest.helpers.anyvar(int))
])
move_mock.assert_called_once_with(local.resolve(), local)
head_mock.assert_called_once_with(local)
def test_fetch_new_without_remote(sources: Sources, mocker: MockerFixture) -> None:
"""
must fetch nothing in case if no remote set
"""
mocker.patch("pathlib.Path.is_dir", return_value=False)
check_output_mock = mocker.patch("ahriman.core.build_tools.sources.check_output")
move_mock = mocker.patch("ahriman.core.build_tools.sources.Sources.move")
head_mock = mocker.patch("ahriman.core.build_tools.sources.Sources.head", return_value="sha")
local = Path("local")
assert sources.fetch(local, RemoteSource(source=PackageSource.Archive)) == "sha"
check_output_mock.assert_has_calls([
MockCall(*sources.git(), "checkout", "--force", sources.DEFAULT_BRANCH,
cwd=local, logger=pytest.helpers.anyvar(int)),
MockCall(*sources.git(), "reset", "--quiet", "--hard", f"origin/{sources.DEFAULT_BRANCH}",
cwd=local, logger=pytest.helpers.anyvar(int))
])
move_mock.assert_called_once_with(local.resolve(), local)
head_mock.assert_called_once_with(local)
def test_fetch_relative(remote_source: RemoteSource, mocker: MockerFixture) -> None:
"""
must process move correctly on relative directory
"""
mocker.patch("ahriman.core.build_tools.sources.check_output")
move_mock = mocker.patch("ahriman.core.build_tools.sources.Sources.move")
head_mock = mocker.patch("ahriman.core.build_tools.sources.Sources.head", return_value="sha")
local = Path("local")
assert Sources.fetch(local, remote_source) == "sha"
move_mock.assert_called_once_with(local.resolve(), local)
head_mock.assert_called_once_with(local)
def test_has_remotes(sources: Sources, mocker: MockerFixture) -> None:
"""
must ask for remotes
"""
check_output_mock = mocker.patch("ahriman.core.build_tools.sources.check_output", return_value="origin")
local = Path("local")
assert sources.has_remotes(local)
check_output_mock.assert_called_once_with(*sources.git(), "remote", cwd=local, logger=pytest.helpers.anyvar(int))
def test_has_remotes_empty(mocker: MockerFixture) -> None:
"""
must ask for remotes and return false in case if no remotes found
"""
mocker.patch("ahriman.core.build_tools.sources.check_output", return_value="")
assert not Sources.has_remotes(Path("local"))
def test_init(sources: Sources, mocker: MockerFixture) -> None:
"""
must create empty repository at the specified path
"""
mocker.patch("ahriman.models.pkgbuild.Pkgbuild.local_files", return_value=[Path("local")])
mocker.patch("pathlib.Path.is_dir", return_value=False)
add_mock = mocker.patch("ahriman.core.build_tools.sources.Sources.add")
check_output_mock = mocker.patch("ahriman.core.build_tools.sources.check_output")
commit_mock = mocker.patch("ahriman.core.build_tools.sources.Sources.commit")
local = Path("local")
sources.init(local)
check_output_mock.assert_called_once_with(*sources.git(), "init", "--quiet", "--initial-branch",
sources.DEFAULT_BRANCH, cwd=local, logger=pytest.helpers.anyvar(int))
add_mock.assert_called_once_with(local, "PKGBUILD", ".SRCINFO", "local")
commit_mock.assert_called_once_with(local)
def test_init_skip(mocker: MockerFixture) -> None:
"""
must skip git init if it was already
"""
mocker.patch("ahriman.models.pkgbuild.Pkgbuild.local_files", return_value=[Path("local")])
mocker.patch("pathlib.Path.is_dir", return_value=True)
mocker.patch("ahriman.core.build_tools.sources.Sources.add")
mocker.patch("ahriman.core.build_tools.sources.Sources.commit")
check_output_mock = mocker.patch("ahriman.core.build_tools.sources.check_output")
Sources.init(Path("local"))
check_output_mock.assert_not_called()
def test_load(package_ahriman: Package, repository_paths: RepositoryPaths, mocker: MockerFixture) -> None:
"""
must load packages sources correctly
"""
patch = PkgbuildPatch(None, "patch")
path = Path("local")
fetch_mock = mocker.patch("ahriman.core.build_tools.sources.Sources.fetch", return_value="sha")
patch_mock = mocker.patch("ahriman.core.build_tools.sources.Sources.patch_apply")
architectures_mock = mocker.patch("ahriman.core.build_tools.sources.Sources.extend_architectures", return_value=[])
assert Sources.load(path, package_ahriman, [patch], repository_paths) == "sha"
fetch_mock.assert_called_once_with(path, package_ahriman.remote)
patch_mock.assert_called_once_with(path, patch)
architectures_mock.assert_called_once_with(path, repository_paths.repository_id.architecture)
def test_load_no_patch(package_ahriman: Package, repository_paths: RepositoryPaths, mocker: MockerFixture) -> None:
"""
must load packages sources correctly without patches
"""
mocker.patch("pathlib.Path.is_dir", return_value=False)
mocker.patch("ahriman.core.build_tools.sources.Sources.fetch", return_value="sha")
mocker.patch("ahriman.core.build_tools.sources.Sources.extend_architectures", return_value=[])
patch_mock = mocker.patch("ahriman.core.build_tools.sources.Sources.patch_apply")
assert Sources.load(Path("local"), package_ahriman, [], repository_paths) == "sha"
patch_mock.assert_not_called()
def test_load_with_cache(package_ahriman: Package, repository_paths: RepositoryPaths, mocker: MockerFixture) -> None:
"""
must load sources by using local cache
"""
mocker.patch("pathlib.Path.is_dir", return_value=True)
copytree_mock = mocker.patch("shutil.copytree")
mocker.patch("ahriman.core.build_tools.sources.Sources.fetch", return_value="sha")
mocker.patch("ahriman.core.build_tools.sources.Sources.extend_architectures", return_value=[])
assert Sources.load(Path("local"), package_ahriman, [], repository_paths) == "sha"
copytree_mock.assert_called_once() # we do not check full command here, sorry
def test_patch_create(mocker: MockerFixture) -> None:
"""
must create patch set for the package
"""
add_mock = mocker.patch("ahriman.core.build_tools.sources.Sources.add")
diff_mock = mocker.patch("ahriman.core.build_tools.sources.Sources.diff")
Sources.patch_create(Path("local"), "glob")
add_mock.assert_called_once_with(Path("local"), "glob", intent_to_add=True)
diff_mock.assert_called_once_with(Path("local"))
def test_patch_create_with_newline(mocker: MockerFixture) -> None:
"""
created patch must have new line at the end
"""
mocker.patch("ahriman.core.build_tools.sources.Sources.add")
mocker.patch("ahriman.core.build_tools.sources.Sources.diff", return_value="diff")
assert Sources.patch_create(Path("local"), "glob").endswith("\n")
def test_push(package_ahriman: Package, sources: Sources, mocker: MockerFixture) -> None:
"""
must correctly push files to remote repository
"""
add_mock = mocker.patch("ahriman.core.build_tools.sources.Sources.add")
commit_mock = mocker.patch("ahriman.core.build_tools.sources.Sources.commit", return_value=True)
check_output_mock = mocker.patch("ahriman.core.build_tools.sources.check_output")
commit_author = ("commit author", "user@host")
local = Path("local")
sources.push(local, package_ahriman.remote, "glob", commit_author=commit_author)
add_mock.assert_called_once_with(local, "glob")
commit_mock.assert_called_once_with(local, commit_author=commit_author)
check_output_mock.assert_called_once_with(
*sources.git(), "push", "--quiet", package_ahriman.remote.git_url, package_ahriman.remote.branch,
cwd=local, logger=pytest.helpers.anyvar(int))
def test_push_skipped(package_ahriman: Package, mocker: MockerFixture) -> None:
"""
must skip push if no changes were committed
"""
mocker.patch("ahriman.core.build_tools.sources.Sources.add")
mocker.patch("ahriman.core.build_tools.sources.Sources.commit", return_value=False)
check_output_mock = mocker.patch("ahriman.core.build_tools.sources.check_output")
Sources.push(Path("local"), package_ahriman.remote)
check_output_mock.assert_not_called()
def test_add(sources: Sources, mocker: MockerFixture) -> None:
"""
must add files to git
"""
glob_mock = mocker.patch("pathlib.Path.glob", return_value=[Path("local/1"), Path("local/2")])
check_output_mock = mocker.patch("ahriman.core.build_tools.sources.check_output")
local = Path("local")
sources.add(local, "pattern1", "pattern2")
glob_mock.assert_has_calls([MockCall("pattern1"), MockCall("pattern2")])
check_output_mock.assert_called_once_with(
*sources.git(), "add", "1", "2", "1", "2", cwd=local, logger=sources.logger
)
def test_add_intent_to_add(sources: Sources, mocker: MockerFixture) -> None:
"""
must add files to git with --intent-to-add flag
"""
glob_mock = mocker.patch("pathlib.Path.glob", return_value=[Path("local/1"), Path("local/2")])
check_output_mock = mocker.patch("ahriman.core.build_tools.sources.check_output")
local = Path("local")
sources.add(local, "pattern1", "pattern2", intent_to_add=True)
glob_mock.assert_has_calls([MockCall("pattern1"), MockCall("pattern2")])
check_output_mock.assert_called_once_with(
*sources.git(), "add", "--intent-to-add", "1", "2", "1", "2", cwd=local, logger=sources.logger
)
def test_add_skip(sources: Sources, mocker: MockerFixture) -> None:
"""
must skip addition of files to index if no fields found
"""
mocker.patch("pathlib.Path.glob", return_value=[])
check_output_mock = mocker.patch("ahriman.core.build_tools.sources.check_output")
sources.add(Path("local"), "pattern1")
check_output_mock.assert_not_called()
def test_commit(sources: Sources, mocker: MockerFixture) -> None:
"""
must commit changes
"""
mocker.patch("ahriman.core.build_tools.sources.Sources.has_changes", return_value=True)
check_output_mock = mocker.patch("ahriman.core.build_tools.sources.check_output")
local = Path("local")
message = "Commit message"
user, email = sources.DEFAULT_COMMIT_AUTHOR
assert sources.commit(local, message=message)
check_output_mock.assert_called_once_with(
*sources.git(), "-c", f"user.email=\"{email}\"", "-c", f"user.name=\"{user}\"",
"commit", "--quiet", "--message", message, cwd=local, logger=sources.logger
)
def test_commit_no_changes(sources: Sources, mocker: MockerFixture) -> None:
"""
must skip commit if there are no changes
"""
mocker.patch("ahriman.core.build_tools.sources.Sources.has_changes", return_value=False)
check_output_mock = mocker.patch("ahriman.core.build_tools.sources.check_output")
assert not sources.commit(Path("local"))
check_output_mock.assert_not_called()
def test_commit_author(sources: Sources, mocker: MockerFixture) -> None:
"""
must commit changes with commit author
"""
mocker.patch("ahriman.core.build_tools.sources.Sources.has_changes", return_value=True)
check_output_mock = mocker.patch("ahriman.core.build_tools.sources.check_output")
local = Path("local")
message = "Commit message"
user, email = author = ("commit author", "user@host")
assert sources.commit(Path("local"), message=message, commit_author=author)
check_output_mock.assert_called_once_with(
*sources.git(), "-c", f"user.email=\"{email}\"", "-c", f"user.name=\"{user}\"",
"commit", "--quiet", "--message", message, cwd=local, logger=sources.logger
)
def test_commit_autogenerated_message(sources: Sources, mocker: MockerFixture) -> None:
"""
must commit changes with autogenerated commit message
"""
mocker.patch("ahriman.core.build_tools.sources.Sources.has_changes", return_value=True)
check_output_mock = mocker.patch("ahriman.core.build_tools.sources.check_output")
local = Path("local")
assert sources.commit(Path("local"))
user, email = sources.DEFAULT_COMMIT_AUTHOR
check_output_mock.assert_called_once_with(
*sources.git(), "-c", f"user.email=\"{email}\"", "-c", f"user.name=\"{user}\"",
"commit", "--quiet", "--message", pytest.helpers.anyvar(str, strict=True), cwd=local, logger=sources.logger
)
def test_diff(sources: Sources, mocker: MockerFixture) -> None:
"""
must calculate diff
"""
check_output_mock = mocker.patch("ahriman.core.build_tools.sources.check_output")
local = Path("local")
assert sources.diff(local)
check_output_mock.assert_called_once_with(*sources.git(), "diff", cwd=local, logger=sources.logger)
def test_diff_specific(sources: Sources, mocker: MockerFixture) -> None:
"""
must calculate diff from specific ref
"""
check_output_mock = mocker.patch("ahriman.core.build_tools.sources.check_output")
local = Path("local")
assert sources.diff(local, "hash")
check_output_mock.assert_called_once_with(*sources.git(), "diff", "hash", cwd=local, logger=sources.logger)
def test_fetch_until(sources: Sources, mocker: MockerFixture) -> None:
"""
must fetch until the specified commit
"""
check_output_mock = mocker.patch("ahriman.core.build_tools.sources.check_output", side_effect=[
"",
CalledProcessError(1, ["command"], "error"),
"",
"",
])
local = Path("local")
last_commit_sha = "sha"
assert sources.fetch_until(local, branch="master", commit_sha="sha") == last_commit_sha
check_output_mock.assert_has_calls([
MockCall(*sources.git(), "fetch", "--quiet", "--depth", "1", "origin", "master",
cwd=local, logger=sources.logger),
MockCall(*sources.git(), "cat-file", "-e", last_commit_sha, cwd=local, logger=sources.logger),
MockCall(*sources.git(), "fetch", "--quiet", "--depth", "2", "origin", "master",
cwd=local, logger=sources.logger),
MockCall(*sources.git(), "cat-file", "-e", last_commit_sha, cwd=local, logger=sources.logger),
])
def test_fetch_until_first(sources: Sources, mocker: MockerFixture) -> None:
"""
must fetch first commit only
"""
check_output_mock = mocker.patch("ahriman.core.build_tools.sources.check_output")
local = Path("local")
assert sources.fetch_until(local, branch="master") == "HEAD"
check_output_mock.assert_has_calls([
MockCall(*sources.git(), "fetch", "--quiet", "--depth", "1", "origin", "master",
cwd=local, logger=sources.logger),
MockCall(*sources.git(), "cat-file", "-e", "HEAD", cwd=local, logger=sources.logger),
])
def test_fetch_until_all_branches(sources: Sources, mocker: MockerFixture) -> None:
"""
must fetch all branches
"""
check_output_mock = mocker.patch("ahriman.core.build_tools.sources.check_output")
local = Path("local")
assert sources.fetch_until(local) == "HEAD"
check_output_mock.assert_has_calls([
MockCall(*sources.git(), "fetch", "--quiet", "--depth", "1", cwd=local, logger=sources.logger),
MockCall(*sources.git(), "cat-file", "-e", "HEAD", cwd=local, logger=sources.logger),
])
def test_fetch_until_not_found(sources: Sources, mocker: MockerFixture) -> None:
"""
must return None in case if no commit found at the required maximal depth
"""
mocker.patch("ahriman.core.build_tools.sources.check_output", side_effect=[
"",
CalledProcessError(1, ["command"], "error"),
"",
CalledProcessError(1, ["command"], "error"),
])
assert sources.fetch_until(Path("local"), branch="master", commit_sha="sha", max_depth=2) is None
def test_git(sources: Sources) -> None:
"""
must correctly generate git command
"""
assert sources.git() == ["git", "-c", "init.defaultBranch=\"master\""]
def test_git_overrides(sources: Sources) -> None:
"""
must correctly generate git command with additional settings
"""
assert sources.git({"user.email": "ahriman@localhost"}) == [
"git", "-c", "init.defaultBranch=\"master\"", "-c", "user.email=\"ahriman@localhost\""
]
def test_has_changes(sources: Sources, mocker: MockerFixture) -> None:
"""
must correctly identify if there are changes
"""
local = Path("local")
check_output_mock = mocker.patch("ahriman.core.build_tools.sources.check_output", return_value="M a.txt")
assert sources.has_changes(local)
check_output_mock.assert_called_once_with(*sources.git(), "diff", "--cached", "--name-only",
cwd=local, logger=sources.logger)
check_output_mock = mocker.patch("ahriman.core.build_tools.sources.check_output", return_value="")
assert not sources.has_changes(local)
check_output_mock.assert_called_once_with(*sources.git(), "diff", "--cached", "--name-only",
cwd=local, logger=sources.logger)
def test_head(sources: Sources, mocker: MockerFixture) -> None:
"""
must correctly define HEAD hash
"""
check_output_mock = mocker.patch("ahriman.core.build_tools.sources.check_output", return_value="sha")
local = Path("local")
assert sources.head(local) == "sha"
check_output_mock.assert_called_once_with(*sources.git(), "rev-parse", "HEAD", cwd=local, logger=sources.logger)
def test_head_specific(sources: Sources, mocker: MockerFixture) -> None:
"""
must correctly define ref hash
"""
check_output_mock = mocker.patch("ahriman.core.build_tools.sources.check_output", return_value="sha")
local = Path("local")
assert sources.head(local, "master") == "sha"
check_output_mock.assert_called_once_with(*sources.git(), "rev-parse", "master", cwd=local, logger=sources.logger)
def test_move(sources: Sources, mocker: MockerFixture) -> None:
"""
must move content between directories
"""
mocker.patch("ahriman.core.build_tools.sources.walk", return_value=[Path("/source/path")])
move_mock = mocker.patch("shutil.move")
sources.move(Path("/source"), Path("/destination"))
move_mock.assert_called_once_with(Path("/source/path"), Path("/destination/path"))
def test_move_same(sources: Sources, mocker: MockerFixture) -> None:
"""
must not do anything in case if directories are the same
"""
walk_mock = mocker.patch("ahriman.core.build_tools.sources.walk")
sources.move(Path("/same"), Path("/same"))
walk_mock.assert_not_called()
def test_patch_apply(sources: Sources, mocker: MockerFixture) -> None:
"""
must apply patches if any
"""
patch = PkgbuildPatch(None, "patch")
check_output_mock = mocker.patch("ahriman.core.build_tools.sources.check_output")
local = Path("local")
sources.patch_apply(local, patch)
check_output_mock.assert_called_once_with(
*sources.git(), "apply", "--ignore-space-change", "--ignore-whitespace",
cwd=local, input_data=patch.value, logger=sources.logger
)
def test_patch_apply_function(sources: Sources, mocker: MockerFixture) -> None:
"""
must apply single-function patches
"""
patch = PkgbuildPatch("version", "42")
local = Path("local")
write_mock = mocker.patch("ahriman.models.pkgbuild_patch.PkgbuildPatch.write")
sources.patch_apply(local, patch)
write_mock.assert_called_once_with(local / "PKGBUILD")
def test_read(sources: Sources, mocker: MockerFixture) -> None:
"""
must read file from commit
"""
check_output_mock = mocker.patch("ahriman.core.build_tools.sources.check_output", return_value="content")
assert sources.read(Path("local"), "sha", Path("PKGBUILD")) == "content"
check_output_mock.assert_called_once()
def test_read_failed(sources: Sources, mocker: MockerFixture) -> None:
"""
must return None in case if file cannot be read from commit
"""
mocker.patch("ahriman.core.build_tools.sources.check_output",
side_effect=CalledProcessError(1, ["command"], "error"))
assert sources.read(Path("local"), "sha", Path("PKGBUILD")) is None
@@ -0,0 +1,168 @@
import pytest
from pathlib import Path
from pytest_mock import MockerFixture
from ahriman.core.build_tools.task import Task
from ahriman.models.pkgbuild_patch import PkgbuildPatch
def test_package_archives(task_ahriman: Task, mocker: MockerFixture) -> None:
"""
must correctly return list of new files
"""
mocker.patch("pathlib.Path.iterdir", return_value=[
Path(f"{task_ahriman.package.base}-{task_ahriman.package.version}-any.pkg.tar.xz"),
Path(f"{task_ahriman.package.base}-debug-{task_ahriman.package.version}-any.pkg.tar.xz"),
Path("source.pkg.tar.xz"),
Path("randomfile"),
Path("namcap.log"),
])
assert task_ahriman._package_archives(Path("local"), [Path("source.pkg.tar.xz")]) == [
Path(f"{task_ahriman.package.base}-{task_ahriman.package.version}-any.pkg.tar.xz"),
Path(f"{task_ahriman.package.base}-debug-{task_ahriman.package.version}-any.pkg.tar.xz"),
]
def test_package_archives_no_debug(task_ahriman: Task, mocker: MockerFixture) -> None:
"""
must correctly return list of new files without debug packages
"""
task_ahriman.include_debug_packages = False
mocker.patch("pathlib.Path.iterdir", return_value=[
Path(f"{task_ahriman.package.base}-{task_ahriman.package.version}-any.pkg.tar.xz"),
Path(f"{task_ahriman.package.base}-debug-{task_ahriman.package.version}-any.pkg.tar.xz"),
Path("source.pkg.tar.xz"),
Path("randomfile"),
Path("namcap.log"),
])
assert task_ahriman._package_archives(Path("local"), [Path("source.pkg.tar.xz")]) == [
Path(f"{task_ahriman.package.base}-{task_ahriman.package.version}-any.pkg.tar.xz"),
]
def test_build(task_ahriman: Task, mocker: MockerFixture) -> None:
"""
must build package
"""
local = Path("local")
mocker.patch("pathlib.Path.iterdir", return_value=["file"])
check_output_mock = mocker.patch("ahriman.core.build_tools.task.check_output")
archives_mock = mocker.patch("ahriman.core.build_tools.task.Task._package_archives",
return_value=[task_ahriman.package.base])
assert task_ahriman.build(local) == [task_ahriman.package.base]
check_output_mock.assert_called_once_with(
"extra-x86_64-build",
"-r", str(task_ahriman.paths.chroot),
"--", "-D", str(task_ahriman.paths.archive),
"--", "--skippgpcheck",
exception=pytest.helpers.anyvar(int),
cwd=local,
logger=task_ahriman.logger,
user=task_ahriman.uid,
environment={},
)
archives_mock.assert_called_once_with(local, ["file"])
def test_build_environment(task_ahriman: Task, mocker: MockerFixture) -> None:
"""
must build package with environment variables set
"""
local = Path("local")
mocker.patch("pathlib.Path.iterdir", return_value=["file"])
mocker.patch("ahriman.core.build_tools.task.Task._package_archives", return_value=[task_ahriman.package.base])
check_output_mock = mocker.patch("ahriman.core.build_tools.task.check_output")
environment = {"variable": "value"}
task_ahriman.build(local, **environment, empty=None)
check_output_mock.assert_called_once_with(
"extra-x86_64-build",
"-r", str(task_ahriman.paths.chroot),
"--", "-D", str(task_ahriman.paths.archive),
"--", "--skippgpcheck",
exception=pytest.helpers.anyvar(int),
cwd=local,
logger=task_ahriman.logger,
user=task_ahriman.uid,
environment=environment,
)
def test_build_dry_run(task_ahriman: Task, mocker: MockerFixture) -> None:
"""
must run devtools in dry-run mode
"""
local = Path("local")
mocker.patch("pathlib.Path.iterdir", return_value=["file"])
mocker.patch("ahriman.core.build_tools.task.Task._package_archives", return_value=[task_ahriman.package.base])
check_output_mock = mocker.patch("ahriman.core.build_tools.task.check_output")
assert task_ahriman.build(local, dry_run=True) == [task_ahriman.package.base]
check_output_mock.assert_called_once_with(
"extra-x86_64-build",
"-r", str(task_ahriman.paths.chroot),
"--", "-D", str(task_ahriman.paths.archive),
"--", "--skippgpcheck",
"--nobuild",
exception=pytest.helpers.anyvar(int),
cwd=local,
logger=task_ahriman.logger,
user=task_ahriman.uid,
environment={},
)
def test_init(task_ahriman: Task, mocker: MockerFixture) -> None:
"""
must copy tree instead of fetch
"""
patches = [PkgbuildPatch("hash", "patch")]
mocker.patch("ahriman.models.package.Package.from_build", return_value=task_ahriman.package)
load_mock = mocker.patch("ahriman.core.build_tools.sources.Sources.load", return_value="sha")
assert task_ahriman.init(Path("ahriman"), patches, None) == "sha"
load_mock.assert_called_once_with(Path("ahriman"), task_ahriman.package, patches, task_ahriman.paths)
def test_init_vcs(task_ahriman: Task, mocker: MockerFixture) -> None:
"""
must copy tree instead of fetch
"""
task_ahriman.package.base += "-git"
mocker.patch("ahriman.models.package.Package.from_build", return_value=task_ahriman.package)
load_mock = mocker.patch("ahriman.core.build_tools.sources.Sources.load", return_value="sha")
build_mock = mocker.patch("ahriman.core.build_tools.task.Task.build")
local = Path("ahriman")
assert task_ahriman.init(local, [], None) == "sha"
load_mock.assert_called_once_with(local, task_ahriman.package, [], task_ahriman.paths)
build_mock.assert_called_once_with(local, dry_run=True)
def test_init_bump_pkgrel(task_ahriman: Task, mocker: MockerFixture) -> None:
"""
must bump pkgrel if it is same as provided
"""
mocker.patch("ahriman.models.package.Package.from_build", return_value=task_ahriman.package)
mocker.patch("ahriman.core.build_tools.sources.Sources.load", return_value="sha")
write_mock = mocker.patch("ahriman.models.pkgbuild_patch.PkgbuildPatch.write")
local = Path("ahriman")
assert task_ahriman.init(local, [], task_ahriman.package.version) == "sha"
write_mock.assert_called_once_with(local / "PKGBUILD")
def test_init_bump_pkgrel_skip(task_ahriman: Task, mocker: MockerFixture) -> None:
"""
must keep pkgrel if version is different from provided
"""
task_ahriman.package.version = "2.0.0-1"
mocker.patch("ahriman.models.package.Package.from_build", return_value=task_ahriman.package)
mocker.patch("ahriman.core.build_tools.sources.Sources.load", return_value="sha")
write_mock = mocker.patch("ahriman.models.pkgbuild_patch.PkgbuildPatch.write")
assert task_ahriman.init(Path("ahriman"), [], "1.0.0-1") == "sha"
write_mock.assert_not_called()
@@ -0,0 +1,19 @@
import pytest
from ahriman.core.configuration import Configuration
from ahriman.core.configuration.schema import CONFIGURATION_SCHEMA
from ahriman.core.configuration.validator import Validator
@pytest.fixture
def validator(configuration: Configuration) -> Validator:
"""
fixture for validator
Args:
configuration(Configuration): configuration fixture
Returns:
Validator: validator test instance
"""
return Validator(configuration=configuration, schema=CONFIGURATION_SCHEMA)
@@ -0,0 +1,547 @@
import configparser
import pytest
import os
from io import StringIO
from pathlib import Path
from pytest_mock import MockerFixture
from unittest.mock import call as MockCall
from ahriman.core.configuration import Configuration
from ahriman.core.exceptions import InitializeError
from ahriman.models.repository_id import RepositoryId
from ahriman.models.repository_paths import RepositoryPaths
def test_architecture(configuration: Configuration) -> None:
"""
must return valid repository architecture
"""
assert configuration.architecture == "x86_64"
def test_repository_id(configuration: Configuration, repository_id: RepositoryId) -> None:
"""
must return repository identifier
"""
assert configuration.repository_id == repository_id
assert configuration.get("repository", "name") == repository_id.name
assert configuration.get("repository", "architecture") == repository_id.architecture
def test_repository_id_erase(configuration: Configuration) -> None:
"""
must remove repository identifier properties if empty identifier supplied
"""
configuration.repository_id = None
assert configuration.get("repository", "name", fallback=None) is None
assert configuration.get("repository", "architecture", fallback=None) is None
configuration.repository_id = RepositoryId("", "")
assert configuration.get("repository", "name", fallback=None) is None
assert configuration.get("repository", "architecture", fallback=None) is None
def test_repository_id_update(configuration: Configuration, repository_id: RepositoryId) -> None:
"""
must update repository identifier and related configuration options
"""
repository_id = RepositoryId("i686", repository_id.name)
configuration.repository_id = repository_id
assert configuration.repository_id == repository_id
assert configuration.get("repository", "name") == repository_id.name
assert configuration.get("repository", "architecture") == repository_id.architecture
def test_repository_name(configuration: Configuration) -> None:
"""
must return valid repository name
"""
assert configuration.repository_name == "aur"
def test_repository_paths(configuration: Configuration, repository_paths: RepositoryPaths) -> None:
"""
must return repository paths
"""
assert configuration.repository_paths == repository_paths
def test_from_path(repository_id: RepositoryId, mocker: MockerFixture) -> None:
"""
must load configuration
"""
mocker.patch("pathlib.Path.is_file", return_value=True)
mocker.patch("ahriman.core.configuration.Configuration.get", return_value="ahriman.ini.d")
read_mock = mocker.patch("ahriman.core.configuration.Configuration.read")
load_includes_mock = mocker.patch("ahriman.core.configuration.Configuration.load_includes")
merge_mock = mocker.patch("ahriman.core.configuration.Configuration.merge_sections")
environment_mock = mocker.patch("ahriman.core.configuration.Configuration.load_environment")
path = Path("path")
configuration = Configuration.from_path(path, repository_id)
assert configuration.path == path
read_mock.assert_called_once_with(path)
load_includes_mock.assert_called_once_with()
merge_mock.assert_called_once_with(repository_id)
environment_mock.assert_called_once_with()
def test_from_path_file_missing(repository_id: RepositoryId, mocker: MockerFixture) -> None:
"""
must load configuration based on package files
"""
mocker.patch("pathlib.Path.is_file", return_value=False)
mocker.patch("ahriman.core.configuration.Configuration.load_includes")
mocker.patch("ahriman.core.configuration.Configuration.get", return_value="ahriman.ini.d")
read_mock = mocker.patch("ahriman.core.configuration.Configuration.read")
configuration = Configuration.from_path(Path("path"), repository_id)
read_mock.assert_called_once_with(configuration.SYSTEM_CONFIGURATION_PATH)
def test_section_name(configuration: Configuration) -> None:
"""
must return architecture specific group
"""
assert configuration.section_name("build") == "build"
assert configuration.section_name("build", None) == "build"
assert configuration.section_name("build", "x86_64") == "build:x86_64"
assert configuration.section_name("build", "aur", "x86_64") == "build:aur:x86_64"
assert configuration.section_name("build", "aur", None) == "build:aur"
assert configuration.section_name("build", None, "x86_64") == "build:x86_64"
def test_check_loaded(configuration: Configuration) -> None:
"""
must return valid path and architecture
"""
path, repository_id = configuration.check_loaded()
assert path == configuration.path
assert repository_id == configuration.repository_id
def test_check_loaded_path(configuration: Configuration) -> None:
"""
must raise exception if path is none
"""
configuration.path = None
with pytest.raises(InitializeError):
configuration.check_loaded()
def test_check_loaded_architecture(configuration: Configuration) -> None:
"""
must raise exception if architecture is none
"""
configuration.repository_id = None
with pytest.raises(InitializeError):
configuration.check_loaded()
def test_copy_from(configuration: Configuration) -> None:
"""
must copy values from another instance
"""
instance = Configuration()
instance.copy_from(configuration)
assert instance.dump() == configuration.dump()
def test_dump(configuration: Configuration) -> None:
"""
dump must not be empty
"""
assert configuration.dump()
def test_dump_architecture_specific(configuration: Configuration) -> None:
"""
dump must contain architecture specific settings
"""
section = configuration.section_name("build", configuration.architecture)
configuration.set_option(section, "archbuild_flags", "hello flag")
configuration.merge_sections(configuration.repository_id)
dump = configuration.dump()
assert dump
assert "build" in dump
assert section not in dump
assert dump["build"]["archbuild_flags"] == "hello flag"
def test_getintlist(configuration: Configuration) -> None:
"""
must extract list of integers
"""
configuration.set_option("build", "test_int_list", "1 42 3")
assert configuration.getintlist("build", "test_int_list") == [1, 42, 3]
def test_getlist(configuration: Configuration) -> None:
"""
must return list of string correctly
"""
configuration.set_option("build", "test_list", "a b c")
assert configuration.getlist("build", "test_list") == ["a", "b", "c"]
def test_getlist_empty(configuration: Configuration) -> None:
"""
must return list of string correctly for non-existing option
"""
assert configuration.getlist("build", "test_list", fallback=[]) == []
configuration.set_option("build", "test_list", "")
assert configuration.getlist("build", "test_list") == []
def test_getlist_single(configuration: Configuration) -> None:
"""
must return list of strings for single string
"""
configuration.set_option("build", "test_list", "a")
assert configuration.getlist("build", "test_list") == ["a"]
assert configuration.getlist("build", "test_list") == ["a"]
def test_getlist_with_spaces(configuration: Configuration) -> None:
"""
must return list of string if there is string with spaces in quotes
"""
configuration.set_option("build", "test_list", """"ahriman is" cool""")
assert configuration.getlist("build", "test_list") == ["""ahriman is""", """cool"""]
configuration.set_option("build", "test_list", """'ahriman is' cool""")
assert configuration.getlist("build", "test_list") == ["""ahriman is""", """cool"""]
def test_getlist_with_quotes(configuration: Configuration) -> None:
"""
must return list of string if there is string with quote inside quote
"""
configuration.set_option("build", "test_list", """"ahriman is" c"'"ool""")
assert configuration.getlist("build", "test_list") == ["""ahriman is""", """c'ool"""]
configuration.set_option("build", "test_list", """'ahriman is' c'"'ool""")
assert configuration.getlist("build", "test_list") == ["""ahriman is""", """c"ool"""]
def test_getlist_unmatched_quote(configuration: Configuration) -> None:
"""
must raise exception on unmatched quote in string value
"""
configuration.set_option("build", "test_list", """ahri"man is cool""")
with pytest.raises(ValueError):
configuration.getlist("build", "test_list")
configuration.set_option("build", "test_list", """ahri'man is cool""")
with pytest.raises(ValueError):
configuration.getlist("build", "test_list")
def test_getlist_append() -> None:
"""
must correctly append list values
"""
configuration = Configuration()
configuration._read(
StringIO("""
[section]
list1[] = value1
list1[] = value2
list2[] = value3
list2[] =
list2[] = value4
list2[] = value5
list3[] = value6
list3 = value7
list3[] = value8
"""), "io")
assert configuration.getlist("section", "list1") == ["value1", "value2"]
assert configuration.getlist("section", "list2") == ["value4", "value5"]
assert configuration.getlist("section", "list3") == ["value7", "value8"]
def test_getpath_absolute_to_absolute(configuration: Configuration) -> None:
"""
must not change path for absolute path in settings
"""
path = Path("/a/b/c")
configuration.set_option("build", "path", str(path))
assert configuration.getpath("build", "path") == path
def test_getpath_absolute_to_relative(configuration: Configuration) -> None:
"""
must prepend root path to relative path
"""
path = Path("a")
configuration.set_option("build", "path", str(path))
result = configuration.getpath("build", "path")
assert result.is_absolute()
assert result.parent == configuration.path.parent
assert result.name == path.name
def test_getpath_with_fallback(configuration: Configuration) -> None:
"""
must return fallback path
"""
path = Path("a")
assert configuration.getpath("some", "option", fallback=path).name == str(path)
assert configuration.getpath("some", "option", fallback=None) is None
def test_getpath_without_fallback(configuration: Configuration) -> None:
"""
must raise exception without fallback
"""
with pytest.raises(configparser.NoSectionError):
assert configuration.getpath("some", "option")
with pytest.raises(configparser.NoOptionError):
assert configuration.getpath("build", "option")
def test_getpathlist(configuration: Configuration) -> None:
"""
must extract path list
"""
path = Path("/a/b/c")
configuration.set_option("build", "path", f"""{path} {path.relative_to("/")}""")
result = configuration.getpathlist("build", "path")
assert all(element.is_absolute() for element in result)
assert path in result
assert all(element.is_relative_to("/") for element in result)
def test_gettype(configuration: Configuration) -> None:
"""
must extract type from variable
"""
section, provider = configuration.gettype("customs3", configuration.repository_id)
assert section == "customs3"
assert provider == "s3"
def test_gettype_with_fallback(configuration: Configuration) -> None:
"""
must return same provider name as in fallback
"""
section, provider = configuration.gettype("rsync", configuration.repository_id, fallback="abracadabra")
assert section == "rsync"
assert provider == "abracadabra"
def test_gettype_from_section(configuration: Configuration) -> None:
"""
must extract type from section name
"""
section, provider = configuration.gettype("rsync", configuration.repository_id)
assert section == "rsync"
assert provider == "rsync"
def test_gettype_from_section_with_architecture(configuration: Configuration) -> None:
"""
must extract type from section name with architecture
"""
section, provider = configuration.gettype("github", configuration.repository_id)
assert section == "github:x86_64"
assert provider == "github"
def test_gettype_from_section_no_section(configuration: Configuration) -> None:
"""
must raise NoSectionError during type extraction from section name with architecture
"""
# technically rsync:x86_64 is valid section
# but in current configuration it must be considered as missing section
with pytest.raises(configparser.NoSectionError):
configuration.gettype("rsync:x86_64", configuration.repository_id)
def test_load_environment(configuration: Configuration) -> None:
"""
must load environment variables
"""
os.environ["section:key"] = "value1"
os.environ["section:identifier:key"] = "value2"
configuration.load_environment()
assert configuration.get("section", "key") == "value1"
assert configuration.get("section:identifier", "key") == "value2"
def test_load_includes(mocker: MockerFixture) -> None:
"""
must load includes
"""
mocker.patch.object(Configuration, "logging_path", Path("logging"))
read_mock = mocker.patch("ahriman.core.configuration.Configuration.read")
glob_mock = mocker.patch("pathlib.Path.glob", autospec=True, return_value=[Path("include"), Path("logging")])
configuration = Configuration()
configuration.load_includes(Path("path"))
glob_mock.assert_called_once_with(Path("path"), "*.ini")
read_mock.assert_called_once_with(Path("include"))
assert configuration.includes == [Path("include")]
def test_load_includes_missing() -> None:
"""
must not fail if not include directory found
"""
configuration = Configuration()
configuration.set_option("settings", "include", "path")
configuration.load_includes()
def test_load_includes_no_option() -> None:
"""
must not fail if no option set
"""
configuration = Configuration()
configuration.set_option("settings", "key", "value")
configuration.load_includes()
def test_load_includes_no_section() -> None:
"""
must not fail if no section set
"""
configuration = Configuration()
configuration.load_includes()
def test_load_includes_default_path(mocker: MockerFixture) -> None:
"""
must load includes from default path
"""
mocker.patch.object(Configuration, "include", Path("path"))
glob_mock = mocker.patch("pathlib.Path.glob", autospec=True, return_value=[])
Configuration().load_includes()
glob_mock.assert_called_once_with(Path("path"), "*.ini")
def test_merge_sections_missing(configuration: Configuration) -> None:
"""
must merge create section if not exists
"""
section = configuration.section_name("build", configuration.architecture)
configuration.remove_section("build")
configuration.set_option(section, "key", "value")
configuration.merge_sections(configuration.repository_id)
assert configuration.get("build", "key") == "value"
def test_merge_sections_priority(configuration: Configuration) -> None:
"""
must merge sections in valid order
"""
empty = "build"
arch = configuration.section_name(empty, configuration.architecture)
repo = configuration.section_name(empty, configuration.repository_name)
repo_arch = configuration.section_name(empty, configuration.repository_name, configuration.architecture)
configuration.set_option(empty, "key1", "key1_value1")
configuration.set_option(arch, "key1", "key1_value2")
configuration.set_option(repo, "key1", "key1_value3")
configuration.set_option(repo_arch, "key1", "key1_value4")
configuration.set_option(empty, "key2", "key2_value1")
configuration.set_option(arch, "key2", "key2_value2")
configuration.set_option(repo, "key2", "key2_value3")
configuration.set_option(empty, "key3", "key3_value1")
configuration.set_option(arch, "key3", "key3_value2")
configuration.set_option(empty, "key4", "key4_value1")
configuration.merge_sections(configuration.repository_id)
assert configuration.get("build", "key1") == "key1_value4"
assert configuration.get("build", "key2") == "key2_value3"
assert configuration.get("build", "key3") == "key3_value2"
assert configuration.get("build", "key4") == "key4_value1"
def test_override_sections(configuration: Configuration, repository_id: RepositoryId) -> None:
"""
must correctly generate override section names
"""
assert configuration.override_sections("build", repository_id) == [
"build:x86_64",
"build:aur",
"build:aur:x86_64",
]
def test_override_sections_empty(configuration: Configuration) -> None:
"""
must look up for sections if repository identifier is empty
"""
configuration.set_option("web:x86_64", "port", "8080")
configuration.set_option("web:i686", "port", "8080")
assert configuration.override_sections("web", RepositoryId("", "")) == ["web:i686", "web:x86_64"]
def test_reload(configuration: Configuration, mocker: MockerFixture) -> None:
"""
must reload configuration
"""
load_mock = mocker.patch("ahriman.core.configuration.Configuration.load")
merge_mock = mocker.patch("ahriman.core.configuration.Configuration.merge_sections")
environment_mock = mocker.patch("ahriman.core.configuration.Configuration.load_environment")
configuration.reload()
load_mock.assert_called_once_with(configuration.path)
merge_mock.assert_called_once_with(configuration.repository_id)
environment_mock.assert_called_once_with()
def test_reload_clear(configuration: Configuration, mocker: MockerFixture) -> None:
"""
must clear current settings before configuration reload
"""
clear_mock = mocker.patch("ahriman.core.configuration.Configuration.remove_section")
sections = configuration.sections()
configuration.reload()
clear_mock.assert_has_calls([MockCall(section) for section in sections])
def test_reload_no_architecture(configuration: Configuration) -> None:
"""
must raise exception on reload if no architecture set
"""
configuration.repository_id = None
with pytest.raises(InitializeError):
configuration.reload()
def test_reload_no_path(configuration: Configuration) -> None:
"""
must raise exception on reload if no path set
"""
configuration.path = None
with pytest.raises(InitializeError):
configuration.reload()
def test_set_option(configuration: Configuration) -> None:
"""
must set option correctly
"""
configuration.set_option("settings", "option", "value")
assert configuration.get("settings", "option") == "value"
def test_set_option_new_section(configuration: Configuration) -> None:
"""
must set option correctly even if no section found
"""
configuration.set_option("section", "option", "value")
assert configuration.get("section", "option") == "value"
@@ -0,0 +1,54 @@
import pytest
from ahriman.core.configuration.configuration_multi_dict import ConfigurationMultiDict
from ahriman.core.exceptions import OptionError
def test_setitem_non_list() -> None:
"""
must insert not list correctly
"""
instance = ConfigurationMultiDict()
instance["key"] = "value"
assert instance["key"] == "value"
def test_setitem_remove() -> None:
"""
must remove key
"""
instance = ConfigurationMultiDict()
instance["key"] = "value"
instance["key"] = [""]
assert "key" not in instance
def test_setitem_array() -> None:
"""
must set array correctly
"""
instance = ConfigurationMultiDict()
instance["key[]"] = ["value1"]
instance["key[]"] = ["value2"]
assert instance["key"] == ["value1 value2"]
def test_setitem_array_exception() -> None:
"""
must raise exception if the current value is not a single value array
"""
instance = ConfigurationMultiDict()
instance["key[]"] = ["value1", "value2"]
with pytest.raises(OptionError):
instance["key[]"] = ["value3"]
def test_setitem_exception() -> None:
"""
must raise exception on invalid key
"""
instance = ConfigurationMultiDict()
with pytest.raises(OptionError):
instance["prefix[]suffix"] = "value"
@@ -0,0 +1,100 @@
import os
from ahriman.core.configuration import Configuration
from ahriman.core.configuration.shell_interpolator import ShellInterpolator
def _parser() -> dict[str, dict[str, str]]:
"""
parser mock
Returns:
dict[str, dict[str, str]]: options to be used as configparser mock
"""
return {
"section1": {
"home": "$HOME",
"key1": "value1",
"key4": "${home}",
},
"section2": {
"key2": "value2",
},
"section3": {
"key3": "${section1:home}",
"key5": "${section1:key4}",
},
"section4:suffix": {
"key6": "value6"
},
}
def test_extract_variables() -> None:
"""
must extract variables list
"""
parser = _parser()
assert dict(ShellInterpolator._extract_variables(parser, "${key1}", parser["section1"])) == {
"key1": "value1",
}
assert not dict(ShellInterpolator._extract_variables(parser, "${key2}", parser["section1"]))
assert dict(ShellInterpolator._extract_variables(parser, "${section2:key2}", parser["section1"])) == {
"section2:key2": "value2",
}
assert not dict(ShellInterpolator._extract_variables(parser, "${section2:key1}", parser["section1"]))
assert not dict(ShellInterpolator._extract_variables(parser, "${section4:key1}", parser["section1"]))
assert dict(ShellInterpolator._extract_variables(parser, "${section4:suffix:key6}", parser["section1"])) == {
"section4:suffix:key6": "value6",
}
def test_environment() -> None:
"""
must extend environment variables
"""
assert "HOME" in ShellInterpolator.environment()
assert "prefix" in ShellInterpolator.environment()
def test_before_get() -> None:
"""
must correctly extract environment variables
"""
interpolator = ShellInterpolator()
assert interpolator.before_get({}, "", "", "value", {}) == "value"
assert interpolator.before_get({}, "", "", "$value", {}) == "$value"
assert interpolator.before_get({}, "", "", "$HOME", {}) == os.environ["HOME"]
def test_before_get_escaped() -> None:
"""
must correctly read escaped variables
"""
interpolator = ShellInterpolator()
assert interpolator.before_get({}, "", "", "$$HOME", {}) == "$HOME"
def test_before_get_reference() -> None:
"""
must correctly extract environment variables after resolving cross-reference
"""
interpolator = ShellInterpolator()
assert interpolator.before_get(_parser(), "", "", "${section1:home}", {}) == os.environ["HOME"]
def test_before_get_reference_recursive(configuration: Configuration) -> None:
"""
must correctly extract environment variables after resolving cross-reference recursively
"""
interpolator = ShellInterpolator()
for section, items in _parser().items():
for option, value in items.items():
configuration.set_option(section, option, value)
assert interpolator.before_get(configuration, "", "", "${section1:home}", {}) == os.environ["HOME"]
assert interpolator.before_get(configuration, "", "", "${section3:key5}", {}) == os.environ["HOME"]
@@ -0,0 +1,81 @@
from ahriman.core.configuration.shell_template import ShellTemplate
def test_shell_template_braceidpattern() -> None:
"""
must match colons in braces
"""
assert ShellTemplate("$k:value").get_identifiers() == ["k"]
assert ShellTemplate("${k:value}").get_identifiers() == ["k:value"]
def test_remove_back() -> None:
"""
must remove substring from the back
"""
assert ShellTemplate("${k%removeme}").shell_substitute({"k": "please removeme"}) == "please "
assert ShellTemplate("${k%removeme*}").shell_substitute({"k": "please removeme removeme"}) == "please removeme "
assert ShellTemplate("${k%removem?}").shell_substitute({"k": "please removeme removeme"}) == "please removeme "
assert ShellTemplate("${k%%removeme}").shell_substitute({"k": "please removeme removeme"}) == "please removeme "
assert ShellTemplate("${k%%removeme*}").shell_substitute({"k": "please removeme removeme"}) == "please "
assert ShellTemplate("${k%%removem?}").shell_substitute({"k": "please removeme removeme"}) == "please removeme "
assert ShellTemplate("${k%removeme}").shell_substitute({}) == "${k%removeme}"
assert ShellTemplate("${k%%removeme}").shell_substitute({}) == "${k%%removeme}"
assert ShellTemplate("${k%r3m0v3m3}").shell_substitute({"k": "please removeme"}) == "please removeme"
assert ShellTemplate("${k%%r3m0v3m3}").shell_substitute({"k": "please removeme"}) == "please removeme"
def test_remove_front() -> None:
"""
must remove substring from the front
"""
assert ShellTemplate("${k#removeme}").shell_substitute({"k": "removeme please"}) == " please"
assert ShellTemplate("${k#*removeme}").shell_substitute({"k": "removeme removeme please"}) == " removeme please"
assert ShellTemplate("${k#removem?}").shell_substitute({"k": "removeme removeme please"}) == " removeme please"
assert ShellTemplate("${k##removeme}").shell_substitute({"k": "removeme removeme please"}) == " removeme please"
assert ShellTemplate("${k##*removeme}").shell_substitute({"k": "removeme removeme please"}) == " please"
assert ShellTemplate("${k##removem?}").shell_substitute({"k": "removeme removeme please"}) == " removeme please"
assert ShellTemplate("${k#removeme}").shell_substitute({}) == "${k#removeme}"
assert ShellTemplate("${k##removeme}").shell_substitute({}) == "${k##removeme}"
assert ShellTemplate("${k#r3m0v3m3}").shell_substitute({"k": "removeme please"}) == "removeme please"
assert ShellTemplate("${k##r3m0v3m3}").shell_substitute({"k": "removeme please"}) == "removeme please"
def test_replace() -> None:
"""
must perform regular replacement
"""
assert ShellTemplate("${k/in/out}").shell_substitute({"k": "in replace in"}) == "out replace in"
assert ShellTemplate("${k/in*/out}").shell_substitute({"k": "in replace in"}) == "out"
assert ShellTemplate("${k/*in/out}").shell_substitute({"k": "in replace in replace"}) == "out replace"
assert ShellTemplate("${k/i?/out}").shell_substitute({"k": "in replace in"}) == "out replace in"
assert ShellTemplate("${k//in/out}").shell_substitute({"k": "in replace in"}) == "out replace out"
assert ShellTemplate("${k//in*/out}").shell_substitute({"k": "in replace in"}) == "out"
assert ShellTemplate("${k//*in/out}").shell_substitute({"k": "in replace in replace"}) == "out replace"
assert ShellTemplate("${k//i?/out}").shell_substitute({"k": "in replace in replace"}) == "out replace out replace"
assert ShellTemplate("${k/in/out}").shell_substitute({}) == "${k/in/out}"
assert ShellTemplate("${k//in/out}").shell_substitute({}) == "${k//in/out}"
def test_replace_back() -> None:
"""
must replace substring from the back
"""
assert ShellTemplate("${k/%in/out}").shell_substitute({"k": "in replace in"}) == "in replace out"
assert ShellTemplate("${k/%in/out}").shell_substitute({"k": "in replace in "}) == "in replace in "
def test_replace_front() -> None:
"""
must replace substring from the front
"""
assert ShellTemplate("${k/#in/out}").shell_substitute({"k": "in replace in"}) == "out replace in"
assert ShellTemplate("${k/#in/out}").shell_substitute({"k": " in replace in"}) == " in replace in"
@@ -0,0 +1,152 @@
from pathlib import Path
from pytest_mock import MockerFixture
from unittest.mock import MagicMock, call as MockCall
from ahriman.core.configuration.validator import Validator
def test_types_mapping() -> None:
"""
must set custom types
"""
assert "path" in Validator.types_mapping
assert Path in Validator.types_mapping["path"].included_types
def test_normalize_coerce_absolute_path(validator: Validator) -> None:
"""
must convert string value to path by using configuration converters
"""
convert_mock = MagicMock()
validator.configuration.converters["path"] = convert_mock
validator._normalize_coerce_absolute_path("value")
convert_mock.assert_called_once_with("value")
def test_normalize_coerce_boolean(validator: Validator, mocker: MockerFixture) -> None:
"""
must convert string value to boolean by using configuration converters
"""
convert_mock = mocker.patch("ahriman.core.configuration.Configuration._convert_to_boolean")
validator._normalize_coerce_boolean("1")
convert_mock.assert_called_once_with("1")
def test_normalize_coerce_float(validator: Validator) -> None:
"""
must convert string value to float by using configuration converters
"""
assert validator._normalize_coerce_float("1.5") == 1.5
assert validator._normalize_coerce_float("0.0") == 0.0
def test_normalize_coerce_integer(validator: Validator) -> None:
"""
must convert string value to integer by using configuration converters
"""
assert validator._normalize_coerce_integer("1") == 1
assert validator._normalize_coerce_integer("42") == 42
def test_normalize_coerce_list(validator: Validator) -> None:
"""
must convert string value to list by using configuration converters
"""
convert_mock = MagicMock()
validator.configuration.converters["list"] = convert_mock
validator._normalize_coerce_list("value")
convert_mock.assert_called_once_with("value")
def test_validate_is_ip_address(validator: Validator, mocker: MockerFixture) -> None:
"""
must validate addresses correctly
"""
error_mock = mocker.patch("ahriman.core.configuration.validator.Validator._error")
validator._validate_is_ip_address(["localhost"], "field", "localhost")
validator._validate_is_ip_address([], "field", "localhost")
validator._validate_is_ip_address([], "field", "127.0.0.1")
validator._validate_is_ip_address([], "field", "::") # nosec
validator._validate_is_ip_address([], "field", "0.0.0.0") # nosec
validator._validate_is_ip_address([], "field", "random string")
error_mock.assert_has_calls([
MockCall("field", "Value localhost must be valid IP address"),
MockCall("field", "Value random string must be valid IP address"),
])
def test_validate_is_url(validator: Validator, mocker: MockerFixture) -> None:
"""
must validate url correctly
"""
error_mock = mocker.patch("ahriman.core.configuration.validator.Validator._error")
validator._validate_is_url([], "field", "http://example.com")
validator._validate_is_url([], "field", "https://example.com")
validator._validate_is_url([], "field", "file:///tmp")
validator._validate_is_url(["http", "https"], "field", "file:///tmp")
validator._validate_is_url([], "field", "http:///path")
validator._validate_is_url([], "field", "random string")
error_mock.assert_has_calls([
MockCall("field", "Url file:///tmp scheme must be one of ['http', 'https']"),
MockCall("field", "Location must be set for url http:///path of scheme http"),
MockCall("field", "Url scheme is not set for random string"),
])
def test_validate_path_exists(validator: Validator, mocker: MockerFixture) -> None:
"""
must validate that paths exists
"""
error_mock = mocker.patch("ahriman.core.configuration.validator.Validator._error")
mocker.patch("pathlib.Path.exists", return_value=False)
validator._validate_path_exists(False, "field", Path("1"))
mocker.patch("pathlib.Path.exists", return_value=True)
validator._validate_path_exists(False, "field", Path("2"))
mocker.patch("pathlib.Path.exists", return_value=False)
validator._validate_path_exists(True, "field", Path("3"))
mocker.patch("pathlib.Path.exists", return_value=True)
validator._validate_path_exists(True, "field", Path("4"))
error_mock.assert_has_calls([
MockCall("field", "Path 2 must not exist"),
MockCall("field", "Path 3 must exist"),
])
def test_validate_path_type(validator: Validator, mocker: MockerFixture) -> None:
"""
must correctly validate path type
"""
error_mock = mocker.patch("ahriman.core.configuration.validator.Validator._error")
mocker.patch("pathlib.Path.is_file", return_value=True)
validator._validate_path_type("file", "field", Path("1"))
mocker.patch("pathlib.Path.is_file", return_value=False)
validator._validate_path_type("file", "field", Path("2"))
mocker.patch("pathlib.Path.is_dir", return_value=True)
validator._validate_path_type("dir", "field", Path("3"))
mocker.patch("pathlib.Path.is_dir", return_value=False)
validator._validate_path_type("dir", "field", Path("4"))
error_mock.assert_has_calls([
MockCall("field", "Path 2 must be type of file"),
MockCall("field", "Path 4 must be type of dir"),
])
@@ -0,0 +1,94 @@
import logging
import pytest
from ahriman.core.alpm.repo import Repo
from ahriman.core.build_tools.task import Task
from ahriman.core.configuration import Configuration
from ahriman.core.sign.gpg import GPG
from ahriman.core.tree import Leaf
from ahriman.models.package import Package
from ahriman.models.repository_paths import RepositoryPaths
@pytest.fixture
def leaf_ahriman(package_ahriman: Package) -> Leaf:
"""
fixture for tree leaf with package
Args:
package_ahriman(Package): package fixture
Returns:
Leaf: tree leaf test instance
"""
return Leaf(package_ahriman)
@pytest.fixture
def leaf_python_schedule(package_python_schedule: Package) -> Leaf:
"""
fixture for tree leaf with package
Args:
package_python_schedule(Package): package fixture
Returns:
Leaf: tree leaf test instance
"""
return Leaf(package_python_schedule)
@pytest.fixture
def log_record() -> logging.LogRecord:
"""
fixture for log record object
Returns:
logging.LogRecord: log record test instance
"""
return logging.LogRecord("record", logging.INFO, "path", 42, "log message", args=(), exc_info=None)
@pytest.fixture
def repo(configuration: Configuration, repository_paths: RepositoryPaths) -> Repo:
"""
fixture for repository wrapper
Args:
configuration(Configuration): configuration fixture
repository_paths(RepositoryPaths): repository paths fixture
Returns:
Repo: repository wrapper test instance
"""
return Repo(configuration.get("repository", "name"), repository_paths, [])
@pytest.fixture
def gpg(configuration: Configuration) -> GPG:
"""
fixture for empty GPG
Args:
configuration(Configuration): configuration fixture
Returns:
GPG: GPG test instance
"""
return GPG(configuration)
@pytest.fixture
def task_ahriman(package_ahriman: Package, configuration: Configuration, repository_paths: RepositoryPaths) -> Task:
"""
fixture for built task
Args:
package_ahriman(Package): package fixture
configuration(Configuration): configuration fixture
repository_paths(RepositoryPaths): repository paths fixture
Returns:
Task: built task test instance
"""
return Task(package_ahriman, configuration, "x86_64", repository_paths)
@@ -0,0 +1,15 @@
import pytest
from sqlite3 import Connection
from unittest.mock import MagicMock
@pytest.fixture
def connection() -> Connection:
"""
mock object for sqlite3 connection
Returns:
Connection: sqlite3 connection test instance
"""
return MagicMock()
@@ -0,0 +1,21 @@
import pytest
from sqlite3 import Connection
from ahriman.core.configuration import Configuration
from ahriman.core.database.migrations import Migrations
@pytest.fixture
def migrations(connection: Connection, configuration: Configuration) -> Migrations:
"""
fixture for migrations object
Args:
connection(Connection): sqlite connection fixture
configuration(Configuration): configuration fixture
Returns:
Migrations: migrations test instance
"""
return Migrations(connection, configuration)
@@ -0,0 +1,120 @@
import pytest
from pathlib import Path
from pytest_mock import MockerFixture
from sqlite3 import Connection
from unittest.mock import call as MockCall
from ahriman.core.configuration import Configuration
from ahriman.core.database.migrations.m000_initial import migrate_data, migrate_package_statuses, migrate_patches, \
migrate_users_data, steps
from ahriman.models.package import Package
from ahriman.models.repository_paths import RepositoryPaths
def test_migration_initial() -> None:
"""
migration must not be empty
"""
assert steps
def test_migrate_data(connection: Connection, configuration: Configuration, mocker: MockerFixture) -> None:
"""
must perform data migration
"""
packages = mocker.patch("ahriman.core.database.migrations.m000_initial.migrate_package_statuses")
patches = mocker.patch("ahriman.core.database.migrations.m000_initial.migrate_patches")
users = mocker.patch("ahriman.core.database.migrations.m000_initial.migrate_users_data")
migrate_data(connection, configuration)
packages.assert_called_once_with(connection, configuration.repository_paths)
patches.assert_called_once_with(connection, configuration.repository_paths)
users.assert_called_once_with(connection, configuration)
def test_migrate_package_statuses(connection: Connection, package_ahriman: Package, repository_paths: RepositoryPaths,
mocker: MockerFixture) -> None:
"""
must migrate packages to database
"""
response = {"packages": [pytest.helpers.get_package_status_extended(package_ahriman)]}
mocker.patch("pathlib.Path.is_file", return_value=True)
mocker.patch("pathlib.Path.open")
mocker.patch("json.load", return_value=response)
migrate_package_statuses(connection, repository_paths)
connection.execute.assert_has_calls([
MockCall(pytest.helpers.anyvar(str, strict=True), pytest.helpers.anyvar(int)),
MockCall(pytest.helpers.anyvar(str, strict=True), pytest.helpers.anyvar(int)),
])
connection.executemany.assert_has_calls([
MockCall(pytest.helpers.anyvar(str, strict=True), pytest.helpers.anyvar(int)),
])
def test_migrate_package_statuses_skip(connection: Connection, repository_paths: RepositoryPaths,
mocker: MockerFixture) -> None:
"""
must skip packages migration if no cache file found
"""
mocker.patch("pathlib.Path.is_file", return_value=False)
migrate_package_statuses(connection, repository_paths)
def test_migrate_patches(connection: Connection, repository_paths: RepositoryPaths,
package_ahriman: Package, mocker: MockerFixture) -> None:
"""
must perform migration for patches
"""
mocker.patch("pathlib.Path.is_dir", return_value=True)
mocker.patch("pathlib.Path.is_file", return_value=True)
iterdir_mock = mocker.patch("pathlib.Path.iterdir", return_value=[Path(package_ahriman.base)])
read_mock = mocker.patch("pathlib.Path.read_text", return_value="patch")
migrate_patches(connection, repository_paths)
iterdir_mock.assert_called_once_with()
read_mock.assert_called_once_with(encoding="utf8")
connection.execute.assert_called_once_with(pytest.helpers.anyvar(str, strict=True), pytest.helpers.anyvar(int))
def test_migrate_patches_skip(connection: Connection, repository_paths: RepositoryPaths,
mocker: MockerFixture) -> None:
"""
must skip patches migration in case if no root found
"""
mocker.patch("pathlib.Path.is_dir", return_value=False)
iterdir_mock = mocker.patch("pathlib.Path.iterdir")
migrate_patches(connection, repository_paths)
iterdir_mock.assert_not_called()
def test_migrate_patches_no_patch(connection: Connection, repository_paths: RepositoryPaths,
package_ahriman: Package, mocker: MockerFixture) -> None:
"""
must skip package if no match found
"""
mocker.patch("pathlib.Path.is_dir", return_value=True)
mocker.patch("pathlib.Path.is_file", return_value=False)
iterdir_mock = mocker.patch("pathlib.Path.iterdir", return_value=[Path(package_ahriman.base)])
read_mock = mocker.patch("pathlib.Path.read_text")
migrate_patches(connection, repository_paths)
iterdir_mock.assert_called_once_with()
read_mock.assert_not_called()
def test_migrate_users_data(connection: Connection, configuration: Configuration) -> None:
"""
must put users to database
"""
configuration.set_option("auth:read", "user1", "password1")
configuration.set_option("auth:write", "user2", "password2")
migrate_users_data(connection, configuration)
connection.execute.assert_has_calls([
MockCall(pytest.helpers.anyvar(str, strict=True), pytest.helpers.anyvar(int)),
MockCall(pytest.helpers.anyvar(str, strict=True), pytest.helpers.anyvar(int)),
])
@@ -0,0 +1,82 @@
import pytest
from pytest_mock import MockerFixture
from sqlite3 import Connection
from unittest.mock import call as MockCall
from ahriman.core.configuration import Configuration
from ahriman.core.database.migrations.m001_package_source import migrate_data, migrate_package_remotes, steps
from ahriman.models.package import Package
from ahriman.models.package_source import PackageSource
from ahriman.models.repository_paths import RepositoryPaths
def test_migration_package_source() -> None:
"""
migration must not be empty
"""
assert steps
def test_migrate_data(connection: Connection, configuration: Configuration, mocker: MockerFixture) -> None:
"""
must perform data migration
"""
remotes = mocker.patch("ahriman.core.database.migrations.m001_package_source.migrate_package_remotes")
migrate_data(connection, configuration)
remotes.assert_called_once_with(connection, configuration.repository_paths)
def test_migrate_package_remotes(package_ahriman: Package, connection: Connection, repository_paths: RepositoryPaths,
mocker: MockerFixture) -> None:
"""
must put package remotes to database
"""
connection.execute.return_value = [{
"package_base": package_ahriman.base,
"version": package_ahriman.version,
"source": PackageSource.AUR,
}]
mocker.patch("pathlib.Path.exists", return_value=False)
migrate_package_remotes(connection, repository_paths)
connection.execute.assert_has_calls([
MockCall(pytest.helpers.anyvar(str, strict=True)),
MockCall(pytest.helpers.anyvar(str, strict=True), pytest.helpers.anyvar(int)),
])
def test_migrate_package_remotes_has_local(package_ahriman: Package, connection: Connection,
repository_paths: RepositoryPaths, mocker: MockerFixture) -> None:
"""
must skip processing for packages which have local cache
"""
connection.execute.return_value = [{
"package_base": package_ahriman.base,
"version": package_ahriman.version,
"source": PackageSource.AUR,
}]
mocker.patch("pathlib.Path.exists", return_value=True)
migrate_package_remotes(connection, repository_paths)
connection.execute.assert_called_once_with(pytest.helpers.anyvar(str, strict=True))
def test_migrate_package_remotes_vcs(package_ahriman: Package, connection: Connection,
repository_paths: RepositoryPaths, mocker: MockerFixture) -> None:
"""
must process VCS packages with local cache
"""
connection.execute.return_value = [{
"package_base": package_ahriman.base,
"version": package_ahriman.version,
"source": PackageSource.AUR,
}]
mocker.patch("pathlib.Path.exists", return_value=True)
mocker.patch.object(Package, "is_vcs", True)
migrate_package_remotes(connection, repository_paths)
connection.execute.assert_has_calls([
MockCall(pytest.helpers.anyvar(str, strict=True)),
MockCall(pytest.helpers.anyvar(str, strict=True), pytest.helpers.anyvar(int)),
])
@@ -0,0 +1,8 @@
from ahriman.core.database.migrations.m002_user_access import steps
def test_migration_user_access() -> None:
"""
migration must not be empty
"""
assert steps
@@ -0,0 +1,8 @@
from ahriman.core.database.migrations.m003_patch_variables import steps
def test_migration_patches() -> None:
"""
migration must not be empty
"""
assert steps
@@ -0,0 +1,8 @@
from ahriman.core.database.migrations.m004_logs import steps
def test_migration_logs() -> None:
"""
migration must not be empty
"""
assert steps
@@ -0,0 +1,52 @@
import pytest
from pytest_mock import MockerFixture
from sqlite3 import Connection
from ahriman.core.configuration import Configuration
from ahriman.core.database.migrations.m005_make_opt_depends import migrate_data, migrate_package_depends, steps
from ahriman.models.package import Package
def test_migration_make_opt_depends() -> None:
"""
migration must not be empty
"""
assert steps
def test_migrate_data(connection: Connection, configuration: Configuration, mocker: MockerFixture) -> None:
"""
must perform data migration
"""
depends_mock = mocker.patch("ahriman.core.database.migrations.m005_make_opt_depends.migrate_package_depends")
migrate_data(connection, configuration)
depends_mock.assert_called_once_with(connection, configuration)
def test_migrate_package_depends(connection: Connection, configuration: Configuration, package_ahriman: Package,
mocker: MockerFixture) -> None:
"""
must update make and opt depends list
"""
mocker.patch("pathlib.Path.is_dir", return_value=True)
mocker.patch("pathlib.Path.iterdir", return_value=[package_ahriman.packages[package_ahriman.base].filepath])
package_mock = mocker.patch("ahriman.models.package.Package.from_archive", return_value=package_ahriman)
migrate_package_depends(connection, configuration)
package_mock.assert_called_once_with(package_ahriman.packages[package_ahriman.base].filepath)
connection.executemany.assert_called_once_with(pytest.helpers.anyvar(str, strict=True), [{
"make_depends": package_ahriman.packages[package_ahriman.base].make_depends,
"opt_depends": package_ahriman.packages[package_ahriman.base].opt_depends,
"package": package_ahriman.base,
}])
def test_migrate_package_depends_skip(connection: Connection, configuration: Configuration,
mocker: MockerFixture) -> None:
"""
must skip update make and opt depends list if no repository directory found
"""
mocker.patch("pathlib.Path.is_dir", return_value=False)
migrate_package_depends(connection, configuration)
connection.executemany.assert_not_called()
@@ -0,0 +1,8 @@
from ahriman.core.database.migrations.m006_packages_architecture_required import steps
def test_migration_logs() -> None:
"""
migration must not be empty
"""
assert steps
@@ -0,0 +1,51 @@
import pytest
from pytest_mock import MockerFixture
from sqlite3 import Connection
from ahriman.core.configuration import Configuration
from ahriman.core.database.migrations.m007_check_depends import migrate_data, migrate_package_check_depends, steps
from ahriman.models.package import Package
def test_migration_check_depends() -> None:
"""
migration must not be empty
"""
assert steps
def test_migrate_data(connection: Connection, configuration: Configuration, mocker: MockerFixture) -> None:
"""
must perform data migration
"""
depends_mock = mocker.patch("ahriman.core.database.migrations.m007_check_depends.migrate_package_check_depends")
migrate_data(connection, configuration)
depends_mock.assert_called_once_with(connection, configuration)
def test_migrate_package_depends(connection: Connection, configuration: Configuration, package_ahriman: Package,
mocker: MockerFixture) -> None:
"""
must update check depends list
"""
mocker.patch("pathlib.Path.is_dir", return_value=True)
mocker.patch("pathlib.Path.iterdir", return_value=[package_ahriman.packages[package_ahriman.base].filepath])
package_mock = mocker.patch("ahriman.models.package.Package.from_archive", return_value=package_ahriman)
migrate_package_check_depends(connection, configuration)
package_mock.assert_called_once_with(package_ahriman.packages[package_ahriman.base].filepath)
connection.executemany.assert_called_once_with(pytest.helpers.anyvar(str, strict=True), [{
"check_depends": package_ahriman.packages[package_ahriman.base].check_depends,
"package": package_ahriman.base,
}])
def test_migrate_package_depends_skip(connection: Connection, configuration: Configuration,
mocker: MockerFixture) -> None:
"""
must skip update check depends list if no repository directory found
"""
mocker.patch("pathlib.Path.is_dir", return_value=False)
migrate_package_check_depends(connection, configuration)
connection.executemany.assert_not_called()
@@ -0,0 +1,51 @@
import pytest
from pytest_mock import MockerFixture
from sqlite3 import Connection
from ahriman.core.configuration import Configuration
from ahriman.core.database.migrations.m008_packagers import migrate_data, migrate_package_base_packager, steps
from ahriman.models.package import Package
def test_migration_packagers() -> None:
"""
migration must not be empty
"""
assert steps
def test_migrate_data(connection: Connection, configuration: Configuration, mocker: MockerFixture) -> None:
"""
must perform data migration
"""
depends_mock = mocker.patch("ahriman.core.database.migrations.m008_packagers.migrate_package_base_packager")
migrate_data(connection, configuration)
depends_mock.assert_called_once_with(connection, configuration)
def test_migrate_package_base_packager(connection: Connection, configuration: Configuration, package_ahriman: Package,
mocker: MockerFixture) -> None:
"""
must update packagers
"""
mocker.patch("pathlib.Path.is_dir", return_value=True)
mocker.patch("pathlib.Path.iterdir", return_value=[package_ahriman.packages[package_ahriman.base].filepath])
package_mock = mocker.patch("ahriman.models.package.Package.from_archive", return_value=package_ahriman)
migrate_package_base_packager(connection, configuration)
package_mock.assert_called_once_with(package_ahriman.packages[package_ahriman.base].filepath)
connection.executemany.assert_called_once_with(pytest.helpers.anyvar(str, strict=True), [{
"package_base": package_ahriman.base,
"packager": package_ahriman.packager,
}])
def test_migrate_package_depends_skip(connection: Connection, configuration: Configuration,
mocker: MockerFixture) -> None:
"""
must skip update packagers if no repository directory found
"""
mocker.patch("pathlib.Path.is_dir", return_value=False)
migrate_package_base_packager(connection, configuration)
connection.executemany.assert_not_called()
@@ -0,0 +1,8 @@
from ahriman.core.database.migrations.m009_local_source import steps
def test_migration_local_source() -> None:
"""
migration must not be empty
"""
assert steps
@@ -0,0 +1,8 @@
from ahriman.core.database.migrations.m010_version_based_logs_removal import steps
def test_migration_version_based_logs_removal() -> None:
"""
migration must not be empty
"""
assert steps
@@ -0,0 +1,39 @@
import pytest
from pytest_mock import MockerFixture
from sqlite3 import Connection
from unittest.mock import call as MockCall
from ahriman.core.configuration import Configuration
from ahriman.core.database.migrations.m011_repository_name import migrate_data, migrate_package_repository, steps
def test_migration_repository_name() -> None:
"""
migration must not be empty
"""
assert steps
def test_migrate_data(connection: Connection, configuration: Configuration, mocker: MockerFixture) -> None:
"""
must perform data migration
"""
repository_mock = mocker.patch("ahriman.core.database.migrations.m011_repository_name.migrate_package_repository")
migrate_data(connection, configuration)
repository_mock.assert_called_once_with(connection, configuration)
def test_migrate_package_repository(connection: Connection, configuration: Configuration) -> None:
"""
must correctly set repository and architecture
"""
migrate_package_repository(connection, configuration)
connection.execute.assert_has_calls([
MockCall(pytest.helpers.anyvar(str, strict=True), {"repository": configuration.repository_id.id}),
MockCall(pytest.helpers.anyvar(str, strict=True), {"repository": configuration.repository_id.id}),
MockCall(pytest.helpers.anyvar(str, strict=True), {"repository": configuration.repository_id.id}),
MockCall(pytest.helpers.anyvar(str, strict=True), {"repository": configuration.repository_id.id}),
MockCall(pytest.helpers.anyvar(str, strict=True), {"repository": configuration.repository_id.id}),
])
@@ -0,0 +1,8 @@
from ahriman.core.database.migrations.m012_last_commit_sha import steps
def test_migration_last_commit_sha() -> None:
"""
migration must not be empty
"""
assert steps
@@ -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
@@ -0,0 +1,8 @@
from ahriman.core.database.migrations.m014_auditlog import steps
def test_migration_auditlog() -> None:
"""
migration must not be empty
"""
assert steps
@@ -0,0 +1,8 @@
from ahriman.core.database.migrations.m015_logs_process_id import steps
def test_migration_logs_process_id() -> None:
"""
migration must not be empty
"""
assert steps
@@ -0,0 +1,77 @@
import pytest
from dataclasses import replace
from pathlib import Path
from pytest_mock import MockerFixture
from sqlite3 import Connection
from typing import Any
from unittest.mock import call as MockCall
from ahriman.core.configuration import Configuration
from ahriman.core.database.migrations.m016_archive import migrate_data, move_packages
from ahriman.models.package import Package
from ahriman.models.repository_paths import RepositoryPaths
def test_migrate_data(connection: Connection, configuration: Configuration, mocker: MockerFixture) -> None:
"""
must perform data migration
"""
_, repository_id = configuration.check_loaded()
repositories = [
repository_id,
replace(repository_id, architecture="i686"),
]
mocker.patch("ahriman.core.repository.Explorer.repositories_extract", return_value=repositories)
migration_mock = mocker.patch("ahriman.core.database.migrations.m016_archive.move_packages")
migrate_data(connection, configuration)
migration_mock.assert_has_calls([
MockCall(replace(configuration.repository_paths, repository_id=repository))
for repository in repositories
])
def test_move_packages(repository_paths: RepositoryPaths, package_ahriman: Package,
mocker: MockerFixture) -> None:
"""
must move packages to the archive directory
"""
def is_file(self: Path, *args: Any, **kwargs: Any) -> bool:
return "file" in self.name
mocker.patch("pathlib.Path.iterdir", return_value=[
repository_paths.repository / ".hidden-file.pkg.tar.xz",
repository_paths.repository / "directory",
repository_paths.repository / "file.pkg.tar.xz",
repository_paths.repository / "file.pkg.tar.xz.sig",
repository_paths.repository / "file2.pkg.tar.xz",
repository_paths.repository / "symlink.pkg.tar.xz",
])
mocker.patch("pathlib.Path.is_dir", return_value=True)
mocker.patch("pathlib.Path.is_file", autospec=True, side_effect=is_file)
mocker.patch("pathlib.Path.exists", return_value=True)
archive_mock = mocker.patch("ahriman.models.package.Package.from_archive", return_value=package_ahriman)
move_mock = mocker.patch("ahriman.core.database.migrations.m016_archive.atomic_move")
symlink_mock = mocker.patch("pathlib.Path.symlink_to")
move_packages(repository_paths)
archive_mock.assert_has_calls([
MockCall(repository_paths.repository / filename)
for filename in ("file.pkg.tar.xz", "file2.pkg.tar.xz")
])
move_mock.assert_has_calls([
MockCall(repository_paths.repository / filename, repository_paths.archive_for(package_ahriman.base) / filename)
for filename in ("file.pkg.tar.xz", "file.pkg.tar.xz.sig", "file2.pkg.tar.xz")
])
symlink_mock.assert_has_calls([
MockCall(
Path("..") /
".." /
".." /
repository_paths.archive_for(package_ahriman.base).relative_to(repository_paths.root) /
filename
)
for filename in ("file.pkg.tar.xz", "file.pkg.tar.xz.sig", "file2.pkg.tar.xz")
])
@@ -0,0 +1,8 @@
from ahriman.core.database.migrations.m017_pkgbuild import steps
def test_migration_pkgbuild() -> None:
"""
migration must not be empty
"""
assert steps
@@ -0,0 +1,8 @@
from ahriman.core.database.migrations.m018_package_hold import steps
def test_migration_package_hold() -> None:
"""
migration must not be empty
"""
assert steps
@@ -0,0 +1,126 @@
import pytest
from pytest_mock import MockerFixture
from sqlite3 import Connection
from unittest.mock import MagicMock, call as MockCall
from ahriman.core.configuration import Configuration
from ahriman.core.database.migrations import Migrations
from ahriman.models.migration import Migration
from ahriman.models.migration_result import MigrationResult
def test_migrate(connection: Connection, configuration: Configuration, mocker: MockerFixture) -> None:
"""
must perform migrations
"""
run_mock = mocker.patch("ahriman.core.database.migrations.Migrations.run")
Migrations.migrate(connection, configuration)
run_mock.assert_called_once_with()
def test_apply_migrations(migrations: Migrations, mocker: MockerFixture) -> None:
"""
must apply list of migrations
"""
cursor = MagicMock()
migration = Migration(index=0, name="test", steps=["select 1"], migrate_data=MagicMock())
migrations.connection.cursor.return_value = cursor
migration_mock = mocker.patch("ahriman.core.database.migrations.Migrations.perform_migration")
migrations.apply_migrations([migration])
cursor.execute.assert_has_calls([
MockCall("begin exclusive"),
MockCall("commit"),
])
cursor.close.assert_called_once_with()
migration_mock.assert_called_once_with(cursor, migration)
def test_apply_migration_exception(migrations: Migrations, mocker: MockerFixture) -> None:
"""
must roll back and close cursor on exception during migration
"""
cursor = MagicMock()
mocker.patch("logging.Logger.info", side_effect=Exception)
migrations.connection.cursor.return_value = cursor
with pytest.raises(Exception):
migrations.apply_migrations([Migration(index=0, name="test", steps=["select 1"], migrate_data=MagicMock())])
cursor.execute.assert_has_calls([
MockCall("begin exclusive"),
MockCall("rollback"),
])
cursor.close.assert_called_once_with()
def test_apply_migration_sql_exception(migrations: Migrations) -> None:
"""
must close cursor on general migration error
"""
cursor = MagicMock()
cursor.execute.side_effect = Exception
migrations.connection.cursor.return_value = cursor
with pytest.raises(Exception):
migrations.apply_migrations([Migration(index=0, name="test", steps=["select 1"], migrate_data=MagicMock())])
cursor.close.assert_called_once_with()
def test_migrations(migrations: Migrations) -> None:
"""
must retrieve migrations
"""
assert migrations.migrations()
def test_perform_migration(migrations: Migrations) -> None:
"""
must perform single migration
"""
migrate_data_mock = MagicMock()
cursor = MagicMock()
migration = Migration(index=0, name="test", steps=["select 1"], migrate_data=migrate_data_mock)
migrations.perform_migration(cursor, migration)
cursor.execute.assert_called_once_with("select 1")
migrate_data_mock.assert_called_once_with(migrations.connection, migrations.configuration)
def test_run_skip(migrations: Migrations, mocker: MockerFixture) -> None:
"""
must skip migration if version is the same
"""
mocker.patch.object(MigrationResult, "is_outdated", False)
migrations.run()
migrations.connection.cursor.assert_not_called()
def test_run(migrations: Migrations, mocker: MockerFixture) -> None:
"""
must run migration
"""
migration = Migration(index=0, name="test", steps=["select 1"], migrate_data=MagicMock())
mocker.patch("ahriman.core.database.migrations.Migrations.user_version", return_value=0)
mocker.patch("ahriman.core.database.migrations.Migrations.migrations", return_value=[migration])
validate_mock = mocker.patch("ahriman.models.migration_result.MigrationResult.validate")
apply_mock = mocker.patch("ahriman.core.database.migrations.Migrations.apply_migrations")
migrations.run()
apply_mock.assert_called_once_with([migration])
validate_mock.assert_called_once_with()
migrations.connection.execute.assert_called_once_with("pragma user_version = 1")
def test_user_version(migrations: Migrations) -> None:
"""
must correctly extract current migration version
"""
cursor = MagicMock()
cursor.fetchone.return_value = {"user_version": 42}
migrations.connection.execute.return_value = cursor
version = migrations.user_version()
migrations.connection.execute.assert_called_once_with("pragma user_version")
assert version == 42
@@ -0,0 +1,98 @@
from ahriman.core.database import SQLite
from ahriman.models.user import User
from ahriman.models.user_access import UserAccess
def test_user_get_update(database: SQLite, user: User) -> None:
"""
must retrieve user from the database
"""
database.user_update(user)
assert database.user_get(user.username) == user
def test_user_list(database: SQLite, user: User) -> None:
"""
must return all users
"""
database.user_update(user)
second = User(username=user.password, password=user.username, access=user.access, packager_id=None, key=None)
database.user_update(second)
users = database.user_list(None, None)
assert len(users) == 2
assert user in users
assert second in users
def test_user_list_filter_by_username(database: SQLite) -> None:
"""
must return users filtered by its id
"""
first = User(username="1", password="", access=UserAccess.Read, packager_id=None, key=None)
second = User(username="2", password="", access=UserAccess.Full, packager_id=None, key=None)
third = User(username="3", password="", access=UserAccess.Read, packager_id=None, key=None)
database.user_update(first)
database.user_update(second)
database.user_update(third)
assert database.user_list("1", None) == [first]
assert database.user_list("2", None) == [second]
assert database.user_list("3", None) == [third]
def test_user_list_filter_by_access(database: SQLite) -> None:
"""
must return users filtered by its access
"""
first = User(username="1", password="", access=UserAccess.Read, packager_id=None, key=None)
second = User(username="2", password="", access=UserAccess.Full, packager_id=None, key=None)
third = User(username="3", password="", access=UserAccess.Read, packager_id=None, key=None)
database.user_update(first)
database.user_update(second)
database.user_update(third)
users = database.user_list(None, UserAccess.Read)
assert len(users) == 2
assert first in users
assert third in users
def test_user_list_filter_by_username_access(database: SQLite) -> None:
"""
must return users filtered by its access and username
"""
first = User(username="1", password="", access=UserAccess.Read, packager_id=None, key=None)
second = User(username="2", password="", access=UserAccess.Full, packager_id=None, key=None)
third = User(username="3", password="", access=UserAccess.Read, packager_id=None, key=None)
database.user_update(first)
database.user_update(second)
database.user_update(third)
assert database.user_list("1", UserAccess.Read) == [first]
assert not database.user_list("1", UserAccess.Full)
def test_user_remove_update(database: SQLite, user: User) -> None:
"""
must remove user from the database
"""
database.user_update(user)
database.user_remove(user.username)
assert database.user_get(user.username) is None
def test_user_update(database: SQLite, user: User) -> None:
"""
must update user in the database
"""
database.user_update(user)
assert database.user_get(user.username) == user
new_user = User(username=user.username, password=user.hash_password("salt").password, access=UserAccess.Full,
packager_id=None, key="new key")
database.user_update(new_user)
assert database.user_get(new_user.username) == new_user

Some files were not shown because too many files have changed in this diff Show More