diff --git a/lib/hawat/blueprints/dbstatus/__init__.py b/lib/hawat/blueprints/dbstatus/__init__.py index eb40c245308a94164a610ed0f1c8e040f6e00069..4e27e2e76e9a4c2eede23686c0dd4e1d0451c208 100644 --- a/lib/hawat/blueprints/dbstatus/__init__.py +++ b/lib/hawat/blueprints/dbstatus/__init__.py @@ -9,9 +9,6 @@ """ -Description ------------ - This pluggable module provides access to database status information. The following information is provided: @@ -59,18 +56,12 @@ import sys import datetime import traceback -# -# Flask related modules. -# import werkzeug.routing import flask import flask_login from flask_babel import gettext, lazy_gettext from sqlalchemy import or_ -# -# Custom modules. -# import mentat.const import mentat.system from mentat.datatype.sqldb import UserModel, GroupModel, FilterModel, SettingsReportingModel @@ -99,22 +90,18 @@ class ViewView(HTMLMixin, PsycopgMixin, SimpleView): @classmethod def get_view_name(cls): - """*Implementation* of :py:func:`vial.view.BaseView.get_view_name`.""" return 'view' @classmethod def get_view_icon(cls): - """*Implementation* of :py:func:`vial.view.BaseView.get_view_icon`.""" return 'module-{}'.format(BLUEPRINT_NAME) @classmethod def get_menu_title(cls, **kwargs): - """*Implementation* of :py:func:`vial.view.BaseView.get_menu_title`.""" return lazy_gettext('Database status') @classmethod def get_view_title(cls, **kwargs): - """*Implementation* of :py:func:`vial.view.BaseView.get_view_title`.""" return lazy_gettext('Database status') def _enrich_result_queries(self, result): @@ -134,7 +121,6 @@ class ViewView(HTMLMixin, PsycopgMixin, SimpleView): return result def do_before_response(self, **kwargs): - """*Implementation* of :py:func:`vial.view.RenderableView.do_before_response`.""" self.response_context.update( query_status_events = self._enrich_result_queries(self.get_db().queries_status()), database_status_events = self.get_db().database_status(), @@ -181,22 +167,18 @@ class MyQueriesView(HTMLMixin, PsycopgMixin, SimpleView): @classmethod def get_view_name(cls): - """*Implementation* of :py:func:`vial.view.BaseView.get_view_name`.""" return 'queries_my' @classmethod def get_view_icon(cls): - """*Implementation* of :py:func:`vial.view.BaseView.get_view_icon`.""" return 'module-{}'.format(BLUEPRINT_NAME) @classmethod def get_menu_title(cls, **kwargs): - """*Implementation* of :py:func:`vial.view.BaseView.get_menu_title`.""" return lazy_gettext('My queries') @classmethod def get_view_title(cls, **kwargs): - """*Implementation* of :py:func:`vial.view.BaseView.get_view_title`.""" return lazy_gettext('My currently running queries') def _enrich_result_queries(self, result): @@ -216,7 +198,6 @@ class MyQueriesView(HTMLMixin, PsycopgMixin, SimpleView): return result def do_before_response(self, **kwargs): - """*Implementation* of :py:func:`vial.view.RenderableView.do_before_response`.""" self.response_context.update( query_status_events = self._enrich_result_queries( self.get_db().queries_status( @@ -249,34 +230,28 @@ class QueryStatusView(AJAXMixin, PsycopgMixin, RenderableView): @classmethod def get_view_name(cls): - """*Implementation* of :py:func:`vial.view.BaseView.get_view_name`.""" return 'query-status' @classmethod def get_view_icon(cls): - """*Implementation* of :py:func:`vial.view.BaseView.get_view_icon`.""" return 'module-{}'.format(BLUEPRINT_NAME) @classmethod def get_menu_title(cls, **kwargs): - """*Implementation* of :py:func:`vial.view.BaseView.get_menu_title`.""" return lazy_gettext('Query status') @classmethod def get_view_title(cls, **kwargs): - """*Implementation* of :py:func:`vial.view.BaseView.get_view_title`.""" return lazy_gettext('Query status') @classmethod def get_view_url(cls, **kwargs): - """*Implementation* of :py:func:`vial.view.BaseView.get_view_url`.""" return flask.url_for( cls.get_view_endpoint(), item_id = kwargs['item']['query_name'] ) def do_before_response(self, **kwargs): - """*Implementation* of :py:func:`vial.view.RenderableView.do_before_response`.""" query_status = self.get_db().query_status(kwargs['item_id']) if not query_status: self.abort(404) @@ -303,7 +278,7 @@ class QueryStatusView(AJAXMixin, PsycopgMixin, RenderableView): return self.generate_response() -class AbstractQueryStopView(PsycopgMixin, RenderableView): +class AbstractQueryStopView(PsycopgMixin, RenderableView): # pylint: disable=locally-disabled,abstract-method """ Application view providing ability to stop given query. """ @@ -315,22 +290,18 @@ class AbstractQueryStopView(PsycopgMixin, RenderableView): @classmethod def get_view_icon(cls): - """*Implementation* of :py:func:`vial.view.BaseView.get_view_icon`.""" return 'action-stop' @classmethod def get_menu_title(cls, **kwargs): - """*Implementation* of :py:func:`vial.view.BaseView.get_menu_title`.""" return lazy_gettext('Stop query') @classmethod def get_view_title(cls, **kwargs): - """*Implementation* of :py:func:`vial.view.BaseView.get_view_title`.""" return lazy_gettext('Stop query') @classmethod def get_view_url(cls, **kwargs): - """*Implementation* of :py:func:`vial.view.BaseView.get_view_url`.""" return flask.url_for( cls.get_view_endpoint(), item_id = kwargs['item']['query_name'] @@ -360,11 +331,6 @@ class AbstractQueryStopView(PsycopgMixin, RenderableView): return gettext('Canceled stopping query <strong>%(item_id)s</strong>.', item_id = str(kwargs['item']['query_name'])) def get_url_next(self): - """ - *Hook method*. Must return URL for redirection after action *success*. In - most cases there should be call for :py:func:`flask.url_for` function - somewhere in this method. - """ try: return flask.url_for( '{}.{}'.format(self.module_name, 'view') @@ -453,12 +419,10 @@ class QueryStopView(HTMLMixin, AbstractQueryStopView): """ @classmethod def get_view_name(cls): - """*Implementation* of :py:func:`vial.view.BaseView.get_view_name`.""" return 'query-stop' @classmethod def get_view_template(cls): - """*Implementation* of :py:func:`vial.view.RenderableView.get_view_template`.""" return '{}/query_stop.html'.format(cls.module_name) @@ -468,7 +432,6 @@ class ApiQueryStopView(AJAXMixin, AbstractQueryStopView): """ @classmethod def get_view_name(cls): - """*Implementation* of :py:func:`vial.view.BaseView.get_view_name`.""" return 'api-query-stop' @@ -478,26 +441,22 @@ class DashboardView(HTMLMixin, SQLAlchemyMixin, SimpleView): # pylint: disable= """ authentication = True - authorization = [vial.acl.PERMISSION_ADMIN] + authorization = [vial.acl.PERMISSION_POWER] @classmethod def get_view_name(cls): - """*Implementation* of :py:func:`vial.view.BaseView.get_view_name`.""" return 'dashboard' @classmethod def get_menu_title(cls, **kwargs): - """*Implementation* of :py:func:`vial.view.BaseView.get_menu_title`.""" return lazy_gettext('Object management') @classmethod def get_view_title(cls, **kwargs): - """*Implementation* of :py:func:`vial.view.BaseView.get_view_title`.""" return lazy_gettext('Object management dashboards') @classmethod def get_view_template(cls): - """*Implementation* of :py:func:`vial.view.RenderableView.get_view_template`.""" return '{}/dashboard.html'.format(cls.module_name) #--------------------------------------------------------------------------- @@ -711,25 +670,13 @@ class DashboardView(HTMLMixin, SQLAlchemyMixin, SimpleView): # pylint: disable= class DatabaseStatusBlueprint(VialBlueprint): - """ - Hawat pluggable module - database status. - """ + """Pluggable module - database status.""" @classmethod def get_module_title(cls): - """*Implementation* of :py:func:`vial.app.VialBlueprint.get_module_title`.""" return lazy_gettext('Database status overview pluggable module') def register_app(self, app): - """ - *Callback method*. Will be called from :py:func:`hawat.base.HawatApp.register_blueprint` - method and can be used to customize the Flask application object. Possible - use cases: - - * application menu customization - - :param hawat.base.HawatApp app: Flask application to be customized. - """ app.menu_main.add_entry( 'view', 'dashboards.dbstatus', diff --git a/lib/hawat/blueprints/dbstatus/test/__init__.py b/lib/hawat/blueprints/dbstatus/test/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..c613a164942fbdcebe696ab3167e872266b61388 --- /dev/null +++ b/lib/hawat/blueprints/dbstatus/test/__init__.py @@ -0,0 +1,160 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +#------------------------------------------------------------------------------- +# This file is part of Mentat system (https://mentat.cesnet.cz/). +# +# Copyright (C) since 2011 CESNET, z.s.p.o (http://www.ces.net/) +# Use of this source is governed by the MIT license, see LICENSE file. +#------------------------------------------------------------------------------- + + +""" +Unit tests for :py:mod:`hawat.blueprints.dbstatus`. +""" + + +import unittest + +import vial.const +import vial.test +import vial.db + +from hawat.test import BaseAppTestCase + + +class DBStatusViewTestCase(BaseAppTestCase): + """Class for testing ``dbstatus.view`` endpoint.""" + + def _attempt_fail(self): + self.assertGetURL( + '/dbstatus/view', + 403 + ) + + def _attempt_succeed(self): + self.assertGetURL( + '/dbstatus/view', + 200, + [ + b'Database status', + b'<strong>Event database</strong>', + b'<strong>PostgreSQL</strong>', + ] + ) + + def test_01_as_anonymous(self): + """Test access as anonymous user.""" + self._attempt_fail() + + @vial.test.do_as_user_decorator(vial.const.ROLE_USER) + def test_02_as_user(self): + """Test access as user ``user``.""" + self._attempt_fail() + + @vial.test.do_as_user_decorator(vial.const.ROLE_DEVELOPER) + def test_03_as_developer(self): + """Test access as user ``developer``.""" + self._attempt_fail() + + @vial.test.do_as_user_decorator(vial.const.ROLE_MAINTAINER) + def test_04_as_maintainer(self): + """Test access as user ``maintainer``.""" + self._attempt_fail() + + @vial.test.do_as_user_decorator(vial.const.ROLE_ADMIN) + def test_05_as_admin(self): + """Test access as user ``admin``.""" + self._attempt_succeed() + + +class DBStatusMyQueriesTestCase(BaseAppTestCase): + """Class for testing ``dbstatus.queries_my`` endpoint.""" + + def _attempt_fail(self): + self.assertGetURL( + '/dbstatus/query/my', + 403 + ) + + def _attempt_succeed(self): + self.assertGetURL( + '/dbstatus/query/my', + 200, + [ + b'My currently running queries' + ] + ) + + def test_01_as_anonymous(self): + """Test access as anonymous user.""" + self._attempt_fail() + + @vial.test.do_as_user_decorator(vial.const.ROLE_USER) + def test_02_as_user(self): + """Test access as user ``user``.""" + self._attempt_succeed() + + @vial.test.do_as_user_decorator(vial.const.ROLE_DEVELOPER) + def test_03_as_developer(self): + """Test access as user ``developer``.""" + self._attempt_succeed() + + @vial.test.do_as_user_decorator(vial.const.ROLE_MAINTAINER) + def test_04_as_maintainer(self): + """Test access as user ``maintainer``.""" + self._attempt_succeed() + + @vial.test.do_as_user_decorator(vial.const.ROLE_ADMIN) + def test_05_as_admin(self): + """Test access as user ``admin``.""" + self._attempt_succeed() + + +class DBStatusDashboardTestCase(BaseAppTestCase): + """Class for testing ``dbstatus.dashboard`` endpoint.""" + + def _attempt_fail(self): + self.assertGetURL( + '/dbstatus/dashboard', + 403 + ) + + def _attempt_succeed(self): + self.assertGetURL( + '/dbstatus/dashboard', + 200, + [ + b'Object management dashboards' + ] + ) + + def test_01_as_anonymous(self): + """Test access as anonymous user.""" + self._attempt_fail() + + @vial.test.do_as_user_decorator(vial.const.ROLE_USER) + def test_02_as_user(self): + """Test access as user ``user``.""" + self._attempt_fail() + + @vial.test.do_as_user_decorator(vial.const.ROLE_DEVELOPER) + def test_03_as_developer(self): + """Test access as user ``developer``.""" + self._attempt_fail() + + @vial.test.do_as_user_decorator(vial.const.ROLE_MAINTAINER) + def test_04_as_maintainer(self): + """Test access as user ``maintainer``.""" + self._attempt_succeed() + + @vial.test.do_as_user_decorator(vial.const.ROLE_ADMIN) + def test_05_as_admin(self): + """Test access as user ``admin``.""" + self._attempt_succeed() + + +#------------------------------------------------------------------------------- + + +if __name__ == "__main__": + unittest.main()