mirror of
https://github.com/arcan1s/ahriman.git
synced 2025-07-31 14:49:56 +00:00
write tests to support new changes
This commit is contained in:
@ -50,7 +50,7 @@ class TreeMigrate(Handler):
|
||||
target_tree.tree_create()
|
||||
# perform migration
|
||||
TreeMigrate.tree_move(current_tree, target_tree)
|
||||
TreeMigrate.fix_symlinks(current_tree)
|
||||
TreeMigrate.fix_symlinks(target_tree)
|
||||
|
||||
@staticmethod
|
||||
def _set_service_tree_migrate_parser(root: SubParserAction) -> argparse.ArgumentParser:
|
||||
|
@ -59,7 +59,7 @@ class Repo(LazyLogging):
|
||||
"""
|
||||
return self.root / f"{self.name}.db.tar.gz"
|
||||
|
||||
def add(self, path: Path, remove: bool = True) -> None:
|
||||
def add(self, path: Path, *, remove: bool = True) -> None:
|
||||
"""
|
||||
add new package to repository
|
||||
|
||||
@ -97,7 +97,7 @@ class Repo(LazyLogging):
|
||||
filename(Path): package filename to remove
|
||||
"""
|
||||
# remove package and signature (if any) from filesystem
|
||||
for full_path in self.root.glob(f"**/{filename}*"):
|
||||
for full_path in self.root.glob(f"**/{filename.name}*"):
|
||||
full_path.unlink()
|
||||
|
||||
# remove package from registry
|
||||
|
@ -41,7 +41,7 @@ class Executor(PackageInfo, Cleaner):
|
||||
trait for common repository update processes
|
||||
"""
|
||||
|
||||
def _archive_remove(self, description: PackageDescription, package_base: str) -> None:
|
||||
def _archive_rename(self, description: PackageDescription, package_base: str) -> None:
|
||||
"""
|
||||
rename package archive removing special symbols
|
||||
|
||||
@ -259,7 +259,7 @@ class Executor(PackageInfo, Cleaner):
|
||||
packager = self.packager(packagers, local.base)
|
||||
|
||||
for description in local.packages.values():
|
||||
self._archive_remove(description, local.base)
|
||||
self._archive_rename(description, local.base)
|
||||
self._package_update(description.filename, local.base, packager.key)
|
||||
self.reporter.set_success(local)
|
||||
result.add_updated(local)
|
||||
|
@ -309,6 +309,9 @@ class RepositoryPaths(LazyLogging):
|
||||
path = path or self.root
|
||||
|
||||
def walk(root: Path) -> Generator[Path, None, None]:
|
||||
if not root.exists():
|
||||
return
|
||||
|
||||
# basically walk, but skipping some content
|
||||
for child in root.iterdir():
|
||||
yield child
|
||||
|
@ -6,6 +6,7 @@ from unittest.mock import call as MockCall
|
||||
|
||||
from ahriman.application.handlers.tree_migrate import TreeMigrate
|
||||
from ahriman.core.configuration import Configuration
|
||||
from ahriman.models.package import Package
|
||||
from ahriman.models.repository_id import RepositoryId
|
||||
from ahriman.models.repository_paths import RepositoryPaths
|
||||
|
||||
@ -16,6 +17,7 @@ def test_run(args: argparse.Namespace, configuration: Configuration, mocker: Moc
|
||||
"""
|
||||
tree_create_mock = mocker.patch("ahriman.models.repository_paths.RepositoryPaths.tree_create")
|
||||
application_mock = mocker.patch("ahriman.application.handlers.tree_migrate.TreeMigrate.tree_move")
|
||||
symlinks_mock = mocker.patch("ahriman.application.handlers.tree_migrate.TreeMigrate.fix_symlinks")
|
||||
_, repository_id = configuration.check_loaded()
|
||||
old_paths = configuration.repository_paths
|
||||
new_paths = RepositoryPaths(old_paths.root, old_paths.repository_id, _force_current_tree=True)
|
||||
@ -23,6 +25,37 @@ def test_run(args: argparse.Namespace, configuration: Configuration, mocker: Moc
|
||||
TreeMigrate.run(args, repository_id, configuration, report=False)
|
||||
tree_create_mock.assert_called_once_with()
|
||||
application_mock.assert_called_once_with(old_paths, new_paths)
|
||||
symlinks_mock.assert_called_once_with(new_paths)
|
||||
|
||||
|
||||
def test_fix_symlinks(repository_paths: RepositoryPaths, package_ahriman: Package, mocker: MockerFixture) -> None:
|
||||
"""
|
||||
must replace symlinks during migration
|
||||
"""
|
||||
mocker.patch("ahriman.models.repository_paths.RepositoryPaths.preserve_owner")
|
||||
mocker.patch("ahriman.application.handlers.tree_migrate.walk", side_effect=[
|
||||
[
|
||||
repository_paths.archive_for(package_ahriman.base) / "file",
|
||||
repository_paths.archive_for(package_ahriman.base) / "symlink",
|
||||
],
|
||||
[
|
||||
repository_paths.repository / "file",
|
||||
repository_paths.repository / "symlink",
|
||||
],
|
||||
])
|
||||
mocker.patch("pathlib.Path.exists", autospec=True, side_effect=lambda p: p.name == "file")
|
||||
unlink_mock = mocker.patch("pathlib.Path.unlink")
|
||||
symlink_mock = mocker.patch("pathlib.Path.symlink_to")
|
||||
|
||||
TreeMigrate.fix_symlinks(repository_paths)
|
||||
unlink_mock.assert_called_once_with()
|
||||
symlink_mock.assert_called_once_with(
|
||||
Path("..") /
|
||||
".." /
|
||||
".." /
|
||||
repository_paths.archive_for(package_ahriman.base).relative_to(repository_paths.root) /
|
||||
"symlink"
|
||||
)
|
||||
|
||||
|
||||
def test_move_tree(mocker: MockerFixture) -> None:
|
||||
@ -36,6 +69,7 @@ def test_move_tree(mocker: MockerFixture) -> None:
|
||||
|
||||
TreeMigrate.tree_move(from_paths, to_paths)
|
||||
rename_mock.assert_has_calls([
|
||||
MockCall(from_paths.archive, to_paths.archive),
|
||||
MockCall(from_paths.packages, to_paths.packages),
|
||||
MockCall(from_paths.pacman, to_paths.pacman),
|
||||
MockCall(from_paths.repository, to_paths.repository),
|
||||
|
@ -293,7 +293,7 @@ def database(configuration: Configuration, mocker: MockerFixture) -> SQLite:
|
||||
def perform_migration(self: Migrations, cursor: Cursor, migration: Migration) -> None:
|
||||
original_method(self, cursor, replace(migration, migrate_data=lambda *args: None))
|
||||
|
||||
mocker.patch.object(Migrations, "perform_migration", side_effect=perform_migration, autospec=True)
|
||||
mocker.patch.object(Migrations, "perform_migration", autospec=True, side_effect=perform_migration)
|
||||
return SQLite.load(configuration)
|
||||
|
||||
|
||||
|
@ -4,6 +4,15 @@ from pathlib import Path
|
||||
from pytest_mock import MockerFixture
|
||||
|
||||
from ahriman.core.alpm.repo import Repo
|
||||
from ahriman.models.repository_paths import RepositoryPaths
|
||||
|
||||
|
||||
def test_root(repository_paths: RepositoryPaths) -> None:
|
||||
"""
|
||||
must correctly define repository root
|
||||
"""
|
||||
assert Repo(repository_paths.repository_id.name, repository_paths, []).root == repository_paths.repository
|
||||
assert Repo(repository_paths.repository_id.name, repository_paths, [], Path("path")).root == Path("path")
|
||||
|
||||
|
||||
def test_repo_path(repo: Repo) -> None:
|
||||
@ -22,6 +31,18 @@ def test_repo_add(repo: Repo, mocker: MockerFixture) -> None:
|
||||
repo.add(Path("path"))
|
||||
check_output_mock.assert_called_once() # it will be checked later
|
||||
assert check_output_mock.call_args[0][0] == "repo-add"
|
||||
assert "--remove" in check_output_mock.call_args[0]
|
||||
|
||||
|
||||
def test_repo_add_no_remove(repo: Repo, mocker: MockerFixture) -> None:
|
||||
"""
|
||||
must call repo-add without remove flag
|
||||
"""
|
||||
check_output_mock = mocker.patch("ahriman.core.alpm.repo.check_output")
|
||||
|
||||
repo.add(Path("path"), remove=False)
|
||||
check_output_mock.assert_called_once() # it will be checked later
|
||||
assert "--remove" not in check_output_mock.call_args[0]
|
||||
|
||||
|
||||
def test_repo_init(repo: Repo, mocker: MockerFixture) -> None:
|
||||
|
82
tests/ahriman/core/database/migrations/test_m016_archive.py
Normal file
82
tests/ahriman/core/database/migrations/test_m016_archive.py
Normal file
@ -0,0 +1,82 @@
|
||||
import pytest
|
||||
|
||||
from dataclasses import replace
|
||||
from pathlib import Path
|
||||
from pytest_mock import MockerFixture
|
||||
from sqlite3 import Connection
|
||||
from typing import Any
|
||||
from unittest.mock import call as MockCall
|
||||
|
||||
from ahriman.core.alpm.pacman import Pacman
|
||||
from ahriman.core.configuration import Configuration
|
||||
from ahriman.core.database.migrations.m016_archive import migrate_data, move_packages
|
||||
from ahriman.models.package import Package
|
||||
from ahriman.models.repository_paths import RepositoryPaths
|
||||
|
||||
|
||||
def test_migrate_data(connection: Connection, configuration: Configuration, mocker: MockerFixture) -> None:
|
||||
"""
|
||||
must perform data migration
|
||||
"""
|
||||
_, repository_id = configuration.check_loaded()
|
||||
repositories = [
|
||||
repository_id,
|
||||
replace(repository_id, architecture="i686"),
|
||||
]
|
||||
mocker.patch("ahriman.application.handlers.handler.Handler.repositories_extract", return_value=repositories)
|
||||
migration_mock = mocker.patch("ahriman.core.database.migrations.m016_archive.move_packages")
|
||||
|
||||
migrate_data(connection, configuration)
|
||||
migration_mock.assert_has_calls([
|
||||
MockCall(replace(configuration.repository_paths, repository_id=repository), pytest.helpers.anyvar(int))
|
||||
for repository in repositories
|
||||
])
|
||||
|
||||
|
||||
def test_move_packages(repository_paths: RepositoryPaths, pacman: Pacman, package_ahriman: Package,
|
||||
mocker: MockerFixture) -> None:
|
||||
"""
|
||||
must move packages to the archive directory
|
||||
"""
|
||||
|
||||
def is_file(self: Path, *args: Any, **kwargs: Any) -> bool:
|
||||
return "file" in self.name
|
||||
|
||||
mocker.patch("pathlib.Path.iterdir", return_value=[
|
||||
repository_paths.repository / ".hidden-file.pkg.tar.xz",
|
||||
repository_paths.repository / "directory",
|
||||
repository_paths.repository / "file.pkg.tar.xz",
|
||||
repository_paths.repository / "file.pkg.tar.xz.sig",
|
||||
repository_paths.repository / "symlink.pkg.tar.xz",
|
||||
])
|
||||
mocker.patch("pathlib.Path.is_dir", return_value=True)
|
||||
mocker.patch("pathlib.Path.is_file", autospec=True, side_effect=is_file)
|
||||
archive_mock = mocker.patch("ahriman.models.package.Package.from_archive", return_value=package_ahriman)
|
||||
rename_mock = mocker.patch("pathlib.Path.rename")
|
||||
symlink_mock = mocker.patch("pathlib.Path.symlink_to")
|
||||
|
||||
move_packages(repository_paths, pacman)
|
||||
archive_mock.assert_has_calls([
|
||||
MockCall(repository_paths.repository / "file.pkg.tar.xz", pacman),
|
||||
MockCall(repository_paths.repository / "file.pkg.tar.xz.sig", pacman),
|
||||
])
|
||||
rename_mock.assert_has_calls([
|
||||
MockCall(repository_paths.archive_for(package_ahriman.base) / "file.pkg.tar.xz"),
|
||||
MockCall(repository_paths.archive_for(package_ahriman.base) / "file.pkg.tar.xz.sig"),
|
||||
])
|
||||
symlink_mock.assert_has_calls([
|
||||
MockCall(
|
||||
Path("..") /
|
||||
".." /
|
||||
".." /
|
||||
repository_paths.archive_for(package_ahriman.base).relative_to(repository_paths.root) /
|
||||
"file.pkg.tar.xz"
|
||||
),
|
||||
MockCall(
|
||||
Path("..") /
|
||||
".." /
|
||||
".." /
|
||||
repository_paths.archive_for(package_ahriman.base).relative_to(repository_paths.root) /
|
||||
"file.pkg.tar.xz.sig"
|
||||
),
|
||||
])
|
@ -1,13 +1,28 @@
|
||||
import pytest
|
||||
|
||||
from ahriman.core.configuration import Configuration
|
||||
from ahriman.core.housekeeping import LogsRotationTrigger
|
||||
from ahriman.core.housekeeping import ArchiveRotationTrigger, LogsRotationTrigger
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def archive_rotation_trigger(configuration: Configuration) -> ArchiveRotationTrigger:
|
||||
"""
|
||||
archive rotation trigger fixture
|
||||
|
||||
Args:
|
||||
configuration(Configuration): configuration fixture
|
||||
|
||||
Returns:
|
||||
ArchiveRotationTrigger: archive rotation trigger test instance
|
||||
"""
|
||||
_, repository_id = configuration.check_loaded()
|
||||
return ArchiveRotationTrigger(repository_id, configuration)
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def logs_rotation_trigger(configuration: Configuration) -> LogsRotationTrigger:
|
||||
"""
|
||||
logs roration trigger fixture
|
||||
logs rotation trigger fixture
|
||||
|
||||
Args:
|
||||
configuration(Configuration): configuration fixture
|
||||
|
@ -0,0 +1,83 @@
|
||||
import pytest
|
||||
|
||||
from dataclasses import replace
|
||||
from pathlib import Path
|
||||
from pytest_mock import MockerFixture
|
||||
from typing import Any
|
||||
from unittest.mock import call as MockCall
|
||||
|
||||
from ahriman.core.alpm.pacman import Pacman
|
||||
from ahriman.core.configuration import Configuration
|
||||
from ahriman.core.housekeeping import ArchiveRotationTrigger
|
||||
from ahriman.models.package import Package
|
||||
from ahriman.models.result import Result
|
||||
|
||||
|
||||
def test_configuration_sections(configuration: Configuration) -> None:
|
||||
"""
|
||||
must correctly parse target list
|
||||
"""
|
||||
assert ArchiveRotationTrigger.configuration_sections(configuration) == ["archive"]
|
||||
|
||||
|
||||
def test_archives_remove(archive_rotation_trigger: ArchiveRotationTrigger, package_ahriman: Package,
|
||||
pacman: Pacman, mocker: MockerFixture) -> None:
|
||||
"""
|
||||
must remove older packages
|
||||
"""
|
||||
def package(version: Any, *args: Any, **kwargs: Any) -> Package:
|
||||
generated = replace(package_ahriman, version=str(version))
|
||||
generated.packages = {
|
||||
key: replace(value, filename=str(version))
|
||||
for key, value in generated.packages.items()
|
||||
}
|
||||
return generated
|
||||
|
||||
mocker.patch("pathlib.Path.is_dir", return_value=True)
|
||||
mocker.patch("ahriman.core.housekeeping.archive_rotation_trigger.package_like", return_value=True)
|
||||
mocker.patch("pathlib.Path.glob", return_value=[Path(str(i)) for i in range(5)])
|
||||
mocker.patch("pathlib.Path.iterdir", return_value=[Path(str(i)) for i in range(5)])
|
||||
mocker.patch("ahriman.models.package.Package.from_archive", side_effect=package)
|
||||
unlink_mock = mocker.patch("pathlib.Path.unlink", autospec=True)
|
||||
|
||||
archive_rotation_trigger.archives_remove(package_ahriman, pacman)
|
||||
unlink_mock.assert_has_calls([
|
||||
MockCall(Path("0")),
|
||||
MockCall(Path("1")),
|
||||
])
|
||||
|
||||
|
||||
def test_archives_remove_keep(archive_rotation_trigger: ArchiveRotationTrigger, package_ahriman: Package,
|
||||
pacman: Pacman, mocker: MockerFixture) -> None:
|
||||
"""
|
||||
must keep all packages if set to
|
||||
"""
|
||||
def package(version: Any, *args: Any, **kwargs: Any) -> Package:
|
||||
generated = replace(package_ahriman, version=str(version))
|
||||
generated.packages = {
|
||||
key: replace(value, filename=str(version))
|
||||
for key, value in generated.packages.items()
|
||||
}
|
||||
return generated
|
||||
|
||||
mocker.patch("pathlib.Path.is_dir", return_value=True)
|
||||
mocker.patch("ahriman.core.housekeeping.archive_rotation_trigger.package_like", return_value=True)
|
||||
mocker.patch("pathlib.Path.glob", return_value=[Path(str(i)) for i in range(5)])
|
||||
mocker.patch("pathlib.Path.iterdir", return_value=[Path(str(i)) for i in range(5)])
|
||||
mocker.patch("ahriman.models.package.Package.from_archive", side_effect=package)
|
||||
unlink_mock = mocker.patch("pathlib.Path.unlink", autospec=True)
|
||||
|
||||
archive_rotation_trigger.keep_built_packages = 0
|
||||
archive_rotation_trigger.archives_remove(package_ahriman, pacman)
|
||||
unlink_mock.assert_not_called()
|
||||
|
||||
|
||||
def test_on_result(archive_rotation_trigger: ArchiveRotationTrigger, package_ahriman: Package,
|
||||
package_python_schedule: Package, mocker: MockerFixture) -> None:
|
||||
"""
|
||||
must rotate archives
|
||||
"""
|
||||
mocker.patch("ahriman.core._Context.get")
|
||||
remove_mock = mocker.patch("ahriman.core.housekeeping.ArchiveRotationTrigger.archives_remove")
|
||||
archive_rotation_trigger.on_result(Result(added=[package_ahriman], failed=[package_python_schedule]), [])
|
||||
remove_mock.assert_called_once_with(package_ahriman, pytest.helpers.anyvar(int))
|
@ -14,7 +14,7 @@ def test_configuration_sections(configuration: Configuration) -> None:
|
||||
assert LogsRotationTrigger.configuration_sections(configuration) == ["logs-rotation"]
|
||||
|
||||
|
||||
def test_rotate(logs_rotation_trigger: LogsRotationTrigger, mocker: MockerFixture) -> None:
|
||||
def test_on_result(logs_rotation_trigger: LogsRotationTrigger, mocker: MockerFixture) -> None:
|
||||
"""
|
||||
must rotate logs
|
||||
"""
|
||||
|
@ -13,34 +13,139 @@ from ahriman.models.packagers import Packagers
|
||||
from ahriman.models.user import User
|
||||
|
||||
|
||||
def test_archive_rename(executor: Executor, package_ahriman: Package, mocker: MockerFixture) -> None:
|
||||
"""
|
||||
must correctly remove package archive
|
||||
"""
|
||||
path = "gconf-3.2.6+11+g07808097-10-x86_64.pkg.tar.zst"
|
||||
safe_path = "gconf-3.2.6-11-g07808097-10-x86_64.pkg.tar.zst"
|
||||
package_ahriman.packages[package_ahriman.base].filename = path
|
||||
rename_mock = mocker.patch("pathlib.Path.rename")
|
||||
|
||||
executor._archive_rename(package_ahriman.packages[package_ahriman.base], package_ahriman.base)
|
||||
rename_mock.assert_called_once_with(executor.paths.packages / safe_path)
|
||||
assert package_ahriman.packages[package_ahriman.base].filename == safe_path
|
||||
|
||||
|
||||
def test_archive_rename_empty_filename(executor: Executor, package_ahriman: Package, mocker: MockerFixture) -> None:
|
||||
"""
|
||||
must skip renaming if filename is not set
|
||||
"""
|
||||
package_ahriman.packages[package_ahriman.base].filename = None
|
||||
rename_mock = mocker.patch("pathlib.Path.rename")
|
||||
|
||||
executor._archive_rename(package_ahriman.packages[package_ahriman.base], package_ahriman.base)
|
||||
rename_mock.assert_not_called()
|
||||
|
||||
|
||||
def test_package_build(executor: Executor, package_ahriman: Package, mocker: MockerFixture) -> None:
|
||||
"""
|
||||
must build single package
|
||||
"""
|
||||
mocker.patch("ahriman.core.build_tools.task.Task.build", return_value=[Path(package_ahriman.base)])
|
||||
status_client_mock = mocker.patch("ahriman.core.status.Client.set_building")
|
||||
init_mock = mocker.patch("ahriman.core.build_tools.task.Task.init", return_value="sha")
|
||||
with_packages_mock = mocker.patch("ahriman.models.package.Package.with_packages")
|
||||
move_mock = mocker.patch("shutil.move")
|
||||
|
||||
assert executor._package_build(package_ahriman, Path("local"), "packager", None) == "sha"
|
||||
status_client_mock.assert_called_once_with(package_ahriman.base)
|
||||
init_mock.assert_called_once_with(pytest.helpers.anyvar(int), pytest.helpers.anyvar(int), None)
|
||||
with_packages_mock.assert_called_once_with([Path(package_ahriman.base)], executor.pacman)
|
||||
move_mock.assert_called_once_with(Path(package_ahriman.base), executor.paths.packages / package_ahriman.base)
|
||||
|
||||
|
||||
def test_package_remove(executor: Executor, package_ahriman: Package, mocker: MockerFixture) -> None:
|
||||
"""
|
||||
must run remove for packages
|
||||
"""
|
||||
repo_remove_mock = mocker.patch("ahriman.core.alpm.repo.Repo.remove")
|
||||
executor._package_remove(package_ahriman.base, package_ahriman.packages[package_ahriman.base].filepath)
|
||||
repo_remove_mock.assert_called_once_with(
|
||||
package_ahriman.base, package_ahriman.packages[package_ahriman.base].filepath)
|
||||
|
||||
|
||||
def test_package_remove_failed(executor: Executor, package_ahriman: Package, mocker: MockerFixture) -> None:
|
||||
"""
|
||||
must suppress errors during archive removal
|
||||
"""
|
||||
mocker.patch("ahriman.core.alpm.repo.Repo.remove", side_effect=Exception)
|
||||
executor._package_remove(package_ahriman.base, package_ahriman.packages[package_ahriman.base].filepath)
|
||||
|
||||
|
||||
def test_package_remove_base(executor: Executor, package_ahriman: Package, mocker: MockerFixture) -> None:
|
||||
"""
|
||||
must run remove base from status client
|
||||
"""
|
||||
status_client_mock = mocker.patch("ahriman.core.status.local_client.LocalClient.package_remove")
|
||||
executor._package_remove_base(package_ahriman.base)
|
||||
status_client_mock.assert_called_once_with(package_ahriman.base)
|
||||
|
||||
|
||||
def test_package_remove_base_failed(executor: Executor, package_ahriman: Package, mocker: MockerFixture) -> None:
|
||||
"""
|
||||
must suppress errors during base removal
|
||||
"""
|
||||
mocker.patch("ahriman.core.status.local_client.LocalClient.package_remove", side_effect=Exception)
|
||||
executor._package_remove_base(package_ahriman.base)
|
||||
|
||||
|
||||
def test_package_update(executor: Executor, package_ahriman: Package, user: User, mocker: MockerFixture) -> None:
|
||||
"""
|
||||
must update built package in repository
|
||||
"""
|
||||
rename_mock = mocker.patch("pathlib.Path.rename")
|
||||
symlink_mock = mocker.patch("pathlib.Path.symlink_to")
|
||||
repo_add_mock = mocker.patch("ahriman.core.alpm.repo.Repo.add")
|
||||
sign_package_mock = mocker.patch("ahriman.core.sign.gpg.GPG.process_sign_package", side_effect=lambda fn, _: [fn])
|
||||
filepath = next(package.filepath for package in package_ahriman.packages.values())
|
||||
|
||||
executor._package_update(filepath, package_ahriman.base, user.key)
|
||||
# must move files (once)
|
||||
rename_mock.assert_called_once_with(executor.paths.archive_for(package_ahriman.base) / filepath)
|
||||
# must sign package
|
||||
sign_package_mock.assert_called_once_with(executor.paths.packages / filepath, user.key)
|
||||
# symlink to the archive
|
||||
symlink_mock.assert_called_once_with(
|
||||
Path("..") /
|
||||
".." /
|
||||
".." /
|
||||
executor.paths.archive_for(
|
||||
package_ahriman.base).relative_to(
|
||||
executor.paths.root) /
|
||||
filepath)
|
||||
# must add package
|
||||
repo_add_mock.assert_called_once_with(executor.paths.repository / filepath)
|
||||
|
||||
|
||||
def test_package_update_empty_filename(executor: Executor, package_ahriman: Package, mocker: MockerFixture) -> None:
|
||||
"""
|
||||
must skip update for package which does not have path
|
||||
"""
|
||||
rename_mock = mocker.patch("pathlib.Path.rename")
|
||||
executor._package_update(None, package_ahriman.base, None)
|
||||
rename_mock.assert_not_called()
|
||||
|
||||
|
||||
def test_process_build(executor: Executor, package_ahriman: Package, passwd: Any, mocker: MockerFixture) -> None:
|
||||
"""
|
||||
must run build process
|
||||
"""
|
||||
mocker.patch("ahriman.models.repository_paths.getpwuid", return_value=passwd)
|
||||
mocker.patch("ahriman.core.repository.executor.Executor.packages", return_value=[package_ahriman])
|
||||
mocker.patch("ahriman.core.build_tools.task.Task.build", return_value=[Path(package_ahriman.base)])
|
||||
init_mock = mocker.patch("ahriman.core.build_tools.task.Task.init", return_value="sha")
|
||||
move_mock = mocker.patch("shutil.move")
|
||||
status_client_mock = mocker.patch("ahriman.core.status.Client.set_building")
|
||||
changes_mock = mocker.patch("ahriman.core.status.local_client.LocalClient.package_changes_get",
|
||||
return_value=Changes("commit", "change"))
|
||||
commit_sha_mock = mocker.patch("ahriman.core.status.local_client.LocalClient.package_changes_update")
|
||||
depends_on_mock = mocker.patch("ahriman.core.build_tools.package_archive.PackageArchive.depends_on",
|
||||
return_value=Dependencies())
|
||||
dependencies_mock = mocker.patch("ahriman.core.status.local_client.LocalClient.package_dependencies_update")
|
||||
with_packages_mock = mocker.patch("ahriman.models.package.Package.with_packages")
|
||||
build_mock = mocker.patch("ahriman.core.repository.executor.Executor._package_build", return_value="sha")
|
||||
|
||||
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)
|
||||
with_packages_mock.assert_called_once_with([Path(package_ahriman.base)], executor.pacman)
|
||||
changes_mock.assert_called_once_with(package_ahriman.base)
|
||||
build_mock.assert_called_once_with(package_ahriman, pytest.helpers.anyvar(Path, strict=True), None, None)
|
||||
depends_on_mock.assert_called_once_with()
|
||||
dependencies_mock.assert_called_once_with(package_ahriman.base, Dependencies())
|
||||
# must move files (once)
|
||||
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", "change"))
|
||||
|
||||
|
||||
@ -79,15 +184,15 @@ def test_process_remove_base(executor: Executor, package_ahriman: Package, mocke
|
||||
must run remove process for whole base
|
||||
"""
|
||||
mocker.patch("ahriman.core.repository.executor.Executor.packages", return_value=[package_ahriman])
|
||||
repo_remove_mock = mocker.patch("ahriman.core.alpm.repo.Repo.remove")
|
||||
status_client_mock = mocker.patch("ahriman.core.status.local_client.LocalClient.package_remove")
|
||||
remove_mock = mocker.patch("ahriman.core.repository.executor.Executor._package_remove")
|
||||
base_remove_mock = mocker.patch("ahriman.core.repository.executor.Executor._package_remove_base")
|
||||
|
||||
executor.process_remove([package_ahriman.base])
|
||||
# must remove via alpm wrapper
|
||||
repo_remove_mock.assert_called_once_with(
|
||||
remove_mock.assert_called_once_with(
|
||||
package_ahriman.base, package_ahriman.packages[package_ahriman.base].filepath)
|
||||
# must update status and remove package files
|
||||
status_client_mock.assert_called_once_with(package_ahriman.base)
|
||||
base_remove_mock.assert_called_once_with(package_ahriman.base)
|
||||
|
||||
|
||||
def test_process_remove_with_debug(executor: Executor, package_ahriman: Package, mocker: MockerFixture) -> None:
|
||||
@ -99,12 +204,12 @@ def test_process_remove_with_debug(executor: Executor, package_ahriman: Package,
|
||||
f"{package_ahriman.base}-debug": package_ahriman.packages[package_ahriman.base],
|
||||
}
|
||||
mocker.patch("ahriman.core.repository.executor.Executor.packages", return_value=[package_ahriman])
|
||||
mocker.patch("ahriman.core.status.local_client.LocalClient.package_remove")
|
||||
repo_remove_mock = mocker.patch("ahriman.core.alpm.repo.Repo.remove")
|
||||
mocker.patch("ahriman.core.repository.executor.Executor._package_remove_base")
|
||||
remove_mock = mocker.patch("ahriman.core.repository.executor.Executor._package_remove")
|
||||
|
||||
executor.process_remove([package_ahriman.base])
|
||||
# must remove via alpm wrapper
|
||||
repo_remove_mock.assert_has_calls([
|
||||
remove_mock.assert_has_calls([
|
||||
MockCall(package_ahriman.base, package_ahriman.packages[package_ahriman.base].filepath),
|
||||
MockCall(f"{package_ahriman.base}-debug", package_ahriman.packages[package_ahriman.base].filepath),
|
||||
])
|
||||
@ -116,12 +221,12 @@ def test_process_remove_base_multiple(executor: Executor, package_python_schedul
|
||||
must run remove process for whole base with multiple packages
|
||||
"""
|
||||
mocker.patch("ahriman.core.repository.executor.Executor.packages", return_value=[package_python_schedule])
|
||||
repo_remove_mock = mocker.patch("ahriman.core.alpm.repo.Repo.remove")
|
||||
status_client_mock = mocker.patch("ahriman.core.status.local_client.LocalClient.package_remove")
|
||||
remove_mock = mocker.patch("ahriman.core.repository.executor.Executor._package_remove")
|
||||
status_client_mock = mocker.patch("ahriman.core.repository.executor.Executor._package_remove_base")
|
||||
|
||||
executor.process_remove([package_python_schedule.base])
|
||||
# must remove via alpm wrapper
|
||||
repo_remove_mock.assert_has_calls([
|
||||
remove_mock.assert_has_calls([
|
||||
MockCall(package, props.filepath)
|
||||
for package, props in package_python_schedule.packages.items()
|
||||
], any_order=True)
|
||||
@ -135,45 +240,27 @@ def test_process_remove_base_single(executor: Executor, package_python_schedule:
|
||||
must run remove process for single package in base
|
||||
"""
|
||||
mocker.patch("ahriman.core.repository.executor.Executor.packages", return_value=[package_python_schedule])
|
||||
repo_remove_mock = mocker.patch("ahriman.core.alpm.repo.Repo.remove")
|
||||
status_client_mock = mocker.patch("ahriman.core.status.local_client.LocalClient.package_remove")
|
||||
remove_mock = mocker.patch("ahriman.core.repository.executor.Executor._package_remove")
|
||||
status_client_mock = mocker.patch("ahriman.core.repository.executor.Executor._package_remove_base")
|
||||
|
||||
executor.process_remove(["python2-schedule"])
|
||||
# must remove via alpm wrapper
|
||||
repo_remove_mock.assert_called_once_with(
|
||||
remove_mock.assert_called_once_with(
|
||||
"python2-schedule", package_python_schedule.packages["python2-schedule"].filepath)
|
||||
# must not update status
|
||||
status_client_mock.assert_not_called()
|
||||
|
||||
|
||||
def test_process_remove_failed(executor: Executor, package_ahriman: Package, mocker: MockerFixture) -> None:
|
||||
"""
|
||||
must suppress tree clear errors during package base removal
|
||||
"""
|
||||
mocker.patch("ahriman.core.repository.executor.Executor.packages", return_value=[package_ahriman])
|
||||
mocker.patch("ahriman.core.status.local_client.LocalClient.package_remove", side_effect=Exception)
|
||||
executor.process_remove([package_ahriman.base])
|
||||
|
||||
|
||||
def test_process_remove_tree_clear_failed(executor: Executor, package_ahriman: Package, mocker: MockerFixture) -> None:
|
||||
"""
|
||||
must suppress remove errors
|
||||
"""
|
||||
mocker.patch("ahriman.core.repository.executor.Executor.packages", return_value=[package_ahriman])
|
||||
mocker.patch("ahriman.core.alpm.repo.Repo.remove", side_effect=Exception)
|
||||
executor.process_remove([package_ahriman.base])
|
||||
|
||||
|
||||
def test_process_remove_nothing(executor: Executor, package_ahriman: Package, package_python_schedule: Package,
|
||||
mocker: MockerFixture) -> None:
|
||||
"""
|
||||
must not remove anything if it was not requested
|
||||
"""
|
||||
mocker.patch("ahriman.core.repository.executor.Executor.packages", return_value=[package_ahriman])
|
||||
repo_remove_mock = mocker.patch("ahriman.core.alpm.repo.Repo.remove")
|
||||
remove_mock = mocker.patch("ahriman.core.repository.executor.Executor._package_remove")
|
||||
|
||||
executor.process_remove([package_python_schedule.base])
|
||||
repo_remove_mock.assert_not_called()
|
||||
remove_mock.assert_not_called()
|
||||
|
||||
|
||||
def test_process_remove_unknown(executor: Executor, package_ahriman: Package, mocker: MockerFixture) -> None:
|
||||
@ -181,11 +268,11 @@ def test_process_remove_unknown(executor: Executor, package_ahriman: Package, mo
|
||||
must remove unknown package base
|
||||
"""
|
||||
mocker.patch("ahriman.core.repository.executor.Executor.packages", return_value=[])
|
||||
repo_remove_mock = mocker.patch("ahriman.core.alpm.repo.Repo.remove")
|
||||
status_client_mock = mocker.patch("ahriman.core.status.local_client.LocalClient.package_remove")
|
||||
remove_mock = mocker.patch("ahriman.core.repository.executor.Executor._package_remove")
|
||||
status_client_mock = mocker.patch("ahriman.core.repository.executor.Executor._package_remove_base")
|
||||
|
||||
executor.process_remove([package_ahriman.base])
|
||||
repo_remove_mock.assert_not_called()
|
||||
remove_mock.assert_not_called()
|
||||
status_client_mock.assert_called_once_with(package_ahriman.base)
|
||||
|
||||
|
||||
@ -195,9 +282,8 @@ def test_process_update(executor: Executor, package_ahriman: Package, user: User
|
||||
"""
|
||||
mocker.patch("ahriman.core.repository.executor.Executor.load_archives", return_value=[package_ahriman])
|
||||
mocker.patch("ahriman.core.repository.executor.Executor.packages", return_value=[package_ahriman])
|
||||
move_mock = mocker.patch("shutil.move")
|
||||
repo_add_mock = mocker.patch("ahriman.core.alpm.repo.Repo.add")
|
||||
sign_package_mock = mocker.patch("ahriman.core.sign.gpg.GPG.process_sign_package", side_effect=lambda fn, _: [fn])
|
||||
rename_mock = mocker.patch("ahriman.core.repository.executor.Executor._archive_rename")
|
||||
update_mock = mocker.patch("ahriman.core.repository.executor.Executor._package_update")
|
||||
status_client_mock = mocker.patch("ahriman.core.status.Client.set_success")
|
||||
remove_mock = mocker.patch("ahriman.core.repository.executor.Executor.process_remove")
|
||||
packager_mock = mocker.patch("ahriman.core.repository.executor.Executor.packager", return_value=user)
|
||||
@ -206,12 +292,8 @@ def test_process_update(executor: Executor, package_ahriman: Package, user: User
|
||||
# must return complete
|
||||
assert executor.process_update([filepath], Packagers("packager"))
|
||||
packager_mock.assert_called_once_with(Packagers("packager"), "ahriman")
|
||||
# must move files (once)
|
||||
move_mock.assert_called_once_with(executor.paths.packages / filepath, executor.paths.repository / filepath)
|
||||
# must sign package
|
||||
sign_package_mock.assert_called_once_with(executor.paths.packages / filepath, user.key)
|
||||
# must add package
|
||||
repo_add_mock.assert_called_once_with(executor.paths.repository / filepath)
|
||||
rename_mock.assert_called_once_with(package_ahriman.packages[package_ahriman.base], package_ahriman.base)
|
||||
update_mock.assert_called_once_with(filepath.name, package_ahriman.base, user.key)
|
||||
# must update status
|
||||
status_client_mock.assert_called_once_with(package_ahriman)
|
||||
# must clear directory
|
||||
@ -226,58 +308,26 @@ def test_process_update_group(executor: Executor, package_python_schedule: Packa
|
||||
"""
|
||||
must group single packages under one base
|
||||
"""
|
||||
mocker.patch("shutil.move")
|
||||
mocker.patch("ahriman.core.repository.executor.Executor.load_archives", return_value=[package_python_schedule])
|
||||
mocker.patch("ahriman.core.repository.executor.Executor.packages", return_value=[package_python_schedule])
|
||||
repo_add_mock = mocker.patch("ahriman.core.alpm.repo.Repo.add")
|
||||
update_mock = mocker.patch("ahriman.core.repository.executor.Executor._package_update")
|
||||
status_client_mock = mocker.patch("ahriman.core.status.Client.set_success")
|
||||
remove_mock = mocker.patch("ahriman.core.repository.executor.Executor.process_remove")
|
||||
|
||||
executor.process_update([package.filepath for package in package_python_schedule.packages.values()])
|
||||
repo_add_mock.assert_has_calls([
|
||||
MockCall(executor.paths.repository / package.filepath)
|
||||
update_mock.assert_has_calls([
|
||||
MockCall(package.filename, package_python_schedule.base, None)
|
||||
for package in package_python_schedule.packages.values()
|
||||
], any_order=True)
|
||||
status_client_mock.assert_called_once_with(package_python_schedule)
|
||||
remove_mock.assert_called_once_with([])
|
||||
|
||||
|
||||
def test_process_update_unsafe(executor: Executor, package_ahriman: Package, mocker: MockerFixture) -> None:
|
||||
"""
|
||||
must encode file name
|
||||
"""
|
||||
path = "gconf-3.2.6+11+g07808097-10-x86_64.pkg.tar.zst"
|
||||
safe_path = "gconf-3.2.6-11-g07808097-10-x86_64.pkg.tar.zst"
|
||||
package_ahriman.packages[package_ahriman.base].filename = path
|
||||
|
||||
mocker.patch("ahriman.core.repository.executor.Executor.load_archives", return_value=[package_ahriman])
|
||||
mocker.patch("ahriman.core.repository.executor.Executor.packages", return_value=[package_ahriman])
|
||||
move_mock = mocker.patch("shutil.move")
|
||||
repo_add_mock = mocker.patch("ahriman.core.alpm.repo.Repo.add")
|
||||
|
||||
executor.process_update([Path(path)])
|
||||
move_mock.assert_has_calls([
|
||||
MockCall(executor.paths.packages / path, executor.paths.packages / safe_path),
|
||||
MockCall(executor.paths.packages / safe_path, executor.paths.repository / safe_path)
|
||||
])
|
||||
repo_add_mock.assert_called_once_with(executor.paths.repository / safe_path)
|
||||
|
||||
|
||||
def test_process_empty_filename(executor: Executor, package_ahriman: Package, mocker: MockerFixture) -> None:
|
||||
"""
|
||||
must skip update for package which does not have path
|
||||
"""
|
||||
package_ahriman.packages[package_ahriman.base].filename = None
|
||||
mocker.patch("ahriman.core.repository.executor.Executor.load_archives", return_value=[package_ahriman])
|
||||
mocker.patch("ahriman.core.repository.executor.Executor.packages", return_value=[package_ahriman])
|
||||
executor.process_update([package.filepath for package in package_ahriman.packages.values()])
|
||||
|
||||
|
||||
def test_process_update_failed(executor: Executor, package_ahriman: Package, mocker: MockerFixture) -> None:
|
||||
"""
|
||||
must process update for failed package
|
||||
"""
|
||||
mocker.patch("shutil.move", side_effect=Exception)
|
||||
mocker.patch("ahriman.core.repository.executor.Executor._package_update", side_effect=Exception)
|
||||
mocker.patch("ahriman.core.repository.executor.Executor.load_archives", return_value=[package_ahriman])
|
||||
mocker.patch("ahriman.core.repository.executor.Executor.packages", return_value=[package_ahriman])
|
||||
status_client_mock = mocker.patch("ahriman.core.status.Client.set_failed")
|
||||
@ -294,8 +344,7 @@ def test_process_update_removed_package(executor: Executor, package_python_sched
|
||||
without_python2 = Package.from_json(package_python_schedule.view())
|
||||
del without_python2.packages["python2-schedule"]
|
||||
|
||||
mocker.patch("shutil.move")
|
||||
mocker.patch("ahriman.core.alpm.repo.Repo.add")
|
||||
mocker.patch("ahriman.core.repository.executor.Executor._package_update")
|
||||
mocker.patch("ahriman.core.repository.executor.Executor.load_archives", return_value=[without_python2])
|
||||
mocker.patch("ahriman.core.repository.executor.Executor.packages", return_value=[package_python_schedule])
|
||||
remove_mock = mocker.patch("ahriman.core.repository.executor.Executor.process_remove")
|
||||
|
@ -516,6 +516,15 @@ def test_build_status_pretty_print(package_ahriman: Package) -> None:
|
||||
assert isinstance(package_ahriman.pretty_print(), str)
|
||||
|
||||
|
||||
def test_vercmp(package_ahriman: Package, mocker: MockerFixture) -> None:
|
||||
"""
|
||||
must call vercmp
|
||||
"""
|
||||
vercmp_mock = mocker.patch("ahriman.models.package.vercmp")
|
||||
package_ahriman.vercmp("version")
|
||||
vercmp_mock.assert_called_once_with(package_ahriman.version, "version")
|
||||
|
||||
|
||||
def test_with_packages(package_ahriman: Package, package_python_schedule: Package, pacman: Pacman,
|
||||
mocker: MockerFixture) -> None:
|
||||
"""
|
||||
|
@ -248,6 +248,28 @@ def test_chown_invalid_path(repository_paths: RepositoryPaths) -> None:
|
||||
repository_paths._chown(repository_paths.root.parent)
|
||||
|
||||
|
||||
def test_archive_for(repository_paths: RepositoryPaths, package_ahriman: Package, mocker: MockerFixture) -> None:
|
||||
"""
|
||||
must correctly define archive path
|
||||
"""
|
||||
mocker.patch("pathlib.Path.is_dir", return_value=True)
|
||||
path = repository_paths.archive_for(package_ahriman.base)
|
||||
assert path == repository_paths.archive / "packages" / "a" / package_ahriman.base
|
||||
|
||||
|
||||
def test_archive_for_create_tree(repository_paths: RepositoryPaths, package_ahriman: Package,
|
||||
mocker: MockerFixture) -> None:
|
||||
"""
|
||||
must create archive directory if it doesn't exist
|
||||
"""
|
||||
owner_mock = mocker.patch("ahriman.models.repository_paths.RepositoryPaths.preserve_owner")
|
||||
mkdir_mock = mocker.patch("pathlib.Path.mkdir")
|
||||
|
||||
repository_paths.archive_for(package_ahriman.base)
|
||||
owner_mock.assert_called_once_with(repository_paths.archive)
|
||||
mkdir_mock.assert_called_once_with(mode=0o755, parents=True)
|
||||
|
||||
|
||||
def test_cache_for(repository_paths: RepositoryPaths, package_ahriman: Package) -> None:
|
||||
"""
|
||||
must return correct path for cache directory
|
||||
@ -287,13 +309,24 @@ def test_preserve_owner_specific(tmp_path: Path, repository_id: RepositoryId, mo
|
||||
chown_mock.assert_has_calls([MockCall(repository_paths.root / "content" / "created2")])
|
||||
|
||||
|
||||
def test_preserve_owner_no_directory(tmp_path: Path, repository_id: RepositoryId, mocker: MockerFixture) -> None:
|
||||
"""
|
||||
must skip directory scan if it does not exist
|
||||
"""
|
||||
repository_paths = RepositoryPaths(tmp_path, repository_id)
|
||||
chown_mock = mocker.patch("ahriman.models.repository_paths.RepositoryPaths._chown")
|
||||
|
||||
with repository_paths.preserve_owner(Path("empty")):
|
||||
(repository_paths.root / "created1").touch()
|
||||
chown_mock.assert_not_called()
|
||||
|
||||
|
||||
def test_tree_clear(repository_paths: RepositoryPaths, package_ahriman: Package, mocker: MockerFixture) -> None:
|
||||
"""
|
||||
must remove any package related files
|
||||
"""
|
||||
paths = {
|
||||
getattr(repository_paths, prop)(package_ahriman.base)
|
||||
for prop in dir(repository_paths) if prop.endswith("_for")
|
||||
repository_paths.cache_for(package_ahriman.base),
|
||||
}
|
||||
rmtree_mock = mocker.patch("shutil.rmtree")
|
||||
|
||||
@ -313,6 +346,7 @@ def test_tree_create(repository_paths: RepositoryPaths, mocker: MockerFixture) -
|
||||
for prop in dir(repository_paths)
|
||||
if not prop.startswith("_")
|
||||
and prop not in (
|
||||
"archive_for",
|
||||
"build_root",
|
||||
"logger_name",
|
||||
"logger",
|
||||
|
@ -10,6 +10,9 @@ root = /
|
||||
sync_files_database = no
|
||||
use_ahriman_cache = no
|
||||
|
||||
[archive]
|
||||
keep_built_packages = 3
|
||||
|
||||
[auth]
|
||||
client_id = client_id
|
||||
client_secret = client_secret
|
||||
|
Reference in New Issue
Block a user