From 2c74be31bd52b76462e1a7f62e1f879b41430600 Mon Sep 17 00:00:00 2001 From: Evgeniy Alekseev Date: Mon, 13 Sep 2021 22:38:38 +0300 Subject: [PATCH] raise InvalidCommand exception in case if remove option supplied without package --- .../application/handlers/status_update.py | 3 + src/ahriman/core/exceptions.py | 61 +++++++++++-------- .../handlers/test_handler_status_update.py | 15 +++++ 3 files changed, 55 insertions(+), 24 deletions(-) diff --git a/src/ahriman/application/handlers/status_update.py b/src/ahriman/application/handlers/status_update.py index 1b80ded4..319b1e6a 100644 --- a/src/ahriman/application/handlers/status_update.py +++ b/src/ahriman/application/handlers/status_update.py @@ -24,6 +24,7 @@ from typing import Callable, Type from ahriman.application.application import Application from ahriman.application.handlers.handler import Handler from ahriman.core.configuration import Configuration +from ahriman.core.exceptions import InvalidCommand class StatusUpdate(Handler): @@ -48,6 +49,8 @@ class StatusUpdate(Handler): # update packages statuses for package in args.package: callback(package) + elif args.remove: + raise InvalidCommand("Remove option is supplied, but no packages set") else: # update service status client.update_self(args.status) diff --git a/src/ahriman/core/exceptions.py b/src/ahriman/core/exceptions.py index a2daf49d..762da9f6 100644 --- a/src/ahriman/core/exceptions.py +++ b/src/ahriman/core/exceptions.py @@ -20,7 +20,7 @@ from typing import Any -class BuildFailed(Exception): +class BuildFailed(RuntimeError): """ base exception for failed builds """ @@ -30,10 +30,10 @@ class BuildFailed(Exception): default constructor :param package: package base raised exception """ - Exception.__init__(self, f"Package {package} build failed, check logs for details") + RuntimeError.__init__(self, f"Package {package} build failed, check logs for details") -class DuplicateRun(Exception): +class DuplicateRun(RuntimeError): """ exception which will be raised if there is another application instance """ @@ -42,10 +42,10 @@ class DuplicateRun(Exception): """ default constructor """ - Exception.__init__(self, "Another application instance is run") + RuntimeError.__init__(self, "Another application instance is run") -class DuplicateUser(Exception): +class DuplicateUser(ValueError): """ exception which will be thrown in case if there are two users with different settings """ @@ -55,10 +55,10 @@ class DuplicateUser(Exception): default constructor :param username: username with duplicates """ - Exception.__init__(self, f"Found duplicate user with username {username}") + ValueError.__init__(self, f"Found duplicate user with username {username}") -class InitializeException(Exception): +class InitializeException(RuntimeError): """ base service initialization exception """ @@ -68,10 +68,23 @@ class InitializeException(Exception): default constructor :param details: details of the exception """ - Exception.__init__(self, f"Could not load service: {details}") + RuntimeError.__init__(self, f"Could not load service: {details}") -class InvalidOption(Exception): +class InvalidCommand(ValueError): + """ + exception raised on invalid command line options + """ + + def __init__(self, details: Any) -> None: + """ + default constructor + :param details" error details + """ + ValueError.__init__(self, details) + + +class InvalidOption(ValueError): """ exception which will be raised on configuration errors """ @@ -81,10 +94,10 @@ class InvalidOption(Exception): default constructor :param value: option value """ - Exception.__init__(self, f"Invalid or unknown option value `{value}`") + ValueError.__init__(self, f"Invalid or unknown option value `{value}`") -class InvalidPackageInfo(Exception): +class InvalidPackageInfo(RuntimeError): """ exception which will be raised on package load errors """ @@ -94,10 +107,10 @@ class InvalidPackageInfo(Exception): default constructor :param details: error details """ - Exception.__init__(self, f"There are errors during reading package information: `{details}`") + RuntimeError.__init__(self, f"There are errors during reading package information: `{details}`") -class MissingArchitecture(Exception): +class MissingArchitecture(ValueError): """ exception which will be raised if architecture is required, but missing """ @@ -107,10 +120,10 @@ class MissingArchitecture(Exception): default constructor :param command: command name which throws exception """ - Exception.__init__(self, f"Architecture required for subcommand {command}, but missing") + ValueError.__init__(self, f"Architecture required for subcommand {command}, but missing") -class MultipleArchitecture(Exception): +class MultipleArchitecture(ValueError): """ exception which will be raised if multiple architectures are not supported by the handler """ @@ -120,10 +133,10 @@ class MultipleArchitecture(Exception): default constructor :param command: command name which throws exception """ - Exception.__init__(self, f"Multiple architectures are not supported by subcommand {command}") + ValueError.__init__(self, f"Multiple architectures are not supported by subcommand {command}") -class ReportFailed(Exception): +class ReportFailed(RuntimeError): """ report generation exception """ @@ -132,10 +145,10 @@ class ReportFailed(Exception): """ default constructor """ - Exception.__init__(self, "Report failed") + RuntimeError.__init__(self, "Report failed") -class SyncFailed(Exception): +class SyncFailed(RuntimeError): """ remote synchronization exception """ @@ -144,19 +157,19 @@ class SyncFailed(Exception): """ default constructor """ - Exception.__init__(self, "Sync failed") + RuntimeError.__init__(self, "Sync failed") -class UnknownPackage(Exception): +class UnknownPackage(ValueError): """ exception for status watcher which will be thrown on unknown package """ def __init__(self, base: str) -> None: - Exception.__init__(self, f"Package base {base} is unknown") + ValueError.__init__(self, f"Package base {base} is unknown") -class UnsafeRun(Exception): +class UnsafeRun(RuntimeError): """ exception which will be raised in case if user is not owner of repository """ @@ -165,7 +178,7 @@ class UnsafeRun(Exception): """ default constructor """ - Exception.__init__( + RuntimeError.__init__( self, f"""Current UID {current_uid} differs from root owner {root_uid}. Note that for the most actions it is unsafe to run application as different user. diff --git a/tests/ahriman/application/handlers/test_handler_status_update.py b/tests/ahriman/application/handlers/test_handler_status_update.py index dc793577..e9ac615c 100644 --- a/tests/ahriman/application/handlers/test_handler_status_update.py +++ b/tests/ahriman/application/handlers/test_handler_status_update.py @@ -1,9 +1,11 @@ import argparse +import pytest from pytest_mock import MockerFixture from ahriman.application.handlers import StatusUpdate from ahriman.core.configuration import Configuration +from ahriman.core.exceptions import InvalidCommand from ahriman.models.build_status import BuildStatusEnum from ahriman.models.package import Package @@ -61,6 +63,19 @@ def test_run_remove(args: argparse.Namespace, configuration: Configuration, pack update_mock.assert_called_once() +def test_run_remove_without_packages(args: argparse.Namespace, configuration: Configuration, + mocker: MockerFixture) -> None: + """ + must raise exception when no packages set and remove called + """ + args = _default_args(args) + args.remove = True + mocker.patch("pathlib.Path.mkdir") + + with pytest.raises(InvalidCommand): + StatusUpdate.run(args, "x86_64", configuration, True) + + def test_imply_with_report(args: argparse.Namespace, configuration: Configuration, mocker: MockerFixture) -> None: """ must create application object with native reporting