feat: changes screen implementation (#117)

Add support of changes generation. Changes will be generated (unless explicitly asked not to) automatically during check process (i.e. `repo-update --dry-run` and aliases) and uploaded to the remote server. Changes can be reviewed either by web interface or by special subcommands.

Changes will be automatically cleared during next successful build
This commit is contained in:
2023-11-30 14:56:41 +02:00
committed by GitHub
parent a689448854
commit 2760b36977
81 changed files with 2107 additions and 382 deletions

View File

@ -93,7 +93,7 @@ def test_with_dependencies(application: Application, package_ahriman: Package, p
side_effect=lambda *args: packages[args[0].name])
packages_mock = mocker.patch("ahriman.application.application.Application._known_packages",
return_value={"devtools", "python-build", "python-pytest"})
update_remote_mock = mocker.patch("ahriman.core.database.SQLite.remote_update")
update_remote_mock = mocker.patch("ahriman.core.database.SQLite.package_base_update")
status_client_mock = mocker.patch("ahriman.core.status.client.Client.set_unknown")
result = application.with_dependencies([package_ahriman], process_dependencies=True)

View File

@ -41,7 +41,7 @@ def test_add_aur(application_packages: ApplicationPackages, package_ahriman: Pac
"""
mocker.patch("ahriman.models.package.Package.from_aur", return_value=package_ahriman)
build_queue_mock = mocker.patch("ahriman.core.database.SQLite.build_queue_insert")
update_remote_mock = mocker.patch("ahriman.core.database.SQLite.remote_update")
update_remote_mock = mocker.patch("ahriman.core.database.SQLite.package_base_update")
application_packages._add_aur(package_ahriman.base, "packager")
build_queue_mock.assert_called_once_with(package_ahriman)
@ -153,7 +153,7 @@ def test_add_repository(application_packages: ApplicationPackages, package_ahrim
"""
mocker.patch("ahriman.models.package.Package.from_official", return_value=package_ahriman)
build_queue_mock = mocker.patch("ahriman.core.database.SQLite.build_queue_insert")
update_remote_mock = mocker.patch("ahriman.core.database.SQLite.remote_update")
update_remote_mock = mocker.patch("ahriman.core.database.SQLite.package_base_update")
application_packages._add_repository(package_ahriman.base, "packager")
build_queue_mock.assert_called_once_with(package_ahriman)

View File

@ -5,16 +5,49 @@ from unittest.mock import call as MockCall
from ahriman.application.application.application_repository import ApplicationRepository
from ahriman.core.tree import Leaf, Tree
from ahriman.models.changes import Changes
from ahriman.models.package import Package
from ahriman.models.packagers import Packagers
from ahriman.models.result import Result
def test_changes(application_repository: ApplicationRepository, package_ahriman: Package,
mocker: MockerFixture) -> None:
"""
must generate changes for the packages
"""
changes = Changes("hash", "change")
hashes_mock = mocker.patch("ahriman.core.database.SQLite.hashes_get", return_value={
package_ahriman.base: changes.last_commit_sha,
})
changes_mock = mocker.patch("ahriman.core.repository.Repository.package_changes", return_value=changes)
report_mock = mocker.patch("ahriman.core.status.client.Client.package_changes_set")
application_repository.changes([package_ahriman])
hashes_mock.assert_called_once_with()
changes_mock.assert_called_once_with(package_ahriman, changes.last_commit_sha)
report_mock.assert_called_once_with(package_ahriman.base, changes)
def test_changes_skip(application_repository: ApplicationRepository, package_ahriman: Package,
mocker: MockerFixture) -> None:
"""
must skip change generation if no last commit sha has been found
"""
mocker.patch("ahriman.core.database.SQLite.hashes_get", return_value={})
changes_mock = mocker.patch("ahriman.core.repository.Repository.package_changes")
report_mock = mocker.patch("ahriman.core.status.client.Client.package_changes_set")
application_repository.changes([package_ahriman])
changes_mock.assert_not_called()
report_mock.assert_not_called()
def test_clean_cache(application_repository: ApplicationRepository, mocker: MockerFixture) -> None:
"""
must clean cache directory
"""
clear_mock = mocker.patch("ahriman.core.repository.cleaner.Cleaner.clear_cache")
clear_mock = mocker.patch("ahriman.core.repository.Repository.clear_cache")
application_repository.clean(cache=True, chroot=False, manual=False, packages=False, pacman=False)
clear_mock.assert_called_once_with()
@ -23,7 +56,7 @@ def test_clean_chroot(application_repository: ApplicationRepository, mocker: Moc
"""
must clean chroot directory
"""
clear_mock = mocker.patch("ahriman.core.repository.cleaner.Cleaner.clear_chroot")
clear_mock = mocker.patch("ahriman.core.repository.Repository.clear_chroot")
application_repository.clean(cache=False, chroot=True, manual=False, packages=False, pacman=False)
clear_mock.assert_called_once_with()
@ -32,7 +65,7 @@ def test_clean_manual(application_repository: ApplicationRepository, mocker: Moc
"""
must clean manual directory
"""
clear_mock = mocker.patch("ahriman.core.repository.cleaner.Cleaner.clear_queue")
clear_mock = mocker.patch("ahriman.core.repository.Repository.clear_queue")
application_repository.clean(cache=False, chroot=False, manual=True, packages=False, pacman=False)
clear_mock.assert_called_once_with()
@ -41,7 +74,7 @@ def test_clean_packages(application_repository: ApplicationRepository, mocker: M
"""
must clean packages directory
"""
clear_mock = mocker.patch("ahriman.core.repository.cleaner.Cleaner.clear_packages")
clear_mock = mocker.patch("ahriman.core.repository.Repository.clear_packages")
application_repository.clean(cache=False, chroot=False, manual=False, packages=True, pacman=False)
clear_mock.assert_called_once_with()
@ -50,7 +83,7 @@ def test_clean_pacman(application_repository: ApplicationRepository, mocker: Moc
"""
must clean packages directory
"""
clear_mock = mocker.patch("ahriman.core.repository.cleaner.Cleaner.clear_pacman")
clear_mock = mocker.patch("ahriman.core.repository.Repository.clear_pacman")
application_repository.clean(cache=False, chroot=False, manual=False, packages=False, pacman=True)
clear_mock.assert_called_once_with()

View File

@ -0,0 +1,98 @@
import argparse
import pytest
from pytest_mock import MockerFixture
from ahriman.application.handlers import Change
from ahriman.core.configuration import Configuration
from ahriman.core.database import SQLite
from ahriman.core.repository import Repository
from ahriman.models.action import Action
from ahriman.models.changes import Changes
def _default_args(args: argparse.Namespace) -> argparse.Namespace:
"""
default arguments for these test cases
Args:
args(argparse.Namespace): command line arguments fixture
Returns:
argparse.Namespace: generated arguments for these test cases
"""
args.action = Action.List
args.exit_code = False
args.package = "package"
return args
def test_run(args: argparse.Namespace, configuration: Configuration, repository: Repository,
mocker: MockerFixture) -> None:
"""
must run command
"""
args = _default_args(args)
mocker.patch("ahriman.core.repository.Repository.load", return_value=repository)
application_mock = mocker.patch("ahriman.core.status.client.Client.package_changes_get",
return_value=Changes("sha", "change"))
check_mock = mocker.patch("ahriman.application.handlers.Handler.check_if_empty")
print_mock = mocker.patch("ahriman.core.formatters.Printer.print")
_, repository_id = configuration.check_loaded()
Change.run(args, repository_id, configuration, report=False)
application_mock.assert_called_once_with(args.package)
check_mock.assert_called_once_with(False, False)
print_mock.assert_called_once_with(verbose=True, log_fn=pytest.helpers.anyvar(int), separator="")
def test_run_empty_exception(args: argparse.Namespace, configuration: Configuration, repository: Repository,
mocker: MockerFixture) -> None:
"""
must raise ExitCode exception on empty changes result
"""
args = _default_args(args)
args.exit_code = True
mocker.patch("ahriman.core.repository.Repository.load", return_value=repository)
mocker.patch("ahriman.core.status.client.Client.package_changes_get", return_value=Changes())
check_mock = mocker.patch("ahriman.application.handlers.Handler.check_if_empty")
_, repository_id = configuration.check_loaded()
Change.run(args, repository_id, configuration, report=False)
check_mock.assert_called_once_with(True, True)
def test_run_remove(args: argparse.Namespace, configuration: Configuration, repository: Repository,
mocker: MockerFixture) -> None:
"""
must remove package changes
"""
args = _default_args(args)
args.action = Action.Remove
mocker.patch("ahriman.core.repository.Repository.load", return_value=repository)
update_mock = mocker.patch("ahriman.core.status.client.Client.package_changes_set")
_, repository_id = configuration.check_loaded()
Change.run(args, repository_id, configuration, report=False)
update_mock.assert_called_once_with(args.package, Changes())
def test_imply_with_report(args: argparse.Namespace, configuration: Configuration, database: SQLite,
mocker: MockerFixture) -> None:
"""
must create application object with native reporting
"""
args = _default_args(args)
mocker.patch("ahriman.core.database.SQLite.load", return_value=database)
load_mock = mocker.patch("ahriman.core.repository.Repository.load")
_, repository_id = configuration.check_loaded()
Change.run(args, repository_id, configuration, report=False)
load_mock.assert_called_once_with(repository_id, configuration, database, report=True, refresh_pacman_database=0)
def test_disallow_multi_architecture_run() -> None:
"""
must not allow multi architecture run
"""
assert not Change.ALLOW_MULTI_ARCHITECTURE_RUN

View File

@ -23,17 +23,18 @@ def _default_args(args: argparse.Namespace) -> argparse.Namespace:
Returns:
argparse.Namespace: generated arguments for these test cases
"""
args.aur = True
args.changes = True
args.package = []
args.dependencies = True
args.dry_run = False
args.exit_code = False
args.increment = True
args.aur = True
args.local = True
args.manual = True
args.vcs = True
args.refresh = 0
args.username = "username"
args.vcs = True
return args
@ -51,6 +52,7 @@ def test_run(args: argparse.Namespace, package_ahriman: Package, configuration:
dependencies_mock = mocker.patch("ahriman.application.application.Application.with_dependencies",
return_value=[package_ahriman])
updates_mock = mocker.patch("ahriman.application.application.Application.updates", return_value=[package_ahriman])
changes_mock = mocker.patch("ahriman.application.application.Application.changes")
on_start_mock = mocker.patch("ahriman.application.application.Application.on_start")
print_mock = mocker.patch("ahriman.application.application.Application.print_updates")
@ -60,8 +62,9 @@ def test_run(args: argparse.Namespace, package_ahriman: Package, configuration:
Packagers(args.username, {package_ahriman.base: "packager"}),
bump_pkgrel=args.increment)
updates_mock.assert_called_once_with(args.package, aur=args.aur, local=args.local, manual=args.manual, vcs=args.vcs)
changes_mock.assert_not_called()
dependencies_mock.assert_called_once_with([package_ahriman], process_dependencies=args.dependencies)
check_mock.assert_has_calls([MockCall(False, False), MockCall(False, False)])
check_mock.assert_called_once_with(False, False)
on_start_mock.assert_called_once_with()
print_mock.assert_called_once_with([package_ahriman], log_fn=pytest.helpers.anyvar(int))
@ -95,15 +98,17 @@ def test_run_update_empty_exception(args: argparse.Namespace, package_ahriman: P
mocker.patch("ahriman.application.application.Application.updates", return_value=[package_ahriman])
mocker.patch("ahriman.application.application.Application.with_dependencies", return_value=[package_ahriman])
mocker.patch("ahriman.application.application.Application.print_updates")
changes_mock = mocker.patch("ahriman.application.application.Application.changes")
check_mock = mocker.patch("ahriman.application.handlers.Handler.check_if_empty")
_, repository_id = configuration.check_loaded()
Update.run(args, repository_id, configuration, report=False)
check_mock.assert_has_calls([MockCall(True, False), MockCall(True, True)])
changes_mock.assert_not_called()
check_mock.assert_called_once_with(True, True)
def test_run_dry_run(args: argparse.Namespace, configuration: Configuration, repository: Repository,
mocker: MockerFixture) -> None:
def test_run_dry_run(args: argparse.Namespace, package_ahriman: Package, configuration: Configuration,
repository: Repository, mocker: MockerFixture) -> None:
"""
must run simplified command
"""
@ -112,15 +117,36 @@ def test_run_dry_run(args: argparse.Namespace, configuration: Configuration, rep
mocker.patch("ahriman.core.repository.Repository.load", return_value=repository)
application_mock = mocker.patch("ahriman.application.application.Application.update")
check_mock = mocker.patch("ahriman.application.handlers.Handler.check_if_empty")
updates_mock = mocker.patch("ahriman.application.application.Application.updates")
updates_mock = mocker.patch("ahriman.application.application.Application.updates", return_value=[package_ahriman])
changes_mock = mocker.patch("ahriman.application.application.Application.changes")
_, repository_id = configuration.check_loaded()
Update.run(args, repository_id, configuration, report=False)
updates_mock.assert_called_once_with(args.package, aur=args.aur, local=args.local, manual=args.manual, vcs=args.vcs)
application_mock.assert_not_called()
changes_mock.assert_called_once_with([package_ahriman])
check_mock.assert_called_once_with(False, pytest.helpers.anyvar(int))
def test_run_no_changes(args: argparse.Namespace, configuration: Configuration, repository: Repository,
mocker: MockerFixture) -> None:
"""
must skip changes calculation
"""
args = _default_args(args)
args.dry_run = True
args.changes = False
mocker.patch("ahriman.core.repository.Repository.load", return_value=repository)
mocker.patch("ahriman.application.application.Application.update")
mocker.patch("ahriman.application.handlers.Handler.check_if_empty")
mocker.patch("ahriman.application.application.Application.updates")
changes_mock = mocker.patch("ahriman.application.application.Application.changes")
_, repository_id = configuration.check_loaded()
Update.run(args, repository_id, configuration, report=False)
changes_mock.assert_not_called()
def test_log_fn(application: Application, mocker: MockerFixture) -> None:
"""
must print package updates

View File

@ -270,6 +270,34 @@ def test_subparsers_package_add_option_variable_multiple(parser: argparse.Argume
assert args.variable == ["var1", "var2"]
def test_subparsers_package_changes(parser: argparse.ArgumentParser) -> None:
"""
package-changes command must imply action, lock, quiet, report and unsafe
"""
args = parser.parse_args(["-a", "x86_64", "-r", "repo", "package-changes", "ahriman"])
assert args.action == Action.List
assert args.architecture == "x86_64"
assert args.lock is None
assert args.quiet
assert not args.report
assert args.repository == "repo"
assert args.unsafe
def test_subparsers_package_changes_remove(parser: argparse.ArgumentParser) -> None:
"""
package-changes-remove command must imply action, lock, quiet, report and unsafe
"""
args = parser.parse_args(["-a", "x86_64", "-r", "repo", "package-changes-remove", "ahriman"])
assert args.action == Action.Remove
assert args.architecture == "x86_64"
assert args.lock is None
assert args.quiet
assert not args.report
assert args.repository == "repo"
assert args.unsafe
def test_subparsers_package_remove_option_architecture(parser: argparse.ArgumentParser) -> None:
"""
package-remove command must correctly parse architecture list
@ -633,10 +661,9 @@ def test_subparsers_repo_create_mirrorlist_option_repository(parser: argparse.Ar
def test_subparsers_repo_daemon(parser: argparse.ArgumentParser) -> None:
"""
repo-daemon command must imply dry run, exit code and package
repo-daemon command must imply exit code and package
"""
args = parser.parse_args(["repo-daemon"])
assert not args.dry_run
assert not args.exit_code
assert args.package == []

View File

@ -5,6 +5,7 @@ from pytest_mock import MockerFixture
from unittest.mock import call as MockCall
from ahriman.core.build_tools.sources import Sources
from ahriman.core.exceptions import CalledProcessError
from ahriman.models.package import Package
from ahriman.models.package_source import PackageSource
from ahriman.models.pkgbuild_patch import PkgbuildPatch
@ -12,6 +13,32 @@ from ahriman.models.remote_source import RemoteSource
from ahriman.models.repository_paths import RepositoryPaths
def test_changes(mocker: MockerFixture) -> None:
"""
must calculate changes from the last known commit
"""
fetch_mock = mocker.patch("ahriman.core.build_tools.sources.Sources.fetch_until")
diff_mock = mocker.patch("ahriman.core.build_tools.sources.Sources.diff", return_value="diff")
local = Path("local")
last_commit_sha = "sha"
assert Sources.changes(local, last_commit_sha) == "diff"
fetch_mock.assert_called_once_with(local, commit_sha=last_commit_sha)
diff_mock.assert_called_once_with(local, last_commit_sha)
def test_changes_skip(package_ahriman: Package, mocker: MockerFixture) -> None:
"""
must return none in case if commit sha is not available
"""
fetch_mock = mocker.patch("ahriman.core.build_tools.sources.Sources.fetch_until")
diff_mock = mocker.patch("ahriman.core.build_tools.sources.Sources.diff")
assert Sources.changes(Path("local"), None) is None
fetch_mock.assert_not_called()
diff_mock.assert_not_called()
def test_extend_architectures(mocker: MockerFixture) -> None:
"""
must update available architecture list
@ -38,9 +65,12 @@ def test_fetch_empty(remote_source: RemoteSource, mocker: MockerFixture) -> None
"""
mocker.patch("pathlib.Path.is_dir", return_value=True)
mocker.patch("ahriman.core.build_tools.sources.Sources.has_remotes", return_value=False)
head_mock = mocker.patch("ahriman.core.build_tools.sources.Sources.head", return_value="sha")
check_output_mock = mocker.patch("ahriman.core.build_tools.sources.check_output")
Sources.fetch(Path("local"), remote_source)
local = Path("local")
assert Sources.fetch(local, remote_source) == "sha"
head_mock.assert_called_once_with(local)
check_output_mock.assert_not_called()
@ -51,18 +81,20 @@ def test_fetch_existing(remote_source: RemoteSource, mocker: MockerFixture) -> N
mocker.patch("pathlib.Path.is_dir", return_value=True)
mocker.patch("ahriman.core.build_tools.sources.Sources.has_remotes", return_value=True)
check_output_mock = mocker.patch("ahriman.core.build_tools.sources.check_output")
fetch_mock = mocker.patch("ahriman.core.build_tools.sources.Sources.fetch_until")
move_mock = mocker.patch("ahriman.core.build_tools.sources.Sources.move")
head_mock = mocker.patch("ahriman.core.build_tools.sources.Sources.head", return_value="sha")
local = Path("local")
Sources.fetch(local, remote_source)
assert Sources.fetch(local, remote_source) == "sha"
fetch_mock.assert_called_once_with(local, branch=remote_source.branch)
check_output_mock.assert_has_calls([
MockCall("git", "fetch", "--quiet", "--depth", "1", "origin",
remote_source.branch, cwd=local, logger=pytest.helpers.anyvar(int)),
MockCall("git", "checkout", "--force", remote_source.branch, cwd=local, logger=pytest.helpers.anyvar(int)),
MockCall("git", "reset", "--quiet", "--hard", f"origin/{remote_source.branch}",
cwd=local, logger=pytest.helpers.anyvar(int)),
])
move_mock.assert_called_once_with(local.resolve(), local)
head_mock.assert_called_once_with(local)
def test_fetch_new(remote_source: RemoteSource, mocker: MockerFixture) -> None:
@ -72,9 +104,10 @@ def test_fetch_new(remote_source: RemoteSource, mocker: MockerFixture) -> None:
mocker.patch("pathlib.Path.is_dir", return_value=False)
check_output_mock = mocker.patch("ahriman.core.build_tools.sources.check_output")
move_mock = mocker.patch("ahriman.core.build_tools.sources.Sources.move")
head_mock = mocker.patch("ahriman.core.build_tools.sources.Sources.head", return_value="sha")
local = Path("local")
Sources.fetch(local, remote_source)
assert Sources.fetch(local, remote_source) == "sha"
check_output_mock.assert_has_calls([
MockCall("git", "clone", "--quiet", "--depth", "1", "--branch", remote_source.branch, "--single-branch",
remote_source.git_url, str(local), cwd=local.parent, logger=pytest.helpers.anyvar(int)),
@ -83,6 +116,7 @@ def test_fetch_new(remote_source: RemoteSource, mocker: MockerFixture) -> None:
cwd=local, logger=pytest.helpers.anyvar(int))
])
move_mock.assert_called_once_with(local.resolve(), local)
head_mock.assert_called_once_with(local)
def test_fetch_new_without_remote(mocker: MockerFixture) -> None:
@ -92,15 +126,17 @@ def test_fetch_new_without_remote(mocker: MockerFixture) -> None:
mocker.patch("pathlib.Path.is_dir", return_value=False)
check_output_mock = mocker.patch("ahriman.core.build_tools.sources.check_output")
move_mock = mocker.patch("ahriman.core.build_tools.sources.Sources.move")
head_mock = mocker.patch("ahriman.core.build_tools.sources.Sources.head", return_value="sha")
local = Path("local")
Sources.fetch(local, RemoteSource(source=PackageSource.Archive))
assert Sources.fetch(local, RemoteSource(source=PackageSource.Archive)) == "sha"
check_output_mock.assert_has_calls([
MockCall("git", "checkout", "--force", Sources.DEFAULT_BRANCH, cwd=local, logger=pytest.helpers.anyvar(int)),
MockCall("git", "reset", "--quiet", "--hard", f"origin/{Sources.DEFAULT_BRANCH}",
cwd=local, logger=pytest.helpers.anyvar(int))
])
move_mock.assert_called_once_with(local.resolve(), local)
head_mock.assert_called_once_with(local)
def test_fetch_relative(remote_source: RemoteSource, mocker: MockerFixture) -> None:
@ -109,9 +145,12 @@ def test_fetch_relative(remote_source: RemoteSource, mocker: MockerFixture) -> N
"""
mocker.patch("ahriman.core.build_tools.sources.check_output")
move_mock = mocker.patch("ahriman.core.build_tools.sources.Sources.move")
head_mock = mocker.patch("ahriman.core.build_tools.sources.Sources.head", return_value="sha")
Sources.fetch(Path("path"), remote_source)
move_mock.assert_called_once_with(Path("path").resolve(), Path("path"))
local = Path("local")
assert Sources.fetch(local, remote_source) == "sha"
move_mock.assert_called_once_with(local.resolve(), local)
head_mock.assert_called_once_with(local)
def test_has_remotes(mocker: MockerFixture) -> None:
@ -171,11 +210,11 @@ def test_load(package_ahriman: Package, repository_paths: RepositoryPaths, mocke
"""
patch = PkgbuildPatch(None, "patch")
path = Path("local")
fetch_mock = mocker.patch("ahriman.core.build_tools.sources.Sources.fetch")
fetch_mock = mocker.patch("ahriman.core.build_tools.sources.Sources.fetch", return_value="sha")
patch_mock = mocker.patch("ahriman.core.build_tools.sources.Sources.patch_apply")
architectures_mock = mocker.patch("ahriman.core.build_tools.sources.Sources.extend_architectures", return_value=[])
Sources.load(path, package_ahriman, [patch], repository_paths)
assert Sources.load(path, package_ahriman, [patch], repository_paths) == "sha"
fetch_mock.assert_called_once_with(path, package_ahriman.remote)
patch_mock.assert_called_once_with(path, patch)
architectures_mock.assert_called_once_with(path, repository_paths.repository_id.architecture)
@ -186,11 +225,11 @@ def test_load_no_patch(package_ahriman: Package, repository_paths: RepositoryPat
must load packages sources correctly without patches
"""
mocker.patch("pathlib.Path.is_dir", return_value=False)
mocker.patch("ahriman.core.build_tools.sources.Sources.fetch")
mocker.patch("ahriman.core.build_tools.sources.Sources.fetch", return_value="sha")
mocker.patch("ahriman.core.build_tools.sources.Sources.extend_architectures", return_value=[])
patch_mock = mocker.patch("ahriman.core.build_tools.sources.Sources.patch_apply")
Sources.load(Path("local"), package_ahriman, [], repository_paths)
assert Sources.load(Path("local"), package_ahriman, [], repository_paths) == "sha"
patch_mock.assert_not_called()
@ -200,10 +239,10 @@ def test_load_with_cache(package_ahriman: Package, repository_paths: RepositoryP
"""
mocker.patch("pathlib.Path.is_dir", return_value=True)
copytree_mock = mocker.patch("shutil.copytree")
mocker.patch("ahriman.core.build_tools.sources.Sources.fetch")
mocker.patch("ahriman.core.build_tools.sources.Sources.fetch", return_value="sha")
mocker.patch("ahriman.core.build_tools.sources.Sources.extend_architectures", return_value=[])
Sources.load(Path("local"), package_ahriman, [], repository_paths)
assert Sources.load(Path("local"), package_ahriman, [], repository_paths) == "sha"
copytree_mock.assert_called_once() # we do not check full command here, sorry
@ -269,7 +308,7 @@ def test_add(sources: Sources, mocker: MockerFixture) -> None:
sources.add(local, "pattern1", "pattern2")
glob_mock.assert_has_calls([MockCall("pattern1"), MockCall("pattern2")])
check_output_mock.assert_called_once_with(
"git", "add", "1", "2", "1", "2", cwd=local, logger=pytest.helpers.anyvar(int)
"git", "add", "1", "2", "1", "2", cwd=local, logger=sources.logger
)
@ -284,7 +323,7 @@ def test_add_intent_to_add(sources: Sources, mocker: MockerFixture) -> None:
sources.add(local, "pattern1", "pattern2", intent_to_add=True)
glob_mock.assert_has_calls([MockCall("pattern1"), MockCall("pattern2")])
check_output_mock.assert_called_once_with(
"git", "add", "--intent-to-add", "1", "2", "1", "2", cwd=local, logger=pytest.helpers.anyvar(int)
"git", "add", "--intent-to-add", "1", "2", "1", "2", cwd=local, logger=sources.logger
)
@ -312,7 +351,7 @@ def test_commit(sources: Sources, mocker: MockerFixture) -> None:
assert sources.commit(local, message=message)
check_output_mock.assert_called_once_with(
"git", "commit", "--quiet", "--message", message,
cwd=local, logger=pytest.helpers.anyvar(int), environment={
cwd=local, logger=sources.logger, environment={
"GIT_AUTHOR_NAME": user,
"GIT_AUTHOR_EMAIL": email,
"GIT_COMMITTER_NAME": user,
@ -345,7 +384,7 @@ def test_commit_author(sources: Sources, mocker: MockerFixture) -> None:
assert sources.commit(Path("local"), message=message, commit_author=author)
check_output_mock.assert_called_once_with(
"git", "commit", "--quiet", "--message", message,
cwd=local, logger=pytest.helpers.anyvar(int), environment={
cwd=local, logger=sources.logger, environment={
"GIT_AUTHOR_NAME": user,
"GIT_AUTHOR_EMAIL": email,
"GIT_COMMITTER_NAME": user,
@ -366,7 +405,7 @@ def test_commit_autogenerated_message(sources: Sources, mocker: MockerFixture) -
user, email = sources.DEFAULT_COMMIT_AUTHOR
check_output_mock.assert_called_once_with(
"git", "commit", "--quiet", "--message", pytest.helpers.anyvar(str, strict=True),
cwd=local, logger=pytest.helpers.anyvar(int), environment={
cwd=local, logger=sources.logger, environment={
"GIT_AUTHOR_NAME": user,
"GIT_AUTHOR_EMAIL": email,
"GIT_COMMITTER_NAME": user,
@ -383,7 +422,67 @@ def test_diff(sources: Sources, mocker: MockerFixture) -> None:
local = Path("local")
assert sources.diff(local)
check_output_mock.assert_called_once_with("git", "diff", cwd=local, logger=pytest.helpers.anyvar(int))
check_output_mock.assert_called_once_with("git", "diff", cwd=local, logger=sources.logger)
def test_diff_specific(sources: Sources, mocker: MockerFixture) -> None:
"""
must calculate diff from specific ref
"""
check_output_mock = mocker.patch("ahriman.core.build_tools.sources.check_output")
local = Path("local")
assert sources.diff(local, "hash")
check_output_mock.assert_called_once_with("git", "diff", "hash", cwd=local, logger=sources.logger)
def test_fetch_until(sources: Sources, mocker: MockerFixture) -> None:
"""
must fetch until the specified commit
"""
check_output_mock = mocker.patch("ahriman.core.build_tools.sources.check_output", side_effect=[
"",
CalledProcessError(1, ["command"], "error"),
"",
"",
])
local = Path("local")
sources.fetch_until(local, branch="master", commit_sha="sha")
check_output_mock.assert_has_calls([
MockCall("git", "fetch", "--quiet", "--depth", "1", "origin", "master", cwd=local, logger=sources.logger),
MockCall("git", "cat-file", "-e", "sha", cwd=local, logger=sources.logger),
MockCall("git", "fetch", "--quiet", "--depth", "2", "origin", "master", cwd=local, logger=sources.logger),
MockCall("git", "cat-file", "-e", "sha", cwd=local, logger=sources.logger),
])
def test_fetch_until_first(sources: Sources, mocker: MockerFixture) -> None:
"""
must fetch first commit only
"""
check_output_mock = mocker.patch("ahriman.core.build_tools.sources.check_output")
local = Path("local")
sources.fetch_until(local, branch="master")
check_output_mock.assert_has_calls([
MockCall("git", "fetch", "--quiet", "--depth", "1", "origin", "master", cwd=local, logger=sources.logger),
MockCall("git", "cat-file", "-e", "HEAD", cwd=local, logger=sources.logger),
])
def test_fetch_until_all_branches(sources: Sources, mocker: MockerFixture) -> None:
"""
must fetch all branches
"""
check_output_mock = mocker.patch("ahriman.core.build_tools.sources.check_output")
local = Path("local")
sources.fetch_until(local)
check_output_mock.assert_has_calls([
MockCall("git", "fetch", "--quiet", "--depth", "1", cwd=local, logger=sources.logger),
MockCall("git", "cat-file", "-e", "HEAD", cwd=local, logger=sources.logger),
])
def test_has_changes(sources: Sources, mocker: MockerFixture) -> None:
@ -395,12 +494,34 @@ def test_has_changes(sources: Sources, mocker: MockerFixture) -> None:
check_output_mock = mocker.patch("ahriman.core.build_tools.sources.check_output", return_value="M a.txt")
assert sources.has_changes(local)
check_output_mock.assert_called_once_with("git", "diff", "--cached", "--name-only",
cwd=local, logger=pytest.helpers.anyvar(int))
cwd=local, logger=sources.logger)
check_output_mock = mocker.patch("ahriman.core.build_tools.sources.check_output", return_value="")
assert not sources.has_changes(local)
check_output_mock.assert_called_once_with("git", "diff", "--cached", "--name-only",
cwd=local, logger=pytest.helpers.anyvar(int))
cwd=local, logger=sources.logger)
def test_head(sources: Sources, mocker: MockerFixture) -> None:
"""
must correctly define HEAD hash
"""
check_output_mock = mocker.patch("ahriman.core.build_tools.sources.check_output", return_value="sha")
local = Path("local")
assert sources.head(local) == "sha"
check_output_mock.assert_called_once_with("git", "rev-parse", "HEAD", cwd=local)
def test_head_specific(sources: Sources, mocker: MockerFixture) -> None:
"""
must correctly define ref hash
"""
check_output_mock = mocker.patch("ahriman.core.build_tools.sources.check_output", return_value="sha")
local = Path("local")
assert sources.head(local, "master") == "sha"
check_output_mock.assert_called_once_with("git", "rev-parse", "master", cwd=local)
def test_move(sources: Sources, mocker: MockerFixture) -> None:
@ -434,7 +555,7 @@ def test_patch_apply(sources: Sources, mocker: MockerFixture) -> None:
sources.patch_apply(local, patch)
check_output_mock.assert_called_once_with(
"git", "apply", "--ignore-space-change", "--ignore-whitespace",
cwd=local, input_data=patch.value, logger=pytest.helpers.anyvar(int)
cwd=local, input_data=patch.value, logger=sources.logger
)

View File

@ -19,9 +19,9 @@ def test_init(task_ahriman: Task, database: SQLite, mocker: MockerFixture) -> No
must copy tree instead of fetch
"""
mocker.patch("ahriman.models.package.Package.from_build", return_value=task_ahriman.package)
load_mock = mocker.patch("ahriman.core.build_tools.sources.Sources.load")
load_mock = mocker.patch("ahriman.core.build_tools.sources.Sources.load", return_value="sha")
task_ahriman.init(Path("ahriman"), database, None)
assert task_ahriman.init(Path("ahriman"), database, None) == "sha"
load_mock.assert_called_once_with(Path("ahriman"), task_ahriman.package, [], task_ahriman.paths)
@ -30,11 +30,11 @@ def test_init_bump_pkgrel(task_ahriman: Task, database: SQLite, mocker: MockerFi
must bump pkgrel if it is same as provided
"""
mocker.patch("ahriman.models.package.Package.from_build", return_value=task_ahriman.package)
mocker.patch("ahriman.core.build_tools.sources.Sources.load")
mocker.patch("ahriman.core.build_tools.sources.Sources.load", return_value="sha")
write_mock = mocker.patch("ahriman.models.pkgbuild_patch.PkgbuildPatch.write")
local = Path("ahriman")
task_ahriman.init(local, database, task_ahriman.package.version)
assert task_ahriman.init(local, database, task_ahriman.package.version) == "sha"
write_mock.assert_called_once_with(local / "PKGBUILD")
@ -43,8 +43,8 @@ def test_init_bump_pkgrel_skip(task_ahriman: Task, database: SQLite, mocker: Moc
must keep pkgrel if version is different from provided
"""
mocker.patch("ahriman.models.package.Package.from_build", return_value=task_ahriman.package)
mocker.patch("ahriman.core.build_tools.sources.Sources.load")
mocker.patch("ahriman.core.build_tools.sources.Sources.load", return_value="sha")
write_mock = mocker.patch("ahriman.models.pkgbuild_patch.PkgbuildPatch.write")
task_ahriman.init(Path("ahriman"), database, f"2:{task_ahriman.package.version}")
assert task_ahriman.init(Path("ahriman"), database, f"2:{task_ahriman.package.version}") == "sha"
write_mock.assert_not_called()

View File

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

View File

@ -0,0 +1,65 @@
from ahriman.core.database import SQLite
from ahriman.models.changes import Changes
from ahriman.models.package import Package
from ahriman.models.repository_id import RepositoryId
def test_changes_insert_get(database: SQLite, package_ahriman: Package) -> None:
"""
must insert and get changes
"""
database.changes_insert(package_ahriman.base, Changes("sha1", "change1"))
assert database.changes_get(package_ahriman.base).changes == "change1"
database.changes_insert(package_ahriman.base, Changes("sha2", "change2"),
RepositoryId("i686", database._repository_id.name))
assert database.changes_get(package_ahriman.base).changes == "change1"
assert database.changes_get(
package_ahriman.base, RepositoryId("i686", database._repository_id.name)).changes == "change2"
def test_changes_insert_remove(database: SQLite, package_ahriman: Package, package_python_schedule: Package) -> None:
"""
must remove changes for the package
"""
database.changes_insert(package_ahriman.base, Changes("sha1", "change1"))
database.changes_insert(package_python_schedule.base, Changes("sha3", "change3"))
database.changes_insert(package_ahriman.base, Changes("sha2", "change2"),
RepositoryId("i686", database._repository_id.name))
database.changes_remove(package_ahriman.base)
assert database.changes_get(package_ahriman.base).changes is None
assert database.changes_get(package_python_schedule.base).changes == "change3"
# insert null
database.changes_insert(package_ahriman.base, Changes(), RepositoryId("i686", database._repository_id.name))
assert database.changes_get(
package_ahriman.base, RepositoryId("i686", database._repository_id.name)).changes is None
assert database.changes_get(package_python_schedule.base).changes == "change3"
def test_changes_insert_remove_full(database: SQLite, package_ahriman: Package,
package_python_schedule: Package) -> None:
"""
must remove all changes for the repository
"""
database.changes_insert(package_ahriman.base, Changes("sha1", "change1"))
database.changes_insert(package_python_schedule.base, Changes("sha3", "change3"))
database.changes_insert(package_ahriman.base, Changes("sha2", "change2"),
RepositoryId("i686", database._repository_id.name))
database.changes_remove(None)
assert database.changes_get(package_ahriman.base).changes is None
assert database.changes_get(package_python_schedule.base).changes is None
assert database.changes_get(
package_ahriman.base, RepositoryId("i686", database._repository_id.name)).changes == "change2"
def test_hashes_get(database: SQLite, package_ahriman: Package, package_python_schedule: Package) -> None:
"""
must return non-empty hashes for packages
"""
database.changes_insert(package_ahriman.base, Changes("sha1", "change1"))
database.changes_insert(package_python_schedule.base, Changes())
assert database.hashes_get() == {package_ahriman.base: "sha1"}

View File

@ -199,7 +199,7 @@ def test_remote_update_get(database: SQLite, package_ahriman: Package) -> None:
"""
must insert and retrieve package remote
"""
database.remote_update(package_ahriman)
database.package_base_update(package_ahriman)
assert database.remotes_get()[package_ahriman.base] == package_ahriman.remote
@ -207,9 +207,9 @@ def test_remote_update_update(database: SQLite, package_ahriman: Package) -> Non
"""
must perform package remote update for existing package
"""
database.remote_update(package_ahriman)
database.package_base_update(package_ahriman)
remote_source = RemoteSource(source=PackageSource.Repository)
package_ahriman.remote = remote_source
database.remote_update(package_ahriman)
database.package_base_update(package_ahriman)
assert database.remotes_get()[package_ahriman.base] == remote_source

View File

@ -2,11 +2,12 @@ import pytest
from pathlib import Path
from ahriman.core.formatters import AurPrinter, ConfigurationPrinter, ConfigurationPathsPrinter, PackagePrinter, \
PatchPrinter, RepositoryPrinter, StatusPrinter, StringPrinter, TreePrinter, UpdatePrinter, UserPrinter, \
ValidationPrinter, VersionPrinter
from ahriman.core.formatters import AurPrinter, ChangesPrinter, ConfigurationPrinter, ConfigurationPathsPrinter, \
PackagePrinter, PatchPrinter, RepositoryPrinter, StatusPrinter, StringPrinter, TreePrinter, UpdatePrinter, \
UserPrinter, ValidationPrinter, VersionPrinter
from ahriman.models.aur_package import AURPackage
from ahriman.models.build_status import BuildStatus
from ahriman.models.changes import Changes
from ahriman.models.package import Package
from ahriman.models.pkgbuild_patch import PkgbuildPatch
from ahriman.models.repository_id import RepositoryId
@ -27,6 +28,17 @@ def aur_package_ahriman_printer(aur_package_ahriman: AURPackage) -> AurPrinter:
return AurPrinter(aur_package_ahriman)
@pytest.fixture
def changes_printer() -> ChangesPrinter:
"""
fixture for changes printer
Returns:
ChangesPrinter: changes printer test instance
"""
return ChangesPrinter(Changes("sha", "changes"))
@pytest.fixture
def configuration_paths_printer() -> ConfigurationPathsPrinter:
"""

View File

@ -0,0 +1,32 @@
from ahriman.core.formatters import ChangesPrinter
from ahriman.models.changes import Changes
def test_properties(changes_printer: ChangesPrinter) -> None:
"""
must return non-empty properties list
"""
assert changes_printer.properties()
def test_properties_empty() -> None:
"""
must return empty properties list if change is empty
"""
assert not ChangesPrinter(Changes()).properties()
assert not ChangesPrinter(Changes("sha")).properties()
def test_title(changes_printer: ChangesPrinter) -> None:
"""
must return non-empty title
"""
assert changes_printer.title()
def test_title_empty() -> None:
"""
must return empty title if change is empty
"""
assert not ChangesPrinter(Changes()).title()
assert not ChangesPrinter(Changes("sha")).title()

View File

@ -6,6 +6,7 @@ from ahriman.core.configuration import Configuration
from ahriman.core.database import SQLite
from ahriman.core.repository.cleaner import Cleaner
from ahriman.core.repository.executor import Executor
from ahriman.core.repository.package_info import PackageInfo
from ahriman.core.repository.update_handler import UpdateHandler
from ahriman.models.pacman_synchronization import PacmanSynchronization
@ -49,6 +50,23 @@ def executor(configuration: Configuration, database: SQLite, mocker: MockerFixtu
refresh_pacman_database=PacmanSynchronization.Disabled)
@pytest.fixture
def package_info(configuration: Configuration, database: SQLite) -> PackageInfo:
"""
fixture for package info
Args:
configuration(Configuration): configuration fixture
database(SQLite): database fixture
Returns:
PackageInfo: package info test instance
"""
_, repository_id = configuration.check_loaded()
return PackageInfo(repository_id, configuration, database, report=False,
refresh_pacman_database=PacmanSynchronization.Disabled)
@pytest.fixture
def update_handler(configuration: Configuration, database: SQLite, mocker: MockerFixture) -> UpdateHandler:
"""

View File

@ -5,36 +5,22 @@ from pytest_mock import MockerFixture
from unittest.mock import call as MockCall
from ahriman.core.repository.executor import Executor
from ahriman.models.changes import Changes
from ahriman.models.package import Package
from ahriman.models.packagers import Packagers
from ahriman.models.user import User
def test_load_archives(executor: Executor) -> None:
"""
must raise NotImplemented for missing load_archives method
"""
with pytest.raises(NotImplementedError):
executor.load_archives([])
def test_packages(executor: Executor) -> None:
"""
must raise NotImplemented for missing method
"""
with pytest.raises(NotImplementedError):
executor.packages()
def test_process_build(executor: Executor, package_ahriman: Package, mocker: MockerFixture) -> None:
"""
must run build process
"""
mocker.patch("ahriman.core.repository.executor.Executor.packages", return_value=[package_ahriman])
mocker.patch("ahriman.core.build_tools.task.Task.build", return_value=[Path(package_ahriman.base)])
init_mock = mocker.patch("ahriman.core.build_tools.task.Task.init")
init_mock = mocker.patch("ahriman.core.build_tools.task.Task.init", return_value="sha")
move_mock = mocker.patch("shutil.move")
status_client_mock = mocker.patch("ahriman.core.status.client.Client.set_building")
commit_sha_mock = mocker.patch("ahriman.core.status.client.Client.package_changes_set")
executor.process_build([package_ahriman], Packagers("packager"), bump_pkgrel=False)
init_mock.assert_called_once_with(pytest.helpers.anyvar(int), pytest.helpers.anyvar(int), None)
@ -42,6 +28,7 @@ def test_process_build(executor: Executor, package_ahriman: Package, mocker: Moc
move_mock.assert_called_once_with(Path(package_ahriman.base), executor.paths.packages / package_ahriman.base)
# must update status
status_client_mock.assert_called_once_with(package_ahriman.base)
commit_sha_mock.assert_called_once_with(package_ahriman.base, Changes("sha"))
def test_process_build_bump_pkgrel(executor: Executor, package_ahriman: Package, mocker: MockerFixture) -> None:
@ -52,6 +39,7 @@ def test_process_build_bump_pkgrel(executor: Executor, package_ahriman: Package,
mocker.patch("ahriman.core.build_tools.task.Task.build", return_value=[Path(package_ahriman.base)])
mocker.patch("shutil.move")
mocker.patch("ahriman.core.status.client.Client.set_building")
mocker.patch("ahriman.core.status.client.Client.package_changes_set")
init_mock = mocker.patch("ahriman.core.build_tools.task.Task.init")
executor.process_build([package_ahriman], Packagers("packager"), bump_pkgrel=True)
@ -85,6 +73,7 @@ def test_process_remove_base(executor: Executor, package_ahriman: Package, mocke
build_queue_mock = mocker.patch("ahriman.core.database.SQLite.build_queue_clear")
patches_mock = mocker.patch("ahriman.core.database.SQLite.patches_remove")
logs_mock = mocker.patch("ahriman.core.database.SQLite.logs_remove")
commit_sha_mock = mocker.patch("ahriman.core.database.SQLite.changes_remove")
status_client_mock = mocker.patch("ahriman.core.status.client.Client.package_remove")
executor.process_remove([package_ahriman.base])
@ -97,6 +86,7 @@ def test_process_remove_base(executor: Executor, package_ahriman: Package, mocke
patches_mock.assert_called_once_with(package_ahriman.base, [])
logs_mock.assert_called_once_with(package_ahriman.base, None)
status_client_mock.assert_called_once_with(package_ahriman.base)
commit_sha_mock.assert_called_once_with(package_ahriman.base)
def test_process_remove_base_multiple(executor: Executor, package_python_schedule: Package,

View File

@ -0,0 +1,136 @@
import pytest
from pathlib import Path
from pytest_mock import MockerFixture
from ahriman.core.repository.package_info import PackageInfo
from ahriman.models.changes import Changes
from ahriman.models.package import Package
def test_load_archives(package_ahriman: Package, package_python_schedule: Package,
package_info: PackageInfo, mocker: MockerFixture) -> None:
"""
must return all packages grouped by package base
"""
single_packages = [
Package(base=package_python_schedule.base,
version=package_python_schedule.version,
remote=package_python_schedule.remote,
packages={package: props})
for package, props in package_python_schedule.packages.items()
] + [package_ahriman]
mocker.patch("ahriman.models.package.Package.from_archive", side_effect=single_packages)
mocker.patch("ahriman.core.database.SQLite.remotes_get", return_value={
package_ahriman.base: package_ahriman.base
})
packages = package_info.load_archives([Path("a.pkg.tar.xz"), Path("b.pkg.tar.xz"), Path("c.pkg.tar.xz")])
assert len(packages) == 2
assert {package.base for package in packages} == {package_ahriman.base, package_python_schedule.base}
archives = sum([list(package.packages.keys()) for package in packages], start=[])
assert len(archives) == 3
expected = set(package_ahriman.packages.keys())
expected.update(package_python_schedule.packages.keys())
assert set(archives) == expected
def test_load_archives_failed(package_info: PackageInfo, mocker: MockerFixture) -> None:
"""
must skip packages which cannot be loaded
"""
mocker.patch("ahriman.models.package.Package.from_archive", side_effect=Exception())
assert not package_info.load_archives([Path("a.pkg.tar.xz")])
def test_load_archives_not_package(package_info: PackageInfo) -> None:
"""
must skip not packages from iteration
"""
assert not package_info.load_archives([Path("a.tar.xz")])
def test_load_archives_different_version(package_info: PackageInfo, package_python_schedule: Package,
mocker: MockerFixture) -> None:
"""
must load packages with different versions choosing maximal
"""
single_packages = [
Package(base=package_python_schedule.base,
version=package_python_schedule.version,
remote=package_python_schedule.remote,
packages={package: props})
for package, props in package_python_schedule.packages.items()
]
single_packages[0].version = "0.0.1-1"
mocker.patch("ahriman.models.package.Package.from_archive", side_effect=single_packages)
packages = package_info.load_archives([Path("a.pkg.tar.xz"), Path("b.pkg.tar.xz")])
assert len(packages) == 1
assert packages[0].version == package_python_schedule.version
def test_package_changes(package_info: PackageInfo, package_ahriman: Package, mocker: MockerFixture) -> None:
"""
must load package changes
"""
changes = Changes("sha", "change")
load_mock = mocker.patch("ahriman.core.build_tools.sources.Sources.load", return_value="sha2")
changes_mock = mocker.patch("ahriman.core.build_tools.sources.Sources.changes", return_value=changes.changes)
assert package_info.package_changes(package_ahriman, changes.last_commit_sha) == changes
load_mock.assert_called_once_with(pytest.helpers.anyvar(int), package_ahriman, [], package_info.paths)
changes_mock.assert_called_once_with(pytest.helpers.anyvar(int), changes.last_commit_sha)
def test_package_changes_skip(package_info: PackageInfo, package_ahriman: Package, mocker: MockerFixture) -> None:
"""
must skip loading package changes if no new commits
"""
changes = Changes("sha")
mocker.patch("ahriman.core.build_tools.sources.Sources.load", return_value=changes.last_commit_sha)
changes_mock = mocker.patch("ahriman.core.build_tools.sources.Sources.changes")
assert package_info.package_changes(package_ahriman, changes.last_commit_sha) == changes
changes_mock.assert_not_called()
def test_packages(package_info: PackageInfo, package_ahriman: Package, mocker: MockerFixture) -> None:
"""
must return repository packages
"""
mocker.patch("pathlib.Path.iterdir", return_value=[package_ahriman])
load_mock = mocker.patch("ahriman.core.repository.package_info.PackageInfo.load_archives")
package_info.packages()
# it uses filter object, so we cannot verify argument list =/
load_mock.assert_called_once_with(pytest.helpers.anyvar(int))
def test_packages_built(package_info: PackageInfo, mocker: MockerFixture) -> None:
"""
must return build packages
"""
mocker.patch("pathlib.Path.iterdir", return_value=[Path("a.tar.xz"), Path("b.pkg.tar.xz")])
assert package_info.packages_built() == [Path("b.pkg.tar.xz")]
def test_packages_depend_on(package_info: PackageInfo, package_ahriman: Package, package_python_schedule: Package,
mocker: MockerFixture) -> None:
"""
must filter packages by depends list
"""
mocker.patch("ahriman.core.repository.repository.Repository.packages",
return_value=[package_ahriman, package_python_schedule])
assert package_info.packages_depend_on([package_ahriman], {"python-srcinfo"}) == [package_ahriman]
def test_packages_depend_on_empty(package_info: PackageInfo, package_ahriman: Package, package_python_schedule: Package,
mocker: MockerFixture) -> None:
"""
must return all packages in case if no filter is provided
"""
mocker.patch("ahriman.core.repository.repository.Repository.packages",
return_value=[package_ahriman, package_python_schedule])
assert package_info.packages_depend_on([package_ahriman, package_python_schedule], None) == \
[package_ahriman, package_python_schedule]

View File

@ -1,6 +1,3 @@
import pytest
from pathlib import Path
from pytest_mock import MockerFixture
from unittest.mock import call as MockCall
@ -10,9 +7,6 @@ from ahriman.core.database import SQLite
from ahriman.core.repository import Repository
from ahriman.core.sign.gpg import GPG
from ahriman.models.context_key import ContextKey
from ahriman.models.package import Package
from ahriman.models.package_source import PackageSource
from ahriman.models.remote_source import RemoteSource
def test_load(configuration: Configuration, database: SQLite, mocker: MockerFixture) -> None:
@ -41,105 +35,3 @@ def test_set_context(configuration: Configuration, database: SQLite, mocker: Moc
MockCall(ContextKey("sign", GPG), instance.sign),
MockCall(ContextKey("repository", Repository), instance),
])
def test_load_archives(package_ahriman: Package, package_python_schedule: Package,
repository: Repository, mocker: MockerFixture) -> None:
"""
must return all packages grouped by package base
"""
single_packages = [
Package(base=package_python_schedule.base,
version=package_python_schedule.version,
remote=package_python_schedule.remote,
packages={package: props})
for package, props in package_python_schedule.packages.items()
] + [package_ahriman]
mocker.patch("ahriman.models.package.Package.from_archive", side_effect=single_packages)
mocker.patch("ahriman.core.database.SQLite.remotes_get", return_value={
package_ahriman.base: package_ahriman.base
})
packages = repository.load_archives([Path("a.pkg.tar.xz"), Path("b.pkg.tar.xz"), Path("c.pkg.tar.xz")])
assert len(packages) == 2
assert {package.base for package in packages} == {package_ahriman.base, package_python_schedule.base}
archives = sum([list(package.packages.keys()) for package in packages], start=[])
assert len(archives) == 3
expected = set(package_ahriman.packages.keys())
expected.update(package_python_schedule.packages.keys())
assert set(archives) == expected
def test_load_archives_failed(repository: Repository, mocker: MockerFixture) -> None:
"""
must skip packages which cannot be loaded
"""
mocker.patch("ahriman.models.package.Package.from_archive", side_effect=Exception())
assert not repository.load_archives([Path("a.pkg.tar.xz")])
def test_load_archives_not_package(repository: Repository) -> None:
"""
must skip not packages from iteration
"""
assert not repository.load_archives([Path("a.tar.xz")])
def test_load_archives_different_version(repository: Repository, package_python_schedule: Package,
mocker: MockerFixture) -> None:
"""
must load packages with different versions choosing maximal
"""
single_packages = [
Package(base=package_python_schedule.base,
version=package_python_schedule.version,
remote=package_python_schedule.remote,
packages={package: props})
for package, props in package_python_schedule.packages.items()
]
single_packages[0].version = "0.0.1-1"
mocker.patch("ahriman.models.package.Package.from_archive", side_effect=single_packages)
packages = repository.load_archives([Path("a.pkg.tar.xz"), Path("b.pkg.tar.xz")])
assert len(packages) == 1
assert packages[0].version == package_python_schedule.version
def test_packages(repository: Repository, mocker: MockerFixture) -> None:
"""
must return repository packages
"""
load_mock = mocker.patch("ahriman.core.repository.repository.Repository.load_archives")
repository.packages()
# it uses filter object, so we cannot verify argument list =/
load_mock.assert_called_once_with(pytest.helpers.anyvar(int))
def test_packages_built(repository: Repository, mocker: MockerFixture) -> None:
"""
must return build packages
"""
mocker.patch("pathlib.Path.iterdir", return_value=[Path("a.tar.xz"), Path("b.pkg.tar.xz")])
assert repository.packages_built() == [Path("b.pkg.tar.xz")]
def test_packages_depend_on(repository: Repository, package_ahriman: Package, package_python_schedule: Package,
mocker: MockerFixture) -> None:
"""
must filter packages by depends list
"""
mocker.patch("ahriman.core.repository.repository.Repository.packages",
return_value=[package_ahriman, package_python_schedule])
assert repository.packages_depend_on([package_ahriman], {"python-srcinfo"}) == [package_ahriman]
def test_packages_depend_on_empty(repository: Repository, package_ahriman: Package, package_python_schedule: Package,
mocker: MockerFixture) -> None:
"""
must return all packages in case if no filter is provided
"""
mocker.patch("ahriman.core.repository.repository.Repository.packages",
return_value=[package_ahriman, package_python_schedule])
assert repository.packages_depend_on([package_ahriman, package_python_schedule], None) ==\
[package_ahriman, package_python_schedule]

View File

@ -11,14 +11,6 @@ from ahriman.models.package_source import PackageSource
from ahriman.models.remote_source import RemoteSource
def test_packages(update_handler: UpdateHandler) -> None:
"""
must raise NotImplemented for missing method
"""
with pytest.raises(NotImplementedError):
update_handler.packages()
def test_updates_aur(update_handler: UpdateHandler, package_ahriman: Package,
mocker: MockerFixture) -> None:
"""

View File

@ -109,8 +109,7 @@ def test_key_export(gpg: GPG, mocker: MockerFixture) -> None:
"""
check_output_mock = mocker.patch("ahriman.core.sign.gpg.check_output", return_value="key")
assert gpg.key_export("k") == "key"
check_output_mock.assert_called_once_with("gpg", "--armor", "--no-emit-version", "--export", "k",
logger=pytest.helpers.anyvar(int))
check_output_mock.assert_called_once_with("gpg", "--armor", "--no-emit-version", "--export", "k", logger=gpg.logger)
def test_key_fingerprint(gpg: GPG, mocker: MockerFixture) -> None:
@ -126,8 +125,7 @@ fpr:::::::::43A663569A07EE1E4ECC55CC7E3A4240CE3C45C2:""")
key = "0xCE3C45C2"
assert gpg.key_fingerprint(key) == "C6EBB9222C3C8078631A0DE4BD2AC8C5E989490C"
check_output_mock.assert_called_once_with("gpg", "--with-colons", "--fingerprint", key,
logger=pytest.helpers.anyvar(int))
check_output_mock.assert_called_once_with("gpg", "--with-colons", "--fingerprint", key, logger=gpg.logger)
def test_key_import(gpg: GPG, mocker: MockerFixture) -> None:
@ -138,7 +136,7 @@ def test_key_import(gpg: GPG, mocker: MockerFixture) -> None:
check_output_mock = mocker.patch("ahriman.core.sign.gpg.check_output")
gpg.key_import("keyserver.ubuntu.com", "0xE989490C")
check_output_mock.assert_called_once_with("gpg", "--import", input_data="key", logger=pytest.helpers.anyvar(int))
check_output_mock.assert_called_once_with("gpg", "--import", input_data="key", logger=gpg.logger)
def test_process(gpg_with_key: GPG, mocker: MockerFixture) -> None:

View File

@ -6,6 +6,7 @@ from ahriman.core.configuration import Configuration
from ahriman.core.status.client import Client
from ahriman.core.status.web_client import WebClient
from ahriman.models.build_status import BuildStatus, BuildStatusEnum
from ahriman.models.changes import Changes
from ahriman.models.internal_status import InternalStatus
from ahriman.models.log_record_id import LogRecordId
from ahriman.models.package import Package
@ -87,6 +88,20 @@ def test_package_add(client: Client, package_ahriman: Package) -> None:
client.package_add(package_ahriman, BuildStatusEnum.Unknown)
def test_package_changes_get(client: Client, package_ahriman: Package) -> None:
"""
must return null changes
"""
assert client.package_changes_get(package_ahriman.base) == Changes()
def test_package_changes_set(client: Client, package_ahriman: Package) -> None:
"""
must process changes update without errors
"""
client.package_changes_set(package_ahriman.base, Changes())
def test_package_get(client: Client, package_ahriman: Package) -> None:
"""
must return empty package list

View File

@ -6,6 +6,7 @@ from unittest.mock import call as MockCall
from ahriman.core.exceptions import UnknownPackageError
from ahriman.core.status.watcher import Watcher
from ahriman.models.build_status import BuildStatus, BuildStatusEnum
from ahriman.models.changes import Changes
from ahriman.models.log_record_id import LogRecordId
from ahriman.models.package import Package
from ahriman.models.pkgbuild_patch import PkgbuildPatch
@ -88,6 +89,25 @@ def test_logs_update_update(watcher: Watcher, package_ahriman: Package, mocker:
insert_mock.assert_called_once_with(log_record_id, 42.01, "log record", watcher.repository_id)
def test_package_changes_get(watcher: Watcher, package_ahriman: Package, mocker: MockerFixture) -> None:
"""
must return package changes
"""
get_mock = mocker.patch("ahriman.core.database.SQLite.changes_get", return_value=Changes("sha"))
watcher.known = {package_ahriman.base: (package_ahriman, BuildStatus())}
assert watcher.package_changes_get(package_ahriman.base) == Changes("sha")
get_mock.assert_called_once_with(package_ahriman.base, watcher.repository_id)
def test_package_changes_get_failed(watcher: Watcher, package_ahriman: Package) -> None:
"""
must raise UnknownPackageError in case of unknown package
"""
with pytest.raises(UnknownPackageError):
watcher.package_changes_get(package_ahriman.base)
def test_package_get(watcher: Watcher, package_ahriman: Package) -> None:
"""
must return package status

View File

@ -8,6 +8,7 @@ from pytest_mock import MockerFixture
from ahriman.core.configuration import Configuration
from ahriman.core.status.web_client import WebClient
from ahriman.models.build_status import BuildStatus, BuildStatusEnum
from ahriman.models.changes import Changes
from ahriman.models.internal_status import InternalStatus
from ahriman.models.log_record_id import LogRecordId
from ahriman.models.package import Package
@ -39,6 +40,14 @@ def test_status_url(web_client: WebClient) -> None:
assert web_client._status_url().endswith("/api/v1/status")
def test_changes_url(web_client: WebClient, package_ahriman: Package) -> None:
"""
must generate changes url correctly
"""
assert web_client._changes_url(package_ahriman.base).startswith(web_client.address)
assert web_client._changes_url(package_ahriman.base).endswith(f"/api/v1/packages/{package_ahriman.base}/changes")
def test_logs_url(web_client: WebClient, package_ahriman: Package) -> None:
"""
must generate logs url correctly
@ -111,6 +120,121 @@ def test_package_add_failed_http_error_suppress(web_client: WebClient, package_a
logging_mock.assert_not_called()
def test_package_changes_get(web_client: WebClient, package_ahriman: Package, mocker: MockerFixture) -> None:
"""
must get changes
"""
changes = Changes("sha")
response_obj = requests.Response()
response_obj._content = json.dumps(changes.view()).encode("utf8")
response_obj.status_code = 200
requests_mock = mocker.patch("ahriman.core.status.web_client.WebClient.make_request", return_value=response_obj)
result = web_client.package_changes_get(package_ahriman.base)
requests_mock.assert_called_once_with("GET", pytest.helpers.anyvar(str, True),
params=web_client.repository_id.query())
assert result == changes
def test_package_changes_get_failed(web_client: WebClient, package_ahriman: Package, mocker: MockerFixture) -> None:
"""
must suppress any exception happened during changes fetch
"""
mocker.patch("requests.Session.request", side_effect=Exception())
web_client.package_changes_get(package_ahriman.base)
def test_package_changes_get_failed_http_error(web_client: WebClient, package_ahriman: Package,
mocker: MockerFixture) -> None:
"""
must suppress HTTP exception happened during changes fetch
"""
mocker.patch("requests.Session.request", side_effect=requests.HTTPError())
web_client.package_changes_get(package_ahriman.base)
def test_package_changes_get_failed_suppress(web_client: WebClient, package_ahriman: Package,
mocker: MockerFixture) -> None:
"""
must suppress any exception happened during changes fetch and don't log
"""
web_client.suppress_errors = True
mocker.patch("requests.Session.request", side_effect=Exception())
logging_mock = mocker.patch("logging.exception")
web_client.package_changes_get(package_ahriman.base)
logging_mock.assert_not_called()
def test_package_changes_get_failed_http_error_suppress(web_client: WebClient, package_ahriman: Package,
mocker: MockerFixture) -> None:
"""
must suppress HTTP exception happened during changes fetch and don't log
"""
web_client.suppress_errors = True
mocker.patch("requests.Session.request", side_effect=requests.HTTPError())
logging_mock = mocker.patch("logging.exception")
web_client.package_changes_get(package_ahriman.base)
logging_mock.assert_not_called()
def test_package_changes_set(web_client: WebClient, package_ahriman: Package, mocker: MockerFixture) -> None:
"""
must set changes
"""
changes = Changes("sha")
requests_mock = mocker.patch("ahriman.core.status.web_client.WebClient.make_request")
web_client.package_changes_set(package_ahriman.base, changes)
requests_mock.assert_called_once_with("POST", pytest.helpers.anyvar(str, True),
params=web_client.repository_id.query(), json=changes.view())
def test_package_changes_set_failed(web_client: WebClient, package_ahriman: Package, mocker: MockerFixture) -> None:
"""
must suppress any exception happened during changes update
"""
mocker.patch("requests.Session.request", side_effect=Exception())
web_client.package_changes_set(package_ahriman.base, Changes())
def test_package_changes_set_failed_http_error(web_client: WebClient, package_ahriman: Package,
mocker: MockerFixture) -> None:
"""
must suppress HTTP exception happened during changes update
"""
mocker.patch("requests.Session.request", side_effect=requests.HTTPError())
web_client.package_changes_set(package_ahriman.base, Changes())
def test_package_changes_set_failed_suppress(web_client: WebClient, package_ahriman: Package,
mocker: MockerFixture) -> None:
"""
must suppress any exception happened during changes update and don't log
"""
web_client.suppress_errors = True
mocker.patch("requests.Session.request", side_effect=Exception())
logging_mock = mocker.patch("logging.exception")
web_client.package_changes_set(package_ahriman.base, Changes())
logging_mock.assert_not_called()
def test_package_changes_set_failed_http_error_suppress(web_client: WebClient, package_ahriman: Package,
mocker: MockerFixture) -> None:
"""
must suppress HTTP exception happened during changes update and don't log
"""
web_client.suppress_errors = True
mocker.patch("requests.Session.request", side_effect=requests.HTTPError())
logging_mock = mocker.patch("logging.exception")
web_client.package_changes_set(package_ahriman.base, Changes())
logging_mock.assert_not_called()
def test_package_get_all(web_client: WebClient, package_ahriman: Package, mocker: MockerFixture) -> None:
"""
must return all packages status

View File

@ -0,0 +1,26 @@
from ahriman.models.changes import Changes
def test_is_empty() -> None:
"""
must check if changes are empty
"""
assert Changes().is_empty
assert Changes("sha").is_empty
assert not Changes("sha", "change").is_empty
assert not Changes(None, "change").is_empty # well, ok
def test_changes_from_json_view() -> None:
"""
must construct same object from json
"""
changes = Changes()
assert Changes.from_json(changes.view()) == changes
changes = Changes("sha")
assert Changes.from_json(changes.view()) == changes
changes = Changes("sha", "change")
assert Changes.from_json(changes.view()) == changes

View File

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

View File

@ -0,0 +1,84 @@
import pytest
from aiohttp.test_utils import TestClient
from ahriman.models.build_status import BuildStatusEnum
from ahriman.models.changes import Changes
from ahriman.models.package import Package
from ahriman.models.user_access import UserAccess
from ahriman.web.views.v1.status.changes import ChangesView
async def test_get_permission() -> None:
"""
must return correct permission for the request
"""
for method in ("GET",):
request = pytest.helpers.request("", "", method)
assert await ChangesView.get_permission(request) == UserAccess.Reporter
for method in ("POST",):
request = pytest.helpers.request("", "", method)
assert await ChangesView.get_permission(request) == UserAccess.Full
def test_routes() -> None:
"""
must return correct routes
"""
assert ChangesView.ROUTES == ["/api/v1/packages/{package}/changes"]
async def test_get(client: TestClient, package_ahriman: Package) -> None:
"""
must get changes for package
"""
changes = Changes("sha", "change")
await client.post(f"/api/v1/packages/{package_ahriman.base}",
json={"status": BuildStatusEnum.Success.value, "package": package_ahriman.view()})
await client.post(f"/api/v1/packages/{package_ahriman.base}/changes", json=changes.view())
response_schema = pytest.helpers.schema_response(ChangesView.get)
response = await client.get(f"/api/v1/packages/{package_ahriman.base}/changes")
assert response.status == 200
assert await response.json() == changes.view()
assert not response_schema.validate(changes.view())
async def test_get_not_found(client: TestClient, package_ahriman: Package) -> None:
"""
must return not found for missing package
"""
response_schema = pytest.helpers.schema_response(ChangesView.get, code=404)
response = await client.get(f"/api/v1/packages/{package_ahriman.base}/changes")
assert response.status == 404
assert not response_schema.validate(await response.json())
async def test_post(client: TestClient, package_ahriman: Package) -> None:
"""
must update package changes
"""
await client.post(f"/api/v1/packages/{package_ahriman.base}",
json={"status": BuildStatusEnum.Success.value, "package": package_ahriman.view()})
request_schema = pytest.helpers.schema_request(ChangesView.post)
changes = Changes("sha", "change")
assert not request_schema.validate(changes.view())
response = await client.post(f"/api/v1/packages/{package_ahriman.base}/changes", json=changes.view())
assert response.status == 204
response = await client.get(f"/api/v1/packages/{package_ahriman.base}/changes")
assert await response.json() == changes.view()
async def test_post_exception(client: TestClient, package_ahriman: Package) -> None:
"""
must raise exception on invalid payload
"""
response_schema = pytest.helpers.schema_response(ChangesView.post, code=400)
response = await client.post(f"/api/v1/packages/{package_ahriman.base}/changes", json=[])
assert response.status == 400
assert not response_schema.validate(await response.json())