add migration docs and ability to migrate tree automatically

This commit is contained in:
2023-09-01 16:52:21 +03:00
committed by Evgenii Alekseev
parent 384ef189c4
commit 406162a489
24 changed files with 469 additions and 131 deletions

View File

@ -21,8 +21,8 @@ def test_call(args: argparse.Namespace, configuration: Configuration, mocker: Mo
args.report = False
mocker.patch("ahriman.application.handlers.Handler.run")
configuration_mock = mocker.patch("ahriman.core.configuration.Configuration.from_path", return_value=configuration)
log_handler_mock = mocker.patch("ahriman.core.log.Log.handler", return_value=args.log_handler)
log_load_mock = mocker.patch("ahriman.core.log.Log.load")
log_handler_mock = mocker.patch("ahriman.core.log.log_loader.LogLoader.handler", return_value=args.log_handler)
log_load_mock = mocker.patch("ahriman.core.log.log_loader.LogLoader.load")
enter_mock = mocker.patch("ahriman.application.lock.Lock.__enter__")
exit_mock = mocker.patch("ahriman.application.lock.Lock.__exit__")

View File

@ -0,0 +1,42 @@
import argparse
from pathlib import Path
from pytest_mock import MockerFixture
from unittest.mock import call as MockCall
from ahriman.application.handlers import TreeMigrate
from ahriman.core.configuration import Configuration
from ahriman.models.repository_id import RepositoryId
from ahriman.models.repository_paths import RepositoryPaths
def test_run(args: argparse.Namespace, configuration: Configuration, mocker: MockerFixture) -> None:
"""
must run command
"""
tree_create_mock = mocker.patch("ahriman.models.repository_paths.RepositoryPaths.tree_create")
application_mock = mocker.patch("ahriman.application.handlers.TreeMigrate.tree_move")
_, repository_id = configuration.check_loaded()
old_paths = configuration.repository_paths
new_paths = RepositoryPaths(old_paths.root, old_paths.repository_id, _force_current_tree=True)
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)
def test_move_tree(mocker: MockerFixture) -> None:
"""
must move tree
"""
rename_mock = mocker.patch("pathlib.Path.rename", autospec=True)
root = Path("local")
from_paths = RepositoryPaths(root, RepositoryId("arch", ""))
to_paths = RepositoryPaths(root, RepositoryId("arch", "repo"))
TreeMigrate.tree_move(from_paths, to_paths)
rename_mock.assert_has_calls([
MockCall(from_paths.packages, to_paths.packages),
MockCall(from_paths.pacman, to_paths.pacman),
MockCall(from_paths.repository, to_paths.repository),
])

View File

@ -93,14 +93,14 @@ def test_parser_option_repository_multiple(parser: argparse.ArgumentParser) -> N
def test_subparsers_aur_search(parser: argparse.ArgumentParser) -> None:
"""
aur-search command must imply architecture list, lock, report, repository, quiet and unsafe
aur-search command must imply architecture list, lock, quiet, report, repository and unsafe
"""
args = parser.parse_args(["aur-search", "ahriman"])
assert args.architecture == [""]
assert args.lock is None
assert args.quiet
assert not args.report
assert args.repository == [""]
assert args.quiet
assert args.unsafe
@ -122,14 +122,14 @@ def test_subparsers_aur_search_option_repository(parser: argparse.ArgumentParser
def test_subparsers_help(parser: argparse.ArgumentParser) -> None:
"""
help command must imply architecture list, lock, report, repository, quiet, unsafe and parser
help command must imply architecture list, lock, quiet, report, repository, unsafe and parser
"""
args = parser.parse_args(["help"])
assert args.architecture == [""]
assert args.lock is None
assert args.quiet
assert not args.report
assert args.repository == [""]
assert args.quiet
assert args.unsafe
assert args.parser is not None and args.parser()
@ -152,14 +152,14 @@ def test_subparsers_help_option_repository(parser: argparse.ArgumentParser) -> N
def test_subparsers_help_commands_unsafe(parser: argparse.ArgumentParser) -> None:
"""
help-commands-unsafe command must imply architecture list, lock, report, repository, quiet, unsafe and parser
help-commands-unsafe command must imply architecture list, lock, quiet, report, repository, unsafe and parser
"""
args = parser.parse_args(["help-commands-unsafe"])
assert args.architecture == [""]
assert args.lock is None
assert args.quiet
assert not args.report
assert args.repository == [""]
assert args.quiet
assert args.unsafe
assert args.parser is not None and args.parser()
@ -182,14 +182,14 @@ def test_subparsers_help_commands_unsafe_option_repository(parser: argparse.Argu
def test_subparsers_help_updates(parser: argparse.ArgumentParser) -> None:
"""
help-updates command must imply architecture list, lock, report, repository, quiet and unsafe
help-updates command must imply architecture list, lock, quiet, report, repository, and unsafe
"""
args = parser.parse_args(["help-updates"])
assert args.architecture == [""]
assert args.lock is None
assert args.quiet
assert not args.report
assert args.repository == [""]
assert args.quiet
assert args.unsafe
@ -211,14 +211,14 @@ def test_subparsers_help_updates_option_repository(parser: argparse.ArgumentPars
def test_subparsers_help_version(parser: argparse.ArgumentParser) -> None:
"""
help-version command must imply architecture, lock, report, repository, quiet and unsafe
help-version command must imply architecture, lock, quiet, report, repository and unsafe
"""
args = parser.parse_args(["help-version"])
assert args.architecture == [""]
assert args.lock is None
assert args.quiet
assert not args.report
assert args.repository == [""]
assert args.quiet
assert args.unsafe
@ -292,42 +292,42 @@ def test_subparsers_package_remove_option_repository(parser: argparse.ArgumentPa
def test_subparsers_package_status(parser: argparse.ArgumentParser) -> None:
"""
package-status command must imply lock, report, quiet and unsafe
package-status command must imply lock, quiet, report and unsafe
"""
args = parser.parse_args(["-a", "x86_64", "-r", "repo", "package-status"])
assert args.architecture == ["x86_64"]
assert args.lock is None
assert args.quiet
assert not args.report
assert args.repository == ["repo"]
assert args.quiet
assert args.unsafe
def test_subparsers_package_status_remove(parser: argparse.ArgumentParser) -> None:
"""
package-status-remove command must imply action, lock, report, quiet and unsafe
package-status-remove command must imply action, lock, quiet, report and unsafe
"""
args = parser.parse_args(["-a", "x86_64", "-r", "repo", "package-status-remove", "ahriman"])
assert args.architecture == ["x86_64"]
assert args.action == Action.Remove
assert args.lock is None
assert args.quiet
assert not args.report
assert args.repository == ["repo"]
assert args.quiet
assert args.unsafe
def test_subparsers_package_status_update(parser: argparse.ArgumentParser) -> None:
"""
package-status-update command must imply action, lock, report, quiet and unsafe
package-status-update command must imply action, lock, quiet, report and unsafe
"""
args = parser.parse_args(["-a", "x86_64", "-r", "repo", "package-status-update"])
assert args.architecture == ["x86_64"]
assert args.action == Action.Update
assert args.lock is None
assert args.quiet
assert not args.report
assert args.repository == ["repo"]
assert args.quiet
assert args.unsafe
@ -805,16 +805,16 @@ def test_subparsers_repo_sign_option_repository(parser: argparse.ArgumentParser)
def test_subparsers_repo_status_update(parser: argparse.ArgumentParser) -> None:
"""
re[p-status-update command must imply action, lock, report, package, quiet and unsafe
re[p-status-update command must imply action, lock, quiet, report, package and unsafe
"""
args = parser.parse_args(["-a", "x86_64", "-r", "repo", "package-status-update"])
assert args.architecture == ["x86_64"]
assert args.action == Action.Update
assert args.lock is None
assert args.quiet
assert not args.report
assert args.repository == ["repo"]
assert not args.package
assert args.quiet
assert args.unsafe
@ -858,12 +858,12 @@ def test_subparsers_repo_sync_option_repository(parser: argparse.ArgumentParser)
def test_subparsers_repo_tree(parser: argparse.ArgumentParser) -> None:
"""
repo-tree command must imply lock, report, quiet and unsafe
repo-tree command must imply lock, quiet, report and unsafe
"""
args = parser.parse_args(["repo-tree"])
assert args.lock is None
assert not args.report
assert args.quiet
assert not args.report
assert args.unsafe
@ -980,27 +980,27 @@ def test_subparsers_service_clean_option_repository(parser: argparse.ArgumentPar
def test_subparsers_service_config(parser: argparse.ArgumentParser) -> None:
"""
service-config command must imply lock, report, quiet and unsafe
service-config command must imply lock, quiet, report and unsafe
"""
args = parser.parse_args(["-a", "x86_64", "-r", "repo", "service-config"])
assert args.architecture == ["x86_64"]
assert args.lock is None
assert args.quiet
assert not args.report
assert args.repository == ["repo"]
assert args.quiet
assert args.unsafe
def test_subparsers_service_config_validate(parser: argparse.ArgumentParser) -> None:
"""
service-config-validate command must imply lock, report, quiet and unsafe
service-config-validate command must imply lock, quiet, report and unsafe
"""
args = parser.parse_args(["-a", "x86_64", "-r", "repo", "service-config-validate"])
assert args.architecture == ["x86_64"]
assert args.lock is None
assert args.quiet
assert not args.report
assert args.repository == ["repo"]
assert args.quiet
assert args.unsafe
@ -1033,14 +1033,14 @@ def test_subparsers_service_key_import_option_repository(parser: argparse.Argume
def test_subparsers_service_setup(parser: argparse.ArgumentParser) -> None:
"""
service-setup command must imply lock, report, quiet and unsafe
service-setup command must imply lock, quiet, report and unsafe
"""
args = parser.parse_args(["-a", "x86_64", "-r", "repo", "service-setup", "--packager", "John Doe <john@doe.com>"])
assert args.architecture == ["x86_64"]
assert args.lock is None
assert args.quiet
assert not args.report
assert args.repository == ["repo"]
assert args.quiet
assert args.unsafe
@ -1091,17 +1091,27 @@ def test_subparsers_service_shell(parser: argparse.ArgumentParser) -> None:
assert not args.report
def test_subparsers_service_tree_migrate(parser: argparse.ArgumentParser) -> None:
"""
service-tree-migrate command must imply lock, quiet and report
"""
args = parser.parse_args(["service-tree-migrate"])
assert args.lock is None
assert args.quiet
assert not args.report
def test_subparsers_user_add(parser: argparse.ArgumentParser) -> None:
"""
user-add command must imply action, architecture, lock, report, repository and quiet
user-add command must imply action, architecture, lock, quiet, report and repository
"""
args = parser.parse_args(["user-add", "username"])
assert args.action == Action.Update
assert args.architecture == [""]
assert args.lock is None
assert args.quiet
assert not args.report
assert args.repository == [""]
assert args.quiet
def test_subparsers_user_add_option_architecture(parser: argparse.ArgumentParser) -> None:
@ -1132,15 +1142,15 @@ def test_subparsers_user_add_option_role(parser: argparse.ArgumentParser) -> Non
def test_subparsers_user_list(parser: argparse.ArgumentParser) -> None:
"""
user-list command must imply action, architecture, lock, report, repository, quiet and unsafe
user-list command must imply action, architecture, lock, quiet, report, repository and unsafe
"""
args = parser.parse_args(["user-list"])
assert args.action == Action.List
assert args.architecture == [""]
assert args.lock is None
assert args.quiet
assert not args.report
assert args.repository == [""]
assert args.quiet
assert args.unsafe
@ -1170,15 +1180,15 @@ def test_subparsers_user_list_option_role(parser: argparse.ArgumentParser) -> No
def test_subparsers_user_remove(parser: argparse.ArgumentParser) -> None:
"""
user-remove command must imply action, architecture, lock, report, repository and quiet
user-remove command must imply action, architecture, lock, quiet, report and repository
"""
args = parser.parse_args(["user-remove", "username"])
assert args.action == Action.Remove
assert args.architecture == [""]
assert args.lock is None
assert args.quiet
assert not args.report
assert args.repository == [""]
assert args.quiet
def test_subparsers_user_remove_option_architecture(parser: argparse.ArgumentParser) -> None:

View File

@ -58,10 +58,12 @@ def test_database_copy(pacman: Pacman, repository_paths: RepositoryPaths, mocker
mocker.patch("pathlib.Path.is_dir", return_value=True)
# root database exists, local database does not
mocker.patch("pathlib.Path.is_file", autospec=True, side_effect=lambda p: p.is_relative_to(path))
mkdir_mock = mocker.patch("pathlib.Path.mkdir")
copy_mock = mocker.patch("shutil.copy")
chown_mock = mocker.patch("ahriman.models.repository_paths.RepositoryPaths.chown")
pacman.database_copy(pacman.handle, database, path, repository_paths, use_ahriman_cache=True)
mkdir_mock.assert_called_once_with(mode=0o755, exist_ok=True)
copy_mock.assert_called_once_with(path / "sync" / "core.db", dst_path)
chown_mock.assert_called_once_with(dst_path)

View File

@ -7,7 +7,7 @@ from pytest_mock import MockerFixture
from systemd.journal import JournalHandler
from ahriman.core.configuration import Configuration
from ahriman.core.log import Log
from ahriman.core.log.log_loader import LogLoader
from ahriman.models.log_handler import LogHandler
@ -15,14 +15,14 @@ def test_handler() -> None:
"""
must extract journald handler if available
"""
assert Log.handler(None) == LogHandler.Journald
assert LogLoader.handler(None) == LogHandler.Journald
def test_handler_selected() -> None:
"""
must return selected log handler
"""
assert Log.handler(LogHandler.Console) == LogHandler.Console
assert LogLoader.handler(LogHandler.Console) == LogHandler.Console
def test_handler_syslog(mocker: MockerFixture) -> None:
@ -31,7 +31,7 @@ def test_handler_syslog(mocker: MockerFixture) -> None:
"""
mocker.patch("pathlib.Path.exists", return_value=True)
mocker.patch.dict(sys.modules, {"systemd.journal": None})
assert Log.handler(None) == LogHandler.Syslog
assert LogLoader.handler(None) == LogHandler.Syslog
def test_handler_console(mocker: MockerFixture) -> None:
@ -40,17 +40,17 @@ def test_handler_console(mocker: MockerFixture) -> None:
"""
mocker.patch("pathlib.Path.exists", return_value=False)
mocker.patch.dict(sys.modules, {"systemd.journal": None})
assert Log.handler(None) == LogHandler.Console
assert LogLoader.handler(None) == LogHandler.Console
def test_load(configuration: Configuration, mocker: MockerFixture) -> None:
"""
must load logging
"""
logging_mock = mocker.patch("ahriman.core.log.log.fileConfig", side_effect=fileConfig)
logging_mock = mocker.patch("ahriman.core.log.log_loader.fileConfig", side_effect=fileConfig)
http_log_mock = mocker.patch("ahriman.core.log.http_log_handler.HttpLogHandler.load")
Log.load(configuration, LogHandler.Journald, quiet=False, report=False)
LogLoader.load(configuration, LogHandler.Journald, quiet=False, report=False)
logging_mock.assert_called_once_with(pytest.helpers.anyvar(int), disable_existing_loggers=True)
http_log_mock.assert_called_once_with(configuration, report=False)
assert all(isinstance(handler, JournalHandler) for handler in logging.getLogger().handlers)
@ -60,8 +60,8 @@ def test_load_fallback(configuration: Configuration, mocker: MockerFixture) -> N
"""
must fall back to stderr without errors
"""
mocker.patch("ahriman.core.log.log.fileConfig", side_effect=PermissionError())
Log.load(configuration, LogHandler.Journald, quiet=False, report=False)
mocker.patch("ahriman.core.log.log_loader.fileConfig", side_effect=PermissionError())
LogLoader.load(configuration, LogHandler.Journald, quiet=False, report=False)
def test_load_quiet(configuration: Configuration, mocker: MockerFixture) -> None:
@ -69,5 +69,5 @@ def test_load_quiet(configuration: Configuration, mocker: MockerFixture) -> None
must disable logging in case if quiet flag set
"""
disable_mock = mocker.patch("logging.disable")
Log.load(configuration, LogHandler.Journald, quiet=True, report=False)
LogLoader.load(configuration, LogHandler.Journald, quiet=True, report=False)
disable_mock.assert_called_once_with(logging.WARNING)

View File

@ -3,6 +3,16 @@ import pytest
from ahriman.models.repository_id import RepositoryId
def test_is_empty() -> None:
"""
must check if repository id is empty or not
"""
assert RepositoryId("", "").is_empty
assert RepositoryId("arch", "").is_empty
assert RepositoryId("", "repo").is_empty
assert not RepositoryId("arch", "repo").is_empty
def test_lt() -> None:
"""
must correctly compare instances

View File

@ -31,12 +31,24 @@ def test_suffix(repository_id: RepositoryId, mocker: MockerFixture) -> None:
"""
must correctly define suffix
"""
is_dir_mock = mocker.patch("pathlib.Path.is_dir", autospec=True, return_value=True)
assert RepositoryPaths(Path("root"), repository_id)._suffix == Path(repository_id.architecture)
is_dir_mock.assert_called_once_with(Path("root") / "repository" / repository_id.architecture)
is_dir_mock = mocker.patch("pathlib.Path.is_dir", autospec=True)
mocker.patch("pathlib.Path.is_dir", return_value=False)
assert RepositoryPaths(Path("root"), repository_id)._suffix == Path(repository_id.name) / repository_id.architecture
is_dir_mock.return_value = True
instance = RepositoryPaths(Path("root"), repository_id)
assert instance._suffix == Path(repository_id.architecture)
is_dir_mock.return_value = False
instance = RepositoryPaths(Path("root"), repository_id)
assert instance._suffix == Path(repository_id.name) / repository_id.architecture
is_dir_mock.return_value = True
instance = RepositoryPaths(Path("root"), repository_id, _force_current_tree=True)
assert instance._suffix == Path(repository_id.name) / repository_id.architecture
is_dir_mock.assert_has_calls([
MockCall(Path("root") / "repository" / repository_id.architecture),
MockCall(Path("root") / "repository" / repository_id.architecture),
])
def test_root_owner(repository_paths: RepositoryPaths, mocker: MockerFixture) -> None:
@ -270,20 +282,29 @@ def test_tree_create(repository_paths: RepositoryPaths, mocker: MockerFixture) -
prop
for prop in dir(repository_paths)
if not prop.startswith("_")
and not prop.endswith("_for")
and prop not in ("chown",
"known_architectures",
"known_repositories",
"owner",
and not callable(getattr(repository_paths, prop))
and prop not in ("logger_name",
"logger",
"repository_id",
"root",
"root_owner",
"tree_clear",
"tree_create")
"root_owner")
}
mkdir_mock = mocker.patch("pathlib.Path.mkdir")
chown_mock = mocker.patch("ahriman.models.repository_paths.RepositoryPaths.chown")
print(paths)
repository_paths.tree_create()
mkdir_mock.assert_has_calls([MockCall(mode=0o755, parents=True, exist_ok=True) for _ in paths], any_order=True)
chown_mock.assert_has_calls([MockCall(pytest.helpers.anyvar(int)) for _ in paths], any_order=True)
def test_tree_create_skip(mocker: MockerFixture) -> None:
"""
must skip tree creation if repository id is not set
"""
mkdir_mock = mocker.patch("pathlib.Path.mkdir")
repository_paths = RepositoryPaths(Path("local"), RepositoryId("", ""))
repository_paths.tree_create()
mkdir_mock.assert_not_called()