From 65668dcec05a139f0410ab1b92e4fd0261435870 Mon Sep 17 00:00:00 2001 From: Jens Diemer Date: Sun, 26 Nov 2023 21:01:26 +0100 Subject: [PATCH] Apply manageprojects updates --- doc/ADMIN.md | 49 ++++- for_runners_ynh/cli/dev.py | 111 ++++++------ for_runners_ynh/tests/test_django_project.py | 170 ++++++++++++++++++ ...roject_create_unknown_user_1.snapshot.html | 32 ++++ for_runners_ynh/tests/test_project_setup.py | 144 +++++++++------ for_runners_ynh/tests/test_readme.py | 36 ++++ pyproject.toml | 42 +---- requirements.dev.txt | 48 ++--- 8 files changed, 440 insertions(+), 192 deletions(-) create mode 100644 for_runners_ynh/tests/test_django_project.py create mode 100644 for_runners_ynh/tests/test_django_project_create_unknown_user_1.snapshot.html create mode 100644 for_runners_ynh/tests/test_readme.py 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. +

+
+ +
+
+ + +
+
\ 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 \