Esempio n. 1
0
def test_destroy() -> None:

    # Only executed if tests are run with --destroy flag
    if os.getenv("TEST_DESTROY_MODE", "0") != "1":
        log.info("Skipping destroy test, TEST_DESTROY_MODE not enabled")
        return

    # Always enable during core tests
    if not Connector.check_availability("authentication"):  # pragma: no cover
        log.warning("Skipping authentication test: service not available")
        return

    # if Connector.check_availability("sqlalchemy"):
    #     sql = sqlalchemy.get_instance()
    #     # Close previous connections, otherwise the new create_app will hang
    #     sql.session.remove()
    #     sql.session.close_all()

    auth = Connector.get_authentication_instance()

    user = auth.get_user(username=BaseAuthentication.default_user)
    assert user is not None

    create_app(mode=ServerModes.DESTROY)

    try:
        auth = Connector.get_authentication_instance()
        user = auth.get_user(username=BaseAuthentication.default_user)
        assert user is None
    except ServiceUnavailable:
        pass
Esempio n. 2
0
def clearcache():
    from restapi.server import create_app
    from restapi.services.cache import Cache

    create_app(name="Cache clearing")

    Cache.clear()

    log.info("Cache cleared")
Esempio n. 3
0
def clean() -> None:  # pragma: no cover
    """Destroy current services data"""

    from restapi.server import ServerModes, create_app

    log.info("Launching destruction app")

    create_app(name="Removing data", mode=ServerModes.DESTROY)

    log.info("Destruction completed")
Esempio n. 4
0
def forced_clean() -> None:  # pragma: no cover
    """DANGEROUS: Destroy current data without asking yes/no"""

    from restapi.server import ServerModes, create_app

    log.info("Launching destruction app")

    create_app(name="Removing data", mode=ServerModes.DESTROY)

    log.info("Destruction completed")
Esempio n. 5
0
def clearcache() -> None:
    """Clear all data from the endpoints cache"""
    from restapi.server import create_app
    from restapi.services.cache import Cache

    create_app(name="Cache clearing")

    Cache.clear()

    log.info("Cache cleared")
Esempio n. 6
0
def flask_cli(options=None):
    log.info("Launching the app")
    from restapi.server import create_app

    if options is None:
        options = {'name': 'RESTful HTTP API server'}
        app = create_app(**options)
        app.run(host=BIND_INTERFACE, threaded=True)
    else:
        create_app(**options)
    log.debug("cli execution completed")
Esempio n. 7
0
def flask_cli(options=None):
    log.info("Launching the app")
    from restapi.server import create_app
    # log.warning("TEST")
    if options is None:
        options = {'name': 'RESTful HTTP API server'}
        app = create_app(**options)
        app.run(host='0.0.0.0', threaded=True)
    else:
        create_app(**options)
        # app.run(debug=False)
    log.warning("Completed")
Esempio n. 8
0
def init(wait, force_user, force_group):
    """Initialize data for connected services"""
    if wait:
        mywait()

    from restapi.server import ServerModes, create_app

    log.info("Launching initialization app")

    options = {
        "force_user": force_user,
        "force_group": force_group,
    }
    create_app(name="Initializing services", mode=ServerModes.INIT, options=options)

    log.info("Initialization requested")
Esempio n. 9
0
def test_destroy() -> None:

    auth = Connector.get_authentication_instance()

    user = auth.get_user(username=BaseAuthentication.default_user)
    assert user is not None

    create_app(mode=ServerModes.DESTROY)

    if Connector.check_availability("sqlalchemy"):
        with pytest.raises(ServiceUnavailable):
            auth = Connector.get_authentication_instance()
            user = auth.get_user(username=BaseAuthentication.default_user)
    else:
        auth = Connector.get_authentication_instance()
        user = auth.get_user(username=BaseAuthentication.default_user)
        assert user is None
Esempio n. 10
0
def test_init() -> None:

    auth = Connector.get_authentication_instance()
    if Connector.authentication_service == "sqlalchemy":
        # Re-init does not work with MySQL due to issues with previous connections
        # Considering that:
        # 1) this is a workaround to test the initialization
        #       (not the normal workflow used by the application)
        # 2) the init is already tested with any other DB, included postgres
        # 3) MySQL is not used by any project
        # => there is no need to go crazy in debugging this issue!
        if auth.db.is_mysql():  # type: ignore
            return

        # sql = sqlalchemy.get_instance()

    if Connector.check_availability("sqlalchemy"):
        # Prevents errors like:
        # sqlalchemy.exc.ResourceClosedError: This Connection is closed
        Connector.disconnect_all()

        # sql = sqlalchemy.get_instance()
        # # Close previous connections, otherwise the new create_app will hang
        # sql.session.remove()
        # sql.session.close_all()

    try:
        create_app(mode=ServerModes.INIT)
        # This is only a rough retry to prevent random errors from sqlalchemy
    except Exception:  # pragma: no cover
        create_app(mode=ServerModes.INIT)

    auth = Connector.get_authentication_instance()
    try:
        user = auth.get_user(username=BaseAuthentication.default_user)
    # SqlAlchemy sometimes can raise an:
    # AttributeError: 'NoneType' object has no attribute 'twophase'
    # due to the multiple app created... should be an issue specific of this test
    # In that case... simply retry.
    except AttributeError:  # pragma: no cover
        user = auth.get_user(username=BaseAuthentication.default_user)

    assert user is not None
Esempio n. 11
0
    def setUp(self):
        """
        Note: in this base tests,
        I also want to check if i can run multiple Flask applications.

        Thi is why i prefer setUp on setUpClass
        """
        logger.debug('### Setting up the Flask server ###')
        app = create_app(testing_mode=True)
        self.app = app.test_client()
    def setUpClass(cls):
        logger.info('### Setting up flask server ###')
        app = create_app(testing_mode=True)
        cls.app = app.test_client()

        r = cls.app.post(
            AUTH_URI + '/login',
            data=json.dumps({'username': USER, 'password': PWD}))
        content = json.loads(r.data.decode('utf-8'))
        cls.auth_header = {
            'Authorization': 'Bearer ' + content['Response']['data']['token']}
Esempio n. 13
0
def test_init() -> None:

    # Only executed if tests are run with --destroy flag
    if os.getenv("TEST_DESTROY_MODE", "0") != "1":
        log.info("Skipping destroy test, TEST_DESTROY_MODE not enabled")
        return

    # Always enable during core tests
    if not Connector.check_availability("authentication"):  # pragma: no cover
        log.warning("Skipping authentication test: service not available")
        return

    auth = Connector.get_authentication_instance()
    if Connector.authentication_service == "sqlalchemy":
        # Re-init does not work with MySQL due to issues with previous connections
        # Considering that:
        # 1) this is a workaround to test the initialization
        #       (not the normal workflow used by the application)
        # 2) the init is already tested with any other DB, included postgres
        # 3) MySQL is not used by any project
        # => there is no need to go crazy in debugging this issue!
        if auth.db.is_mysql():  # type: ignore
            return

        # sql = sqlalchemy.get_instance()

    create_app(mode=ServerModes.INIT)

    auth = Connector.get_authentication_instance()
    try:
        user = auth.get_user(username=BaseAuthentication.default_user)
    # SqlAlchemy sometimes can raise an:
    # AttributeError: 'NoneType' object has no attribute 'twophase'
    # due to the multiple app created... should be an issue specific of this test
    # In that case... simply retry.
    except AttributeError:  # pragma: no cover
        user = auth.get_user(username=BaseAuthentication.default_user)

    assert user is not None
    def setUp(self):
        logger.info("### Setting up flask server ###")
        app = create_app(testing_mode=True)
        self.app = app.test_client()

        logger.info("*** VERIFY valid credentials")
        endpoint = AUTH_URI + "/login"
        r = self.app.post(endpoint, data=json.dumps({"username": USER, "password": PWD}))
        self.assertEqual(r.status_code, hcodes.HTTP_OK_BASIC)

        content = json.loads(r.data.decode("utf-8"))
        # Since unittests use class object and not instances
        # This is the only workaround to set a persistent variable
        # self.auth_header does not work
        self.__class__.auth_header = {"Authorization": "Bearer " + content["Response"]["data"]["token"]}
Esempio n. 15
0
    def setUp(self):
        """
        Note: in this base tests,
        I also want to check if i can run multiple Flask applications.

        Thi is why i prefer setUp on setUpClass
        """
        log.debug('### Setting up the Flask server ###')
        app = create_app(testing_mode=True)
        self.app = app.test_client()

        # Auth init from base/custom config
        ba.myinit()
        self._username = ba.default_user
        self._password = ba.default_password
Esempio n. 16
0
if args is not None:
    if args.debug:
        enable_debug = True
        logger.warning("Enabling DEBUG mode")
        time.sleep(1)

    if not args.security:
        enable_security = False
        logger.warning("No security enabled! Are you really sure?")
        time.sleep(1)

# The connection is HTTP internally to containers
# The proxy will handle HTTPS calls
# We can safely disable HTTPS on OAUTHLIB requests
# http://stackoverflow.com/a/27785830/2114395
if PRODUCTION:
    os.environ['OAUTHLIB_INSECURE_TRANSPORT'] = '1'


#############################
# BE FLASK
app = create_app(
    name='REST_API', enable_security=enable_security, debug=enable_debug)

if __name__ == "__main__":
    # Note: 'threaded' option avoid to see
    # angular request on this server dropping
    # and becoming slow if not totally frozen
    logger.info("*** Running Flask!")
    app.run(host=SERVER_HOSTS, port=SERVER_PORT, threaded=True)
Esempio n. 17
0
from restapi.connectors import celery
from restapi.server import ServerModes, create_app
from restapi.utilities.logs import log

instance = celery.get_instance()
# Used by Celery to run the instance (--app app)
celery_app = instance.celery_app

# Reload Flask app code for the worker (needed to have the app context available)
celery.CeleryExt.app = create_app(mode=ServerModes.WORKER)

log.debug("Celery worker is ready {}", celery_app)
Esempio n. 18
0
                    data["new_password"] = currentpwd
                    data["password_confirm"] = currentpwd
                    return get_auth_token(client, data)

        assert r.status_code == 200
        assert content is not None

        headers: CaseInsensitiveDict[str] = CaseInsensitiveDict()
        headers["Authorization"] = f"Bearer {content}"
        return content, headers

    # No need to restore the logger after this test because
    # schemathesis test is the last one!
    # (just because in alphabetic order there are no other tests)
    set_logger("WARNING")
    app = create_app()
    client = werkzeug.Client(app, werkzeug.wrappers.Response)

    if Env.get_bool("AUTH_ENABLE"):
        BaseAuthentication.load_default_user()
        BaseAuthentication.load_roles()
        USER = BaseAuthentication.default_user
        PWD = BaseAuthentication.default_password
        data = {"username": USER, "password": PWD}
        token, auth_header = get_auth_token(client, data)

        # it does not handle custom headers => the endpoint will provide partial schema
        # due to missing authentication => skipping all private endpoints and schemas
        # schema = schemathesis.from_wsgi('/api/specs', app)
        r = client.get(f"/api/specs?access_token={token}")
    else:
Esempio n. 19
0
def app() -> Flask:
    return create_app()
Esempio n. 20
0
So we made some improvement along the code.

"""

# from restapi.resources.services.celery.tasks import MyCelery
from commons.services.celery import celery_app
from restapi.server import create_app
from commons.meta import Meta
from commons.logs import get_logger

logger = get_logger(__name__)

################################################
# Reload Flask app code also for the worker
# This is necessary to have the app context available
app = create_app(worker_mode=True, debug=True)

# Recover celery app with current app
# celery_app = MyCelery(app)._current

# celery_app = MyCelery(app)._current
logger.debug("Celery %s" % celery_app)

################################################
# Import tasks modules to make sure all tasks are avaiable

meta = Meta()
main_package = "commons.tasks."
# Base tasks
submodules = meta.import_submodules_from_package(main_package + "base")
# Custom tasks
Esempio n. 21
0
 def setUpClass(cls):
     logger.info('### Setting up flask server ###')
     app = create_app(testing=True)
     cls.app = app.test_client()
Esempio n. 22
0
#!/usr/bin/env python
"""

RESTful API Python 3 Flask server

"""

import os

from restapi.config import PRODUCTION
from restapi.server import create_app

# Connection internal to containers, proxy handle all HTTPS calls
# We may safely disable HTTPS on OAUTHLIB requests
if PRODUCTION:
    # http://stackoverflow.com/a/27785830/2114395
    os.environ["OAUTHLIB_INSECURE_TRANSPORT"] = "1"

app = create_app(name="REST_API")

if __name__ == "__main__":
    app.run(host="0.0.0.0", threaded=True)
Esempio n. 23
0
def app():
    return create_app()
Esempio n. 24
0
def test_cli() -> None:
    runner = CliRunner()

    response = runner.invoke(cli.verify, ["test"])
    assert response.exit_code == 2

    assert "Got unexpected extra argument (test)" in response.output

    response = runner.invoke(cli.verify, ["--services", "neo4j"])
    assert response.exit_code == 2
    assert "Error: No such option: --services" in response.output

    response = runner.invoke(cli.verify, ["--service", "x"])
    assert response.exit_code == 1

    for service in ("neo4j", "sqlalchemy"):
        if not Connector.check_availability(service):
            continue

        response = runner.invoke(cli.verify, ["--service", service])
        assert response.exit_code == 0

    response = runner.invoke(cli.wait, [])
    assert response.exit_code == 0

    response = runner.invoke(cli.init, [])
    assert response.exit_code == 0

    response = runner.invoke(cli.init, ["--wait"])
    assert response.exit_code == 0

    response = runner.invoke(cli.clean, [])
    assert response.exit_code == 1
    assert "Do you want to continue? [y/N]:" in response.output

    response = runner.invoke(cli.tests, ["--file", "x"])
    assert response.exit_code == 1

    response = runner.invoke(cli.tests, ["--folder", "x"])
    assert response.exit_code == 1

    response = runner.invoke(cli.tests, ["--wait", "--file", "x"])
    assert response.exit_code == 1

    response = runner.invoke(cli.tests, ["--core", "--file", "x"])
    assert response.exit_code == 1

    variables = {
        "myhost": "myvalue",
        "myport": "111",
    }
    with pytest.raises(SystemExit):
        cli.get_service_address(variables, "host", "port", "myservice")

    with pytest.raises(SystemExit):
        cli.get_service_address(variables, "myhost", "port", "myservice")

    h, p = cli.get_service_address(variables, "myhost", "myport", "myservice")

    assert h == "myvalue"
    assert isinstance(p, int)
    assert p == 111

    from restapi.server import create_app

    create_app(name="Cache clearing")

    # make_name prevents the use of rapydo default make_name function, that is only
    # working from the endpoints context since it is based on tokens from flask.requests
    @decorators.cache(timeout=3600, make_name=None)
    def random_values() -> int:
        return random.randrange(0, 100000)

    val = random_values()
    time.sleep(0.9)
    assert random_values() == val
    time.sleep(0.9)
    assert random_values() == val

    # Let's clear the cache
    response = runner.invoke(cli.clearcache, [])
    assert response.exit_code == 0

    assert random_values() != val
Esempio n. 25
0
def app():
    app = create_app(testing_mode=True)
    return app
Esempio n. 26
0
def test_celery(app: Flask, faker: Faker) -> None:

    if not Connector.check_availability(CONNECTOR):

        try:
            obj = connector.get_instance()
            pytest.fail("No exception raised")  # pragma: no cover
        except ServiceUnavailable:
            pass

        log.warning("Skipping {} tests: service not available", CONNECTOR)
        return None

    log.info("Executing {} tests", CONNECTOR)

    obj = connector.get_instance()
    assert obj is not None

    task = obj.celery_app.send_task("test_task")

    assert task is not None
    assert task.id is not None

    if obj.variables.get("backend") == "RABBIT":
        log.warning(
            "Due to limitations on RABBIT backend task results will not be tested"
        )
    else:
        try:
            r = task.get(timeout=10)
            assert r is not None
            # This is the task output, as defined in task_template.py.j2
            assert r == "Task executed!"
            assert task.status == "SUCCESS"
            assert task.result == "Task executed!"
        except celery.exceptions.TimeoutError:  # pragma: no cover
            pytest.fail(
                f"Task timeout, result={task.result}, status={task.status}")

    if CeleryExt.CELERYBEAT_SCHEDULER is None:

        try:
            obj.get_periodic_task("does_not_exist")
            pytest.fail("get_periodic_task with unknown CELERYBEAT_SCHEDULER"
                        )  # pragma: no cover
        except AttributeError as e:
            assert str(e) == "Unsupported celery-beat scheduler: None"
        except BaseException:  # pragma: no cover
            pytest.fail("Unexpected exception raised")

        try:
            obj.delete_periodic_task("does_not_exist")
            pytest.fail(
                "delete_periodic_task with unknown CELERYBEAT_SCHEDULER"
            )  # pragma: no cover
        except AttributeError as e:
            assert str(e) == "Unsupported celery-beat scheduler: None"
        except BaseException:  # pragma: no cover
            pytest.fail("Unexpected exception raised")

        try:
            obj.create_periodic_task(name="task1",
                                     task="task.does.not.exists",
                                     every="60")
            pytest.fail(
                "create_periodic_task with unknown CELERYBEAT_SCHEDULER"
            )  # pragma: no cover
        except AttributeError as e:
            assert str(e) == "Unsupported celery-beat scheduler: None"
        except BaseException:  # pragma: no cover
            pytest.fail("Unexpected exception raised")

        try:
            obj.create_crontab_task(name="task2",
                                    task="task.does.not.exists",
                                    minute="0",
                                    hour="1")
            pytest.fail("create_crontab_task with unknown CELERYBEAT_SCHEDULER"
                        )  # pragma: no cover
        except AttributeError as e:
            assert str(e) == "Unsupported celery-beat scheduler: None"
        except BaseException:  # pragma: no cover
            pytest.fail("Unexpected exception raised")

    else:
        assert obj.get_periodic_task("does_not_exist") is None
        assert not obj.delete_periodic_task("does_not_exist")

        obj.create_periodic_task(name="task1",
                                 task="task.does.not.exists",
                                 every="60")

        assert obj.delete_periodic_task("task1")
        assert not obj.delete_periodic_task("task1")

        obj.create_periodic_task(
            name="task1_bis",
            task="task.does.not.exists",
            every="60",
            period="seconds",
            args=["a", "b", "c"],
            kwargs={
                "a": 1,
                "b": 2,
                "c": 3
            },
        )

        assert obj.delete_periodic_task("task1_bis")
        assert not obj.delete_periodic_task("task1_bis")

        # cron at 01:00
        obj.create_crontab_task(name="task2",
                                task="task.does.not.exists",
                                minute="0",
                                hour="1")

        assert obj.delete_periodic_task("task2")
        assert not obj.delete_periodic_task("task2")

        obj.create_crontab_task(
            name="task2_bis",
            task="task.does.not.exists",
            minute="0",
            hour="1",
            day_of_week="*",
            day_of_month="*",
            month_of_year="*",
            args=["a", "b", "c"],
            kwargs={
                "a": 1,
                "b": 2,
                "c": 3
            },
        )

        assert obj.delete_periodic_task("task2_bis")
        assert not obj.delete_periodic_task("task2_bis")

        if CeleryExt.CELERYBEAT_SCHEDULER == "REDIS":

            obj.create_periodic_task(
                name="task3",
                task="task.does.not.exists",
                every=60,
            )
            assert obj.delete_periodic_task("task3")

            obj.create_periodic_task(name="task4",
                                     task="task.does.not.exists",
                                     every=60,
                                     period="seconds")
            assert obj.delete_periodic_task("task4")

            obj.create_periodic_task(name="task5",
                                     task="task.does.not.exists",
                                     every=60,
                                     period="minutes")
            assert obj.delete_periodic_task("task5")

            obj.create_periodic_task(name="task6",
                                     task="task.does.not.exists",
                                     every=60,
                                     period="hours")
            assert obj.delete_periodic_task("task6")

            obj.create_periodic_task(name="task7",
                                     task="task.does.not.exists",
                                     every=60,
                                     period="days")
            assert obj.delete_periodic_task("task7")

            try:
                obj.create_periodic_task(
                    name="task8",
                    task="task.does.not.exists",
                    every="60",
                    period="years",  # type: ignore
                )
            except BadRequest as e:
                assert str(e) == "Invalid timedelta period: years"

            obj.create_periodic_task(
                name="task9",
                task="task.does.not.exists",
                every=timedelta(seconds=60),
            )
            assert obj.delete_periodic_task("task9")

            try:
                obj.create_periodic_task(
                    name="task10",
                    task="task.does.not.exists",
                    every=["60"],  # type: ignore
                )
            except AttributeError as e:
                assert str(
                    e) == "Invalid input parameter every = ['60'] (type list)"

            try:
                obj.create_periodic_task(
                    name="task11",
                    task="task.does.not.exists",
                    every="invalid",
                )
            except AttributeError as e:
                assert str(
                    e) == "Invalid input parameter every = invalid (type str)"

        else:
            obj.create_periodic_task(name="task3",
                                     task="task.does.not.exists",
                                     every="60",
                                     period="minutes")
            assert obj.delete_periodic_task("task3")

    obj.disconnect()

    # a second disconnect should not raise any error
    obj.disconnect()

    # Create new connector with short expiration time
    obj = connector.get_instance(expiration=2, verification=1)
    obj_id = id(obj)

    # Connector is expected to be still valid
    obj = connector.get_instance(expiration=2, verification=1)
    assert id(obj) == obj_id

    time.sleep(1)

    # The connection should have been checked and should be still valid
    obj = connector.get_instance(expiration=2, verification=1)
    assert id(obj) == obj_id

    time.sleep(1)

    # Connection should have been expired and a new connector been created
    obj = connector.get_instance(expiration=2, verification=1)
    assert id(obj) != obj_id

    assert obj.is_connected()
    obj.disconnect()
    assert not obj.is_connected()

    # ... close connection again ... nothing should happens
    obj.disconnect()

    with connector.get_instance() as obj:
        assert obj is not None

    app = create_app(mode=ServerModes.WORKER)
    assert app is not None
    from restapi.utilities.logs import LOGS_FILE

    assert os.environ["HOSTNAME"] == "backend-server"
    assert LOGS_FILE == "backend-server"

    # this decorator is expected to be used in celery context, i.e. the self reference
    # should contains a request, injected by celery. Let's mock this by injecting an
    # artificial self
    @send_errors_by_email
    def this_function_raises_exceptions(self):
        raise AttributeError("Just an exception")

    class FakeRequest:
        def __init__(self, task_id, task, args):
            self.id = task_id
            self.task = task
            self.args = args

    class FakeSelf:
        def __init__(self, task_id, task, args):
            self.request = FakeRequest(task_id, task, args)

    task_id = faker.pystr()
    task_name = faker.pystr()
    task_args = [faker.pystr()]

    this_function_raises_exceptions(FakeSelf(task_id, task_name, task_args))

    mail = BaseTests.read_mock_email()
    assert mail.get("body") is not None

    assert f"Celery task {task_id} failed" in mail.get("body")
    assert f"Name: {task_name}" in mail.get("body")
    assert f"Arguments: {str(task_args)}" in mail.get("body")
    assert "Error: Traceback (most recent call last):" in mail.get("body")
    assert 'raise AttributeError("Just an exception")' in mail.get("body")
Esempio n. 27
0
#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""

RESTful API Python 3 Flask server

"""

import os
import pretty_errors
from restapi.confs import PRODUCTION
from restapi.server import create_app
from restapi.utilities.logs import log

# Connection internal to containers, proxy handle all HTTPS calls
# We may safely disable HTTPS on OAUTHLIB requests
if PRODUCTION:
    # http://stackoverflow.com/a/27785830/2114395
    os.environ['OAUTHLIB_INSECURE_TRANSPORT'] = '1'
BIND_INTERFACE = "0.0.0.0"

#############################
# BE FLASK
app = create_app(name='REST_API')

if __name__ == "__main__":
    log.debug("Server running (w/ {})", pretty_errors.__name__)
    app.run(host=BIND_INTERFACE, threaded=True)
Esempio n. 28
0
a flask templating framework like ours.
So we made some improvement along the code.

"""

from restapi.server import create_app
from utilities import CUSTOM_PACKAGE
from utilities.meta import Meta
from utilities.logs import get_logger

log = get_logger(__name__)

################################################
# Reload Flask app code also for the worker
# This is necessary to have the app context available
app = create_app(worker_mode=True)

celery_app = app.extensions.get('celery').celery_app
celery_app.app = app


def get_service(service):
    return celery_app.app.extensions.get(service).get_instance()


celery_app.get_service = get_service

################################################
# Import tasks modules to make sure all tasks are available

meta = Meta()
Esempio n. 29
0
def test_celery(app: Flask, faker: Faker) -> None:

    log.info("Executing {} tests", CONNECTOR)

    obj = connector.get_instance()
    assert obj is not None

    task = obj.celery_app.send_task("test_task", args=("myinput", ))

    assert task is not None
    assert task.id is not None

    # Mocked task
    task_output = BaseTests.send_task(app, "test_task", "myinput")

    # As defined in task template
    assert task_output == "Task executed!"

    # wrong is a special value included in tasks template
    with pytest.raises(Ignore):
        BaseTests.send_task(app, "test_task", "wrong")

    project_title = get_project_configuration("project.title",
                                              default="YourProject")

    mail = BaseTests.read_mock_email()

    body = mail.get("body")
    headers = mail.get("headers")
    assert body is not None
    assert headers is not None
    assert f"Subject: {project_title}: Task test_task failed" in headers
    assert "this email is to notify you that a Celery task failed!" in body
    # fixed-id is a mocked value set in TESTING mode by @task in Celery connector
    assert "Task ID: fixed-id" in body
    assert "Task name: test_task" in body
    assert "Arguments: ('wrong',)" in body
    assert "Error Stack" in body
    assert "Traceback (most recent call last):" in body

    exc = (
        "AttributeError: "
        "You can raise exceptions to stop the task execution in case of errors"
    )
    assert exc in body

    # celery.exceptions.Ignore exceptions are ignored

    BaseTests.delete_mock_email()
    # ignore is a special value included in tasks template
    with pytest.raises(Ignore):
        BaseTests.send_task(app, "test_task", "ignore")
    # the errors decorator re-raise the Ignore exception, without any further action
    # No email is sent in case of Ignore exceptions
    with pytest.raises(FileNotFoundError):
        mail = BaseTests.read_mock_email()

    # retry is a special value included in tasks template
    with pytest.raises(CeleryRetryTask):
        BaseTests.send_task(app, "test_task", "retry")

    mail = BaseTests.read_mock_email()

    body = mail.get("body")
    headers = mail.get("headers")
    assert body is not None
    assert headers is not None
    assert f"Subject: {project_title}: Task test_task failed (failure #1)" in headers
    assert "this email is to notify you that a Celery task failed!" in body
    # fixed-id is a mocked value set in TESTING mode by @task in Celery connector
    assert "Task ID: fixed-id" in body
    assert "Task name: test_task" in body
    assert "Arguments: ('retry',)" in body
    assert "Error Stack" in body
    assert "Traceback (most recent call last):" in body

    exc = "CeleryRetryTask: Force the retry of this task"
    assert exc in body

    # retry2 is a special value included in tasks template
    # Can't easily import the custom exception defined in the task...
    # a generic exception is enough here
    with pytest.raises(Exception):
        BaseTests.send_task(app, "test_task", "retry2")

    mail = BaseTests.read_mock_email()

    body = mail.get("body")
    headers = mail.get("headers")
    assert body is not None
    assert headers is not None
    assert f"Subject: {project_title}: Task test_task failed (failure #1)" in headers
    assert "this email is to notify you that a Celery task failed!" in body
    # fixed-id is a mocked value set in TESTING mode by @task in Celery connector
    assert "Task ID: fixed-id" in body
    assert "Task name: test_task" in body
    assert "Arguments: ('retry2',)" in body
    assert "Error Stack" in body
    assert "Traceback (most recent call last):" in body

    exc = "MyException: Force the retry of this task by using a custom exception"
    assert exc in body

    with pytest.raises(AttributeError, match=r"Task not found"):
        BaseTests.send_task(app, "does-not-exist")

    if obj.variables.get("backend_service") == "RABBIT":
        log.warning(
            "Due to limitations on RABBIT backend task results will not be tested"
        )
    else:
        try:
            r = task.get(timeout=10)
            assert r is not None
            # This is the task output, as defined in task_template.py.j2
            assert r == "Task executed!"
            assert task.status == "SUCCESS"
            assert task.result == "Task executed!"
        except celery.exceptions.TimeoutError:  # pragma: no cover
            pytest.fail(
                f"Task timeout, result={task.result}, status={task.status}")

    obj.disconnect()

    # a second disconnect should not raise any error
    obj.disconnect()

    # Create new connector with short expiration time
    obj = connector.get_instance(expiration=2, verification=1)
    obj_id = id(obj)

    # Connector is expected to be still valid
    obj = connector.get_instance(expiration=2, verification=1)
    assert id(obj) == obj_id

    time.sleep(1)

    # The connection should have been checked and should be still valid
    obj = connector.get_instance(expiration=2, verification=1)
    assert id(obj) == obj_id

    time.sleep(1)

    # Connection should have been expired and a new connector been created
    obj = connector.get_instance(expiration=2, verification=1)
    assert id(obj) != obj_id

    assert obj.is_connected()
    obj.disconnect()
    assert not obj.is_connected()

    # ... close connection again ... nothing should happen
    obj.disconnect()

    with connector.get_instance() as obj:
        assert obj is not None

    app = create_app(mode=ServerModes.WORKER)
    assert app is not None