From 86f68f82ee828abb9c80bc817284263a7b04a010 Mon Sep 17 00:00:00 2001 From: Mariagabriela Jaimes Date: Tue, 22 Oct 2024 09:07:34 -0400 Subject: [PATCH] refactor: update integration tests workflow and add pytest configuration (#221) * refactor(ci): update workflow, script, and tests to use latest action version for integration tests * fix: pytest path * fix: install-dev-dependencies target * fix: test-python target * fix: remove self from backend test * chore: add pytest and pytest-django to test requirements add pylint constraint * fix: remove fixtures file * fix: integration tests * fix: include settings file for python unit tests * fix: typo * fix: install dev dependencies * fix: quality * fix: app name * fix: use branch with optional extra requirements * chore(ci): remove integration tests for nightly * fix: quality * fix: set default config file for Pytest * fix: add EOX_TENANT_BASE_URL setting * feat: add type hint to setup class * fix: run integration tests in nightly * fix: upgrade requirements with py3.8 * fix: undo change in coverage configs * fix: run isort for backend tests * refactor: create makefile target to execute integration tests * fix: integration tests target * fix: integration tests target in makefile * fix: improve info view test * chore(ci): use main branch from integration tests action and remove unnecessary input --- .github/workflows/integration-test.yml | 12 ++- Makefile | 6 +- .../tests}/__init__.py | 0 .../tests/integration/__init__.py | 0 .../tests/integration/test_backends.py | 18 +++++ eox_tenant/settings/test.py | 8 ++ eox_tenant/test/integration/__init__.py | 0 eox_tenant/test/integration/test_views.py | 33 ++++++++ eox_tenant/test/tutor/conftest.py | 16 ---- eox_tenant/test/tutor/integration.sh | 13 ---- .../test/tutor/integration_test_tutor.py | 46 ----------- eox_tenant/test/tutor/pytest.ini | 7 -- requirements/base.txt | 38 ++++----- requirements/constraints.txt | 4 + requirements/django42.txt | 2 +- requirements/pip-tools.txt | 16 ++-- requirements/test.in | 2 + requirements/test.txt | 78 ++++++++++++------- requirements/tox.txt | 18 +++-- scripts/execute_integration_tests.sh | 3 + setup.cfg | 3 + 21 files changed, 166 insertions(+), 157 deletions(-) rename eox_tenant/{test/tutor => edxapp_wrapper/tests}/__init__.py (100%) create mode 100644 eox_tenant/edxapp_wrapper/tests/integration/__init__.py create mode 100644 eox_tenant/edxapp_wrapper/tests/integration/test_backends.py create mode 100644 eox_tenant/test/integration/__init__.py create mode 100644 eox_tenant/test/integration/test_views.py delete mode 100644 eox_tenant/test/tutor/conftest.py delete mode 100644 eox_tenant/test/tutor/integration.sh delete mode 100644 eox_tenant/test/tutor/integration_test_tutor.py delete mode 100644 eox_tenant/test/tutor/pytest.ini create mode 100644 scripts/execute_integration_tests.sh diff --git a/.github/workflows/integration-test.yml b/.github/workflows/integration-test.yml index 045d0cd7..43e8d698 100644 --- a/.github/workflows/integration-test.yml +++ b/.github/workflows/integration-test.yml @@ -7,13 +7,11 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - tutor_version: ["<17.0.0", "<18.0.0", "<19.0.0"] + tutor_version: ['<18.0.0', '<19.0.0', 'nightly'] steps: - - uses: actions/checkout@v4 - with: - path: eox-tenant - - uses: eduNEXT/integration-test-in-tutor@main + - name: Run Integration Tests + uses: eduNEXT/integration-test-in-tutor@main with: tutor_version: ${{ matrix.tutor_version }} - app_name: "eox-tenant" - shell_file_to_run: "eox_tenant/test/tutor/integration.sh" + app_name: 'eox-tenant' + openedx_imports_test_file_path: 'eox_tenant/edxapp_wrapper/tests/integration/test_backends.py' diff --git a/Makefile b/Makefile index 4e4b65eb..6f8e913f 100644 --- a/Makefile +++ b/Makefile @@ -41,6 +41,10 @@ upgrade: ## update the requirements/*.txt files with the latest packages satisfy sed '/^[dD]jango==/d;' requirements/test.txt > requirements/test.tmp mv requirements/test.tmp requirements/test.txt +run-integration-tests: + pip install -r requirements/test.txt + pytest -rPf ./eox_tenant/test/integration + quality: clean ## check coding style with pycodestyle and pylint $(TOX) pycodestyle ./eox_tenant $(TOX) pylint ./eox_tenant --rcfile=./setup.cfg @@ -48,7 +52,7 @@ quality: clean ## check coding style with pycodestyle and pylint test-python: clean ## Run test suite. $(TOX) pip install -r requirements/test.txt --exists-action w - $(TOX) coverage run --source ./eox_tenant manage.py test + $(TOX) coverage run --source="." -m pytest ./eox_tenant --ignore-glob='**/integration/*' $(TOX) coverage report -m --fail-under=71 run-tests: test-python quality diff --git a/eox_tenant/test/tutor/__init__.py b/eox_tenant/edxapp_wrapper/tests/__init__.py similarity index 100% rename from eox_tenant/test/tutor/__init__.py rename to eox_tenant/edxapp_wrapper/tests/__init__.py diff --git a/eox_tenant/edxapp_wrapper/tests/integration/__init__.py b/eox_tenant/edxapp_wrapper/tests/integration/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/eox_tenant/edxapp_wrapper/tests/integration/test_backends.py b/eox_tenant/edxapp_wrapper/tests/integration/test_backends.py new file mode 100644 index 00000000..404434eb --- /dev/null +++ b/eox_tenant/edxapp_wrapper/tests/integration/test_backends.py @@ -0,0 +1,18 @@ +""" +This module tests the backends of the edxapp_wrapper +""" + + +# pylint: disable=import-outside-toplevel,unused-import +def test_current_settings_code_imports(): + """ + Running this imports means that our backends import the right signature + """ + import eox_tenant.edxapp_wrapper.backends.bearer_authentication_l_v1 + import eox_tenant.edxapp_wrapper.backends.branding_api_l_v1 + import eox_tenant.edxapp_wrapper.backends.edx_auth_n_v1 + import eox_tenant.edxapp_wrapper.backends.edxmako_l_v1 + import eox_tenant.edxapp_wrapper.backends.oauth_dispatch_j_v1 + import eox_tenant.edxapp_wrapper.backends.site_configuration_module_i_v1 + import eox_tenant.edxapp_wrapper.backends.theming_helpers_h_v1 + import eox_tenant.edxapp_wrapper.backends.users_p_v1 diff --git a/eox_tenant/settings/test.py b/eox_tenant/settings/test.py index 1d3990b0..4466a1dc 100644 --- a/eox_tenant/settings/test.py +++ b/eox_tenant/settings/test.py @@ -99,3 +99,11 @@ def plugin_settings(settings): # pylint: disable=function-redefined with codecs.open(os.environ['LMS_CFG'], encoding='utf-8') as f: env_tokens = yaml.safe_load(f) settings.DATABASES = env_tokens['DATABASES'] + + +# Integration test settings +INTEGRATION_TEST_SETTINGS = { + # Retrieved from the Tutor environment where the integration tests run + "EOX_TENANT_BASE_URL": f"http://{os.environ.get('LMS_HOST', 'local.edly.io')}/eox-tenant", + "API_TIMEOUT": 5, +} diff --git a/eox_tenant/test/integration/__init__.py b/eox_tenant/test/integration/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/eox_tenant/test/integration/test_views.py b/eox_tenant/test/integration/test_views.py new file mode 100644 index 00000000..6092c8ca --- /dev/null +++ b/eox_tenant/test/integration/test_views.py @@ -0,0 +1,33 @@ +""" +Test integration file. +""" +import requests +from django.conf import settings as ds +from django.test import TestCase +from django.urls import reverse +from rest_framework import status + +settings = ds.INTEGRATION_TEST_SETTINGS + + +class TestInfoView(TestCase): + """ + Integration test suite for the info view. + """ + + def test_info_view_success(self) -> None: + """Test the info view. + + Expected result: + - The status code is 200. + - The response contains the version, name and git commit hash. + """ + url = f"{settings['EOX_TENANT_BASE_URL']}{reverse('eox-info')}" + + response = requests.get(url, timeout=settings["API_TIMEOUT"]) + + response_data = response.json() + self.assertEqual(response.status_code, status.HTTP_200_OK) + self.assertIn("version", response_data) + self.assertIn("name", response_data) + self.assertIn("git", response_data) diff --git a/eox_tenant/test/tutor/conftest.py b/eox_tenant/test/tutor/conftest.py deleted file mode 100644 index cf72ca93..00000000 --- a/eox_tenant/test/tutor/conftest.py +++ /dev/null @@ -1,16 +0,0 @@ -""" -The conftest module sets up the database connection for pytest-django. - -The integration tests will reuse the database from tutor local so a noop -django_db_setup is required. -See: https://pytest-django.readthedocs.io/en/latest/database.html -""" - -import pytest # pylint: disable=import-error - - -@pytest.fixture(scope='session') -def django_db_setup(): - """ - Makes the tests reuse the existing database - """ diff --git a/eox_tenant/test/tutor/integration.sh b/eox_tenant/test/tutor/integration.sh deleted file mode 100644 index 71719ad4..00000000 --- a/eox_tenant/test/tutor/integration.sh +++ /dev/null @@ -1,13 +0,0 @@ -#!/bin/bash - -# This script installs the package in the edxapp environment, installs test requirements from Open edX and runs the tests using the Tutor settings. -echo "Install package" -pip install -e /openedx/eox-tenant -python manage.py lms makemigrations -python manage.py lms migrate - -echo "Install test-requirements" -make test-requirements - -echo "Run tests" -pytest -s --ds=lms.envs.tutor.test /openedx/eox-tenant/eox_tenant/test/tutor diff --git a/eox_tenant/test/tutor/integration_test_tutor.py b/eox_tenant/test/tutor/integration_test_tutor.py deleted file mode 100644 index 2f8c4d21..00000000 --- a/eox_tenant/test/tutor/integration_test_tutor.py +++ /dev/null @@ -1,46 +0,0 @@ -""" -Test integration file. -""" -from django.test import TestCase, override_settings - - -@override_settings(ALLOWED_HOSTS=['testserver'], SITE_ID=2) -class TutorIntegrationTestCase(TestCase): - """ - Tests integration with openedx - """ - - def setUp(self): - """ - Set up the base URL for the tests - """ - self.base_url = 'http://local.edly.io' - - # pylint: disable=import-outside-toplevel,unused-import - def test_current_settings_code_imports(self): - """ - Running this imports means that our backends import the right signature - """ - import eox_tenant.edxapp_wrapper.backends.oauth_dispatch_j_v1 # isort:skip - import eox_tenant.edxapp_wrapper.backends.branding_api_l_v1 # isort:skip - import eox_tenant.edxapp_wrapper.backends.site_configuration_module_i_v1 # isort:skip - import eox_tenant.edxapp_wrapper.backends.theming_helpers_h_v1 # isort:skip - import eox_tenant.edxapp_wrapper.backends.users_p_v1 # isort:skip - import eox_tenant.edxapp_wrapper.backends.bearer_authentication_l_v1 # isort:skip - import eox_tenant.edxapp_wrapper.backends.edxmako_l_v1 # isort:skip - import eox_tenant.edxapp_wrapper.backends.edx_auth_n_v1 # isort:skip - - def test_info_view(self): - """ - Tests the info view endpoint in Tutor - """ - info_view_url = f'{self.base_url}/eox-tenant/eox-info' - - response = self.client.get(info_view_url) - - self.assertEqual(response.status_code, 200) - - response_data = response.json() - self.assertIn('version', response_data) - self.assertIn('name', response_data) - self.assertIn('git', response_data) diff --git a/eox_tenant/test/tutor/pytest.ini b/eox_tenant/test/tutor/pytest.ini deleted file mode 100644 index f3b74624..00000000 --- a/eox_tenant/test/tutor/pytest.ini +++ /dev/null @@ -1,7 +0,0 @@ -[pytest] -python_files = integration_test_*.py -filterwarnings = - default - # We ignore every warning while we actually get the testing infrastructure - # running for different version of tutor in gh actions - ignore: diff --git a/requirements/base.txt b/requirements/base.txt index c842eeff..af82044b 100644 --- a/requirements/base.txt +++ b/requirements/base.txt @@ -11,17 +11,17 @@ backports-zoneinfo==0.2.1 ; python_version < "3.9" # -c requirements/constraints.txt # django # djangorestframework -certifi==2024.6.2 +certifi==2024.8.30 # via requests -cffi==1.16.0 +cffi==1.17.1 # via # cryptography # pynacl -charset-normalizer==3.3.2 +charset-normalizer==3.4.0 # via requests click==8.1.7 # via edx-django-utils -cryptography==42.0.8 +cryptography==43.0.1 # via # pyjwt # social-auth-core @@ -29,7 +29,7 @@ defusedxml==0.8.0rc2 # via # python3-openid # social-auth-core -django==4.2.13 +django==4.2.16 # via # -c https://raw.githubusercontent.com/openedx/edx-lint/master/edx_lint/files/common_constraints.txt # -c requirements/constraints.txt @@ -49,7 +49,7 @@ django-crum==0.7.9 # via # -r requirements/base.in # edx-django-utils -django-model-utils==4.5.1 +django-model-utils==5.0.0 # via edx-organizations django-mysql==4.14.0 # via -r requirements/base.in @@ -72,24 +72,24 @@ dnspython==2.6.1 # via pymongo drf-jwt==1.19.2 # via edx-drf-extensions -edx-django-utils==5.14.2 +edx-django-utils==6.0.0 # via edx-drf-extensions -edx-drf-extensions==10.3.0 +edx-drf-extensions==10.4.0 # via # -r requirements/base.in # edx-organizations -edx-opaque-keys[django]==2.10.0 +edx-opaque-keys[django]==2.11.0 # via # -r requirements/base.in # edx-drf-extensions # edx-organizations -edx-organizations==6.13.0 +edx-organizations==7.0.0 # via -r requirements/base.in -idna==3.7 +idna==3.10 # via requests jsonfield==3.1.0 # via -r requirements/base.in -newrelic==9.11.0 +newrelic==10.1.0 # via edx-django-utils oauthlib==3.2.2 # via @@ -97,20 +97,20 @@ oauthlib==3.2.2 # social-auth-core openedx-filters==1.9.0 # via -r requirements/base.in -pbr==6.0.0 +pbr==6.1.0 # via stevedore -pillow==10.3.0 +pillow==10.4.0 # via edx-organizations psutil==6.0.0 # via edx-django-utils pycparser==2.22 # via cffi -pyjwt[crypto]==2.8.0 +pyjwt[crypto]==2.9.0 # via # drf-jwt # edx-drf-extensions # social-auth-core -pymongo==4.7.3 +pymongo==4.10.1 # via edx-opaque-keys pynacl==1.5.0 # via edx-django-utils @@ -129,9 +129,9 @@ six==1.16.0 # via -r requirements/base.in social-auth-core==4.5.4 # via -r requirements/base.in -sqlparse==0.5.0 +sqlparse==0.5.1 # via django -stevedore==5.2.0 +stevedore==5.3.0 # via # edx-django-utils # edx-opaque-keys @@ -139,7 +139,7 @@ typing-extensions==4.12.2 # via # asgiref # edx-opaque-keys -urllib3==2.2.2 +urllib3==2.2.3 # via requests # The following packages are considered to be unsafe in a requirements file: diff --git a/requirements/constraints.txt b/requirements/constraints.txt index c711b6c5..e977c2a1 100644 --- a/requirements/constraints.txt +++ b/requirements/constraints.txt @@ -19,3 +19,7 @@ backports.zoneinfo; python_version<'3.9' # Adding pin to avoid any major upgrade. # 3.15.1 is the latest version that works with Django 3.2 djangorestframework<3.15.2 + +# Versions >= 3.3.0 drop support for Python 3.8 +# Ensures a consistent pylint version across all Python environments to avoid triggering varying warnings +pylint<3.3.0 diff --git a/requirements/django42.txt b/requirements/django42.txt index 092a8ec1..64aaf996 100644 --- a/requirements/django42.txt +++ b/requirements/django42.txt @@ -1 +1 @@ -django==4.2.13 +django==4.2.16 diff --git a/requirements/pip-tools.txt b/requirements/pip-tools.txt index 69e02c90..c9392931 100644 --- a/requirements/pip-tools.txt +++ b/requirements/pip-tools.txt @@ -4,29 +4,27 @@ # # make upgrade # -build==1.2.1 +build==1.2.2.post1 # via pip-tools click==8.1.7 # via pip-tools -importlib-metadata==6.11.0 - # via - # -c https://raw.githubusercontent.com/openedx/edx-lint/master/edx_lint/files/common_constraints.txt - # build +importlib-metadata==8.5.0 + # via build packaging==24.1 # via build pip-tools==7.4.1 # via -r requirements/pip-tools.in -pyproject-hooks==1.1.0 +pyproject-hooks==1.2.0 # via # build # pip-tools -tomli==2.0.1 +tomli==2.0.2 # via # build # pip-tools -wheel==0.43.0 +wheel==0.44.0 # via pip-tools -zipp==3.19.2 +zipp==3.20.2 # via importlib-metadata # The following packages are considered to be unsafe in a requirements file: diff --git a/requirements/test.in b/requirements/test.in index bd60164e..ef9a5b81 100644 --- a/requirements/test.in +++ b/requirements/test.in @@ -5,6 +5,8 @@ django-fake-model ddt pycodestyle pylint +pytest +pytest-django coverage mock testfixtures diff --git a/requirements/test.txt b/requirements/test.txt index e07ac4e1..a6bae6ed 100644 --- a/requirements/test.txt +++ b/requirements/test.txt @@ -8,7 +8,7 @@ asgiref==3.8.1 # via # -r requirements/base.txt # django -astroid==3.2.2 +astroid==3.2.4 # via pylint backports-zoneinfo==0.2.1 ; python_version < "3.9" # via @@ -16,16 +16,16 @@ backports-zoneinfo==0.2.1 ; python_version < "3.9" # -r requirements/base.txt # django # djangorestframework -certifi==2024.6.2 +certifi==2024.8.30 # via # -r requirements/base.txt # requests -cffi==1.16.0 +cffi==1.17.1 # via # -r requirements/base.txt # cryptography # pynacl -charset-normalizer==3.3.2 +charset-normalizer==3.4.0 # via # -r requirements/base.txt # requests @@ -33,9 +33,9 @@ click==8.1.7 # via # -r requirements/base.txt # edx-django-utils -coverage==7.5.3 +coverage==7.6.1 # via -r requirements/test.in -cryptography==42.0.8 +cryptography==43.0.1 # via # -r requirements/base.txt # pyjwt @@ -47,7 +47,7 @@ defusedxml==0.8.0rc2 # -r requirements/base.txt # python3-openid # social-auth-core -dill==0.3.8 +dill==0.3.9 # via pylint # via # -c https://raw.githubusercontent.com/openedx/edx-lint/master/edx_lint/files/common_constraints.txt @@ -70,7 +70,7 @@ django-crum==0.7.9 # edx-django-utils django-fake-model==0.1.4 # via -r requirements/test.in -django-model-utils==4.5.1 +django-model-utils==5.0.0 # via # -r requirements/base.txt # edx-organizations @@ -101,25 +101,29 @@ drf-jwt==1.19.2 # via # -r requirements/base.txt # edx-drf-extensions -edx-django-utils==5.14.2 +edx-django-utils==6.0.0 # via # -r requirements/base.txt # edx-drf-extensions -edx-drf-extensions==10.3.0 +edx-drf-extensions==10.4.0 # via # -r requirements/base.txt # edx-organizations -edx-opaque-keys[django]==2.10.0 +edx-opaque-keys[django]==2.11.0 # via # -r requirements/base.txt # edx-drf-extensions # edx-organizations -edx-organizations==6.13.0 +edx-organizations==7.0.0 # via -r requirements/base.txt -idna==3.7 +exceptiongroup==1.2.2 + # via pytest +idna==3.10 # via # -r requirements/base.txt # requests +iniconfig==2.0.0 + # via pytest isort==5.13.2 # via pylint jsonfield==3.1.0 @@ -128,7 +132,7 @@ mccabe==0.7.0 # via pylint mock==5.1.0 # via -r requirements/test.in -newrelic==9.11.0 +newrelic==10.1.0 # via # -r requirements/base.txt # edx-django-utils @@ -139,39 +143,45 @@ oauthlib==3.2.2 # social-auth-core openedx-filters==1.9.0 # via -r requirements/base.txt -path==16.14.0 +packaging==24.1 + # via pytest +path==17.0.0 # via path-py path-py==12.5.0 # via -r requirements/test.in -pbr==6.0.0 +pbr==6.1.0 # via # -r requirements/base.txt # stevedore -pillow==10.3.0 +pillow==10.4.0 # via # -r requirements/base.txt # edx-organizations -platformdirs==4.2.2 +platformdirs==4.3.6 # via pylint +pluggy==1.5.0 + # via pytest psutil==6.0.0 # via # -r requirements/base.txt # edx-django-utils -pycodestyle==2.12.0 +pycodestyle==2.12.1 # via -r requirements/test.in pycparser==2.22 # via # -r requirements/base.txt # cffi -pyjwt[crypto]==2.8.0 +pyjwt[crypto]==2.9.0 # via # -r requirements/base.txt # drf-jwt # edx-drf-extensions # social-auth-core -pylint==3.2.3 - # via -r requirements/test.in -pymongo==4.7.3 +pylint==3.2.7 + # via + # -c requirements/constraints.txt + # -r requirements/test.in +pymongo==4.10.1 # via # -r requirements/base.txt # edx-opaque-keys @@ -179,11 +189,17 @@ pynacl==1.5.0 # via # -r requirements/base.txt # edx-django-utils +pytest==8.3.3 + # via + # -r requirements/test.in + # pytest-django +pytest-django==4.9.0 + # via -r requirements/test.in python3-openid==3.2.0 # via # -r requirements/base.txt # social-auth-core -pyyaml==6.0.1 +pyyaml==6.0.2 # via -r requirements/test.in requests==2.32.3 # via @@ -203,20 +219,22 @@ six==1.16.0 # via -r requirements/base.txt social-auth-core==4.5.4 # via -r requirements/base.txt -sqlparse==0.5.0 +sqlparse==0.5.1 # via # -r requirements/base.txt # django -stevedore==5.2.0 +stevedore==5.3.0 # via # -r requirements/base.txt # edx-django-utils # edx-opaque-keys testfixtures==8.3.0 # via -r requirements/test.in -tomli==2.0.1 - # via pylint -tomlkit==0.12.5 +tomli==2.0.2 + # via + # pylint + # pytest +tomlkit==0.13.2 # via pylint typing-extensions==4.12.2 # via @@ -225,7 +243,7 @@ typing-extensions==4.12.2 # astroid # edx-opaque-keys # pylint -urllib3==2.2.2 +urllib3==2.2.3 # via # -r requirements/base.txt # requests diff --git a/requirements/tox.txt b/requirements/tox.txt index 4721f999..28360259 100644 --- a/requirements/tox.txt +++ b/requirements/tox.txt @@ -4,15 +4,15 @@ # # make upgrade # -cachetools==5.3.3 +cachetools==5.5.0 # via tox chardet==5.2.0 # via tox colorama==0.4.6 # via tox -distlib==0.3.8 +distlib==0.3.9 # via virtualenv -filelock==3.15.3 +filelock==3.16.1 # via # tox # virtualenv @@ -20,19 +20,21 @@ packaging==24.1 # via # pyproject-api # tox -platformdirs==4.2.2 +platformdirs==4.3.6 # via # tox # virtualenv pluggy==1.5.0 # via tox -pyproject-api==1.6.1 +pyproject-api==1.8.0 # via tox -tomli==2.0.1 +tomli==2.0.2 # via # pyproject-api # tox -tox==4.15.1 +tox==4.21.2 # via -r requirements/tox.in -virtualenv==20.26.2 +typing-extensions==4.12.2 + # via tox +virtualenv==20.26.6 # via tox diff --git a/scripts/execute_integration_tests.sh b/scripts/execute_integration_tests.sh new file mode 100644 index 00000000..c1f256c1 --- /dev/null +++ b/scripts/execute_integration_tests.sh @@ -0,0 +1,3 @@ +#!/bin/bash + +make run-integration-tests diff --git a/setup.cfg b/setup.cfg index 75b003ba..ea59a22a 100644 --- a/setup.cfg +++ b/setup.cfg @@ -5,6 +5,9 @@ tag = False [bumpversion:file:eox_tenant/__init__.py] +[tool:pytest] +DJANGO_SETTINGS_MODULE = eox_tenant.settings.test + [coverage:run] data_file = .coverage omit =