django-pfx 1.7.6.dev23__tar.gz → 1.7.6.dev25__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.
Files changed (326) hide show
  1. {django_pfx-1.7.6.dev23 → django_pfx-1.7.6.dev25}/PKG-INFO +1 -1
  2. {django_pfx-1.7.6.dev23 → django_pfx-1.7.6.dev25}/django_pfx.egg-info/PKG-INFO +1 -1
  3. {django_pfx-1.7.6.dev23 → django_pfx-1.7.6.dev25}/django_pfx.egg-info/SOURCES.txt +6 -0
  4. {django_pfx-1.7.6.dev23 → django_pfx-1.7.6.dev25}/django_pfx.egg-info/scm_file_list.json +6 -0
  5. {django_pfx-1.7.6.dev23 → django_pfx-1.7.6.dev25}/django_pfx.egg-info/scm_version.json +2 -2
  6. {django_pfx-1.7.6.dev23 → django_pfx-1.7.6.dev25}/doc/source/authentication.md +2 -0
  7. {django_pfx-1.7.6.dev23 → django_pfx-1.7.6.dev25}/doc/source/testing.md +90 -0
  8. {django_pfx-1.7.6.dev23 → django_pfx-1.7.6.dev25}/pfx/emails/email_backend/__init__.py +1 -0
  9. django_pfx-1.7.6.dev25/pfx/emails/email_backend/memory_email_backend.py +14 -0
  10. django_pfx-1.7.6.dev25/pfx/emails/test.py +40 -0
  11. django_pfx-1.7.6.dev25/pfx/pfxcore/sms/backends/memory.py +15 -0
  12. {django_pfx-1.7.6.dev23 → django_pfx-1.7.6.dev25}/pfx/pfxcore/test.py +99 -0
  13. django_pfx-1.7.6.dev25/pfx/pfxcore/xml_test_runner.py +8 -0
  14. django_pfx-1.7.6.dev25/tests/tests/test_emails_test_tools.py +60 -0
  15. django_pfx-1.7.6.dev25/tests/tests/test_test_runner.py +117 -0
  16. {django_pfx-1.7.6.dev23 → django_pfx-1.7.6.dev25}/.gitignore +0 -0
  17. {django_pfx-1.7.6.dev23 → django_pfx-1.7.6.dev25}/.gitlab-ci.yml +0 -0
  18. {django_pfx-1.7.6.dev23 → django_pfx-1.7.6.dev25}/.pre-commit-config.yaml +0 -0
  19. {django_pfx-1.7.6.dev23 → django_pfx-1.7.6.dev25}/LICENSE +0 -0
  20. {django_pfx-1.7.6.dev23 → django_pfx-1.7.6.dev25}/MANIFEST.in +0 -0
  21. {django_pfx-1.7.6.dev23 → django_pfx-1.7.6.dev25}/README.md +0 -0
  22. {django_pfx-1.7.6.dev23 → django_pfx-1.7.6.dev25}/django_pfx.egg-info/dependency_links.txt +0 -0
  23. {django_pfx-1.7.6.dev23 → django_pfx-1.7.6.dev25}/django_pfx.egg-info/requires.txt +0 -0
  24. {django_pfx-1.7.6.dev23 → django_pfx-1.7.6.dev25}/django_pfx.egg-info/top_level.txt +0 -0
  25. {django_pfx-1.7.6.dev23 → django_pfx-1.7.6.dev25}/doc/Makefile +0 -0
  26. {django_pfx-1.7.6.dev23 → django_pfx-1.7.6.dev25}/doc/conf.py +0 -0
  27. {django_pfx-1.7.6.dev23 → django_pfx-1.7.6.dev25}/doc/index.rst +0 -0
  28. {django_pfx-1.7.6.dev23 → django_pfx-1.7.6.dev25}/doc/source/api.views.rst +0 -0
  29. {django_pfx-1.7.6.dev23 → django_pfx-1.7.6.dev25}/doc/source/decorator.md +0 -0
  30. {django_pfx-1.7.6.dev23 → django_pfx-1.7.6.dev25}/doc/source/generate_openapi.md +0 -0
  31. {django_pfx-1.7.6.dev23 → django_pfx-1.7.6.dev25}/doc/source/getting_started.md +0 -0
  32. {django_pfx-1.7.6.dev23 → django_pfx-1.7.6.dev25}/doc/source/internationalisation.md +0 -0
  33. {django_pfx-1.7.6.dev23 → django_pfx-1.7.6.dev25}/doc/source/model.md +0 -0
  34. {django_pfx-1.7.6.dev23 → django_pfx-1.7.6.dev25}/doc/source/pfx_views.md +0 -0
  35. {django_pfx-1.7.6.dev23 → django_pfx-1.7.6.dev25}/doc/source/profiling.md +0 -0
  36. {django_pfx-1.7.6.dev23 → django_pfx-1.7.6.dev25}/doc/source/settings.md +0 -0
  37. {django_pfx-1.7.6.dev23 → django_pfx-1.7.6.dev25}/doc/source/user_jobs.md +0 -0
  38. {django_pfx-1.7.6.dev23 → django_pfx-1.7.6.dev25}/img/pfx.png +0 -0
  39. {django_pfx-1.7.6.dev23 → django_pfx-1.7.6.dev25}/img/pfx.svg +0 -0
  40. {django_pfx-1.7.6.dev23 → django_pfx-1.7.6.dev25}/make_messages +0 -0
  41. {django_pfx-1.7.6.dev23 → django_pfx-1.7.6.dev25}/manage.py +0 -0
  42. {django_pfx-1.7.6.dev23 → django_pfx-1.7.6.dev25}/pfx/__init__.py +0 -0
  43. {django_pfx-1.7.6.dev23 → django_pfx-1.7.6.dev25}/pfx/emails/__init__.py +0 -0
  44. {django_pfx-1.7.6.dev23 → django_pfx-1.7.6.dev25}/pfx/emails/apps.py +0 -0
  45. {django_pfx-1.7.6.dev23 → django_pfx-1.7.6.dev25}/pfx/emails/email_backend/q_mailjet_email_backend.py +0 -0
  46. {django_pfx-1.7.6.dev23 → django_pfx-1.7.6.dev25}/pfx/emails/exceptions.py +0 -0
  47. {django_pfx-1.7.6.dev23 → django_pfx-1.7.6.dev25}/pfx/emails/locale/fr/LC_MESSAGES/django.mo +0 -0
  48. {django_pfx-1.7.6.dev23 → django_pfx-1.7.6.dev25}/pfx/emails/locale/fr/LC_MESSAGES/django.po +0 -0
  49. {django_pfx-1.7.6.dev23 → django_pfx-1.7.6.dev25}/pfx/emails/mailjet/__init__.py +0 -0
  50. {django_pfx-1.7.6.dev23 → django_pfx-1.7.6.dev25}/pfx/emails/mailjet/mailjet.py +0 -0
  51. {django_pfx-1.7.6.dev23 → django_pfx-1.7.6.dev25}/pfx/emails/mailjet/message_result.py +0 -0
  52. {django_pfx-1.7.6.dev23 → django_pfx-1.7.6.dev25}/pfx/emails/migrations/0001_initial.py +0 -0
  53. {django_pfx-1.7.6.dev23 → django_pfx-1.7.6.dev25}/pfx/emails/migrations/0002_alter_email_headers.py +0 -0
  54. {django_pfx-1.7.6.dev23 → django_pfx-1.7.6.dev25}/pfx/emails/migrations/0003_alter_email_options.py +0 -0
  55. {django_pfx-1.7.6.dev23 → django_pfx-1.7.6.dev25}/pfx/emails/migrations/0004_alter_email_mail_from.py +0 -0
  56. {django_pfx-1.7.6.dev23 → django_pfx-1.7.6.dev25}/pfx/emails/migrations/0005_lowercase_verbose_names.py +0 -0
  57. {django_pfx-1.7.6.dev23 → django_pfx-1.7.6.dev25}/pfx/emails/migrations/__init__.py +0 -0
  58. {django_pfx-1.7.6.dev23 → django_pfx-1.7.6.dev25}/pfx/emails/models/__init__.py +0 -0
  59. {django_pfx-1.7.6.dev23 → django_pfx-1.7.6.dev25}/pfx/emails/models/email.py +0 -0
  60. {django_pfx-1.7.6.dev23 → django_pfx-1.7.6.dev25}/pfx/emails/providers/__init__.py +0 -0
  61. {django_pfx-1.7.6.dev23 → django_pfx-1.7.6.dev25}/pfx/emails/providers/email_django_q_executor.py +0 -0
  62. {django_pfx-1.7.6.dev23 → django_pfx-1.7.6.dev25}/pfx/emails/shortcuts.py +0 -0
  63. {django_pfx-1.7.6.dev23 → django_pfx-1.7.6.dev25}/pfx/emails/tasks.py +0 -0
  64. {django_pfx-1.7.6.dev23 → django_pfx-1.7.6.dev25}/pfx/emails/templates/emails/base_template.html +0 -0
  65. {django_pfx-1.7.6.dev23 → django_pfx-1.7.6.dev25}/pfx/emails/templates/emails/base_template.txt +0 -0
  66. {django_pfx-1.7.6.dev23 → django_pfx-1.7.6.dev25}/pfx/emails/templates/emails/base_template_subject.txt +0 -0
  67. {django_pfx-1.7.6.dev23 → django_pfx-1.7.6.dev25}/pfx/emails/urls.py +0 -0
  68. {django_pfx-1.7.6.dev23 → django_pfx-1.7.6.dev25}/pfx/emails/views/__init__.py +0 -0
  69. {django_pfx-1.7.6.dev23 → django_pfx-1.7.6.dev25}/pfx/emails/views/email_rest_view.py +0 -0
  70. {django_pfx-1.7.6.dev23 → django_pfx-1.7.6.dev25}/pfx/pfxcore/__init__.py +0 -0
  71. {django_pfx-1.7.6.dev23 → django_pfx-1.7.6.dev25}/pfx/pfxcore/apidoc/__init__.py +0 -0
  72. {django_pfx-1.7.6.dev23 → django_pfx-1.7.6.dev25}/pfx/pfxcore/apidoc/parameters.py +0 -0
  73. {django_pfx-1.7.6.dev23 → django_pfx-1.7.6.dev25}/pfx/pfxcore/apidoc/schema.py +0 -0
  74. {django_pfx-1.7.6.dev23 → django_pfx-1.7.6.dev25}/pfx/pfxcore/apidoc/tags.py +0 -0
  75. {django_pfx-1.7.6.dev23 → django_pfx-1.7.6.dev25}/pfx/pfxcore/apps.py +0 -0
  76. {django_pfx-1.7.6.dev23 → django_pfx-1.7.6.dev25}/pfx/pfxcore/decorator/__init__.py +0 -0
  77. {django_pfx-1.7.6.dev23 → django_pfx-1.7.6.dev25}/pfx/pfxcore/decorator/rest.py +0 -0
  78. {django_pfx-1.7.6.dev23 → django_pfx-1.7.6.dev25}/pfx/pfxcore/default_settings.py +0 -0
  79. {django_pfx-1.7.6.dev23 → django_pfx-1.7.6.dev25}/pfx/pfxcore/exceptions.py +0 -0
  80. {django_pfx-1.7.6.dev23 → django_pfx-1.7.6.dev25}/pfx/pfxcore/fields/__init__.py +0 -0
  81. {django_pfx-1.7.6.dev23 → django_pfx-1.7.6.dev25}/pfx/pfxcore/fields/decimal_field.py +0 -0
  82. {django_pfx-1.7.6.dev23 → django_pfx-1.7.6.dev25}/pfx/pfxcore/fields/media_field.py +0 -0
  83. {django_pfx-1.7.6.dev23 → django_pfx-1.7.6.dev25}/pfx/pfxcore/fields/minutes_duration_field.py +0 -0
  84. {django_pfx-1.7.6.dev23 → django_pfx-1.7.6.dev25}/pfx/pfxcore/fields/rich_text_field.py +0 -0
  85. {django_pfx-1.7.6.dev23 → django_pfx-1.7.6.dev25}/pfx/pfxcore/http/__init__.py +0 -0
  86. {django_pfx-1.7.6.dev23 → django_pfx-1.7.6.dev25}/pfx/pfxcore/http/json_response.py +0 -0
  87. {django_pfx-1.7.6.dev23 → django_pfx-1.7.6.dev25}/pfx/pfxcore/locale/fr/LC_MESSAGES/django.mo +0 -0
  88. {django_pfx-1.7.6.dev23 → django_pfx-1.7.6.dev25}/pfx/pfxcore/locale/fr/LC_MESSAGES/django.po +0 -0
  89. {django_pfx-1.7.6.dev23 → django_pfx-1.7.6.dev25}/pfx/pfxcore/management/__init__.py +0 -0
  90. {django_pfx-1.7.6.dev23 → django_pfx-1.7.6.dev25}/pfx/pfxcore/management/commands/__init__.py +0 -0
  91. {django_pfx-1.7.6.dev23 → django_pfx-1.7.6.dev25}/pfx/pfxcore/management/commands/makeapidoc.py +0 -0
  92. {django_pfx-1.7.6.dev23 → django_pfx-1.7.6.dev25}/pfx/pfxcore/management/commands/profile.py +0 -0
  93. {django_pfx-1.7.6.dev23 → django_pfx-1.7.6.dev25}/pfx/pfxcore/mfa/__init__.py +0 -0
  94. {django_pfx-1.7.6.dev23 → django_pfx-1.7.6.dev25}/pfx/pfxcore/mfa/authenticator.py +0 -0
  95. {django_pfx-1.7.6.dev23 → django_pfx-1.7.6.dev25}/pfx/pfxcore/mfa/base.py +0 -0
  96. {django_pfx-1.7.6.dev23 → django_pfx-1.7.6.dev25}/pfx/pfxcore/mfa/email.py +0 -0
  97. {django_pfx-1.7.6.dev23 → django_pfx-1.7.6.dev25}/pfx/pfxcore/mfa/legacy.py +0 -0
  98. {django_pfx-1.7.6.dev23 → django_pfx-1.7.6.dev25}/pfx/pfxcore/mfa/sms.py +0 -0
  99. {django_pfx-1.7.6.dev23 → django_pfx-1.7.6.dev25}/pfx/pfxcore/middleware/__init__.py +0 -0
  100. {django_pfx-1.7.6.dev23 → django_pfx-1.7.6.dev25}/pfx/pfxcore/middleware/authentication.py +0 -0
  101. {django_pfx-1.7.6.dev23 → django_pfx-1.7.6.dev25}/pfx/pfxcore/middleware/locale.py +0 -0
  102. {django_pfx-1.7.6.dev23 → django_pfx-1.7.6.dev25}/pfx/pfxcore/middleware/profiling.py +0 -0
  103. {django_pfx-1.7.6.dev23 → django_pfx-1.7.6.dev25}/pfx/pfxcore/middleware/request_too_big_middleware.py +0 -0
  104. {django_pfx-1.7.6.dev23 → django_pfx-1.7.6.dev25}/pfx/pfxcore/middleware/ws_cookie_authentication_middleware.py +0 -0
  105. {django_pfx-1.7.6.dev23 → django_pfx-1.7.6.dev25}/pfx/pfxcore/migrations/0001_initial.py +0 -0
  106. {django_pfx-1.7.6.dev23 → django_pfx-1.7.6.dev25}/pfx/pfxcore/migrations/0002_pfxpermissionsuser.py +0 -0
  107. {django_pfx-1.7.6.dev23 → django_pfx-1.7.6.dev25}/pfx/pfxcore/migrations/0003_delete_pfxpermissionsuser.py +0 -0
  108. {django_pfx-1.7.6.dev23 → django_pfx-1.7.6.dev25}/pfx/pfxcore/migrations/0004_alter_loginban_failed_counter_and_more.py +0 -0
  109. {django_pfx-1.7.6.dev23 → django_pfx-1.7.6.dev25}/pfx/pfxcore/migrations/0005_mfadevice_mfadevice_mfa_device_unique_backend.py +0 -0
  110. {django_pfx-1.7.6.dev23 → django_pfx-1.7.6.dev25}/pfx/pfxcore/migrations/0006_alter_pfxuser_managers.py +0 -0
  111. {django_pfx-1.7.6.dev23 → django_pfx-1.7.6.dev25}/pfx/pfxcore/migrations/0007_alter_pfxuser_options.py +0 -0
  112. {django_pfx-1.7.6.dev23 → django_pfx-1.7.6.dev25}/pfx/pfxcore/migrations/0008_lowercase_verbose_names.py +0 -0
  113. {django_pfx-1.7.6.dev23 → django_pfx-1.7.6.dev25}/pfx/pfxcore/migrations/__init__.py +0 -0
  114. {django_pfx-1.7.6.dev23 → django_pfx-1.7.6.dev25}/pfx/pfxcore/migrations/operations/__init__.py +0 -0
  115. {django_pfx-1.7.6.dev23 → django_pfx-1.7.6.dev25}/pfx/pfxcore/migrations/operations/permissions.py +0 -0
  116. {django_pfx-1.7.6.dev23 → django_pfx-1.7.6.dev25}/pfx/pfxcore/models/__init__.py +0 -0
  117. {django_pfx-1.7.6.dev23 → django_pfx-1.7.6.dev25}/pfx/pfxcore/models/abstract_pfx_base_user.py +0 -0
  118. {django_pfx-1.7.6.dev23 → django_pfx-1.7.6.dev25}/pfx/pfxcore/models/attachment_mixin.py +0 -0
  119. {django_pfx-1.7.6.dev23 → django_pfx-1.7.6.dev25}/pfx/pfxcore/models/cache_mixins.py +0 -0
  120. {django_pfx-1.7.6.dev23 → django_pfx-1.7.6.dev25}/pfx/pfxcore/models/login_ban.py +0 -0
  121. {django_pfx-1.7.6.dev23 → django_pfx-1.7.6.dev25}/pfx/pfxcore/models/mfa_device.py +0 -0
  122. {django_pfx-1.7.6.dev23 → django_pfx-1.7.6.dev25}/pfx/pfxcore/models/mfa_user_mixin.py +0 -0
  123. {django_pfx-1.7.6.dev23 → django_pfx-1.7.6.dev25}/pfx/pfxcore/models/not_null_fields.py +0 -0
  124. {django_pfx-1.7.6.dev23 → django_pfx-1.7.6.dev25}/pfx/pfxcore/models/ordered_model_mixin.py +0 -0
  125. {django_pfx-1.7.6.dev23 → django_pfx-1.7.6.dev25}/pfx/pfxcore/models/pfx_models.py +0 -0
  126. {django_pfx-1.7.6.dev23 → django_pfx-1.7.6.dev25}/pfx/pfxcore/models/pfx_user.py +0 -0
  127. {django_pfx-1.7.6.dev23 → django_pfx-1.7.6.dev25}/pfx/pfxcore/models/user_filtered_queryset_mixin.py +0 -0
  128. {django_pfx-1.7.6.dev23 → django_pfx-1.7.6.dev25}/pfx/pfxcore/serializers/__init__.py +0 -0
  129. {django_pfx-1.7.6.dev23 → django_pfx-1.7.6.dev25}/pfx/pfxcore/serializers/json.py +0 -0
  130. {django_pfx-1.7.6.dev23 → django_pfx-1.7.6.dev25}/pfx/pfxcore/settings.py +0 -0
  131. {django_pfx-1.7.6.dev23 → django_pfx-1.7.6.dev25}/pfx/pfxcore/shortcuts.py +0 -0
  132. {django_pfx-1.7.6.dev23 → django_pfx-1.7.6.dev25}/pfx/pfxcore/sms/__init__.py +0 -0
  133. {django_pfx-1.7.6.dev23 → django_pfx-1.7.6.dev25}/pfx/pfxcore/sms/backends/__init__.py +0 -0
  134. {django_pfx-1.7.6.dev23 → django_pfx-1.7.6.dev25}/pfx/pfxcore/sms/backends/base.py +0 -0
  135. {django_pfx-1.7.6.dev23 → django_pfx-1.7.6.dev25}/pfx/pfxcore/sms/backends/console.py +0 -0
  136. {django_pfx-1.7.6.dev23 → django_pfx-1.7.6.dev25}/pfx/pfxcore/storage/__init__.py +0 -0
  137. {django_pfx-1.7.6.dev23 → django_pfx-1.7.6.dev25}/pfx/pfxcore/storage/exceptions.py +0 -0
  138. {django_pfx-1.7.6.dev23 → django_pfx-1.7.6.dev25}/pfx/pfxcore/storage/local_storage.py +0 -0
  139. {django_pfx-1.7.6.dev23 → django_pfx-1.7.6.dev25}/pfx/pfxcore/storage/s3_storage.py +0 -0
  140. {django_pfx-1.7.6.dev23 → django_pfx-1.7.6.dev25}/pfx/pfxcore/templates/registration/otp_code_email.txt +0 -0
  141. {django_pfx-1.7.6.dev23 → django_pfx-1.7.6.dev25}/pfx/pfxcore/templates/registration/otp_code_subject.txt +0 -0
  142. {django_pfx-1.7.6.dev23 → django_pfx-1.7.6.dev25}/pfx/pfxcore/templates/registration/password_reset_email.txt +0 -0
  143. {django_pfx-1.7.6.dev23 → django_pfx-1.7.6.dev25}/pfx/pfxcore/templates/registration/password_reset_subject.txt +0 -0
  144. {django_pfx-1.7.6.dev23 → django_pfx-1.7.6.dev25}/pfx/pfxcore/templates/registration/welcome_email.txt +0 -0
  145. {django_pfx-1.7.6.dev23 → django_pfx-1.7.6.dev25}/pfx/pfxcore/templates/registration/welcome_subject.txt +0 -0
  146. {django_pfx-1.7.6.dev23 → django_pfx-1.7.6.dev25}/pfx/pfxcore/urls.py +0 -0
  147. {django_pfx-1.7.6.dev23 → django_pfx-1.7.6.dev25}/pfx/pfxcore/version_check.py +0 -0
  148. {django_pfx-1.7.6.dev23 → django_pfx-1.7.6.dev25}/pfx/pfxcore/views/__init__.py +0 -0
  149. {django_pfx-1.7.6.dev23 → django_pfx-1.7.6.dev25}/pfx/pfxcore/views/authentication_views.py +0 -0
  150. {django_pfx-1.7.6.dev23 → django_pfx-1.7.6.dev25}/pfx/pfxcore/views/fields.py +0 -0
  151. {django_pfx-1.7.6.dev23 → django_pfx-1.7.6.dev25}/pfx/pfxcore/views/filters_views.py +0 -0
  152. {django_pfx-1.7.6.dev23 → django_pfx-1.7.6.dev25}/pfx/pfxcore/views/locale_views.py +0 -0
  153. {django_pfx-1.7.6.dev23 → django_pfx-1.7.6.dev25}/pfx/pfxcore/views/media_rest_view_mixin.py +0 -0
  154. {django_pfx-1.7.6.dev23 → django_pfx-1.7.6.dev25}/pfx/pfxcore/views/mfa_authentication_views.py +0 -0
  155. {django_pfx-1.7.6.dev23 → django_pfx-1.7.6.dev25}/pfx/pfxcore/views/ordered_rest_view_mixin.py +0 -0
  156. {django_pfx-1.7.6.dev23 → django_pfx-1.7.6.dev25}/pfx/pfxcore/views/parameters/__init__.py +0 -0
  157. {django_pfx-1.7.6.dev23 → django_pfx-1.7.6.dev25}/pfx/pfxcore/views/parameters/date_format.py +0 -0
  158. {django_pfx-1.7.6.dev23 → django_pfx-1.7.6.dev25}/pfx/pfxcore/views/parameters/groups.py +0 -0
  159. {django_pfx-1.7.6.dev23 → django_pfx-1.7.6.dev25}/pfx/pfxcore/views/parameters/list_count.py +0 -0
  160. {django_pfx-1.7.6.dev23 → django_pfx-1.7.6.dev25}/pfx/pfxcore/views/parameters/list_items.py +0 -0
  161. {django_pfx-1.7.6.dev23 → django_pfx-1.7.6.dev25}/pfx/pfxcore/views/parameters/list_mode.py +0 -0
  162. {django_pfx-1.7.6.dev23 → django_pfx-1.7.6.dev25}/pfx/pfxcore/views/parameters/list_order.py +0 -0
  163. {django_pfx-1.7.6.dev23 → django_pfx-1.7.6.dev25}/pfx/pfxcore/views/parameters/list_search.py +0 -0
  164. {django_pfx-1.7.6.dev23 → django_pfx-1.7.6.dev25}/pfx/pfxcore/views/parameters/media_redirect.py +0 -0
  165. {django_pfx-1.7.6.dev23 → django_pfx-1.7.6.dev25}/pfx/pfxcore/views/parameters/meta_fields.py +0 -0
  166. {django_pfx-1.7.6.dev23 → django_pfx-1.7.6.dev25}/pfx/pfxcore/views/parameters/meta_filters.py +0 -0
  167. {django_pfx-1.7.6.dev23 → django_pfx-1.7.6.dev25}/pfx/pfxcore/views/parameters/meta_orders.py +0 -0
  168. {django_pfx-1.7.6.dev23 → django_pfx-1.7.6.dev25}/pfx/pfxcore/views/parameters/subset.py +0 -0
  169. {django_pfx-1.7.6.dev23 → django_pfx-1.7.6.dev25}/pfx/pfxcore/views/parameters/subset_limit.py +0 -0
  170. {django_pfx-1.7.6.dev23 → django_pfx-1.7.6.dev25}/pfx/pfxcore/views/parameters/subset_offset.py +0 -0
  171. {django_pfx-1.7.6.dev23 → django_pfx-1.7.6.dev25}/pfx/pfxcore/views/parameters/subset_page.py +0 -0
  172. {django_pfx-1.7.6.dev23 → django_pfx-1.7.6.dev25}/pfx/pfxcore/views/parameters/subset_page_size.py +0 -0
  173. {django_pfx-1.7.6.dev23 → django_pfx-1.7.6.dev25}/pfx/pfxcore/views/parameters/subset_page_subset.py +0 -0
  174. {django_pfx-1.7.6.dev23 → django_pfx-1.7.6.dev25}/pfx/pfxcore/views/rest_views.py +0 -0
  175. {django_pfx-1.7.6.dev23 → django_pfx-1.7.6.dev25}/pfx/settings/__init__.py +0 -0
  176. {django_pfx-1.7.6.dev23 → django_pfx-1.7.6.dev25}/pfx/settings/dev.py +0 -0
  177. {django_pfx-1.7.6.dev23 → django_pfx-1.7.6.dev25}/pfx/userjobs/__init__.py +0 -0
  178. {django_pfx-1.7.6.dev23 → django_pfx-1.7.6.dev25}/pfx/userjobs/apps.py +0 -0
  179. {django_pfx-1.7.6.dev23 → django_pfx-1.7.6.dev25}/pfx/userjobs/locale/fr/LC_MESSAGES/django.mo +0 -0
  180. {django_pfx-1.7.6.dev23 → django_pfx-1.7.6.dev25}/pfx/userjobs/locale/fr/LC_MESSAGES/django.po +0 -0
  181. {django_pfx-1.7.6.dev23 → django_pfx-1.7.6.dev25}/pfx/userjobs/migrations/0001_initial.py +0 -0
  182. {django_pfx-1.7.6.dev23 → django_pfx-1.7.6.dev25}/pfx/userjobs/migrations/0002_alter_userjob_options_alter_userjob_auto_queued_and_more.py +0 -0
  183. {django_pfx-1.7.6.dev23 → django_pfx-1.7.6.dev25}/pfx/userjobs/migrations/0003_clean_scheduled_tasks.py +0 -0
  184. {django_pfx-1.7.6.dev23 → django_pfx-1.7.6.dev25}/pfx/userjobs/migrations/0004_userjob_summary_url.py +0 -0
  185. {django_pfx-1.7.6.dev23 → django_pfx-1.7.6.dev25}/pfx/userjobs/migrations/0005_swappable_user_job.py +0 -0
  186. {django_pfx-1.7.6.dev23 → django_pfx-1.7.6.dev25}/pfx/userjobs/migrations/0006_lowercase_verbose_names.py +0 -0
  187. {django_pfx-1.7.6.dev23 → django_pfx-1.7.6.dev25}/pfx/userjobs/migrations/__init__.py +0 -0
  188. {django_pfx-1.7.6.dev23 → django_pfx-1.7.6.dev25}/pfx/userjobs/models/__init__.py +0 -0
  189. {django_pfx-1.7.6.dev23 → django_pfx-1.7.6.dev25}/pfx/userjobs/models/user_job.py +0 -0
  190. {django_pfx-1.7.6.dev23 → django_pfx-1.7.6.dev25}/pfx/userjobs/models/user_job_attachment.py +0 -0
  191. {django_pfx-1.7.6.dev23 → django_pfx-1.7.6.dev25}/pfx/userjobs/providers/__init__.py +0 -0
  192. {django_pfx-1.7.6.dev23 → django_pfx-1.7.6.dev25}/pfx/userjobs/providers/user_job_django_q_executor.py +0 -0
  193. {django_pfx-1.7.6.dev23 → django_pfx-1.7.6.dev25}/pfx/userjobs/providers/user_job_websocket_channel_provider.py +0 -0
  194. {django_pfx-1.7.6.dev23 → django_pfx-1.7.6.dev25}/pfx/userjobs/tasks.py +0 -0
  195. {django_pfx-1.7.6.dev23 → django_pfx-1.7.6.dev25}/pfx/userjobs/urls.py +0 -0
  196. {django_pfx-1.7.6.dev23 → django_pfx-1.7.6.dev25}/pfx/userjobs/views/__init__.py +0 -0
  197. {django_pfx-1.7.6.dev23 → django_pfx-1.7.6.dev25}/pfx/userjobs/views/user_job_attachment_view.py +0 -0
  198. {django_pfx-1.7.6.dev23 → django_pfx-1.7.6.dev25}/pfx/userjobs/views/user_job_view.py +0 -0
  199. {django_pfx-1.7.6.dev23 → django_pfx-1.7.6.dev25}/pfx/userjobs/views/user_websocket_consumer.py +0 -0
  200. {django_pfx-1.7.6.dev23 → django_pfx-1.7.6.dev25}/pyproject.toml +0 -0
  201. {django_pfx-1.7.6.dev23 → django_pfx-1.7.6.dev25}/requirements.txt +0 -0
  202. {django_pfx-1.7.6.dev23 → django_pfx-1.7.6.dev25}/serve-doc +0 -0
  203. {django_pfx-1.7.6.dev23 → django_pfx-1.7.6.dev25}/setup.cfg +0 -0
  204. {django_pfx-1.7.6.dev23 → django_pfx-1.7.6.dev25}/setup.py +0 -0
  205. {django_pfx-1.7.6.dev23 → django_pfx-1.7.6.dev25}/tests/__init__.py +0 -0
  206. {django_pfx-1.7.6.dev23 → django_pfx-1.7.6.dev25}/tests/apps.py +0 -0
  207. {django_pfx-1.7.6.dev23 → django_pfx-1.7.6.dev25}/tests/channel_group_send_calls.py +0 -0
  208. {django_pfx-1.7.6.dev23 → django_pfx-1.7.6.dev25}/tests/locale/fr/LC_MESSAGES/django.po +0 -0
  209. {django_pfx-1.7.6.dev23 → django_pfx-1.7.6.dev25}/tests/mfa_urls.py +0 -0
  210. {django_pfx-1.7.6.dev23 → django_pfx-1.7.6.dev25}/tests/migrations/0001_initial.py +0 -0
  211. {django_pfx-1.7.6.dev23 → django_pfx-1.7.6.dev25}/tests/migrations/__init__.py +0 -0
  212. {django_pfx-1.7.6.dev23 → django_pfx-1.7.6.dev25}/tests/models.py +0 -0
  213. {django_pfx-1.7.6.dev23 → django_pfx-1.7.6.dev25}/tests/settings/__init__.py +0 -0
  214. {django_pfx-1.7.6.dev23 → django_pfx-1.7.6.dev25}/tests/settings/ci.py +0 -0
  215. {django_pfx-1.7.6.dev23 → django_pfx-1.7.6.dev25}/tests/settings/common.py +0 -0
  216. {django_pfx-1.7.6.dev23 → django_pfx-1.7.6.dev25}/tests/settings/dev.py +0 -0
  217. {django_pfx-1.7.6.dev23 → django_pfx-1.7.6.dev25}/tests/settings/dev_custom_example.py +0 -0
  218. {django_pfx-1.7.6.dev23 → django_pfx-1.7.6.dev25}/tests/settings/dev_default.py +0 -0
  219. {django_pfx-1.7.6.dev23 → django_pfx-1.7.6.dev25}/tests/tests/__init__.py +0 -0
  220. {django_pfx-1.7.6.dev23 → django_pfx-1.7.6.dev25}/tests/tests/basic_api_errors.py +0 -0
  221. {django_pfx-1.7.6.dev23 → django_pfx-1.7.6.dev25}/tests/tests/basic_api_test.py +0 -0
  222. {django_pfx-1.7.6.dev23 → django_pfx-1.7.6.dev25}/tests/tests/mfa_test_utils.py +0 -0
  223. {django_pfx-1.7.6.dev23 → django_pfx-1.7.6.dev25}/tests/tests/test_api_doc.py +0 -0
  224. {django_pfx-1.7.6.dev23 → django_pfx-1.7.6.dev25}/tests/tests/test_api_doc_search.py +0 -0
  225. {django_pfx-1.7.6.dev23 → django_pfx-1.7.6.dev25}/tests/tests/test_auth_api.py +0 -0
  226. {django_pfx-1.7.6.dev23 → django_pfx-1.7.6.dev25}/tests/tests/test_body_mixin.py +0 -0
  227. {django_pfx-1.7.6.dev23 → django_pfx-1.7.6.dev25}/tests/tests/test_cache.py +0 -0
  228. {django_pfx-1.7.6.dev23 → django_pfx-1.7.6.dev25}/tests/tests/test_client.py +0 -0
  229. {django_pfx-1.7.6.dev23 → django_pfx-1.7.6.dev25}/tests/tests/test_emails_email_api.py +0 -0
  230. {django_pfx-1.7.6.dev23 → django_pfx-1.7.6.dev25}/tests/tests/test_emails_email_model.py +0 -0
  231. {django_pfx-1.7.6.dev23 → django_pfx-1.7.6.dev25}/tests/tests/test_emails_q_mailjet_email_backend.py +0 -0
  232. {django_pfx-1.7.6.dev23 → django_pfx-1.7.6.dev25}/tests/tests/test_emails_send_email_message.py +0 -0
  233. {django_pfx-1.7.6.dev23 → django_pfx-1.7.6.dev25}/tests/tests/test_fields_choices.py +0 -0
  234. {django_pfx-1.7.6.dev23 → django_pfx-1.7.6.dev25}/tests/tests/test_fields_date.py +0 -0
  235. {django_pfx-1.7.6.dev23 → django_pfx-1.7.6.dev25}/tests/tests/test_fields_decimal.py +0 -0
  236. {django_pfx-1.7.6.dev23 → django_pfx-1.7.6.dev25}/tests/tests/test_fields_help_text.py +0 -0
  237. {django_pfx-1.7.6.dev23 → django_pfx-1.7.6.dev25}/tests/tests/test_fields_minutes_duration.py +0 -0
  238. {django_pfx-1.7.6.dev23 → django_pfx-1.7.6.dev25}/tests/tests/test_fields_one2many.py +0 -0
  239. {django_pfx-1.7.6.dev23 → django_pfx-1.7.6.dev25}/tests/tests/test_fields_rich_text.py +0 -0
  240. {django_pfx-1.7.6.dev23 → django_pfx-1.7.6.dev25}/tests/tests/test_fields_time.py +0 -0
  241. {django_pfx-1.7.6.dev23 → django_pfx-1.7.6.dev25}/tests/tests/test_filters.py +0 -0
  242. {django_pfx-1.7.6.dev23 → django_pfx-1.7.6.dev25}/tests/tests/test_locale_api.py +0 -0
  243. {django_pfx-1.7.6.dev23 → django_pfx-1.7.6.dev25}/tests/tests/test_mfa_legacy.py +0 -0
  244. {django_pfx-1.7.6.dev23 → django_pfx-1.7.6.dev25}/tests/tests/test_mfa_login_api.py +0 -0
  245. {django_pfx-1.7.6.dev23 → django_pfx-1.7.6.dev25}/tests/tests/test_mfa_setup_api.py +0 -0
  246. {django_pfx-1.7.6.dev23 → django_pfx-1.7.6.dev25}/tests/tests/test_ordered_rest_view_mixin.py +0 -0
  247. {django_pfx-1.7.6.dev23 → django_pfx-1.7.6.dev25}/tests/tests/test_perm_tests.py +0 -0
  248. {django_pfx-1.7.6.dev23 → django_pfx-1.7.6.dev25}/tests/tests/test_permissions.py +0 -0
  249. {django_pfx-1.7.6.dev23 → django_pfx-1.7.6.dev25}/tests/tests/test_perms_api.py +0 -0
  250. {django_pfx-1.7.6.dev23 → django_pfx-1.7.6.dev25}/tests/tests/test_post_migrate_groups_update.py +0 -0
  251. {django_pfx-1.7.6.dev23 → django_pfx-1.7.6.dev25}/tests/tests/test_profiling_middleware.py +0 -0
  252. {django_pfx-1.7.6.dev23 → django_pfx-1.7.6.dev25}/tests/tests/test_settings.py +0 -0
  253. {django_pfx-1.7.6.dev23 → django_pfx-1.7.6.dev25}/tests/tests/test_shortcuts.py +0 -0
  254. {django_pfx-1.7.6.dev23 → django_pfx-1.7.6.dev25}/tests/tests/test_timezone_middleware.py +0 -0
  255. {django_pfx-1.7.6.dev23 → django_pfx-1.7.6.dev25}/tests/tests/test_tools.py +0 -0
  256. {django_pfx-1.7.6.dev23 → django_pfx-1.7.6.dev25}/tests/tests/test_user_job_api.py +0 -0
  257. {django_pfx-1.7.6.dev23 → django_pfx-1.7.6.dev25}/tests/tests/test_user_job_messages.py +0 -0
  258. {django_pfx-1.7.6.dev23 → django_pfx-1.7.6.dev25}/tests/tests/test_user_job_model.py +0 -0
  259. {django_pfx-1.7.6.dev23 → django_pfx-1.7.6.dev25}/tests/tests/test_user_queryset.py +0 -0
  260. {django_pfx-1.7.6.dev23 → django_pfx-1.7.6.dev25}/tests/tests/test_user_websocket.py +0 -0
  261. {django_pfx-1.7.6.dev23 → django_pfx-1.7.6.dev25}/tests/tests/test_verbose_names.py +0 -0
  262. {django_pfx-1.7.6.dev23 → django_pfx-1.7.6.dev25}/tests/tests/test_version_check.py +0 -0
  263. {django_pfx-1.7.6.dev23 → django_pfx-1.7.6.dev25}/tests/tests/test_view_decorators.py +0 -0
  264. {django_pfx-1.7.6.dev23 → django_pfx-1.7.6.dev25}/tests/tests/test_view_fields.py +0 -0
  265. {django_pfx-1.7.6.dev23 → django_pfx-1.7.6.dev25}/tests/tests/test_ws_cookie_middleware.py +0 -0
  266. {django_pfx-1.7.6.dev23 → django_pfx-1.7.6.dev25}/tests/urls.py +0 -0
  267. {django_pfx-1.7.6.dev23 → django_pfx-1.7.6.dev25}/tests/views.py +0 -0
  268. {django_pfx-1.7.6.dev23 → django_pfx-1.7.6.dev25}/tests_base_user/__init__.py +0 -0
  269. {django_pfx-1.7.6.dev23 → django_pfx-1.7.6.dev25}/tests_base_user/migrations/__init__.py +0 -0
  270. {django_pfx-1.7.6.dev23 → django_pfx-1.7.6.dev25}/tests_base_user/settings/__init__.py +0 -0
  271. {django_pfx-1.7.6.dev23 → django_pfx-1.7.6.dev25}/tests_base_user/settings/ci.py +0 -0
  272. {django_pfx-1.7.6.dev23 → django_pfx-1.7.6.dev25}/tests_base_user/settings/common.py +0 -0
  273. {django_pfx-1.7.6.dev23 → django_pfx-1.7.6.dev25}/tests_base_user/settings/dev.py +0 -0
  274. {django_pfx-1.7.6.dev23 → django_pfx-1.7.6.dev25}/tests_base_user/settings/dev_custom_example.py +0 -0
  275. {django_pfx-1.7.6.dev23 → django_pfx-1.7.6.dev25}/tests_base_user/settings/dev_default.py +0 -0
  276. {django_pfx-1.7.6.dev23 → django_pfx-1.7.6.dev25}/tests_base_user/tests/__init__.py +0 -0
  277. {django_pfx-1.7.6.dev23 → django_pfx-1.7.6.dev25}/tests_base_user/tests/test_api.py +0 -0
  278. {django_pfx-1.7.6.dev23 → django_pfx-1.7.6.dev25}/tests_base_user/tests/test_auth_api.py +0 -0
  279. {django_pfx-1.7.6.dev23 → django_pfx-1.7.6.dev25}/tests_base_user/urls.py +0 -0
  280. {django_pfx-1.7.6.dev23 → django_pfx-1.7.6.dev25}/tests_base_user/views.py +0 -0
  281. {django_pfx-1.7.6.dev23 → django_pfx-1.7.6.dev25}/tests_custom_user/__init__.py +0 -0
  282. {django_pfx-1.7.6.dev23 → django_pfx-1.7.6.dev25}/tests_custom_user/migrations/0001_initial.py +0 -0
  283. {django_pfx-1.7.6.dev23 → django_pfx-1.7.6.dev25}/tests_custom_user/migrations/__init__.py +0 -0
  284. {django_pfx-1.7.6.dev23 → django_pfx-1.7.6.dev25}/tests_custom_user/models.py +0 -0
  285. {django_pfx-1.7.6.dev23 → django_pfx-1.7.6.dev25}/tests_custom_user/settings/__init__.py +0 -0
  286. {django_pfx-1.7.6.dev23 → django_pfx-1.7.6.dev25}/tests_custom_user/settings/ci.py +0 -0
  287. {django_pfx-1.7.6.dev23 → django_pfx-1.7.6.dev25}/tests_custom_user/settings/common.py +0 -0
  288. {django_pfx-1.7.6.dev23 → django_pfx-1.7.6.dev25}/tests_custom_user/settings/dev.py +0 -0
  289. {django_pfx-1.7.6.dev23 → django_pfx-1.7.6.dev25}/tests_custom_user/settings/dev_custom_example.py +0 -0
  290. {django_pfx-1.7.6.dev23 → django_pfx-1.7.6.dev25}/tests_custom_user/settings/dev_default.py +0 -0
  291. {django_pfx-1.7.6.dev23 → django_pfx-1.7.6.dev25}/tests_custom_user/tests/__init__.py +0 -0
  292. {django_pfx-1.7.6.dev23 → django_pfx-1.7.6.dev25}/tests_custom_user/tests/test_api.py +0 -0
  293. {django_pfx-1.7.6.dev23 → django_pfx-1.7.6.dev25}/tests_custom_user/tests/test_auth_api.py +0 -0
  294. {django_pfx-1.7.6.dev23 → django_pfx-1.7.6.dev25}/tests_custom_user/urls.py +0 -0
  295. {django_pfx-1.7.6.dev23 → django_pfx-1.7.6.dev25}/tests_custom_user/views.py +0 -0
  296. {django_pfx-1.7.6.dev23 → django_pfx-1.7.6.dev25}/tests_custom_user_job/__init__.py +0 -0
  297. {django_pfx-1.7.6.dev23 → django_pfx-1.7.6.dev25}/tests_custom_user_job/migrations/0001_initial.py +0 -0
  298. {django_pfx-1.7.6.dev23 → django_pfx-1.7.6.dev25}/tests_custom_user_job/migrations/0002_lowercase_verbose_names.py +0 -0
  299. {django_pfx-1.7.6.dev23 → django_pfx-1.7.6.dev25}/tests_custom_user_job/migrations/__init__.py +0 -0
  300. {django_pfx-1.7.6.dev23 → django_pfx-1.7.6.dev25}/tests_custom_user_job/models.py +0 -0
  301. {django_pfx-1.7.6.dev23 → django_pfx-1.7.6.dev25}/tests_custom_user_job/providers.py +0 -0
  302. {django_pfx-1.7.6.dev23 → django_pfx-1.7.6.dev25}/tests_custom_user_job/scope.py +0 -0
  303. {django_pfx-1.7.6.dev23 → django_pfx-1.7.6.dev25}/tests_custom_user_job/settings/__init__.py +0 -0
  304. {django_pfx-1.7.6.dev23 → django_pfx-1.7.6.dev25}/tests_custom_user_job/settings/ci.py +0 -0
  305. {django_pfx-1.7.6.dev23 → django_pfx-1.7.6.dev25}/tests_custom_user_job/settings/common.py +0 -0
  306. {django_pfx-1.7.6.dev23 → django_pfx-1.7.6.dev25}/tests_custom_user_job/settings/dev.py +0 -0
  307. {django_pfx-1.7.6.dev23 → django_pfx-1.7.6.dev25}/tests_custom_user_job/settings/dev_custom_example.py +0 -0
  308. {django_pfx-1.7.6.dev23 → django_pfx-1.7.6.dev25}/tests_custom_user_job/settings/dev_default.py +0 -0
  309. {django_pfx-1.7.6.dev23 → django_pfx-1.7.6.dev25}/tests_custom_user_job/tests/__init__.py +0 -0
  310. {django_pfx-1.7.6.dev23 → django_pfx-1.7.6.dev25}/tests_custom_user_job/tests/test_user_job.py +0 -0
  311. {django_pfx-1.7.6.dev23 → django_pfx-1.7.6.dev25}/tests_custom_user_job/urls.py +0 -0
  312. {django_pfx-1.7.6.dev23 → django_pfx-1.7.6.dev25}/tests_user_email_login/__init__.py +0 -0
  313. {django_pfx-1.7.6.dev23 → django_pfx-1.7.6.dev25}/tests_user_email_login/migrations/0001_initial.py +0 -0
  314. {django_pfx-1.7.6.dev23 → django_pfx-1.7.6.dev25}/tests_user_email_login/migrations/__init__.py +0 -0
  315. {django_pfx-1.7.6.dev23 → django_pfx-1.7.6.dev25}/tests_user_email_login/models.py +0 -0
  316. {django_pfx-1.7.6.dev23 → django_pfx-1.7.6.dev25}/tests_user_email_login/settings/__init__.py +0 -0
  317. {django_pfx-1.7.6.dev23 → django_pfx-1.7.6.dev25}/tests_user_email_login/settings/ci.py +0 -0
  318. {django_pfx-1.7.6.dev23 → django_pfx-1.7.6.dev25}/tests_user_email_login/settings/common.py +0 -0
  319. {django_pfx-1.7.6.dev23 → django_pfx-1.7.6.dev25}/tests_user_email_login/settings/dev.py +0 -0
  320. {django_pfx-1.7.6.dev23 → django_pfx-1.7.6.dev25}/tests_user_email_login/settings/dev_custom_example.py +0 -0
  321. {django_pfx-1.7.6.dev23 → django_pfx-1.7.6.dev25}/tests_user_email_login/settings/dev_default.py +0 -0
  322. {django_pfx-1.7.6.dev23 → django_pfx-1.7.6.dev25}/tests_user_email_login/tests/__init__.py +0 -0
  323. {django_pfx-1.7.6.dev23 → django_pfx-1.7.6.dev25}/tests_user_email_login/tests/test_api.py +0 -0
  324. {django_pfx-1.7.6.dev23 → django_pfx-1.7.6.dev25}/tests_user_email_login/tests/test_auth_api.py +0 -0
  325. {django_pfx-1.7.6.dev23 → django_pfx-1.7.6.dev25}/tests_user_email_login/urls.py +0 -0
  326. {django_pfx-1.7.6.dev23 → django_pfx-1.7.6.dev25}/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.dev23
3
+ Version: 1.7.6.dev25
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.dev23
3
+ Version: 1.7.6.dev25
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
@@ -41,8 +41,10 @@ pfx/emails/apps.py
41
41
  pfx/emails/exceptions.py
42
42
  pfx/emails/shortcuts.py
43
43
  pfx/emails/tasks.py
44
+ pfx/emails/test.py
44
45
  pfx/emails/urls.py
45
46
  pfx/emails/email_backend/__init__.py
47
+ pfx/emails/email_backend/memory_email_backend.py
46
48
  pfx/emails/email_backend/q_mailjet_email_backend.py
47
49
  pfx/emails/locale/fr/LC_MESSAGES/django.mo
48
50
  pfx/emails/locale/fr/LC_MESSAGES/django.po
@@ -73,6 +75,7 @@ pfx/pfxcore/shortcuts.py
73
75
  pfx/pfxcore/test.py
74
76
  pfx/pfxcore/urls.py
75
77
  pfx/pfxcore/version_check.py
78
+ pfx/pfxcore/xml_test_runner.py
76
79
  pfx/pfxcore/apidoc/__init__.py
77
80
  pfx/pfxcore/apidoc/parameters.py
78
81
  pfx/pfxcore/apidoc/schema.py
@@ -133,6 +136,7 @@ pfx/pfxcore/sms/__init__.py
133
136
  pfx/pfxcore/sms/backends/__init__.py
134
137
  pfx/pfxcore/sms/backends/base.py
135
138
  pfx/pfxcore/sms/backends/console.py
139
+ pfx/pfxcore/sms/backends/memory.py
136
140
  pfx/pfxcore/storage/__init__.py
137
141
  pfx/pfxcore/storage/exceptions.py
138
142
  pfx/pfxcore/storage/local_storage.py
@@ -225,6 +229,7 @@ tests/tests/test_emails_email_api.py
225
229
  tests/tests/test_emails_email_model.py
226
230
  tests/tests/test_emails_q_mailjet_email_backend.py
227
231
  tests/tests/test_emails_send_email_message.py
232
+ tests/tests/test_emails_test_tools.py
228
233
  tests/tests/test_fields_choices.py
229
234
  tests/tests/test_fields_date.py
230
235
  tests/tests/test_fields_decimal.py
@@ -246,6 +251,7 @@ tests/tests/test_post_migrate_groups_update.py
246
251
  tests/tests/test_profiling_middleware.py
247
252
  tests/tests/test_settings.py
248
253
  tests/tests/test_shortcuts.py
254
+ tests/tests/test_test_runner.py
249
255
  tests/tests/test_timezone_middleware.py
250
256
  tests/tests/test_tools.py
251
257
  tests/tests/test_user_job_api.py
@@ -29,6 +29,7 @@
29
29
  "pfx/emails/__init__.py",
30
30
  "pfx/emails/apps.py",
31
31
  "pfx/emails/email_backend/__init__.py",
32
+ "pfx/emails/email_backend/memory_email_backend.py",
32
33
  "pfx/emails/email_backend/q_mailjet_email_backend.py",
33
34
  "pfx/emails/exceptions.py",
34
35
  "pfx/emails/locale/fr/LC_MESSAGES/django.po",
@@ -50,6 +51,7 @@
50
51
  "pfx/emails/templates/emails/base_template.html",
51
52
  "pfx/emails/templates/emails/base_template.txt",
52
53
  "pfx/emails/templates/emails/base_template_subject.txt",
54
+ "pfx/emails/test.py",
53
55
  "pfx/emails/urls.py",
54
56
  "pfx/emails/views/__init__.py",
55
57
  "pfx/emails/views/email_rest_view.py",
@@ -118,6 +120,7 @@
118
120
  "pfx/pfxcore/sms/backends/__init__.py",
119
121
  "pfx/pfxcore/sms/backends/base.py",
120
122
  "pfx/pfxcore/sms/backends/console.py",
123
+ "pfx/pfxcore/sms/backends/memory.py",
121
124
  "pfx/pfxcore/storage/__init__.py",
122
125
  "pfx/pfxcore/storage/exceptions.py",
123
126
  "pfx/pfxcore/storage/local_storage.py",
@@ -158,6 +161,7 @@
158
161
  "pfx/pfxcore/views/parameters/subset_page_size.py",
159
162
  "pfx/pfxcore/views/parameters/subset_page_subset.py",
160
163
  "pfx/pfxcore/views/rest_views.py",
164
+ "pfx/pfxcore/xml_test_runner.py",
161
165
  "pfx/settings/__init__.py",
162
166
  "pfx/settings/dev.py",
163
167
  "pfx/userjobs/__init__.py",
@@ -215,6 +219,7 @@
215
219
  "tests/tests/test_emails_email_model.py",
216
220
  "tests/tests/test_emails_q_mailjet_email_backend.py",
217
221
  "tests/tests/test_emails_send_email_message.py",
222
+ "tests/tests/test_emails_test_tools.py",
218
223
  "tests/tests/test_fields_choices.py",
219
224
  "tests/tests/test_fields_date.py",
220
225
  "tests/tests/test_fields_decimal.py",
@@ -236,6 +241,7 @@
236
241
  "tests/tests/test_profiling_middleware.py",
237
242
  "tests/tests/test_settings.py",
238
243
  "tests/tests/test_shortcuts.py",
244
+ "tests/tests/test_test_runner.py",
239
245
  "tests/tests/test_timezone_middleware.py",
240
246
  "tests/tests/test_tools.py",
241
247
  "tests/tests/test_user_job_api.py",
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "tag": "1.7.5",
3
- "distance": 23,
4
- "node": "g844445a9b16ab6666628821913df2ba5050b36d4",
3
+ "distance": 25,
4
+ "node": "gf7233748745b72df513a97dd8d75f55284f00bed",
5
5
  "dirty": false,
6
6
  "branch": "HEAD",
7
7
  "node_date": "2026-10-05"
@@ -183,6 +183,8 @@ removed.
183
183
  * `PFX_SMS_BACKEND`: Dotted path to the SMS backend class (optional, default
184
184
  `'pfx.pfxcore.sms.backends.console.ConsoleSMSBackend'`).
185
185
  Implement {class}`pfx.pfxcore.sms.backends.base.BaseSMSBackend` to integrate a real SMS provider.
186
+ In the tests, `pfx.pfxcore.test.TestRunner` replaces it with
187
+ {class}`pfx.pfxcore.sms.backends.memory.MemorySMSBackend` (see [Testing](testing.md)).
186
188
  * `PFX_TOKEN_OTP_VALIDITY`: Validity for OTP tokens (corresponds to the maximum time to enter
187
189
  an OTP code after logging in with a password) (optional, default `{'minutes': 15}`)
188
190
  * `PFX_HOTP_CODE_VALIDITY`: Validity of HOTP codes in minutes (used to send code by email or SMS) (optional, default `15`).
@@ -340,3 +340,93 @@ class BookPermsAPITest(TestPermsAssertMixin):
340
340
  post=403, put=403, delete=403),
341
341
  }
342
342
  ```
343
+
344
+ ## Test runner
345
+ `pfx.pfxcore.test.TestRunner` replaces the SMS and email backends for the
346
+ whole run, parallel workers included: nothing leaves the tests, whatever
347
+ the local settings contain, and what would be sent is kept for the
348
+ assertions.
349
+
350
+ ```python
351
+ TEST_RUNNER = 'pfx.pfxcore.test.TestRunner'
352
+ ```
353
+
354
+ - `PFX_SMS_BACKEND` becomes
355
+ `pfx.pfxcore.sms.backends.memory.MemorySMSBackend`, which collects the
356
+ SMS in memory.
357
+ - If `pfx.emails` is installed, `EMAIL_BACKEND` becomes
358
+ `pfx.emails.email_backend.MemoryEmailBackend` instead of the `locmem`
359
+ backend set by Django. `locmem` puts the emails in
360
+ `django.core.mail.outbox`: the email model is never built nor validated,
361
+ and a sending path that would create an invalid email goes unnoticed.
362
+ `MemoryEmailBackend` builds, validates and saves the email as
363
+ `QMailjetEmailBackend` does, with the `manual` send mode: nothing is
364
+ sent.
365
+
366
+ A project without `pfx.emails` can use the runner as well: only the SMS
367
+ backend is replaced, and the emails stay in Django's `locmem` outbox.
368
+
369
+ To use subclasses of these backends (to fill extra fields of your email
370
+ model, for instance):
371
+ ```python
372
+ PFX_TEST_SMS_BACKEND = 'myapp.tests.MyMemorySMSBackend'
373
+ PFX_TEST_EMAIL_BACKEND = 'myapp.tests.MyMemoryEmailBackend'
374
+ ```
375
+
376
+ For the XML reports of `unittest-xml-reporting` (to install in the
377
+ project), use the XML variant:
378
+ ```python
379
+ TEST_RUNNER = 'pfx.pfxcore.xml_test_runner.XMLTestRunner'
380
+ ```
381
+
382
+ With another runner, combine it with `MemoryBackendsRunnerMixin`:
383
+ ```python
384
+ from pfx.pfxcore.test import MemoryBackendsRunnerMixin
385
+
386
+
387
+ class MyTestRunner(MemoryBackendsRunnerMixin, OtherTestRunner):
388
+ pass
389
+ ```
390
+
391
+ A test class that needs another backend keeps its
392
+ `override_settings(EMAIL_BACKEND=…)` or `override_settings(PFX_SMS_BACKEND=…)`.
393
+
394
+ ## SMSTestMixin
395
+ Helpers to check the SMS of a test. The outbox is emptied before each
396
+ test. They fail if the SMS backend is not a `MemorySMSBackend`, rather
397
+ than reporting an empty outbox.
398
+
399
+ - `sms_outbox()`: the SMS sent so far, as `(to, message)` tuples.
400
+ - `clear_sms_outbox()`: forget the SMS sent so far.
401
+
402
+ ## EmailTestMixin
403
+ `pfx.emails.test.EmailTestMixin` gives helpers to check the emails of a
404
+ test. They fail if the emails are not saved in the email model (with
405
+ `locmem`, for instance), rather than reporting an empty outbox.
406
+
407
+ - `outbox()`: the emails created so far, in creation order.
408
+ - `clear_outbox()`: delete the emails created so far.
409
+ - `email_to(email)`: the addresses of the `To`, without the names.
410
+ - `get_email_html(email)`: the HTML part, fails if there is none.
411
+
412
+ Example :
413
+ ```python
414
+ from django.test import TestCase
415
+
416
+ from pfx.emails.test import EmailTestMixin
417
+ from pfx.pfxcore.test import SMSTestMixin
418
+
419
+
420
+ class ATestClass(EmailTestMixin, SMSTestMixin, TestCase):
421
+
422
+ def a_test(self):
423
+ # … something that sends an email to jrr@example.com
424
+ [email] = self.outbox()
425
+ self.assertEqual(self.email_to(email), ['jrr@example.com'])
426
+ self.assertIn('Welcome', self.get_email_html(email))
427
+
428
+ def another_test(self):
429
+ # … something that sends an SMS
430
+ [(to, message)] = self.sms_outbox()
431
+ self.assertEqual(to, '+41791234567')
432
+ ```
@@ -1 +1,2 @@
1
+ from .memory_email_backend import MemoryEmailBackend
1
2
  from .q_mailjet_email_backend import QMailjetEmailBackend
@@ -0,0 +1,14 @@
1
+ from .q_mailjet_email_backend import QMailjetEmailBackend
2
+
3
+
4
+ class MemoryEmailBackend(QMailjetEmailBackend):
5
+ """Create the emails as :class:`QMailjetEmailBackend` does, but never
6
+ send them.
7
+
8
+ The email model is built, validated and saved, so a sending path that
9
+ would create an invalid email fails. The ``manual`` send mode stops
10
+ there: nothing is queued nor sent to Mailjet."""
11
+
12
+ def create_mail(self, message):
13
+ message.send_mode = 'manual'
14
+ super().create_mail(message)
@@ -0,0 +1,40 @@
1
+ from django.conf import settings
2
+ from django.utils.module_loading import import_string
3
+
4
+ from pfx.emails.email_backend import QMailjetEmailBackend
5
+ from pfx.emails.models import get_email_model
6
+
7
+
8
+ class EmailTestMixin:
9
+ """Helpers to check the emails created by a test."""
10
+
11
+ @classmethod
12
+ def _check_email_backend(cls):
13
+ backend = import_string(settings.EMAIL_BACKEND)
14
+ if not issubclass(backend, QMailjetEmailBackend):
15
+ raise AssertionError(
16
+ f"EMAIL_BACKEND is {settings.EMAIL_BACKEND}, the emails are "
17
+ f"not saved in the email model. Run the tests with a runner "
18
+ f"using pfx.pfxcore.test.MemoryBackendsRunnerMixin.")
19
+
20
+ @classmethod
21
+ def outbox(cls):
22
+ """The emails created so far, in creation order."""
23
+ cls._check_email_backend()
24
+ return list(get_email_model().objects.order_by('pk'))
25
+
26
+ @classmethod
27
+ def clear_outbox(cls):
28
+ cls._check_email_backend()
29
+ get_email_model().objects.all().delete()
30
+
31
+ @classmethod
32
+ def email_to(cls, email):
33
+ """The addresses of the ``To`` of ``email``, without the names."""
34
+ return [r['Email'] for r in email.mail_to]
35
+
36
+ @classmethod
37
+ def get_email_html(cls, email):
38
+ if not email.html_part:
39
+ raise AssertionError("Email does not contain an HTML part.")
40
+ return email.html_part
@@ -0,0 +1,15 @@
1
+ from .base import BaseSMSBackend
2
+
3
+
4
+ class MemorySMSBackend(BaseSMSBackend):
5
+ """Collect the SMS in :attr:`outbox` instead of sending them.
6
+
7
+ A new backend instance is built for each send (see
8
+ :func:`pfx.pfxcore.sms.get_sms_backend`), so the outbox is a class
9
+ attribute."""
10
+
11
+ #: The sent SMS, as ``(to, message)`` tuples.
12
+ outbox = []
13
+
14
+ def send_sms(self, to, message):
15
+ self.outbox.append((to, message))
@@ -5,8 +5,16 @@ from functools import reduce
5
5
  from http.cookies import SimpleCookie
6
6
  from json import JSONDecodeError
7
7
 
8
+ from django.apps import apps
8
9
  from django.db import transaction
10
+ from django.test import override_settings
9
11
  from django.test.client import Client as DjangoClient
12
+ from django.test.runner import DiscoverRunner, ParallelTestSuite
13
+ from django.test.runner import _init_worker as django_init_worker
14
+ from django.utils.module_loading import import_string
15
+
16
+ from pfx.pfxcore.settings import settings
17
+ from pfx.pfxcore.sms.backends.memory import MemorySMSBackend
10
18
 
11
19
  logger = logging.getLogger(__name__)
12
20
 
@@ -322,3 +330,94 @@ class MockBoto3Client:
322
330
 
323
331
  def delete_object(self, *args, **kwargs):
324
332
  pass
333
+
334
+
335
+ def get_test_backends():
336
+ """The backends that replace the real ones during the tests."""
337
+ backends = dict(PFX_SMS_BACKEND=getattr(
338
+ settings, 'PFX_TEST_SMS_BACKEND',
339
+ 'pfx.pfxcore.sms.backends.memory.MemorySMSBackend'))
340
+ if apps.is_installed('pfx.emails'):
341
+ backends['EMAIL_BACKEND'] = getattr(
342
+ settings, 'PFX_TEST_EMAIL_BACKEND',
343
+ 'pfx.emails.email_backend.MemoryEmailBackend')
344
+ return backends
345
+
346
+
347
+ def _init_worker(*args, **kwargs):
348
+ # A spawned worker calls setup_test_environment() again, which puts
349
+ # locmem back.
350
+ django_init_worker(*args, **kwargs)
351
+ override_settings(**get_test_backends()).enable()
352
+
353
+
354
+ class MemoryBackendsParallelTestSuite(ParallelTestSuite):
355
+ init_worker = _init_worker
356
+
357
+
358
+ class MemoryBackendsRunnerMixin:
359
+ """Replace the SMS and email backends for the whole run.
360
+
361
+ Nothing leaves the tests, whatever the local settings contain, and
362
+ what would be sent is kept for the assertions (see
363
+ :class:`SMSTestMixin` and :class:`pfx.emails.test.EmailTestMixin`).
364
+
365
+ - ``PFX_SMS_BACKEND`` becomes ``PFX_TEST_SMS_BACKEND`` (default
366
+ :class:`pfx.pfxcore.sms.backends.memory.MemorySMSBackend`).
367
+ - If ``pfx.emails`` is installed, ``EMAIL_BACKEND`` becomes
368
+ ``PFX_TEST_EMAIL_BACKEND`` (default
369
+ :class:`pfx.emails.email_backend.MemoryEmailBackend`) instead of
370
+ the ``locmem`` backend set by Django: the email model is built and
371
+ validated, which ``locmem`` never does.
372
+
373
+ A test class decorated with ``override_settings`` keeps its own
374
+ backends."""
375
+
376
+ parallel_test_suite = MemoryBackendsParallelTestSuite
377
+
378
+ def setup_test_environment(self, **kwargs):
379
+ super().setup_test_environment(**kwargs)
380
+ self._test_backends = override_settings(**get_test_backends())
381
+ self._test_backends.enable()
382
+
383
+ def teardown_test_environment(self, **kwargs):
384
+ self._test_backends.disable()
385
+ super().teardown_test_environment(**kwargs)
386
+
387
+
388
+ class TestRunner(MemoryBackendsRunnerMixin, DiscoverRunner):
389
+ pass
390
+
391
+
392
+ class SMSTestMixin:
393
+ """Helpers to check the SMS sent by a test.
394
+
395
+ The outbox is emptied before each test, as Django does for
396
+ ``mail.outbox``."""
397
+
398
+ @classmethod
399
+ def _sms_backend(cls):
400
+ backend = import_string(settings.PFX_SMS_BACKEND)
401
+ if not issubclass(backend, MemorySMSBackend):
402
+ raise AssertionError(
403
+ f"PFX_SMS_BACKEND is {settings.PFX_SMS_BACKEND}, the SMS "
404
+ f"are not collected. Run the tests with a runner using "
405
+ f"pfx.pfxcore.test.MemoryBackendsRunnerMixin.")
406
+ return backend
407
+
408
+ # __call__ rather than _pre_setup: a classmethod in Django 5.2, an
409
+ # instance method in 4.2.
410
+ def __call__(self, *args, **kwargs):
411
+ backend = import_string(settings.PFX_SMS_BACKEND)
412
+ if issubclass(backend, MemorySMSBackend):
413
+ backend.outbox.clear()
414
+ return super().__call__(*args, **kwargs)
415
+
416
+ @classmethod
417
+ def sms_outbox(cls):
418
+ """The SMS sent so far, as ``(to, message)`` tuples."""
419
+ return list(cls._sms_backend().outbox)
420
+
421
+ @classmethod
422
+ def clear_sms_outbox(cls):
423
+ cls._sms_backend().outbox.clear()
@@ -0,0 +1,8 @@
1
+ from xmlrunner.extra.djangotestrunner import XMLTestRunner as BaseXMLTestRunner
2
+
3
+ from pfx.pfxcore.test import MemoryBackendsRunnerMixin
4
+
5
+
6
+ class XMLTestRunner(MemoryBackendsRunnerMixin, BaseXMLTestRunner):
7
+ """:class:`pfx.pfxcore.test.TestRunner` with the XML reports of
8
+ ``unittest-xml-reporting``, which must be installed."""
@@ -0,0 +1,60 @@
1
+ from django.core import mail
2
+ from django.core.exceptions import ValidationError
3
+ from django.core.mail import EmailMultiAlternatives, send_mail
4
+ from django.test import TestCase, override_settings
5
+
6
+ from pfx.emails.models import Email
7
+ from pfx.emails.test import EmailTestMixin
8
+
9
+ MEMORY_BACKEND = 'pfx.emails.email_backend.MemoryEmailBackend'
10
+
11
+
12
+ @override_settings(EMAIL_BACKEND=MEMORY_BACKEND)
13
+ class TestMemoryEmailBackend(EmailTestMixin, TestCase):
14
+ """The backend goes as far as the saved email, never further."""
15
+
16
+ def test_the_email_is_saved_and_not_sent(self):
17
+ send_mail(
18
+ 'Subject', 'Content', 'from@example.com', ['to@example.com'])
19
+ email = Email.objects.get()
20
+ self.assertEqual(email.send_mode, 'manual')
21
+ self.assertEqual(email.status, 'to_send')
22
+ self.assertEqual(mail.outbox, [])
23
+
24
+ def test_an_invalid_email_fails(self):
25
+ """What locmem cannot see: the email model refuses the message."""
26
+ with self.assertRaises(ValidationError):
27
+ send_mail(
28
+ 'Subject', 'Content', 'from@example.com', ['not-an-email'])
29
+
30
+ def test_outbox_helpers(self):
31
+ send_mail(
32
+ 'First', 'Content', 'from@example.com',
33
+ ['"Jean Dupont" <jean@example.com>', 'paul@example.com'])
34
+ message = EmailMultiAlternatives(
35
+ 'Second', 'Text', 'from@example.com', ['marie@example.com'])
36
+ message.attach_alternative('<p>HTML</p>', 'text/html')
37
+ message.send()
38
+
39
+ first, second = self.outbox()
40
+ self.assertEqual(
41
+ [first.subject, second.subject], ['First', 'Second'])
42
+ self.assertEqual(
43
+ self.email_to(first), ['jean@example.com', 'paul@example.com'])
44
+ self.assertEqual(self.get_email_html(second), '<p>HTML</p>')
45
+ with self.assertRaises(AssertionError):
46
+ self.get_email_html(first)
47
+
48
+ self.clear_outbox()
49
+ self.assertEqual(self.outbox(), [])
50
+
51
+
52
+ class TestEmailTestMixinWithLocmem(EmailTestMixin, TestCase):
53
+ """Without the runner, locmem is still there: the helpers refuse to
54
+ answer instead of reporting an empty outbox."""
55
+
56
+ def test_the_helpers_refuse_locmem(self):
57
+ with self.assertRaises(AssertionError):
58
+ self.outbox()
59
+ with self.assertRaises(AssertionError):
60
+ self.clear_outbox()
@@ -0,0 +1,117 @@
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)