mirror of
https://github.com/arcan1s/ahriman.git
synced 2025-08-27 20:09:57 +00:00
* add models tests (#1) also replace single quote to double one to confort PEP docstring + move _check_output to class properties to make it available for mocking * alpm tests implementation * try to replace os with pathlib * update tests for pathlib * fix includes glob and trim version from dependencies * build_tools package tests * repository component tests * add sign tests * complete status tests * handle exceptions in actual_version calls * complete core tests * move configuration to root conftest * application tests * complete application tests * change copyright to more generic one * base web tests * complete web tests * complete testkit also add argument parsers test
This commit is contained in:
@ -1,5 +1,5 @@
|
||||
#
|
||||
# Copyright (c) 2021 Evgenii Alekseev.
|
||||
# Copyright (c) 2021 ahriman team.
|
||||
#
|
||||
# This file is part of ahriman
|
||||
# (see https://github.com/arcan1s/ahriman).
|
||||
|
@ -1,5 +1,5 @@
|
||||
#
|
||||
# Copyright (c) 2021 Evgenii Alekseev.
|
||||
# Copyright (c) 2021 ahriman team.
|
||||
#
|
||||
# This file is part of ahriman
|
||||
# (see https://github.com/arcan1s/ahriman).
|
||||
|
@ -1,5 +1,5 @@
|
||||
#
|
||||
# Copyright (c) 2021 Evgenii Alekseev.
|
||||
# Copyright (c) 2021 ahriman team.
|
||||
#
|
||||
# This file is part of ahriman
|
||||
# (see https://github.com/arcan1s/ahriman).
|
||||
@ -28,11 +28,11 @@ HandlerType = Callable[[Request], Awaitable[StreamResponse]]
|
||||
|
||||
|
||||
def exception_handler(logger: Logger) -> Callable[[Request, HandlerType], Awaitable[StreamResponse]]:
|
||||
'''
|
||||
"""
|
||||
exception handler middleware. Just log any exception (except for client ones)
|
||||
:param logger: class logger
|
||||
:return: built middleware
|
||||
'''
|
||||
"""
|
||||
@middleware
|
||||
async def handle(request: Request, handler: HandlerType) -> StreamResponse:
|
||||
try:
|
||||
@ -40,7 +40,7 @@ def exception_handler(logger: Logger) -> Callable[[Request, HandlerType], Awaita
|
||||
except HTTPClientError:
|
||||
raise
|
||||
except Exception:
|
||||
logger.exception(f'exception during performing request to {request.path}', exc_info=True)
|
||||
logger.exception(f"exception during performing request to {request.path}")
|
||||
raise
|
||||
|
||||
return handle
|
||||
|
@ -1,5 +1,5 @@
|
||||
#
|
||||
# Copyright (c) 2021 Evgenii Alekseev.
|
||||
# Copyright (c) 2021 ahriman team.
|
||||
#
|
||||
# This file is part of ahriman
|
||||
# (see https://github.com/arcan1s/ahriman).
|
||||
@ -26,7 +26,7 @@ from ahriman.web.views.packages import PackagesView
|
||||
|
||||
|
||||
def setup_routes(application: Application) -> None:
|
||||
'''
|
||||
"""
|
||||
setup all defined routes
|
||||
|
||||
Available routes are:
|
||||
@ -45,16 +45,16 @@ def setup_routes(application: Application) -> None:
|
||||
POST /api/v1/package/:base update package base status
|
||||
|
||||
:param application: web application instance
|
||||
'''
|
||||
application.router.add_get('/', IndexView)
|
||||
application.router.add_get('/index.html', IndexView)
|
||||
"""
|
||||
application.router.add_get("/", IndexView)
|
||||
application.router.add_get("/index.html", IndexView)
|
||||
|
||||
application.router.add_get('/api/v1/ahriman', AhrimanView)
|
||||
application.router.add_post('/api/v1/ahriman', AhrimanView)
|
||||
application.router.add_get("/api/v1/ahriman", AhrimanView)
|
||||
application.router.add_post("/api/v1/ahriman", AhrimanView)
|
||||
|
||||
application.router.add_get('/api/v1/packages', PackagesView)
|
||||
application.router.add_post('/api/v1/packages', PackagesView)
|
||||
application.router.add_get("/api/v1/packages", PackagesView)
|
||||
application.router.add_post("/api/v1/packages", PackagesView)
|
||||
|
||||
application.router.add_delete('/api/v1/packages/{package}', PackageView)
|
||||
application.router.add_get('/api/v1/packages/{package}', PackageView)
|
||||
application.router.add_post('/api/v1/packages/{package}', PackageView)
|
||||
application.router.add_delete("/api/v1/packages/{package}", PackageView)
|
||||
application.router.add_get("/api/v1/packages/{package}", PackageView)
|
||||
application.router.add_post("/api/v1/packages/{package}", PackageView)
|
||||
|
@ -1,5 +1,5 @@
|
||||
#
|
||||
# Copyright (c) 2021 Evgenii Alekseev.
|
||||
# Copyright (c) 2021 ahriman team.
|
||||
#
|
||||
# This file is part of ahriman
|
||||
# (see https://github.com/arcan1s/ahriman).
|
||||
|
@ -1,5 +1,5 @@
|
||||
#
|
||||
# Copyright (c) 2021 Evgenii Alekseev.
|
||||
# Copyright (c) 2021 ahriman team.
|
||||
#
|
||||
# This file is part of ahriman
|
||||
# (see https://github.com/arcan1s/ahriman).
|
||||
@ -24,19 +24,19 @@ from ahriman.web.views.base import BaseView
|
||||
|
||||
|
||||
class AhrimanView(BaseView):
|
||||
'''
|
||||
"""
|
||||
service status web view
|
||||
'''
|
||||
"""
|
||||
|
||||
async def get(self) -> Response:
|
||||
'''
|
||||
"""
|
||||
get current service status
|
||||
:return: 200 with service status object
|
||||
'''
|
||||
"""
|
||||
return json_response(self.service.status.view())
|
||||
|
||||
async def post(self) -> Response:
|
||||
'''
|
||||
"""
|
||||
update service status
|
||||
|
||||
JSON body must be supplied, the following model is used:
|
||||
@ -45,11 +45,11 @@ class AhrimanView(BaseView):
|
||||
}
|
||||
|
||||
:return: 204 on success
|
||||
'''
|
||||
"""
|
||||
data = await self.request.json()
|
||||
|
||||
try:
|
||||
status = BuildStatusEnum(data['status'])
|
||||
status = BuildStatusEnum(data["status"])
|
||||
except Exception as e:
|
||||
raise HTTPBadRequest(text=str(e))
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
#
|
||||
# Copyright (c) 2021 Evgenii Alekseev.
|
||||
# Copyright (c) 2021 ahriman team.
|
||||
#
|
||||
# This file is part of ahriman
|
||||
# (see https://github.com/arcan1s/ahriman).
|
||||
@ -19,18 +19,18 @@
|
||||
#
|
||||
from aiohttp.web import View
|
||||
|
||||
from ahriman.core.watcher.watcher import Watcher
|
||||
from ahriman.core.status.watcher import Watcher
|
||||
|
||||
|
||||
class BaseView(View):
|
||||
'''
|
||||
"""
|
||||
base web view to make things typed
|
||||
'''
|
||||
"""
|
||||
|
||||
@property
|
||||
def service(self) -> Watcher:
|
||||
'''
|
||||
"""
|
||||
:return: build status watcher instance
|
||||
'''
|
||||
watcher: Watcher = self.request.app['watcher']
|
||||
"""
|
||||
watcher: Watcher = self.request.app["watcher"]
|
||||
return watcher
|
||||
|
@ -1,5 +1,5 @@
|
||||
#
|
||||
# Copyright (c) 2021 Evgenii Alekseev.
|
||||
# Copyright (c) 2021 ahriman team.
|
||||
#
|
||||
# This file is part of ahriman
|
||||
# (see https://github.com/arcan1s/ahriman).
|
||||
@ -28,7 +28,7 @@ from ahriman.web.views.base import BaseView
|
||||
|
||||
|
||||
class IndexView(BaseView):
|
||||
'''
|
||||
"""
|
||||
root view
|
||||
|
||||
It uses jinja2 templates for report generation, the following variables are allowed:
|
||||
@ -39,35 +39,35 @@ class IndexView(BaseView):
|
||||
repository - repository name, string, required
|
||||
service - service status properties: status, status_color, timestamp. Required
|
||||
version - ahriman version, string, required
|
||||
'''
|
||||
"""
|
||||
|
||||
@aiohttp_jinja2.template('build-status.jinja2')
|
||||
@aiohttp_jinja2.template("build-status.jinja2")
|
||||
async def get(self) -> Dict[str, Any]:
|
||||
'''
|
||||
"""
|
||||
process get request. No parameters supported here
|
||||
:return: parameters for jinja template
|
||||
'''
|
||||
"""
|
||||
# some magic to make it jinja-friendly
|
||||
packages = [
|
||||
{
|
||||
'base': package.base,
|
||||
'packages': list(sorted(package.packages)),
|
||||
'status': status.status.value,
|
||||
'timestamp': pretty_datetime(status.timestamp),
|
||||
'version': package.version,
|
||||
'web_url': package.web_url
|
||||
"base": package.base,
|
||||
"packages": list(sorted(package.packages)),
|
||||
"status": status.status.value,
|
||||
"timestamp": pretty_datetime(status.timestamp),
|
||||
"version": package.version,
|
||||
"web_url": package.web_url
|
||||
} for package, status in sorted(self.service.packages, key=lambda item: item[0].base)
|
||||
]
|
||||
service = {
|
||||
'status': self.service.status.status.value,
|
||||
'status_color': self.service.status.status.badges_color(),
|
||||
'timestamp': pretty_datetime(self.service.status.timestamp)
|
||||
"status": self.service.status.status.value,
|
||||
"status_color": self.service.status.status.badges_color(),
|
||||
"timestamp": pretty_datetime(self.service.status.timestamp)
|
||||
}
|
||||
|
||||
return {
|
||||
'architecture': self.service.architecture,
|
||||
'packages': packages,
|
||||
'repository': self.service.repository.name,
|
||||
'service': service,
|
||||
'version': version.__version__,
|
||||
"architecture": self.service.architecture,
|
||||
"packages": packages,
|
||||
"repository": self.service.repository.name,
|
||||
"service": service,
|
||||
"version": version.__version__,
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
#
|
||||
# Copyright (c) 2021 Evgenii Alekseev.
|
||||
# Copyright (c) 2021 ahriman team.
|
||||
#
|
||||
# This file is part of ahriman
|
||||
# (see https://github.com/arcan1s/ahriman).
|
||||
@ -19,48 +19,49 @@
|
||||
#
|
||||
from aiohttp.web import HTTPBadRequest, HTTPNoContent, HTTPNotFound, Response, json_response
|
||||
|
||||
from ahriman.core.exceptions import UnknownPackage
|
||||
from ahriman.models.build_status import BuildStatusEnum
|
||||
from ahriman.models.package import Package
|
||||
from ahriman.web.views.base import BaseView
|
||||
|
||||
|
||||
class PackageView(BaseView):
|
||||
'''
|
||||
"""
|
||||
package base specific web view
|
||||
'''
|
||||
"""
|
||||
|
||||
async def get(self) -> Response:
|
||||
'''
|
||||
"""
|
||||
get current package base status
|
||||
:return: 200 with package description on success
|
||||
'''
|
||||
base = self.request.match_info['package']
|
||||
"""
|
||||
base = self.request.match_info["package"]
|
||||
|
||||
try:
|
||||
package, status = self.service.get(base)
|
||||
except KeyError:
|
||||
except UnknownPackage:
|
||||
raise HTTPNotFound()
|
||||
|
||||
response = [
|
||||
{
|
||||
'package': package.view(),
|
||||
'status': status.view()
|
||||
"package": package.view(),
|
||||
"status": status.view()
|
||||
}
|
||||
]
|
||||
return json_response(response)
|
||||
|
||||
async def delete(self) -> Response:
|
||||
'''
|
||||
"""
|
||||
delete package base from status page
|
||||
:return: 204 on success
|
||||
'''
|
||||
base = self.request.match_info['package']
|
||||
"""
|
||||
base = self.request.match_info["package"]
|
||||
self.service.remove(base)
|
||||
|
||||
return HTTPNoContent()
|
||||
|
||||
async def post(self) -> Response:
|
||||
'''
|
||||
"""
|
||||
update package build status
|
||||
|
||||
JSON body must be supplied, the following model is used:
|
||||
@ -71,19 +72,19 @@ class PackageView(BaseView):
|
||||
}
|
||||
|
||||
:return: 204 on success
|
||||
'''
|
||||
base = self.request.match_info['package']
|
||||
"""
|
||||
base = self.request.match_info["package"]
|
||||
data = await self.request.json()
|
||||
|
||||
try:
|
||||
package = Package.from_json(data['package']) if 'package' in data else None
|
||||
status = BuildStatusEnum(data['status'])
|
||||
package = Package.from_json(data["package"]) if "package" in data else None
|
||||
status = BuildStatusEnum(data["status"])
|
||||
except Exception as e:
|
||||
raise HTTPBadRequest(text=str(e))
|
||||
|
||||
try:
|
||||
self.service.update(base, status, package)
|
||||
except KeyError:
|
||||
raise HTTPBadRequest(text=f'Package {base} is unknown, but no package body set')
|
||||
except UnknownPackage:
|
||||
raise HTTPBadRequest(text=f"Package {base} is unknown, but no package body set")
|
||||
|
||||
return HTTPNoContent()
|
||||
|
@ -1,5 +1,5 @@
|
||||
#
|
||||
# Copyright (c) 2021 Evgenii Alekseev.
|
||||
# Copyright (c) 2021 ahriman team.
|
||||
#
|
||||
# This file is part of ahriman
|
||||
# (see https://github.com/arcan1s/ahriman).
|
||||
@ -23,28 +23,28 @@ from ahriman.web.views.base import BaseView
|
||||
|
||||
|
||||
class PackagesView(BaseView):
|
||||
'''
|
||||
"""
|
||||
global watcher view
|
||||
'''
|
||||
"""
|
||||
|
||||
async def get(self) -> Response:
|
||||
'''
|
||||
"""
|
||||
get current packages status
|
||||
:return: 200 with package description on success
|
||||
'''
|
||||
"""
|
||||
response = [
|
||||
{
|
||||
'package': package.view(),
|
||||
'status': status.view()
|
||||
"package": package.view(),
|
||||
"status": status.view()
|
||||
} for package, status in self.service.packages
|
||||
]
|
||||
return json_response(response)
|
||||
|
||||
async def post(self) -> Response:
|
||||
'''
|
||||
"""
|
||||
reload all packages from repository. No parameters supported here
|
||||
:return: 204 on success
|
||||
'''
|
||||
"""
|
||||
self.service.load()
|
||||
|
||||
return HTTPNoContent()
|
||||
|
@ -1,5 +1,5 @@
|
||||
#
|
||||
# Copyright (c) 2021 Evgenii Alekseev.
|
||||
# Copyright (c) 2021 ahriman team.
|
||||
#
|
||||
# This file is part of ahriman
|
||||
# (see https://github.com/arcan1s/ahriman).
|
||||
@ -25,71 +25,72 @@ from aiohttp import web
|
||||
|
||||
from ahriman.core.configuration import Configuration
|
||||
from ahriman.core.exceptions import InitializeException
|
||||
from ahriman.core.watcher.watcher import Watcher
|
||||
from ahriman.core.status.watcher import Watcher
|
||||
from ahriman.web.middlewares.exception_handler import exception_handler
|
||||
from ahriman.web.routes import setup_routes
|
||||
|
||||
|
||||
async def on_shutdown(application: web.Application) -> None:
|
||||
'''
|
||||
"""
|
||||
web application shutdown handler
|
||||
:param application: web application instance
|
||||
'''
|
||||
application.logger.warning('server terminated')
|
||||
"""
|
||||
application.logger.warning("server terminated")
|
||||
|
||||
|
||||
async def on_startup(application: web.Application) -> None:
|
||||
'''
|
||||
"""
|
||||
web application start handler
|
||||
:param application: web application instance
|
||||
'''
|
||||
application.logger.info('server started')
|
||||
"""
|
||||
application.logger.info("server started")
|
||||
try:
|
||||
application['watcher'].load()
|
||||
application["watcher"].load()
|
||||
except Exception:
|
||||
application.logger.exception('could not load packages', exc_info=True)
|
||||
application.logger.exception("could not load packages")
|
||||
raise InitializeException()
|
||||
|
||||
|
||||
def run_server(application: web.Application, architecture: str) -> None:
|
||||
'''
|
||||
def run_server(application: web.Application) -> None:
|
||||
"""
|
||||
run web application
|
||||
:param application: web application instance
|
||||
:param architecture: repository architecture
|
||||
'''
|
||||
application.logger.info('start server')
|
||||
"""
|
||||
application.logger.info("start server")
|
||||
|
||||
section = application['config'].get_section_name('web', architecture)
|
||||
host = application['config'].get(section, 'host')
|
||||
port = application['config'].getint(section, 'port')
|
||||
section = application["config"].get_section_name("web", application["architecture"])
|
||||
host = application["config"].get(section, "host")
|
||||
port = application["config"].getint(section, "port")
|
||||
|
||||
web.run_app(application, host=host, port=port, handle_signals=False,
|
||||
access_log=logging.getLogger('http'))
|
||||
access_log=logging.getLogger("http"))
|
||||
|
||||
|
||||
def setup_service(architecture: str, config: Configuration) -> web.Application:
|
||||
'''
|
||||
"""
|
||||
create web application
|
||||
:param architecture: repository architecture
|
||||
:param config: configuration instance
|
||||
:return: web application instance
|
||||
'''
|
||||
application = web.Application(logger=logging.getLogger('http'))
|
||||
"""
|
||||
application = web.Application(logger=logging.getLogger("http"))
|
||||
application.on_shutdown.append(on_shutdown)
|
||||
application.on_startup.append(on_startup)
|
||||
|
||||
application.middlewares.append(web.normalize_path_middleware(append_slash=False, remove_slash=True))
|
||||
application.middlewares.append(exception_handler(application.logger))
|
||||
|
||||
application.logger.info('setup routes')
|
||||
application.logger.info("setup routes")
|
||||
setup_routes(application)
|
||||
application.logger.info('setup templates')
|
||||
aiohttp_jinja2.setup(application, loader=jinja2.FileSystemLoader(config.get('web', 'templates')))
|
||||
|
||||
application.logger.info('setup configuration')
|
||||
application['config'] = config
|
||||
application.logger.info("setup templates")
|
||||
aiohttp_jinja2.setup(application, loader=jinja2.FileSystemLoader(config.getpath("web", "templates")))
|
||||
|
||||
application.logger.info('setup watcher')
|
||||
application['watcher'] = Watcher(architecture, config)
|
||||
application.logger.info("setup configuration")
|
||||
application["config"] = config
|
||||
application["architecture"] = architecture
|
||||
|
||||
application.logger.info("setup watcher")
|
||||
application["watcher"] = Watcher(architecture, config)
|
||||
|
||||
return application
|
||||
|
Reference in New Issue
Block a user