mirror of
https://github.com/arcan1s/ahriman.git
synced 2025-04-28 09:17:17 +00:00
add remote call trigger implementation
This commit is contained in:
parent
37d3b9fa83
commit
af1803ed26
@ -50,14 +50,14 @@ class Status(Handler):
|
|||||||
# we are using reporter here
|
# we are using reporter here
|
||||||
client = Application(architecture, configuration, report=True).repository.reporter
|
client = Application(architecture, configuration, report=True).repository.reporter
|
||||||
if args.ahriman:
|
if args.ahriman:
|
||||||
service_status = client.get_internal()
|
service_status = client.status_get()
|
||||||
StatusPrinter(service_status.status).print(verbose=args.info)
|
StatusPrinter(service_status.status).print(verbose=args.info)
|
||||||
if args.package:
|
if args.package:
|
||||||
packages: list[tuple[Package, BuildStatus]] = sum(
|
packages: list[tuple[Package, BuildStatus]] = sum(
|
||||||
(client.get(base) for base in args.package),
|
(client.package_get(base) for base in args.package),
|
||||||
start=[])
|
start=[])
|
||||||
else:
|
else:
|
||||||
packages = client.get(None)
|
packages = client.package_get(None)
|
||||||
|
|
||||||
Status.check_if_empty(args.exit_code, not packages)
|
Status.check_if_empty(args.exit_code, not packages)
|
||||||
|
|
||||||
|
@ -49,10 +49,10 @@ class StatusUpdate(Handler):
|
|||||||
if args.action == Action.Update and args.package:
|
if args.action == Action.Update and args.package:
|
||||||
# update packages statuses
|
# update packages statuses
|
||||||
for package in args.package:
|
for package in args.package:
|
||||||
client.update(package, args.status)
|
client.package_update(package, args.status)
|
||||||
elif args.action == Action.Update:
|
elif args.action == Action.Update:
|
||||||
# update service status
|
# update service status
|
||||||
client.update_self(args.status)
|
client.status_update(args.status)
|
||||||
elif args.action == Action.Remove:
|
elif args.action == Action.Remove:
|
||||||
for package in args.package:
|
for package in args.package:
|
||||||
client.remove(package)
|
client.package_remove(package)
|
||||||
|
@ -18,7 +18,6 @@
|
|||||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
#
|
#
|
||||||
import argparse
|
import argparse
|
||||||
import time
|
|
||||||
|
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from types import TracebackType
|
from types import TracebackType
|
||||||
@ -31,6 +30,7 @@ from ahriman.core.log import LazyLogging
|
|||||||
from ahriman.core.status.client import Client
|
from ahriman.core.status.client import Client
|
||||||
from ahriman.core.util import check_user
|
from ahriman.core.util import check_user
|
||||||
from ahriman.models.build_status import BuildStatusEnum
|
from ahriman.models.build_status import BuildStatusEnum
|
||||||
|
from ahriman.models.waiter import Waiter
|
||||||
|
|
||||||
|
|
||||||
class Lock(LazyLogging):
|
class Lock(LazyLogging):
|
||||||
@ -81,7 +81,7 @@ class Lock(LazyLogging):
|
|||||||
"""
|
"""
|
||||||
check web server version
|
check web server version
|
||||||
"""
|
"""
|
||||||
status = self.reporter.get_internal()
|
status = self.reporter.status_get()
|
||||||
if status.version is not None and status.version != __version__:
|
if status.version is not None and status.version != __version__:
|
||||||
self.logger.warning("status watcher version mismatch, our %s, their %s",
|
self.logger.warning("status watcher version mismatch, our %s, their %s",
|
||||||
__version__, status.version)
|
__version__, status.version)
|
||||||
@ -115,26 +115,18 @@ class Lock(LazyLogging):
|
|||||||
except FileExistsError:
|
except FileExistsError:
|
||||||
raise DuplicateRunError()
|
raise DuplicateRunError()
|
||||||
|
|
||||||
def watch(self, interval: int = 10) -> None:
|
def watch(self) -> None:
|
||||||
"""
|
"""
|
||||||
watch until lock disappear
|
watch until lock disappear
|
||||||
|
|
||||||
Args:
|
|
||||||
interval(int, optional): interval to check in seconds (Default value = 10)
|
|
||||||
"""
|
"""
|
||||||
def is_timed_out(start: float) -> bool:
|
|
||||||
since_start: float = time.monotonic() - start
|
|
||||||
return self.wait_timeout != 0 and since_start > self.wait_timeout
|
|
||||||
|
|
||||||
# there are reasons why we are not using inotify here. First of all, if we would use it, it would bring to
|
# there are reasons why we are not using inotify here. First of all, if we would use it, it would bring to
|
||||||
# race conditions because multiple processes will be notified in the same time. Secondly, it is good library,
|
# race conditions because multiple processes will be notified in the same time. Secondly, it is good library,
|
||||||
# but platform-specific, and we only need to check if file exists
|
# but platform-specific, and we only need to check if file exists
|
||||||
if self.path is None:
|
if self.path is None:
|
||||||
return
|
return
|
||||||
|
|
||||||
start_time = time.monotonic()
|
waiter = Waiter(self.wait_timeout)
|
||||||
while not is_timed_out(start_time) and self.path.is_file():
|
waiter.wait(self.path.is_file)
|
||||||
time.sleep(interval)
|
|
||||||
|
|
||||||
def __enter__(self) -> Self:
|
def __enter__(self) -> Self:
|
||||||
"""
|
"""
|
||||||
@ -154,7 +146,7 @@ class Lock(LazyLogging):
|
|||||||
self.check_version()
|
self.check_version()
|
||||||
self.watch()
|
self.watch()
|
||||||
self.create()
|
self.create()
|
||||||
self.reporter.update_self(BuildStatusEnum.Building)
|
self.reporter.status_update(BuildStatusEnum.Building)
|
||||||
return self
|
return self
|
||||||
|
|
||||||
def __exit__(self, exc_type: type[Exception] | None, exc_val: Exception | None,
|
def __exit__(self, exc_type: type[Exception] | None, exc_val: Exception | None,
|
||||||
@ -172,5 +164,5 @@ class Lock(LazyLogging):
|
|||||||
"""
|
"""
|
||||||
self.clear()
|
self.clear()
|
||||||
status = BuildStatusEnum.Success if exc_val is None else BuildStatusEnum.Failed
|
status = BuildStatusEnum.Success if exc_val is None else BuildStatusEnum.Failed
|
||||||
self.reporter.update_self(status)
|
self.reporter.status_update(status)
|
||||||
return False
|
return False
|
||||||
|
@ -31,17 +31,15 @@ class HttpLogHandler(logging.Handler):
|
|||||||
|
|
||||||
Attributes:
|
Attributes:
|
||||||
reporter(Client): build status reporter instance
|
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, suppress_errors: bool) -> None:
|
def __init__(self, configuration: Configuration, *, report: bool) -> None:
|
||||||
"""
|
"""
|
||||||
default constructor
|
default constructor
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
configuration(Configuration): configuration instance
|
configuration(Configuration): configuration instance
|
||||||
report(bool): force enable or disable reporting
|
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
|
# we don't really care about those parameters because they will be handled by the reporter
|
||||||
logging.Handler.__init__(self)
|
logging.Handler.__init__(self)
|
||||||
@ -49,7 +47,6 @@ class HttpLogHandler(logging.Handler):
|
|||||||
# client has to be imported here because of circular imports
|
# client has to be imported here because of circular imports
|
||||||
from ahriman.core.status.client import Client
|
from ahriman.core.status.client import Client
|
||||||
self.reporter = Client.load(configuration, report=report)
|
self.reporter = Client.load(configuration, report=report)
|
||||||
self.suppress_errors = suppress_errors
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def load(cls, configuration: Configuration, *, report: bool) -> Self:
|
def load(cls, configuration: Configuration, *, report: bool) -> Self:
|
||||||
@ -68,8 +65,7 @@ class HttpLogHandler(logging.Handler):
|
|||||||
if (handler := next((handler for handler in root.handlers if isinstance(handler, cls)), None)) is not None:
|
if (handler := next((handler for handler in root.handlers if isinstance(handler, cls)), None)) is not None:
|
||||||
return handler # there is already registered instance
|
return handler # there is already registered instance
|
||||||
|
|
||||||
suppress_errors = configuration.getboolean("settings", "suppress_http_log_errors", fallback=False)
|
handler = cls(configuration, report=report)
|
||||||
handler = cls(configuration, report=report, suppress_errors=suppress_errors)
|
|
||||||
root.addHandler(handler)
|
root.addHandler(handler)
|
||||||
|
|
||||||
return handler
|
return handler
|
||||||
@ -85,9 +81,4 @@ class HttpLogHandler(logging.Handler):
|
|||||||
if package_base is None:
|
if package_base is None:
|
||||||
return # in case if no package base supplied we need just skip log message
|
return # in case if no package base supplied we need just skip log message
|
||||||
|
|
||||||
try:
|
self.reporter.package_logs(package_base, record)
|
||||||
self.reporter.logs(package_base, record)
|
|
||||||
except Exception:
|
|
||||||
if self.suppress_errors:
|
|
||||||
return
|
|
||||||
self.handleError(record)
|
|
||||||
|
123
src/ahriman/core/report/remote_call.py
Normal file
123
src/ahriman/core/report/remote_call.py
Normal file
@ -0,0 +1,123 @@
|
|||||||
|
#
|
||||||
|
# Copyright (c) 2021-2023 ahriman team.
|
||||||
|
#
|
||||||
|
# This file is part of ahriman
|
||||||
|
# (see https://github.com/arcan1s/ahriman).
|
||||||
|
#
|
||||||
|
# This program is free software: you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU General Public License as published by
|
||||||
|
# the Free Software Foundation, either version 3 of the License, or
|
||||||
|
# (at your option) any later version.
|
||||||
|
#
|
||||||
|
# This program is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU General Public License
|
||||||
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
#
|
||||||
|
from ahriman.core.configuration import Configuration
|
||||||
|
from ahriman.core.report.report import Report
|
||||||
|
from ahriman.core.status.web_client import WebClient
|
||||||
|
from ahriman.models.package import Package
|
||||||
|
from ahriman.models.result import Result
|
||||||
|
from ahriman.models.waiter import Waiter
|
||||||
|
|
||||||
|
|
||||||
|
class RemoteCall(Report):
|
||||||
|
"""
|
||||||
|
trigger implementation which call remote service with update
|
||||||
|
|
||||||
|
Attributes:
|
||||||
|
client(WebClient): web client instance
|
||||||
|
update_aur(bool): check for AUR updates
|
||||||
|
update_local(bool): check for local packages update
|
||||||
|
update_manual(bool): check for manually built packages
|
||||||
|
wait_timeout(int): timeout to wait external process
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, architecture: str, configuration: Configuration, section: str) -> None:
|
||||||
|
"""
|
||||||
|
default constructor
|
||||||
|
|
||||||
|
Args:
|
||||||
|
architecture(str): repository architecture
|
||||||
|
configuration(Configuration): configuration instance
|
||||||
|
section(str): settings section name
|
||||||
|
"""
|
||||||
|
Report.__init__(self, architecture, configuration)
|
||||||
|
|
||||||
|
self.client = WebClient(configuration)
|
||||||
|
|
||||||
|
self.update_aur = configuration.getboolean(section, "aur", fallback=False)
|
||||||
|
self.update_local = configuration.getboolean(section, "local", fallback=False)
|
||||||
|
self.update_manual = configuration.getboolean(section, "manual", fallback=False)
|
||||||
|
|
||||||
|
self.wait_timeout = configuration.getint(section, "wait_timeout", fallback=-1)
|
||||||
|
|
||||||
|
def generate(self, packages: list[Package], result: Result) -> None:
|
||||||
|
"""
|
||||||
|
generate report for the specified packages
|
||||||
|
|
||||||
|
Args:
|
||||||
|
packages(list[Package]): list of packages to generate report
|
||||||
|
result(Result): build result
|
||||||
|
"""
|
||||||
|
process_id = self.remote_update()
|
||||||
|
self.remote_wait(process_id)
|
||||||
|
|
||||||
|
def is_process_alive(self, process_id: str) -> bool:
|
||||||
|
"""
|
||||||
|
check if process is alive
|
||||||
|
|
||||||
|
Args:
|
||||||
|
process_id(str): remote process id
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
bool: True in case if remote process is alive and False otherwise
|
||||||
|
"""
|
||||||
|
response = self.client.make_request("GET", f"{self.client.address}/api/v1/service/process/{process_id}")
|
||||||
|
if response is None:
|
||||||
|
return False
|
||||||
|
|
||||||
|
response_json = response.json()
|
||||||
|
is_alive: bool = response_json["is_alive"]
|
||||||
|
|
||||||
|
return is_alive
|
||||||
|
|
||||||
|
def remote_update(self) -> str | None:
|
||||||
|
"""
|
||||||
|
call remote server for update
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
str | None: remote process id on success and ``None`` otherwise
|
||||||
|
"""
|
||||||
|
response = self.client.make_request(
|
||||||
|
"POST",
|
||||||
|
f"{self.client.address}/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:
|
||||||
|
"""
|
||||||
|
wait for remote process termination
|
||||||
|
|
||||||
|
Args:
|
||||||
|
process_id(str | None): 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)
|
@ -93,6 +93,9 @@ class Report(LazyLogging):
|
|||||||
if provider == ReportSettings.Telegram:
|
if provider == ReportSettings.Telegram:
|
||||||
from ahriman.core.report.telegram import Telegram
|
from ahriman.core.report.telegram import Telegram
|
||||||
return Telegram(architecture, configuration, section)
|
return Telegram(architecture, configuration, section)
|
||||||
|
if provider == ReportSettings.RemoteCall:
|
||||||
|
from ahriman.core.report.remote_call import RemoteCall
|
||||||
|
return RemoteCall(architecture, configuration, section)
|
||||||
return Report(architecture, configuration) # should never happen
|
return Report(architecture, configuration) # should never happen
|
||||||
|
|
||||||
def generate(self, packages: list[Package], result: Result) -> None:
|
def generate(self, packages: list[Package], result: Result) -> None:
|
||||||
|
@ -191,6 +191,31 @@ class ReportTrigger(Trigger):
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
"remote-call": {
|
||||||
|
"type": "dict",
|
||||||
|
"schema": {
|
||||||
|
"type": {
|
||||||
|
"type": "string",
|
||||||
|
"allowed": ["remote-call"],
|
||||||
|
},
|
||||||
|
"aur": {
|
||||||
|
"type": "boolean",
|
||||||
|
"coerce": "boolean",
|
||||||
|
},
|
||||||
|
"local": {
|
||||||
|
"type": "boolean",
|
||||||
|
"coerce": "boolean",
|
||||||
|
},
|
||||||
|
"manual": {
|
||||||
|
"type": "boolean",
|
||||||
|
"coerce": "boolean",
|
||||||
|
},
|
||||||
|
"wait_timeout": {
|
||||||
|
"type": "integer",
|
||||||
|
"coerce": "integer",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
def __init__(self, architecture: str, configuration: Configuration) -> None:
|
def __init__(self, architecture: str, configuration: Configuration) -> None:
|
||||||
|
@ -121,7 +121,7 @@ class Executor(Cleaner):
|
|||||||
self.database.build_queue_clear(package_base)
|
self.database.build_queue_clear(package_base)
|
||||||
self.database.patches_remove(package_base, [])
|
self.database.patches_remove(package_base, [])
|
||||||
self.database.logs_remove(package_base, None)
|
self.database.logs_remove(package_base, None)
|
||||||
self.reporter.remove(package_base) # we only update status page in case of base removal
|
self.reporter.package_remove(package_base) # we only update status page in case of base removal
|
||||||
except Exception:
|
except Exception:
|
||||||
self.logger.exception("could not remove base %s", package_base)
|
self.logger.exception("could not remove base %s", package_base)
|
||||||
|
|
||||||
|
@ -60,7 +60,7 @@ class Client:
|
|||||||
return WebClient(configuration)
|
return WebClient(configuration)
|
||||||
return Client()
|
return Client()
|
||||||
|
|
||||||
def add(self, package: Package, status: BuildStatusEnum) -> None:
|
def package_add(self, package: Package, status: BuildStatusEnum) -> None:
|
||||||
"""
|
"""
|
||||||
add new package with status
|
add new package with status
|
||||||
|
|
||||||
@ -69,7 +69,7 @@ class Client:
|
|||||||
status(BuildStatusEnum): current package build status
|
status(BuildStatusEnum): current package build status
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def get(self, package_base: str | None) -> list[tuple[Package, BuildStatus]]:
|
def package_get(self, package_base: str | None) -> list[tuple[Package, BuildStatus]]:
|
||||||
"""
|
"""
|
||||||
get package status
|
get package status
|
||||||
|
|
||||||
@ -82,16 +82,7 @@ class Client:
|
|||||||
del package_base
|
del package_base
|
||||||
return []
|
return []
|
||||||
|
|
||||||
def get_internal(self) -> InternalStatus:
|
def package_logs(self, package_base: str, record: logging.LogRecord) -> None:
|
||||||
"""
|
|
||||||
get internal service status
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
InternalStatus: current internal (web) service status
|
|
||||||
"""
|
|
||||||
return InternalStatus(status=BuildStatus())
|
|
||||||
|
|
||||||
def logs(self, package_base: str, record: logging.LogRecord) -> None:
|
|
||||||
"""
|
"""
|
||||||
post log record
|
post log record
|
||||||
|
|
||||||
@ -100,7 +91,7 @@ class Client:
|
|||||||
record(logging.LogRecord): log record to post to api
|
record(logging.LogRecord): log record to post to api
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def remove(self, package_base: str) -> None:
|
def package_remove(self, package_base: str) -> None:
|
||||||
"""
|
"""
|
||||||
remove packages from watcher
|
remove packages from watcher
|
||||||
|
|
||||||
@ -108,7 +99,7 @@ class Client:
|
|||||||
package_base(str): package base to remove
|
package_base(str): package base to remove
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def update(self, package_base: str, status: BuildStatusEnum) -> None:
|
def package_update(self, package_base: str, status: BuildStatusEnum) -> None:
|
||||||
"""
|
"""
|
||||||
update package build status. Unlike ``add`` it does not update package properties
|
update package build status. Unlike ``add`` it does not update package properties
|
||||||
|
|
||||||
@ -117,14 +108,6 @@ class Client:
|
|||||||
status(BuildStatusEnum): current package build status
|
status(BuildStatusEnum): current package build status
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def update_self(self, status: BuildStatusEnum) -> None:
|
|
||||||
"""
|
|
||||||
update ahriman status itself
|
|
||||||
|
|
||||||
Args:
|
|
||||||
status(BuildStatusEnum): current ahriman status
|
|
||||||
"""
|
|
||||||
|
|
||||||
def set_building(self, package_base: str) -> None:
|
def set_building(self, package_base: str) -> None:
|
||||||
"""
|
"""
|
||||||
set package status to building
|
set package status to building
|
||||||
@ -132,7 +115,7 @@ class Client:
|
|||||||
Args:
|
Args:
|
||||||
package_base(str): package base to update
|
package_base(str): package base to update
|
||||||
"""
|
"""
|
||||||
return self.update(package_base, BuildStatusEnum.Building)
|
return self.package_update(package_base, BuildStatusEnum.Building)
|
||||||
|
|
||||||
def set_failed(self, package_base: str) -> None:
|
def set_failed(self, package_base: str) -> None:
|
||||||
"""
|
"""
|
||||||
@ -141,7 +124,7 @@ class Client:
|
|||||||
Args:
|
Args:
|
||||||
package_base(str): package base to update
|
package_base(str): package base to update
|
||||||
"""
|
"""
|
||||||
return self.update(package_base, BuildStatusEnum.Failed)
|
return self.package_update(package_base, BuildStatusEnum.Failed)
|
||||||
|
|
||||||
def set_pending(self, package_base: str) -> None:
|
def set_pending(self, package_base: str) -> None:
|
||||||
"""
|
"""
|
||||||
@ -150,7 +133,7 @@ class Client:
|
|||||||
Args:
|
Args:
|
||||||
package_base(str): package base to update
|
package_base(str): package base to update
|
||||||
"""
|
"""
|
||||||
return self.update(package_base, BuildStatusEnum.Pending)
|
return self.package_update(package_base, BuildStatusEnum.Pending)
|
||||||
|
|
||||||
def set_success(self, package: Package) -> None:
|
def set_success(self, package: Package) -> None:
|
||||||
"""
|
"""
|
||||||
@ -159,7 +142,7 @@ class Client:
|
|||||||
Args:
|
Args:
|
||||||
package(Package): current package properties
|
package(Package): current package properties
|
||||||
"""
|
"""
|
||||||
return self.add(package, BuildStatusEnum.Success)
|
return self.package_add(package, BuildStatusEnum.Success)
|
||||||
|
|
||||||
def set_unknown(self, package: Package) -> None:
|
def set_unknown(self, package: Package) -> None:
|
||||||
"""
|
"""
|
||||||
@ -168,4 +151,21 @@ class Client:
|
|||||||
Args:
|
Args:
|
||||||
package(Package): current package properties
|
package(Package): current package properties
|
||||||
"""
|
"""
|
||||||
return self.add(package, BuildStatusEnum.Unknown)
|
return self.package_add(package, BuildStatusEnum.Unknown)
|
||||||
|
|
||||||
|
def status_get(self) -> InternalStatus:
|
||||||
|
"""
|
||||||
|
get internal service status
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
InternalStatus: current internal (web) service status
|
||||||
|
"""
|
||||||
|
return InternalStatus(status=BuildStatus())
|
||||||
|
|
||||||
|
def status_update(self, status: BuildStatusEnum) -> None:
|
||||||
|
"""
|
||||||
|
update ahriman status itself
|
||||||
|
|
||||||
|
Args:
|
||||||
|
status(BuildStatusEnum): current ahriman status
|
||||||
|
"""
|
||||||
|
@ -22,6 +22,7 @@ import logging
|
|||||||
import requests
|
import requests
|
||||||
|
|
||||||
from collections.abc import Generator
|
from collections.abc import Generator
|
||||||
|
from typing import Any, Literal
|
||||||
from urllib.parse import quote_plus as urlencode
|
from urllib.parse import quote_plus as urlencode
|
||||||
|
|
||||||
from ahriman import __version__
|
from ahriman import __version__
|
||||||
@ -164,10 +165,7 @@ class WebClient(Client, LazyLogging):
|
|||||||
"username": self.user.username,
|
"username": self.user.username,
|
||||||
"password": self.user.password
|
"password": self.user.password
|
||||||
}
|
}
|
||||||
|
self.make_request("POST", self._login_url, json=payload, session=session)
|
||||||
with self.__get_session(session):
|
|
||||||
response = session.post(self._login_url, json=payload)
|
|
||||||
response.raise_for_status()
|
|
||||||
|
|
||||||
def _logs_url(self, package_base: str) -> str:
|
def _logs_url(self, package_base: str) -> str:
|
||||||
"""
|
"""
|
||||||
@ -195,7 +193,31 @@ class WebClient(Client, LazyLogging):
|
|||||||
suffix = f"/{package_base}" if package_base else ""
|
suffix = f"/{package_base}" if package_base else ""
|
||||||
return f"{self.address}/api/v1/packages{suffix}"
|
return f"{self.address}/api/v1/packages{suffix}"
|
||||||
|
|
||||||
def add(self, package: Package, status: BuildStatusEnum) -> None:
|
def make_request(self, method: Literal["DELETE", "GET", "POST"], url: str,
|
||||||
|
params: list[tuple[str, str]] | None = None, json: dict[str, Any] | None = None,
|
||||||
|
session: requests.Session | None = None) -> requests.Response | None:
|
||||||
|
"""
|
||||||
|
perform request with specified parameters
|
||||||
|
|
||||||
|
Args:
|
||||||
|
method(Literal["DELETE", "GET", "POST"]): HTTP method to call
|
||||||
|
url(str): remote url to call
|
||||||
|
params(list[tuple[str, str]] | None, optional): request query parameters (Default value = None)
|
||||||
|
json(dict[str, Any] | None, optional): request json parameters (Default value = None)
|
||||||
|
session(requests.Session | None, optional): session object if any (Default value = None)
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
requests.Response | None: response object or None in case of errors
|
||||||
|
"""
|
||||||
|
with self.__get_session(session) as _session:
|
||||||
|
response = _session.request(method, url, params=params, json=json)
|
||||||
|
response.raise_for_status()
|
||||||
|
return response
|
||||||
|
|
||||||
|
# noinspection PyUnreachableCode
|
||||||
|
return None
|
||||||
|
|
||||||
|
def package_add(self, package: Package, status: BuildStatusEnum) -> None:
|
||||||
"""
|
"""
|
||||||
add new package with status
|
add new package with status
|
||||||
|
|
||||||
@ -207,12 +229,9 @@ class WebClient(Client, LazyLogging):
|
|||||||
"status": status.value,
|
"status": status.value,
|
||||||
"package": package.view()
|
"package": package.view()
|
||||||
}
|
}
|
||||||
|
self.make_request("POST", self._package_url(package.base), json=payload)
|
||||||
|
|
||||||
with self.__get_session() as session:
|
def package_get(self, package_base: str | None) -> list[tuple[Package, BuildStatus]]:
|
||||||
response = session.post(self._package_url(package.base), json=payload)
|
|
||||||
response.raise_for_status()
|
|
||||||
|
|
||||||
def get(self, package_base: str | None) -> list[tuple[Package, BuildStatus]]:
|
|
||||||
"""
|
"""
|
||||||
get package status
|
get package status
|
||||||
|
|
||||||
@ -222,37 +241,17 @@ class WebClient(Client, LazyLogging):
|
|||||||
Returns:
|
Returns:
|
||||||
list[tuple[Package, BuildStatus]]: list of current package description and status if it has been found
|
list[tuple[Package, BuildStatus]]: list of current package description and status if it has been found
|
||||||
"""
|
"""
|
||||||
with self.__get_session() as session:
|
response = self.make_request("GET", self._package_url(package_base or ""))
|
||||||
response = session.get(self._package_url(package_base or ""))
|
if response is None:
|
||||||
response.raise_for_status()
|
|
||||||
|
|
||||||
status_json = response.json()
|
|
||||||
return [
|
|
||||||
(Package.from_json(package["package"]), BuildStatus.from_json(package["status"]))
|
|
||||||
for package in status_json
|
|
||||||
]
|
|
||||||
|
|
||||||
# noinspection PyUnreachableCode
|
|
||||||
return []
|
return []
|
||||||
|
|
||||||
def get_internal(self) -> InternalStatus:
|
response_json = response.json()
|
||||||
"""
|
return [
|
||||||
get internal service status
|
(Package.from_json(package["package"]), BuildStatus.from_json(package["status"]))
|
||||||
|
for package in response_json
|
||||||
|
]
|
||||||
|
|
||||||
Returns:
|
def package_logs(self, package_base: str, record: logging.LogRecord) -> None:
|
||||||
InternalStatus: current internal (web) service status
|
|
||||||
"""
|
|
||||||
with self.__get_session() as session:
|
|
||||||
response = session.get(self._status_url)
|
|
||||||
response.raise_for_status()
|
|
||||||
|
|
||||||
status_json = response.json()
|
|
||||||
return InternalStatus.from_json(status_json)
|
|
||||||
|
|
||||||
# noinspection PyUnreachableCode
|
|
||||||
return InternalStatus(status=BuildStatus())
|
|
||||||
|
|
||||||
def logs(self, package_base: str, record: logging.LogRecord) -> None:
|
|
||||||
"""
|
"""
|
||||||
post log record
|
post log record
|
||||||
|
|
||||||
@ -265,23 +264,18 @@ class WebClient(Client, LazyLogging):
|
|||||||
"message": record.getMessage(),
|
"message": record.getMessage(),
|
||||||
"process_id": record.process,
|
"process_id": record.process,
|
||||||
}
|
}
|
||||||
|
self.make_request("POST", self._logs_url(package_base), json=payload)
|
||||||
|
|
||||||
# in this method exception has to be handled outside in logger handler
|
def package_remove(self, package_base: str) -> None:
|
||||||
response = self.__session.post(self._logs_url(package_base), json=payload)
|
|
||||||
response.raise_for_status()
|
|
||||||
|
|
||||||
def remove(self, package_base: str) -> None:
|
|
||||||
"""
|
"""
|
||||||
remove packages from watcher
|
remove packages from watcher
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
package_base(str): basename to remove
|
package_base(str): basename to remove
|
||||||
"""
|
"""
|
||||||
with self.__get_session() as session:
|
self.make_request("DELETE", self._package_url(package_base))
|
||||||
response = session.delete(self._package_url(package_base))
|
|
||||||
response.raise_for_status()
|
|
||||||
|
|
||||||
def update(self, package_base: str, status: BuildStatusEnum) -> None:
|
def package_update(self, package_base: str, status: BuildStatusEnum) -> None:
|
||||||
"""
|
"""
|
||||||
update package build status. Unlike ``add`` it does not update package properties
|
update package build status. Unlike ``add`` it does not update package properties
|
||||||
|
|
||||||
@ -290,12 +284,23 @@ class WebClient(Client, LazyLogging):
|
|||||||
status(BuildStatusEnum): current package build status
|
status(BuildStatusEnum): current package build status
|
||||||
"""
|
"""
|
||||||
payload = {"status": status.value}
|
payload = {"status": status.value}
|
||||||
|
self.make_request("POST", self._package_url(package_base), json=payload)
|
||||||
|
|
||||||
with self.__get_session() as session:
|
def status_get(self) -> InternalStatus:
|
||||||
response = session.post(self._package_url(package_base), json=payload)
|
"""
|
||||||
response.raise_for_status()
|
get internal service status
|
||||||
|
|
||||||
def update_self(self, status: BuildStatusEnum) -> None:
|
Returns:
|
||||||
|
InternalStatus: current internal (web) service status
|
||||||
|
"""
|
||||||
|
response = self.make_request("GET", self._status_url)
|
||||||
|
if response is None:
|
||||||
|
return InternalStatus(status=BuildStatus())
|
||||||
|
|
||||||
|
response_json = response.json()
|
||||||
|
return InternalStatus.from_json(response_json)
|
||||||
|
|
||||||
|
def status_update(self, status: BuildStatusEnum) -> None:
|
||||||
"""
|
"""
|
||||||
update ahriman status itself
|
update ahriman status itself
|
||||||
|
|
||||||
@ -303,7 +308,4 @@ class WebClient(Client, LazyLogging):
|
|||||||
status(BuildStatusEnum): current ahriman status
|
status(BuildStatusEnum): current ahriman status
|
||||||
"""
|
"""
|
||||||
payload = {"status": status.value}
|
payload = {"status": status.value}
|
||||||
|
self.make_request("POST", self._status_url, json=payload)
|
||||||
with self.__get_session() as session:
|
|
||||||
response = session.post(self._status_url, json=payload)
|
|
||||||
response.raise_for_status()
|
|
||||||
|
@ -32,6 +32,7 @@ class ReportSettings(str, Enum):
|
|||||||
Email(ReportSettings): (class attribute) email report generation
|
Email(ReportSettings): (class attribute) email report generation
|
||||||
Console(ReportSettings): (class attribute) print result to console
|
Console(ReportSettings): (class attribute) print result to console
|
||||||
Telegram(ReportSettings): (class attribute) markdown report to telegram channel
|
Telegram(ReportSettings): (class attribute) markdown report to telegram channel
|
||||||
|
RemoteCall(ReportSettings): (class attribute) remote server call
|
||||||
"""
|
"""
|
||||||
|
|
||||||
Disabled = "disabled" # for testing purpose
|
Disabled = "disabled" # for testing purpose
|
||||||
@ -39,6 +40,7 @@ class ReportSettings(str, Enum):
|
|||||||
Email = "email"
|
Email = "email"
|
||||||
Console = "console"
|
Console = "console"
|
||||||
Telegram = "telegram"
|
Telegram = "telegram"
|
||||||
|
RemoteCall = "remote-call"
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def from_option(value: str) -> ReportSettings:
|
def from_option(value: str) -> ReportSettings:
|
||||||
@ -59,4 +61,6 @@ class ReportSettings(str, Enum):
|
|||||||
return ReportSettings.Console
|
return ReportSettings.Console
|
||||||
if value.lower() in ("telegram",):
|
if value.lower() in ("telegram",):
|
||||||
return ReportSettings.Telegram
|
return ReportSettings.Telegram
|
||||||
|
if value.lower() in ("remote-call",):
|
||||||
|
return ReportSettings.RemoteCall
|
||||||
return ReportSettings.Disabled
|
return ReportSettings.Disabled
|
||||||
|
72
src/ahriman/models/waiter.py
Normal file
72
src/ahriman/models/waiter.py
Normal file
@ -0,0 +1,72 @@
|
|||||||
|
#
|
||||||
|
# Copyright (c) 2021-2023 ahriman team.
|
||||||
|
#
|
||||||
|
# This file is part of ahriman
|
||||||
|
# (see https://github.com/arcan1s/ahriman).
|
||||||
|
#
|
||||||
|
# This program is free software: you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU General Public License as published by
|
||||||
|
# the Free Software Foundation, either version 3 of the License, or
|
||||||
|
# (at your option) any later version.
|
||||||
|
#
|
||||||
|
# This program is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU General Public License
|
||||||
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
#
|
||||||
|
import time
|
||||||
|
|
||||||
|
from collections.abc import Callable
|
||||||
|
from dataclasses import dataclass, field
|
||||||
|
from typing import ParamSpec
|
||||||
|
|
||||||
|
|
||||||
|
Params = ParamSpec("Params")
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass(frozen=True)
|
||||||
|
class Waiter:
|
||||||
|
"""
|
||||||
|
simple waiter implementation
|
||||||
|
|
||||||
|
Attributes:
|
||||||
|
interval(int): interval in seconds between checks
|
||||||
|
start_time(float): monotonic time of the waiter start. More likely must not be assigned explicitly
|
||||||
|
wait_timeout(int): timeout in seconds to wait for. Negative value will result in immediate exit. Zero value
|
||||||
|
means infinite timeout
|
||||||
|
"""
|
||||||
|
|
||||||
|
wait_timeout: int
|
||||||
|
start_time: float = field(default_factory=time.monotonic, kw_only=True)
|
||||||
|
interval: int = field(default=10, kw_only=True)
|
||||||
|
|
||||||
|
def is_timed_out(self) -> bool:
|
||||||
|
"""
|
||||||
|
check if timer is out
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
bool: True in case current monotonic time is more than ``Waiter.start_time`` and
|
||||||
|
``Waiter.wait_timeout`` doesn't equal to 0
|
||||||
|
"""
|
||||||
|
since_start: float = time.monotonic() - self.start_time
|
||||||
|
return self.wait_timeout != 0 and since_start > self.wait_timeout
|
||||||
|
|
||||||
|
def wait(self, in_progress: Callable[Params, bool], *args: Params.args, **kwargs: Params.kwargs) -> float:
|
||||||
|
"""
|
||||||
|
wait until requirements are not met
|
||||||
|
|
||||||
|
Args:
|
||||||
|
in_progress(Callable[Params, bool]): function to check if timer should wait for another cycle
|
||||||
|
*args(Params.args): positional arguments for check call
|
||||||
|
**kwargs(Params.kwargs): keyword arguments for check call
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
float: consumed time in seconds
|
||||||
|
"""
|
||||||
|
while not self.is_timed_out() and in_progress(*args, **kwargs):
|
||||||
|
time.sleep(self.interval)
|
||||||
|
|
||||||
|
return time.monotonic() - self.start_time
|
@ -36,8 +36,8 @@ def test_run(args: argparse.Namespace, configuration: Configuration, repository:
|
|||||||
"""
|
"""
|
||||||
args = _default_args(args)
|
args = _default_args(args)
|
||||||
mocker.patch("ahriman.core.repository.Repository.load", return_value=repository)
|
mocker.patch("ahriman.core.repository.Repository.load", return_value=repository)
|
||||||
application_mock = mocker.patch("ahriman.core.status.client.Client.get_internal")
|
application_mock = mocker.patch("ahriman.core.status.client.Client.status_get")
|
||||||
packages_mock = mocker.patch("ahriman.core.status.client.Client.get",
|
packages_mock = mocker.patch("ahriman.core.status.client.Client.package_get",
|
||||||
return_value=[(package_ahriman, BuildStatus(BuildStatusEnum.Success)),
|
return_value=[(package_ahriman, BuildStatus(BuildStatusEnum.Success)),
|
||||||
(package_python_schedule, BuildStatus(BuildStatusEnum.Failed))])
|
(package_python_schedule, BuildStatus(BuildStatusEnum.Failed))])
|
||||||
check_mock = mocker.patch("ahriman.application.handlers.Handler.check_if_empty")
|
check_mock = mocker.patch("ahriman.application.handlers.Handler.check_if_empty")
|
||||||
@ -58,8 +58,8 @@ def test_run_empty_exception(args: argparse.Namespace, configuration: Configurat
|
|||||||
args = _default_args(args)
|
args = _default_args(args)
|
||||||
args.exit_code = True
|
args.exit_code = True
|
||||||
mocker.patch("ahriman.core.repository.Repository.load", return_value=repository)
|
mocker.patch("ahriman.core.repository.Repository.load", return_value=repository)
|
||||||
mocker.patch("ahriman.core.status.client.Client.get_internal")
|
mocker.patch("ahriman.core.status.client.Client.status_get")
|
||||||
mocker.patch("ahriman.core.status.client.Client.get", return_value=[])
|
mocker.patch("ahriman.core.status.client.Client.package_get", return_value=[])
|
||||||
check_mock = mocker.patch("ahriman.application.handlers.Handler.check_if_empty")
|
check_mock = mocker.patch("ahriman.application.handlers.Handler.check_if_empty")
|
||||||
|
|
||||||
Status.run(args, "x86_64", configuration, report=False)
|
Status.run(args, "x86_64", configuration, report=False)
|
||||||
@ -74,7 +74,7 @@ def test_run_verbose(args: argparse.Namespace, configuration: Configuration, rep
|
|||||||
args = _default_args(args)
|
args = _default_args(args)
|
||||||
args.info = True
|
args.info = True
|
||||||
mocker.patch("ahriman.core.repository.Repository.load", return_value=repository)
|
mocker.patch("ahriman.core.repository.Repository.load", return_value=repository)
|
||||||
mocker.patch("ahriman.core.status.client.Client.get",
|
mocker.patch("ahriman.core.status.client.Client.package_get",
|
||||||
return_value=[(package_ahriman, BuildStatus(BuildStatusEnum.Success))])
|
return_value=[(package_ahriman, BuildStatus(BuildStatusEnum.Success))])
|
||||||
print_mock = mocker.patch("ahriman.core.formatters.Printer.print")
|
print_mock = mocker.patch("ahriman.core.formatters.Printer.print")
|
||||||
|
|
||||||
@ -90,7 +90,7 @@ def test_run_with_package_filter(args: argparse.Namespace, configuration: Config
|
|||||||
args = _default_args(args)
|
args = _default_args(args)
|
||||||
args.package = [package_ahriman.base]
|
args.package = [package_ahriman.base]
|
||||||
mocker.patch("ahriman.core.repository.Repository.load", return_value=repository)
|
mocker.patch("ahriman.core.repository.Repository.load", return_value=repository)
|
||||||
packages_mock = mocker.patch("ahriman.core.status.client.Client.get",
|
packages_mock = mocker.patch("ahriman.core.status.client.Client.package_get",
|
||||||
return_value=[(package_ahriman, BuildStatus(BuildStatusEnum.Success))])
|
return_value=[(package_ahriman, BuildStatus(BuildStatusEnum.Success))])
|
||||||
|
|
||||||
Status.run(args, "x86_64", configuration, report=False)
|
Status.run(args, "x86_64", configuration, report=False)
|
||||||
@ -104,7 +104,7 @@ def test_run_by_status(args: argparse.Namespace, configuration: Configuration, r
|
|||||||
"""
|
"""
|
||||||
args = _default_args(args)
|
args = _default_args(args)
|
||||||
args.status = BuildStatusEnum.Failed
|
args.status = BuildStatusEnum.Failed
|
||||||
mocker.patch("ahriman.core.status.client.Client.get",
|
mocker.patch("ahriman.core.status.client.Client.package_get",
|
||||||
return_value=[(package_ahriman, BuildStatus(BuildStatusEnum.Success)),
|
return_value=[(package_ahriman, BuildStatus(BuildStatusEnum.Success)),
|
||||||
(package_python_schedule, BuildStatus(BuildStatusEnum.Failed))])
|
(package_python_schedule, BuildStatus(BuildStatusEnum.Failed))])
|
||||||
mocker.patch("ahriman.core.repository.Repository.load", return_value=repository)
|
mocker.patch("ahriman.core.repository.Repository.load", return_value=repository)
|
||||||
|
@ -34,7 +34,7 @@ def test_run(args: argparse.Namespace, configuration: Configuration, repository:
|
|||||||
"""
|
"""
|
||||||
args = _default_args(args)
|
args = _default_args(args)
|
||||||
mocker.patch("ahriman.core.repository.Repository.load", return_value=repository)
|
mocker.patch("ahriman.core.repository.Repository.load", return_value=repository)
|
||||||
update_self_mock = mocker.patch("ahriman.core.status.client.Client.update_self")
|
update_self_mock = mocker.patch("ahriman.core.status.client.Client.status_update")
|
||||||
|
|
||||||
StatusUpdate.run(args, "x86_64", configuration, report=False)
|
StatusUpdate.run(args, "x86_64", configuration, report=False)
|
||||||
update_self_mock.assert_called_once_with(args.status)
|
update_self_mock.assert_called_once_with(args.status)
|
||||||
@ -48,7 +48,7 @@ def test_run_packages(args: argparse.Namespace, configuration: Configuration, re
|
|||||||
args = _default_args(args)
|
args = _default_args(args)
|
||||||
args.package = [package_ahriman.base]
|
args.package = [package_ahriman.base]
|
||||||
mocker.patch("ahriman.core.repository.Repository.load", return_value=repository)
|
mocker.patch("ahriman.core.repository.Repository.load", return_value=repository)
|
||||||
update_mock = mocker.patch("ahriman.core.status.client.Client.update")
|
update_mock = mocker.patch("ahriman.core.status.client.Client.package_update")
|
||||||
|
|
||||||
StatusUpdate.run(args, "x86_64", configuration, report=False)
|
StatusUpdate.run(args, "x86_64", configuration, report=False)
|
||||||
update_mock.assert_called_once_with(package_ahriman.base, args.status)
|
update_mock.assert_called_once_with(package_ahriman.base, args.status)
|
||||||
@ -63,7 +63,7 @@ def test_run_remove(args: argparse.Namespace, configuration: Configuration, repo
|
|||||||
args.package = [package_ahriman.base]
|
args.package = [package_ahriman.base]
|
||||||
args.action = Action.Remove
|
args.action = Action.Remove
|
||||||
mocker.patch("ahriman.core.repository.Repository.load", return_value=repository)
|
mocker.patch("ahriman.core.repository.Repository.load", return_value=repository)
|
||||||
update_mock = mocker.patch("ahriman.core.status.client.Client.remove")
|
update_mock = mocker.patch("ahriman.core.status.client.Client.package_remove")
|
||||||
|
|
||||||
StatusUpdate.run(args, "x86_64", configuration, report=False)
|
StatusUpdate.run(args, "x86_64", configuration, report=False)
|
||||||
update_mock.assert_called_once_with(package_ahriman.base)
|
update_mock.assert_called_once_with(package_ahriman.base)
|
||||||
|
@ -67,6 +67,7 @@ def test_schema(configuration: Configuration) -> None:
|
|||||||
assert schema.pop("keyring-generator")
|
assert schema.pop("keyring-generator")
|
||||||
assert schema.pop("mirrorlist")
|
assert schema.pop("mirrorlist")
|
||||||
assert schema.pop("mirrorlist-generator")
|
assert schema.pop("mirrorlist-generator")
|
||||||
|
assert schema.pop("remote-call")
|
||||||
assert schema.pop("remote-pull")
|
assert schema.pop("remote-pull")
|
||||||
assert schema.pop("remote-push")
|
assert schema.pop("remote-push")
|
||||||
assert schema.pop("report")
|
assert schema.pop("report")
|
||||||
|
@ -32,7 +32,7 @@ def test_check_version(lock: Lock, mocker: MockerFixture) -> None:
|
|||||||
"""
|
"""
|
||||||
must check version correctly
|
must check version correctly
|
||||||
"""
|
"""
|
||||||
mocker.patch("ahriman.core.status.client.Client.get_internal",
|
mocker.patch("ahriman.core.status.client.Client.status_get",
|
||||||
return_value=InternalStatus(status=BuildStatus(), version=__version__))
|
return_value=InternalStatus(status=BuildStatus(), version=__version__))
|
||||||
logging_mock = mocker.patch("logging.Logger.warning")
|
logging_mock = mocker.patch("logging.Logger.warning")
|
||||||
|
|
||||||
@ -44,7 +44,7 @@ def test_check_version_mismatch(lock: Lock, mocker: MockerFixture) -> None:
|
|||||||
"""
|
"""
|
||||||
must check mismatched version correctly
|
must check mismatched version correctly
|
||||||
"""
|
"""
|
||||||
mocker.patch("ahriman.core.status.client.Client.get_internal",
|
mocker.patch("ahriman.core.status.client.Client.status_get",
|
||||||
return_value=InternalStatus(status=BuildStatus(), version="version"))
|
return_value=InternalStatus(status=BuildStatus(), version="version"))
|
||||||
logging_mock = mocker.patch("logging.Logger.warning")
|
logging_mock = mocker.patch("logging.Logger.warning")
|
||||||
|
|
||||||
@ -156,30 +156,13 @@ def test_create_unsafe(lock: Lock) -> None:
|
|||||||
|
|
||||||
def test_watch(lock: Lock, mocker: MockerFixture) -> None:
|
def test_watch(lock: Lock, mocker: MockerFixture) -> None:
|
||||||
"""
|
"""
|
||||||
must check if lock file exists in cycle
|
must check if lock file exists
|
||||||
"""
|
"""
|
||||||
mocker.patch("pathlib.Path.is_file", return_value=False)
|
wait_mock = mocker.patch("ahriman.models.waiter.Waiter.wait")
|
||||||
lock.watch()
|
|
||||||
|
|
||||||
|
|
||||||
def test_watch_wait(lock: Lock, mocker: MockerFixture) -> None:
|
|
||||||
"""
|
|
||||||
must wait until file will disappear
|
|
||||||
"""
|
|
||||||
mocker.patch("pathlib.Path.is_file", side_effect=[True, False])
|
|
||||||
lock.path = Path(tempfile.mktemp()) # nosec
|
lock.path = Path(tempfile.mktemp()) # nosec
|
||||||
lock.wait_timeout = 1
|
|
||||||
|
|
||||||
lock.watch(1)
|
|
||||||
|
|
||||||
|
|
||||||
def test_watch_empty_timeout(lock: Lock, mocker: MockerFixture) -> None:
|
|
||||||
"""
|
|
||||||
must skip watch on empty timeout
|
|
||||||
"""
|
|
||||||
mocker.patch("pathlib.Path.is_file", return_value=True)
|
|
||||||
lock.path = Path(tempfile.mktemp()) # nosec
|
|
||||||
lock.watch()
|
lock.watch()
|
||||||
|
wait_mock.assert_called_once_with(lock.path.is_file)
|
||||||
|
|
||||||
|
|
||||||
def test_watch_skip(lock: Lock, mocker: MockerFixture) -> None:
|
def test_watch_skip(lock: Lock, mocker: MockerFixture) -> None:
|
||||||
@ -199,7 +182,7 @@ def test_enter(lock: Lock, mocker: MockerFixture) -> None:
|
|||||||
watch_mock = mocker.patch("ahriman.application.lock.Lock.watch")
|
watch_mock = mocker.patch("ahriman.application.lock.Lock.watch")
|
||||||
clear_mock = mocker.patch("ahriman.application.lock.Lock.clear")
|
clear_mock = mocker.patch("ahriman.application.lock.Lock.clear")
|
||||||
create_mock = mocker.patch("ahriman.application.lock.Lock.create")
|
create_mock = mocker.patch("ahriman.application.lock.Lock.create")
|
||||||
update_status_mock = mocker.patch("ahriman.core.status.client.Client.update_self")
|
update_status_mock = mocker.patch("ahriman.core.status.client.Client.status_update")
|
||||||
|
|
||||||
with lock:
|
with lock:
|
||||||
pass
|
pass
|
||||||
@ -218,7 +201,7 @@ def test_exit_with_exception(lock: Lock, mocker: MockerFixture) -> None:
|
|||||||
mocker.patch("ahriman.application.lock.Lock.check_user")
|
mocker.patch("ahriman.application.lock.Lock.check_user")
|
||||||
mocker.patch("ahriman.application.lock.Lock.clear")
|
mocker.patch("ahriman.application.lock.Lock.clear")
|
||||||
mocker.patch("ahriman.application.lock.Lock.create")
|
mocker.patch("ahriman.application.lock.Lock.create")
|
||||||
update_status_mock = mocker.patch("ahriman.core.status.client.Client.update_self")
|
update_status_mock = mocker.patch("ahriman.core.status.client.Client.status_update")
|
||||||
|
|
||||||
with pytest.raises(Exception):
|
with pytest.raises(Exception):
|
||||||
with lock:
|
with lock:
|
||||||
|
@ -40,48 +40,20 @@ def test_emit(configuration: Configuration, log_record: logging.LogRecord, packa
|
|||||||
must emit log record to reporter
|
must emit log record to reporter
|
||||||
"""
|
"""
|
||||||
log_record.package_base = package_ahriman.base
|
log_record.package_base = package_ahriman.base
|
||||||
log_mock = mocker.patch("ahriman.core.status.client.Client.logs")
|
log_mock = mocker.patch("ahriman.core.status.client.Client.package_logs")
|
||||||
|
|
||||||
handler = HttpLogHandler(configuration, report=False, suppress_errors=False)
|
handler = HttpLogHandler(configuration, report=False)
|
||||||
|
|
||||||
handler.emit(log_record)
|
handler.emit(log_record)
|
||||||
log_mock.assert_called_once_with(package_ahriman.base, log_record)
|
log_mock.assert_called_once_with(package_ahriman.base, 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_base = package_ahriman.base
|
|
||||||
mocker.patch("ahriman.core.status.client.Client.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_base = package_ahriman.base
|
|
||||||
mocker.patch("ahriman.core.status.client.Client.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:
|
def test_emit_skip(configuration: Configuration, log_record: logging.LogRecord, mocker: MockerFixture) -> None:
|
||||||
"""
|
"""
|
||||||
must skip log record posting if no package base set
|
must skip log record posting if no package base set
|
||||||
"""
|
"""
|
||||||
log_mock = mocker.patch("ahriman.core.status.client.Client.logs")
|
log_mock = mocker.patch("ahriman.core.status.client.Client.package_logs")
|
||||||
handler = HttpLogHandler(configuration, report=False, suppress_errors=False)
|
handler = HttpLogHandler(configuration, report=False)
|
||||||
|
|
||||||
handler.emit(log_record)
|
handler.emit(log_record)
|
||||||
log_mock.assert_not_called()
|
log_mock.assert_not_called()
|
||||||
|
20
tests/ahriman/core/report/conftest.py
Normal file
20
tests/ahriman/core/report/conftest.py
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
import pytest
|
||||||
|
|
||||||
|
from ahriman.core.configuration import Configuration
|
||||||
|
from ahriman.core.report.remote_call import RemoteCall
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def remote_call(configuration: Configuration) -> RemoteCall:
|
||||||
|
"""
|
||||||
|
fixture for remote update trigger
|
||||||
|
|
||||||
|
Args:
|
||||||
|
configuration(Configuration): configuration fixture
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
RemoteCall: remote update trigger test instance
|
||||||
|
"""
|
||||||
|
configuration.set_option("web", "host", "localhost")
|
||||||
|
configuration.set_option("web", "port", "8080")
|
||||||
|
return RemoteCall("x86_64", configuration, "remote-call")
|
85
tests/ahriman/core/report/test_remote_call.py
Normal file
85
tests/ahriman/core/report/test_remote_call.py
Normal file
@ -0,0 +1,85 @@
|
|||||||
|
import pytest
|
||||||
|
import requests
|
||||||
|
|
||||||
|
from pytest_mock import MockerFixture
|
||||||
|
|
||||||
|
from ahriman.core.report.remote_call import RemoteCall
|
||||||
|
from ahriman.models.result import Result
|
||||||
|
|
||||||
|
|
||||||
|
def test_generate(remote_call: RemoteCall, mocker: MockerFixture) -> None:
|
||||||
|
"""
|
||||||
|
must correctly call client
|
||||||
|
"""
|
||||||
|
update_mock = mocker.patch("ahriman.core.report.remote_call.RemoteCall.remote_update", return_value="id")
|
||||||
|
wait_mock = mocker.patch("ahriman.core.report.remote_call.RemoteCall.remote_wait")
|
||||||
|
|
||||||
|
remote_call.generate([], Result())
|
||||||
|
update_mock.assert_called_once_with()
|
||||||
|
wait_mock.assert_called_once_with("id")
|
||||||
|
|
||||||
|
|
||||||
|
def test_is_process_alive(remote_call: RemoteCall, mocker: MockerFixture) -> None:
|
||||||
|
"""
|
||||||
|
must correctly define if process is alive
|
||||||
|
"""
|
||||||
|
response_obj = requests.Response()
|
||||||
|
response_obj._content = """{"is_alive": true}""".encode("utf8")
|
||||||
|
response_obj.status_code = 200
|
||||||
|
|
||||||
|
request_mock = mocker.patch("ahriman.core.status.web_client.WebClient.make_request", return_value=response_obj)
|
||||||
|
|
||||||
|
assert remote_call.is_process_alive("id")
|
||||||
|
request_mock.assert_called_once_with("GET", f"{remote_call.client.address}/api/v1/service/process/id")
|
||||||
|
|
||||||
|
|
||||||
|
def test_is_process_alive_unknown(remote_call: RemoteCall, mocker: MockerFixture) -> None:
|
||||||
|
"""
|
||||||
|
must correctly define if process is unknown
|
||||||
|
"""
|
||||||
|
mocker.patch("ahriman.core.status.web_client.WebClient.make_request", return_value=None)
|
||||||
|
assert not remote_call.is_process_alive("id")
|
||||||
|
|
||||||
|
|
||||||
|
def test_remote_update(remote_call: RemoteCall, mocker: MockerFixture) -> None:
|
||||||
|
"""
|
||||||
|
must call remote server for update process
|
||||||
|
"""
|
||||||
|
response_obj = requests.Response()
|
||||||
|
response_obj._content = """{"process_id": "id"}""".encode("utf8")
|
||||||
|
response_obj.status_code = 200
|
||||||
|
|
||||||
|
request_mock = mocker.patch("ahriman.core.status.web_client.WebClient.make_request", return_value=response_obj)
|
||||||
|
|
||||||
|
assert remote_call.remote_update() == "id"
|
||||||
|
request_mock.assert_called_once_with("POST", f"{remote_call.client.address}/api/v1/service/update", json={
|
||||||
|
"aur": False,
|
||||||
|
"local": False,
|
||||||
|
"manual": True,
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
|
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
|
||||||
|
"""
|
||||||
|
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()
|
@ -24,6 +24,7 @@ def test_report_dummy(configuration: Configuration, result: Result, mocker: Mock
|
|||||||
"""
|
"""
|
||||||
mocker.patch("ahriman.models.report_settings.ReportSettings.from_option", return_value=ReportSettings.Disabled)
|
mocker.patch("ahriman.models.report_settings.ReportSettings.from_option", return_value=ReportSettings.Disabled)
|
||||||
report_mock = mocker.patch("ahriman.core.report.report.Report.generate")
|
report_mock = mocker.patch("ahriman.core.report.report.Report.generate")
|
||||||
|
|
||||||
Report.load("x86_64", configuration, "disabled").run(result, [])
|
Report.load("x86_64", configuration, "disabled").run(result, [])
|
||||||
report_mock.assert_called_once_with([], result)
|
report_mock.assert_called_once_with([], result)
|
||||||
|
|
||||||
@ -55,6 +56,18 @@ def test_report_html(configuration: Configuration, result: Result, mocker: Mocke
|
|||||||
report_mock.assert_called_once_with([], result)
|
report_mock.assert_called_once_with([], result)
|
||||||
|
|
||||||
|
|
||||||
|
def test_report_remote_call(configuration: Configuration, result: Result, mocker: MockerFixture) -> None:
|
||||||
|
"""
|
||||||
|
must instantiate remote call trigger
|
||||||
|
"""
|
||||||
|
configuration.set_option("web", "host", "localhost")
|
||||||
|
configuration.set_option("web", "port", "8080")
|
||||||
|
report_mock = mocker.patch("ahriman.core.report.remote_call.RemoteCall.generate")
|
||||||
|
|
||||||
|
Report.load("x86_64", configuration, "remote-call").run(result, [])
|
||||||
|
report_mock.assert_called_once_with([], result)
|
||||||
|
|
||||||
|
|
||||||
def test_report_telegram(configuration: Configuration, result: Result, mocker: MockerFixture) -> None:
|
def test_report_telegram(configuration: Configuration, result: Result, mocker: MockerFixture) -> None:
|
||||||
"""
|
"""
|
||||||
must generate telegram report
|
must generate telegram report
|
||||||
|
@ -85,7 +85,7 @@ def test_process_remove_base(executor: Executor, package_ahriman: Package, mocke
|
|||||||
build_queue_mock = mocker.patch("ahriman.core.database.SQLite.build_queue_clear")
|
build_queue_mock = mocker.patch("ahriman.core.database.SQLite.build_queue_clear")
|
||||||
patches_mock = mocker.patch("ahriman.core.database.SQLite.patches_remove")
|
patches_mock = mocker.patch("ahriman.core.database.SQLite.patches_remove")
|
||||||
logs_mock = mocker.patch("ahriman.core.database.SQLite.logs_remove")
|
logs_mock = mocker.patch("ahriman.core.database.SQLite.logs_remove")
|
||||||
status_client_mock = mocker.patch("ahriman.core.status.client.Client.remove")
|
status_client_mock = mocker.patch("ahriman.core.status.client.Client.package_remove")
|
||||||
|
|
||||||
executor.process_remove([package_ahriman.base])
|
executor.process_remove([package_ahriman.base])
|
||||||
# must remove via alpm wrapper
|
# must remove via alpm wrapper
|
||||||
@ -106,7 +106,7 @@ def test_process_remove_base_multiple(executor: Executor, package_python_schedul
|
|||||||
"""
|
"""
|
||||||
mocker.patch("ahriman.core.repository.executor.Executor.packages", return_value=[package_python_schedule])
|
mocker.patch("ahriman.core.repository.executor.Executor.packages", return_value=[package_python_schedule])
|
||||||
repo_remove_mock = mocker.patch("ahriman.core.alpm.repo.Repo.remove")
|
repo_remove_mock = mocker.patch("ahriman.core.alpm.repo.Repo.remove")
|
||||||
status_client_mock = mocker.patch("ahriman.core.status.client.Client.remove")
|
status_client_mock = mocker.patch("ahriman.core.status.client.Client.package_remove")
|
||||||
|
|
||||||
executor.process_remove([package_python_schedule.base])
|
executor.process_remove([package_python_schedule.base])
|
||||||
# must remove via alpm wrapper
|
# must remove via alpm wrapper
|
||||||
@ -125,7 +125,7 @@ def test_process_remove_base_single(executor: Executor, package_python_schedule:
|
|||||||
"""
|
"""
|
||||||
mocker.patch("ahriman.core.repository.executor.Executor.packages", return_value=[package_python_schedule])
|
mocker.patch("ahriman.core.repository.executor.Executor.packages", return_value=[package_python_schedule])
|
||||||
repo_remove_mock = mocker.patch("ahriman.core.alpm.repo.Repo.remove")
|
repo_remove_mock = mocker.patch("ahriman.core.alpm.repo.Repo.remove")
|
||||||
status_client_mock = mocker.patch("ahriman.core.status.client.Client.remove")
|
status_client_mock = mocker.patch("ahriman.core.status.client.Client.package_remove")
|
||||||
|
|
||||||
executor.process_remove(["python2-schedule"])
|
executor.process_remove(["python2-schedule"])
|
||||||
# must remove via alpm wrapper
|
# must remove via alpm wrapper
|
||||||
@ -171,7 +171,7 @@ def test_process_remove_unknown(executor: Executor, package_ahriman: Package, mo
|
|||||||
"""
|
"""
|
||||||
mocker.patch("ahriman.core.repository.executor.Executor.packages", return_value=[])
|
mocker.patch("ahriman.core.repository.executor.Executor.packages", return_value=[])
|
||||||
repo_remove_mock = mocker.patch("ahriman.core.alpm.repo.Repo.remove")
|
repo_remove_mock = mocker.patch("ahriman.core.alpm.repo.Repo.remove")
|
||||||
status_client_mock = mocker.patch("ahriman.core.status.client.Client.remove")
|
status_client_mock = mocker.patch("ahriman.core.status.client.Client.package_remove")
|
||||||
|
|
||||||
executor.process_remove([package_ahriman.base])
|
executor.process_remove([package_ahriman.base])
|
||||||
repo_remove_mock.assert_not_called()
|
repo_remove_mock.assert_not_called()
|
||||||
|
@ -51,64 +51,47 @@ def test_load_full_client_from_unix_socket(configuration: Configuration) -> None
|
|||||||
assert isinstance(Client.load(configuration, report=True), WebClient)
|
assert isinstance(Client.load(configuration, report=True), WebClient)
|
||||||
|
|
||||||
|
|
||||||
def test_add(client: Client, package_ahriman: Package) -> None:
|
def test_package_add(client: Client, package_ahriman: Package) -> None:
|
||||||
"""
|
"""
|
||||||
must process package addition without errors
|
must process package addition without errors
|
||||||
"""
|
"""
|
||||||
client.add(package_ahriman, BuildStatusEnum.Unknown)
|
client.package_add(package_ahriman, BuildStatusEnum.Unknown)
|
||||||
|
|
||||||
|
|
||||||
def test_get(client: Client, package_ahriman: Package) -> None:
|
def test_package_get(client: Client, package_ahriman: Package) -> None:
|
||||||
"""
|
"""
|
||||||
must return empty package list
|
must return empty package list
|
||||||
"""
|
"""
|
||||||
assert client.get(package_ahriman.base) == []
|
assert client.package_get(package_ahriman.base) == []
|
||||||
assert client.get(None) == []
|
assert client.package_get(None) == []
|
||||||
|
|
||||||
|
|
||||||
def test_get_internal(client: Client) -> None:
|
def test_package_log(client: Client, package_ahriman: Package, log_record: logging.LogRecord) -> None:
|
||||||
"""
|
|
||||||
must return dummy status for web service
|
|
||||||
"""
|
|
||||||
actual = client.get_internal()
|
|
||||||
expected = InternalStatus(status=BuildStatus(timestamp=actual.status.timestamp))
|
|
||||||
|
|
||||||
assert actual == expected
|
|
||||||
|
|
||||||
|
|
||||||
def test_log(client: Client, package_ahriman: Package, log_record: logging.LogRecord) -> None:
|
|
||||||
"""
|
"""
|
||||||
must process log record without errors
|
must process log record without errors
|
||||||
"""
|
"""
|
||||||
client.logs(package_ahriman.base, log_record)
|
client.package_logs(package_ahriman.base, log_record)
|
||||||
|
|
||||||
|
|
||||||
def test_remove(client: Client, package_ahriman: Package) -> None:
|
def test_package_remove(client: Client, package_ahriman: Package) -> None:
|
||||||
"""
|
"""
|
||||||
must process remove without errors
|
must process remove without errors
|
||||||
"""
|
"""
|
||||||
client.remove(package_ahriman.base)
|
client.package_remove(package_ahriman.base)
|
||||||
|
|
||||||
|
|
||||||
def test_update(client: Client, package_ahriman: Package) -> None:
|
def test_package_update(client: Client, package_ahriman: Package) -> None:
|
||||||
"""
|
"""
|
||||||
must update package status without errors
|
must update package status without errors
|
||||||
"""
|
"""
|
||||||
client.update(package_ahriman.base, BuildStatusEnum.Unknown)
|
client.package_update(package_ahriman.base, BuildStatusEnum.Unknown)
|
||||||
|
|
||||||
|
|
||||||
def test_update_self(client: Client) -> None:
|
|
||||||
"""
|
|
||||||
must update self status without errors
|
|
||||||
"""
|
|
||||||
client.update_self(BuildStatusEnum.Unknown)
|
|
||||||
|
|
||||||
|
|
||||||
def test_set_building(client: Client, package_ahriman: Package, mocker: MockerFixture) -> None:
|
def test_set_building(client: Client, package_ahriman: Package, mocker: MockerFixture) -> None:
|
||||||
"""
|
"""
|
||||||
must set building status to the package
|
must set building status to the package
|
||||||
"""
|
"""
|
||||||
update_mock = mocker.patch("ahriman.core.status.client.Client.update")
|
update_mock = mocker.patch("ahriman.core.status.client.Client.package_update")
|
||||||
client.set_building(package_ahriman.base)
|
client.set_building(package_ahriman.base)
|
||||||
|
|
||||||
update_mock.assert_called_once_with(package_ahriman.base, BuildStatusEnum.Building)
|
update_mock.assert_called_once_with(package_ahriman.base, BuildStatusEnum.Building)
|
||||||
@ -118,7 +101,7 @@ def test_set_failed(client: Client, package_ahriman: Package, mocker: MockerFixt
|
|||||||
"""
|
"""
|
||||||
must set failed status to the package
|
must set failed status to the package
|
||||||
"""
|
"""
|
||||||
update_mock = mocker.patch("ahriman.core.status.client.Client.update")
|
update_mock = mocker.patch("ahriman.core.status.client.Client.package_update")
|
||||||
client.set_failed(package_ahriman.base)
|
client.set_failed(package_ahriman.base)
|
||||||
|
|
||||||
update_mock.assert_called_once_with(package_ahriman.base, BuildStatusEnum.Failed)
|
update_mock.assert_called_once_with(package_ahriman.base, BuildStatusEnum.Failed)
|
||||||
@ -128,7 +111,7 @@ def test_set_pending(client: Client, package_ahriman: Package, mocker: MockerFix
|
|||||||
"""
|
"""
|
||||||
must set building status to the package
|
must set building status to the package
|
||||||
"""
|
"""
|
||||||
update_mock = mocker.patch("ahriman.core.status.client.Client.update")
|
update_mock = mocker.patch("ahriman.core.status.client.Client.package_update")
|
||||||
client.set_pending(package_ahriman.base)
|
client.set_pending(package_ahriman.base)
|
||||||
|
|
||||||
update_mock.assert_called_once_with(package_ahriman.base, BuildStatusEnum.Pending)
|
update_mock.assert_called_once_with(package_ahriman.base, BuildStatusEnum.Pending)
|
||||||
@ -138,7 +121,7 @@ def test_set_success(client: Client, package_ahriman: Package, mocker: MockerFix
|
|||||||
"""
|
"""
|
||||||
must set success status to the package
|
must set success status to the package
|
||||||
"""
|
"""
|
||||||
add_mock = mocker.patch("ahriman.core.status.client.Client.add")
|
add_mock = mocker.patch("ahriman.core.status.client.Client.package_add")
|
||||||
client.set_success(package_ahriman)
|
client.set_success(package_ahriman)
|
||||||
|
|
||||||
add_mock.assert_called_once_with(package_ahriman, BuildStatusEnum.Success)
|
add_mock.assert_called_once_with(package_ahriman, BuildStatusEnum.Success)
|
||||||
@ -148,7 +131,24 @@ def test_set_unknown(client: Client, package_ahriman: Package, mocker: MockerFix
|
|||||||
"""
|
"""
|
||||||
must add new package with unknown status
|
must add new package with unknown status
|
||||||
"""
|
"""
|
||||||
add_mock = mocker.patch("ahriman.core.status.client.Client.add")
|
add_mock = mocker.patch("ahriman.core.status.client.Client.package_add")
|
||||||
client.set_unknown(package_ahriman)
|
client.set_unknown(package_ahriman)
|
||||||
|
|
||||||
add_mock.assert_called_once_with(package_ahriman, BuildStatusEnum.Unknown)
|
add_mock.assert_called_once_with(package_ahriman, BuildStatusEnum.Unknown)
|
||||||
|
|
||||||
|
|
||||||
|
def test_status_get(client: Client) -> None:
|
||||||
|
"""
|
||||||
|
must return dummy status for web service
|
||||||
|
"""
|
||||||
|
actual = client.status_get()
|
||||||
|
expected = InternalStatus(status=BuildStatus(timestamp=actual.status.timestamp))
|
||||||
|
|
||||||
|
assert actual == expected
|
||||||
|
|
||||||
|
|
||||||
|
def test_status_update(client: Client) -> None:
|
||||||
|
"""
|
||||||
|
must update self status without errors
|
||||||
|
"""
|
||||||
|
client.status_update(BuildStatusEnum.Unknown)
|
||||||
|
@ -5,7 +5,7 @@ import requests
|
|||||||
import requests_unixsocket
|
import requests_unixsocket
|
||||||
|
|
||||||
from pytest_mock import MockerFixture
|
from pytest_mock import MockerFixture
|
||||||
from requests import Response
|
from unittest.mock import call as MockCall
|
||||||
|
|
||||||
from ahriman.core.configuration import Configuration
|
from ahriman.core.configuration import Configuration
|
||||||
from ahriman.core.status.web_client import WebClient
|
from ahriman.core.status.web_client import WebClient
|
||||||
@ -74,14 +74,14 @@ def test_login(web_client: WebClient, user: User, mocker: MockerFixture) -> None
|
|||||||
must login user
|
must login user
|
||||||
"""
|
"""
|
||||||
web_client.user = user
|
web_client.user = user
|
||||||
requests_mock = mocker.patch("requests.Session.post")
|
requests_mock = mocker.patch("requests.Session.request")
|
||||||
payload = {
|
payload = {
|
||||||
"username": user.username,
|
"username": user.username,
|
||||||
"password": user.password
|
"password": user.password
|
||||||
}
|
}
|
||||||
|
|
||||||
web_client._login(requests.Session())
|
web_client._login(requests.Session())
|
||||||
requests_mock.assert_called_once_with(pytest.helpers.anyvar(str, True), json=payload)
|
requests_mock.assert_called_once_with("POST", pytest.helpers.anyvar(str, True), params=None, json=payload)
|
||||||
|
|
||||||
|
|
||||||
def test_login_failed(web_client: WebClient, user: User, mocker: MockerFixture) -> None:
|
def test_login_failed(web_client: WebClient, user: User, mocker: MockerFixture) -> None:
|
||||||
@ -89,7 +89,7 @@ def test_login_failed(web_client: WebClient, user: User, mocker: MockerFixture)
|
|||||||
must suppress any exception happened during login
|
must suppress any exception happened during login
|
||||||
"""
|
"""
|
||||||
web_client.user = user
|
web_client.user = user
|
||||||
mocker.patch("requests.Session.post", side_effect=Exception())
|
mocker.patch("requests.Session.request", side_effect=Exception())
|
||||||
web_client._login(requests.Session())
|
web_client._login(requests.Session())
|
||||||
|
|
||||||
|
|
||||||
@ -98,7 +98,7 @@ def test_login_failed_http_error(web_client: WebClient, user: User, mocker: Mock
|
|||||||
must suppress HTTP exception happened during login
|
must suppress HTTP exception happened during login
|
||||||
"""
|
"""
|
||||||
web_client.user = user
|
web_client.user = user
|
||||||
mocker.patch("requests.Session.post", side_effect=requests.exceptions.HTTPError())
|
mocker.patch("requests.Session.request", side_effect=requests.exceptions.HTTPError())
|
||||||
web_client._login(requests.Session())
|
web_client._login(requests.Session())
|
||||||
|
|
||||||
|
|
||||||
@ -106,7 +106,7 @@ def test_login_skip(web_client: WebClient, mocker: MockerFixture) -> None:
|
|||||||
"""
|
"""
|
||||||
must skip login if no user set
|
must skip login if no user set
|
||||||
"""
|
"""
|
||||||
requests_mock = mocker.patch("requests.Session.post")
|
requests_mock = mocker.patch("requests.Session.request")
|
||||||
web_client._login(requests.Session())
|
web_client._login(requests.Session())
|
||||||
requests_mock.assert_not_called()
|
requests_mock.assert_not_called()
|
||||||
|
|
||||||
@ -130,241 +130,292 @@ def test_package_url(web_client: WebClient, package_ahriman: Package) -> None:
|
|||||||
assert web_client._package_url(package_ahriman.base).endswith(f"/api/v1/packages/{package_ahriman.base}")
|
assert web_client._package_url(package_ahriman.base).endswith(f"/api/v1/packages/{package_ahriman.base}")
|
||||||
|
|
||||||
|
|
||||||
def test_add(web_client: WebClient, package_ahriman: Package, mocker: MockerFixture) -> None:
|
def test_make_request(web_client: WebClient, mocker: MockerFixture) -> None:
|
||||||
|
"""
|
||||||
|
must make HTTP request
|
||||||
|
"""
|
||||||
|
request_mock = mocker.patch("requests.Session.request")
|
||||||
|
|
||||||
|
assert web_client.make_request("GET", "url") is not None
|
||||||
|
assert web_client.make_request("GET", "url", params=[("param", "value")]) is not None
|
||||||
|
|
||||||
|
assert web_client.make_request("POST", "url") is not None
|
||||||
|
assert web_client.make_request("POST", "url", json={"param": "value"}) is not None
|
||||||
|
|
||||||
|
assert web_client.make_request("DELETE", "url") is not None
|
||||||
|
|
||||||
|
request_mock.assert_has_calls([
|
||||||
|
MockCall("GET", "url", params=None, json=None),
|
||||||
|
MockCall().raise_for_status(),
|
||||||
|
MockCall("GET", "url", params=[("param", "value")], json=None),
|
||||||
|
MockCall().raise_for_status(),
|
||||||
|
MockCall("POST", "url", params=None, json=None),
|
||||||
|
MockCall().raise_for_status(),
|
||||||
|
MockCall("POST", "url", params=None, json={"param": "value"}),
|
||||||
|
MockCall().raise_for_status(),
|
||||||
|
MockCall("DELETE", "url", params=None, json=None),
|
||||||
|
MockCall().raise_for_status(),
|
||||||
|
])
|
||||||
|
|
||||||
|
|
||||||
|
def test_make_request_failed(web_client: WebClient, mocker: MockerFixture) -> None:
|
||||||
|
"""
|
||||||
|
must make HTTP request
|
||||||
|
"""
|
||||||
|
mocker.patch("requests.Session.request", side_effect=Exception())
|
||||||
|
assert web_client.make_request("GET", "url") is None
|
||||||
|
|
||||||
|
|
||||||
|
def test_package_add(web_client: WebClient, package_ahriman: Package, mocker: MockerFixture) -> None:
|
||||||
"""
|
"""
|
||||||
must process package addition
|
must process package addition
|
||||||
"""
|
"""
|
||||||
requests_mock = mocker.patch("requests.Session.post")
|
requests_mock = mocker.patch("requests.Session.request")
|
||||||
payload = pytest.helpers.get_package_status(package_ahriman)
|
payload = pytest.helpers.get_package_status(package_ahriman)
|
||||||
|
|
||||||
web_client.add(package_ahriman, BuildStatusEnum.Unknown)
|
web_client.package_add(package_ahriman, BuildStatusEnum.Unknown)
|
||||||
requests_mock.assert_called_once_with(pytest.helpers.anyvar(str, True), json=payload)
|
requests_mock.assert_called_once_with("POST", pytest.helpers.anyvar(str, True), params=None, json=payload)
|
||||||
|
|
||||||
|
|
||||||
def test_add_failed(web_client: WebClient, package_ahriman: Package, mocker: MockerFixture) -> None:
|
def test_package_add_failed(web_client: WebClient, package_ahriman: Package, mocker: MockerFixture) -> None:
|
||||||
"""
|
"""
|
||||||
must suppress any exception happened during addition
|
must suppress any exception happened during addition
|
||||||
"""
|
"""
|
||||||
mocker.patch("requests.Session.post", side_effect=Exception())
|
mocker.patch("requests.Session.request", side_effect=Exception())
|
||||||
web_client.add(package_ahriman, BuildStatusEnum.Unknown)
|
web_client.package_add(package_ahriman, BuildStatusEnum.Unknown)
|
||||||
|
|
||||||
|
|
||||||
def test_add_failed_http_error(web_client: WebClient, package_ahriman: Package, mocker: MockerFixture) -> None:
|
def test_package_add_failed_http_error(web_client: WebClient, package_ahriman: Package, mocker: MockerFixture) -> None:
|
||||||
"""
|
"""
|
||||||
must suppress HTTP exception happened during addition
|
must suppress HTTP exception happened during addition
|
||||||
"""
|
"""
|
||||||
mocker.patch("requests.Session.post", side_effect=requests.exceptions.HTTPError())
|
mocker.patch("requests.Session.request", side_effect=requests.exceptions.HTTPError())
|
||||||
web_client.add(package_ahriman, BuildStatusEnum.Unknown)
|
web_client.package_add(package_ahriman, BuildStatusEnum.Unknown)
|
||||||
|
|
||||||
|
|
||||||
def test_add_failed_suppress(web_client: WebClient, package_ahriman: Package, mocker: MockerFixture) -> None:
|
def test_package_add_failed_suppress(web_client: WebClient, package_ahriman: Package, mocker: MockerFixture) -> None:
|
||||||
"""
|
"""
|
||||||
must suppress any exception happened during addition and don't log
|
must suppress any exception happened during addition and don't log
|
||||||
"""
|
"""
|
||||||
web_client.suppress_errors = True
|
web_client.suppress_errors = True
|
||||||
mocker.patch("requests.Session.post", side_effect=Exception())
|
mocker.patch("requests.Session.request", side_effect=Exception())
|
||||||
logging_mock = mocker.patch("logging.exception")
|
logging_mock = mocker.patch("logging.exception")
|
||||||
|
|
||||||
web_client.add(package_ahriman, BuildStatusEnum.Unknown)
|
web_client.package_add(package_ahriman, BuildStatusEnum.Unknown)
|
||||||
logging_mock.assert_not_called()
|
logging_mock.assert_not_called()
|
||||||
|
|
||||||
|
|
||||||
def test_add_failed_http_error_suppress(web_client: WebClient, package_ahriman: Package, mocker: MockerFixture) -> None:
|
def test_package_add_failed_http_error_suppress(web_client: WebClient, package_ahriman: Package,
|
||||||
|
mocker: MockerFixture) -> None:
|
||||||
"""
|
"""
|
||||||
must suppress HTTP exception happened during addition and don't log
|
must suppress HTTP exception happened during addition and don't log
|
||||||
"""
|
"""
|
||||||
web_client.suppress_errors = True
|
web_client.suppress_errors = True
|
||||||
mocker.patch("requests.Session.post", side_effect=requests.exceptions.HTTPError())
|
mocker.patch("requests.Session.request", side_effect=requests.exceptions.HTTPError())
|
||||||
logging_mock = mocker.patch("logging.exception")
|
logging_mock = mocker.patch("logging.exception")
|
||||||
|
|
||||||
web_client.add(package_ahriman, BuildStatusEnum.Unknown)
|
web_client.package_add(package_ahriman, BuildStatusEnum.Unknown)
|
||||||
logging_mock.assert_not_called()
|
logging_mock.assert_not_called()
|
||||||
|
|
||||||
|
|
||||||
def test_get_all(web_client: WebClient, package_ahriman: Package, mocker: MockerFixture) -> None:
|
def test_package_get_all(web_client: WebClient, package_ahriman: Package, mocker: MockerFixture) -> None:
|
||||||
"""
|
"""
|
||||||
must return all packages status
|
must return all packages status
|
||||||
"""
|
"""
|
||||||
response = [pytest.helpers.get_package_status_extended(package_ahriman)]
|
response = [pytest.helpers.get_package_status_extended(package_ahriman)]
|
||||||
response_obj = Response()
|
response_obj = requests.Response()
|
||||||
response_obj._content = json.dumps(response).encode("utf8")
|
response_obj._content = json.dumps(response).encode("utf8")
|
||||||
response_obj.status_code = 200
|
response_obj.status_code = 200
|
||||||
|
|
||||||
requests_mock = mocker.patch("requests.Session.get", return_value=response_obj)
|
requests_mock = mocker.patch("requests.Session.request", return_value=response_obj)
|
||||||
|
|
||||||
result = web_client.get(None)
|
result = web_client.package_get(None)
|
||||||
requests_mock.assert_called_once_with(web_client._package_url())
|
requests_mock.assert_called_once_with("GET", web_client._package_url(), params=None, json=None)
|
||||||
assert len(result) == len(response)
|
assert len(result) == len(response)
|
||||||
assert (package_ahriman, BuildStatusEnum.Unknown) in [(package, status.status) for package, status in result]
|
assert (package_ahriman, BuildStatusEnum.Unknown) in [(package, status.status) for package, status in result]
|
||||||
|
|
||||||
|
|
||||||
def test_get_failed(web_client: WebClient, mocker: MockerFixture) -> None:
|
def test_package_get_failed(web_client: WebClient, mocker: MockerFixture) -> None:
|
||||||
"""
|
"""
|
||||||
must suppress any exception happened during status getting
|
must suppress any exception happened during status getting
|
||||||
"""
|
"""
|
||||||
mocker.patch("requests.Session.get", side_effect=Exception())
|
mocker.patch("requests.Session.request", side_effect=Exception())
|
||||||
assert web_client.get(None) == []
|
assert web_client.package_get(None) == []
|
||||||
|
|
||||||
|
|
||||||
def test_get_failed_http_error(web_client: WebClient, mocker: MockerFixture) -> None:
|
def test_package_get_failed_http_error(web_client: WebClient, mocker: MockerFixture) -> None:
|
||||||
"""
|
"""
|
||||||
must suppress HTTP exception happened during status getting
|
must suppress HTTP exception happened during status getting
|
||||||
"""
|
"""
|
||||||
mocker.patch("requests.Session.get", side_effect=requests.exceptions.HTTPError())
|
mocker.patch("requests.Session.request", side_effect=requests.exceptions.HTTPError())
|
||||||
assert web_client.get(None) == []
|
assert web_client.package_get(None) == []
|
||||||
|
|
||||||
|
|
||||||
def test_get_single(web_client: WebClient, package_ahriman: Package, mocker: MockerFixture) -> None:
|
def test_package_get_single(web_client: WebClient, package_ahriman: Package, mocker: MockerFixture) -> None:
|
||||||
"""
|
"""
|
||||||
must return single package status
|
must return single package status
|
||||||
"""
|
"""
|
||||||
response = [pytest.helpers.get_package_status_extended(package_ahriman)]
|
response = [pytest.helpers.get_package_status_extended(package_ahriman)]
|
||||||
response_obj = Response()
|
response_obj = requests.Response()
|
||||||
response_obj._content = json.dumps(response).encode("utf8")
|
response_obj._content = json.dumps(response).encode("utf8")
|
||||||
response_obj.status_code = 200
|
response_obj.status_code = 200
|
||||||
|
|
||||||
requests_mock = mocker.patch("requests.Session.get", return_value=response_obj)
|
requests_mock = mocker.patch("requests.Session.request", return_value=response_obj)
|
||||||
|
|
||||||
result = web_client.get(package_ahriman.base)
|
result = web_client.package_get(package_ahriman.base)
|
||||||
requests_mock.assert_called_once_with(web_client._package_url(package_ahriman.base))
|
requests_mock.assert_called_once_with("GET", web_client._package_url(package_ahriman.base),
|
||||||
|
params=None, json=None)
|
||||||
assert len(result) == len(response)
|
assert len(result) == len(response)
|
||||||
assert (package_ahriman, BuildStatusEnum.Unknown) in [(package, status.status) for package, status in result]
|
assert (package_ahriman, BuildStatusEnum.Unknown) in [(package, status.status) for package, status in result]
|
||||||
|
|
||||||
|
|
||||||
def test_get_internal(web_client: WebClient, mocker: MockerFixture) -> None:
|
def test_package_logs(web_client: WebClient, log_record: logging.LogRecord, package_ahriman: Package,
|
||||||
"""
|
|
||||||
must return web service status
|
|
||||||
"""
|
|
||||||
status = InternalStatus(status=BuildStatus(), architecture="x86_64")
|
|
||||||
response_obj = Response()
|
|
||||||
response_obj._content = json.dumps(status.view()).encode("utf8")
|
|
||||||
response_obj.status_code = 200
|
|
||||||
|
|
||||||
requests_mock = mocker.patch("requests.Session.get", return_value=response_obj)
|
|
||||||
|
|
||||||
result = web_client.get_internal()
|
|
||||||
requests_mock.assert_called_once_with(web_client._status_url)
|
|
||||||
assert result.architecture == "x86_64"
|
|
||||||
|
|
||||||
|
|
||||||
def test_get_internal_failed(web_client: WebClient, mocker: MockerFixture) -> None:
|
|
||||||
"""
|
|
||||||
must suppress any exception happened during web service status getting
|
|
||||||
"""
|
|
||||||
mocker.patch("requests.Session.get", side_effect=Exception())
|
|
||||||
assert web_client.get_internal().architecture is None
|
|
||||||
|
|
||||||
|
|
||||||
def test_get_internal_failed_http_error(web_client: WebClient, mocker: MockerFixture) -> None:
|
|
||||||
"""
|
|
||||||
must suppress HTTP exception happened during web service status getting
|
|
||||||
"""
|
|
||||||
mocker.patch("requests.Session.get", side_effect=requests.exceptions.HTTPError())
|
|
||||||
assert web_client.get_internal().architecture is None
|
|
||||||
|
|
||||||
|
|
||||||
def test_logs(web_client: WebClient, log_record: logging.LogRecord, package_ahriman: Package,
|
|
||||||
mocker: MockerFixture) -> None:
|
mocker: MockerFixture) -> None:
|
||||||
"""
|
"""
|
||||||
must process log record
|
must process log record
|
||||||
"""
|
"""
|
||||||
requests_mock = mocker.patch("requests.Session.post")
|
requests_mock = mocker.patch("requests.Session.request")
|
||||||
payload = {
|
payload = {
|
||||||
"created": log_record.created,
|
"created": log_record.created,
|
||||||
"message": log_record.getMessage(),
|
"message": log_record.getMessage(),
|
||||||
"process_id": log_record.process,
|
"process_id": log_record.process,
|
||||||
}
|
}
|
||||||
|
|
||||||
web_client.logs(package_ahriman.base, log_record)
|
web_client.package_logs(package_ahriman.base, log_record)
|
||||||
requests_mock.assert_called_once_with(pytest.helpers.anyvar(str, True), json=payload)
|
requests_mock.assert_called_once_with("POST", pytest.helpers.anyvar(str, True), params=None, json=payload)
|
||||||
|
|
||||||
|
|
||||||
def test_log_failed(web_client: WebClient, log_record: logging.LogRecord, package_ahriman: Package,
|
def test_package_logs_failed(web_client: WebClient, log_record: logging.LogRecord, package_ahriman: Package,
|
||||||
mocker: MockerFixture) -> None:
|
mocker: MockerFixture) -> None:
|
||||||
"""
|
"""
|
||||||
must pass exception during log post
|
must pass exception during log post
|
||||||
"""
|
"""
|
||||||
mocker.patch("requests.Session.post", side_effect=Exception())
|
mocker.patch("requests.Session.request", side_effect=Exception())
|
||||||
log_record.package_base = package_ahriman.base
|
log_record.package_base = package_ahriman.base
|
||||||
with pytest.raises(Exception):
|
web_client.package_logs(package_ahriman.base, log_record)
|
||||||
web_client.logs(package_ahriman.base, log_record)
|
|
||||||
|
|
||||||
|
|
||||||
def test_remove(web_client: WebClient, package_ahriman: Package, mocker: MockerFixture) -> None:
|
def test_package_logs_failed_http_error(web_client: WebClient, log_record: logging.LogRecord, package_ahriman: Package,
|
||||||
|
mocker: MockerFixture) -> None:
|
||||||
|
"""
|
||||||
|
must pass exception during log post
|
||||||
|
"""
|
||||||
|
mocker.patch("requests.Session.request", side_effect=requests.exceptions.HTTPError())
|
||||||
|
log_record.package_base = package_ahriman.base
|
||||||
|
web_client.package_logs(package_ahriman.base, log_record)
|
||||||
|
|
||||||
|
|
||||||
|
def test_package_remove(web_client: WebClient, package_ahriman: Package, mocker: MockerFixture) -> None:
|
||||||
"""
|
"""
|
||||||
must process package removal
|
must process package removal
|
||||||
"""
|
"""
|
||||||
requests_mock = mocker.patch("requests.Session.delete")
|
requests_mock = mocker.patch("requests.Session.request")
|
||||||
|
|
||||||
web_client.remove(package_ahriman.base)
|
web_client.package_remove(package_ahriman.base)
|
||||||
requests_mock.assert_called_once_with(pytest.helpers.anyvar(str, True))
|
requests_mock.assert_called_once_with("DELETE", pytest.helpers.anyvar(str, True), params=None, json=None)
|
||||||
|
|
||||||
|
|
||||||
def test_remove_failed(web_client: WebClient, package_ahriman: Package, mocker: MockerFixture) -> None:
|
def test_package_remove_failed(web_client: WebClient, package_ahriman: Package, mocker: MockerFixture) -> None:
|
||||||
"""
|
"""
|
||||||
must suppress any exception happened during removal
|
must suppress any exception happened during removal
|
||||||
"""
|
"""
|
||||||
mocker.patch("requests.Session.delete", side_effect=Exception())
|
mocker.patch("requests.Session.request", side_effect=Exception())
|
||||||
web_client.remove(package_ahriman.base)
|
web_client.package_remove(package_ahriman.base)
|
||||||
|
|
||||||
|
|
||||||
def test_remove_failed_http_error(web_client: WebClient, package_ahriman: Package, mocker: MockerFixture) -> None:
|
def test_package_remove_failed_http_error(web_client: WebClient, package_ahriman: Package,
|
||||||
|
mocker: MockerFixture) -> None:
|
||||||
"""
|
"""
|
||||||
must suppress HTTP exception happened during removal
|
must suppress HTTP exception happened during removal
|
||||||
"""
|
"""
|
||||||
mocker.patch("requests.Session.delete", side_effect=requests.exceptions.HTTPError())
|
mocker.patch("requests.Session.request", side_effect=requests.exceptions.HTTPError())
|
||||||
web_client.remove(package_ahriman.base)
|
web_client.package_remove(package_ahriman.base)
|
||||||
|
|
||||||
|
|
||||||
def test_update(web_client: WebClient, package_ahriman: Package, mocker: MockerFixture) -> None:
|
def test_package_update(web_client: WebClient, package_ahriman: Package, mocker: MockerFixture) -> None:
|
||||||
"""
|
"""
|
||||||
must process package update
|
must process package update
|
||||||
"""
|
"""
|
||||||
requests_mock = mocker.patch("requests.Session.post")
|
requests_mock = mocker.patch("requests.Session.request")
|
||||||
|
|
||||||
web_client.update(package_ahriman.base, BuildStatusEnum.Unknown)
|
web_client.package_update(package_ahriman.base, BuildStatusEnum.Unknown)
|
||||||
requests_mock.assert_called_once_with(pytest.helpers.anyvar(str, True), json={
|
requests_mock.assert_called_once_with("POST", pytest.helpers.anyvar(str, True), params=None, json={
|
||||||
"status": BuildStatusEnum.Unknown.value})
|
"status": BuildStatusEnum.Unknown.value
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
def test_update_failed(web_client: WebClient, package_ahriman: Package, mocker: MockerFixture) -> None:
|
def test_package_update_failed(web_client: WebClient, package_ahriman: Package, mocker: MockerFixture) -> None:
|
||||||
"""
|
"""
|
||||||
must suppress any exception happened during update
|
must suppress any exception happened during update
|
||||||
"""
|
"""
|
||||||
mocker.patch("requests.Session.post", side_effect=Exception())
|
mocker.patch("requests.Session.request", side_effect=Exception())
|
||||||
web_client.update(package_ahriman.base, BuildStatusEnum.Unknown)
|
web_client.package_update(package_ahriman.base, BuildStatusEnum.Unknown)
|
||||||
|
|
||||||
|
|
||||||
def test_update_failed_http_error(web_client: WebClient, package_ahriman: Package, mocker: MockerFixture) -> None:
|
def test_package_update_failed_http_error(web_client: WebClient, package_ahriman: Package,
|
||||||
|
mocker: MockerFixture) -> None:
|
||||||
"""
|
"""
|
||||||
must suppress HTTP exception happened during update
|
must suppress HTTP exception happened during update
|
||||||
"""
|
"""
|
||||||
mocker.patch("requests.Session.post", side_effect=requests.exceptions.HTTPError())
|
mocker.patch("requests.Session.request", side_effect=requests.exceptions.HTTPError())
|
||||||
web_client.update(package_ahriman.base, BuildStatusEnum.Unknown)
|
web_client.package_update(package_ahriman.base, BuildStatusEnum.Unknown)
|
||||||
|
|
||||||
|
|
||||||
def test_update_self(web_client: WebClient, mocker: MockerFixture) -> None:
|
def test_status_get(web_client: WebClient, mocker: MockerFixture) -> None:
|
||||||
|
"""
|
||||||
|
must return web service status
|
||||||
|
"""
|
||||||
|
status = InternalStatus(status=BuildStatus(), architecture="x86_64")
|
||||||
|
response_obj = requests.Response()
|
||||||
|
response_obj._content = json.dumps(status.view()).encode("utf8")
|
||||||
|
response_obj.status_code = 200
|
||||||
|
|
||||||
|
requests_mock = mocker.patch("requests.Session.request", return_value=response_obj)
|
||||||
|
|
||||||
|
result = web_client.status_get()
|
||||||
|
requests_mock.assert_called_once_with("GET", web_client._status_url, params=None, json=None)
|
||||||
|
assert result.architecture == "x86_64"
|
||||||
|
|
||||||
|
|
||||||
|
def test_status_get_failed(web_client: WebClient, mocker: MockerFixture) -> None:
|
||||||
|
"""
|
||||||
|
must suppress any exception happened during web service status getting
|
||||||
|
"""
|
||||||
|
mocker.patch("requests.Session.request", side_effect=Exception())
|
||||||
|
assert web_client.status_get().architecture is None
|
||||||
|
|
||||||
|
|
||||||
|
def test_status_get_failed_http_error(web_client: WebClient, mocker: MockerFixture) -> None:
|
||||||
|
"""
|
||||||
|
must suppress HTTP exception happened during web service status getting
|
||||||
|
"""
|
||||||
|
mocker.patch("requests.Session.request", side_effect=requests.exceptions.HTTPError())
|
||||||
|
assert web_client.status_get().architecture is None
|
||||||
|
|
||||||
|
|
||||||
|
def test_status_update(web_client: WebClient, mocker: MockerFixture) -> None:
|
||||||
"""
|
"""
|
||||||
must process service update
|
must process service update
|
||||||
"""
|
"""
|
||||||
requests_mock = mocker.patch("requests.Session.post")
|
requests_mock = mocker.patch("requests.Session.request")
|
||||||
|
|
||||||
web_client.update_self(BuildStatusEnum.Unknown)
|
web_client.status_update(BuildStatusEnum.Unknown)
|
||||||
requests_mock.assert_called_once_with(pytest.helpers.anyvar(str, True), json={
|
requests_mock.assert_called_once_with("POST", pytest.helpers.anyvar(str, True), params=None, json={
|
||||||
"status": BuildStatusEnum.Unknown.value})
|
"status": BuildStatusEnum.Unknown.value
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
def test_update_self_failed(web_client: WebClient, mocker: MockerFixture) -> None:
|
def test_status_update_self_failed(web_client: WebClient, mocker: MockerFixture) -> None:
|
||||||
"""
|
"""
|
||||||
must suppress any exception happened during service update
|
must suppress any exception happened during service update
|
||||||
"""
|
"""
|
||||||
mocker.patch("requests.Session.post", side_effect=Exception())
|
mocker.patch("requests.Session.request", side_effect=Exception())
|
||||||
web_client.update_self(BuildStatusEnum.Unknown)
|
web_client.status_update(BuildStatusEnum.Unknown)
|
||||||
|
|
||||||
|
|
||||||
def test_update_self_failed_http_error(web_client: WebClient, mocker: MockerFixture) -> None:
|
def test_status_update_failed_http_error(web_client: WebClient, mocker: MockerFixture) -> None:
|
||||||
"""
|
"""
|
||||||
must suppress HTTP exception happened during service update
|
must suppress HTTP exception happened during service update
|
||||||
"""
|
"""
|
||||||
mocker.patch("requests.Session.post", side_effect=requests.exceptions.HTTPError())
|
mocker.patch("requests.Session.request", side_effect=requests.exceptions.HTTPError())
|
||||||
web_client.update_self(BuildStatusEnum.Unknown)
|
web_client.status_update(BuildStatusEnum.Unknown)
|
||||||
|
@ -23,3 +23,6 @@ def test_from_option_valid() -> None:
|
|||||||
|
|
||||||
assert ReportSettings.from_option("telegram") == ReportSettings.Telegram
|
assert ReportSettings.from_option("telegram") == ReportSettings.Telegram
|
||||||
assert ReportSettings.from_option("TElegraM") == ReportSettings.Telegram
|
assert ReportSettings.from_option("TElegraM") == ReportSettings.Telegram
|
||||||
|
|
||||||
|
assert ReportSettings.from_option("remote-call") == ReportSettings.RemoteCall
|
||||||
|
assert ReportSettings.from_option("reMOte-cALL") == ReportSettings.RemoteCall
|
||||||
|
29
tests/ahriman/models/test_waiter.py
Normal file
29
tests/ahriman/models/test_waiter.py
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
import time
|
||||||
|
|
||||||
|
from ahriman.models.waiter import Waiter
|
||||||
|
|
||||||
|
|
||||||
|
def test_is_timed_out() -> None:
|
||||||
|
"""
|
||||||
|
must correctly check if timer runs out
|
||||||
|
"""
|
||||||
|
assert Waiter(-1).is_timed_out()
|
||||||
|
assert Waiter(1, start_time=time.monotonic() - 10.0).is_timed_out()
|
||||||
|
assert not Waiter(1, start_time=time.monotonic() + 10.0).is_timed_out()
|
||||||
|
|
||||||
|
|
||||||
|
def test_is_timed_out_infinite() -> None:
|
||||||
|
"""
|
||||||
|
must treat 0 wait timeout as infinite
|
||||||
|
"""
|
||||||
|
assert not Waiter(0).is_timed_out()
|
||||||
|
assert not Waiter(0, start_time=time.monotonic() - 10.0).is_timed_out()
|
||||||
|
|
||||||
|
|
||||||
|
def test_wait() -> None:
|
||||||
|
"""
|
||||||
|
must wait until file will disappear
|
||||||
|
"""
|
||||||
|
results = iter([True, False])
|
||||||
|
waiter = Waiter(1, interval=1)
|
||||||
|
assert waiter.wait(lambda: next(results)) > 0
|
@ -74,6 +74,9 @@ homepage =
|
|||||||
link_path =
|
link_path =
|
||||||
template_path = ../web/templates/repo-index.jinja2
|
template_path = ../web/templates/repo-index.jinja2
|
||||||
|
|
||||||
|
[remote-call]
|
||||||
|
manual = yes
|
||||||
|
|
||||||
[telegram]
|
[telegram]
|
||||||
api_key = apikey
|
api_key = apikey
|
||||||
chat_id = @ahrimantestchat
|
chat_id = @ahrimantestchat
|
||||||
|
Loading…
Reference in New Issue
Block a user