From 51c4160247df1216273af02feb42b8a0f81dc81e Mon Sep 17 00:00:00 2001 From: Evgeniy Alekseev Date: Sun, 28 Mar 2021 04:35:01 +0300 Subject: [PATCH] application tests --- src/ahriman/application/application.py | 10 +- src/ahriman/application/lock.py | 4 +- src/ahriman/models/package.py | 2 +- tests/ahriman/application/conftest.py | 20 ++ tests/ahriman/application/test_ahriman.py | 0 tests/ahriman/application/test_application.py | 237 ++++++++++++++++++ tests/ahriman/application/test_lock.py | 151 +++++++++++ tests/ahriman/core/alpm/test_repo.py | 4 +- tests/ahriman/core/build_tools/test_task.py | 4 +- tests/ahriman/models/test_repository_paths.py | 5 +- 10 files changed, 421 insertions(+), 16 deletions(-) create mode 100644 tests/ahriman/application/conftest.py create mode 100644 tests/ahriman/application/test_ahriman.py create mode 100644 tests/ahriman/application/test_application.py create mode 100644 tests/ahriman/application/test_lock.py diff --git a/src/ahriman/application/application.py b/src/ahriman/application/application.py index 76dd36ea..f7bc0648 100644 --- a/src/ahriman/application/application.py +++ b/src/ahriman/application/application.py @@ -105,8 +105,8 @@ class Application: for full_path in filter(package_like, path.iterdir()): add_archive(full_path) - def add_manual(name: str) -> Path: - package = Package.load(name, self.repository.pacman, self.config.get("alpm", "aur_url")) + def add_manual(src: str) -> Path: + package = Package.load(src, self.repository.pacman, self.config.get("alpm", "aur_url")) path = self.repository.paths.manual / package.base Task.fetch(path, package.git_url) return path @@ -121,14 +121,14 @@ class Application: dependencies = Package.dependencies(path) self.add(dependencies.difference(known_packages), without_dependencies) - def process_single(name: str) -> None: - maybe_path = Path(name) + def process_single(src: str) -> None: + maybe_path = Path(src) if maybe_path.is_dir(): add_directory(maybe_path) elif maybe_path.is_file(): add_archive(maybe_path) else: - path = add_manual(name) + path = add_manual(src) process_dependencies(path) for name in names: diff --git a/src/ahriman/application/lock.py b/src/ahriman/application/lock.py index 4dcaf336..8c00cd83 100644 --- a/src/ahriman/application/lock.py +++ b/src/ahriman/application/lock.py @@ -67,8 +67,6 @@ class Lock: report to web if enabled """ self.check_user() - if self.force: - self.remove() self.create() self.reporter.update_self(BuildStatusEnum.Building) return self @@ -105,7 +103,7 @@ class Lock: if self.path is None: return try: - self.path.touch(exist_ok=False) + self.path.touch(exist_ok=self.force) except FileExistsError: raise DuplicateRun() diff --git a/src/ahriman/models/package.py b/src/ahriman/models/package.py index 8374cd65..3b162d14 100644 --- a/src/ahriman/models/package.py +++ b/src/ahriman/models/package.py @@ -151,7 +151,7 @@ class Package: """ # additional function to remove versions from dependencies def trim_version(name: str) -> str: - for symbol in ('<', '=', '>'): + for symbol in ("<", "=", ">"): name = name.split(symbol)[0] return name diff --git a/tests/ahriman/application/conftest.py b/tests/ahriman/application/conftest.py new file mode 100644 index 00000000..18009794 --- /dev/null +++ b/tests/ahriman/application/conftest.py @@ -0,0 +1,20 @@ +import argparse +import pytest + +from pytest_mock import MockerFixture + +from ahriman.application.application import Application +from ahriman.application.lock import Lock +from ahriman.core.configuration import Configuration + + +@pytest.fixture +def application(configuration: Configuration, mocker: MockerFixture) -> Application: + mocker.patch("pathlib.Path.mkdir") + return Application("x86_64", configuration) + + +@pytest.fixture +def lock(configuration: Configuration) -> Lock: + return Lock(argparse.Namespace(lock=None, force=False, unsafe=False, no_report=True), + "x86_64", configuration) diff --git a/tests/ahriman/application/test_ahriman.py b/tests/ahriman/application/test_ahriman.py new file mode 100644 index 00000000..e69de29b diff --git a/tests/ahriman/application/test_application.py b/tests/ahriman/application/test_application.py new file mode 100644 index 00000000..752538de --- /dev/null +++ b/tests/ahriman/application/test_application.py @@ -0,0 +1,237 @@ +from pytest_mock import MockerFixture +from unittest import mock + +from ahriman.application.application import Application +from ahriman.core.tree import Leaf, Tree +from ahriman.models.package import Package + + +def test_finalize(application: Application, mocker: MockerFixture) -> None: + """ + must report and sync at the last + """ + report_mock = mocker.patch("ahriman.application.application.Application.report") + sync_mock = mocker.patch("ahriman.application.application.Application.sync") + + application._finalize() + report_mock.assert_called_once() + sync_mock.assert_called_once() + + +def test_get_updates_all(application: Application, mocker: MockerFixture) -> None: + """ + must get updates for all + """ + updates_aur_mock = mocker.patch("ahriman.core.repository.update_handler.UpdateHandler.updates_aur") + updates_manual_mock = mocker.patch("ahriman.core.repository.update_handler.UpdateHandler.updates_manual") + + application.get_updates([], no_aur=False, no_manual=False, no_vcs=False, log_fn=print) + updates_aur_mock.assert_called_with([], False) + updates_manual_mock.assert_called_once() + + +def test_get_updates_disabled(application: Application, mocker: MockerFixture) -> None: + """ + must get updates without anything + """ + updates_aur_mock = mocker.patch("ahriman.core.repository.update_handler.UpdateHandler.updates_aur") + updates_manual_mock = mocker.patch("ahriman.core.repository.update_handler.UpdateHandler.updates_manual") + + application.get_updates([], no_aur=True, no_manual=True, no_vcs=False, log_fn=print) + updates_aur_mock.assert_not_called() + updates_manual_mock.assert_not_called() + + +def test_get_updates_no_aur(application: Application, mocker: MockerFixture) -> None: + """ + must get updates without aur + """ + updates_aur_mock = mocker.patch("ahriman.core.repository.update_handler.UpdateHandler.updates_aur") + updates_manual_mock = mocker.patch("ahriman.core.repository.update_handler.UpdateHandler.updates_manual") + + application.get_updates([], no_aur=True, no_manual=False, no_vcs=False, log_fn=print) + updates_aur_mock.assert_not_called() + updates_manual_mock.assert_called_once() + + +def test_get_updates_no_manual(application: Application, mocker: MockerFixture) -> None: + """ + must get updates without manual + """ + updates_aur_mock = mocker.patch("ahriman.core.repository.update_handler.UpdateHandler.updates_aur") + updates_manual_mock = mocker.patch("ahriman.core.repository.update_handler.UpdateHandler.updates_manual") + + application.get_updates([], no_aur=False, no_manual=True, no_vcs=False, log_fn=print) + updates_aur_mock.assert_called_with([], False) + updates_manual_mock.assert_not_called() + + +def test_get_updates_no_vcs(application: Application, mocker: MockerFixture) -> None: + """ + must get updates without VCS + """ + updates_aur_mock = mocker.patch("ahriman.core.repository.update_handler.UpdateHandler.updates_aur") + updates_manual_mock = mocker.patch("ahriman.core.repository.update_handler.UpdateHandler.updates_manual") + + application.get_updates([], no_aur=False, no_manual=False, no_vcs=True, log_fn=print) + updates_aur_mock.assert_called_with([], True) + updates_manual_mock.assert_called_once() + + +def test_get_updates_with_filter(application: Application, mocker: MockerFixture) -> None: + """ + must get updates without VCS + """ + updates_aur_mock = mocker.patch("ahriman.core.repository.update_handler.UpdateHandler.updates_aur") + updates_manual_mock = mocker.patch("ahriman.core.repository.update_handler.UpdateHandler.updates_manual") + + application.get_updates(["filter"], no_aur=False, no_manual=False, no_vcs=False, log_fn=print) + updates_aur_mock.assert_called_with(["filter"], False) + updates_manual_mock.assert_called_once() + + +def test_add_directory(application: Application, package_ahriman: Package, mocker: MockerFixture) -> None: + """ + must add packages from directory + """ + mocker.patch("ahriman.application.application.Application._known_packages", return_value=set()) + mocker.patch("pathlib.Path.is_dir", return_value=True) + iterdir_mock = mocker.patch("pathlib.Path.iterdir", + return_value=[package.filepath for package in package_ahriman.packages.values()]) + move_mock = mocker.patch("shutil.move") + + application.add([package_ahriman.base], False) + iterdir_mock.assert_called_once() + move_mock.assert_called_once() + + +def test_add_manual(application: Application, package_ahriman: Package, mocker: MockerFixture) -> None: + """ + must add package from AUR + """ + mocker.patch("ahriman.application.application.Application._known_packages", return_value=set()) + mocker.patch("ahriman.models.package.Package.load", return_value=package_ahriman) + fetch_mock = mocker.patch("ahriman.core.build_tools.task.Task.fetch") + + application.add([package_ahriman.base], True) + fetch_mock.assert_called_once() + + +def test_add_manual_with_dependencies(application: Application, package_ahriman: Package, + mocker: MockerFixture) -> None: + """ + must add package from AUR with dependencies + """ + mocker.patch("ahriman.application.application.Application._known_packages", return_value=set()) + mocker.patch("ahriman.models.package.Package.load", return_value=package_ahriman) + mocker.patch("ahriman.core.build_tools.task.Task.fetch") + dependencies_mock = mocker.patch("ahriman.models.package.Package.dependencies") + + application.add([package_ahriman.base], False) + dependencies_mock.assert_called_once() + + +def test_add_package(application: Application, package_ahriman: Package, mocker: MockerFixture) -> None: + """ + must add package from archive + """ + mocker.patch("ahriman.application.application.Application._known_packages", return_value=set()) + mocker.patch("pathlib.Path.is_file", return_value=True) + move_mock = mocker.patch("shutil.move") + + application.add([package_ahriman.base], False) + move_mock.assert_called_once() + + +def test_clean_build(application: Application, mocker: MockerFixture) -> None: + """ + must clean build directory + """ + clear_mock = mocker.patch("ahriman.core.repository.cleaner.Cleaner.clear_build") + application.clean(False, True, True, True, True) + clear_mock.assert_called_once() + + +def test_clean_cache(application: Application, mocker: MockerFixture) -> None: + """ + must clean cache directory + """ + clear_mock = mocker.patch("ahriman.core.repository.cleaner.Cleaner.clear_cache") + application.clean(True, False, True, True, True) + clear_mock.assert_called_once() + + +def test_clean_chroot(application: Application, mocker: MockerFixture) -> None: + """ + must clean chroot directory + """ + clear_mock = mocker.patch("ahriman.core.repository.cleaner.Cleaner.clear_chroot") + application.clean(True, True, False, True, True) + clear_mock.assert_called_once() + + +def test_clean_manual(application: Application, mocker: MockerFixture) -> None: + """ + must clean manual directory + """ + clear_mock = mocker.patch("ahriman.core.repository.cleaner.Cleaner.clear_manual") + application.clean(True, True, True, False, True) + clear_mock.assert_called_once() + + +def test_clean_packages(application: Application, mocker: MockerFixture) -> None: + """ + must clean packages directory + """ + clear_mock = mocker.patch("ahriman.core.repository.cleaner.Cleaner.clear_packages") + application.clean(True, True, True, True, False) + clear_mock.assert_called_once() + + +def test_remove(application: Application, mocker: MockerFixture) -> None: + """ + must remove package + """ + executor_mock = mocker.patch("ahriman.core.repository.executor.Executor.process_remove") + finalize_mock = mocker.patch("ahriman.application.application.Application._finalize") + + application.remove([]) + executor_mock.assert_called_once() + finalize_mock.assert_called_once() + + +def test_report(application: Application, mocker: MockerFixture) -> None: + """ + must generate report + """ + executor_mock = mocker.patch("ahriman.core.repository.executor.Executor.process_report") + application.report(None) + executor_mock.assert_called_once() + + +def test_sync(application: Application, mocker: MockerFixture) -> None: + """ + must sync to remote + """ + executor_mock = mocker.patch("ahriman.core.repository.executor.Executor.process_sync") + application.sync(None) + executor_mock.assert_called_once() + + +def test_update(application: Application, package_ahriman: Package, mocker: MockerFixture) -> None: + """ + must process package updates + """ + paths = [package.filepath for package in package_ahriman.packages.values()] + tree = Tree([Leaf(package_ahriman, set())]) + + mocker.patch("ahriman.core.tree.Tree.load", return_value=tree) + mocker.patch("ahriman.core.repository.repository.Repository.packages_built", return_value=[]) + build_mock = mocker.patch("ahriman.core.repository.executor.Executor.process_build", return_value=paths) + update_mock = mocker.patch("ahriman.core.repository.executor.Executor.process_update") + finalize_mock = mocker.patch("ahriman.application.application.Application._finalize") + + application.update([package_ahriman]) + build_mock.assert_called_once() + update_mock.assert_has_calls([mock.call([]), mock.call(paths)]) + finalize_mock.assert_has_calls([mock.call(), mock.call()]) diff --git a/tests/ahriman/application/test_lock.py b/tests/ahriman/application/test_lock.py new file mode 100644 index 00000000..c3573558 --- /dev/null +++ b/tests/ahriman/application/test_lock.py @@ -0,0 +1,151 @@ +import pytest +import tempfile + +from pathlib import Path +from pytest_mock import MockerFixture +from unittest import mock + +from ahriman.application.lock import Lock +from ahriman.core.exceptions import DuplicateRun, UnsafeRun +from ahriman.models.build_status import BuildStatusEnum + + +def test_enter(lock: Lock, mocker: MockerFixture) -> None: + """ + must process with context manager + """ + check_user_mock = mocker.patch("ahriman.application.lock.Lock.check_user") + remove_mock = mocker.patch("ahriman.application.lock.Lock.remove") + create_mock = mocker.patch("ahriman.application.lock.Lock.create") + update_status_mock = mocker.patch("ahriman.core.status.client.Client.update_self") + + with lock: + pass + check_user_mock.assert_called_once() + remove_mock.assert_called_once() + create_mock.assert_called_once() + update_status_mock.assert_has_calls([ + mock.call(BuildStatusEnum.Building), + mock.call(BuildStatusEnum.Success) + ]) + + +def test_exit_with_exception(lock: Lock, mocker: MockerFixture) -> None: + """ + must process with context manager in case if exception raised + """ + mocker.patch("ahriman.application.lock.Lock.check_user") + mocker.patch("ahriman.application.lock.Lock.remove") + mocker.patch("ahriman.application.lock.Lock.create") + update_status_mock = mocker.patch("ahriman.core.status.client.Client.update_self") + + with pytest.raises(Exception): + with lock: + raise Exception() + update_status_mock.assert_has_calls([ + mock.call(BuildStatusEnum.Building), + mock.call(BuildStatusEnum.Failed) + ]) + + +def test_check_user(lock: Lock, mocker: MockerFixture) -> None: + """ + must check user correctly + """ + stat = Path.cwd().stat() + mocker.patch("pathlib.Path.stat", return_value=stat) + mocker.patch("os.getuid", return_value=stat.st_uid) + + lock.check_user() + + +def test_check_user_exception(lock: Lock, mocker: MockerFixture) -> None: + """ + must raise exception if user differs + """ + stat = Path.cwd().stat() + mocker.patch("pathlib.Path.stat") + mocker.patch("os.getuid", return_value=stat.st_uid + 1) + + with pytest.raises(UnsafeRun): + lock.check_user() + + +def test_check_user_unsafe(lock: Lock) -> None: + """ + must skip user check if unsafe flag set + """ + lock.unsafe = True + lock.check_user() + + +def test_create(lock: Lock) -> None: + """ + must create lock + """ + lock.path = Path(tempfile.mktemp()) + + lock.create() + assert lock.path.is_file() + lock.path.unlink() + + +def test_create_exception(lock: Lock) -> None: + """ + must raise exception if file already exists + """ + lock.path = Path(tempfile.mktemp()) + lock.path.touch() + + with pytest.raises(DuplicateRun): + lock.create() + lock.path.unlink() + + +def test_create_skip(lock: Lock, mocker: MockerFixture) -> None: + """ + must skip creating if no file set + """ + touch_mock = mocker.patch("pathlib.Path.touch") + lock.create() + touch_mock.assert_not_called() + + +def test_create_unsafe(lock: Lock) -> None: + """ + must not raise exception if force flag set + """ + lock.force = True + lock.path = Path(tempfile.mktemp()) + lock.path.touch() + + lock.create() + lock.path.unlink() + + +def test_remove(lock: Lock) -> None: + """ + must remove lock file + """ + lock.path = Path(tempfile.mktemp()) + lock.path.touch() + + lock.remove() + assert not lock.path.is_file() + + +def test_remove_missing(lock: Lock) -> None: + """ + must not fail on lock removal if file is missing + """ + lock.path = Path(tempfile.mktemp()) + lock.remove() + + +def test_remove_skip(lock: Lock, mocker: MockerFixture) -> None: + """ + must skip removal if no file set + """ + unlink_mock = mocker.patch("pathlib.Path.unlink") + lock.remove() + unlink_mock.assert_not_called() diff --git a/tests/ahriman/core/alpm/test_repo.py b/tests/ahriman/core/alpm/test_repo.py index 480c082d..e2b7a48f 100644 --- a/tests/ahriman/core/alpm/test_repo.py +++ b/tests/ahriman/core/alpm/test_repo.py @@ -20,7 +20,7 @@ def test_repo_add(repo: Repo, mocker: MockerFixture) -> None: check_output_mock = mocker.patch("ahriman.core.alpm.repo.Repo._check_output") repo.add(Path("path")) - Repo._check_output.assert_called_once() + check_output_mock.assert_called_once() assert check_output_mock.call_args[0][0] == "repo-add" @@ -32,7 +32,7 @@ def test_repo_remove(repo: Repo, mocker: MockerFixture) -> None: check_output_mock = mocker.patch("ahriman.core.alpm.repo.Repo._check_output") repo.remove("package", Path("package.pkg.tar.xz")) - Repo._check_output.assert_called_once() + check_output_mock.assert_called_once() assert check_output_mock.call_args[0][0] == "repo-remove" diff --git a/tests/ahriman/core/build_tools/test_task.py b/tests/ahriman/core/build_tools/test_task.py index 7a142112..42b30fb4 100644 --- a/tests/ahriman/core/build_tools/test_task.py +++ b/tests/ahriman/core/build_tools/test_task.py @@ -57,8 +57,8 @@ def test_init_with_cache(task_ahriman: Task, mocker: MockerFixture) -> None: must copy tree instead of fetch """ mocker.patch("pathlib.Path.is_dir", return_value=True) - mocker.patch("shutil.copytree") mocker.patch("ahriman.core.build_tools.task.Task.fetch") + copytree_mock = mocker.patch("shutil.copytree") task_ahriman.init(None) - shutil.copytree.assert_called_once() + copytree_mock.assert_called_once() diff --git a/tests/ahriman/models/test_repository_paths.py b/tests/ahriman/models/test_repository_paths.py index 99730a3a..4f9226fd 100644 --- a/tests/ahriman/models/test_repository_paths.py +++ b/tests/ahriman/models/test_repository_paths.py @@ -1,4 +1,3 @@ -from pathlib import Path from pytest_mock import MockerFixture from unittest import mock @@ -14,10 +13,10 @@ def test_create_tree(repository_paths: RepositoryPaths, mocker: MockerFixture) - for prop in dir(repository_paths) if not prop.startswith("_") and prop not in ("architecture", "create_tree", "root") } - mocker.patch("pathlib.Path.mkdir") + mkdir_mock = mocker.patch("pathlib.Path.mkdir") repository_paths.create_tree() - Path.mkdir.assert_has_calls( + mkdir_mock.assert_has_calls( [ mock.call(mode=0o755, parents=True, exist_ok=True) for _ in paths