halfapi/tests/conftest.py

308 lines
8.2 KiB
Python
Raw Normal View History

#!/usr/bin/env python3
import logging
import functools
import re
import os
import subprocess
import importlib
import tempfile
from typing import Dict, Tuple
from uuid import uuid1, uuid4, UUID
from click.testing import CliRunner
import jwt
[0.5.3] Squashed commit of the following: commit ac935db6d62656713183707707d083298c1f34b0 Author: Maxime Alves LIRMM@home <maxime.alves@lirmm.fr> Date: Thu Jun 17 18:52:49 2021 +0200 [tests] remove dummy-domain from dependencies commit 4d50363c9b1502d1d8b7cbafc207e80cdbe247a4 Author: Maxime Alves LIRMM@home <maxime.alves@lirmm.fr> Date: Thu Jun 17 18:52:18 2021 +0200 [tests] update tests for 0.5.3 commit 6181592692464d21de9807e1e890b4ac92efc387 Author: Maxime Alves LIRMM@home <maxime.alves@lirmm.fr> Date: Thu Jun 17 18:17:51 2021 +0200 [lib.*] Refactor libs commit ed7485a8a16b60dde8acc0d2b9afca00a75fce3c Author: Maxime Alves LIRMM@home <maxime.alves@lirmm.fr> Date: Thu Jun 17 18:15:10 2021 +0200 [app] Use HalfAPI class to be able to use custom configuration à commit fa1ca6bf9df4ea17d7fa7dfdf3694c56746e9c7f Author: Maxime Alves LIRMM <maxime.alves@lirmm.fr> Date: Wed Jun 16 15:34:25 2021 +0200 [wip] tests dummy_domain commit 86e8dd3465e0bd0f3d49f28fd9e05a52874e969a Author: Maxime Alves LIRMM <maxime.alves@lirmm.fr> Date: Tue Jun 15 18:12:13 2021 +0200 [0.5.3] ajout de la config actuelle dans les arguments des routes commit aa7ec62c7a3b5a0ae0dc0cc79bd509eece44d5ff Author: Maxime Alves LIRMM <maxime.alves@lirmm.fr> Date: Tue Jun 15 11:16:23 2021 +0200 [lib.jwtMw] verify signature even if halfapi is in DEBUG mode commit e208728d7ec61b0de583c66c348f73ac7a884108 Author: Maxime Alves LIRMM <maxime.alves@lirmm.fr> Date: Tue Jun 15 10:49:46 2021 +0200 [lib.acl] args_check doesn't check required/optional arguments if "args" is not specified in request, if the target function is not async commit aa4c309778e4c969fe1314da305e02112d4641b7 Author: Maxime Alves LIRMM <maxime.alves@lirmm.fr> Date: Tue Jun 15 09:45:37 2021 +0200 [lib.domain] SUBROUTER can be a path parameter if including ":" commit 138420461d5c1ba0c7379dcda64e9a38aa5d768d Author: Maxime Alves LIRMM@home <maxime.alves@lirmm.fr> Date: Tue Jun 15 07:24:32 2021 +0200 [gitignore] *.swp commit 0c1e2849bad591d62f7399d820a044cf4e06de12 Author: Maxime Alves LIRMM@home <maxime.alves@lirmm.fr> Date: Tue Jun 15 07:24:14 2021 +0200 [tests] test get route with dummy projects commit 7227e2d7f105516b67f3acac27b242fe01b59215 Author: Maxime Alves LIRMM@home <maxime.alves@lirmm.fr> Date: Mon Jun 14 17:18:47 2021 +0200 [lib.domain] handle modules without ROUTES attribute commit 78c75cd60ead023e7a2d7fa2e5d1059fada0044f Author: Maxime Alves LIRMM@home <maxime.alves@lirmm.fr> Date: Mon Jun 14 16:34:58 2021 +0200 [tests] add dummy_project_router tests for path-based routers (without ROUTES variable)
2021-06-17 18:53:23 +02:00
import sys
from unittest.mock import patch
import pytest
from starlette.applications import Starlette
from starlette.responses import PlainTextResponse
from starlette.middleware.authentication import AuthenticationMiddleware
from starlette.testclient import TestClient
from halfapi import __version__
from halfapi.cli.cli import cli
from halfapi.cli.init import init, format_halfapi_etc
from halfapi.cli.domain import domain, create_domain
from halfapi.lib.responses import ORJSONResponse
from halfapi.lib.jwt_middleware import JWTAuthenticationBackend
logger = logging.getLogger('halfapitest')
PROJNAME = os.environ.get('PROJ','tmp_api')
SECRET = 'dummysecret'
from halfapi.lib.jwt_middleware import (
JWTUser, JWTAuthenticationBackend,
JWTWebSocketAuthenticationBackend)
@pytest.fixture
def token_builder():
yield jwt.encode({
'name':'xxx',
'user_id': str(uuid4())},
key=SECRET
)
@pytest.fixture
def token_debug_false_builder():
yield jwt.encode({
'name':'xxx',
'user_id': str(uuid4()),
'debug': False},
key=SECRET
)
@pytest.fixture
def token_debug_true_builder():
yield jwt.encode({
'name':'xxx',
'user_id': str(uuid4()),
'debug': True},
key=SECRET
)
@pytest.fixture
def runner():
return CliRunner()
@pytest.fixture
def cli_runner():
"""Yield a click.testing.CliRunner to invoke the CLI."""
class_ = CliRunner
def invoke_wrapper(f):
"""Augment CliRunner.invoke to emit its output to stdout.
This enables pytest to show the output in its logs on test
failures.
"""
@functools.wraps(f)
def wrapper(*args, **kwargs):
echo = kwargs.pop('echo', False)
result = f(*args, **kwargs)
if echo is True:
sys.stdout.write(result.output)
return result
return wrapper
class_.invoke = invoke_wrapper(class_.invoke)
cli_runner_ = class_()
yield cli_runner_
@pytest.fixture
def halfapicli(cli_runner):
def caller(*args):
return cli_runner.invoke(cli, ' '.join(args))
yield caller
@pytest.fixture
def halfapi_conf_dir():
return confdir('HALFAPI_CONF_DIR')
def confdir(dirname):
d = os.environ.get(dirname)
if not d:
os.environ[dirname] = tempfile.mkdtemp(prefix='halfapi_')
return os.environ.get(dirname)
if not os.path.isdir(d):
os.mkdir(d)
return d
@pytest.fixture
def halform_conf_dir():
return confdir('HALFORM_CONF_DIR')
# store history of failures per test class name and per index in parametrize (if
# parametrize used)
_test_failed_incremental: Dict[str, Dict[Tuple[int, ...], str]] = {}
def pytest_runtest_makereport(item, call):
if "incremental" in item.keywords:
# incremental marker is used
if call.excinfo is not None:
# the test has failed
# retrieve the class name of the test
cls_name = str(item.cls)
# retrieve the index of the test (if parametrize is used in
# combination with incremental)
parametrize_index = (
tuple(item.callspec.indices.values())
if hasattr(item, "callspec")
else ()
)
# retrieve the name of the test function
test_name = item.originalname or item.name
# store in _test_failed_incremental the original name of the failed
# test
_test_failed_incremental.setdefault(cls_name, {}).setdefault(
parametrize_index, test_name
)
def pytest_runtest_setup(item):
if "incremental" in item.keywords:
# retrieve the class name of the test
cls_name = str(item.cls)
# check if a previous test has failed for this class
if cls_name in _test_failed_incremental:
# retrieve the index of the test (if parametrize is used in
# combination with incremental)
parametrize_index = (
tuple(item.callspec.indices.values())
if hasattr(item, "callspec")
else ()
)
# retrieve the name of the first test function to fail for this
# class name and index
test_name = _test_failed_incremental[cls_name].get(parametrize_index, None)
# if name found, test has failed for the combination of class name &
# test name
if test_name is not None:
pytest.xfail("previous test failed ({})".format(test_name))
@pytest.fixture
def project_runner(runner, halfapicli, halfapi_conf_dir):
with runner.isolated_filesystem():
res = halfapicli('init', PROJNAME)
try:
os.chdir(PROJNAME)
except FileNotFoundError as exc:
subprocess.call('tree')
raise exc
secret = tempfile.mkstemp()
SECRET_PATH = secret[1]
with open(SECRET_PATH, 'w') as f:
f.write(str(uuid1()))
with open(os.path.join(halfapi_conf_dir, PROJNAME), 'w') as halfapi_etc:
PROJ_CONFIG = re.sub('secret = .*', f'secret = {SECRET_PATH}',
format_halfapi_etc(PROJNAME, os.getcwd()))
halfapi_etc.write(PROJ_CONFIG)
###
# add dummy domain
###
2021-05-28 22:30:48 +02:00
create_domain('tests', '.dummy_domain.routers')
###
yield halfapicli
@pytest.fixture
def dummy_app():
app = Starlette()
app.add_route('/',
lambda request, *args, **kwargs: PlainTextResponse('Hello test!'))
app.add_middleware(
AuthenticationMiddleware,
backend=JWTAuthenticationBackend(secret_key='dummysecret')
)
return app
[0.5.3] Squashed commit of the following: commit ac935db6d62656713183707707d083298c1f34b0 Author: Maxime Alves LIRMM@home <maxime.alves@lirmm.fr> Date: Thu Jun 17 18:52:49 2021 +0200 [tests] remove dummy-domain from dependencies commit 4d50363c9b1502d1d8b7cbafc207e80cdbe247a4 Author: Maxime Alves LIRMM@home <maxime.alves@lirmm.fr> Date: Thu Jun 17 18:52:18 2021 +0200 [tests] update tests for 0.5.3 commit 6181592692464d21de9807e1e890b4ac92efc387 Author: Maxime Alves LIRMM@home <maxime.alves@lirmm.fr> Date: Thu Jun 17 18:17:51 2021 +0200 [lib.*] Refactor libs commit ed7485a8a16b60dde8acc0d2b9afca00a75fce3c Author: Maxime Alves LIRMM@home <maxime.alves@lirmm.fr> Date: Thu Jun 17 18:15:10 2021 +0200 [app] Use HalfAPI class to be able to use custom configuration à commit fa1ca6bf9df4ea17d7fa7dfdf3694c56746e9c7f Author: Maxime Alves LIRMM <maxime.alves@lirmm.fr> Date: Wed Jun 16 15:34:25 2021 +0200 [wip] tests dummy_domain commit 86e8dd3465e0bd0f3d49f28fd9e05a52874e969a Author: Maxime Alves LIRMM <maxime.alves@lirmm.fr> Date: Tue Jun 15 18:12:13 2021 +0200 [0.5.3] ajout de la config actuelle dans les arguments des routes commit aa7ec62c7a3b5a0ae0dc0cc79bd509eece44d5ff Author: Maxime Alves LIRMM <maxime.alves@lirmm.fr> Date: Tue Jun 15 11:16:23 2021 +0200 [lib.jwtMw] verify signature even if halfapi is in DEBUG mode commit e208728d7ec61b0de583c66c348f73ac7a884108 Author: Maxime Alves LIRMM <maxime.alves@lirmm.fr> Date: Tue Jun 15 10:49:46 2021 +0200 [lib.acl] args_check doesn't check required/optional arguments if "args" is not specified in request, if the target function is not async commit aa4c309778e4c969fe1314da305e02112d4641b7 Author: Maxime Alves LIRMM <maxime.alves@lirmm.fr> Date: Tue Jun 15 09:45:37 2021 +0200 [lib.domain] SUBROUTER can be a path parameter if including ":" commit 138420461d5c1ba0c7379dcda64e9a38aa5d768d Author: Maxime Alves LIRMM@home <maxime.alves@lirmm.fr> Date: Tue Jun 15 07:24:32 2021 +0200 [gitignore] *.swp commit 0c1e2849bad591d62f7399d820a044cf4e06de12 Author: Maxime Alves LIRMM@home <maxime.alves@lirmm.fr> Date: Tue Jun 15 07:24:14 2021 +0200 [tests] test get route with dummy projects commit 7227e2d7f105516b67f3acac27b242fe01b59215 Author: Maxime Alves LIRMM@home <maxime.alves@lirmm.fr> Date: Mon Jun 14 17:18:47 2021 +0200 [lib.domain] handle modules without ROUTES attribute commit 78c75cd60ead023e7a2d7fa2e5d1059fada0044f Author: Maxime Alves LIRMM@home <maxime.alves@lirmm.fr> Date: Mon Jun 14 16:34:58 2021 +0200 [tests] add dummy_project_router tests for path-based routers (without ROUTES variable)
2021-06-17 18:53:23 +02:00
@pytest.fixture
def dummy_debug_app():
app = Starlette(debug=True)
app.add_route('/',
lambda request, *args, **kwargs: PlainTextResponse('Hello test!'))
app.add_middleware(
AuthenticationMiddleware,
backend=JWTAuthenticationBackend(secret_key='dummysecret')
)
return app
@pytest.fixture
def test_client(dummy_app):
return TestClient(dummy_app)
[0.5.3] Squashed commit of the following: commit ac935db6d62656713183707707d083298c1f34b0 Author: Maxime Alves LIRMM@home <maxime.alves@lirmm.fr> Date: Thu Jun 17 18:52:49 2021 +0200 [tests] remove dummy-domain from dependencies commit 4d50363c9b1502d1d8b7cbafc207e80cdbe247a4 Author: Maxime Alves LIRMM@home <maxime.alves@lirmm.fr> Date: Thu Jun 17 18:52:18 2021 +0200 [tests] update tests for 0.5.3 commit 6181592692464d21de9807e1e890b4ac92efc387 Author: Maxime Alves LIRMM@home <maxime.alves@lirmm.fr> Date: Thu Jun 17 18:17:51 2021 +0200 [lib.*] Refactor libs commit ed7485a8a16b60dde8acc0d2b9afca00a75fce3c Author: Maxime Alves LIRMM@home <maxime.alves@lirmm.fr> Date: Thu Jun 17 18:15:10 2021 +0200 [app] Use HalfAPI class to be able to use custom configuration à commit fa1ca6bf9df4ea17d7fa7dfdf3694c56746e9c7f Author: Maxime Alves LIRMM <maxime.alves@lirmm.fr> Date: Wed Jun 16 15:34:25 2021 +0200 [wip] tests dummy_domain commit 86e8dd3465e0bd0f3d49f28fd9e05a52874e969a Author: Maxime Alves LIRMM <maxime.alves@lirmm.fr> Date: Tue Jun 15 18:12:13 2021 +0200 [0.5.3] ajout de la config actuelle dans les arguments des routes commit aa7ec62c7a3b5a0ae0dc0cc79bd509eece44d5ff Author: Maxime Alves LIRMM <maxime.alves@lirmm.fr> Date: Tue Jun 15 11:16:23 2021 +0200 [lib.jwtMw] verify signature even if halfapi is in DEBUG mode commit e208728d7ec61b0de583c66c348f73ac7a884108 Author: Maxime Alves LIRMM <maxime.alves@lirmm.fr> Date: Tue Jun 15 10:49:46 2021 +0200 [lib.acl] args_check doesn't check required/optional arguments if "args" is not specified in request, if the target function is not async commit aa4c309778e4c969fe1314da305e02112d4641b7 Author: Maxime Alves LIRMM <maxime.alves@lirmm.fr> Date: Tue Jun 15 09:45:37 2021 +0200 [lib.domain] SUBROUTER can be a path parameter if including ":" commit 138420461d5c1ba0c7379dcda64e9a38aa5d768d Author: Maxime Alves LIRMM@home <maxime.alves@lirmm.fr> Date: Tue Jun 15 07:24:32 2021 +0200 [gitignore] *.swp commit 0c1e2849bad591d62f7399d820a044cf4e06de12 Author: Maxime Alves LIRMM@home <maxime.alves@lirmm.fr> Date: Tue Jun 15 07:24:14 2021 +0200 [tests] test get route with dummy projects commit 7227e2d7f105516b67f3acac27b242fe01b59215 Author: Maxime Alves LIRMM@home <maxime.alves@lirmm.fr> Date: Mon Jun 14 17:18:47 2021 +0200 [lib.domain] handle modules without ROUTES attribute commit 78c75cd60ead023e7a2d7fa2e5d1059fada0044f Author: Maxime Alves LIRMM@home <maxime.alves@lirmm.fr> Date: Mon Jun 14 16:34:58 2021 +0200 [tests] add dummy_project_router tests for path-based routers (without ROUTES variable)
2021-06-17 18:53:23 +02:00
@pytest.fixture
def create_route():
def wrapped(domain_path, method, path):
stack = [domain_path, *path.split('/')[1:]]
for i in range(len(stack)):
if len(stack[i]) == 0:
continue
path = os.path.join(*stack[0:i+1])
if os.path.isdir(os.path.join(path)):
continue
os.mkdir(path)
init_path = os.path.join(*stack, '__init__.py')
with open(init_path, 'a+') as f:
f.write(f'\ndef {method}():\n raise NotImplementedError')
return wrapped
@pytest.fixture
def dummy_project():
sys.path.insert(0, './tests')
halfapi_config = tempfile.mktemp()
halfapi_secret = tempfile.mktemp()
domain = 'dummy_domain'
with open(halfapi_config, 'w') as f:
f.writelines([
'[project]\n',
'name = lirmm_api\n',
'halfapi_version = 0.5.0\n',
f'secret = {halfapi_secret}\n',
'port = 3050\n',
'loglevel = debug\n',
'[domains]\n',
f'{domain} = .routers',
f'[{domain}]',
'test = True'
[0.5.3] Squashed commit of the following: commit ac935db6d62656713183707707d083298c1f34b0 Author: Maxime Alves LIRMM@home <maxime.alves@lirmm.fr> Date: Thu Jun 17 18:52:49 2021 +0200 [tests] remove dummy-domain from dependencies commit 4d50363c9b1502d1d8b7cbafc207e80cdbe247a4 Author: Maxime Alves LIRMM@home <maxime.alves@lirmm.fr> Date: Thu Jun 17 18:52:18 2021 +0200 [tests] update tests for 0.5.3 commit 6181592692464d21de9807e1e890b4ac92efc387 Author: Maxime Alves LIRMM@home <maxime.alves@lirmm.fr> Date: Thu Jun 17 18:17:51 2021 +0200 [lib.*] Refactor libs commit ed7485a8a16b60dde8acc0d2b9afca00a75fce3c Author: Maxime Alves LIRMM@home <maxime.alves@lirmm.fr> Date: Thu Jun 17 18:15:10 2021 +0200 [app] Use HalfAPI class to be able to use custom configuration à commit fa1ca6bf9df4ea17d7fa7dfdf3694c56746e9c7f Author: Maxime Alves LIRMM <maxime.alves@lirmm.fr> Date: Wed Jun 16 15:34:25 2021 +0200 [wip] tests dummy_domain commit 86e8dd3465e0bd0f3d49f28fd9e05a52874e969a Author: Maxime Alves LIRMM <maxime.alves@lirmm.fr> Date: Tue Jun 15 18:12:13 2021 +0200 [0.5.3] ajout de la config actuelle dans les arguments des routes commit aa7ec62c7a3b5a0ae0dc0cc79bd509eece44d5ff Author: Maxime Alves LIRMM <maxime.alves@lirmm.fr> Date: Tue Jun 15 11:16:23 2021 +0200 [lib.jwtMw] verify signature even if halfapi is in DEBUG mode commit e208728d7ec61b0de583c66c348f73ac7a884108 Author: Maxime Alves LIRMM <maxime.alves@lirmm.fr> Date: Tue Jun 15 10:49:46 2021 +0200 [lib.acl] args_check doesn't check required/optional arguments if "args" is not specified in request, if the target function is not async commit aa4c309778e4c969fe1314da305e02112d4641b7 Author: Maxime Alves LIRMM <maxime.alves@lirmm.fr> Date: Tue Jun 15 09:45:37 2021 +0200 [lib.domain] SUBROUTER can be a path parameter if including ":" commit 138420461d5c1ba0c7379dcda64e9a38aa5d768d Author: Maxime Alves LIRMM@home <maxime.alves@lirmm.fr> Date: Tue Jun 15 07:24:32 2021 +0200 [gitignore] *.swp commit 0c1e2849bad591d62f7399d820a044cf4e06de12 Author: Maxime Alves LIRMM@home <maxime.alves@lirmm.fr> Date: Tue Jun 15 07:24:14 2021 +0200 [tests] test get route with dummy projects commit 7227e2d7f105516b67f3acac27b242fe01b59215 Author: Maxime Alves LIRMM@home <maxime.alves@lirmm.fr> Date: Mon Jun 14 17:18:47 2021 +0200 [lib.domain] handle modules without ROUTES attribute commit 78c75cd60ead023e7a2d7fa2e5d1059fada0044f Author: Maxime Alves LIRMM@home <maxime.alves@lirmm.fr> Date: Mon Jun 14 16:34:58 2021 +0200 [tests] add dummy_project_router tests for path-based routers (without ROUTES variable)
2021-06-17 18:53:23 +02:00
])
with open(halfapi_secret, 'w') as f:
f.write('turlututu')
return (halfapi_config, 'dummy_domain', 'routers')
@pytest.fixture
def routers():
sys.path.insert(0, './tests')
from dummy_domain import routers
return routers
@pytest.fixture
def application_debug():
from halfapi.app import HalfAPI
return HalfAPI({
'SECRET':'turlututu',
'PRODUCTION':False
}).application
@pytest.fixture
def application_domain(routers):
from halfapi.app import HalfAPI
return HalfAPI({
'SECRET':'turlututu',
'PRODUCTION':True,
'DOMAINS':{'dummy_domain':routers},
'CONFIG':{
'domains': {'dummy_domain':routers},
'domain_config': {'dummy_domain': {'test': True}}
}
[0.5.3] Squashed commit of the following: commit ac935db6d62656713183707707d083298c1f34b0 Author: Maxime Alves LIRMM@home <maxime.alves@lirmm.fr> Date: Thu Jun 17 18:52:49 2021 +0200 [tests] remove dummy-domain from dependencies commit 4d50363c9b1502d1d8b7cbafc207e80cdbe247a4 Author: Maxime Alves LIRMM@home <maxime.alves@lirmm.fr> Date: Thu Jun 17 18:52:18 2021 +0200 [tests] update tests for 0.5.3 commit 6181592692464d21de9807e1e890b4ac92efc387 Author: Maxime Alves LIRMM@home <maxime.alves@lirmm.fr> Date: Thu Jun 17 18:17:51 2021 +0200 [lib.*] Refactor libs commit ed7485a8a16b60dde8acc0d2b9afca00a75fce3c Author: Maxime Alves LIRMM@home <maxime.alves@lirmm.fr> Date: Thu Jun 17 18:15:10 2021 +0200 [app] Use HalfAPI class to be able to use custom configuration à commit fa1ca6bf9df4ea17d7fa7dfdf3694c56746e9c7f Author: Maxime Alves LIRMM <maxime.alves@lirmm.fr> Date: Wed Jun 16 15:34:25 2021 +0200 [wip] tests dummy_domain commit 86e8dd3465e0bd0f3d49f28fd9e05a52874e969a Author: Maxime Alves LIRMM <maxime.alves@lirmm.fr> Date: Tue Jun 15 18:12:13 2021 +0200 [0.5.3] ajout de la config actuelle dans les arguments des routes commit aa7ec62c7a3b5a0ae0dc0cc79bd509eece44d5ff Author: Maxime Alves LIRMM <maxime.alves@lirmm.fr> Date: Tue Jun 15 11:16:23 2021 +0200 [lib.jwtMw] verify signature even if halfapi is in DEBUG mode commit e208728d7ec61b0de583c66c348f73ac7a884108 Author: Maxime Alves LIRMM <maxime.alves@lirmm.fr> Date: Tue Jun 15 10:49:46 2021 +0200 [lib.acl] args_check doesn't check required/optional arguments if "args" is not specified in request, if the target function is not async commit aa4c309778e4c969fe1314da305e02112d4641b7 Author: Maxime Alves LIRMM <maxime.alves@lirmm.fr> Date: Tue Jun 15 09:45:37 2021 +0200 [lib.domain] SUBROUTER can be a path parameter if including ":" commit 138420461d5c1ba0c7379dcda64e9a38aa5d768d Author: Maxime Alves LIRMM@home <maxime.alves@lirmm.fr> Date: Tue Jun 15 07:24:32 2021 +0200 [gitignore] *.swp commit 0c1e2849bad591d62f7399d820a044cf4e06de12 Author: Maxime Alves LIRMM@home <maxime.alves@lirmm.fr> Date: Tue Jun 15 07:24:14 2021 +0200 [tests] test get route with dummy projects commit 7227e2d7f105516b67f3acac27b242fe01b59215 Author: Maxime Alves LIRMM@home <maxime.alves@lirmm.fr> Date: Mon Jun 14 17:18:47 2021 +0200 [lib.domain] handle modules without ROUTES attribute commit 78c75cd60ead023e7a2d7fa2e5d1059fada0044f Author: Maxime Alves LIRMM@home <maxime.alves@lirmm.fr> Date: Mon Jun 14 16:34:58 2021 +0200 [tests] add dummy_project_router tests for path-based routers (without ROUTES variable)
2021-06-17 18:53:23 +02:00
}).application