diff --git a/docs/ahriman.core.configuration.rst b/docs/ahriman.core.configuration.rst index f33f01f4..efd53d89 100644 --- a/docs/ahriman.core.configuration.rst +++ b/docs/ahriman.core.configuration.rst @@ -20,6 +20,14 @@ ahriman.core.configuration.schema module :no-undoc-members: :show-inheritance: +ahriman.core.configuration.shell\_interpolator module +----------------------------------------------------- + +.. automodule:: ahriman.core.configuration.shell_interpolator + :members: + :no-undoc-members: + :show-inheritance: + ahriman.core.configuration.validator module ------------------------------------------- diff --git a/docs/ahriman.core.database.migrations.rst b/docs/ahriman.core.database.migrations.rst index cf0cfaf0..a144ed6a 100644 --- a/docs/ahriman.core.database.migrations.rst +++ b/docs/ahriman.core.database.migrations.rst @@ -84,6 +84,14 @@ ahriman.core.database.migrations.m009\_local\_source module :no-undoc-members: :show-inheritance: +ahriman.core.database.migrations.m010\_version\_based\_logs\_removal module +--------------------------------------------------------------------------- + +.. automodule:: ahriman.core.database.migrations.m010_version_based_logs_removal + :members: + :no-undoc-members: + :show-inheritance: + Module contents --------------- diff --git a/package/share/bash-completion/completions/_ahriman b/package/share/bash-completion/completions/_ahriman index 90ffec35..72301b94 100644 --- a/package/share/bash-completion/completions/_ahriman +++ b/package/share/bash-completion/completions/_ahriman @@ -58,11 +58,11 @@ _shtab_ahriman_config_validate_option_strings=('-h' '--help' '-e' '--exit-code') _shtab_ahriman_repo_config_validate_option_strings=('-h' '--help' '-e' '--exit-code') _shtab_ahriman_service_key_import_option_strings=('-h' '--help' '--key-server') _shtab_ahriman_key_import_option_strings=('-h' '--help' '--key-server') -_shtab_ahriman_service_setup_option_strings=('-h' '--help' '--build-as-user' '--build-command' '--from-configuration' '--generate-salt' '--no-generate-salt' '--makeflags-jobs' '--no-makeflags-jobs' '--mirror' '--multilib' '--no-multilib' '--packager' '--repository' '--sign-key' '--sign-target' '--web-port' '--web-unix-socket') -_shtab_ahriman_init_option_strings=('-h' '--help' '--build-as-user' '--build-command' '--from-configuration' '--generate-salt' '--no-generate-salt' '--makeflags-jobs' '--no-makeflags-jobs' '--mirror' '--multilib' '--no-multilib' '--packager' '--repository' '--sign-key' '--sign-target' '--web-port' '--web-unix-socket') -_shtab_ahriman_repo_init_option_strings=('-h' '--help' '--build-as-user' '--build-command' '--from-configuration' '--generate-salt' '--no-generate-salt' '--makeflags-jobs' '--no-makeflags-jobs' '--mirror' '--multilib' '--no-multilib' '--packager' '--repository' '--sign-key' '--sign-target' '--web-port' '--web-unix-socket') -_shtab_ahriman_repo_setup_option_strings=('-h' '--help' '--build-as-user' '--build-command' '--from-configuration' '--generate-salt' '--no-generate-salt' '--makeflags-jobs' '--no-makeflags-jobs' '--mirror' '--multilib' '--no-multilib' '--packager' '--repository' '--sign-key' '--sign-target' '--web-port' '--web-unix-socket') -_shtab_ahriman_setup_option_strings=('-h' '--help' '--build-as-user' '--build-command' '--from-configuration' '--generate-salt' '--no-generate-salt' '--makeflags-jobs' '--no-makeflags-jobs' '--mirror' '--multilib' '--no-multilib' '--packager' '--repository' '--sign-key' '--sign-target' '--web-port' '--web-unix-socket') +_shtab_ahriman_service_setup_option_strings=('-h' '--help' '--build-as-user' '--build-command' '--from-configuration' '--generate-salt' '--no-generate-salt' '--makeflags-jobs' '--no-makeflags-jobs' '--mirror' '--multilib' '--no-multilib' '--packager' '--repository' '--server' '--sign-key' '--sign-target' '--web-port' '--web-unix-socket') +_shtab_ahriman_init_option_strings=('-h' '--help' '--build-as-user' '--build-command' '--from-configuration' '--generate-salt' '--no-generate-salt' '--makeflags-jobs' '--no-makeflags-jobs' '--mirror' '--multilib' '--no-multilib' '--packager' '--repository' '--server' '--sign-key' '--sign-target' '--web-port' '--web-unix-socket') +_shtab_ahriman_repo_init_option_strings=('-h' '--help' '--build-as-user' '--build-command' '--from-configuration' '--generate-salt' '--no-generate-salt' '--makeflags-jobs' '--no-makeflags-jobs' '--mirror' '--multilib' '--no-multilib' '--packager' '--repository' '--server' '--sign-key' '--sign-target' '--web-port' '--web-unix-socket') +_shtab_ahriman_repo_setup_option_strings=('-h' '--help' '--build-as-user' '--build-command' '--from-configuration' '--generate-salt' '--no-generate-salt' '--makeflags-jobs' '--no-makeflags-jobs' '--mirror' '--multilib' '--no-multilib' '--packager' '--repository' '--server' '--sign-key' '--sign-target' '--web-port' '--web-unix-socket') +_shtab_ahriman_setup_option_strings=('-h' '--help' '--build-as-user' '--build-command' '--from-configuration' '--generate-salt' '--no-generate-salt' '--makeflags-jobs' '--no-makeflags-jobs' '--mirror' '--multilib' '--no-multilib' '--packager' '--repository' '--server' '--sign-key' '--sign-target' '--web-port' '--web-unix-socket') _shtab_ahriman_service_shell_option_strings=('-h' '--help') _shtab_ahriman_shell_option_strings=('-h' '--help') _shtab_ahriman_user_add_option_strings=('-h' '--help' '--key' '--packager' '-p' '--password' '-r' '--role') diff --git a/package/share/man/man1/ahriman.1 b/package/share/man/man1/ahriman.1 index 17237ea5..93d108dc 100644 --- a/package/share/man/man1/ahriman.1 +++ b/package/share/man/man1/ahriman.1 @@ -1,4 +1,4 @@ -.TH AHRIMAN "1" "2023\-08\-13" "ahriman" "Generated Python Manual" +.TH AHRIMAN "1" "2023\-08\-19" "ahriman" "Generated Python Manual" .SH NAME ahriman .SH SYNOPSIS @@ -689,7 +689,7 @@ key server for key import usage: ahriman service\-setup [\-h] [\-\-build\-as\-user BUILD_AS_USER] [\-\-build\-command BUILD_COMMAND] [\-\-from\-configuration FROM_CONFIGURATION] [\-\-generate\-salt | \-\-no\-generate\-salt] [\-\-makeflags\-jobs | \-\-no\-makeflags\-jobs] [\-\-mirror MIRROR] [\-\-multilib | \-\-no\-multilib] - \-\-packager PACKAGER \-\-repository REPOSITORY [\-\-sign\-key SIGN_KEY] + \-\-packager PACKAGER \-\-repository REPOSITORY [\-\-server SERVER] [\-\-sign\-key SIGN_KEY] [\-\-sign\-target {disabled,packages,repository}] [\-\-web\-port WEB_PORT] [\-\-web\-unix\-socket WEB_UNIX_SOCKET] @@ -732,6 +732,10 @@ packager name and email \fB\-\-repository\fR \fI\,REPOSITORY\/\fR repository name +.TP +\fB\-\-server\fR \fI\,SERVER\/\fR +server to be used for devtools. If none set, local files will be used + .TP \fB\-\-sign\-key\fR \fI\,SIGN_KEY\/\fR sign key id diff --git a/package/share/zsh/site-functions/_ahriman b/package/share/zsh/site-functions/_ahriman index eb431d40..91316737 100644 --- a/package/share/zsh/site-functions/_ahriman +++ b/package/share/zsh/site-functions/_ahriman @@ -177,6 +177,7 @@ _shtab_ahriman_init_options=( {--multilib,--no-multilib}"[add or do not multilib repository (default\: True)]:multilib:" "--packager[packager name and email (default\: None)]:packager:" "--repository[repository name (default\: None)]:repository:" + "--server[server to be used for devtools. If none set, local files will be used (default\: None)]:server:" "--sign-key[sign key id (default\: None)]:sign_key:" "*--sign-target[sign options (default\: None)]:sign_target:(disabled packages repository)" "--web-port[port of the web service (default\: None)]:web_port:" @@ -347,6 +348,7 @@ _shtab_ahriman_repo_init_options=( {--multilib,--no-multilib}"[add or do not multilib repository (default\: True)]:multilib:" "--packager[packager name and email (default\: None)]:packager:" "--repository[repository name (default\: None)]:repository:" + "--server[server to be used for devtools. If none set, local files will be used (default\: None)]:server:" "--sign-key[sign key id (default\: None)]:sign_key:" "*--sign-target[sign options (default\: None)]:sign_target:(disabled packages repository)" "--web-port[port of the web service (default\: None)]:web_port:" @@ -390,6 +392,7 @@ _shtab_ahriman_repo_setup_options=( {--multilib,--no-multilib}"[add or do not multilib repository (default\: True)]:multilib:" "--packager[packager name and email (default\: None)]:packager:" "--repository[repository name (default\: None)]:repository:" + "--server[server to be used for devtools. If none set, local files will be used (default\: None)]:server:" "--sign-key[sign key id (default\: None)]:sign_key:" "*--sign-target[sign options (default\: None)]:sign_target:(disabled packages repository)" "--web-port[port of the web service (default\: None)]:web_port:" @@ -482,6 +485,7 @@ _shtab_ahriman_service_setup_options=( {--multilib,--no-multilib}"[add or do not multilib repository (default\: True)]:multilib:" "--packager[packager name and email (default\: None)]:packager:" "--repository[repository name (default\: None)]:repository:" + "--server[server to be used for devtools. If none set, local files will be used (default\: None)]:server:" "--sign-key[sign key id (default\: None)]:sign_key:" "*--sign-target[sign options (default\: None)]:sign_target:(disabled packages repository)" "--web-port[port of the web service (default\: None)]:web_port:" @@ -504,6 +508,7 @@ _shtab_ahriman_setup_options=( {--multilib,--no-multilib}"[add or do not multilib repository (default\: True)]:multilib:" "--packager[packager name and email (default\: None)]:packager:" "--repository[repository name (default\: None)]:repository:" + "--server[server to be used for devtools. If none set, local files will be used (default\: None)]:server:" "--sign-key[sign key id (default\: None)]:sign_key:" "*--sign-target[sign options (default\: None)]:sign_target:(disabled packages repository)" "--web-port[port of the web service (default\: None)]:web_port:" diff --git a/src/ahriman/core/database/operations/logs_operations.py b/src/ahriman/core/database/operations/logs_operations.py index f952ee62..202d1eb8 100644 --- a/src/ahriman/core/database/operations/logs_operations.py +++ b/src/ahriman/core/database/operations/logs_operations.py @@ -66,9 +66,9 @@ class LogsOperations(Operations): connection.execute( """ insert into logs - (package_base, created, version, record) + (package_base, version, created, record) values - (:package_base, :created, :version, :record) + (:package_base, :version, :created, :record) """, { "package_base": log_record_id.package_base, diff --git a/src/ahriman/core/log/http_log_handler.py b/src/ahriman/core/log/http_log_handler.py index f1826c1d..6bab9d30 100644 --- a/src/ahriman/core/log/http_log_handler.py +++ b/src/ahriman/core/log/http_log_handler.py @@ -31,15 +31,17 @@ class HttpLogHandler(logging.Handler): Attributes: reporter(Client): build status reporter instance + suppress_errors(bool): suppress logging errors (e.g. if no web server available) """ - def __init__(self, configuration: Configuration, *, report: bool) -> None: + def __init__(self, configuration: Configuration, *, report: bool, suppress_errors: bool) -> None: """ default constructor Args: configuration(Configuration): configuration instance report(bool): force enable or disable reporting + suppress_errors(bool): suppress logging errors (e.g. if no web server available) """ # we don't really care about those parameters because they will be handled by the reporter logging.Handler.__init__(self) @@ -47,6 +49,7 @@ class HttpLogHandler(logging.Handler): # client has to be imported here because of circular imports from ahriman.core.status.client import Client self.reporter = Client.load(configuration, report=report) + self.suppress_errors = suppress_errors @classmethod def load(cls, configuration: Configuration, *, report: bool) -> Self: @@ -65,7 +68,8 @@ class HttpLogHandler(logging.Handler): if (handler := next((handler for handler in root.handlers if isinstance(handler, cls)), None)) is not None: return handler # there is already registered instance - handler = cls(configuration, report=report) + suppress_errors = configuration.getboolean("settings", "suppress_http_log_errors", fallback=False) + handler = cls(configuration, report=report, suppress_errors=suppress_errors) root.addHandler(handler) return handler @@ -81,4 +85,9 @@ class HttpLogHandler(logging.Handler): if log_record_id is None: return # in case if no package base supplied we need just skip log message - self.reporter.package_logs(log_record_id, record) + try: + self.reporter.package_logs(log_record_id, record) + except Exception: + if self.suppress_errors: + return + self.handleError(record) diff --git a/src/ahriman/core/report/remote_call.py b/src/ahriman/core/report/remote_call.py index 160b1aaf..ad467cb5 100644 --- a/src/ahriman/core/report/remote_call.py +++ b/src/ahriman/core/report/remote_call.py @@ -17,6 +17,8 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . # +import requests + from ahriman.core.configuration import Configuration from ahriman.core.report.report import Report from ahriman.core.status.web_client import WebClient @@ -77,43 +79,41 @@ class RemoteCall(Report): Returns: bool: True in case if remote process is alive and False otherwise """ - response = self.client.make_request("GET", f"/api/v1/service/process/{process_id}") - if response is None: - return False + try: + response = self.client.make_request("GET", f"/api/v1/service/process/{process_id}") + except requests.RequestException as e: + if e.response is not None and e.response.status_code == 404: + return False + raise response_json = response.json() is_alive: bool = response_json["is_alive"] return is_alive - def remote_update(self) -> str | None: + def remote_update(self) -> str: """ call remote server for update Returns: - str | None: remote process id on success and ``None`` otherwise + str: remote process id """ response = self.client.make_request("POST", "/api/v1/service/update", json={ "aur": self.update_aur, "local": self.update_local, "manual": self.update_manual, }) - if response is None: - return None # request terminated with error - response_json = response.json() + process_id: str = response_json["process_id"] return process_id - def remote_wait(self, process_id: str | None) -> None: + def remote_wait(self, process_id: str) -> None: """ wait for remote process termination Args: - process_id(str | None): remote process id + process_id(str): remote process id """ - if process_id is None: - return # nothing to track - waiter = Waiter(self.wait_timeout) waiter.wait(self.is_process_alive, process_id) diff --git a/src/ahriman/core/status/web_client.py b/src/ahriman/core/status/web_client.py index f88d6a78..b8514edd 100644 --- a/src/ahriman/core/status/web_client.py +++ b/src/ahriman/core/status/web_client.py @@ -21,7 +21,6 @@ import contextlib import logging import requests -from collections.abc import Generator from functools import cached_property from typing import Any, IO, Literal from urllib.parse import quote_plus as urlencode @@ -129,32 +128,6 @@ class WebClient(Client, LazyLogging): address = f"http://{host}:{port}" return address, False - @contextlib.contextmanager - def __get_session(self, session: requests.Session | None = None) -> Generator[requests.Session, None, None]: - """ - execute request and handle exceptions - - Args: - session(requests.Session | None, optional): session to be used or stored instance property otherwise - (Default value = None) - - Yields: - requests.Session: session for requests - """ - try: - if session is not None: - yield session # use session from arguments - else: - yield self.session # use instance generated session - except requests.RequestException as e: - if self.suppress_errors: - return - self.logger.exception("could not perform http request: %s", exception_response_text(e)) - except Exception: - if self.suppress_errors: - return - self.logger.exception("could not perform http request") - def _create_session(self, *, use_unix_socket: bool) -> requests.Session: """ generate new request session @@ -191,13 +164,15 @@ class WebClient(Client, LazyLogging): "username": self.user.username, "password": self.user.password } - self.make_request("POST", self._login_url, json=payload, session=session) + with contextlib.suppress(Exception): + self.make_request("POST", self._login_url, json=payload, session=session) def make_request(self, method: Literal["DELETE", "GET", "POST"], url: str, *, params: list[tuple[str, str]] | None = None, json: dict[str, Any] | None = None, files: dict[str, MultipartType] | None = None, - session: requests.Session | None = None) -> requests.Response | None: + session: requests.Session | None = None, + suppress_errors: bool | None = None) -> requests.Response: """ perform request with specified parameters @@ -208,17 +183,30 @@ class WebClient(Client, LazyLogging): json(dict[str, Any] | None, optional): request json parameters (Default value = None) files(dict[str, MultipartType] | None, optional): multipart upload (Default value = None) session(requests.Session | None, optional): session object if any (Default value = None) + suppress_errors(bool | None, optional): suppress logging errors (e.g. if no web server available). If none + set, the instance-wide value will be used (Default value = None) Returns: - requests.Response | None: response object or None in case of errors + requests.Response: response object """ - with self.__get_session(session) as _session: - response = _session.request(method, f"{self.address}{url}", params=params, json=json, files=files) + # defaults + if suppress_errors is None: + suppress_errors = self.suppress_errors + if session is None: + session = self.session + + try: + response = session.request(method, f"{self.address}{url}", params=params, json=json, files=files) response.raise_for_status() return response - - # noinspection PyUnreachableCode - return None + except requests.RequestException as e: + if not suppress_errors: + self.logger.exception("could not perform http request: %s", exception_response_text(e)) + raise + except Exception: + if not suppress_errors: + self.logger.exception("could not perform http request") + raise def package_add(self, package: Package, status: BuildStatusEnum) -> None: """ @@ -232,7 +220,8 @@ class WebClient(Client, LazyLogging): "status": status.value, "package": package.view() } - self.make_request("POST", self._package_url(package.base), json=payload) + with contextlib.suppress(Exception): + self.make_request("POST", self._package_url(package.base), json=payload) def package_get(self, package_base: str | None) -> list[tuple[Package, BuildStatus]]: """ @@ -244,15 +233,16 @@ class WebClient(Client, LazyLogging): Returns: list[tuple[Package, BuildStatus]]: list of current package description and status if it has been found """ - response = self.make_request("GET", self._package_url(package_base or "")) - if response is None: - return [] + with contextlib.suppress(Exception): + response = self.make_request("GET", self._package_url(package_base or "")) + response_json = response.json() - response_json = response.json() - return [ - (Package.from_json(package["package"]), BuildStatus.from_json(package["status"])) - for package in response_json - ] + return [ + (Package.from_json(package["package"]), BuildStatus.from_json(package["status"])) + for package in response_json + ] + + return [] def package_logs(self, log_record_id: LogRecordId, record: logging.LogRecord) -> None: """ @@ -267,7 +257,11 @@ class WebClient(Client, LazyLogging): "message": record.getMessage(), "version": log_record_id.version, } - self.make_request("POST", self._logs_url(log_record_id.package_base), json=payload) + + # this is special case, because we would like to do not suppress exception here + # in case of exception raised it will be handled by upstream HttpLogHandler + # In the other hand, we force to suppress all http logs here to avoid cyclic reporting + self.make_request("POST", self._logs_url(log_record_id.package_base), json=payload, suppress_errors=True) def package_remove(self, package_base: str) -> None: """ @@ -276,7 +270,8 @@ class WebClient(Client, LazyLogging): Args: package_base(str): basename to remove """ - self.make_request("DELETE", self._package_url(package_base)) + with contextlib.suppress(Exception): + self.make_request("DELETE", self._package_url(package_base)) def package_update(self, package_base: str, status: BuildStatusEnum) -> None: """ @@ -287,7 +282,8 @@ class WebClient(Client, LazyLogging): status(BuildStatusEnum): current package build status """ payload = {"status": status.value} - self.make_request("POST", self._package_url(package_base), json=payload) + with contextlib.suppress(Exception): + self.make_request("POST", self._package_url(package_base), json=payload) def status_get(self) -> InternalStatus: """ @@ -296,12 +292,13 @@ class WebClient(Client, LazyLogging): Returns: InternalStatus: current internal (web) service status """ - response = self.make_request("GET", self._status_url) - if response is None: - return InternalStatus(status=BuildStatus()) + with contextlib.suppress(Exception): + response = self.make_request("GET", self._status_url) + response_json = response.json() - response_json = response.json() - return InternalStatus.from_json(response_json) + return InternalStatus.from_json(response_json) + + return InternalStatus(status=BuildStatus()) def status_update(self, status: BuildStatusEnum) -> None: """ @@ -311,4 +308,5 @@ class WebClient(Client, LazyLogging): status(BuildStatusEnum): current ahriman status """ payload = {"status": status.value} - self.make_request("POST", self._status_url, json=payload) + with contextlib.suppress(Exception): + self.make_request("POST", self._status_url, json=payload) diff --git a/tests/ahriman/core/log/test_http_log_handler.py b/tests/ahriman/core/log/test_http_log_handler.py index e37ad4fd..fbe89adc 100644 --- a/tests/ahriman/core/log/test_http_log_handler.py +++ b/tests/ahriman/core/log/test_http_log_handler.py @@ -43,18 +43,46 @@ def test_emit(configuration: Configuration, log_record: logging.LogRecord, packa log_record_id = log_record.package_id = LogRecordId(package_ahriman.base, package_ahriman.version) log_mock = mocker.patch("ahriman.core.status.client.Client.package_logs") - handler = HttpLogHandler(configuration, report=False) + handler = HttpLogHandler(configuration, report=False, suppress_errors=False) handler.emit(log_record) log_mock.assert_called_once_with(log_record_id, log_record) +def test_emit_failed(configuration: Configuration, log_record: logging.LogRecord, package_ahriman: Package, + mocker: MockerFixture) -> None: + """ + must call handle error on exception + """ + log_record.package_id = LogRecordId(package_ahriman.base, package_ahriman.version) + mocker.patch("ahriman.core.status.client.Client.package_logs", side_effect=Exception()) + handle_error_mock = mocker.patch("logging.Handler.handleError") + handler = HttpLogHandler(configuration, report=False, suppress_errors=False) + + handler.emit(log_record) + handle_error_mock.assert_called_once_with(log_record) + + +def test_emit_suppress_failed(configuration: Configuration, log_record: logging.LogRecord, package_ahriman: Package, + mocker: MockerFixture) -> None: + """ + must not call handle error on exception if suppress flag is set + """ + log_record.package_id = LogRecordId(package_ahriman.base, package_ahriman.version) + mocker.patch("ahriman.core.status.client.Client.package_logs", side_effect=Exception()) + handle_error_mock = mocker.patch("logging.Handler.handleError") + handler = HttpLogHandler(configuration, report=False, suppress_errors=True) + + handler.emit(log_record) + handle_error_mock.assert_not_called() + + def test_emit_skip(configuration: Configuration, log_record: logging.LogRecord, mocker: MockerFixture) -> None: """ must skip log record posting if no package base set """ log_mock = mocker.patch("ahriman.core.status.client.Client.package_logs") - handler = HttpLogHandler(configuration, report=False) + handler = HttpLogHandler(configuration, report=False, suppress_errors=False) handler.emit(log_record) log_mock.assert_not_called() diff --git a/tests/ahriman/core/report/test_remote_call.py b/tests/ahriman/core/report/test_remote_call.py index 6f71c9b9..117129b9 100644 --- a/tests/ahriman/core/report/test_remote_call.py +++ b/tests/ahriman/core/report/test_remote_call.py @@ -37,10 +37,37 @@ def test_is_process_alive_unknown(remote_call: RemoteCall, mocker: MockerFixture """ must correctly define if process is unknown """ - mocker.patch("ahriman.core.status.web_client.WebClient.make_request", return_value=None) + response = requests.Response() + response.status_code = 404 + mocker.patch("ahriman.core.status.web_client.WebClient.make_request", + side_effect=requests.RequestException(response=response)) + assert not remote_call.is_process_alive("id") +def test_is_process_alive_error(remote_call: RemoteCall, mocker: MockerFixture) -> None: + """ + must reraise exception on process request + """ + mocker.patch("ahriman.core.status.web_client.WebClient.make_request", side_effect=Exception) + + with pytest.raises(Exception): + remote_call.is_process_alive("id") + + +def test_is_process_alive_http_error(remote_call: RemoteCall, mocker: MockerFixture) -> None: + """ + must reraise http exception on process request + """ + response = requests.Response() + response.status_code = 500 + mocker.patch("ahriman.core.status.web_client.WebClient.make_request", + side_effect=requests.RequestException(response=response)) + + with pytest.raises(requests.RequestException): + remote_call.is_process_alive("id") + + def test_remote_update(remote_call: RemoteCall, mocker: MockerFixture) -> None: """ must call remote server for update process @@ -59,14 +86,6 @@ def test_remote_update(remote_call: RemoteCall, mocker: MockerFixture) -> None: }) -def test_remote_update_failed(remote_call: RemoteCall, mocker: MockerFixture) -> None: - """ - must return empty process id in case of errors - """ - mocker.patch("ahriman.core.status.web_client.WebClient.make_request", return_value=None) - assert remote_call.generate([], Result()) is None - - def test_remote_wait(remote_call: RemoteCall, mocker: MockerFixture) -> None: """ must wait for remote process to success @@ -74,12 +93,3 @@ def test_remote_wait(remote_call: RemoteCall, mocker: MockerFixture) -> None: wait_mock = mocker.patch("ahriman.models.waiter.Waiter.wait") remote_call.remote_wait("id") wait_mock.assert_called_once_with(pytest.helpers.anyvar(int), "id") - - -def test_remote_wait_skip(remote_call: RemoteCall, mocker: MockerFixture) -> None: - """ - must skip wait if process id is unknown - """ - wait_mock = mocker.patch("ahriman.models.waiter.Waiter.wait") - remote_call.remote_wait(None) - wait_mock.assert_not_called() diff --git a/tests/ahriman/core/status/test_web_client.py b/tests/ahriman/core/status/test_web_client.py index b562bcbd..f16687a3 100644 --- a/tests/ahriman/core/status/test_web_client.py +++ b/tests/ahriman/core/status/test_web_client.py @@ -163,7 +163,8 @@ def test_make_request_failed(web_client: WebClient, mocker: MockerFixture) -> No must make HTTP request """ mocker.patch("requests.Session.request", side_effect=Exception()) - assert web_client.make_request("GET", "url") is None + with pytest.raises(Exception): + web_client.make_request("GET", "url") def test_package_add(web_client: WebClient, package_ahriman: Package, mocker: MockerFixture) -> None: @@ -296,7 +297,8 @@ def test_package_logs_failed(web_client: WebClient, log_record: logging.LogRecor """ mocker.patch("requests.Session.request", side_effect=Exception()) log_record.package_base = package_ahriman.base - web_client.package_logs(LogRecordId(package_ahriman.base, package_ahriman.version), log_record) + with pytest.raises(Exception): + web_client.package_logs(LogRecordId(package_ahriman.base, package_ahriman.version), log_record) def test_package_logs_failed_http_error(web_client: WebClient, log_record: logging.LogRecord, package_ahriman: Package, @@ -306,7 +308,8 @@ def test_package_logs_failed_http_error(web_client: WebClient, log_record: loggi """ mocker.patch("requests.Session.request", side_effect=requests.exceptions.HTTPError()) log_record.package_base = package_ahriman.base - web_client.package_logs(LogRecordId(package_ahriman.base, package_ahriman.version), log_record) + with pytest.raises(Exception): + web_client.package_logs(LogRecordId(package_ahriman.base, package_ahriman.version), log_record) def test_package_remove(web_client: WebClient, package_ahriman: Package, mocker: MockerFixture) -> None: