diff --git a/doc/ADMIN.md b/doc/ADMIN.md
index a7363cc..78f46de 100644
--- a/doc/ADMIN.md
+++ b/doc/ADMIN.md
@@ -1,7 +1,7 @@
## Settings and upgrades
Almost everything related to django-for-runners's configuration is handled in a `"../conf/settings.py"` file.
-You can edit the file `/home/yunohost.app/django_for_runners/local_settings.py` to enable or disable features.
+You can edit the file `/home/yunohost.app/django_example/local_settings.py` to enable or disable features.
Test sending emails, e.g.:
@@ -12,4 +12,49 @@ root@yunohost:~# /home/yunohost.app/django_for_runners/manage.py sendtestemail -
How to debug a django YunoHost app, take a look into:
-* https://github.com/YunoHost-Apps/django_example_ynh#developer-info
+* https://github.com/YunoHost-Apps/django-for-runners_ynh#developer-info
+
+## local test
+
+For quicker developing of for_runners_ynh in the context of YunoHost app,
+it's possible to run the Django developer server with the settings
+and urls made for YunoHost installation.
+
+e.g.:
+```bash
+~$ git clone https://github.com/YunoHost-Apps/django_example.git
+~$ cd for_runners_ynh/
+~/django_example$ ./dev-cli.py --help
+```
+
+
+The output will looks like:
+
+[comment]: <> (✂✂✂ auto generated help start ✂✂✂)
+```
+Usage: ./dev-cli.py [OPTIONS] COMMAND [ARGS]...
+
+╭─ Options ────────────────────────────────────────────────────────────────────────────────────────╮
+│ --help Show this message and exit. │
+╰──────────────────────────────────────────────────────────────────────────────────────────────────╯
+╭─ Commands ───────────────────────────────────────────────────────────────────────────────────────╮
+│ check-code-style Check code style by calling darker + flake8 │
+│ coverage Run and show coverage. │
+│ diffsettings Run "diffsettings" manage command against a "local_test" YunoHost │
+│ installation. │
+│ fix-code-style Fix code style of all for_runners_ynh source code files via darker │
+│ install Run pip-sync and install 'for_runners_ynh' via pip as editable. │
+│ local-test Build a "local_test" YunoHost installation and start the Django dev. │
+│ server against it. │
+│ mypy Run Mypy (configured in pyproject.toml) │
+│ publish Build and upload this project to PyPi │
+│ safety Run safety check against current requirements files │
+│ test Compile YunoHost files and run Django unittests │
+│ tox Run tox │
+│ update Update "requirements*.txt" dependencies files │
+│ update-test-snapshot-files Update all test snapshot files (by remove and recreate all snapshot │
+│ files) │
+│ version Print version and exit │
+╰──────────────────────────────────────────────────────────────────────────────────────────────────╯
+```
+[comment]: <> (✂✂✂ auto generated help end ✂✂✂)
diff --git a/for_runners_ynh/cli/dev.py b/for_runners_ynh/cli/dev.py
index fbf1d89..1d5cfe2 100644
--- a/for_runners_ynh/cli/dev.py
+++ b/for_runners_ynh/cli/dev.py
@@ -2,14 +2,17 @@
CLI for development
"""
import logging
+import os
import sys
from pathlib import Path
+import django
import rich_click as click
from bx_py_utils.path import assert_is_file
from cli_base.cli_tools.subprocess_utils import verbose_check_call
from cli_base.cli_tools.version_info import print_version
-from django_yunohost_integration.local_test import create_local_test
+from django.core.management.commands.test import Command as DjangoTestCommand
+from django_yunohost_integration.local_test import CreateResults, create_local_test
from manageprojects.utilities import code_style
from manageprojects.utilities.publish import publish_package
from rich import print # noqa; noqa
@@ -42,6 +45,7 @@ ARGUMENT_NOT_EXISTING_DIR = dict(
ARGUMENT_EXISTING_FILE = dict(
type=click.Path(exists=True, file_okay=True, dir_okay=False, readable=True, path_type=Path)
)
+CLI_EPILOG = 'Project Homepage: https://github.com/YunoHost-Apps/django-for-runners_ynh'
class ClickGroup(RichGroup): # FIXME: How to set the "info_name" easier?
@@ -50,10 +54,7 @@ class ClickGroup(RichGroup): # FIXME: How to set the "info_name" easier?
return super().make_context(info_name, *args, **kwargs)
-@click.group(
- cls=ClickGroup,
- epilog='Project Homepage: https://github.com/YunoHost-Apps/django-for-runners_ynh',
-)
+@click.group(cls=ClickGroup, epilog=CLI_EPILOG)
def cli():
pass
@@ -76,7 +77,7 @@ def coverage(verbose: bool = True):
"""
verbose_check_call('coverage', 'run', verbose=verbose, exit_on_error=True)
verbose_check_call('coverage', 'combine', '--append', verbose=verbose, exit_on_error=True)
- verbose_check_call('coverage', 'report', '--fail-under=30', verbose=verbose, exit_on_error=True)
+ verbose_check_call('coverage', 'report', '--fail-under=10', verbose=verbose, exit_on_error=True)
verbose_check_call('coverage', 'xml', verbose=verbose, exit_on_error=True)
verbose_check_call('coverage', 'json', verbose=verbose, exit_on_error=True)
@@ -163,7 +164,10 @@ def publish():
"""
Build and upload this project to PyPi
"""
- _run_unittest_cli(verbose=False, exit_after_run=False) # Don't publish a broken state
+ try:
+ _run_django_test_cli() # Don't publish a broken state
+ except SystemExit as err:
+ assert err.code == 0, f'Exit code is not 0: {err.code}'
publish_package(
module=for_runners_ynh,
@@ -217,64 +221,59 @@ def update_test_snapshot_files():
print(f'{removed_file_count} test snapshot files removed... run tests...')
# Just recreate them by running tests:
- _run_unittest_cli(
- extra_env=dict(
- RAISE_SNAPSHOT_ERRORS='0', # Recreate snapshot files without error
- ),
- verbose=False,
- exit_after_run=False,
- )
-
- new_files = len(list(iter_snapshot_files()))
- print(f'{new_files} test snapshot files created, ok.\n')
+ os.environ['RAISE_SNAPSHOT_ERRORS'] = '0' # Recreate snapshot files without error
+ try:
+ _run_django_test_cli()
+ finally:
+ new_files = len(list(iter_snapshot_files()))
+ print(f'{new_files} test snapshot files created, ok.\n')
cli.add_command(update_test_snapshot_files)
-def _run_unittest_cli(extra_env=None, verbose=True, exit_after_run=True):
+def _run_django_test_cli():
"""
- Call the origin unittest CLI and pass all args to it.
+ Call the origin Django test manage command CLI and pass all args to it.
"""
- if extra_env is None:
- extra_env = dict()
+ os.environ['DJANGO_SETTINGS_MODULE'] = 'settings'
- extra_env.update(
- dict(
- PYTHONUNBUFFERED='1',
- PYTHONWARNINGS='always',
- )
+ print('Compile YunoHost files...')
+ result: CreateResults = create_local_test(
+ django_settings_path=PACKAGE_ROOT / 'conf' / 'settings.py',
+ destination=PACKAGE_ROOT / 'local_test',
+ runserver=False,
+ extra_replacements={
+ '__DEBUG_ENABLED__': '0', # "1" or "0" string
+ '__LOG_LEVEL__': 'INFO',
+ '__ADMIN_EMAIL__': 'foo-bar@test.tld',
+ '__DEFAULT_FROM_EMAIL__': 'django_app@test.tld',
+ },
)
+ print('Local test files created:')
+ print(result)
- args = sys.argv[2:]
- if not args:
- if verbose:
- args = ('--verbose', '--locals', '--buffer')
- else:
- args = ('--locals', '--buffer')
+ data_dir = str(result.data_dir_path)
+ if data_dir not in sys.path:
+ sys.path.insert(0, data_dir)
- verbose_check_call(
- sys.executable,
- '-m',
- 'unittest',
- *args,
- timeout=15 * 60,
- extra_env=extra_env,
- )
- if exit_after_run:
- sys.exit(0)
+ django.setup()
+
+ os.chdir(Path(for_runners_ynh.__file__).parent)
+
+ test_command = DjangoTestCommand()
+ test_command.run_from_argv(sys.argv)
@click.command() # Dummy command
def test():
"""
- Run unittests
+ Compile YunoHost files and run Django unittests
"""
- _run_unittest_cli()
+ _run_django_test_cli()
-# TODO: Replace pytest with normal Django unittests:
-# cli.add_command(test)
+cli.add_command(test)
def _run_tox():
@@ -290,7 +289,7 @@ def tox():
_run_tox()
-# TODO: cli.add_command(tox)
+cli.add_command(tox)
@click.command()
@@ -347,27 +346,19 @@ def diffsettings():
cli.add_command(diffsettings)
-@click.command()
-def pytest():
- """
- Run tests via "pytest"
- """
- verbose_check_call(sys.executable, '-m', 'pytest', *sys.argv[2:], cwd=PACKAGE_ROOT)
-
-
-cli.add_command(pytest)
-
-
def main():
print_version(for_runners_ynh)
+ print(f'{sys.argv=}')
if len(sys.argv) >= 2:
# Check if we just pass a command call
command = sys.argv[1]
if command == 'test':
- _run_unittest_cli()
+ _run_django_test_cli()
+ sys.exit(0)
elif command == 'tox':
_run_tox()
+ sys.exit(0)
- # Execute Click CLI:
+ print('Execute Click CLI')
cli()
diff --git a/for_runners_ynh/tests/test_django_project.py b/for_runners_ynh/tests/test_django_project.py
new file mode 100644
index 0000000..26c1fad
--- /dev/null
+++ b/for_runners_ynh/tests/test_django_project.py
@@ -0,0 +1,170 @@
+from unittest.mock import patch
+
+from axes.models import AccessLog
+from bx_django_utils.test_utils.html_assertion import HtmlAssertionMixin, assert_html_response_snapshot
+from django.conf import LazySettings, settings
+from django.contrib.auth.models import User
+from django.template.defaulttags import CsrfTokenNode
+from django.test import override_settings
+from django.test.testcases import TestCase
+from django.urls.base import reverse
+from django_yunohost_integration.test_utils import generate_basic_auth
+from for_runners import __version__ as upstream_version
+
+
+@override_settings(DEBUG=False)
+class DjangoYnhTestCase(HtmlAssertionMixin, TestCase):
+ def setUp(self):
+ super().setUp()
+
+ # Always start a fresh session:
+ self.client = self.client_class()
+
+ def test_settings(self):
+ assert isinstance(settings, LazySettings)
+ assert settings.configured is True
+
+ assert settings.PATH_URL == 'app_path'
+
+ assert str(settings.DATA_DIR_PATH).endswith('/local_test/opt_yunohost')
+ assert str(settings.INSTALL_DIR_PATH).endswith('/local_test/var_www')
+ assert str(settings.LOG_FILE_PATH).endswith(
+ '/local_test/var_log_django-for-runners.log'
+ ), f'{settings.LOG_FILE_PATH=}'
+
+ assert settings.ROOT_URLCONF == 'urls'
+
+ def test_config_panel_settings(self):
+ # config_panel.toml settings, set via tests.conftest.pytest_configure():
+ assert settings.DEBUG_ENABLED == '0' and settings.DEBUG is False
+ assert settings.LOG_LEVEL == 'INFO'
+ assert settings.ADMIN_EMAIL == 'foo-bar@test.tld'
+ assert settings.DEFAULT_FROM_EMAIL == 'django_app@test.tld'
+
+ def test_auth(self):
+ assert settings.PATH_URL == 'app_path'
+ self.assertEqual(reverse('admin:index'), '/app_path/')
+
+ # SecurityMiddleware should redirects all non-HTTPS requests to HTTPS:
+ assert settings.SECURE_SSL_REDIRECT is True
+ response = self.client.get('/app_path/', secure=False)
+ self.assertRedirects(
+ response,
+ status_code=301, # permanent redirect
+ expected_url='https://testserver/app_path/',
+ fetch_redirect_response=False,
+ )
+
+ response = self.client.get('/app_path/', secure=True)
+ self.assertRedirects(
+ response,
+ expected_url='/app_path/login/?next=%2Fapp_path%2F',
+ fetch_redirect_response=False,
+ )
+
+ def test_create_unknown_user(self):
+ assert User.objects.count() == 0
+
+ self.client.cookies['SSOwAuthUser'] = 'test'
+
+ with patch.object(CsrfTokenNode, 'render', return_value='MockedCsrfTokenNode'):
+ response = self.client.get(
+ path='/app_path/',
+ HTTP_REMOTE_USER='test',
+ HTTP_AUTH_USER='test',
+ HTTP_AUTHORIZATION='basic dGVzdDp0ZXN0MTIz',
+ secure=True,
+ )
+
+ assert User.objects.count() == 1
+ user = User.objects.first()
+ assert user.username == 'test'
+ assert user.is_active is True
+ assert user.is_staff is True # Set by: conf.setup_user.setup_project_user
+ assert user.is_superuser is False
+
+ self.assert_html_parts(
+ response,
+ parts=(
+ f'
Site administration | Django-ForRunners v{upstream_version}',
+ 'test',
+ ),
+ )
+ assert_html_response_snapshot(response, query_selector='#main', validate=False)
+
+ def test_wrong_auth_user(self):
+ assert User.objects.count() == 0
+ assert AccessLog.objects.count() == 0
+
+ self.client.cookies['SSOwAuthUser'] = 'test'
+
+ response = self.client.get(
+ path='/app_path/admin/',
+ HTTP_REMOTE_USER='test',
+ HTTP_AUTH_USER='foobar', # <<< wrong user name
+ HTTP_AUTHORIZATION='basic dGVzdDp0ZXN0MTIz',
+ secure=True,
+ )
+
+ assert User.objects.count() == 1
+ user = User.objects.first()
+ assert user.username == 'test'
+ assert user.is_active is True
+ assert user.is_staff is True # Set by: conf.setup_user.setup_project_user
+ assert user.is_superuser is False
+
+ assert AccessLog.objects.count() == 1
+
+ assert response.status_code == 403 # Forbidden
+
+ def test_wrong_cookie(self):
+ assert User.objects.count() == 0
+ assert AccessLog.objects.count() == 0
+
+ self.client.cookies['SSOwAuthUser'] = 'foobar' # <<< wrong user name
+
+ response = self.client.get(
+ path='/app_path/',
+ HTTP_REMOTE_USER='test',
+ HTTP_AUTH_USER='test',
+ HTTP_AUTHORIZATION='basic dGVzdDp0ZXN0MTIz',
+ secure=True,
+ )
+
+ assert User.objects.count() == 1
+ user = User.objects.first()
+ assert user.username == 'test'
+ assert user.is_active is True
+ assert user.is_staff is True # Set by: conf.setup_user.setup_project_user
+ assert user.is_superuser is False
+
+ assert AccessLog.objects.count() == 1
+
+ assert response.status_code == 403 # Forbidden
+
+ def test_wrong_authorization_user(self):
+ assert User.objects.count() == 0
+
+ self.client.cookies['SSOwAuthUser'] = 'test'
+
+ response = self.client.get(
+ path='/app_path/',
+ HTTP_REMOTE_USER='test',
+ HTTP_AUTH_USER='test',
+ HTTP_AUTHORIZATION=generate_basic_auth(
+ username='foobar', # <<< wrong user name
+ password='test123',
+ ),
+ secure=True,
+ )
+
+ assert User.objects.count() == 1
+ user = User.objects.first()
+ assert user.username == 'test'
+ assert user.is_active is True
+ assert user.is_staff is True # Set by: conf.setup_user.setup_project_user
+ assert user.is_superuser is False
+
+ assert AccessLog.objects.count() == 1
+
+ assert response.status_code == 403 # Forbidden
diff --git a/for_runners_ynh/tests/test_django_project_create_unknown_user_1.snapshot.html b/for_runners_ynh/tests/test_django_project_create_unknown_user_1.snapshot.html
new file mode 100644
index 0000000..2cd5ede
--- /dev/null
+++ b/for_runners_ynh/tests/test_django_project_create_unknown_user_1.snapshot.html
@@ -0,0 +1,32 @@
+
+
+
+
+
+ Site administration
+
+
+
+ You don’t have permission to view or edit anything.
+
+
+
+
+
+ Recent actions
+
+
+ My actions
+
+
+ None available
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/for_runners_ynh/tests/test_project_setup.py b/for_runners_ynh/tests/test_project_setup.py
index 166b2e3..b4c0118 100644
--- a/for_runners_ynh/tests/test_project_setup.py
+++ b/for_runners_ynh/tests/test_project_setup.py
@@ -1,68 +1,104 @@
-import subprocess
-from unittest import TestCase
+import os
-from bx_py_utils.path import assert_is_file
-from manageprojects.test_utils.click_cli_utils import subprocess_cli
-from manageprojects.test_utils.project_setup import check_editor_config, get_py_max_line_length
-from manageprojects.utilities import code_style
-from packaging.version import Version
-
-from for_runners_ynh import __version__
from for_runners_ynh.cli.dev import PACKAGE_ROOT
+try:
+ import tomllib # New in Python 3.11
+except ImportError:
+ import tomli as tomllib
+
+from bx_django_utils.filename import clean_filename
+from bx_py_utils.path import assert_is_dir, assert_is_file
+from django.test.testcases import TestCase
+from django_tools.unittest_utils.project_setup import check_editor_config
+from django_yunohost_integration.test_utils import assert_project_version
+
+from for_runners import __version__ as upstream_version
+from for_runners_ynh import __version__ as ynh_pkg_version
+
+
+def assert_file_contains_string(file_path, string):
+ with file_path.open('r') as f:
+ for line in f:
+ if string in line:
+ return
+ raise AssertionError(f'File {file_path} does not contain {string!r} !')
+
+
class ProjectSetupTestCase(TestCase):
+ @classmethod
+ def setUpClass(cls):
+ super().setUpClass()
+
+ manifest_path = PACKAGE_ROOT / 'manifest.toml'
+ assert_is_file(manifest_path)
+
+ cls.manifest_cfg = tomllib.loads(manifest_path.read_text(encoding='UTF-8'))
+
def test_version(self):
- self.assertIsNotNone(__version__)
+ assert ynh_pkg_version.startswith(
+ upstream_version
+ ), f'{ynh_pkg_version=} does not start with {upstream_version=}'
+ self.assertIn('+ynh', ynh_pkg_version)
- version = Version(__version__) # Will raise InvalidVersion() if wrong formatted
- self.assertEqual(str(version), __version__)
+ # pyproject.toml needs a PEP 440 conform version and used "+ynh"
+ # the YunoHost syntax is: "~ynh", just "convert this:
+ manifest_version = ynh_pkg_version.replace('+', '~')
+ self.assertEqual(self.manifest_cfg['version'], manifest_version)
- dev_cli_bin = PACKAGE_ROOT / 'dev-cli.py'
- assert_is_file(dev_cli_bin)
-
- output = subprocess.check_output([dev_cli_bin, 'version'], text=True)
- self.assertIn(f'for_runners_ynh v{__version__}', output)
-
- def test_code_style(self):
- dev_cli_bin = PACKAGE_ROOT / 'dev-cli.py'
- assert_is_file(dev_cli_bin)
-
- try:
- output = subprocess_cli(
- cli_bin=dev_cli_bin,
- args=('check-code-style',),
- exit_on_error=False,
+ if 'GITHUB_ACTION' not in os.environ:
+ # Github has a rate-limiting... So don't fetch the API if we run as GitHub action
+ assert_project_version(
+ current_version=ynh_pkg_version,
+ github_project_url='https://github.com/jedie/django-for-runners',
)
- except subprocess.CalledProcessError as err:
- self.assertIn('.venv/bin/darker', err.stdout) # darker was called?
- else:
- if 'Code style: OK' in output:
- self.assertIn('.venv/bin/darker', output) # darker was called?
- return # Nothing to fix -> OK
- # Try to "auto" fix code style:
-
- try:
- output = subprocess_cli(
- cli_bin=dev_cli_bin,
- args=('fix-code-style',),
- exit_on_error=False,
- )
- except subprocess.CalledProcessError as err:
- output = err.stdout
-
- self.assertIn('.venv/bin/darker', output) # darker was called?
-
- # Check again and display the output:
-
- try:
- code_style.check(package_root=PACKAGE_ROOT)
- except SystemExit as err:
- self.assertEqual(err.code, 0, 'Code style error, see output above!')
+ def test_screenshot_filenames(self):
+ """
+ https://forum.yunohost.org/t/yunohost-bot-cant-handle-spaces-in-screenshots/19483
+ """
+ screenshot_path = PACKAGE_ROOT / 'doc' / 'screenshots'
+ assert_is_dir(screenshot_path)
+ renamed = []
+ for file_path in screenshot_path.iterdir():
+ file_name = file_path.name
+ if file_name.startswith('.'):
+ continue
+ cleaned_name = clean_filename(file_name)
+ if cleaned_name != file_name:
+ new_path = file_path.with_name(cleaned_name)
+ file_path.rename(new_path)
+ renamed.append(f'{file_name!r} renamed to {cleaned_name!r}')
+ assert not renamed, f'Bad screenshots file names found: {", ".join(renamed)}'
def test_check_editor_config(self):
check_editor_config(package_root=PACKAGE_ROOT)
- max_line_length = get_py_max_line_length(package_root=PACKAGE_ROOT)
- self.assertEqual(max_line_length, 119)
+ def test_manifest_toml(self):
+ self.assertEqual(self.manifest_cfg['packaging_format'], 2)
+ self.assertEqual(
+ set(self.manifest_cfg['install'].keys()),
+ {
+ 'admin',
+ 'admin_email',
+ 'debug_enabled',
+ 'default_from_email',
+ 'domain',
+ 'init_main_permission',
+ 'log_level',
+ 'path',
+ },
+ )
+ self.assertEqual(
+ set(self.manifest_cfg['resources'].keys()),
+ {
+ 'apt',
+ 'data_dir',
+ 'database',
+ 'install_dir',
+ 'permissions',
+ 'ports',
+ 'system_user',
+ },
+ )
diff --git a/for_runners_ynh/tests/test_readme.py b/for_runners_ynh/tests/test_readme.py
new file mode 100644
index 0000000..044abe6
--- /dev/null
+++ b/for_runners_ynh/tests/test_readme.py
@@ -0,0 +1,36 @@
+from pathlib import Path
+
+from bx_py_utils.auto_doc import assert_readme_block
+from manageprojects.test_utils.click_cli_utils import invoke_click
+from manageprojects.tests.base import BaseTestCase
+
+from for_runners_ynh.cli.dev import CLI_EPILOG, PACKAGE_ROOT, cli
+
+
+def assert_cli_help_in_readme(text_block: str, marker: str, readme_path: Path):
+ text_block = text_block.replace(CLI_EPILOG, '')
+ text_block = f'```\n{text_block.strip()}\n```'
+ assert_readme_block(
+ readme_path=readme_path,
+ text_block=text_block,
+ start_marker_line=f'[comment]: <> (✂✂✂ auto generated {marker} start ✂✂✂)',
+ end_marker_line=f'[comment]: <> (✂✂✂ auto generated {marker} end ✂✂✂)',
+ )
+
+
+class ReadmeTestCase(BaseTestCase):
+ def test_main_help(self):
+ stdout = invoke_click(cli, '--help')
+ self.assert_in_content(
+ got=stdout,
+ parts=(
+ 'Usage: ./dev-cli.py [OPTIONS] COMMAND [ARGS]...',
+ ' local-test ',
+ CLI_EPILOG,
+ ),
+ )
+ assert_cli_help_in_readme(
+ text_block=stdout,
+ marker='help',
+ readme_path=PACKAGE_ROOT / 'doc' / 'ADMIN.md',
+ )
diff --git a/pyproject.toml b/pyproject.toml
index 2262df2..57001ad 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -25,14 +25,9 @@ dependencies = [
dev = [
"bx_django_utils", # https://github.com/boxine/bx_django_utils
"beautifulsoup4", # https://pypi.org/project/beautifulsoup4/
- #
- # TODO: Remove "pytest" and use normal unittests ;)
- "pytest",
- "pytest-cov",
- "pytest-django",
- #
"manageprojects>=0.15.0", # https://github.com/jedie/manageprojects
"pip-tools", # https://github.com/jazzband/pip-tools/
+ "tblib", # https://github.com/ionelmc/python-tblib
"tox", # https://github.com/tox-dev/tox
"coverage", # https://github.com/nedbat/coveragepy
"autopep8", # https://github.com/hhatto/autopep8
@@ -56,7 +51,6 @@ dev = [
# to avoid errors like:
# In --require-hashes mode, all requirements must have their versions pinned with ==. These do not: ...
"tomli", # Only needed for Python <3.11
- "exceptiongroup", # needed by pytest
]
[project.urls]
@@ -108,47 +102,18 @@ line_length=119
lines_after_imports=2
-[tool.pytest.ini_options]
-# https://docs.pytest.org/en/latest/customize.html#pyproject-toml
-minversion = "6.0"
-norecursedirs = ".* .git __pycache__ conf local_test coverage* dist htmlcov"
-# sometimes helpfull "addopts" arguments:
-# -vv
-# --verbose
-# --capture=no
-# --trace-config
-# --full-trace
-# -p no:warnings
-addopts = """
- --reuse-db
- --nomigrations
- --cov=.
- --cov-config=pyproject.toml
- --cov-report term-missing
- --cov-report html
- --cov-report xml
- --no-cov-on-fail
- --showlocals
- --doctest-modules
- --failed-first
- --new-first
-"""
-
-
[tool.coverage.run]
branch = true
parallel = true
concurrency = ["multiprocessing"]
source = ['.']
-# TODO: pytest -> Django unitests:
-#command_line = '-m unittest --verbose --locals --buffer'
-command_line = '-m pytest'
+command_line = './dev-cli.py test'
disable_warnings = ["couldnt-parse"]
[tool.coverage.report]
omit = ['.*', '*/tests/*']
skip_empty = true
-fail_under = 30
+fail_under = 10
show_missing = true
exclude_lines = [
'if self.debug:',
@@ -197,6 +162,7 @@ applied_migrations = [
"183124a", # 2023-04-04T12:26:15+02:00
"3383cb0", # 2023-11-09T20:14:05+01:00
"4abd4c0", # 2023-11-25T15:59:31+01:00
+ "2f9fd7b", # 2023-11-26T20:13:32+01:00
]
[manageprojects.cookiecutter_context.cookiecutter]
diff --git a/requirements.dev.txt b/requirements.dev.txt
index 965e0a1..f104fe9 100644
--- a/requirements.dev.txt
+++ b/requirements.dev.txt
@@ -328,7 +328,7 @@ cookiecutter==2.5.0 \
--hash=sha256:8aa2f12ed11bc05628651e9dc4353a10571dd9908aaaaeec959a2b9ea465a5d2 \
--hash=sha256:e61e9034748e3f41b8bd2c11f00d030784b48711c4d5c42363c50989a65331ec
# via manageprojects
-coverage[toml]==7.3.2 \
+coverage==7.3.2 \
--hash=sha256:0cbf38419fb1a347aaf63481c00f0bdc86889d9fbf3f25109cf96c26b403fda1 \
--hash=sha256:12d15ab5833a997716d76f2ac1e4b4d536814fc213c85ca72756c19e5a6b3d63 \
--hash=sha256:149de1d2401ae4655c436a3dced6dd153f4c3309f599c3d4bd97ab172eaf02d9 \
@@ -381,9 +381,7 @@ coverage[toml]==7.3.2 \
--hash=sha256:f94b734214ea6a36fe16e96a70d941af80ff3bfd716c141300d95ebc85339738 \
--hash=sha256:fa28e909776dc69efb6ed975a63691bc8172b64ff357e663a1bb06ff3c9b589a \
--hash=sha256:fe494faa90ce6381770746077243231e0b83ff3f17069d748f645617cefe19d4
- # via
- # for-runners-ynh (pyproject.toml)
- # pytest-cov
+ # via for-runners-ynh (pyproject.toml)
cryptography==41.0.5 \
--hash=sha256:0c327cac00f082013c7c9fb6c46b7cc9fa3c288ca702c74773968173bda421bf \
--hash=sha256:0d2a6a598847c46e3e321a7aef8af1436f11c27f1254933746304ff014664d84 \
@@ -513,12 +511,6 @@ et-xmlfile==1.1.0 \
--hash=sha256:8eb9e2bc2f8c97e37a2dc85a09ecdcdec9d8a396530a6d5a33b30b9a92da0c5c \
--hash=sha256:a2ba85d1d6a74ef63837eed693bcb89c3f752169b0e3e7ae5b16ca5e1b3deada
# via openpyxl
-exceptiongroup==1.2.0 \
- --hash=sha256:4bfd3996ac73b41e9b9628b04e079f193850720ea5945fc96a08633c66912f14 \
- --hash=sha256:91f5c769735f051a4290d52edd0858999b57e5876e9f85937691bd4c9fa3ed68
- # via
- # for-runners-ynh (pyproject.toml)
- # pytest
filelock==3.13.1 \
--hash=sha256:521f5f56c50f8426f5e03ad3b281b490a87ef15bc6c526f168290f0c7148d44e \
--hash=sha256:57dbda9b35157b05fb3e58ee91448612eb674172fab98ee235ccb0b5bee19a1c
@@ -615,10 +607,6 @@ importlib-resources==6.1.1 \
--hash=sha256:3893a00122eafde6894c59914446a512f728a0c1a45f9bb9b63721b6bacf0b4a \
--hash=sha256:e8bf90d8213b486f428c9c39714b920041cb02c184686a3dee24905aaa8105d6
# via django-for-runners
-iniconfig==2.0.0 \
- --hash=sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3 \
- --hash=sha256:b6a85871a79d2e3b22d2d1b94ac2824226a63c6b741c88f7ae975f18b6778374
- # via pytest
isort==5.12.0 \
--hash=sha256:8bef7dde241278824a6d83f44a544709b065191b95b6e50894bdc722fcba0504 \
--hash=sha256:f84c2818376e66cf843d497486ea8fed8700b340f308f076c6fb1229dff318b6
@@ -1069,7 +1057,6 @@ packaging==23.2 \
# gunicorn
# matplotlib
# pyproject-api
- # pytest
# safety
# tox
pathspec==0.11.2 \
@@ -1150,9 +1137,7 @@ platformdirs==4.0.0 \
pluggy==1.3.0 \
--hash=sha256:cf61ae8f126ac6f7c451172cf30e3e43d3ca77615509771b3a984a0730651e12 \
--hash=sha256:d89c696a773f8bd377d18e5ecda92b7a3793cbe66c87060a6fb58c7b6e1061f7
- # via
- # pytest
- # tox
+ # via tox
pprintpp==0.4.0 \
--hash=sha256:b6b4dcdd0c0c0d75e4d7b2f21a9e933e5b2ce62b26e1a54537f9651ae5a5c01d \
--hash=sha256:ea826108e2c7f49dc6d66c752973c3fc9749142a798d6b254e1e301cfdbc6403
@@ -1209,21 +1194,6 @@ pyproject-hooks==1.0.0 \
--hash=sha256:283c11acd6b928d2f6a7c73fa0d01cb2bdc5f07c57a2eeb6e83d5e56b97976f8 \
--hash=sha256:f271b298b97f5955d53fb12b72c1fb1948c22c1a6b70b315c54cedaca0264ef5
# via build
-pytest==7.4.3 \
- --hash=sha256:0d009c083ea859a71b76adf7c1d502e4bc170b80a8ef002da5806527b9591fac \
- --hash=sha256:d989d136982de4e3b29dabcc838ad581c64e8ed52c11fbe86ddebd9da0818cd5
- # via
- # for-runners-ynh (pyproject.toml)
- # pytest-cov
- # pytest-django
-pytest-cov==4.1.0 \
- --hash=sha256:3904b13dfbfec47f003b8e77fd5b589cd11904a21ddf1ab38a64f204d6a10ef6 \
- --hash=sha256:6ba70b9e97e69fcc3fb45bfeab2d0a138fb65c4d0d6a41ef33983ad114be8c3a
- # via for-runners-ynh (pyproject.toml)
-pytest-django==4.7.0 \
- --hash=sha256:4e1c79d5261ade2dd58d91208017cd8f62cb4710b56e012ecd361d15d5d662a2 \
- --hash=sha256:92d6fd46b1d79b54fb6b060bbb39428073396cec717d5f2e122a990d4b6aa5e8
- # via for-runners-ynh (pyproject.toml)
python-dateutil==2.8.2 \
--hash=sha256:0123cacc1627ae19ddf3c27a5de5bd67ee4586fbdd6440d9748f8abb483d3e86 \
--hash=sha256:961d03dc3453ebbc59dbdea9e4e11c5651520a876d0f4db161e8674aae935da9
@@ -1434,6 +1404,10 @@ tablib[html,ods,xls,xlsx,yaml]==3.5.0 \
# via
# django-import-export
# tablib
+tblib==3.0.0 \
+ --hash=sha256:80a6c77e59b55e83911e1e607c649836a69c103963c5f28a46cbeef44acf8129 \
+ --hash=sha256:93622790a0a29e04f0346458face1e144dc4d32f493714c6c3dff82a4adb77e6
+ # via for-runners-ynh (pyproject.toml)
text-unidecode==1.3 \
--hash=sha256:1311f10e8b895935241623731c2ba64f4c455287888b18189350b67134a822e8 \
--hash=sha256:bad6603bb14d279193107714b288be206cac565dfa49aa5b105294dd5c4aab93
@@ -1454,7 +1428,6 @@ tomli==2.0.1 \
# autopep8
# black
# build
- # coverage
# dparse
# flynt
# for-runners-ynh (pyproject.toml)
@@ -1462,7 +1435,6 @@ tomli==2.0.1 \
# pip-tools
# pyproject-api
# pyproject-hooks
- # pytest
# tox
tomlkit==0.12.3 \
--hash=sha256:75baf5012d06501f07bee5bf8e801b9f343e7aac5a92581f20f80ce632e6b5a4 \
@@ -1504,9 +1476,9 @@ webencodings==0.5.1 \
--hash=sha256:a0af1213f3c2226497a97e2b3aa01a7e4bee4f403f95be16fc9acd2947514a78 \
--hash=sha256:b36a1c245f2d304965eb4e0a82848379241dc04b865afcc4aab16748587e1923
# via bleach
-wheel==0.41.3 \
- --hash=sha256:488609bc63a29322326e05560731bf7bfea8e48ad646e1f5e40d366607de0942 \
- --hash=sha256:4d4987ce51a49370ea65c0bfd2234e8ce80a12780820d9dc462597a6e60d0841
+wheel==0.42.0 \
+ --hash=sha256:177f9c9b0d45c47873b619f5b650346d632cdc35fb5e4d25058e09c9e581433d \
+ --hash=sha256:c45be39f7882c9d34243236f2d63cbd58039e360f85d0913425fbd7ceea617a8
# via pip-tools
xlrd==2.0.1 \
--hash=sha256:6a33ee89877bd9abc1158129f6e94be74e2679636b8a205b43b85206c3f0bbdd \