implement support of rollback handler

This commit is contained in:
2026-03-16 10:06:15 +02:00
parent 96684d7ddd
commit 7d20d24d86
32 changed files with 834 additions and 246 deletions

View File

@@ -190,13 +190,14 @@ def test_update(application_repository: ApplicationRepository, package_ahriman:
"""
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=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")
@@ -204,7 +205,24 @@ def test_update(application_repository: ApplicationRepository, package_ahriman:
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(result), MockCall(result)])
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,

View File

@@ -3,14 +3,12 @@ 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 import Package
from ahriman.models.package_source import PackageSource
from ahriman.models.packagers import Packagers
from ahriman.models.pkgbuild_patch import PkgbuildPatch
from ahriman.models.result import Result
def _default_args(args: argparse.Namespace) -> argparse.Namespace:
@@ -24,13 +22,9 @@ def _default_args(args: argparse.Namespace) -> argparse.Namespace:
argparse.Namespace: generated arguments for these test cases
"""
args.package = ["ahriman"]
args.changes = True
args.exit_code = False
args.increment = True
args.now = False
args.refresh = 0
args.source = PackageSource.Auto
args.dependencies = True
args.username = "username"
args.variable = None
return args
@@ -43,103 +37,49 @@ def test_run(args: argparse.Namespace, configuration: Configuration, repository:
"""
args = _default_args(args)
mocker.patch("ahriman.core.repository.Repository.load", return_value=repository)
application_mock = mocker.patch("ahriman.application.application.Application.add")
dependencies_mock = mocker.patch("ahriman.application.application.Application.with_dependencies")
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)
application_mock.assert_called_once_with(args.package, args.source, args.username)
dependencies_mock.assert_not_called()
on_start_mock.assert_called_once_with()
perform_mock.assert_called_once_with(pytest.helpers.anyvar(int), args)
def test_run_with_patches(args: argparse.Namespace, configuration: Configuration, repository: Repository,
mocker: MockerFixture) -> None:
def test_perform_action(args: argparse.Namespace, application: Application, mocker: MockerFixture) -> None:
"""
must run command and insert temporary patches
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.core.repository.Repository.load", return_value=repository)
mocker.patch("ahriman.application.application.Application.add")
application_mock = mocker.patch("ahriman.core.status.local_client.LocalClient.package_patches_update")
patches_mock = mocker.patch("ahriman.core.status.local_client.LocalClient.package_patches_update")
_, repository_id = configuration.check_loaded()
Add.run(args, repository_id, configuration, report=False)
application_mock.assert_called_once_with(args.package[0], PkgbuildPatch("KEY", "VALUE"))
Add.perform_action(application, args)
patches_mock.assert_called_once_with(args.package[0], PkgbuildPatch("KEY", "VALUE"))
def test_run_with_updates(args: argparse.Namespace, configuration: Configuration, repository: Repository,
package_ahriman: Package, mocker: MockerFixture) -> None:
def test_perform_action_with_updates(args: argparse.Namespace, application: Application, mocker: MockerFixture) -> None:
"""
must run command with updates after
must perform add action with updates after
"""
args = _default_args(args)
args.now = True
result = Result()
result.add_updated(package_ahriman)
mocker.patch("ahriman.application.application.Application.add")
mocker.patch("ahriman.core.repository.Repository.load", return_value=repository)
application_mock = mocker.patch("ahriman.application.application.Application.update", return_value=result)
check_mock = mocker.patch("ahriman.application.handlers.handler.Handler.check_status")
changes_mock = mocker.patch("ahriman.application.application.Application.changes")
updates_mock = mocker.patch("ahriman.application.application.Application.updates", return_value=[package_ahriman])
dependencies_mock = mocker.patch("ahriman.application.application.Application.with_dependencies",
return_value=[package_ahriman])
print_mock = mocker.patch("ahriman.application.application.Application.print_updates")
update_mock = mocker.patch("ahriman.application.handlers.update.Update.perform_action")
_, repository_id = configuration.check_loaded()
Add.run(args, repository_id, configuration, report=False)
updates_mock.assert_called_once_with(args.package,
aur=False, local=False, manual=True, vcs=False, check_files=False)
changes_mock.assert_called_once_with([package_ahriman])
application_mock.assert_called_once_with([package_ahriman],
Packagers(args.username, {package_ahriman.base: "packager"}),
bump_pkgrel=args.increment)
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_run_no_changes(args: argparse.Namespace, configuration: Configuration, repository: Repository,
mocker: MockerFixture) -> None:
"""
must skip changes calculation during package addition
"""
args = _default_args(args)
args.now = True
args.changes = False
mocker.patch("ahriman.application.application.Application.add")
mocker.patch("ahriman.core.repository.Repository.load", return_value=repository)
mocker.patch("ahriman.application.application.Application.update")
mocker.patch("ahriman.application.handlers.handler.Handler.check_status")
mocker.patch("ahriman.application.application.Application.updates")
mocker.patch("ahriman.application.application.Application.with_dependencies")
mocker.patch("ahriman.application.application.Application.print_updates")
changes_mock = mocker.patch("ahriman.application.application.Application.changes")
_, repository_id = configuration.check_loaded()
Add.run(args, repository_id, configuration, report=False)
changes_mock.assert_not_called()
def test_run_empty_exception(args: argparse.Namespace, configuration: Configuration, repository: Repository,
mocker: MockerFixture) -> None:
"""
must raise ExitCode exception on empty result
"""
args = _default_args(args)
args.now = True
args.exit_code = True
mocker.patch("ahriman.application.application.Application.add")
mocker.patch("ahriman.core.repository.Repository.load", return_value=repository)
mocker.patch("ahriman.application.application.Application.update", return_value=Result())
mocker.patch("ahriman.application.application.Application.with_dependencies")
mocker.patch("ahriman.application.application.Application.updates")
mocker.patch("ahriman.application.application.Application.print_updates")
check_mock = mocker.patch("ahriman.application.handlers.handler.Handler.check_status")
_, repository_id = configuration.check_loaded()
Add.run(args, repository_id, configuration, report=False)
check_mock.assert_called_once_with(True, False)
Add.perform_action(application, args)
update_mock.assert_called_once_with(application, args)

View File

@@ -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)

View File

@@ -39,26 +39,39 @@ def _default_args(args: argparse.Namespace) -> argparse.Namespace:
return args
def test_run(args: argparse.Namespace, package_ahriman: Package, configuration: Configuration, repository: Repository,
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)
mocker.patch("ahriman.core.repository.Repository.load", return_value=repository)
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")
on_start_mock = mocker.patch("ahriman.application.application.Application.on_start")
print_mock = mocker.patch("ahriman.application.application.Application.print_updates")
_, repository_id = configuration.check_loaded()
Update.run(args, repository_id, configuration, report=False)
Update.perform_action(application, args)
application_mock.assert_called_once_with([package_ahriman],
Packagers(args.username, {package_ahriman.base: "packager"}),
bump_pkgrel=args.increment)
@@ -67,35 +80,31 @@ def test_run(args: argparse.Namespace, package_ahriman: Package, configuration:
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)
on_start_mock.assert_called_once_with()
print_mock.assert_called_once_with([package_ahriman], log_fn=pytest.helpers.anyvar(int))
def test_run_empty_exception(args: argparse.Namespace, configuration: Configuration, repository: Repository,
mocker: MockerFixture) -> None:
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.core.repository.Repository.load", return_value=repository)
mocker.patch("ahriman.application.application.Application.updates", return_value=[])
check_mock = mocker.patch("ahriman.application.handlers.handler.Handler.check_status")
_, repository_id = configuration.check_loaded()
Update.run(args, repository_id, configuration, report=False)
Update.perform_action(application, args)
check_mock.assert_called_once_with(True, [])
def test_run_update_empty_exception(args: argparse.Namespace, package_ahriman: Package, configuration: Configuration,
repository: Repository, mocker: MockerFixture) -> None:
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.core.repository.Repository.load", return_value=repository)
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])
@@ -103,26 +112,23 @@ def test_run_update_empty_exception(args: argparse.Namespace, package_ahriman: P
mocker.patch("ahriman.application.application.Application.changes")
check_mock = mocker.patch("ahriman.application.handlers.handler.Handler.check_status")
_, repository_id = configuration.check_loaded()
Update.run(args, repository_id, configuration, report=False)
Update.perform_action(application, args)
check_mock.assert_called_once_with(True, False)
def test_run_dry_run(args: argparse.Namespace, package_ahriman: Package, configuration: Configuration,
repository: Repository, mocker: MockerFixture) -> None:
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
mocker.patch("ahriman.core.repository.Repository.load", return_value=repository)
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")
_, repository_id = configuration.check_loaded()
Update.run(args, repository_id, configuration, report=False)
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()
@@ -130,22 +136,19 @@ def test_run_dry_run(args: argparse.Namespace, package_ahriman: Package, configu
check_mock.assert_called_once_with(False, [package_ahriman])
def test_run_no_changes(args: argparse.Namespace, configuration: Configuration, repository: Repository,
mocker: MockerFixture) -> None:
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.core.repository.Repository.load", return_value=repository)
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")
_, repository_id = configuration.check_loaded()
Update.run(args, repository_id, configuration, report=False)
Update.perform_action(application, args)
changes_mock.assert_not_called()

View File

@@ -271,6 +271,18 @@ def test_subparsers_package_add_option_variable_multiple(parser: argparse.Argume
assert args.variable == ["var1", "var2"]
def test_subparsers_package_add_repo_update(parser: argparse.ArgumentParser) -> None:
"""
package-add must have same keys as repo-update
"""
args = parser.parse_args(["package-add", "ahriman"])
reference_args = parser.parse_args(["repo-update"])
del args.now
del args.source
del args.variable
assert dir(args) == dir(reference_args)
def test_subparsers_package_archives(parser: argparse.ArgumentParser) -> None:
"""
package-archives command must imply action, exit code, info, lock, quiet, report and unsafe
@@ -325,6 +337,26 @@ def test_subparsers_package_changes_remove_package_changes(parser: argparse.Argu
assert dir(args) == dir(reference_args)
def test_subparsers_package_copy_option_architecture(parser: argparse.ArgumentParser) -> None:
"""
package-copy command must correctly parse architecture list
"""
args = parser.parse_args(["package-copy", "source", "ahriman"])
assert args.architecture is None
args = parser.parse_args(["-a", "x86_64", "package-copy", "source", "ahriman"])
assert args.architecture == "x86_64"
def test_subparsers_package_copy_option_repository(parser: argparse.ArgumentParser) -> None:
"""
package-copy command must correctly parse repository list
"""
args = parser.parse_args(["package-copy", "source", "ahriman"])
assert args.repository is None
args = parser.parse_args(["-r", "repo", "package-copy", "source", "ahriman"])
assert args.repository == "repo"
def test_subparsers_package_pkgbuild(parser: argparse.ArgumentParser) -> None:
"""
package-pkgbuild command must imply action, exit code, lock, quiet, report and unsafe
@@ -363,26 +395,6 @@ def test_subparsers_package_pkgbuild_remove_package_pkgbuild(parser: argparse.Ar
assert dir(args) == dir(reference_args)
def test_subparsers_package_copy_option_architecture(parser: argparse.ArgumentParser) -> None:
"""
package-copy command must correctly parse architecture list
"""
args = parser.parse_args(["package-copy", "source", "ahriman"])
assert args.architecture is None
args = parser.parse_args(["-a", "x86_64", "package-copy", "source", "ahriman"])
assert args.architecture == "x86_64"
def test_subparsers_package_copy_option_repository(parser: argparse.ArgumentParser) -> None:
"""
package-copy command must correctly parse repository list
"""
args = parser.parse_args(["package-copy", "source", "ahriman"])
assert args.repository is None
args = parser.parse_args(["-r", "repo", "package-copy", "source", "ahriman"])
assert args.repository == "repo"
def test_subparsers_package_remove_option_architecture(parser: argparse.ArgumentParser) -> None:
"""
package-remove command must correctly parse architecture list
@@ -403,6 +415,68 @@ def test_subparsers_package_remove_option_repository(parser: argparse.ArgumentPa
assert args.repository == "repo"
def test_subparsers_package_rollback(parser: argparse.ArgumentParser) -> None:
"""
package-rollback command must imply aur, changes, check-files, dependencies, dry-run, exit-code, increment, now,
local, manual, refresh, source, variable and vcs
"""
args = parser.parse_args(["package-rollback", "ahriman", "1.0.0-1"])
assert not args.aur
assert not args.changes
assert not args.check_files
assert not args.dependencies
assert not args.dry_run
assert args.exit_code
assert not args.increment
assert not args.local
assert not args.manual
assert args.now
assert not args.refresh
assert not args.vcs
assert args.variable is None
def test_subparsers_package_rollback_option_architecture(parser: argparse.ArgumentParser) -> None:
"""
package-rollback command must correctly parse architecture list
"""
args = parser.parse_args(["package-rollback", "ahriman", "1.0.0-1"])
assert args.architecture is None
args = parser.parse_args(["-a", "x86_64", "package-rollback", "ahriman", "1.0.0-1"])
assert args.architecture == "x86_64"
def test_subparsers_package_rollback_option_repository(parser: argparse.ArgumentParser) -> None:
"""
package-rollback command must correctly parse repository list
"""
args = parser.parse_args(["package-rollback", "ahriman", "1.0.0-1"])
assert args.repository is None
args = parser.parse_args(["-r", "repo", "package-rollback", "ahriman", "1.0.0-1"])
assert args.repository == "repo"
def test_subparsers_package_rollback_option_hold(parser: argparse.ArgumentParser) -> None:
"""
package-rollback command must correctly parse hold option
"""
args = parser.parse_args(["package-rollback", "ahriman", "1.0.0-1"])
assert args.hold
args = parser.parse_args(["package-rollback", "ahriman", "1.0.0-1", "--no-hold"])
assert not args.hold
def test_subparsers_package_rollback_package_add(parser: argparse.ArgumentParser) -> None:
"""
package-rollback must have same keys as package-add
"""
args = parser.parse_args(["package-rollback", "ahriman", "1.0.0-1"])
reference_args = parser.parse_args(["package-add", "ahriman"])
del args.hold
del args.version
assert dir(args) == dir(reference_args)
def test_subparsers_package_status(parser: argparse.ArgumentParser) -> None:
"""
package-status command must imply lock, quiet, report and unsafe

View File

@@ -8,7 +8,6 @@ from unittest.mock import MagicMock
from ahriman.core.repository import Repository
from ahriman.models.changes import Changes
from ahriman.models.package import Package
from ahriman.models.repository_id import RepositoryId
def test_full_depends(repository: Repository, package_ahriman: Package, package_python_schedule: Package,
@@ -93,18 +92,41 @@ def test_load_archives_different_version(repository: Repository, package_python_
assert packages[0].version == package_python_schedule.version
def test_load_archives_all_versions(repository: Repository, package_ahriman: Package,
mocker: MockerFixture) -> None:
"""
must load packages with different versions keeping all when latest_only is False
"""
mocker.patch("ahriman.models.package.Package.from_archive",
side_effect=[package_ahriman, replace(package_ahriman, version="0.0.1-1")])
mocker.patch("ahriman.core.status.local_client.LocalClient.package_get", return_value=[])
packages = repository.load_archives([Path("a.pkg.tar.xz"), Path("b.pkg.tar.xz")], latest_only=False)
assert len(packages) == 2
def test_package_archives(repository: Repository, package_ahriman: Package, mocker: MockerFixture) -> None:
"""
must load package archives sorted by version
"""
mocker.patch("ahriman.core.repository.package_info.package_like", return_value=True)
mocker.patch("pathlib.Path.iterdir", return_value=[str(i) for i in range(5)])
mocker.patch("ahriman.models.package.Package.from_archive",
side_effect=lambda version: replace(package_ahriman, version=version))
mocker.patch("pathlib.Path.is_dir", return_value=True)
mocker.patch("pathlib.Path.iterdir")
load_mock = mocker.patch("ahriman.core.repository.package_info.PackageInfo.load_archives",
return_value=[replace(package_ahriman, version=str(i)) for i in range(5)])
result = repository.package_archives(package_ahriman.base)
assert len(result) == 5
assert [p.version for p in result] == [str(i) for i in range(5)]
load_mock.assert_called_once_with(pytest.helpers.anyvar(int), latest_only=False)
def test_package_archives_no_directory(repository: Repository, package_ahriman: Package,
mocker: MockerFixture) -> None:
"""
must return empty list if archive directory does not exist
"""
mocker.patch("pathlib.Path.is_dir", return_value=False)
assert repository.package_archives(package_ahriman.base) == []
def test_package_archives_architecture_mismatch(repository: Repository, package_ahriman: Package,
@@ -114,8 +136,10 @@ def test_package_archives_architecture_mismatch(repository: Repository, package_
"""
package_ahriman.packages[package_ahriman.base].architecture = "i686"
mocker.patch("pathlib.Path.iterdir", return_value=[package_ahriman.packages[package_ahriman.base].filepath])
mocker.patch("ahriman.models.package.Package.from_archive", return_value=package_ahriman)
mocker.patch("pathlib.Path.is_dir", return_value=True)
mocker.patch("pathlib.Path.iterdir")
mocker.patch("ahriman.core.repository.package_info.PackageInfo.load_archives",
return_value=[package_ahriman])
result = repository.package_archives(package_ahriman.base)
assert len(result) == 0
@@ -126,13 +150,7 @@ def test_package_archives_lookup(repository: Repository, package_ahriman: Packag
"""
must existing packages which match the version
"""
mocker.patch("pathlib.Path.is_dir", return_value=True)
mocker.patch("pathlib.Path.iterdir", return_value=[
Path("1.pkg.tar.zst"),
Path("2.pkg.tar.zst"),
Path("3.pkg.tar.zst"),
])
mocker.patch("ahriman.models.package.Package.from_archive", side_effect=[
archives_mock = mocker.patch("ahriman.core.repository.package_info.PackageInfo.package_archives", return_value=[
package_ahriman,
package_python_schedule,
replace(package_ahriman, version="1"),
@@ -140,6 +158,7 @@ def test_package_archives_lookup(repository: Repository, package_ahriman: Packag
glob_mock = mocker.patch("pathlib.Path.glob", return_value=[Path("1.pkg.tar.xz")])
assert repository.package_archives_lookup(package_ahriman) == [Path("1.pkg.tar.xz")]
archives_mock.assert_called_once_with(package_ahriman.base)
glob_mock.assert_called_once_with(f"{package_ahriman.packages[package_ahriman.base].filename}*")
@@ -148,12 +167,8 @@ def test_package_archives_lookup_version_mismatch(repository: Repository, packag
"""
must return nothing if no packages found with the same version
"""
mocker.patch("pathlib.Path.is_dir", return_value=True)
mocker.patch("pathlib.Path.iterdir", return_value=[
Path("1.pkg.tar.zst"),
])
mocker.patch("ahriman.models.package.Package.from_archive", return_value=replace(package_ahriman, version="1"))
mocker.patch("ahriman.core.repository.package_info.PackageInfo.package_archives",
return_value=[replace(package_ahriman, version="1")])
assert repository.package_archives_lookup(package_ahriman) == []
@@ -162,14 +177,7 @@ def test_package_archives_lookup_architecture_mismatch(repository: Repository, p
"""
must return nothing if architecture doesn't match
"""
package_ahriman.packages[package_ahriman.base].architecture = "x86_64"
mocker.patch("pathlib.Path.is_dir", return_value=True)
repository.repository_id = RepositoryId("i686", repository.repository_id.name)
mocker.patch("pathlib.Path.iterdir", return_value=[
Path("1.pkg.tar.zst"),
])
mocker.patch("ahriman.models.package.Package.from_archive", return_value=package_ahriman)
mocker.patch("ahriman.core.repository.package_info.PackageInfo.package_archives", return_value=[])
assert repository.package_archives_lookup(package_ahriman) == []
@@ -178,7 +186,7 @@ def test_package_archives_lookup_no_archive_directory(repository: Repository, pa
"""
must return nothing if no archive directory found
"""
mocker.patch("pathlib.Path.is_dir", return_value=False)
mocker.patch("ahriman.core.repository.package_info.PackageInfo.package_archives", return_value=[])
assert repository.package_archives_lookup(package_ahriman) == []

View File

@@ -196,6 +196,26 @@ def test_packages_remove(spawner: Spawn, repository_id: RepositoryId, mocker: Mo
spawn_mock.assert_called_once_with(repository_id, "package-remove", "ahriman", "linux")
def test_packages_rollback(spawner: Spawn, repository_id: RepositoryId, mocker: MockerFixture) -> None:
"""
must call package rollback
"""
spawn_mock = mocker.patch("ahriman.core.spawn.Spawn._spawn_process")
assert spawner.packages_rollback(repository_id, "ahriman", "1.0.0-1", "packager", hold=False)
spawn_mock.assert_called_once_with(repository_id, "package-rollback", "ahriman", "1.0.0-1",
**{"username": "packager", "no-hold": ""})
def test_packages_rollback_with_hold(spawner: Spawn, repository_id: RepositoryId, mocker: MockerFixture) -> None:
"""
must call package rollback with hold
"""
spawn_mock = mocker.patch("ahriman.core.spawn.Spawn._spawn_process")
assert spawner.packages_rollback(repository_id, "ahriman", "1.0.0-1", "packager", hold=True)
spawn_mock.assert_called_once_with(repository_id, "package-rollback", "ahriman", "1.0.0-1",
**{"username": "packager", "hold": ""})
def test_packages_update(spawner: Spawn, repository_id: RepositoryId, mocker: MockerFixture) -> None:
"""
must call repo update

View File

@@ -0,0 +1 @@
# schema testing goes in view class tests

View File

@@ -0,0 +1 @@
# schema testing goes in view class tests

View File

@@ -0,0 +1,70 @@
import pytest
from aiohttp.test_utils import TestClient
from pytest_mock import MockerFixture
from unittest.mock import AsyncMock
from ahriman.models.repository_id import RepositoryId
from ahriman.models.user_access import UserAccess
from ahriman.web.views.v1.service.rollback import RollbackView
async def test_get_permission() -> None:
"""
must return correct permission for the request
"""
for method in ("POST",):
request = pytest.helpers.request("", "", method)
assert await RollbackView.get_permission(request) == UserAccess.Full
def test_routes() -> None:
"""
must return correct routes
"""
assert RollbackView.ROUTES == ["/api/v1/service/rollback"]
async def test_post(client: TestClient, repository_id: RepositoryId, mocker: MockerFixture) -> None:
"""
must call post request correctly
"""
rollback_mock = mocker.patch("ahriman.core.spawn.Spawn.packages_rollback", return_value="abc")
user_mock = AsyncMock()
user_mock.return_value = "username"
mocker.patch("ahriman.web.views.base.BaseView.username", side_effect=user_mock)
request_schema = pytest.helpers.schema_request(RollbackView.post)
response_schema = pytest.helpers.schema_response(RollbackView.post)
payload = {"package": "ahriman", "version": "version"}
assert not request_schema.validate(payload)
response = await client.post("/api/v1/service/rollback", json=payload)
assert response.ok
rollback_mock.assert_called_once_with(repository_id, "ahriman", "version", "username", hold=True)
json = await response.json()
assert json["process_id"] == "abc"
assert not response_schema.validate(json)
async def test_post_empty(client: TestClient, mocker: MockerFixture) -> None:
"""
must call raise 400 on empty request
"""
rollback_mock = mocker.patch("ahriman.core.spawn.Spawn.packages_rollback")
response_schema = pytest.helpers.schema_response(RollbackView.post, code=400)
response = await client.post("/api/v1/service/rollback", json={"package": "", "version": "version"})
assert response.status == 400
assert not response_schema.validate(await response.json())
rollback_mock.assert_not_called()
response = await client.post("/api/v1/service/rollback", json={"package": "ahriman", "version": ""})
assert response.status == 400
assert not response_schema.validate(await response.json())
rollback_mock.assert_not_called()
response = await client.post("/api/v1/service/rollback", json={})
assert response.status == 400
assert not response_schema.validate(await response.json())
rollback_mock.assert_not_called()