mirror of
https://github.com/arcan1s/ahriman.git
synced 2026-03-21 17:13:39 +00:00
implement support of rollback handler
This commit is contained in:
@@ -164,6 +164,14 @@ ahriman.application.handlers.restore module
|
|||||||
:no-undoc-members:
|
:no-undoc-members:
|
||||||
:show-inheritance:
|
:show-inheritance:
|
||||||
|
|
||||||
|
ahriman.application.handlers.rollback module
|
||||||
|
--------------------------------------------
|
||||||
|
|
||||||
|
.. automodule:: ahriman.application.handlers.rollback
|
||||||
|
:members:
|
||||||
|
:no-undoc-members:
|
||||||
|
:show-inheritance:
|
||||||
|
|
||||||
ahriman.application.handlers.run module
|
ahriman.application.handlers.run module
|
||||||
---------------------------------------
|
---------------------------------------
|
||||||
|
|
||||||
|
|||||||
@@ -252,6 +252,14 @@ ahriman.web.schemas.package\_version\_schema module
|
|||||||
:no-undoc-members:
|
:no-undoc-members:
|
||||||
:show-inheritance:
|
:show-inheritance:
|
||||||
|
|
||||||
|
ahriman.web.schemas.packager\_schema module
|
||||||
|
-------------------------------------------
|
||||||
|
|
||||||
|
.. automodule:: ahriman.web.schemas.packager_schema
|
||||||
|
:members:
|
||||||
|
:no-undoc-members:
|
||||||
|
:show-inheritance:
|
||||||
|
|
||||||
ahriman.web.schemas.pagination\_schema module
|
ahriman.web.schemas.pagination\_schema module
|
||||||
---------------------------------------------
|
---------------------------------------------
|
||||||
|
|
||||||
@@ -332,6 +340,14 @@ ahriman.web.schemas.repository\_stats\_schema module
|
|||||||
:no-undoc-members:
|
:no-undoc-members:
|
||||||
:show-inheritance:
|
:show-inheritance:
|
||||||
|
|
||||||
|
ahriman.web.schemas.rollback\_schema module
|
||||||
|
-------------------------------------------
|
||||||
|
|
||||||
|
.. automodule:: ahriman.web.schemas.rollback_schema
|
||||||
|
:members:
|
||||||
|
:no-undoc-members:
|
||||||
|
:show-inheritance:
|
||||||
|
|
||||||
ahriman.web.schemas.search\_schema module
|
ahriman.web.schemas.search\_schema module
|
||||||
-----------------------------------------
|
-----------------------------------------
|
||||||
|
|
||||||
|
|||||||
@@ -68,6 +68,14 @@ ahriman.web.views.v1.service.request module
|
|||||||
:no-undoc-members:
|
:no-undoc-members:
|
||||||
:show-inheritance:
|
:show-inheritance:
|
||||||
|
|
||||||
|
ahriman.web.views.v1.service.rollback module
|
||||||
|
--------------------------------------------
|
||||||
|
|
||||||
|
.. automodule:: ahriman.web.views.v1.service.rollback
|
||||||
|
:members:
|
||||||
|
:no-undoc-members:
|
||||||
|
:show-inheritance:
|
||||||
|
|
||||||
ahriman.web.views.v1.service.search module
|
ahriman.web.views.v1.service.search module
|
||||||
------------------------------------------
|
------------------------------------------
|
||||||
|
|
||||||
|
|||||||
@@ -156,12 +156,15 @@ class ApplicationRepository(ApplicationProperties):
|
|||||||
result = Result()
|
result = Result()
|
||||||
|
|
||||||
# process already built packages if any
|
# process already built packages if any
|
||||||
built_packages = self.repository.packages_built()
|
if built_packages := self.repository.packages_built(): # speedup a bit
|
||||||
if built_packages: # speedup a bit
|
|
||||||
build_result = self.repository.process_update(built_packages, packagers)
|
build_result = self.repository.process_update(built_packages, packagers)
|
||||||
self.on_result(build_result)
|
self.on_result(build_result)
|
||||||
result.merge(build_result)
|
result.merge(build_result)
|
||||||
|
|
||||||
|
# filter packages which were prebuilt
|
||||||
|
succeeded = {package.base for package in build_result.success}
|
||||||
|
updates = [package for package in updates if package.base not in succeeded]
|
||||||
|
|
||||||
builder = Updater.load(self.repository_id, self.configuration, self.repository)
|
builder = Updater.load(self.repository_id, self.configuration, self.repository)
|
||||||
|
|
||||||
# ok so for now we split all packages into chunks and process each chunk accordingly
|
# ok so for now we split all packages into chunks and process each chunk accordingly
|
||||||
|
|||||||
@@ -21,10 +21,10 @@ import argparse
|
|||||||
|
|
||||||
from ahriman.application.application import Application
|
from ahriman.application.application import Application
|
||||||
from ahriman.application.handlers.handler import Handler, SubParserAction
|
from ahriman.application.handlers.handler import Handler, SubParserAction
|
||||||
|
from ahriman.application.handlers.update import Update
|
||||||
from ahriman.core.configuration import Configuration
|
from ahriman.core.configuration import Configuration
|
||||||
from ahriman.core.utils import enum_values, extract_user
|
from ahriman.core.utils import enum_values, extract_user
|
||||||
from ahriman.models.package_source import PackageSource
|
from ahriman.models.package_source import PackageSource
|
||||||
from ahriman.models.packagers import Packagers
|
|
||||||
from ahriman.models.pkgbuild_patch import PkgbuildPatch
|
from ahriman.models.pkgbuild_patch import PkgbuildPatch
|
||||||
from ahriman.models.repository_id import RepositoryId
|
from ahriman.models.repository_id import RepositoryId
|
||||||
|
|
||||||
@@ -48,26 +48,7 @@ class Add(Handler):
|
|||||||
"""
|
"""
|
||||||
application = Application(repository_id, configuration, report=report, refresh_pacman_database=args.refresh)
|
application = Application(repository_id, configuration, report=report, refresh_pacman_database=args.refresh)
|
||||||
application.on_start()
|
application.on_start()
|
||||||
|
Add.perform_action(application, args)
|
||||||
application.add(args.package, args.source, args.username)
|
|
||||||
patches = [PkgbuildPatch.from_env(patch) for patch in args.variable] if args.variable is not None else []
|
|
||||||
for package in args.package: # for each requested package insert patch
|
|
||||||
for patch in patches:
|
|
||||||
application.reporter.package_patches_update(package, patch)
|
|
||||||
|
|
||||||
if not args.now:
|
|
||||||
return
|
|
||||||
|
|
||||||
packages = application.updates(args.package, aur=False, local=False, manual=True, vcs=False, check_files=False)
|
|
||||||
if args.changes: # generate changes if requested
|
|
||||||
application.changes(packages)
|
|
||||||
|
|
||||||
packages = application.with_dependencies(packages, process_dependencies=args.dependencies)
|
|
||||||
packagers = Packagers(args.username, {package.base: package.packager for package in packages})
|
|
||||||
|
|
||||||
application.print_updates(packages, log_fn=application.logger.info)
|
|
||||||
result = application.update(packages, packagers, bump_pkgrel=args.increment)
|
|
||||||
Add.check_status(args.exit_code, not result.is_empty)
|
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def _set_package_add_parser(root: SubParserAction) -> argparse.ArgumentParser:
|
def _set_package_add_parser(root: SubParserAction) -> argparse.ArgumentParser:
|
||||||
@@ -103,14 +84,34 @@ class Add(Handler):
|
|||||||
parser.add_argument("--increment", help="increment package release (pkgrel) version on duplicate",
|
parser.add_argument("--increment", help="increment package release (pkgrel) version on duplicate",
|
||||||
action=argparse.BooleanOptionalAction, default=True)
|
action=argparse.BooleanOptionalAction, default=True)
|
||||||
parser.add_argument("-n", "--now", help="run update function after", action="store_true")
|
parser.add_argument("-n", "--now", help="run update function after", action="store_true")
|
||||||
parser.add_argument("-y", "--refresh", help="download fresh package databases from the mirror before actions, "
|
|
||||||
"-yy to force refresh even if up to date",
|
|
||||||
action="count", default=False)
|
|
||||||
parser.add_argument("-s", "--source", help="explicitly specify the package source for this command",
|
parser.add_argument("-s", "--source", help="explicitly specify the package source for this command",
|
||||||
type=PackageSource, choices=enum_values(PackageSource), default=PackageSource.Auto)
|
type=PackageSource, choices=enum_values(PackageSource), default=PackageSource.Auto)
|
||||||
parser.add_argument("-u", "--username", help="build as user", default=extract_user())
|
parser.add_argument("-u", "--username", help="build as user", default=extract_user())
|
||||||
parser.add_argument("-v", "--variable", help="apply specified makepkg variables to the next build",
|
parser.add_argument("-v", "--variable", help="apply specified makepkg variables to the next build",
|
||||||
action="append")
|
action="append")
|
||||||
|
parser.add_argument("-y", "--refresh", help="download fresh package databases from the mirror before actions, "
|
||||||
|
"-yy to force refresh even if up to date",
|
||||||
|
action="count", default=False)
|
||||||
|
parser.set_defaults(aur=False, check_files=False, dry_run=False, local=False, manual=True, vcs=False)
|
||||||
return parser
|
return parser
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def perform_action(application: Application, args: argparse.Namespace) -> None:
|
||||||
|
"""
|
||||||
|
perform add action
|
||||||
|
|
||||||
|
Args:
|
||||||
|
application(Application): application instance
|
||||||
|
args(argparse.Namespace): command line args
|
||||||
|
"""
|
||||||
|
application.add(args.package, args.source, args.username)
|
||||||
|
patches = [PkgbuildPatch.from_env(patch) for patch in args.variable] if args.variable is not None else []
|
||||||
|
for package in args.package: # for each requested package insert patch
|
||||||
|
for patch in patches:
|
||||||
|
application.reporter.package_patches_update(package, patch)
|
||||||
|
|
||||||
|
if not args.now:
|
||||||
|
return
|
||||||
|
Update.perform_action(application, args)
|
||||||
|
|
||||||
arguments = [_set_package_add_parser]
|
arguments = [_set_package_add_parser]
|
||||||
|
|||||||
131
src/ahriman/application/handlers/rollback.py
Normal file
131
src/ahriman/application/handlers/rollback.py
Normal file
@@ -0,0 +1,131 @@
|
|||||||
|
#
|
||||||
|
# Copyright (c) 2021-2026 ahriman team.
|
||||||
|
#
|
||||||
|
# This file is part of ahriman
|
||||||
|
# (see https://github.com/arcan1s/ahriman).
|
||||||
|
#
|
||||||
|
# This program is free software: you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU General Public License as published by
|
||||||
|
# the Free Software Foundation, either version 3 of the License, or
|
||||||
|
# (at your option) any later version.
|
||||||
|
#
|
||||||
|
# This program is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU General Public License
|
||||||
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
#
|
||||||
|
import argparse
|
||||||
|
|
||||||
|
from dataclasses import replace
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
from ahriman.application.application import Application
|
||||||
|
from ahriman.application.handlers.add import Add
|
||||||
|
from ahriman.application.handlers.handler import Handler, SubParserAction
|
||||||
|
from ahriman.core.configuration import Configuration
|
||||||
|
from ahriman.core.exceptions import UnknownPackageError
|
||||||
|
from ahriman.core.utils import extract_user
|
||||||
|
from ahriman.models.package import Package
|
||||||
|
from ahriman.models.package_source import PackageSource
|
||||||
|
from ahriman.models.repository_id import RepositoryId
|
||||||
|
|
||||||
|
|
||||||
|
class Rollback(Handler):
|
||||||
|
"""
|
||||||
|
package rollback handler
|
||||||
|
"""
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def run(cls, args: argparse.Namespace, repository_id: RepositoryId, configuration: Configuration, *,
|
||||||
|
report: bool) -> None:
|
||||||
|
"""
|
||||||
|
callback for command line
|
||||||
|
|
||||||
|
Args:
|
||||||
|
args(argparse.Namespace): command line args
|
||||||
|
repository_id(RepositoryId): repository unique identifier
|
||||||
|
configuration(Configuration): configuration instance
|
||||||
|
report(bool): force enable or disable reporting
|
||||||
|
"""
|
||||||
|
application = Application(repository_id, configuration, report=report)
|
||||||
|
application.on_start()
|
||||||
|
|
||||||
|
package = Rollback.package_load(application, args.package, args.version)
|
||||||
|
artifacts = Rollback.package_artifacts(application, package)
|
||||||
|
|
||||||
|
args.package = [str(artifact) for artifact in artifacts]
|
||||||
|
Add.perform_action(application, args)
|
||||||
|
|
||||||
|
if args.hold:
|
||||||
|
application.reporter.package_hold_update(package.base, enabled=True)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _set_package_rollback_parser(root: SubParserAction) -> argparse.ArgumentParser:
|
||||||
|
"""
|
||||||
|
add parser for package rollback subcommand
|
||||||
|
|
||||||
|
Args:
|
||||||
|
root(SubParserAction): subparsers for the commands
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
argparse.ArgumentParser: created argument parser
|
||||||
|
"""
|
||||||
|
parser = root.add_parser("package-rollback", help="rollback package",
|
||||||
|
description="rollback package to specified version from archives")
|
||||||
|
parser.add_argument("package", help="package base")
|
||||||
|
parser.add_argument("version", help="package version")
|
||||||
|
parser.add_argument("--hold", help="hold package afterwards",
|
||||||
|
action=argparse.BooleanOptionalAction, default=True)
|
||||||
|
parser.add_argument("-u", "--username", help="build as user", default=extract_user())
|
||||||
|
parser.set_defaults(aur=False, changes=False, check_files=False, dependencies=False, dry_run=False,
|
||||||
|
exit_code=True, increment=False, now=True, local=False, manual=False, refresh=False,
|
||||||
|
source=PackageSource.Archive, variable=None, vcs=False)
|
||||||
|
return parser
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def package_artifacts(application: Application, package: Package) -> list[Path]:
|
||||||
|
"""
|
||||||
|
look for requested package artifacts and return paths to them
|
||||||
|
|
||||||
|
Args:
|
||||||
|
application(Application): application instance
|
||||||
|
package(Package): package descriptor
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
list[Path]: paths to found artifacts
|
||||||
|
|
||||||
|
Raises:
|
||||||
|
UnknownPackageError: if artifacts do not exist
|
||||||
|
"""
|
||||||
|
# lookup for built artifacts
|
||||||
|
artifacts = application.repository.package_archives_lookup(package)
|
||||||
|
if not artifacts:
|
||||||
|
raise UnknownPackageError(package.base)
|
||||||
|
return artifacts
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def package_load(application: Application, package_base: str, version: str) -> Package:
|
||||||
|
"""
|
||||||
|
load package from repository, while setting requested version
|
||||||
|
|
||||||
|
Args:
|
||||||
|
application(Application): application instance
|
||||||
|
package_base(str): package base
|
||||||
|
version(str): package version
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Package: loaded package
|
||||||
|
|
||||||
|
Raises:
|
||||||
|
UnknownPackageError: if package does not exist
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
package, _ = next(iter(application.reporter.package_get(package_base)))
|
||||||
|
return replace(package, version=version)
|
||||||
|
except StopIteration:
|
||||||
|
raise UnknownPackageError(package_base) from None
|
||||||
|
|
||||||
|
arguments = [_set_package_rollback_parser]
|
||||||
@@ -48,22 +48,7 @@ class Update(Handler):
|
|||||||
"""
|
"""
|
||||||
application = Application(repository_id, configuration, report=report, refresh_pacman_database=args.refresh)
|
application = Application(repository_id, configuration, report=report, refresh_pacman_database=args.refresh)
|
||||||
application.on_start()
|
application.on_start()
|
||||||
|
Update.perform_action(application, args)
|
||||||
packages = application.updates(args.package, aur=args.aur, local=args.local, manual=args.manual, vcs=args.vcs,
|
|
||||||
check_files=args.check_files)
|
|
||||||
if args.changes: # generate changes if requested
|
|
||||||
application.changes(packages)
|
|
||||||
|
|
||||||
if args.dry_run: # exit from application if no build requested
|
|
||||||
Update.check_status(args.exit_code, packages) # status code check
|
|
||||||
return
|
|
||||||
|
|
||||||
packages = application.with_dependencies(packages, process_dependencies=args.dependencies)
|
|
||||||
packagers = Packagers(args.username, {package.base: package.packager for package in packages})
|
|
||||||
|
|
||||||
application.print_updates(packages, log_fn=application.logger.info)
|
|
||||||
result = application.update(packages, packagers, bump_pkgrel=args.increment)
|
|
||||||
Update.check_status(args.exit_code, not result.is_empty)
|
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def _set_repo_check_parser(root: SubParserAction) -> argparse.ArgumentParser:
|
def _set_repo_check_parser(root: SubParserAction) -> argparse.ArgumentParser:
|
||||||
@@ -153,6 +138,31 @@ class Update(Handler):
|
|||||||
return print(line) if dry_run else application.logger.info(line) # pylint: disable=bad-builtin
|
return print(line) if dry_run else application.logger.info(line) # pylint: disable=bad-builtin
|
||||||
return inner
|
return inner
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def perform_action(application: Application, args: argparse.Namespace) -> None:
|
||||||
|
"""
|
||||||
|
perform update action
|
||||||
|
|
||||||
|
Args:
|
||||||
|
application(Application): application instance
|
||||||
|
args(argparse.Namespace): command line args
|
||||||
|
"""
|
||||||
|
packages = application.updates(args.package, aur=args.aur, local=args.local, manual=args.manual, vcs=args.vcs,
|
||||||
|
check_files=args.check_files)
|
||||||
|
if args.changes: # generate changes if requested
|
||||||
|
application.changes(packages)
|
||||||
|
|
||||||
|
if args.dry_run: # exit from application if no build requested
|
||||||
|
Update.check_status(args.exit_code, packages) # status code check
|
||||||
|
return
|
||||||
|
|
||||||
|
packages = application.with_dependencies(packages, process_dependencies=args.dependencies)
|
||||||
|
packagers = Packagers(args.username, {package.base: package.packager for package in packages})
|
||||||
|
|
||||||
|
application.print_updates(packages, log_fn=application.logger.info)
|
||||||
|
result = application.update(packages, packagers, bump_pkgrel=args.increment)
|
||||||
|
Update.check_status(args.exit_code, not result.is_empty)
|
||||||
|
|
||||||
arguments = [
|
arguments = [
|
||||||
_set_repo_check_parser,
|
_set_repo_check_parser,
|
||||||
_set_repo_update_parser,
|
_set_repo_update_parser,
|
||||||
|
|||||||
@@ -25,7 +25,6 @@ from pathlib import Path
|
|||||||
from tempfile import TemporaryDirectory
|
from tempfile import TemporaryDirectory
|
||||||
|
|
||||||
from ahriman.core.alpm.pacman import Pacman
|
from ahriman.core.alpm.pacman import Pacman
|
||||||
from ahriman.core.build_tools.package_version import PackageVersion
|
|
||||||
from ahriman.core.build_tools.sources import Sources
|
from ahriman.core.build_tools.sources import Sources
|
||||||
from ahriman.core.configuration import Configuration
|
from ahriman.core.configuration import Configuration
|
||||||
from ahriman.core.log import LazyLogging
|
from ahriman.core.log import LazyLogging
|
||||||
@@ -89,19 +88,21 @@ class PackageInfo(LazyLogging):
|
|||||||
|
|
||||||
return sorted(result)
|
return sorted(result)
|
||||||
|
|
||||||
def load_archives(self, packages: Iterable[Path]) -> list[Package]:
|
def load_archives(self, packages: Iterable[Path], *, latest_only: bool = True) -> list[Package]:
|
||||||
"""
|
"""
|
||||||
load packages from list of archives
|
load packages from list of archives
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
packages(Iterable[Path]): paths to package archives
|
packages(Iterable[Path]): paths to package archives
|
||||||
|
latest_only(bool, optional): filter packages with the same base, keeping only fresh packages installed
|
||||||
|
(Default value = True)
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
list[Package]: list of read packages
|
list[Package]: list of read packages
|
||||||
"""
|
"""
|
||||||
sources = {package.base: package.remote for package, _, in self.reporter.package_get(None)}
|
sources = {package.base: package.remote for package, _, in self.reporter.package_get(None)}
|
||||||
|
|
||||||
result: dict[str, Package] = {}
|
result: dict[str, dict[str, Package]] = {}
|
||||||
# we are iterating over bases, not single packages
|
# we are iterating over bases, not single packages
|
||||||
for full_path in packages:
|
for full_path in packages:
|
||||||
try:
|
try:
|
||||||
@@ -109,17 +110,23 @@ class PackageInfo(LazyLogging):
|
|||||||
if (source := sources.get(local.base)) is not None: # update source with remote
|
if (source := sources.get(local.base)) is not None: # update source with remote
|
||||||
local.remote = source
|
local.remote = source
|
||||||
|
|
||||||
current = result.setdefault(local.base, local)
|
loaded_versions = result.setdefault(local.base, {})
|
||||||
if current.version != local.version:
|
current = loaded_versions.setdefault(local.version, local)
|
||||||
# force version to max of them
|
|
||||||
self.logger.warning("version of %s differs, found %s and %s",
|
|
||||||
current.base, current.version, local.version)
|
|
||||||
if PackageVersion(current).is_outdated(local, self.configuration, calculate_version=False):
|
|
||||||
current.version = local.version
|
|
||||||
current.packages.update(local.packages)
|
current.packages.update(local.packages)
|
||||||
except Exception:
|
except Exception:
|
||||||
self.logger.exception("could not load package from %s", full_path)
|
self.logger.exception("could not load package from %s", full_path)
|
||||||
return list(result.values())
|
|
||||||
|
if latest_only:
|
||||||
|
comparator: Callable[[Package, Package], int] = lambda left, right: left.vercmp(right.version)
|
||||||
|
for package_base, versions in result.items():
|
||||||
|
newest = max(versions.values(), key=cmp_to_key(comparator))
|
||||||
|
result[package_base] = {newest.version: newest}
|
||||||
|
|
||||||
|
return [
|
||||||
|
package
|
||||||
|
for versions in result.values()
|
||||||
|
for package in versions.values()
|
||||||
|
]
|
||||||
|
|
||||||
def package_archives(self, package_base: str) -> list[Package]:
|
def package_archives(self, package_base: str) -> list[Package]:
|
||||||
"""
|
"""
|
||||||
@@ -133,16 +140,17 @@ class PackageInfo(LazyLogging):
|
|||||||
Returns:
|
Returns:
|
||||||
list[Package]: list of packages belonging to this base, sorted by version by ascension
|
list[Package]: list of packages belonging to this base, sorted by version by ascension
|
||||||
"""
|
"""
|
||||||
packages: dict[tuple[str, str], Package] = {}
|
archive = self.paths.archive_for(package_base)
|
||||||
# we can't use here load_archives, because it ignores versions
|
if not archive.is_dir():
|
||||||
for full_path in filter(package_like, self.paths.archive_for(package_base).iterdir()):
|
return []
|
||||||
local = Package.from_archive(full_path)
|
|
||||||
if not local.supports_architecture(self.repository_id.architecture):
|
packages = self.load_archives(filter(package_like, archive.iterdir()), latest_only=False)
|
||||||
continue
|
|
||||||
packages.setdefault((local.base, local.version), local).packages.update(local.packages)
|
|
||||||
|
|
||||||
comparator: Callable[[Package, Package], int] = lambda left, right: left.vercmp(right.version)
|
comparator: Callable[[Package, Package], int] = lambda left, right: left.vercmp(right.version)
|
||||||
return sorted(packages.values(), key=cmp_to_key(comparator))
|
return sorted(
|
||||||
|
(package for package in packages if package.supports_architecture(self.repository_id.architecture)),
|
||||||
|
key=cmp_to_key(comparator),
|
||||||
|
)
|
||||||
|
|
||||||
def package_archives_lookup(self, package: Package) -> list[Path]:
|
def package_archives_lookup(self, package: Package) -> list[Path]:
|
||||||
"""
|
"""
|
||||||
@@ -155,19 +163,11 @@ class PackageInfo(LazyLogging):
|
|||||||
list[Path]: list of built packages and signatures if available, empty list otherwise
|
list[Path]: list of built packages and signatures if available, empty list otherwise
|
||||||
"""
|
"""
|
||||||
archive = self.paths.archive_for(package.base)
|
archive = self.paths.archive_for(package.base)
|
||||||
if not archive.is_dir():
|
|
||||||
return []
|
|
||||||
|
|
||||||
for path in filter(package_like, archive.iterdir()):
|
for built in self.package_archives(package.base):
|
||||||
# check if package version is the same
|
|
||||||
built = Package.from_archive(path)
|
|
||||||
if built.version != package.version:
|
if built.version != package.version:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
# all packages must be either any or same architecture
|
|
||||||
if not built.supports_architecture(self.repository_id.architecture):
|
|
||||||
continue
|
|
||||||
|
|
||||||
return list_flatmap(built.packages.values(), lambda single: archive.glob(f"{single.filename}*"))
|
return list_flatmap(built.packages.values(), lambda single: archive.glob(f"{single.filename}*"))
|
||||||
|
|
||||||
return []
|
return []
|
||||||
|
|||||||
@@ -232,6 +232,27 @@ class Spawn(Thread, LazyLogging):
|
|||||||
"""
|
"""
|
||||||
return self._spawn_process(repository_id, "package-remove", *packages)
|
return self._spawn_process(repository_id, "package-remove", *packages)
|
||||||
|
|
||||||
|
def packages_rollback(self, repository_id: RepositoryId, package: str, version: str, username: str | None, *,
|
||||||
|
hold: bool) -> str:
|
||||||
|
"""
|
||||||
|
rollback package
|
||||||
|
|
||||||
|
Args:
|
||||||
|
repository_id(RepositoryId): repository unique identifier
|
||||||
|
package(str): package base to rollback
|
||||||
|
version(str): package version to rollback
|
||||||
|
username(str | None): optional override of username for build process
|
||||||
|
hold(bool): hold package after rollback
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
str: spawned process identifier
|
||||||
|
"""
|
||||||
|
kwargs = {
|
||||||
|
"username": username,
|
||||||
|
self.boolean_action_argument("hold", hold): "",
|
||||||
|
}
|
||||||
|
return self._spawn_process(repository_id, "package-rollback", package, version, **kwargs)
|
||||||
|
|
||||||
def packages_update(self, repository_id: RepositoryId, username: str | None, *,
|
def packages_update(self, repository_id: RepositoryId, username: str | None, *,
|
||||||
aur: bool, local: bool, manual: bool, increment: bool, refresh: bool) -> str:
|
aur: bool, local: bool, manual: bool, increment: bool, refresh: bool) -> str:
|
||||||
"""
|
"""
|
||||||
|
|||||||
@@ -48,6 +48,7 @@ from ahriman.web.schemas.package_properties_schema import PackagePropertiesSchem
|
|||||||
from ahriman.web.schemas.package_schema import PackageSchema
|
from ahriman.web.schemas.package_schema import PackageSchema
|
||||||
from ahriman.web.schemas.package_status_schema import PackageStatusSchema, PackageStatusSimplifiedSchema
|
from ahriman.web.schemas.package_status_schema import PackageStatusSchema, PackageStatusSimplifiedSchema
|
||||||
from ahriman.web.schemas.package_version_schema import PackageVersionSchema
|
from ahriman.web.schemas.package_version_schema import PackageVersionSchema
|
||||||
|
from ahriman.web.schemas.packager_schema import PackagerSchema
|
||||||
from ahriman.web.schemas.pagination_schema import PaginationSchema
|
from ahriman.web.schemas.pagination_schema import PaginationSchema
|
||||||
from ahriman.web.schemas.patch_name_schema import PatchNameSchema
|
from ahriman.web.schemas.patch_name_schema import PatchNameSchema
|
||||||
from ahriman.web.schemas.patch_schema import PatchSchema
|
from ahriman.web.schemas.patch_schema import PatchSchema
|
||||||
@@ -58,6 +59,7 @@ from ahriman.web.schemas.process_schema import ProcessSchema
|
|||||||
from ahriman.web.schemas.remote_schema import RemoteSchema
|
from ahriman.web.schemas.remote_schema import RemoteSchema
|
||||||
from ahriman.web.schemas.repository_id_schema import RepositoryIdSchema
|
from ahriman.web.schemas.repository_id_schema import RepositoryIdSchema
|
||||||
from ahriman.web.schemas.repository_stats_schema import RepositoryStatsSchema
|
from ahriman.web.schemas.repository_stats_schema import RepositoryStatsSchema
|
||||||
|
from ahriman.web.schemas.rollback_schema import RollbackSchema
|
||||||
from ahriman.web.schemas.search_schema import SearchSchema
|
from ahriman.web.schemas.search_schema import SearchSchema
|
||||||
from ahriman.web.schemas.status_schema import StatusSchema
|
from ahriman.web.schemas.status_schema import StatusSchema
|
||||||
from ahriman.web.schemas.update_flags_schema import UpdateFlagsSchema
|
from ahriman.web.schemas.update_flags_schema import UpdateFlagsSchema
|
||||||
|
|||||||
@@ -17,10 +17,11 @@
|
|||||||
# You should have received a copy of the GNU General Public License
|
# You should have received a copy of the GNU General Public License
|
||||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
#
|
#
|
||||||
from ahriman.web.apispec import Schema, fields
|
from ahriman.web.apispec import fields
|
||||||
|
from ahriman.web.schemas.packager_schema import PackagerSchema
|
||||||
|
|
||||||
|
|
||||||
class BuildOptionsSchema(Schema):
|
class BuildOptionsSchema(PackagerSchema):
|
||||||
"""
|
"""
|
||||||
request build options schema
|
request build options schema
|
||||||
"""
|
"""
|
||||||
@@ -28,9 +29,6 @@ class BuildOptionsSchema(Schema):
|
|||||||
increment = fields.Boolean(dump_default=True, metadata={
|
increment = fields.Boolean(dump_default=True, metadata={
|
||||||
"description": "Increment pkgrel on conflicts",
|
"description": "Increment pkgrel on conflicts",
|
||||||
})
|
})
|
||||||
packager = fields.String(metadata={
|
|
||||||
"description": "Packager identity if applicable",
|
|
||||||
})
|
|
||||||
refresh = fields.Boolean(dump_default=True, metadata={
|
refresh = fields.Boolean(dump_default=True, metadata={
|
||||||
"description": "Refresh pacman database"
|
"description": "Refresh pacman database"
|
||||||
})
|
})
|
||||||
|
|||||||
30
src/ahriman/web/schemas/packager_schema.py
Normal file
30
src/ahriman/web/schemas/packager_schema.py
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
#
|
||||||
|
# Copyright (c) 2021-2026 ahriman team.
|
||||||
|
#
|
||||||
|
# This file is part of ahriman
|
||||||
|
# (see https://github.com/arcan1s/ahriman).
|
||||||
|
#
|
||||||
|
# This program is free software: you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU General Public License as published by
|
||||||
|
# the Free Software Foundation, either version 3 of the License, or
|
||||||
|
# (at your option) any later version.
|
||||||
|
#
|
||||||
|
# This program is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU General Public License
|
||||||
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
#
|
||||||
|
from ahriman.web.apispec import Schema, fields
|
||||||
|
|
||||||
|
|
||||||
|
class PackagerSchema(Schema):
|
||||||
|
"""
|
||||||
|
request packager schema
|
||||||
|
"""
|
||||||
|
|
||||||
|
packager = fields.String(metadata={
|
||||||
|
"description": "Packager identity if applicable",
|
||||||
|
})
|
||||||
40
src/ahriman/web/schemas/rollback_schema.py
Normal file
40
src/ahriman/web/schemas/rollback_schema.py
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
#
|
||||||
|
# Copyright (c) 2021-2026 ahriman team.
|
||||||
|
#
|
||||||
|
# This file is part of ahriman
|
||||||
|
# (see https://github.com/arcan1s/ahriman).
|
||||||
|
#
|
||||||
|
# This program is free software: you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU General Public License as published by
|
||||||
|
# the Free Software Foundation, either version 3 of the License, or
|
||||||
|
# (at your option) any later version.
|
||||||
|
#
|
||||||
|
# This program is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU General Public License
|
||||||
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
#
|
||||||
|
from ahriman import __version__
|
||||||
|
from ahriman.web.apispec import fields
|
||||||
|
from ahriman.web.schemas.packager_schema import PackagerSchema
|
||||||
|
|
||||||
|
|
||||||
|
class RollbackSchema(PackagerSchema):
|
||||||
|
"""
|
||||||
|
request schema for package rollback
|
||||||
|
"""
|
||||||
|
|
||||||
|
hold = fields.Boolean(dump_default=True, metadata={
|
||||||
|
"description": "Hold package after rollback",
|
||||||
|
})
|
||||||
|
package = fields.String(required=True, metadata={
|
||||||
|
"description": "Package name",
|
||||||
|
"example": "ahriman",
|
||||||
|
})
|
||||||
|
version = fields.String(required=True, metadata={
|
||||||
|
"description": "Package version",
|
||||||
|
"example": __version__,
|
||||||
|
})
|
||||||
@@ -227,7 +227,7 @@ class BaseView(View, CorsViewMixin):
|
|||||||
extract repository from request
|
extract repository from request
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
RepositoryIde: repository if possible to construct and first one otherwise
|
RepositoryId: repository if possible to construct and first one otherwise
|
||||||
"""
|
"""
|
||||||
architecture = self.request.query.get("architecture")
|
architecture = self.request.query.get("architecture")
|
||||||
name = self.request.query.get("repository")
|
name = self.request.query.get("repository")
|
||||||
|
|||||||
@@ -44,7 +44,6 @@ class AddView(BaseView):
|
|||||||
description="Add new package(s) from AUR",
|
description="Add new package(s) from AUR",
|
||||||
permission=POST_PERMISSION,
|
permission=POST_PERMISSION,
|
||||||
error_400_enabled=True,
|
error_400_enabled=True,
|
||||||
error_404_description="Repository is unknown",
|
|
||||||
schema=ProcessIdSchema,
|
schema=ProcessIdSchema,
|
||||||
query_schema=RepositoryIdSchema,
|
query_schema=RepositoryIdSchema,
|
||||||
body_schema=PackagePatchSchema,
|
body_schema=PackagePatchSchema,
|
||||||
|
|||||||
@@ -43,7 +43,6 @@ class RebuildView(BaseView):
|
|||||||
description="Rebuild packages which depend on specified one",
|
description="Rebuild packages which depend on specified one",
|
||||||
permission=POST_PERMISSION,
|
permission=POST_PERMISSION,
|
||||||
error_400_enabled=True,
|
error_400_enabled=True,
|
||||||
error_404_description="Repository is unknown",
|
|
||||||
schema=ProcessIdSchema,
|
schema=ProcessIdSchema,
|
||||||
query_schema=RepositoryIdSchema,
|
query_schema=RepositoryIdSchema,
|
||||||
body_schema=PackageNamesSchema,
|
body_schema=PackageNamesSchema,
|
||||||
|
|||||||
@@ -43,7 +43,6 @@ class RemoveView(BaseView):
|
|||||||
description="Remove specified packages from the repository",
|
description="Remove specified packages from the repository",
|
||||||
permission=POST_PERMISSION,
|
permission=POST_PERMISSION,
|
||||||
error_400_enabled=True,
|
error_400_enabled=True,
|
||||||
error_404_description="Repository is unknown",
|
|
||||||
schema=ProcessIdSchema,
|
schema=ProcessIdSchema,
|
||||||
query_schema=RepositoryIdSchema,
|
query_schema=RepositoryIdSchema,
|
||||||
body_schema=PackageNamesSchema,
|
body_schema=PackageNamesSchema,
|
||||||
|
|||||||
@@ -44,7 +44,6 @@ class RequestView(BaseView):
|
|||||||
description="Request new package(s) to be added from AUR",
|
description="Request new package(s) to be added from AUR",
|
||||||
permission=POST_PERMISSION,
|
permission=POST_PERMISSION,
|
||||||
error_400_enabled=True,
|
error_400_enabled=True,
|
||||||
error_404_description="Repository is unknown",
|
|
||||||
schema=ProcessIdSchema,
|
schema=ProcessIdSchema,
|
||||||
query_schema=RepositoryIdSchema,
|
query_schema=RepositoryIdSchema,
|
||||||
body_schema=PackagePatchSchema,
|
body_schema=PackagePatchSchema,
|
||||||
|
|||||||
77
src/ahriman/web/views/v1/service/rollback.py
Normal file
77
src/ahriman/web/views/v1/service/rollback.py
Normal file
@@ -0,0 +1,77 @@
|
|||||||
|
#
|
||||||
|
# Copyright (c) 2021-2026 ahriman team.
|
||||||
|
#
|
||||||
|
# This file is part of ahriman
|
||||||
|
# (see https://github.com/arcan1s/ahriman).
|
||||||
|
#
|
||||||
|
# This program is free software: you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU General Public License as published by
|
||||||
|
# the Free Software Foundation, either version 3 of the License, or
|
||||||
|
# (at your option) any later version.
|
||||||
|
#
|
||||||
|
# This program is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU General Public License
|
||||||
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
#
|
||||||
|
from aiohttp.web import HTTPBadRequest, Response
|
||||||
|
from typing import ClassVar
|
||||||
|
|
||||||
|
from ahriman.models.user_access import UserAccess
|
||||||
|
from ahriman.web.apispec.decorators import apidocs
|
||||||
|
from ahriman.web.schemas import ProcessIdSchema, RepositoryIdSchema, RollbackSchema
|
||||||
|
from ahriman.web.views.base import BaseView
|
||||||
|
|
||||||
|
|
||||||
|
class RollbackView(BaseView):
|
||||||
|
"""
|
||||||
|
package rollback web view
|
||||||
|
|
||||||
|
Attributes:
|
||||||
|
POST_PERMISSION(UserAccess): (class attribute) post permissions of self
|
||||||
|
"""
|
||||||
|
|
||||||
|
POST_PERMISSION: ClassVar[UserAccess] = UserAccess.Full
|
||||||
|
ROUTES = ["/api/v1/service/rollback"]
|
||||||
|
|
||||||
|
@apidocs(
|
||||||
|
tags=["Actions"],
|
||||||
|
summary="Rollback package",
|
||||||
|
description="Rollback package to specified version",
|
||||||
|
permission=POST_PERMISSION,
|
||||||
|
error_400_enabled=True,
|
||||||
|
schema=ProcessIdSchema,
|
||||||
|
query_schema=RepositoryIdSchema,
|
||||||
|
body_schema=RollbackSchema,
|
||||||
|
)
|
||||||
|
async def post(self) -> Response:
|
||||||
|
"""
|
||||||
|
run package rollback
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Response: 200 with spawned process id
|
||||||
|
|
||||||
|
Raises:
|
||||||
|
HTTPBadRequest: if bad data is supplied
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
data = await self.request.json()
|
||||||
|
package = self.get_non_empty(lambda key: data[key], "package")
|
||||||
|
version = self.get_non_empty(lambda key: data[key], "version")
|
||||||
|
except Exception as ex:
|
||||||
|
raise HTTPBadRequest(reason=str(ex))
|
||||||
|
|
||||||
|
repository_id = self.repository_id()
|
||||||
|
username = await self.username()
|
||||||
|
process_id = self.spawner.packages_rollback(
|
||||||
|
repository_id,
|
||||||
|
package,
|
||||||
|
version,
|
||||||
|
username,
|
||||||
|
hold=data.get("hold", True),
|
||||||
|
)
|
||||||
|
|
||||||
|
return self.json_response({"process_id": process_id})
|
||||||
@@ -43,14 +43,13 @@ class UpdateView(BaseView):
|
|||||||
description="Run repository update process",
|
description="Run repository update process",
|
||||||
permission=POST_PERMISSION,
|
permission=POST_PERMISSION,
|
||||||
error_400_enabled=True,
|
error_400_enabled=True,
|
||||||
error_404_description="Repository is unknown",
|
|
||||||
schema=ProcessIdSchema,
|
schema=ProcessIdSchema,
|
||||||
query_schema=RepositoryIdSchema,
|
query_schema=RepositoryIdSchema,
|
||||||
body_schema=UpdateFlagsSchema,
|
body_schema=UpdateFlagsSchema,
|
||||||
)
|
)
|
||||||
async def post(self) -> Response:
|
async def post(self) -> Response:
|
||||||
"""
|
"""
|
||||||
run repository update. No parameters supported here
|
run repository update
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
Response: 200 with spawned process id
|
Response: 200 with spawned process id
|
||||||
|
|||||||
@@ -118,7 +118,6 @@ class UploadView(BaseView):
|
|||||||
permission=POST_PERMISSION,
|
permission=POST_PERMISSION,
|
||||||
response_code=HTTPCreated,
|
response_code=HTTPCreated,
|
||||||
error_400_enabled=True,
|
error_400_enabled=True,
|
||||||
error_404_description="Repository is unknown",
|
|
||||||
query_schema=RepositoryIdSchema,
|
query_schema=RepositoryIdSchema,
|
||||||
body_schema=FileSchema,
|
body_schema=FileSchema,
|
||||||
body_location="form",
|
body_location="form",
|
||||||
|
|||||||
@@ -104,6 +104,7 @@ def _create_watcher(path: Path, repository_id: RepositoryId) -> Watcher:
|
|||||||
package_info = PackageInfo()
|
package_info = PackageInfo()
|
||||||
package_info.configuration = configuration
|
package_info.configuration = configuration
|
||||||
package_info.paths = configuration.repository_paths
|
package_info.paths = configuration.repository_paths
|
||||||
|
package_info.reporter = client
|
||||||
package_info.repository_id = repository_id
|
package_info.repository_id = repository_id
|
||||||
|
|
||||||
return Watcher(client, package_info)
|
return Watcher(client, package_info)
|
||||||
|
|||||||
@@ -190,13 +190,14 @@ def test_update(application_repository: ApplicationRepository, package_ahriman:
|
|||||||
"""
|
"""
|
||||||
paths = [package.filepath for package in package_ahriman.packages.values()]
|
paths = [package.filepath for package in package_ahriman.packages.values()]
|
||||||
tree = Tree([Leaf(package_ahriman)])
|
tree = Tree([Leaf(package_ahriman)])
|
||||||
|
prebuilt_result = Result()
|
||||||
|
|
||||||
resolve_mock = mocker.patch("ahriman.application.application.workers.local_updater.LocalUpdater.partition",
|
resolve_mock = mocker.patch("ahriman.application.application.workers.local_updater.LocalUpdater.partition",
|
||||||
return_value=tree.levels())
|
return_value=tree.levels())
|
||||||
mocker.patch("ahriman.core.repository.repository.Repository.packages_built", return_value=paths)
|
mocker.patch("ahriman.core.repository.repository.Repository.packages_built", return_value=paths)
|
||||||
build_mock = mocker.patch("ahriman.application.application.workers.local_updater.LocalUpdater.update",
|
build_mock = mocker.patch("ahriman.application.application.workers.local_updater.LocalUpdater.update",
|
||||||
return_value=result)
|
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(
|
on_result_mock = mocker.patch(
|
||||||
"ahriman.application.application.application_repository.ApplicationRepository.on_result")
|
"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])
|
resolve_mock.assert_called_once_with([package_ahriman])
|
||||||
build_mock.assert_called_once_with([package_ahriman], Packagers("username"), bump_pkgrel=True)
|
build_mock.assert_called_once_with([package_ahriman], Packagers("username"), bump_pkgrel=True)
|
||||||
update_mock.assert_called_once_with(paths, Packagers("username"))
|
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,
|
def test_update_empty(application_repository: ApplicationRepository, package_ahriman: Package, result: Result,
|
||||||
|
|||||||
@@ -3,14 +3,12 @@ import pytest
|
|||||||
|
|
||||||
from pytest_mock import MockerFixture
|
from pytest_mock import MockerFixture
|
||||||
|
|
||||||
|
from ahriman.application.application import Application
|
||||||
from ahriman.application.handlers.add import Add
|
from ahriman.application.handlers.add import Add
|
||||||
from ahriman.core.configuration import Configuration
|
from ahriman.core.configuration import Configuration
|
||||||
from ahriman.core.repository import Repository
|
from ahriman.core.repository import Repository
|
||||||
from ahriman.models.package import Package
|
|
||||||
from ahriman.models.package_source import PackageSource
|
from ahriman.models.package_source import PackageSource
|
||||||
from ahriman.models.packagers import Packagers
|
|
||||||
from ahriman.models.pkgbuild_patch import PkgbuildPatch
|
from ahriman.models.pkgbuild_patch import PkgbuildPatch
|
||||||
from ahriman.models.result import Result
|
|
||||||
|
|
||||||
|
|
||||||
def _default_args(args: argparse.Namespace) -> argparse.Namespace:
|
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
|
argparse.Namespace: generated arguments for these test cases
|
||||||
"""
|
"""
|
||||||
args.package = ["ahriman"]
|
args.package = ["ahriman"]
|
||||||
args.changes = True
|
|
||||||
args.exit_code = False
|
|
||||||
args.increment = True
|
|
||||||
args.now = False
|
args.now = False
|
||||||
args.refresh = 0
|
args.refresh = 0
|
||||||
args.source = PackageSource.Auto
|
args.source = PackageSource.Auto
|
||||||
args.dependencies = True
|
|
||||||
args.username = "username"
|
args.username = "username"
|
||||||
args.variable = None
|
args.variable = None
|
||||||
return args
|
return args
|
||||||
@@ -43,103 +37,49 @@ def test_run(args: argparse.Namespace, configuration: Configuration, repository:
|
|||||||
"""
|
"""
|
||||||
args = _default_args(args)
|
args = _default_args(args)
|
||||||
mocker.patch("ahriman.core.repository.Repository.load", return_value=repository)
|
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")
|
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()
|
_, repository_id = configuration.check_loaded()
|
||||||
Add.run(args, repository_id, configuration, report=False)
|
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()
|
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,
|
def test_perform_action(args: argparse.Namespace, application: Application, mocker: MockerFixture) -> None:
|
||||||
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 = _default_args(args)
|
||||||
args.variable = ["KEY=VALUE"]
|
args.variable = ["KEY=VALUE"]
|
||||||
mocker.patch("ahriman.core.repository.Repository.load", return_value=repository)
|
|
||||||
mocker.patch("ahriman.application.application.Application.add")
|
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.perform_action(application, args)
|
||||||
Add.run(args, repository_id, configuration, report=False)
|
patches_mock.assert_called_once_with(args.package[0], PkgbuildPatch("KEY", "VALUE"))
|
||||||
application_mock.assert_called_once_with(args.package[0], PkgbuildPatch("KEY", "VALUE"))
|
|
||||||
|
|
||||||
|
|
||||||
def test_run_with_updates(args: argparse.Namespace, configuration: Configuration, repository: Repository,
|
def test_perform_action_with_updates(args: argparse.Namespace, application: Application, mocker: MockerFixture) -> None:
|
||||||
package_ahriman: Package, mocker: MockerFixture) -> None:
|
|
||||||
"""
|
"""
|
||||||
must run command with updates after
|
must perform add action with updates after
|
||||||
"""
|
"""
|
||||||
args = _default_args(args)
|
args = _default_args(args)
|
||||||
args.now = True
|
args.now = True
|
||||||
result = Result()
|
|
||||||
result.add_updated(package_ahriman)
|
|
||||||
mocker.patch("ahriman.application.application.Application.add")
|
mocker.patch("ahriman.application.application.Application.add")
|
||||||
mocker.patch("ahriman.core.repository.Repository.load", return_value=repository)
|
update_mock = mocker.patch("ahriman.application.handlers.update.Update.perform_action")
|
||||||
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")
|
|
||||||
|
|
||||||
_, repository_id = configuration.check_loaded()
|
Add.perform_action(application, args)
|
||||||
Add.run(args, repository_id, configuration, report=False)
|
update_mock.assert_called_once_with(application, args)
|
||||||
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)
|
|
||||||
|
|||||||
113
tests/ahriman/application/handlers/test_handler_rollback.py
Normal file
113
tests/ahriman/application/handlers/test_handler_rollback.py
Normal 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)
|
||||||
@@ -39,26 +39,39 @@ def _default_args(args: argparse.Namespace) -> argparse.Namespace:
|
|||||||
return args
|
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:
|
mocker: MockerFixture) -> None:
|
||||||
"""
|
"""
|
||||||
must run command
|
must run command
|
||||||
"""
|
"""
|
||||||
args = _default_args(args)
|
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 = Result()
|
||||||
result.add_updated(package_ahriman)
|
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)
|
application_mock = mocker.patch("ahriman.application.application.Application.update", return_value=result)
|
||||||
check_mock = mocker.patch("ahriman.application.handlers.handler.Handler.check_status")
|
check_mock = mocker.patch("ahriman.application.handlers.handler.Handler.check_status")
|
||||||
dependencies_mock = mocker.patch("ahriman.application.application.Application.with_dependencies",
|
dependencies_mock = mocker.patch("ahriman.application.application.Application.with_dependencies",
|
||||||
return_value=[package_ahriman])
|
return_value=[package_ahriman])
|
||||||
updates_mock = mocker.patch("ahriman.application.application.Application.updates", 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")
|
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")
|
print_mock = mocker.patch("ahriman.application.application.Application.print_updates")
|
||||||
|
|
||||||
_, repository_id = configuration.check_loaded()
|
Update.perform_action(application, args)
|
||||||
Update.run(args, repository_id, configuration, report=False)
|
|
||||||
application_mock.assert_called_once_with([package_ahriman],
|
application_mock.assert_called_once_with([package_ahriman],
|
||||||
Packagers(args.username, {package_ahriman.base: "packager"}),
|
Packagers(args.username, {package_ahriman.base: "packager"}),
|
||||||
bump_pkgrel=args.increment)
|
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])
|
changes_mock.assert_called_once_with([package_ahriman])
|
||||||
dependencies_mock.assert_called_once_with([package_ahriman], process_dependencies=args.dependencies)
|
dependencies_mock.assert_called_once_with([package_ahriman], process_dependencies=args.dependencies)
|
||||||
check_mock.assert_called_once_with(False, True)
|
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))
|
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,
|
def test_perform_action_empty_exception(args: argparse.Namespace, application: Application,
|
||||||
mocker: MockerFixture) -> None:
|
mocker: MockerFixture) -> None:
|
||||||
"""
|
"""
|
||||||
must raise ExitCode exception on empty update list
|
must raise ExitCode exception on empty update list
|
||||||
"""
|
"""
|
||||||
args = _default_args(args)
|
args = _default_args(args)
|
||||||
args.exit_code = True
|
args.exit_code = True
|
||||||
args.dry_run = True
|
args.dry_run = True
|
||||||
mocker.patch("ahriman.core.repository.Repository.load", return_value=repository)
|
|
||||||
mocker.patch("ahriman.application.application.Application.updates", return_value=[])
|
mocker.patch("ahriman.application.application.Application.updates", return_value=[])
|
||||||
check_mock = mocker.patch("ahriman.application.handlers.handler.Handler.check_status")
|
check_mock = mocker.patch("ahriman.application.handlers.handler.Handler.check_status")
|
||||||
|
|
||||||
_, repository_id = configuration.check_loaded()
|
Update.perform_action(application, args)
|
||||||
Update.run(args, repository_id, configuration, report=False)
|
|
||||||
check_mock.assert_called_once_with(True, [])
|
check_mock.assert_called_once_with(True, [])
|
||||||
|
|
||||||
|
|
||||||
def test_run_update_empty_exception(args: argparse.Namespace, package_ahriman: Package, configuration: Configuration,
|
def test_perform_action_update_empty_exception(args: argparse.Namespace, application: Application,
|
||||||
repository: Repository, mocker: MockerFixture) -> None:
|
package_ahriman: Package, mocker: MockerFixture) -> None:
|
||||||
"""
|
"""
|
||||||
must raise ExitCode exception on empty build result
|
must raise ExitCode exception on empty build result
|
||||||
"""
|
"""
|
||||||
args = _default_args(args)
|
args = _default_args(args)
|
||||||
args.exit_code = True
|
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.update", return_value=Result())
|
||||||
mocker.patch("ahriman.application.application.Application.updates", return_value=[package_ahriman])
|
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.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")
|
mocker.patch("ahriman.application.application.Application.changes")
|
||||||
check_mock = mocker.patch("ahriman.application.handlers.handler.Handler.check_status")
|
check_mock = mocker.patch("ahriman.application.handlers.handler.Handler.check_status")
|
||||||
|
|
||||||
_, repository_id = configuration.check_loaded()
|
Update.perform_action(application, args)
|
||||||
Update.run(args, repository_id, configuration, report=False)
|
|
||||||
check_mock.assert_called_once_with(True, False)
|
check_mock.assert_called_once_with(True, False)
|
||||||
|
|
||||||
|
|
||||||
def test_run_dry_run(args: argparse.Namespace, package_ahriman: Package, configuration: Configuration,
|
def test_perform_action_dry_run(args: argparse.Namespace, application: Application, package_ahriman: Package,
|
||||||
repository: Repository, mocker: MockerFixture) -> None:
|
mocker: MockerFixture) -> None:
|
||||||
"""
|
"""
|
||||||
must run simplified command
|
must run simplified command
|
||||||
"""
|
"""
|
||||||
args = _default_args(args)
|
args = _default_args(args)
|
||||||
args.dry_run = True
|
args.dry_run = True
|
||||||
mocker.patch("ahriman.core.repository.Repository.load", return_value=repository)
|
|
||||||
application_mock = mocker.patch("ahriman.application.application.Application.update")
|
application_mock = mocker.patch("ahriman.application.application.Application.update")
|
||||||
check_mock = mocker.patch("ahriman.application.handlers.handler.Handler.check_status")
|
check_mock = mocker.patch("ahriman.application.handlers.handler.Handler.check_status")
|
||||||
updates_mock = mocker.patch("ahriman.application.application.Application.updates", 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")
|
changes_mock = mocker.patch("ahriman.application.application.Application.changes")
|
||||||
|
|
||||||
_, repository_id = configuration.check_loaded()
|
Update.perform_action(application, args)
|
||||||
Update.run(args, repository_id, configuration, report=False)
|
|
||||||
updates_mock.assert_called_once_with(
|
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)
|
args.package, aur=args.aur, local=args.local, manual=args.manual, vcs=args.vcs, check_files=args.check_files)
|
||||||
application_mock.assert_not_called()
|
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])
|
check_mock.assert_called_once_with(False, [package_ahriman])
|
||||||
|
|
||||||
|
|
||||||
def test_run_no_changes(args: argparse.Namespace, configuration: Configuration, repository: Repository,
|
def test_perform_action_no_changes(args: argparse.Namespace, application: Application, mocker: MockerFixture) -> None:
|
||||||
mocker: MockerFixture) -> None:
|
|
||||||
"""
|
"""
|
||||||
must skip changes calculation
|
must skip changes calculation
|
||||||
"""
|
"""
|
||||||
args = _default_args(args)
|
args = _default_args(args)
|
||||||
args.dry_run = True
|
args.dry_run = True
|
||||||
args.changes = False
|
args.changes = False
|
||||||
mocker.patch("ahriman.core.repository.Repository.load", return_value=repository)
|
|
||||||
mocker.patch("ahriman.application.application.Application.update")
|
mocker.patch("ahriman.application.application.Application.update")
|
||||||
mocker.patch("ahriman.application.handlers.handler.Handler.check_status")
|
mocker.patch("ahriman.application.handlers.handler.Handler.check_status")
|
||||||
mocker.patch("ahriman.application.application.Application.updates")
|
mocker.patch("ahriman.application.application.Application.updates")
|
||||||
changes_mock = mocker.patch("ahriman.application.application.Application.changes")
|
changes_mock = mocker.patch("ahriman.application.application.Application.changes")
|
||||||
|
|
||||||
_, repository_id = configuration.check_loaded()
|
Update.perform_action(application, args)
|
||||||
Update.run(args, repository_id, configuration, report=False)
|
|
||||||
changes_mock.assert_not_called()
|
changes_mock.assert_not_called()
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -271,6 +271,18 @@ def test_subparsers_package_add_option_variable_multiple(parser: argparse.Argume
|
|||||||
assert args.variable == ["var1", "var2"]
|
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:
|
def test_subparsers_package_archives(parser: argparse.ArgumentParser) -> None:
|
||||||
"""
|
"""
|
||||||
package-archives command must imply action, exit code, info, lock, quiet, report and unsafe
|
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)
|
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:
|
def test_subparsers_package_pkgbuild(parser: argparse.ArgumentParser) -> None:
|
||||||
"""
|
"""
|
||||||
package-pkgbuild command must imply action, exit code, lock, quiet, report and unsafe
|
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)
|
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:
|
def test_subparsers_package_remove_option_architecture(parser: argparse.ArgumentParser) -> None:
|
||||||
"""
|
"""
|
||||||
package-remove command must correctly parse architecture list
|
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"
|
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:
|
def test_subparsers_package_status(parser: argparse.ArgumentParser) -> None:
|
||||||
"""
|
"""
|
||||||
package-status command must imply lock, quiet, report and unsafe
|
package-status command must imply lock, quiet, report and unsafe
|
||||||
|
|||||||
@@ -8,7 +8,6 @@ from unittest.mock import MagicMock
|
|||||||
from ahriman.core.repository import Repository
|
from ahriman.core.repository import Repository
|
||||||
from ahriman.models.changes import Changes
|
from ahriman.models.changes import Changes
|
||||||
from ahriman.models.package import Package
|
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,
|
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
|
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:
|
def test_package_archives(repository: Repository, package_ahriman: Package, mocker: MockerFixture) -> None:
|
||||||
"""
|
"""
|
||||||
must load package archives sorted by version
|
must load package archives sorted by version
|
||||||
"""
|
"""
|
||||||
mocker.patch("ahriman.core.repository.package_info.package_like", return_value=True)
|
mocker.patch("pathlib.Path.is_dir", return_value=True)
|
||||||
mocker.patch("pathlib.Path.iterdir", return_value=[str(i) for i in range(5)])
|
mocker.patch("pathlib.Path.iterdir")
|
||||||
mocker.patch("ahriman.models.package.Package.from_archive",
|
load_mock = mocker.patch("ahriman.core.repository.package_info.PackageInfo.load_archives",
|
||||||
side_effect=lambda version: replace(package_ahriman, version=version))
|
return_value=[replace(package_ahriman, version=str(i)) for i in range(5)])
|
||||||
|
|
||||||
result = repository.package_archives(package_ahriman.base)
|
result = repository.package_archives(package_ahriman.base)
|
||||||
assert len(result) == 5
|
assert len(result) == 5
|
||||||
assert [p.version for p in result] == [str(i) for i in range(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,
|
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"
|
package_ahriman.packages[package_ahriman.base].architecture = "i686"
|
||||||
|
|
||||||
mocker.patch("pathlib.Path.iterdir", return_value=[package_ahriman.packages[package_ahriman.base].filepath])
|
mocker.patch("pathlib.Path.is_dir", return_value=True)
|
||||||
mocker.patch("ahriman.models.package.Package.from_archive", return_value=package_ahriman)
|
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)
|
result = repository.package_archives(package_ahriman.base)
|
||||||
assert len(result) == 0
|
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
|
must existing packages which match the version
|
||||||
"""
|
"""
|
||||||
mocker.patch("pathlib.Path.is_dir", return_value=True)
|
archives_mock = mocker.patch("ahriman.core.repository.package_info.PackageInfo.package_archives", return_value=[
|
||||||
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=[
|
|
||||||
package_ahriman,
|
package_ahriman,
|
||||||
package_python_schedule,
|
package_python_schedule,
|
||||||
replace(package_ahriman, version="1"),
|
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")])
|
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")]
|
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}*")
|
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
|
must return nothing if no packages found with the same version
|
||||||
"""
|
"""
|
||||||
mocker.patch("pathlib.Path.is_dir", return_value=True)
|
mocker.patch("ahriman.core.repository.package_info.PackageInfo.package_archives",
|
||||||
mocker.patch("pathlib.Path.iterdir", return_value=[
|
return_value=[replace(package_ahriman, version="1")])
|
||||||
Path("1.pkg.tar.zst"),
|
|
||||||
])
|
|
||||||
mocker.patch("ahriman.models.package.Package.from_archive", return_value=replace(package_ahriman, version="1"))
|
|
||||||
|
|
||||||
assert repository.package_archives_lookup(package_ahriman) == []
|
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
|
must return nothing if architecture doesn't match
|
||||||
"""
|
"""
|
||||||
package_ahriman.packages[package_ahriman.base].architecture = "x86_64"
|
mocker.patch("ahriman.core.repository.package_info.PackageInfo.package_archives", return_value=[])
|
||||||
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)
|
|
||||||
|
|
||||||
assert repository.package_archives_lookup(package_ahriman) == []
|
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
|
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) == []
|
assert repository.package_archives_lookup(package_ahriman) == []
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -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")
|
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:
|
def test_packages_update(spawner: Spawn, repository_id: RepositoryId, mocker: MockerFixture) -> None:
|
||||||
"""
|
"""
|
||||||
must call repo update
|
must call repo update
|
||||||
|
|||||||
1
tests/ahriman/web/schemas/test_packager_schema.py
Normal file
1
tests/ahriman/web/schemas/test_packager_schema.py
Normal file
@@ -0,0 +1 @@
|
|||||||
|
# schema testing goes in view class tests
|
||||||
1
tests/ahriman/web/schemas/test_rollback_schema.py
Normal file
1
tests/ahriman/web/schemas/test_rollback_schema.py
Normal file
@@ -0,0 +1 @@
|
|||||||
|
# schema testing goes in view class tests
|
||||||
@@ -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()
|
||||||
Reference in New Issue
Block a user