django-pfx 1.7.6.dev35__tar.gz → 1.7.6.dev37__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {django_pfx-1.7.6.dev35 → django_pfx-1.7.6.dev37}/PKG-INFO +1 -1
- {django_pfx-1.7.6.dev35 → django_pfx-1.7.6.dev37}/django_pfx.egg-info/PKG-INFO +1 -1
- {django_pfx-1.7.6.dev35 → django_pfx-1.7.6.dev37}/django_pfx.egg-info/SOURCES.txt +1 -0
- {django_pfx-1.7.6.dev35 → django_pfx-1.7.6.dev37}/django_pfx.egg-info/scm_file_list.json +1 -0
- django_pfx-1.7.6.dev37/django_pfx.egg-info/scm_version.json +8 -0
- {django_pfx-1.7.6.dev35 → django_pfx-1.7.6.dev37}/pfx/pfxcore/test.py +76 -2
- {django_pfx-1.7.6.dev35 → django_pfx-1.7.6.dev37}/requirements.txt +2 -0
- django_pfx-1.7.6.dev37/tests/tests/parallel_scenarios.py +89 -0
- django_pfx-1.7.6.dev37/tests/tests/test_test_runner.py +234 -0
- django_pfx-1.7.6.dev35/django_pfx.egg-info/scm_version.json +0 -8
- django_pfx-1.7.6.dev35/tests/tests/test_test_runner.py +0 -117
- {django_pfx-1.7.6.dev35 → django_pfx-1.7.6.dev37}/.gitignore +0 -0
- {django_pfx-1.7.6.dev35 → django_pfx-1.7.6.dev37}/.gitlab-ci.yml +0 -0
- {django_pfx-1.7.6.dev35 → django_pfx-1.7.6.dev37}/.pre-commit-config.yaml +0 -0
- {django_pfx-1.7.6.dev35 → django_pfx-1.7.6.dev37}/LICENSE +0 -0
- {django_pfx-1.7.6.dev35 → django_pfx-1.7.6.dev37}/MANIFEST.in +0 -0
- {django_pfx-1.7.6.dev35 → django_pfx-1.7.6.dev37}/README.md +0 -0
- {django_pfx-1.7.6.dev35 → django_pfx-1.7.6.dev37}/django_pfx.egg-info/dependency_links.txt +0 -0
- {django_pfx-1.7.6.dev35 → django_pfx-1.7.6.dev37}/django_pfx.egg-info/requires.txt +0 -0
- {django_pfx-1.7.6.dev35 → django_pfx-1.7.6.dev37}/django_pfx.egg-info/top_level.txt +0 -0
- {django_pfx-1.7.6.dev35 → django_pfx-1.7.6.dev37}/doc/Makefile +0 -0
- {django_pfx-1.7.6.dev35 → django_pfx-1.7.6.dev37}/doc/conf.py +0 -0
- {django_pfx-1.7.6.dev35 → django_pfx-1.7.6.dev37}/doc/index.rst +0 -0
- {django_pfx-1.7.6.dev35 → django_pfx-1.7.6.dev37}/doc/source/api.views.rst +0 -0
- {django_pfx-1.7.6.dev35 → django_pfx-1.7.6.dev37}/doc/source/authentication.md +0 -0
- {django_pfx-1.7.6.dev35 → django_pfx-1.7.6.dev37}/doc/source/decorator.md +0 -0
- {django_pfx-1.7.6.dev35 → django_pfx-1.7.6.dev37}/doc/source/generate_openapi.md +0 -0
- {django_pfx-1.7.6.dev35 → django_pfx-1.7.6.dev37}/doc/source/getting_started.md +0 -0
- {django_pfx-1.7.6.dev35 → django_pfx-1.7.6.dev37}/doc/source/internationalisation.md +0 -0
- {django_pfx-1.7.6.dev35 → django_pfx-1.7.6.dev37}/doc/source/model.md +0 -0
- {django_pfx-1.7.6.dev35 → django_pfx-1.7.6.dev37}/doc/source/pfx_views.md +0 -0
- {django_pfx-1.7.6.dev35 → django_pfx-1.7.6.dev37}/doc/source/profiling.md +0 -0
- {django_pfx-1.7.6.dev35 → django_pfx-1.7.6.dev37}/doc/source/settings.md +0 -0
- {django_pfx-1.7.6.dev35 → django_pfx-1.7.6.dev37}/doc/source/testing.md +0 -0
- {django_pfx-1.7.6.dev35 → django_pfx-1.7.6.dev37}/doc/source/user_jobs.md +0 -0
- {django_pfx-1.7.6.dev35 → django_pfx-1.7.6.dev37}/img/pfx.png +0 -0
- {django_pfx-1.7.6.dev35 → django_pfx-1.7.6.dev37}/img/pfx.svg +0 -0
- {django_pfx-1.7.6.dev35 → django_pfx-1.7.6.dev37}/make_messages +0 -0
- {django_pfx-1.7.6.dev35 → django_pfx-1.7.6.dev37}/manage.py +0 -0
- {django_pfx-1.7.6.dev35 → django_pfx-1.7.6.dev37}/pfx/__init__.py +0 -0
- {django_pfx-1.7.6.dev35 → django_pfx-1.7.6.dev37}/pfx/emails/__init__.py +0 -0
- {django_pfx-1.7.6.dev35 → django_pfx-1.7.6.dev37}/pfx/emails/apps.py +0 -0
- {django_pfx-1.7.6.dev35 → django_pfx-1.7.6.dev37}/pfx/emails/email_backend/__init__.py +0 -0
- {django_pfx-1.7.6.dev35 → django_pfx-1.7.6.dev37}/pfx/emails/email_backend/memory_email_backend.py +0 -0
- {django_pfx-1.7.6.dev35 → django_pfx-1.7.6.dev37}/pfx/emails/email_backend/q_mailjet_email_backend.py +0 -0
- {django_pfx-1.7.6.dev35 → django_pfx-1.7.6.dev37}/pfx/emails/exceptions.py +0 -0
- {django_pfx-1.7.6.dev35 → django_pfx-1.7.6.dev37}/pfx/emails/locale/fr/LC_MESSAGES/django.mo +0 -0
- {django_pfx-1.7.6.dev35 → django_pfx-1.7.6.dev37}/pfx/emails/locale/fr/LC_MESSAGES/django.po +0 -0
- {django_pfx-1.7.6.dev35 → django_pfx-1.7.6.dev37}/pfx/emails/mailjet/__init__.py +0 -0
- {django_pfx-1.7.6.dev35 → django_pfx-1.7.6.dev37}/pfx/emails/mailjet/mailjet.py +0 -0
- {django_pfx-1.7.6.dev35 → django_pfx-1.7.6.dev37}/pfx/emails/mailjet/message_result.py +0 -0
- {django_pfx-1.7.6.dev35 → django_pfx-1.7.6.dev37}/pfx/emails/migrations/0001_initial.py +0 -0
- {django_pfx-1.7.6.dev35 → django_pfx-1.7.6.dev37}/pfx/emails/migrations/0002_alter_email_headers.py +0 -0
- {django_pfx-1.7.6.dev35 → django_pfx-1.7.6.dev37}/pfx/emails/migrations/0003_alter_email_options.py +0 -0
- {django_pfx-1.7.6.dev35 → django_pfx-1.7.6.dev37}/pfx/emails/migrations/0004_alter_email_mail_from.py +0 -0
- {django_pfx-1.7.6.dev35 → django_pfx-1.7.6.dev37}/pfx/emails/migrations/0005_lowercase_verbose_names.py +0 -0
- {django_pfx-1.7.6.dev35 → django_pfx-1.7.6.dev37}/pfx/emails/migrations/0006_alter_email_attachments.py +0 -0
- {django_pfx-1.7.6.dev35 → django_pfx-1.7.6.dev37}/pfx/emails/migrations/__init__.py +0 -0
- {django_pfx-1.7.6.dev35 → django_pfx-1.7.6.dev37}/pfx/emails/models/__init__.py +0 -0
- {django_pfx-1.7.6.dev35 → django_pfx-1.7.6.dev37}/pfx/emails/models/email.py +0 -0
- {django_pfx-1.7.6.dev35 → django_pfx-1.7.6.dev37}/pfx/emails/providers/__init__.py +0 -0
- {django_pfx-1.7.6.dev35 → django_pfx-1.7.6.dev37}/pfx/emails/providers/email_django_q_executor.py +0 -0
- {django_pfx-1.7.6.dev35 → django_pfx-1.7.6.dev37}/pfx/emails/shortcuts.py +0 -0
- {django_pfx-1.7.6.dev35 → django_pfx-1.7.6.dev37}/pfx/emails/tasks.py +0 -0
- {django_pfx-1.7.6.dev35 → django_pfx-1.7.6.dev37}/pfx/emails/templates/emails/base_template.html +0 -0
- {django_pfx-1.7.6.dev35 → django_pfx-1.7.6.dev37}/pfx/emails/templates/emails/base_template.txt +0 -0
- {django_pfx-1.7.6.dev35 → django_pfx-1.7.6.dev37}/pfx/emails/templates/emails/base_template_subject.txt +0 -0
- {django_pfx-1.7.6.dev35 → django_pfx-1.7.6.dev37}/pfx/emails/test.py +0 -0
- {django_pfx-1.7.6.dev35 → django_pfx-1.7.6.dev37}/pfx/emails/urls.py +0 -0
- {django_pfx-1.7.6.dev35 → django_pfx-1.7.6.dev37}/pfx/emails/views/__init__.py +0 -0
- {django_pfx-1.7.6.dev35 → django_pfx-1.7.6.dev37}/pfx/emails/views/email_rest_view.py +0 -0
- {django_pfx-1.7.6.dev35 → django_pfx-1.7.6.dev37}/pfx/pfxcore/__init__.py +0 -0
- {django_pfx-1.7.6.dev35 → django_pfx-1.7.6.dev37}/pfx/pfxcore/apidoc/__init__.py +0 -0
- {django_pfx-1.7.6.dev35 → django_pfx-1.7.6.dev37}/pfx/pfxcore/apidoc/parameters.py +0 -0
- {django_pfx-1.7.6.dev35 → django_pfx-1.7.6.dev37}/pfx/pfxcore/apidoc/schema.py +0 -0
- {django_pfx-1.7.6.dev35 → django_pfx-1.7.6.dev37}/pfx/pfxcore/apidoc/tags.py +0 -0
- {django_pfx-1.7.6.dev35 → django_pfx-1.7.6.dev37}/pfx/pfxcore/apps.py +0 -0
- {django_pfx-1.7.6.dev35 → django_pfx-1.7.6.dev37}/pfx/pfxcore/decorator/__init__.py +0 -0
- {django_pfx-1.7.6.dev35 → django_pfx-1.7.6.dev37}/pfx/pfxcore/decorator/rest.py +0 -0
- {django_pfx-1.7.6.dev35 → django_pfx-1.7.6.dev37}/pfx/pfxcore/default_settings.py +0 -0
- {django_pfx-1.7.6.dev35 → django_pfx-1.7.6.dev37}/pfx/pfxcore/exceptions.py +0 -0
- {django_pfx-1.7.6.dev35 → django_pfx-1.7.6.dev37}/pfx/pfxcore/fields/__init__.py +0 -0
- {django_pfx-1.7.6.dev35 → django_pfx-1.7.6.dev37}/pfx/pfxcore/fields/decimal_field.py +0 -0
- {django_pfx-1.7.6.dev35 → django_pfx-1.7.6.dev37}/pfx/pfxcore/fields/media_field.py +0 -0
- {django_pfx-1.7.6.dev35 → django_pfx-1.7.6.dev37}/pfx/pfxcore/fields/minutes_duration_field.py +0 -0
- {django_pfx-1.7.6.dev35 → django_pfx-1.7.6.dev37}/pfx/pfxcore/fields/rich_text_field.py +0 -0
- {django_pfx-1.7.6.dev35 → django_pfx-1.7.6.dev37}/pfx/pfxcore/http/__init__.py +0 -0
- {django_pfx-1.7.6.dev35 → django_pfx-1.7.6.dev37}/pfx/pfxcore/http/json_response.py +0 -0
- {django_pfx-1.7.6.dev35 → django_pfx-1.7.6.dev37}/pfx/pfxcore/locale/fr/LC_MESSAGES/django.mo +0 -0
- {django_pfx-1.7.6.dev35 → django_pfx-1.7.6.dev37}/pfx/pfxcore/locale/fr/LC_MESSAGES/django.po +0 -0
- {django_pfx-1.7.6.dev35 → django_pfx-1.7.6.dev37}/pfx/pfxcore/management/__init__.py +0 -0
- {django_pfx-1.7.6.dev35 → django_pfx-1.7.6.dev37}/pfx/pfxcore/management/commands/__init__.py +0 -0
- {django_pfx-1.7.6.dev35 → django_pfx-1.7.6.dev37}/pfx/pfxcore/management/commands/makeapidoc.py +0 -0
- {django_pfx-1.7.6.dev35 → django_pfx-1.7.6.dev37}/pfx/pfxcore/management/commands/profile.py +0 -0
- {django_pfx-1.7.6.dev35 → django_pfx-1.7.6.dev37}/pfx/pfxcore/mfa/__init__.py +0 -0
- {django_pfx-1.7.6.dev35 → django_pfx-1.7.6.dev37}/pfx/pfxcore/mfa/authenticator.py +0 -0
- {django_pfx-1.7.6.dev35 → django_pfx-1.7.6.dev37}/pfx/pfxcore/mfa/base.py +0 -0
- {django_pfx-1.7.6.dev35 → django_pfx-1.7.6.dev37}/pfx/pfxcore/mfa/email.py +0 -0
- {django_pfx-1.7.6.dev35 → django_pfx-1.7.6.dev37}/pfx/pfxcore/mfa/legacy.py +0 -0
- {django_pfx-1.7.6.dev35 → django_pfx-1.7.6.dev37}/pfx/pfxcore/mfa/sms.py +0 -0
- {django_pfx-1.7.6.dev35 → django_pfx-1.7.6.dev37}/pfx/pfxcore/middleware/__init__.py +0 -0
- {django_pfx-1.7.6.dev35 → django_pfx-1.7.6.dev37}/pfx/pfxcore/middleware/authentication.py +0 -0
- {django_pfx-1.7.6.dev35 → django_pfx-1.7.6.dev37}/pfx/pfxcore/middleware/locale.py +0 -0
- {django_pfx-1.7.6.dev35 → django_pfx-1.7.6.dev37}/pfx/pfxcore/middleware/profiling.py +0 -0
- {django_pfx-1.7.6.dev35 → django_pfx-1.7.6.dev37}/pfx/pfxcore/middleware/request_too_big_middleware.py +0 -0
- {django_pfx-1.7.6.dev35 → django_pfx-1.7.6.dev37}/pfx/pfxcore/middleware/ws_cookie_authentication_middleware.py +0 -0
- {django_pfx-1.7.6.dev35 → django_pfx-1.7.6.dev37}/pfx/pfxcore/migrations/0001_initial.py +0 -0
- {django_pfx-1.7.6.dev35 → django_pfx-1.7.6.dev37}/pfx/pfxcore/migrations/0002_pfxpermissionsuser.py +0 -0
- {django_pfx-1.7.6.dev35 → django_pfx-1.7.6.dev37}/pfx/pfxcore/migrations/0003_delete_pfxpermissionsuser.py +0 -0
- {django_pfx-1.7.6.dev35 → django_pfx-1.7.6.dev37}/pfx/pfxcore/migrations/0004_alter_loginban_failed_counter_and_more.py +0 -0
- {django_pfx-1.7.6.dev35 → django_pfx-1.7.6.dev37}/pfx/pfxcore/migrations/0005_mfadevice_mfadevice_mfa_device_unique_backend.py +0 -0
- {django_pfx-1.7.6.dev35 → django_pfx-1.7.6.dev37}/pfx/pfxcore/migrations/0006_alter_pfxuser_managers.py +0 -0
- {django_pfx-1.7.6.dev35 → django_pfx-1.7.6.dev37}/pfx/pfxcore/migrations/0007_alter_pfxuser_options.py +0 -0
- {django_pfx-1.7.6.dev35 → django_pfx-1.7.6.dev37}/pfx/pfxcore/migrations/0008_lowercase_verbose_names.py +0 -0
- {django_pfx-1.7.6.dev35 → django_pfx-1.7.6.dev37}/pfx/pfxcore/migrations/__init__.py +0 -0
- {django_pfx-1.7.6.dev35 → django_pfx-1.7.6.dev37}/pfx/pfxcore/migrations/operations/__init__.py +0 -0
- {django_pfx-1.7.6.dev35 → django_pfx-1.7.6.dev37}/pfx/pfxcore/migrations/operations/permissions.py +0 -0
- {django_pfx-1.7.6.dev35 → django_pfx-1.7.6.dev37}/pfx/pfxcore/models/__init__.py +0 -0
- {django_pfx-1.7.6.dev35 → django_pfx-1.7.6.dev37}/pfx/pfxcore/models/abstract_pfx_base_user.py +0 -0
- {django_pfx-1.7.6.dev35 → django_pfx-1.7.6.dev37}/pfx/pfxcore/models/attachment_mixin.py +0 -0
- {django_pfx-1.7.6.dev35 → django_pfx-1.7.6.dev37}/pfx/pfxcore/models/cache_mixins.py +0 -0
- {django_pfx-1.7.6.dev35 → django_pfx-1.7.6.dev37}/pfx/pfxcore/models/login_ban.py +0 -0
- {django_pfx-1.7.6.dev35 → django_pfx-1.7.6.dev37}/pfx/pfxcore/models/mfa_device.py +0 -0
- {django_pfx-1.7.6.dev35 → django_pfx-1.7.6.dev37}/pfx/pfxcore/models/mfa_user_mixin.py +0 -0
- {django_pfx-1.7.6.dev35 → django_pfx-1.7.6.dev37}/pfx/pfxcore/models/not_null_fields.py +0 -0
- {django_pfx-1.7.6.dev35 → django_pfx-1.7.6.dev37}/pfx/pfxcore/models/ordered_model_mixin.py +0 -0
- {django_pfx-1.7.6.dev35 → django_pfx-1.7.6.dev37}/pfx/pfxcore/models/pfx_models.py +0 -0
- {django_pfx-1.7.6.dev35 → django_pfx-1.7.6.dev37}/pfx/pfxcore/models/pfx_user.py +0 -0
- {django_pfx-1.7.6.dev35 → django_pfx-1.7.6.dev37}/pfx/pfxcore/models/user_filtered_queryset_mixin.py +0 -0
- {django_pfx-1.7.6.dev35 → django_pfx-1.7.6.dev37}/pfx/pfxcore/serializers/__init__.py +0 -0
- {django_pfx-1.7.6.dev35 → django_pfx-1.7.6.dev37}/pfx/pfxcore/serializers/json.py +0 -0
- {django_pfx-1.7.6.dev35 → django_pfx-1.7.6.dev37}/pfx/pfxcore/settings.py +0 -0
- {django_pfx-1.7.6.dev35 → django_pfx-1.7.6.dev37}/pfx/pfxcore/shortcuts.py +0 -0
- {django_pfx-1.7.6.dev35 → django_pfx-1.7.6.dev37}/pfx/pfxcore/sms/__init__.py +0 -0
- {django_pfx-1.7.6.dev35 → django_pfx-1.7.6.dev37}/pfx/pfxcore/sms/backends/__init__.py +0 -0
- {django_pfx-1.7.6.dev35 → django_pfx-1.7.6.dev37}/pfx/pfxcore/sms/backends/base.py +0 -0
- {django_pfx-1.7.6.dev35 → django_pfx-1.7.6.dev37}/pfx/pfxcore/sms/backends/console.py +0 -0
- {django_pfx-1.7.6.dev35 → django_pfx-1.7.6.dev37}/pfx/pfxcore/sms/backends/memory.py +0 -0
- {django_pfx-1.7.6.dev35 → django_pfx-1.7.6.dev37}/pfx/pfxcore/storage/__init__.py +0 -0
- {django_pfx-1.7.6.dev35 → django_pfx-1.7.6.dev37}/pfx/pfxcore/storage/exceptions.py +0 -0
- {django_pfx-1.7.6.dev35 → django_pfx-1.7.6.dev37}/pfx/pfxcore/storage/local_storage.py +0 -0
- {django_pfx-1.7.6.dev35 → django_pfx-1.7.6.dev37}/pfx/pfxcore/storage/s3_storage.py +0 -0
- {django_pfx-1.7.6.dev35 → django_pfx-1.7.6.dev37}/pfx/pfxcore/templates/registration/otp_code_email.txt +0 -0
- {django_pfx-1.7.6.dev35 → django_pfx-1.7.6.dev37}/pfx/pfxcore/templates/registration/otp_code_subject.txt +0 -0
- {django_pfx-1.7.6.dev35 → django_pfx-1.7.6.dev37}/pfx/pfxcore/templates/registration/password_reset_email.txt +0 -0
- {django_pfx-1.7.6.dev35 → django_pfx-1.7.6.dev37}/pfx/pfxcore/templates/registration/password_reset_subject.txt +0 -0
- {django_pfx-1.7.6.dev35 → django_pfx-1.7.6.dev37}/pfx/pfxcore/templates/registration/welcome_email.txt +0 -0
- {django_pfx-1.7.6.dev35 → django_pfx-1.7.6.dev37}/pfx/pfxcore/templates/registration/welcome_subject.txt +0 -0
- {django_pfx-1.7.6.dev35 → django_pfx-1.7.6.dev37}/pfx/pfxcore/urls.py +0 -0
- {django_pfx-1.7.6.dev35 → django_pfx-1.7.6.dev37}/pfx/pfxcore/version_check.py +0 -0
- {django_pfx-1.7.6.dev35 → django_pfx-1.7.6.dev37}/pfx/pfxcore/views/__init__.py +0 -0
- {django_pfx-1.7.6.dev35 → django_pfx-1.7.6.dev37}/pfx/pfxcore/views/authentication_views.py +0 -0
- {django_pfx-1.7.6.dev35 → django_pfx-1.7.6.dev37}/pfx/pfxcore/views/fields.py +0 -0
- {django_pfx-1.7.6.dev35 → django_pfx-1.7.6.dev37}/pfx/pfxcore/views/filters_views.py +0 -0
- {django_pfx-1.7.6.dev35 → django_pfx-1.7.6.dev37}/pfx/pfxcore/views/locale_views.py +0 -0
- {django_pfx-1.7.6.dev35 → django_pfx-1.7.6.dev37}/pfx/pfxcore/views/media_rest_view_mixin.py +0 -0
- {django_pfx-1.7.6.dev35 → django_pfx-1.7.6.dev37}/pfx/pfxcore/views/mfa_authentication_views.py +0 -0
- {django_pfx-1.7.6.dev35 → django_pfx-1.7.6.dev37}/pfx/pfxcore/views/ordered_rest_view_mixin.py +0 -0
- {django_pfx-1.7.6.dev35 → django_pfx-1.7.6.dev37}/pfx/pfxcore/views/parameters/__init__.py +0 -0
- {django_pfx-1.7.6.dev35 → django_pfx-1.7.6.dev37}/pfx/pfxcore/views/parameters/date_format.py +0 -0
- {django_pfx-1.7.6.dev35 → django_pfx-1.7.6.dev37}/pfx/pfxcore/views/parameters/groups.py +0 -0
- {django_pfx-1.7.6.dev35 → django_pfx-1.7.6.dev37}/pfx/pfxcore/views/parameters/list_count.py +0 -0
- {django_pfx-1.7.6.dev35 → django_pfx-1.7.6.dev37}/pfx/pfxcore/views/parameters/list_items.py +0 -0
- {django_pfx-1.7.6.dev35 → django_pfx-1.7.6.dev37}/pfx/pfxcore/views/parameters/list_mode.py +0 -0
- {django_pfx-1.7.6.dev35 → django_pfx-1.7.6.dev37}/pfx/pfxcore/views/parameters/list_order.py +0 -0
- {django_pfx-1.7.6.dev35 → django_pfx-1.7.6.dev37}/pfx/pfxcore/views/parameters/list_search.py +0 -0
- {django_pfx-1.7.6.dev35 → django_pfx-1.7.6.dev37}/pfx/pfxcore/views/parameters/media_redirect.py +0 -0
- {django_pfx-1.7.6.dev35 → django_pfx-1.7.6.dev37}/pfx/pfxcore/views/parameters/meta_fields.py +0 -0
- {django_pfx-1.7.6.dev35 → django_pfx-1.7.6.dev37}/pfx/pfxcore/views/parameters/meta_filters.py +0 -0
- {django_pfx-1.7.6.dev35 → django_pfx-1.7.6.dev37}/pfx/pfxcore/views/parameters/meta_orders.py +0 -0
- {django_pfx-1.7.6.dev35 → django_pfx-1.7.6.dev37}/pfx/pfxcore/views/parameters/subset.py +0 -0
- {django_pfx-1.7.6.dev35 → django_pfx-1.7.6.dev37}/pfx/pfxcore/views/parameters/subset_limit.py +0 -0
- {django_pfx-1.7.6.dev35 → django_pfx-1.7.6.dev37}/pfx/pfxcore/views/parameters/subset_offset.py +0 -0
- {django_pfx-1.7.6.dev35 → django_pfx-1.7.6.dev37}/pfx/pfxcore/views/parameters/subset_page.py +0 -0
- {django_pfx-1.7.6.dev35 → django_pfx-1.7.6.dev37}/pfx/pfxcore/views/parameters/subset_page_size.py +0 -0
- {django_pfx-1.7.6.dev35 → django_pfx-1.7.6.dev37}/pfx/pfxcore/views/parameters/subset_page_subset.py +0 -0
- {django_pfx-1.7.6.dev35 → django_pfx-1.7.6.dev37}/pfx/pfxcore/views/rest_views.py +0 -0
- {django_pfx-1.7.6.dev35 → django_pfx-1.7.6.dev37}/pfx/pfxcore/xml_test_runner.py +0 -0
- {django_pfx-1.7.6.dev35 → django_pfx-1.7.6.dev37}/pfx/settings/__init__.py +0 -0
- {django_pfx-1.7.6.dev35 → django_pfx-1.7.6.dev37}/pfx/settings/dev.py +0 -0
- {django_pfx-1.7.6.dev35 → django_pfx-1.7.6.dev37}/pfx/userjobs/__init__.py +0 -0
- {django_pfx-1.7.6.dev35 → django_pfx-1.7.6.dev37}/pfx/userjobs/apps.py +0 -0
- {django_pfx-1.7.6.dev35 → django_pfx-1.7.6.dev37}/pfx/userjobs/locale/fr/LC_MESSAGES/django.mo +0 -0
- {django_pfx-1.7.6.dev35 → django_pfx-1.7.6.dev37}/pfx/userjobs/locale/fr/LC_MESSAGES/django.po +0 -0
- {django_pfx-1.7.6.dev35 → django_pfx-1.7.6.dev37}/pfx/userjobs/migrations/0001_initial.py +0 -0
- {django_pfx-1.7.6.dev35 → django_pfx-1.7.6.dev37}/pfx/userjobs/migrations/0002_alter_userjob_options_alter_userjob_auto_queued_and_more.py +0 -0
- {django_pfx-1.7.6.dev35 → django_pfx-1.7.6.dev37}/pfx/userjobs/migrations/0003_clean_scheduled_tasks.py +0 -0
- {django_pfx-1.7.6.dev35 → django_pfx-1.7.6.dev37}/pfx/userjobs/migrations/0004_userjob_summary_url.py +0 -0
- {django_pfx-1.7.6.dev35 → django_pfx-1.7.6.dev37}/pfx/userjobs/migrations/0005_swappable_user_job.py +0 -0
- {django_pfx-1.7.6.dev35 → django_pfx-1.7.6.dev37}/pfx/userjobs/migrations/0006_lowercase_verbose_names.py +0 -0
- {django_pfx-1.7.6.dev35 → django_pfx-1.7.6.dev37}/pfx/userjobs/migrations/__init__.py +0 -0
- {django_pfx-1.7.6.dev35 → django_pfx-1.7.6.dev37}/pfx/userjobs/models/__init__.py +0 -0
- {django_pfx-1.7.6.dev35 → django_pfx-1.7.6.dev37}/pfx/userjobs/models/user_job.py +0 -0
- {django_pfx-1.7.6.dev35 → django_pfx-1.7.6.dev37}/pfx/userjobs/models/user_job_attachment.py +0 -0
- {django_pfx-1.7.6.dev35 → django_pfx-1.7.6.dev37}/pfx/userjobs/providers/__init__.py +0 -0
- {django_pfx-1.7.6.dev35 → django_pfx-1.7.6.dev37}/pfx/userjobs/providers/user_job_django_q_executor.py +0 -0
- {django_pfx-1.7.6.dev35 → django_pfx-1.7.6.dev37}/pfx/userjobs/providers/user_job_websocket_channel_provider.py +0 -0
- {django_pfx-1.7.6.dev35 → django_pfx-1.7.6.dev37}/pfx/userjobs/tasks.py +0 -0
- {django_pfx-1.7.6.dev35 → django_pfx-1.7.6.dev37}/pfx/userjobs/urls.py +0 -0
- {django_pfx-1.7.6.dev35 → django_pfx-1.7.6.dev37}/pfx/userjobs/views/__init__.py +0 -0
- {django_pfx-1.7.6.dev35 → django_pfx-1.7.6.dev37}/pfx/userjobs/views/user_job_attachment_view.py +0 -0
- {django_pfx-1.7.6.dev35 → django_pfx-1.7.6.dev37}/pfx/userjobs/views/user_job_view.py +0 -0
- {django_pfx-1.7.6.dev35 → django_pfx-1.7.6.dev37}/pfx/userjobs/views/user_websocket_consumer.py +0 -0
- {django_pfx-1.7.6.dev35 → django_pfx-1.7.6.dev37}/pyproject.toml +0 -0
- {django_pfx-1.7.6.dev35 → django_pfx-1.7.6.dev37}/serve-doc +0 -0
- {django_pfx-1.7.6.dev35 → django_pfx-1.7.6.dev37}/setup.cfg +0 -0
- {django_pfx-1.7.6.dev35 → django_pfx-1.7.6.dev37}/setup.py +0 -0
- {django_pfx-1.7.6.dev35 → django_pfx-1.7.6.dev37}/tests/__init__.py +0 -0
- {django_pfx-1.7.6.dev35 → django_pfx-1.7.6.dev37}/tests/apps.py +0 -0
- {django_pfx-1.7.6.dev35 → django_pfx-1.7.6.dev37}/tests/channel_group_send_calls.py +0 -0
- {django_pfx-1.7.6.dev35 → django_pfx-1.7.6.dev37}/tests/locale/fr/LC_MESSAGES/django.po +0 -0
- {django_pfx-1.7.6.dev35 → django_pfx-1.7.6.dev37}/tests/mfa_urls.py +0 -0
- {django_pfx-1.7.6.dev35 → django_pfx-1.7.6.dev37}/tests/migrations/0001_initial.py +0 -0
- {django_pfx-1.7.6.dev35 → django_pfx-1.7.6.dev37}/tests/migrations/__init__.py +0 -0
- {django_pfx-1.7.6.dev35 → django_pfx-1.7.6.dev37}/tests/models.py +0 -0
- {django_pfx-1.7.6.dev35 → django_pfx-1.7.6.dev37}/tests/settings/__init__.py +0 -0
- {django_pfx-1.7.6.dev35 → django_pfx-1.7.6.dev37}/tests/settings/ci.py +0 -0
- {django_pfx-1.7.6.dev35 → django_pfx-1.7.6.dev37}/tests/settings/common.py +0 -0
- {django_pfx-1.7.6.dev35 → django_pfx-1.7.6.dev37}/tests/settings/dev.py +0 -0
- {django_pfx-1.7.6.dev35 → django_pfx-1.7.6.dev37}/tests/settings/dev_custom_example.py +0 -0
- {django_pfx-1.7.6.dev35 → django_pfx-1.7.6.dev37}/tests/settings/dev_default.py +0 -0
- {django_pfx-1.7.6.dev35 → django_pfx-1.7.6.dev37}/tests/tests/__init__.py +0 -0
- {django_pfx-1.7.6.dev35 → django_pfx-1.7.6.dev37}/tests/tests/basic_api_errors.py +0 -0
- {django_pfx-1.7.6.dev35 → django_pfx-1.7.6.dev37}/tests/tests/basic_api_test.py +0 -0
- {django_pfx-1.7.6.dev35 → django_pfx-1.7.6.dev37}/tests/tests/mfa_test_utils.py +0 -0
- {django_pfx-1.7.6.dev35 → django_pfx-1.7.6.dev37}/tests/tests/test_api_doc.py +0 -0
- {django_pfx-1.7.6.dev35 → django_pfx-1.7.6.dev37}/tests/tests/test_api_doc_search.py +0 -0
- {django_pfx-1.7.6.dev35 → django_pfx-1.7.6.dev37}/tests/tests/test_auth_api.py +0 -0
- {django_pfx-1.7.6.dev35 → django_pfx-1.7.6.dev37}/tests/tests/test_body_mixin.py +0 -0
- {django_pfx-1.7.6.dev35 → django_pfx-1.7.6.dev37}/tests/tests/test_cache.py +0 -0
- {django_pfx-1.7.6.dev35 → django_pfx-1.7.6.dev37}/tests/tests/test_client.py +0 -0
- {django_pfx-1.7.6.dev35 → django_pfx-1.7.6.dev37}/tests/tests/test_emails_email_api.py +0 -0
- {django_pfx-1.7.6.dev35 → django_pfx-1.7.6.dev37}/tests/tests/test_emails_email_model.py +0 -0
- {django_pfx-1.7.6.dev35 → django_pfx-1.7.6.dev37}/tests/tests/test_emails_q_mailjet_email_backend.py +0 -0
- {django_pfx-1.7.6.dev35 → django_pfx-1.7.6.dev37}/tests/tests/test_emails_send_email_message.py +0 -0
- {django_pfx-1.7.6.dev35 → django_pfx-1.7.6.dev37}/tests/tests/test_emails_test_tools.py +0 -0
- {django_pfx-1.7.6.dev35 → django_pfx-1.7.6.dev37}/tests/tests/test_fields_choices.py +0 -0
- {django_pfx-1.7.6.dev35 → django_pfx-1.7.6.dev37}/tests/tests/test_fields_date.py +0 -0
- {django_pfx-1.7.6.dev35 → django_pfx-1.7.6.dev37}/tests/tests/test_fields_decimal.py +0 -0
- {django_pfx-1.7.6.dev35 → django_pfx-1.7.6.dev37}/tests/tests/test_fields_help_text.py +0 -0
- {django_pfx-1.7.6.dev35 → django_pfx-1.7.6.dev37}/tests/tests/test_fields_minutes_duration.py +0 -0
- {django_pfx-1.7.6.dev35 → django_pfx-1.7.6.dev37}/tests/tests/test_fields_one2many.py +0 -0
- {django_pfx-1.7.6.dev35 → django_pfx-1.7.6.dev37}/tests/tests/test_fields_rich_text.py +0 -0
- {django_pfx-1.7.6.dev35 → django_pfx-1.7.6.dev37}/tests/tests/test_fields_time.py +0 -0
- {django_pfx-1.7.6.dev35 → django_pfx-1.7.6.dev37}/tests/tests/test_filters.py +0 -0
- {django_pfx-1.7.6.dev35 → django_pfx-1.7.6.dev37}/tests/tests/test_locale_api.py +0 -0
- {django_pfx-1.7.6.dev35 → django_pfx-1.7.6.dev37}/tests/tests/test_mfa_legacy.py +0 -0
- {django_pfx-1.7.6.dev35 → django_pfx-1.7.6.dev37}/tests/tests/test_mfa_login_api.py +0 -0
- {django_pfx-1.7.6.dev35 → django_pfx-1.7.6.dev37}/tests/tests/test_mfa_setup_api.py +0 -0
- {django_pfx-1.7.6.dev35 → django_pfx-1.7.6.dev37}/tests/tests/test_ordered_rest_view_mixin.py +0 -0
- {django_pfx-1.7.6.dev35 → django_pfx-1.7.6.dev37}/tests/tests/test_perm_tests.py +0 -0
- {django_pfx-1.7.6.dev35 → django_pfx-1.7.6.dev37}/tests/tests/test_permissions.py +0 -0
- {django_pfx-1.7.6.dev35 → django_pfx-1.7.6.dev37}/tests/tests/test_perms_api.py +0 -0
- {django_pfx-1.7.6.dev35 → django_pfx-1.7.6.dev37}/tests/tests/test_post_migrate_groups_update.py +0 -0
- {django_pfx-1.7.6.dev35 → django_pfx-1.7.6.dev37}/tests/tests/test_profiling_middleware.py +0 -0
- {django_pfx-1.7.6.dev35 → django_pfx-1.7.6.dev37}/tests/tests/test_settings.py +0 -0
- {django_pfx-1.7.6.dev35 → django_pfx-1.7.6.dev37}/tests/tests/test_shortcuts.py +0 -0
- {django_pfx-1.7.6.dev35 → django_pfx-1.7.6.dev37}/tests/tests/test_timezone_middleware.py +0 -0
- {django_pfx-1.7.6.dev35 → django_pfx-1.7.6.dev37}/tests/tests/test_tools.py +0 -0
- {django_pfx-1.7.6.dev35 → django_pfx-1.7.6.dev37}/tests/tests/test_user_job_api.py +0 -0
- {django_pfx-1.7.6.dev35 → django_pfx-1.7.6.dev37}/tests/tests/test_user_job_messages.py +0 -0
- {django_pfx-1.7.6.dev35 → django_pfx-1.7.6.dev37}/tests/tests/test_user_job_model.py +0 -0
- {django_pfx-1.7.6.dev35 → django_pfx-1.7.6.dev37}/tests/tests/test_user_queryset.py +0 -0
- {django_pfx-1.7.6.dev35 → django_pfx-1.7.6.dev37}/tests/tests/test_user_websocket.py +0 -0
- {django_pfx-1.7.6.dev35 → django_pfx-1.7.6.dev37}/tests/tests/test_verbose_names.py +0 -0
- {django_pfx-1.7.6.dev35 → django_pfx-1.7.6.dev37}/tests/tests/test_version_check.py +0 -0
- {django_pfx-1.7.6.dev35 → django_pfx-1.7.6.dev37}/tests/tests/test_view_decorators.py +0 -0
- {django_pfx-1.7.6.dev35 → django_pfx-1.7.6.dev37}/tests/tests/test_view_fields.py +0 -0
- {django_pfx-1.7.6.dev35 → django_pfx-1.7.6.dev37}/tests/tests/test_ws_cookie_middleware.py +0 -0
- {django_pfx-1.7.6.dev35 → django_pfx-1.7.6.dev37}/tests/urls.py +0 -0
- {django_pfx-1.7.6.dev35 → django_pfx-1.7.6.dev37}/tests/views.py +0 -0
- {django_pfx-1.7.6.dev35 → django_pfx-1.7.6.dev37}/tests_base_user/__init__.py +0 -0
- {django_pfx-1.7.6.dev35 → django_pfx-1.7.6.dev37}/tests_base_user/migrations/__init__.py +0 -0
- {django_pfx-1.7.6.dev35 → django_pfx-1.7.6.dev37}/tests_base_user/settings/__init__.py +0 -0
- {django_pfx-1.7.6.dev35 → django_pfx-1.7.6.dev37}/tests_base_user/settings/ci.py +0 -0
- {django_pfx-1.7.6.dev35 → django_pfx-1.7.6.dev37}/tests_base_user/settings/common.py +0 -0
- {django_pfx-1.7.6.dev35 → django_pfx-1.7.6.dev37}/tests_base_user/settings/dev.py +0 -0
- {django_pfx-1.7.6.dev35 → django_pfx-1.7.6.dev37}/tests_base_user/settings/dev_custom_example.py +0 -0
- {django_pfx-1.7.6.dev35 → django_pfx-1.7.6.dev37}/tests_base_user/settings/dev_default.py +0 -0
- {django_pfx-1.7.6.dev35 → django_pfx-1.7.6.dev37}/tests_base_user/tests/__init__.py +0 -0
- {django_pfx-1.7.6.dev35 → django_pfx-1.7.6.dev37}/tests_base_user/tests/test_api.py +0 -0
- {django_pfx-1.7.6.dev35 → django_pfx-1.7.6.dev37}/tests_base_user/tests/test_auth_api.py +0 -0
- {django_pfx-1.7.6.dev35 → django_pfx-1.7.6.dev37}/tests_base_user/urls.py +0 -0
- {django_pfx-1.7.6.dev35 → django_pfx-1.7.6.dev37}/tests_base_user/views.py +0 -0
- {django_pfx-1.7.6.dev35 → django_pfx-1.7.6.dev37}/tests_custom_user/__init__.py +0 -0
- {django_pfx-1.7.6.dev35 → django_pfx-1.7.6.dev37}/tests_custom_user/migrations/0001_initial.py +0 -0
- {django_pfx-1.7.6.dev35 → django_pfx-1.7.6.dev37}/tests_custom_user/migrations/__init__.py +0 -0
- {django_pfx-1.7.6.dev35 → django_pfx-1.7.6.dev37}/tests_custom_user/models.py +0 -0
- {django_pfx-1.7.6.dev35 → django_pfx-1.7.6.dev37}/tests_custom_user/settings/__init__.py +0 -0
- {django_pfx-1.7.6.dev35 → django_pfx-1.7.6.dev37}/tests_custom_user/settings/ci.py +0 -0
- {django_pfx-1.7.6.dev35 → django_pfx-1.7.6.dev37}/tests_custom_user/settings/common.py +0 -0
- {django_pfx-1.7.6.dev35 → django_pfx-1.7.6.dev37}/tests_custom_user/settings/dev.py +0 -0
- {django_pfx-1.7.6.dev35 → django_pfx-1.7.6.dev37}/tests_custom_user/settings/dev_custom_example.py +0 -0
- {django_pfx-1.7.6.dev35 → django_pfx-1.7.6.dev37}/tests_custom_user/settings/dev_default.py +0 -0
- {django_pfx-1.7.6.dev35 → django_pfx-1.7.6.dev37}/tests_custom_user/tests/__init__.py +0 -0
- {django_pfx-1.7.6.dev35 → django_pfx-1.7.6.dev37}/tests_custom_user/tests/test_api.py +0 -0
- {django_pfx-1.7.6.dev35 → django_pfx-1.7.6.dev37}/tests_custom_user/tests/test_auth_api.py +0 -0
- {django_pfx-1.7.6.dev35 → django_pfx-1.7.6.dev37}/tests_custom_user/urls.py +0 -0
- {django_pfx-1.7.6.dev35 → django_pfx-1.7.6.dev37}/tests_custom_user/views.py +0 -0
- {django_pfx-1.7.6.dev35 → django_pfx-1.7.6.dev37}/tests_custom_user_job/__init__.py +0 -0
- {django_pfx-1.7.6.dev35 → django_pfx-1.7.6.dev37}/tests_custom_user_job/migrations/0001_initial.py +0 -0
- {django_pfx-1.7.6.dev35 → django_pfx-1.7.6.dev37}/tests_custom_user_job/migrations/0002_lowercase_verbose_names.py +0 -0
- {django_pfx-1.7.6.dev35 → django_pfx-1.7.6.dev37}/tests_custom_user_job/migrations/__init__.py +0 -0
- {django_pfx-1.7.6.dev35 → django_pfx-1.7.6.dev37}/tests_custom_user_job/models.py +0 -0
- {django_pfx-1.7.6.dev35 → django_pfx-1.7.6.dev37}/tests_custom_user_job/providers.py +0 -0
- {django_pfx-1.7.6.dev35 → django_pfx-1.7.6.dev37}/tests_custom_user_job/scope.py +0 -0
- {django_pfx-1.7.6.dev35 → django_pfx-1.7.6.dev37}/tests_custom_user_job/settings/__init__.py +0 -0
- {django_pfx-1.7.6.dev35 → django_pfx-1.7.6.dev37}/tests_custom_user_job/settings/ci.py +0 -0
- {django_pfx-1.7.6.dev35 → django_pfx-1.7.6.dev37}/tests_custom_user_job/settings/common.py +0 -0
- {django_pfx-1.7.6.dev35 → django_pfx-1.7.6.dev37}/tests_custom_user_job/settings/dev.py +0 -0
- {django_pfx-1.7.6.dev35 → django_pfx-1.7.6.dev37}/tests_custom_user_job/settings/dev_custom_example.py +0 -0
- {django_pfx-1.7.6.dev35 → django_pfx-1.7.6.dev37}/tests_custom_user_job/settings/dev_default.py +0 -0
- {django_pfx-1.7.6.dev35 → django_pfx-1.7.6.dev37}/tests_custom_user_job/tests/__init__.py +0 -0
- {django_pfx-1.7.6.dev35 → django_pfx-1.7.6.dev37}/tests_custom_user_job/tests/test_user_job.py +0 -0
- {django_pfx-1.7.6.dev35 → django_pfx-1.7.6.dev37}/tests_custom_user_job/urls.py +0 -0
- {django_pfx-1.7.6.dev35 → django_pfx-1.7.6.dev37}/tests_user_email_login/__init__.py +0 -0
- {django_pfx-1.7.6.dev35 → django_pfx-1.7.6.dev37}/tests_user_email_login/migrations/0001_initial.py +0 -0
- {django_pfx-1.7.6.dev35 → django_pfx-1.7.6.dev37}/tests_user_email_login/migrations/__init__.py +0 -0
- {django_pfx-1.7.6.dev35 → django_pfx-1.7.6.dev37}/tests_user_email_login/models.py +0 -0
- {django_pfx-1.7.6.dev35 → django_pfx-1.7.6.dev37}/tests_user_email_login/settings/__init__.py +0 -0
- {django_pfx-1.7.6.dev35 → django_pfx-1.7.6.dev37}/tests_user_email_login/settings/ci.py +0 -0
- {django_pfx-1.7.6.dev35 → django_pfx-1.7.6.dev37}/tests_user_email_login/settings/common.py +0 -0
- {django_pfx-1.7.6.dev35 → django_pfx-1.7.6.dev37}/tests_user_email_login/settings/dev.py +0 -0
- {django_pfx-1.7.6.dev35 → django_pfx-1.7.6.dev37}/tests_user_email_login/settings/dev_custom_example.py +0 -0
- {django_pfx-1.7.6.dev35 → django_pfx-1.7.6.dev37}/tests_user_email_login/settings/dev_default.py +0 -0
- {django_pfx-1.7.6.dev35 → django_pfx-1.7.6.dev37}/tests_user_email_login/tests/__init__.py +0 -0
- {django_pfx-1.7.6.dev35 → django_pfx-1.7.6.dev37}/tests_user_email_login/tests/test_api.py +0 -0
- {django_pfx-1.7.6.dev35 → django_pfx-1.7.6.dev37}/tests_user_email_login/tests/test_auth_api.py +0 -0
- {django_pfx-1.7.6.dev35 → django_pfx-1.7.6.dev37}/tests_user_email_login/urls.py +0 -0
- {django_pfx-1.7.6.dev35 → django_pfx-1.7.6.dev37}/tests_user_email_login/views.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: django-pfx
|
|
3
|
-
Version: 1.7.6.
|
|
3
|
+
Version: 1.7.6.dev37
|
|
4
4
|
Summary: Django PFX is a toolkit designed to streamline the development of RESTful APIs using the Django framework.
|
|
5
5
|
Author: Hervé Martinet
|
|
6
6
|
Author-email: herve.martinet@gmail.com
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: django-pfx
|
|
3
|
-
Version: 1.7.6.
|
|
3
|
+
Version: 1.7.6.dev37
|
|
4
4
|
Summary: Django PFX is a toolkit designed to streamline the development of RESTful APIs using the Django framework.
|
|
5
5
|
Author: Hervé Martinet
|
|
6
6
|
Author-email: herve.martinet@gmail.com
|
|
@@ -220,6 +220,7 @@ tests/tests/__init__.py
|
|
|
220
220
|
tests/tests/basic_api_errors.py
|
|
221
221
|
tests/tests/basic_api_test.py
|
|
222
222
|
tests/tests/mfa_test_utils.py
|
|
223
|
+
tests/tests/parallel_scenarios.py
|
|
223
224
|
tests/tests/test_api_doc.py
|
|
224
225
|
tests/tests/test_api_doc_search.py
|
|
225
226
|
tests/tests/test_auth_api.py
|
|
@@ -210,6 +210,7 @@
|
|
|
210
210
|
"tests/tests/basic_api_errors.py",
|
|
211
211
|
"tests/tests/basic_api_test.py",
|
|
212
212
|
"tests/tests/mfa_test_utils.py",
|
|
213
|
+
"tests/tests/parallel_scenarios.py",
|
|
213
214
|
"tests/tests/test_api_doc.py",
|
|
214
215
|
"tests/tests/test_api_doc_search.py",
|
|
215
216
|
"tests/tests/test_auth_api.py",
|
|
@@ -1,6 +1,8 @@
|
|
|
1
|
+
import copy
|
|
1
2
|
import json
|
|
2
3
|
import logging
|
|
3
4
|
import re
|
|
5
|
+
import traceback
|
|
4
6
|
from functools import reduce
|
|
5
7
|
from http.cookies import SimpleCookie
|
|
6
8
|
from json import JSONDecodeError
|
|
@@ -9,7 +11,12 @@ from django.apps import apps
|
|
|
9
11
|
from django.db import transaction
|
|
10
12
|
from django.test import override_settings
|
|
11
13
|
from django.test.client import Client as DjangoClient
|
|
12
|
-
from django.test.runner import
|
|
14
|
+
from django.test.runner import (
|
|
15
|
+
DiscoverRunner,
|
|
16
|
+
ParallelTestSuite,
|
|
17
|
+
RemoteTestResult,
|
|
18
|
+
RemoteTestRunner,
|
|
19
|
+
)
|
|
13
20
|
from django.test.runner import _init_worker as django_init_worker
|
|
14
21
|
from django.utils.module_loading import import_string
|
|
15
22
|
|
|
@@ -351,8 +358,72 @@ def _init_worker(*args, **kwargs):
|
|
|
351
358
|
override_settings(**get_test_backends()).enable()
|
|
352
359
|
|
|
353
360
|
|
|
361
|
+
class UnpicklableTestError(Exception):
|
|
362
|
+
"""An error a parallel worker could not send back, as text."""
|
|
363
|
+
|
|
364
|
+
|
|
365
|
+
class UnpicklableTestFailure(UnpicklableTestError, AssertionError):
|
|
366
|
+
"""A failure a parallel worker could not send back, as text."""
|
|
367
|
+
|
|
368
|
+
|
|
369
|
+
class _Repr(str):
|
|
370
|
+
def __repr__(self):
|
|
371
|
+
return str(self)
|
|
372
|
+
|
|
373
|
+
|
|
374
|
+
class PicklableRemoteTestResult(RemoteTestResult):
|
|
375
|
+
"""Send every outcome of a parallel worker back to the parent.
|
|
376
|
+
|
|
377
|
+
Django aborts the whole run, without the traceback, on an exception it
|
|
378
|
+
cannot pickle (one holding a request, a lock, a local class…). Such an
|
|
379
|
+
exception is sent as its formatted traceback instead, and the run goes
|
|
380
|
+
on."""
|
|
381
|
+
|
|
382
|
+
def _picklable(self, err):
|
|
383
|
+
try:
|
|
384
|
+
self._confirm_picklable(err)
|
|
385
|
+
except Exception:
|
|
386
|
+
cls = (
|
|
387
|
+
UnpicklableTestFailure if issubclass(err[0], AssertionError)
|
|
388
|
+
else UnpicklableTestError)
|
|
389
|
+
return cls, cls(''.join(traceback.format_exception(*err))), None
|
|
390
|
+
return err
|
|
391
|
+
|
|
392
|
+
def _picklable_subtest(self, test, subtest):
|
|
393
|
+
# The subtest holds the running test case, which can become
|
|
394
|
+
# unpicklable after the failure: a fresh one is always sent.
|
|
395
|
+
subtest = copy.copy(subtest)
|
|
396
|
+
subtest.test_case = type(test)(test._testMethodName)
|
|
397
|
+
try:
|
|
398
|
+
self._confirm_picklable(subtest)
|
|
399
|
+
except Exception:
|
|
400
|
+
subtest.params = {
|
|
401
|
+
k: _Repr(repr(v)) for k, v in subtest.params.items()}
|
|
402
|
+
return subtest
|
|
403
|
+
|
|
404
|
+
def addError(self, test, err):
|
|
405
|
+
super().addError(test, self._picklable(err))
|
|
406
|
+
|
|
407
|
+
def addFailure(self, test, err):
|
|
408
|
+
super().addFailure(test, self._picklable(err))
|
|
409
|
+
|
|
410
|
+
def addExpectedFailure(self, test, err):
|
|
411
|
+
super().addExpectedFailure(test, self._picklable(err))
|
|
412
|
+
|
|
413
|
+
def addSubTest(self, test, subtest, err):
|
|
414
|
+
if err is not None:
|
|
415
|
+
err = self._picklable(err)
|
|
416
|
+
subtest = self._picklable_subtest(test, subtest)
|
|
417
|
+
super().addSubTest(test, subtest, err)
|
|
418
|
+
|
|
419
|
+
|
|
420
|
+
class PicklableRemoteTestRunner(RemoteTestRunner):
|
|
421
|
+
resultclass = PicklableRemoteTestResult
|
|
422
|
+
|
|
423
|
+
|
|
354
424
|
class MemoryBackendsParallelTestSuite(ParallelTestSuite):
|
|
355
425
|
init_worker = _init_worker
|
|
426
|
+
runner_class = PicklableRemoteTestRunner
|
|
356
427
|
|
|
357
428
|
|
|
358
429
|
class MemoryBackendsRunnerMixin:
|
|
@@ -371,7 +442,10 @@ class MemoryBackendsRunnerMixin:
|
|
|
371
442
|
validated, which ``locmem`` never does.
|
|
372
443
|
|
|
373
444
|
A test class decorated with ``override_settings`` keeps its own
|
|
374
|
-
backends.
|
|
445
|
+
backends.
|
|
446
|
+
|
|
447
|
+
With ``--parallel``, the workers also send back the errors Django
|
|
448
|
+
cannot pickle (see :class:`PicklableRemoteTestResult`)."""
|
|
375
449
|
|
|
376
450
|
parallel_test_suite = MemoryBackendsParallelTestSuite
|
|
377
451
|
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
"""Test cases that fail in ways a parallel worker cannot pickle.
|
|
2
|
+
|
|
3
|
+
Run by test_test_runner.py, not by the discovery (the name does not start
|
|
4
|
+
with ``test``). They live at module level as real test classes do: the
|
|
5
|
+
parent process rebuilds a test case from its class."""
|
|
6
|
+
import threading
|
|
7
|
+
import unittest
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class Unpicklable(Exception):
|
|
11
|
+
def __init__(self):
|
|
12
|
+
super().__init__("holds a lock")
|
|
13
|
+
self.lock = threading.Lock()
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class KeywordOnly(Exception):
|
|
17
|
+
"""Pickles, but cannot be rebuilt from its args."""
|
|
18
|
+
|
|
19
|
+
def __init__(self, *, code):
|
|
20
|
+
super().__init__(f"code {code}")
|
|
21
|
+
self.code = code
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
class UnpicklableAssertion(AssertionError):
|
|
25
|
+
def __init__(self):
|
|
26
|
+
super().__init__("assertion holding a lock")
|
|
27
|
+
self.lock = threading.Lock()
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
class Scenarios(unittest.TestCase):
|
|
31
|
+
|
|
32
|
+
def test_plain_failure(self):
|
|
33
|
+
self.assertEqual(1, 2)
|
|
34
|
+
|
|
35
|
+
def test_lock_in_the_exception(self):
|
|
36
|
+
raise Unpicklable()
|
|
37
|
+
|
|
38
|
+
def test_unpicklable_cause(self):
|
|
39
|
+
try:
|
|
40
|
+
raise Unpicklable()
|
|
41
|
+
except Unpicklable as e:
|
|
42
|
+
raise ValueError("wrapped") from e
|
|
43
|
+
|
|
44
|
+
def test_local_class(self):
|
|
45
|
+
class Local(Exception):
|
|
46
|
+
pass
|
|
47
|
+
raise Local("local class")
|
|
48
|
+
|
|
49
|
+
def test_keyword_only_init(self):
|
|
50
|
+
raise KeywordOnly(code=42)
|
|
51
|
+
|
|
52
|
+
def test_unpicklable_assertion(self):
|
|
53
|
+
raise UnpicklableAssertion()
|
|
54
|
+
|
|
55
|
+
@unittest.expectedFailure
|
|
56
|
+
def test_expected_failure(self):
|
|
57
|
+
raise Unpicklable()
|
|
58
|
+
|
|
59
|
+
def test_subtest_then_a_lock(self):
|
|
60
|
+
with self.subTest(i=1):
|
|
61
|
+
self.assertEqual(1, 5)
|
|
62
|
+
self.lock = threading.Lock()
|
|
63
|
+
|
|
64
|
+
def test_subtest_with_a_lock_param(self):
|
|
65
|
+
with self.subTest(lock=threading.Lock()):
|
|
66
|
+
self.fail("with a lock as param")
|
|
67
|
+
|
|
68
|
+
def test_subtest_unpicklable_error(self):
|
|
69
|
+
with self.subTest(n=1):
|
|
70
|
+
raise Unpicklable()
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
class TearDownError(unittest.TestCase):
|
|
74
|
+
|
|
75
|
+
def tearDown(self):
|
|
76
|
+
raise Unpicklable()
|
|
77
|
+
|
|
78
|
+
def test_ok(self):
|
|
79
|
+
pass
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
class SetUpClassError(unittest.TestCase):
|
|
83
|
+
|
|
84
|
+
@classmethod
|
|
85
|
+
def setUpClass(cls):
|
|
86
|
+
raise Unpicklable()
|
|
87
|
+
|
|
88
|
+
def test_never_runs(self):
|
|
89
|
+
pass
|
|
@@ -0,0 +1,234 @@
|
|
|
1
|
+
import io
|
|
2
|
+
import pickle
|
|
3
|
+
import re
|
|
4
|
+
import unittest
|
|
5
|
+
from contextlib import redirect_stdout
|
|
6
|
+
from unittest.mock import patch
|
|
7
|
+
|
|
8
|
+
from django.conf import settings
|
|
9
|
+
from django.test import SimpleTestCase, override_settings
|
|
10
|
+
from django.test.runner import RemoteTestRunner
|
|
11
|
+
from django.test.utils import setup_test_environment, teardown_test_environment
|
|
12
|
+
|
|
13
|
+
from pfx.pfxcore.settings import settings as pfx_settings
|
|
14
|
+
from pfx.pfxcore.sms import get_sms_backend
|
|
15
|
+
from pfx.pfxcore.sms.backends.memory import MemorySMSBackend
|
|
16
|
+
from pfx.pfxcore.test import (
|
|
17
|
+
MemoryBackendsParallelTestSuite,
|
|
18
|
+
MemoryBackendsRunnerMixin,
|
|
19
|
+
PicklableRemoteTestResult,
|
|
20
|
+
PicklableRemoteTestRunner,
|
|
21
|
+
SMSTestMixin,
|
|
22
|
+
TestRunner,
|
|
23
|
+
_init_worker,
|
|
24
|
+
get_test_backends,
|
|
25
|
+
)
|
|
26
|
+
|
|
27
|
+
from . import parallel_scenarios
|
|
28
|
+
|
|
29
|
+
MEMORY_EMAIL = 'pfx.emails.email_backend.MemoryEmailBackend'
|
|
30
|
+
MEMORY_SMS = 'pfx.pfxcore.sms.backends.memory.MemorySMSBackend'
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
class TestMemoryBackendsRunner(SimpleTestCase):
|
|
34
|
+
|
|
35
|
+
def test_the_runner_replaces_the_backends(self):
|
|
36
|
+
"""The runner swaps the backends for the whole run, and puts the
|
|
37
|
+
original ones back at the end, locmem restored by Django included.
|
|
38
|
+
|
|
39
|
+
The test environment of the current run is torn down first, as
|
|
40
|
+
Django refuses to set it up twice, and restored afterwards."""
|
|
41
|
+
teardown_test_environment()
|
|
42
|
+
try:
|
|
43
|
+
email = settings.EMAIL_BACKEND
|
|
44
|
+
sms = pfx_settings.PFX_SMS_BACKEND
|
|
45
|
+
runner = TestRunner()
|
|
46
|
+
runner.setup_test_environment()
|
|
47
|
+
try:
|
|
48
|
+
self.assertEqual(settings.EMAIL_BACKEND, MEMORY_EMAIL)
|
|
49
|
+
self.assertEqual(pfx_settings.PFX_SMS_BACKEND, MEMORY_SMS)
|
|
50
|
+
finally:
|
|
51
|
+
runner.teardown_test_environment()
|
|
52
|
+
self.assertEqual(settings.EMAIL_BACKEND, email)
|
|
53
|
+
self.assertEqual(pfx_settings.PFX_SMS_BACKEND, sms)
|
|
54
|
+
finally:
|
|
55
|
+
setup_test_environment(debug=False)
|
|
56
|
+
|
|
57
|
+
def test_the_parallel_workers_get_the_backends(self):
|
|
58
|
+
"""A spawned worker sets up the test environment again, locmem
|
|
59
|
+
included: the suite of the runner uses a worker init that puts the
|
|
60
|
+
backends back."""
|
|
61
|
+
self.assertIs(
|
|
62
|
+
TestRunner.parallel_test_suite, MemoryBackendsParallelTestSuite)
|
|
63
|
+
self.assertIs(
|
|
64
|
+
MemoryBackendsParallelTestSuite.init_worker, _init_worker)
|
|
65
|
+
|
|
66
|
+
def test_the_backends_are_configurable(self):
|
|
67
|
+
self.assertEqual(get_test_backends(), dict(
|
|
68
|
+
EMAIL_BACKEND=MEMORY_EMAIL, PFX_SMS_BACKEND=MEMORY_SMS))
|
|
69
|
+
with override_settings(
|
|
70
|
+
PFX_TEST_EMAIL_BACKEND='my.EmailBackend',
|
|
71
|
+
PFX_TEST_SMS_BACKEND='my.SMSBackend'):
|
|
72
|
+
self.assertEqual(get_test_backends(), dict(
|
|
73
|
+
EMAIL_BACKEND='my.EmailBackend',
|
|
74
|
+
PFX_SMS_BACKEND='my.SMSBackend'))
|
|
75
|
+
|
|
76
|
+
def test_the_email_backend_needs_pfx_emails(self):
|
|
77
|
+
"""Without pfx.emails, there is no email model to fill: locmem
|
|
78
|
+
stays."""
|
|
79
|
+
with patch('pfx.pfxcore.test.apps.is_installed', return_value=False):
|
|
80
|
+
self.assertEqual(
|
|
81
|
+
get_test_backends(), dict(PFX_SMS_BACKEND=MEMORY_SMS))
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
def run_in_worker(runner_class, *tests):
|
|
85
|
+
"""Run the tests as a parallel worker does, send its events through
|
|
86
|
+
pickle as multiprocessing does, and replay them in a parent result.
|
|
87
|
+
|
|
88
|
+
The tests are listed before the run: the suite drops them as they
|
|
89
|
+
run."""
|
|
90
|
+
suite = unittest.TestSuite(tests)
|
|
91
|
+
listed = list(suite)
|
|
92
|
+
events = pickle.loads(pickle.dumps(runner_class().run(suite).events))
|
|
93
|
+
parent = unittest.TestResult()
|
|
94
|
+
for event in events:
|
|
95
|
+
MemoryBackendsParallelTestSuite([], 1).handle_event(
|
|
96
|
+
parent, listed, event)
|
|
97
|
+
return parent
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
def outcomes(results):
|
|
101
|
+
"""{test id without its module: last line of the traceback}
|
|
102
|
+
|
|
103
|
+
The ``[<object object at 0x…>]`` of a subtest without message is left
|
|
104
|
+
out: unittest compares its message to a sentinel object, which does not
|
|
105
|
+
survive pickling, in Django as here."""
|
|
106
|
+
prefix = f'{parallel_scenarios.__name__}.'
|
|
107
|
+
return {
|
|
108
|
+
re.sub(r' \[<object object at 0x\w+>\]', '', test.id())
|
|
109
|
+
.removeprefix(prefix): text.strip().splitlines()[-1]
|
|
110
|
+
for test, text in results}
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
class TestPicklableRemoteTestResult(SimpleTestCase):
|
|
114
|
+
|
|
115
|
+
def test_the_runners_use_it(self):
|
|
116
|
+
self.assertIs(
|
|
117
|
+
MemoryBackendsParallelTestSuite.runner_class,
|
|
118
|
+
PicklableRemoteTestRunner)
|
|
119
|
+
|
|
120
|
+
def test_django_aborts_on_an_unpicklable_error(self):
|
|
121
|
+
"""Why this result exists: the Django one lets the pickling error
|
|
122
|
+
rise, which aborts the whole run in the parent process."""
|
|
123
|
+
with redirect_stdout(io.StringIO()), self.assertRaises(TypeError):
|
|
124
|
+
run_in_worker(RemoteTestRunner, parallel_scenarios.Scenarios(
|
|
125
|
+
'test_lock_in_the_exception'))
|
|
126
|
+
|
|
127
|
+
def test_every_outcome_is_sent_back(self):
|
|
128
|
+
"""Each way of failing Django cannot pickle reaches the parent,
|
|
129
|
+
under its test, as an error or a failure as it was raised, with
|
|
130
|
+
the original exception at the end of the traceback."""
|
|
131
|
+
names = unittest.TestLoader().getTestCaseNames(
|
|
132
|
+
parallel_scenarios.Scenarios)
|
|
133
|
+
parent = run_in_worker(
|
|
134
|
+
PicklableRemoteTestRunner,
|
|
135
|
+
*map(parallel_scenarios.Scenarios, names),
|
|
136
|
+
parallel_scenarios.TearDownError('test_ok'))
|
|
137
|
+
module = parallel_scenarios.__name__
|
|
138
|
+
lock = f'{module}.Unpicklable: holds a lock'
|
|
139
|
+
self.assertEqual(outcomes(parent.errors), {
|
|
140
|
+
'Scenarios.test_keyword_only_init':
|
|
141
|
+
f'{module}.KeywordOnly: code 42',
|
|
142
|
+
'Scenarios.test_local_class':
|
|
143
|
+
f'{module}.Scenarios.test_local_class.<locals>.Local: '
|
|
144
|
+
'local class',
|
|
145
|
+
'Scenarios.test_lock_in_the_exception': lock,
|
|
146
|
+
'Scenarios.test_subtest_unpicklable_error (n=1)': lock,
|
|
147
|
+
'Scenarios.test_unpicklable_cause': 'ValueError: wrapped',
|
|
148
|
+
'TearDownError.test_ok': lock,
|
|
149
|
+
})
|
|
150
|
+
[cause] = [
|
|
151
|
+
text for test, text in parent.errors
|
|
152
|
+
if test.id().endswith('test_unpicklable_cause')]
|
|
153
|
+
self.assertIn(lock, cause)
|
|
154
|
+
failures = outcomes(parent.failures)
|
|
155
|
+
# The lock of the subtest params is sent as its repr.
|
|
156
|
+
[lock_param] = [
|
|
157
|
+
k for k in failures if k.startswith(
|
|
158
|
+
'Scenarios.test_subtest_with_a_lock_param (lock=<unlocked')]
|
|
159
|
+
self.assertEqual(failures, {
|
|
160
|
+
'Scenarios.test_plain_failure': 'AssertionError: 1 != 2',
|
|
161
|
+
'Scenarios.test_subtest_then_a_lock (i=1)':
|
|
162
|
+
'AssertionError: 1 != 5',
|
|
163
|
+
lock_param: 'AssertionError: with a lock as param',
|
|
164
|
+
'Scenarios.test_unpicklable_assertion':
|
|
165
|
+
f'{module}.UnpicklableAssertion: assertion holding a lock',
|
|
166
|
+
})
|
|
167
|
+
self.assertEqual(
|
|
168
|
+
list(outcomes(parent.expectedFailures)),
|
|
169
|
+
['Scenarios.test_expected_failure'])
|
|
170
|
+
|
|
171
|
+
def test_a_class_fixture_error_is_sent_back(self):
|
|
172
|
+
"""Django sends each class in its own subsuite: a setUpClass error
|
|
173
|
+
comes before the first test, and is reported under the class."""
|
|
174
|
+
parent = run_in_worker(
|
|
175
|
+
PicklableRemoteTestRunner,
|
|
176
|
+
parallel_scenarios.SetUpClassError('test_never_runs'))
|
|
177
|
+
module = parallel_scenarios.__name__
|
|
178
|
+
self.assertEqual(outcomes(parent.errors), {
|
|
179
|
+
f'setUpClass ({module}.SetUpClassError)':
|
|
180
|
+
f'{module}.Unpicklable: holds a lock'})
|
|
181
|
+
|
|
182
|
+
def test_a_picklable_error_is_sent_as_is(self):
|
|
183
|
+
"""What pickles goes through untouched, as with Django.
|
|
184
|
+
|
|
185
|
+
Checked without a traceback: a traceback only pickles with tblib,
|
|
186
|
+
and without it every error is sent as text (where Django aborts
|
|
187
|
+
the run and asks for tblib)."""
|
|
188
|
+
err = (AssertionError, AssertionError('1 != 2'), None)
|
|
189
|
+
self.assertIs(PicklableRemoteTestResult()._picklable(err), err)
|
|
190
|
+
|
|
191
|
+
|
|
192
|
+
@override_settings(PFX_SMS_BACKEND=MEMORY_SMS)
|
|
193
|
+
class TestSMSTestMixin(SMSTestMixin, SimpleTestCase):
|
|
194
|
+
|
|
195
|
+
def test_the_sms_are_collected(self):
|
|
196
|
+
get_sms_backend().send_sms('+41791234567', 'Code: 123456')
|
|
197
|
+
self.assertEqual(
|
|
198
|
+
self.sms_outbox(), [('+41791234567', 'Code: 123456')])
|
|
199
|
+
self.clear_sms_outbox()
|
|
200
|
+
self.assertEqual(self.sms_outbox(), [])
|
|
201
|
+
|
|
202
|
+
def test_the_outbox_is_emptied_before_each_test(self):
|
|
203
|
+
"""An SMS left by a previous test is gone when the next starts."""
|
|
204
|
+
class ATest(SMSTestMixin, SimpleTestCase):
|
|
205
|
+
def test(self):
|
|
206
|
+
self.outbox_at_start = self.sms_outbox()
|
|
207
|
+
|
|
208
|
+
MemorySMSBackend.outbox.append(('+41791234567', 'Left over'))
|
|
209
|
+
test = ATest('test')
|
|
210
|
+
result = unittest.TestResult()
|
|
211
|
+
test(result)
|
|
212
|
+
self.assertTrue(result.wasSuccessful(), result.errors)
|
|
213
|
+
self.assertEqual(test.outbox_at_start, [])
|
|
214
|
+
|
|
215
|
+
@override_settings(
|
|
216
|
+
PFX_SMS_BACKEND='pfx.pfxcore.sms.backends.console.ConsoleSMSBackend')
|
|
217
|
+
def test_the_helpers_refuse_another_backend(self):
|
|
218
|
+
with self.assertRaises(AssertionError):
|
|
219
|
+
self.sms_outbox()
|
|
220
|
+
|
|
221
|
+
|
|
222
|
+
class TestXMLTestRunner(SimpleTestCase):
|
|
223
|
+
|
|
224
|
+
def test_the_xml_runner_replaces_the_backends(self):
|
|
225
|
+
"""The XML variant is the same runner: same backends, same parallel
|
|
226
|
+
workers, and the XML reports."""
|
|
227
|
+
from xmlrunner.extra.djangotestrunner import XMLTestRunner as Base
|
|
228
|
+
|
|
229
|
+
from pfx.pfxcore.xml_test_runner import XMLTestRunner
|
|
230
|
+
|
|
231
|
+
self.assertTrue(issubclass(XMLTestRunner, Base))
|
|
232
|
+
self.assertTrue(issubclass(XMLTestRunner, MemoryBackendsRunnerMixin))
|
|
233
|
+
self.assertIs(
|
|
234
|
+
XMLTestRunner.parallel_test_suite, MemoryBackendsParallelTestSuite)
|
|
@@ -1,117 +0,0 @@
|
|
|
1
|
-
import unittest
|
|
2
|
-
from unittest.mock import patch
|
|
3
|
-
|
|
4
|
-
from django.conf import settings
|
|
5
|
-
from django.test import SimpleTestCase, override_settings
|
|
6
|
-
from django.test.utils import setup_test_environment, teardown_test_environment
|
|
7
|
-
|
|
8
|
-
from pfx.pfxcore.settings import settings as pfx_settings
|
|
9
|
-
from pfx.pfxcore.sms import get_sms_backend
|
|
10
|
-
from pfx.pfxcore.sms.backends.memory import MemorySMSBackend
|
|
11
|
-
from pfx.pfxcore.test import (
|
|
12
|
-
MemoryBackendsParallelTestSuite,
|
|
13
|
-
MemoryBackendsRunnerMixin,
|
|
14
|
-
SMSTestMixin,
|
|
15
|
-
TestRunner,
|
|
16
|
-
_init_worker,
|
|
17
|
-
get_test_backends,
|
|
18
|
-
)
|
|
19
|
-
|
|
20
|
-
MEMORY_EMAIL = 'pfx.emails.email_backend.MemoryEmailBackend'
|
|
21
|
-
MEMORY_SMS = 'pfx.pfxcore.sms.backends.memory.MemorySMSBackend'
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
class TestMemoryBackendsRunner(SimpleTestCase):
|
|
25
|
-
|
|
26
|
-
def test_the_runner_replaces_the_backends(self):
|
|
27
|
-
"""The runner swaps the backends for the whole run, and puts the
|
|
28
|
-
original ones back at the end, locmem restored by Django included.
|
|
29
|
-
|
|
30
|
-
The test environment of the current run is torn down first, as
|
|
31
|
-
Django refuses to set it up twice, and restored afterwards."""
|
|
32
|
-
teardown_test_environment()
|
|
33
|
-
try:
|
|
34
|
-
email = settings.EMAIL_BACKEND
|
|
35
|
-
sms = pfx_settings.PFX_SMS_BACKEND
|
|
36
|
-
runner = TestRunner()
|
|
37
|
-
runner.setup_test_environment()
|
|
38
|
-
try:
|
|
39
|
-
self.assertEqual(settings.EMAIL_BACKEND, MEMORY_EMAIL)
|
|
40
|
-
self.assertEqual(pfx_settings.PFX_SMS_BACKEND, MEMORY_SMS)
|
|
41
|
-
finally:
|
|
42
|
-
runner.teardown_test_environment()
|
|
43
|
-
self.assertEqual(settings.EMAIL_BACKEND, email)
|
|
44
|
-
self.assertEqual(pfx_settings.PFX_SMS_BACKEND, sms)
|
|
45
|
-
finally:
|
|
46
|
-
setup_test_environment(debug=False)
|
|
47
|
-
|
|
48
|
-
def test_the_parallel_workers_get_the_backends(self):
|
|
49
|
-
"""A spawned worker sets up the test environment again, locmem
|
|
50
|
-
included: the suite of the runner uses a worker init that puts the
|
|
51
|
-
backends back."""
|
|
52
|
-
self.assertIs(
|
|
53
|
-
TestRunner.parallel_test_suite, MemoryBackendsParallelTestSuite)
|
|
54
|
-
self.assertIs(
|
|
55
|
-
MemoryBackendsParallelTestSuite.init_worker, _init_worker)
|
|
56
|
-
|
|
57
|
-
def test_the_backends_are_configurable(self):
|
|
58
|
-
self.assertEqual(get_test_backends(), dict(
|
|
59
|
-
EMAIL_BACKEND=MEMORY_EMAIL, PFX_SMS_BACKEND=MEMORY_SMS))
|
|
60
|
-
with override_settings(
|
|
61
|
-
PFX_TEST_EMAIL_BACKEND='my.EmailBackend',
|
|
62
|
-
PFX_TEST_SMS_BACKEND='my.SMSBackend'):
|
|
63
|
-
self.assertEqual(get_test_backends(), dict(
|
|
64
|
-
EMAIL_BACKEND='my.EmailBackend',
|
|
65
|
-
PFX_SMS_BACKEND='my.SMSBackend'))
|
|
66
|
-
|
|
67
|
-
def test_the_email_backend_needs_pfx_emails(self):
|
|
68
|
-
"""Without pfx.emails, there is no email model to fill: locmem
|
|
69
|
-
stays."""
|
|
70
|
-
with patch('pfx.pfxcore.test.apps.is_installed', return_value=False):
|
|
71
|
-
self.assertEqual(
|
|
72
|
-
get_test_backends(), dict(PFX_SMS_BACKEND=MEMORY_SMS))
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
@override_settings(PFX_SMS_BACKEND=MEMORY_SMS)
|
|
76
|
-
class TestSMSTestMixin(SMSTestMixin, SimpleTestCase):
|
|
77
|
-
|
|
78
|
-
def test_the_sms_are_collected(self):
|
|
79
|
-
get_sms_backend().send_sms('+41791234567', 'Code: 123456')
|
|
80
|
-
self.assertEqual(
|
|
81
|
-
self.sms_outbox(), [('+41791234567', 'Code: 123456')])
|
|
82
|
-
self.clear_sms_outbox()
|
|
83
|
-
self.assertEqual(self.sms_outbox(), [])
|
|
84
|
-
|
|
85
|
-
def test_the_outbox_is_emptied_before_each_test(self):
|
|
86
|
-
"""An SMS left by a previous test is gone when the next starts."""
|
|
87
|
-
class ATest(SMSTestMixin, SimpleTestCase):
|
|
88
|
-
def test(self):
|
|
89
|
-
self.outbox_at_start = self.sms_outbox()
|
|
90
|
-
|
|
91
|
-
MemorySMSBackend.outbox.append(('+41791234567', 'Left over'))
|
|
92
|
-
test = ATest('test')
|
|
93
|
-
result = unittest.TestResult()
|
|
94
|
-
test(result)
|
|
95
|
-
self.assertTrue(result.wasSuccessful(), result.errors)
|
|
96
|
-
self.assertEqual(test.outbox_at_start, [])
|
|
97
|
-
|
|
98
|
-
@override_settings(
|
|
99
|
-
PFX_SMS_BACKEND='pfx.pfxcore.sms.backends.console.ConsoleSMSBackend')
|
|
100
|
-
def test_the_helpers_refuse_another_backend(self):
|
|
101
|
-
with self.assertRaises(AssertionError):
|
|
102
|
-
self.sms_outbox()
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
class TestXMLTestRunner(SimpleTestCase):
|
|
106
|
-
|
|
107
|
-
def test_the_xml_runner_replaces_the_backends(self):
|
|
108
|
-
"""The XML variant is the same runner: same backends, same parallel
|
|
109
|
-
workers, and the XML reports."""
|
|
110
|
-
from xmlrunner.extra.djangotestrunner import XMLTestRunner as Base
|
|
111
|
-
|
|
112
|
-
from pfx.pfxcore.xml_test_runner import XMLTestRunner
|
|
113
|
-
|
|
114
|
-
self.assertTrue(issubclass(XMLTestRunner, Base))
|
|
115
|
-
self.assertTrue(issubclass(XMLTestRunner, MemoryBackendsRunnerMixin))
|
|
116
|
-
self.assertIs(
|
|
117
|
-
XMLTestRunner.parallel_test_suite, MemoryBackendsParallelTestSuite)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|