add ability to add manually stored packages (#40)

* add ability to add manually stored packages

* update tests

* handle manual packages in remove-unknown method

* live fixes

also rename branches to has_remotes method and change return type
This commit is contained in:
2021-10-12 21:15:35 +03:00
committed by GitHub
parent ab8ca16981
commit 72b26603bf
14 changed files with 371 additions and 144 deletions

View File

@ -10,11 +10,11 @@ def test_run(args: argparse.Namespace, configuration: Configuration, mocker: Moc
"""
must run command
"""
create_tree_mock = mocker.patch("ahriman.models.repository_paths.RepositoryPaths.create_tree")
tree_create_mock = mocker.patch("ahriman.models.repository_paths.RepositoryPaths.tree_create")
init_mock = mocker.patch("ahriman.core.alpm.repo.Repo.init")
Init.run(args, "x86_64", configuration, True)
create_tree_mock.assert_called_once()
tree_create_mock.assert_called_once()
init_mock.assert_called_once()

View File

@ -1,5 +1,6 @@
import pytest
from pathlib import Path
from pytest_mock import MockerFixture
from unittest import mock
@ -104,6 +105,43 @@ def test_get_updates_with_filter(application: Application, mocker: MockerFixture
updates_manual_mock.assert_called_once()
def test_add_archive(application: Application, package_ahriman: Package, mocker: MockerFixture) -> None:
"""
must add package from archive
"""
mocker.patch("ahriman.application.application.Application._known_packages", return_value=set())
move_mock = mocker.patch("shutil.move")
application.add([package_ahriman.base], PackageSource.Archive, False)
move_mock.assert_called_once()
def test_add_remote(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)
load_mock = mocker.patch("ahriman.core.build_tools.sources.Sources.load")
application.add([package_ahriman.base], PackageSource.AUR, True)
load_mock.assert_called_once()
def test_add_remote_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.sources.Sources.load")
dependencies_mock = mocker.patch("ahriman.models.package.Package.dependencies")
application.add([package_ahriman.base], PackageSource.AUR, False)
dependencies_mock.assert_called_once()
def test_add_directory(application: Application, package_ahriman: Package, mocker: MockerFixture) -> None:
"""
must add packages from directory
@ -118,43 +156,38 @@ def test_add_directory(application: Application, package_ahriman: Package, mocke
move_mock.assert_called_once()
def test_add_manual(application: Application, package_ahriman: Package, mocker: MockerFixture) -> None:
def test_add_local(application: Application, package_ahriman: Package, mocker: MockerFixture) -> None:
"""
must add package from AUR
must add package from local sources
"""
mocker.patch("ahriman.application.application.Application._known_packages", return_value=set())
mocker.patch("ahriman.models.package.Package.load", return_value=package_ahriman)
load_mock = mocker.patch("ahriman.core.build_tools.sources.Sources.load")
init_mock = mocker.patch("ahriman.core.build_tools.sources.Sources.init")
copytree_mock = mocker.patch("shutil.copytree")
application.add([package_ahriman.base], PackageSource.AUR, True)
load_mock.assert_called_once()
application.add([package_ahriman.base], PackageSource.Local, True)
init_mock.assert_called_once()
copytree_mock.assert_has_calls([
mock.call(Path(package_ahriman.base), application.repository.paths.cache_for(package_ahriman.base)),
mock.call(application.repository.paths.cache_for(package_ahriman.base),
application.repository.paths.manual_for(package_ahriman.base)),
])
def test_add_manual_with_dependencies(application: Application, package_ahriman: Package,
mocker: MockerFixture) -> None:
def test_add_local_with_dependencies(application: Application, package_ahriman: Package, mocker: MockerFixture) -> None:
"""
must add package from AUR with dependencies
must add package from local sources 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.sources.Sources.load")
mocker.patch("ahriman.core.build_tools.sources.Sources.init")
mocker.patch("shutil.copytree")
dependencies_mock = mocker.patch("ahriman.models.package.Package.dependencies")
application.add([package_ahriman.base], PackageSource.AUR, False)
application.add([package_ahriman.base], PackageSource.Local, 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())
move_mock = mocker.patch("shutil.move")
application.add([package_ahriman.base], PackageSource.Archive, False)
move_mock.assert_called_once()
def test_clean_build(application: Application, mocker: MockerFixture) -> None:
"""
must clean build directory
@ -282,23 +315,37 @@ def test_sync(application: Application, mocker: MockerFixture) -> None:
executor_mock.assert_called_once()
def test_unknown(application: Application, package_ahriman: Package, mocker: MockerFixture) -> None:
def test_unknown_no_aur(application: Application, package_ahriman: Package, mocker: MockerFixture) -> None:
"""
must return list of packages missing in aur
must return empty list in case if there is locally stored PKGBUILD
"""
mocker.patch("ahriman.core.repository.repository.Repository.packages", return_value=[package_ahriman])
mocker.patch("ahriman.models.package.Package.from_aur", side_effect=Exception())
mocker.patch("pathlib.Path.is_dir", return_value=True)
mocker.patch("ahriman.core.build_tools.sources.Sources.has_remotes", return_value=False)
assert not application.unknown()
def test_unknown_no_aur_no_local(application: Application, package_ahriman: Package, mocker: MockerFixture) -> None:
"""
must return list of packages missing in aur and in local storage
"""
mocker.patch("ahriman.core.repository.repository.Repository.packages", return_value=[package_ahriman])
mocker.patch("ahriman.models.package.Package.from_aur", side_effect=Exception())
mocker.patch("pathlib.Path.is_dir", return_value=False)
packages = application.unknown()
assert packages == [package_ahriman]
def test_unknown_empty(application: Application, package_ahriman: Package, mocker: MockerFixture) -> None:
def test_unknown_no_local(application: Application, package_ahriman: Package, mocker: MockerFixture) -> None:
"""
must return list of packages missing in aur
must return empty list in case if there is package in AUR
"""
mocker.patch("ahriman.core.repository.repository.Repository.packages", return_value=[package_ahriman])
mocker.patch("ahriman.models.package.Package.from_aur")
mocker.patch("pathlib.Path.is_dir", return_value=False)
assert not application.unknown()

View File

@ -35,19 +35,34 @@ def test_diff(mocker: MockerFixture) -> None:
check_output_mock.assert_called_with("git", "diff", exception=None, cwd=local, logger=pytest.helpers.anyvar(int))
def test_fetch_existing(mocker: MockerFixture) -> None:
def test_fetch_empty(mocker: MockerFixture) -> None:
"""
must fetch new package via clone command
must do nothing in case if no branches available
"""
mocker.patch("pathlib.Path.is_dir", return_value=True)
mocker.patch("ahriman.core.build_tools.sources.Sources.has_remotes", return_value=False)
check_output_mock = mocker.patch("ahriman.core.build_tools.sources.Sources._check_output")
Sources.fetch(Path("local"), "remote")
check_output_mock.assert_not_called()
def test_fetch_existing(mocker: MockerFixture) -> None:
"""
must fetch new package via fetch command
"""
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.Sources._check_output")
local = Path("local")
Sources.fetch(local, "remote", "master")
Sources.fetch(local, "remote")
check_output_mock.assert_has_calls([
mock.call("git", "fetch", "origin", "master", exception=None, cwd=local, logger=pytest.helpers.anyvar(int)),
mock.call("git", "checkout", "--force", "master", exception=None, cwd=local, logger=pytest.helpers.anyvar(int)),
mock.call("git", "reset", "--hard", "origin/master",
mock.call("git", "fetch", "origin", Sources._branch,
exception=None, cwd=local, logger=pytest.helpers.anyvar(int)),
mock.call("git", "checkout", "--force", Sources._branch,
exception=None, cwd=local, logger=pytest.helpers.anyvar(int)),
mock.call("git", "reset", "--hard", f"origin/{Sources._branch}",
exception=None, cwd=local, logger=pytest.helpers.anyvar(int))
])
@ -60,15 +75,47 @@ def test_fetch_new(mocker: MockerFixture) -> None:
check_output_mock = mocker.patch("ahriman.core.build_tools.sources.Sources._check_output")
local = Path("local")
Sources.fetch(local, "remote", "master")
Sources.fetch(local, "remote")
check_output_mock.assert_has_calls([
mock.call("git", "clone", "remote", str(local), exception=None, logger=pytest.helpers.anyvar(int)),
mock.call("git", "checkout", "--force", "master", exception=None, cwd=local, logger=pytest.helpers.anyvar(int)),
mock.call("git", "reset", "--hard", "origin/master",
mock.call("git", "checkout", "--force", Sources._branch,
exception=None, cwd=local, logger=pytest.helpers.anyvar(int)),
mock.call("git", "reset", "--hard", f"origin/{Sources._branch}",
exception=None, cwd=local, logger=pytest.helpers.anyvar(int))
])
def test_has_remotes(mocker: MockerFixture) -> None:
"""
must ask for remotes
"""
check_output_mock = mocker.patch("ahriman.core.build_tools.sources.Sources._check_output", return_value="origin")
local = Path("local")
assert Sources.has_remotes(local)
check_output_mock.assert_called_with("git", "remote", exception=None, cwd=local, logger=pytest.helpers.anyvar(int))
def test_has_remotes_empty(mocker: MockerFixture) -> None:
"""
must ask for remotes and return false in case if no remotes found
"""
mocker.patch("ahriman.core.build_tools.sources.Sources._check_output", return_value="")
assert not Sources.has_remotes(Path("local"))
def test_init(mocker: MockerFixture) -> None:
"""
must create empty repository at the specified path
"""
check_output_mock = mocker.patch("ahriman.core.build_tools.sources.Sources._check_output")
local = Path("local")
Sources.init(local)
check_output_mock.assert_called_with("git", "init", "--initial-branch", Sources._branch,
exception=None, cwd=local, logger=pytest.helpers.anyvar(int))
def test_load(mocker: MockerFixture) -> None:
"""
must load packages sources correctly

View File

@ -60,13 +60,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])
tree_clear_mock = mocker.patch("ahriman.models.repository_paths.RepositoryPaths.tree_clear")
repo_remove_mock = mocker.patch("ahriman.core.alpm.repo.Repo.remove")
status_client_mock = mocker.patch("ahriman.core.status.client.Client.remove")
executor.process_remove([package_ahriman.base])
# must remove via alpm wrapper
repo_remove_mock.assert_called_once()
# must update status
# must update status and remove package files
tree_clear_mock.assert_called_with(package_ahriman.base)
status_client_mock.assert_called_once()
@ -106,6 +108,15 @@ def test_process_remove_base_single(executor: Executor, package_python_schedule:
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.models.repository_paths.RepositoryPaths.tree_clear", 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
"""

View File

@ -9,17 +9,17 @@ def test_create_tree_on_load(configuration: Configuration, mocker: MockerFixture
"""
must create tree on load
"""
create_tree_mock = mocker.patch("ahriman.models.repository_paths.RepositoryPaths.create_tree")
tree_create_mock = mocker.patch("ahriman.models.repository_paths.RepositoryPaths.tree_create")
Properties("x86_64", configuration, True)
create_tree_mock.assert_called_once()
tree_create_mock.assert_called_once()
def test_create_dummy_report_client(configuration: Configuration, mocker: MockerFixture) -> None:
"""
must create dummy report client if report is disabled
"""
mocker.patch("ahriman.models.repository_paths.RepositoryPaths.create_tree")
mocker.patch("ahriman.models.repository_paths.RepositoryPaths.tree_create")
load_mock = mocker.patch("ahriman.core.status.client.Client.load")
properties = Properties("x86_64", configuration, True)
@ -31,7 +31,7 @@ def test_create_full_report_client(configuration: Configuration, mocker: MockerF
"""
must create load report client if report is enabled
"""
mocker.patch("ahriman.models.repository_paths.RepositoryPaths.create_tree")
mocker.patch("ahriman.models.repository_paths.RepositoryPaths.tree_create")
load_mock = mocker.patch("ahriman.core.status.client.Client.load")
Properties("x86_64", configuration, False)

View File

@ -1,8 +1,21 @@
from pytest_mock import MockerFixture
from pathlib import Path
from typing import Callable
from ahriman.models.package_source import PackageSource
def _is_file_mock(is_any_file: bool, is_pkgbuild: bool) -> Callable[[Path], bool]:
"""
helper to mock is_file method
:param is_any_file: value which will be return for any file
:param is_pkgbuild: value which will be return if PKGBUILD like path asked
:return: side effect function for the mocker object
"""
side_effect: Callable[[Path], bool] = lambda source: is_pkgbuild if source.name == "PKGBUILD" else is_any_file
return side_effect
def test_resolve_non_auto() -> None:
"""
must resolve non auto type to itself
@ -16,19 +29,10 @@ def test_resolve_archive(mocker: MockerFixture) -> None:
must resolve auto type into the archive
"""
mocker.patch("pathlib.Path.is_dir", return_value=False)
mocker.patch("pathlib.Path.is_file", return_value=True)
mocker.patch("pathlib.Path.is_file", autospec=True, side_effect=_is_file_mock(True, False))
assert PackageSource.Auto.resolve("linux-5.14.2.arch1-2-x86_64.pkg.tar.zst") == PackageSource.Archive
def test_resolve_directory(mocker: MockerFixture) -> None:
"""
must resolve auto type into the directory
"""
mocker.patch("pathlib.Path.is_dir", return_value=True)
mocker.patch("pathlib.Path.is_file", return_value=False)
assert PackageSource.Auto.resolve("path") == PackageSource.Directory
def test_resolve_aur(mocker: MockerFixture) -> None:
"""
must resolve auto type into the AUR package
@ -43,5 +47,23 @@ def test_resolve_aur_not_package_like(mocker: MockerFixture) -> None:
must resolve auto type into the AUR package if it is file, but does not look like a package archive
"""
mocker.patch("pathlib.Path.is_dir", return_value=False)
mocker.patch("pathlib.Path.is_file", return_value=True)
mocker.patch("pathlib.Path.is_file", autospec=True, side_effect=_is_file_mock(True, False))
assert PackageSource.Auto.resolve("package") == PackageSource.AUR
def test_resolve_directory(mocker: MockerFixture) -> None:
"""
must resolve auto type into the directory
"""
mocker.patch("pathlib.Path.is_dir", return_value=True)
mocker.patch("pathlib.Path.is_file", return_value=False)
assert PackageSource.Auto.resolve("path") == PackageSource.Directory
def test_resolve_local(mocker: MockerFixture) -> None:
"""
must resolve auto type into the directory
"""
mocker.patch("pathlib.Path.is_dir", return_value=False)
mocker.patch("pathlib.Path.is_file", autospec=True, side_effect=_is_file_mock(True, True))
assert PackageSource.Auto.resolve("path") == PackageSource.Local

View File

@ -23,27 +23,6 @@ def test_cache_for(repository_paths: RepositoryPaths, package_ahriman: Package)
assert path.parent == repository_paths.cache
def test_create_tree(repository_paths: RepositoryPaths, mocker: MockerFixture) -> None:
"""
must create whole tree
"""
paths = {
prop
for prop in dir(repository_paths)
if not prop.startswith("_")
and not prop.endswith("_for")
and prop not in ("architecture", "create_tree", "known_architectures", "root")
}
mkdir_mock = mocker.patch("pathlib.Path.mkdir")
repository_paths.create_tree()
mkdir_mock.assert_has_calls(
[
mock.call(mode=0o755, parents=True, exist_ok=True)
for _ in paths
])
def test_manual_for(repository_paths: RepositoryPaths, package_ahriman: Package) -> None:
"""
must return correct path for manual directory
@ -69,3 +48,41 @@ def test_sources_for(repository_paths: RepositoryPaths, package_ahriman: Package
path = repository_paths.sources_for(package_ahriman.base)
assert path.name == package_ahriman.base
assert path.parent == repository_paths.sources
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")
}
rmtree_mock = mocker.patch("shutil.rmtree")
repository_paths.tree_clear(package_ahriman.base)
rmtree_mock.assert_has_calls(
[
mock.call(path, ignore_errors=True) for path in paths
], any_order=True)
def test_tree_create(repository_paths: RepositoryPaths, mocker: MockerFixture) -> None:
"""
must create whole tree
"""
paths = {
prop
for prop in dir(repository_paths)
if not prop.startswith("_")
and not prop.endswith("_for")
and prop not in ("architecture", "known_architectures", "root", "tree_clear", "tree_create")
}
mkdir_mock = mocker.patch("pathlib.Path.mkdir")
repository_paths.tree_create()
mkdir_mock.assert_has_calls(
[
mock.call(mode=0o755, parents=True, exist_ok=True)
for _ in paths
], any_order=True)