wbcore 1.46.0__py2.py3-none-any.whl → 1.58.2__py2.py3-none-any.whl

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 (321) hide show
  1. wbcore/cache/decorators.py +5 -3
  2. wbcore/cache/registry.py +14 -7
  3. wbcore/configs/__init__.py +1 -0
  4. wbcore/configs/configs.py +5 -0
  5. wbcore/configs/decorators.py +1 -1
  6. wbcore/configurations/configurations/apps.py +3 -2
  7. wbcore/configurations/configurations/authentication.py +1 -1
  8. wbcore/configurations/configurations/base.py +1 -1
  9. wbcore/configurations/configurations/cache.py +1 -1
  10. wbcore/configurations/configurations/i18nl10n.py +2 -1
  11. wbcore/configurations/configurations/maintenance.py +1 -1
  12. wbcore/configurations/configurations/media.py +1 -1
  13. wbcore/configurations/configurations/middleware.py +1 -1
  14. wbcore/configurations/configurations/rest_framework.py +1 -1
  15. wbcore/configurations/configurations/static.py +3 -3
  16. wbcore/configurations/configurations/wbcore.py +1 -1
  17. wbcore/content_type/serializers.py +13 -5
  18. wbcore/content_type/utils.py +3 -3
  19. wbcore/content_type/viewsets.py +2 -2
  20. wbcore/contrib/agenda/filters/calendar_item.py +5 -4
  21. wbcore/contrib/agenda/locale/de/LC_MESSAGES/django.po +145 -52
  22. wbcore/contrib/agenda/locale/de/LC_MESSAGES/django.po.translated +236 -0
  23. wbcore/contrib/agenda/locale/en/LC_MESSAGES/django.po +200 -0
  24. wbcore/contrib/agenda/locale/fr/LC_MESSAGES/django.po +201 -0
  25. wbcore/contrib/agenda/viewsets/calendar_items.py +7 -7
  26. wbcore/contrib/agenda/viewsets/menu/calendar_items.py +0 -6
  27. wbcore/contrib/ai/exceptions.py +5 -5
  28. wbcore/contrib/ai/llm/config.py +76 -27
  29. wbcore/contrib/ai/llm/mixins.py +5 -8
  30. wbcore/contrib/ai/llm/utils.py +50 -26
  31. wbcore/contrib/authentication/admin.py +2 -2
  32. wbcore/contrib/authentication/factories/__init__.py +8 -1
  33. wbcore/contrib/authentication/factories/users.py +19 -0
  34. wbcore/contrib/authentication/filters.py +1 -2
  35. wbcore/contrib/authentication/locale/de/LC_MESSAGES/django.po +209 -187
  36. wbcore/contrib/authentication/locale/de/LC_MESSAGES/django.po.translated +634 -0
  37. wbcore/contrib/authentication/locale/en/LC_MESSAGES/django.po +590 -0
  38. wbcore/contrib/authentication/locale/fr/LC_MESSAGES/django.po +592 -0
  39. wbcore/contrib/authentication/models/users.py +3 -3
  40. wbcore/contrib/authentication/models/users_activities.py +1 -1
  41. wbcore/contrib/authentication/serializers/users.py +2 -2
  42. wbcore/contrib/authentication/tests/test_tokens.py +3 -3
  43. wbcore/contrib/authentication/tests/test_users.py +0 -1
  44. wbcore/contrib/authentication/urls.py +0 -4
  45. wbcore/contrib/authentication/viewsets/endpoints/user_activities.py +2 -11
  46. wbcore/contrib/authentication/viewsets/endpoints/users.py +0 -3
  47. wbcore/contrib/authentication/viewsets/user_activities.py +2 -1
  48. wbcore/contrib/authentication/viewsets/users.py +6 -4
  49. wbcore/contrib/color/models.py +2 -1
  50. wbcore/contrib/currency/factories.py +1 -1
  51. wbcore/contrib/currency/import_export/backends/fixerio/currency_fx_rates.py +3 -1
  52. wbcore/contrib/currency/models.py +30 -8
  53. wbcore/contrib/currency/serializers.py +5 -1
  54. wbcore/contrib/currency/tests/test_serializers.py +7 -3
  55. wbcore/contrib/currency/tests/test_viewsets.py +1 -1
  56. wbcore/contrib/currency/viewsets/currency.py +2 -2
  57. wbcore/contrib/currency/viewsets/endpoints/currency_fx_rates.py +0 -9
  58. wbcore/contrib/dataloader/tests/test/dataloaders/protocols.py +1 -2
  59. wbcore/contrib/dataloader/utils.py +2 -2
  60. wbcore/contrib/directory/factories/__init__.py +1 -1
  61. wbcore/contrib/directory/factories/entries.py +2 -1
  62. wbcore/contrib/directory/filters/entries.py +9 -0
  63. wbcore/contrib/directory/locale/de/LC_MESSAGES/django.po +728 -714
  64. wbcore/contrib/directory/locale/de/LC_MESSAGES/django.po.translated +1779 -0
  65. wbcore/contrib/directory/locale/en/LC_MESSAGES/django.po +1652 -0
  66. wbcore/contrib/directory/locale/fr/LC_MESSAGES/django.po +1654 -0
  67. wbcore/contrib/directory/migrations/0011_person_description_person_i18n.py +24 -0
  68. wbcore/contrib/directory/migrations/0012_alter_person_managers.py +20 -0
  69. wbcore/contrib/directory/migrations/0013_alter_clientmanagerrelationship_options.py +17 -0
  70. wbcore/contrib/directory/models/contacts.py +2 -2
  71. wbcore/contrib/directory/models/entries.py +31 -5
  72. wbcore/contrib/directory/models/relationships.py +31 -35
  73. wbcore/contrib/directory/permissions.py +6 -0
  74. wbcore/contrib/directory/serializers/companies.py +16 -8
  75. wbcore/contrib/directory/serializers/contacts.py +8 -8
  76. wbcore/contrib/directory/serializers/entries.py +26 -15
  77. wbcore/contrib/directory/serializers/entry_representations.py +4 -2
  78. wbcore/contrib/directory/serializers/persons.py +12 -10
  79. wbcore/contrib/directory/serializers/relationships.py +2 -2
  80. wbcore/contrib/directory/tests/conftest.py +2 -0
  81. wbcore/contrib/directory/tests/disable_signals.py +11 -1
  82. wbcore/contrib/directory/tests/signals.py +2 -2
  83. wbcore/contrib/directory/tests/test_models.py +88 -66
  84. wbcore/contrib/directory/tests/test_serializers.py +1 -1
  85. wbcore/contrib/directory/tests/test_viewsets.py +8 -8
  86. wbcore/contrib/directory/viewsets/buttons/__init__.py +1 -1
  87. wbcore/contrib/directory/viewsets/buttons/relationships.py +32 -0
  88. wbcore/contrib/directory/viewsets/contacts.py +6 -6
  89. wbcore/contrib/directory/viewsets/display/__init__.py +1 -1
  90. wbcore/contrib/directory/viewsets/display/contacts.py +1 -14
  91. wbcore/contrib/directory/viewsets/display/entries.py +68 -38
  92. wbcore/contrib/directory/viewsets/display/relationships.py +26 -50
  93. wbcore/contrib/directory/viewsets/endpoints/relationships.py +1 -26
  94. wbcore/contrib/directory/viewsets/entries.py +8 -6
  95. wbcore/contrib/directory/viewsets/previews/entries.py +3 -3
  96. wbcore/contrib/directory/viewsets/relationships.py +16 -2
  97. wbcore/contrib/directory/viewsets/titles/relationships.py +2 -3
  98. wbcore/contrib/documents/filters.py +0 -2
  99. wbcore/contrib/documents/locale/de/LC_MESSAGES/django.po +103 -94
  100. wbcore/contrib/documents/locale/de/LC_MESSAGES/django.po.translated +285 -0
  101. wbcore/contrib/documents/locale/en/LC_MESSAGES/django.po +271 -0
  102. wbcore/contrib/documents/locale/fr/LC_MESSAGES/django.po +270 -0
  103. wbcore/contrib/documents/tests/test_models.py +32 -28
  104. wbcore/contrib/documents/viewsets/endpoints/shareable_links.py +2 -21
  105. wbcore/contrib/dynamic_preferences/types.py +108 -0
  106. wbcore/contrib/dynamic_preferences/viewsets.py +27 -0
  107. wbcore/contrib/example_app/filters/event.py +3 -1
  108. wbcore/contrib/example_app/filters/match.py +1 -1
  109. wbcore/contrib/example_app/models.py +91 -22
  110. wbcore/contrib/example_app/serializers/person_team.py +4 -4
  111. wbcore/contrib/example_app/templates/example_app/embedded_view.html +19 -0
  112. wbcore/contrib/example_app/tests/e2e/test_teams.py +1 -1
  113. wbcore/contrib/example_app/tests/test_models/test_match.py +17 -7
  114. wbcore/contrib/example_app/urls.py +2 -0
  115. wbcore/contrib/example_app/views.py +7 -0
  116. wbcore/contrib/example_app/viewsets/displays/team.py +23 -4
  117. wbcore/contrib/example_app/viewsets/menu/menus.py +1 -1
  118. wbcore/contrib/example_app/viewsets/menus.py +1 -1
  119. wbcore/contrib/geography/tests/conftest.py +14 -0
  120. wbcore/contrib/geography/tests/test_models.py +23 -8
  121. wbcore/contrib/geography/tests/test_viewsets.py +96 -2
  122. wbcore/contrib/guardian/tests/test_model_mixins.py +3 -4
  123. wbcore/contrib/guardian/tests/test_tasks.py +9 -9
  124. wbcore/contrib/guardian/tests/test_viewsets.py +2 -2
  125. wbcore/contrib/guardian/viewsets/configs/__init__.py +1 -1
  126. wbcore/contrib/guardian/viewsets/configs/buttons.py +9 -0
  127. wbcore/contrib/guardian/viewsets/configs/endpoints.py +7 -0
  128. wbcore/contrib/guardian/viewsets/viewsets.py +2 -0
  129. wbcore/contrib/i18n/__init__.py +2 -0
  130. wbcore/contrib/i18n/buttons.py +33 -0
  131. wbcore/contrib/i18n/serializers/__init__.py +0 -0
  132. wbcore/contrib/i18n/serializers/fields.py +20 -0
  133. wbcore/contrib/i18n/serializers/mixins.py +13 -0
  134. wbcore/contrib/i18n/tests/conftest.py +11 -0
  135. wbcore/contrib/i18n/tests/test_viewsets.py +67 -0
  136. wbcore/contrib/i18n/translation.py +140 -0
  137. wbcore/contrib/i18n/viewsets.py +36 -0
  138. wbcore/contrib/icons/backends/default.py +1 -0
  139. wbcore/contrib/icons/backends/material.py +1 -0
  140. wbcore/contrib/icons/icons.py +5 -8
  141. wbcore/contrib/io/admin.py +1 -0
  142. wbcore/contrib/io/backends/mail.py +3 -2
  143. wbcore/contrib/io/backends/utils.py +14 -17
  144. wbcore/contrib/io/exceptions.py +8 -0
  145. wbcore/contrib/io/factories.py +1 -1
  146. wbcore/contrib/io/import_export/backends/mail.py +1 -0
  147. wbcore/contrib/io/import_export/backends/sftp.py +29 -20
  148. wbcore/contrib/io/import_export/backends/stream.py +2 -2
  149. wbcore/contrib/io/import_export/parsers/__init__.py +0 -0
  150. wbcore/contrib/io/import_export/parsers/base_csv.py +36 -0
  151. wbcore/contrib/io/import_export/parsers/resources.py +50 -0
  152. wbcore/contrib/io/imports.py +33 -25
  153. wbcore/contrib/io/locale/de/LC_MESSAGES/django.po +114 -22
  154. wbcore/contrib/io/locale/de/LC_MESSAGES/django.po.translated +103 -0
  155. wbcore/contrib/io/locale/en/LC_MESSAGES/django.po +138 -0
  156. wbcore/contrib/io/locale/fr/LC_MESSAGES/django.po +138 -0
  157. wbcore/contrib/io/migrations/0008_importsource_resource_kwargs.py +18 -0
  158. wbcore/contrib/io/models.py +65 -45
  159. wbcore/contrib/io/resources.py +0 -6
  160. wbcore/contrib/io/serializers.py +2 -2
  161. wbcore/contrib/io/signals.py +4 -0
  162. wbcore/contrib/io/tests/test_backends.py +19 -13
  163. wbcore/contrib/io/tests/test_exports.py +1 -1
  164. wbcore/contrib/io/tests/test_imports.py +1 -1
  165. wbcore/contrib/io/tests/test_models.py +47 -14
  166. wbcore/contrib/io/tests/test_viewsets.py +271 -0
  167. wbcore/contrib/io/viewset_mixins.py +41 -54
  168. wbcore/contrib/notifications/admin.py +1 -0
  169. wbcore/contrib/notifications/apps.py +2 -1
  170. wbcore/contrib/notifications/backends/abstract_backend.py +2 -4
  171. wbcore/contrib/notifications/backends/firebase/backends.py +5 -2
  172. wbcore/contrib/notifications/dispatch.py +18 -7
  173. wbcore/contrib/notifications/factories/notification_types.py +1 -0
  174. wbcore/contrib/notifications/locale/de/LC_MESSAGES/django.po +25 -19
  175. wbcore/contrib/notifications/locale/de/LC_MESSAGES/django.po.translated +63 -0
  176. wbcore/contrib/notifications/locale/en/LC_MESSAGES/django.po +61 -0
  177. wbcore/contrib/notifications/locale/fr/LC_MESSAGES/django.po +62 -0
  178. wbcore/contrib/notifications/migrations/0008_notificationtype_is_lock.py +18 -0
  179. wbcore/contrib/notifications/migrations/0009_alter_notificationtypesetting_options_and_more.py +32 -0
  180. wbcore/contrib/notifications/models/notification_types.py +67 -24
  181. wbcore/contrib/notifications/serializers/notification_types.py +16 -1
  182. wbcore/contrib/notifications/tests/test_models/test_tokens.py +8 -0
  183. wbcore/contrib/notifications/tests/test_serializers/test_notification_types.py +5 -0
  184. wbcore/contrib/notifications/tests/test_viewsets/test_notification_types.py +3 -5
  185. wbcore/contrib/notifications/utils.py +3 -2
  186. wbcore/contrib/notifications/viewsets/configs/notification_types.py +28 -6
  187. wbcore/contrib/notifications/viewsets/menus.py +1 -1
  188. wbcore/contrib/notifications/viewsets/notification_types.py +12 -2
  189. wbcore/contrib/pandas/fields.py +38 -10
  190. wbcore/contrib/pandas/filters.py +4 -1
  191. wbcore/contrib/pandas/filterset.py +8 -7
  192. wbcore/contrib/pandas/tests/test_fields/test_number_fields.py +2 -7
  193. wbcore/contrib/pandas/utils.py +1 -1
  194. wbcore/contrib/pandas/views.py +14 -13
  195. wbcore/contrib/tags/models/tags.py +4 -1
  196. wbcore/contrib/workflow/factories/display.py +2 -2
  197. wbcore/contrib/workflow/factories/transition.py +16 -15
  198. wbcore/contrib/workflow/locale/de/LC_MESSAGES/django.po +457 -566
  199. wbcore/contrib/workflow/locale/de/LC_MESSAGES/django.po.translated +1326 -0
  200. wbcore/contrib/workflow/locale/en/LC_MESSAGES/django.po +1102 -0
  201. wbcore/contrib/workflow/locale/fr/LC_MESSAGES/django.po +1114 -0
  202. wbcore/contrib/workflow/models/data.py +7 -4
  203. wbcore/contrib/workflow/models/process.py +2 -2
  204. wbcore/contrib/workflow/models/step.py +57 -15
  205. wbcore/contrib/workflow/serializers/data.py +8 -8
  206. wbcore/contrib/workflow/serializers/process.py +3 -2
  207. wbcore/contrib/workflow/tests/conftest.py +224 -0
  208. wbcore/contrib/workflow/tests/test_dispatch.py +82 -77
  209. wbcore/contrib/workflow/tests/test_displays.py +10 -88
  210. wbcore/contrib/workflow/tests/test_filters.py +57 -40
  211. wbcore/contrib/workflow/tests/test_models/step/test_decision_step.py +71 -68
  212. wbcore/contrib/workflow/tests/test_models/step/test_email_step.py +78 -38
  213. wbcore/contrib/workflow/tests/test_models/step/test_finish_step.py +152 -90
  214. wbcore/contrib/workflow/tests/test_models/step/test_join_step.py +100 -110
  215. wbcore/contrib/workflow/tests/test_models/step/test_step.py +168 -33
  216. wbcore/contrib/workflow/tests/test_models/test_condition.py +1 -1
  217. wbcore/contrib/workflow/tests/test_models/test_workflow.py +3 -3
  218. wbcore/contrib/workflow/tests/test_serializers.py +172 -150
  219. wbcore/contrib/workflow/tests/test_viewsets.py +264 -323
  220. wbcore/contrib/workflow/tests/test_workflow_assignees.py +215 -205
  221. wbcore/contrib/workflow/viewsets/process.py +4 -1
  222. wbcore/contrib/workflow/workflows/assignees.py +12 -7
  223. wbcore/dynamic_preferences_registry.py +102 -0
  224. wbcore/enums.py +2 -51
  225. wbcore/filters/fields/choices.py +4 -6
  226. wbcore/filters/fields/content_type.py +15 -4
  227. wbcore/filters/fields/datetime.py +50 -25
  228. wbcore/filters/fields/models.py +18 -9
  229. wbcore/filters/fields/numbers.py +9 -8
  230. wbcore/filters/filterset.py +27 -6
  231. wbcore/filters/mixins.py +41 -42
  232. wbcore/forms.py +6 -6
  233. wbcore/fsm/markdown_extensions.py +1 -1
  234. wbcore/fsm/mixins.py +20 -6
  235. wbcore/locale/de/LC_MESSAGES/django.po +982 -397
  236. wbcore/locale/de/LC_MESSAGES/django.po.translated +1580 -0
  237. wbcore/locale/en/LC_MESSAGES/django.po +1234 -0
  238. wbcore/locale/fr/LC_MESSAGES/django.po +1235 -0
  239. wbcore/markdown/models.py +8 -5
  240. wbcore/markdown/views.py +1 -1
  241. wbcore/menus/menus.py +2 -2
  242. wbcore/metadata/configs/buttons/bases.py +10 -7
  243. wbcore/metadata/configs/buttons/buttons.py +2 -1
  244. wbcore/metadata/configs/buttons/enums.py +50 -0
  245. wbcore/metadata/configs/buttons/view_config.py +13 -46
  246. wbcore/metadata/configs/display/display.py +2 -2
  247. wbcore/metadata/configs/display/formatting.py +6 -9
  248. wbcore/metadata/configs/display/instance_display/display.py +5 -2
  249. wbcore/metadata/configs/display/instance_display/pages.py +1 -1
  250. wbcore/metadata/configs/display/instance_display/shortcuts.py +1 -1
  251. wbcore/metadata/configs/display/list_display.py +54 -40
  252. wbcore/metadata/configs/display/models.py +6 -0
  253. wbcore/metadata/configs/display/view_config.py +11 -9
  254. wbcore/metadata/configs/endpoints.py +11 -4
  255. wbcore/metadata/configs/fields.py +6 -1
  256. wbcore/metadata/configs/filter_fields.py +12 -13
  257. wbcore/metadata/configs/identifiers.py +3 -1
  258. wbcore/metadata/tests/test_buttons.py +13 -16
  259. wbcore/models/fields.py +2 -2
  260. wbcore/pagination.py +1 -2
  261. wbcore/permissions/permissions.py +2 -2
  262. wbcore/permissions/utils.py +2 -2
  263. wbcore/release_notes/display.py +2 -8
  264. wbcore/release_notes/serializers.py +2 -9
  265. wbcore/release_notes/viewsets.py +8 -2
  266. wbcore/reversion/viewsets/titles.py +4 -3
  267. wbcore/serializers/__init__.py +2 -0
  268. wbcore/serializers/fields/__init__.py +2 -1
  269. wbcore/serializers/fields/boolean.py +1 -1
  270. wbcore/serializers/fields/choice.py +28 -4
  271. wbcore/serializers/fields/datetime.py +45 -36
  272. wbcore/serializers/fields/fields.py +1 -1
  273. wbcore/serializers/fields/fsm.py +1 -1
  274. wbcore/serializers/fields/list.py +2 -5
  275. wbcore/serializers/fields/mixins.py +24 -11
  276. wbcore/serializers/fields/number.py +6 -23
  277. wbcore/serializers/fields/other.py +2 -10
  278. wbcore/serializers/fields/related.py +4 -6
  279. wbcore/serializers/fields/text.py +1 -1
  280. wbcore/serializers/fields/types.py +2 -0
  281. wbcore/serializers/serializers.py +12 -3
  282. wbcore/signals/__init__.py +1 -0
  283. wbcore/signals/clone.py +4 -0
  284. wbcore/signals/models.py +2 -6
  285. wbcore/tasks.py +2 -2
  286. wbcore/templates/wbcore/email_base_template.html +3 -3
  287. wbcore/test/e2e_helpers_methods/e2e_checks.py +10 -4
  288. wbcore/test/e2e_helpers_methods/e2e_helper_methods.py +4 -2
  289. wbcore/test/mixins.py +52 -102
  290. wbcore/test/tests.py +6 -9
  291. wbcore/test/utils.py +3 -4
  292. wbcore/tests/e2e/test_e2e.py +2 -2
  293. wbcore/tests/test_cache/test_decorators.py +4 -7
  294. wbcore/tests/test_configs.py +2 -5
  295. wbcore/tests/test_enums.py +2 -1
  296. wbcore/tests/test_fields/test_choice_fields.py +9 -1
  297. wbcore/tests/test_fields/test_number_fields.py +7 -15
  298. wbcore/tests/test_fields/test_other_fields.py +1 -2
  299. wbcore/tests/test_filters/test_mixins.py +35 -35
  300. wbcore/tests/test_list_display.py +0 -2
  301. wbcore/tests/test_models/test_mixins.py +1 -1
  302. wbcore/tests/test_utils/test_date.py +1 -1
  303. wbcore/tests/test_utils/test_date_builder.py +25 -1
  304. wbcore/tests/test_utils/test_primary.py +1 -1
  305. wbcore/urls.py +4 -0
  306. wbcore/utils/date.py +18 -2
  307. wbcore/utils/figures.py +2 -2
  308. wbcore/utils/models.py +21 -4
  309. wbcore/utils/reportlab.py +7 -0
  310. wbcore/utils/rrules.py +3 -1
  311. wbcore/utils/string_loader.py +1 -1
  312. wbcore/utils/strings.py +3 -3
  313. wbcore/utils/views.py +8 -3
  314. wbcore/viewsets/mixins.py +9 -4
  315. {wbcore-1.46.0.dist-info → wbcore-1.58.2.dist-info}/METADATA +9 -5
  316. {wbcore-1.46.0.dist-info → wbcore-1.58.2.dist-info}/RECORD +317 -271
  317. wbcore/contrib/geography/tests/test_serializers.py +0 -7
  318. wbcore/contrib/geography/tests/tests.py +0 -13
  319. wbcore/contrib/io/tests/tests.py +0 -19
  320. wbcore/contrib/workflow/tests/tests.py +0 -25
  321. {wbcore-1.46.0.dist-info → wbcore-1.58.2.dist-info}/WHEEL +0 -0
@@ -1,45 +1,85 @@
1
- from unittest.mock import patch
2
-
3
1
  import pytest
2
+ from django.conf import settings
4
3
  from django.template import TemplateSyntaxError
5
- from wbcore.contrib.directory.factories import EmailContactFactory
4
+ from pytest_mock import MockerFixture
5
+ from wbcore.contrib.workflow.models import EmailStep, ProcessStep
6
6
 
7
7
 
8
- @pytest.mark.django_db
9
8
  class TestEmailStep:
10
- @patch("wbcore.contrib.workflow.models.step.Step.execute_single_next_step")
11
- @patch("django.core.mail.EmailMultiAlternatives.__init__")
12
- @patch("django.core.mail.EmailMultiAlternatives.send")
13
- @patch("django.core.mail.EmailMultiAlternatives.attach_alternative")
14
- def test_run(self, mock_attach, mock_send, mock_init, mock_execute, process_step_factory, email_step_factory):
15
- step = email_step_factory(to=[EmailContactFactory()], cc=[EmailContactFactory(), EmailContactFactory()])
16
- process_step = process_step_factory(step=step)
17
- mock_init.return_value = None
18
- step.run(process_step)
19
- assert mock_init.call_args.args[0] == step.subject
20
- assert mock_init.call_args.kwargs == {
21
- "to": list(step.to.values_list("address", flat=True)),
22
- "cc": list(step.cc.values_list("address", flat=True)),
23
- "bcc": [],
24
- }
25
- assert mock_attach.called
26
- assert mock_send.called
27
- assert mock_execute.call_args.args == (process_step,)
9
+ @pytest.fixture
10
+ def mocked_email_step(self, mocker: MockerFixture):
11
+ mocked_email_step = mocker.MagicMock(spec=EmailStep)
12
+ mocked_email_step.subject = "Test Subject"
13
+ mocked_email_step.template.file = "<html>Email content</html>"
14
+ mocked_email_step.to.values_list.return_value = ["to@example.com"]
15
+ mocked_email_step.cc.values_list.return_value = [
16
+ "cc1@example.com",
17
+ "cc2@example.com",
18
+ ]
19
+ mocked_email_step.bcc.values_list.return_value = []
20
+ return mocked_email_step
21
+
22
+ @pytest.fixture
23
+ def mocked_process_step(self, mocker: MockerFixture):
24
+ mocked_process_step = mocker.MagicMock(spec=ProcessStep)
25
+ mocked_process_step.get_endpoint_basename.return_value = "process-step"
26
+ mocked_process_step.id = 123
27
+ return mocked_process_step
28
+
29
+ @pytest.fixture(autouse=True)
30
+ def patching(self, mocker: MockerFixture):
31
+ mocker.patch(
32
+ "wbcore.contrib.workflow.models.step.base_domain",
33
+ return_value="https://testserver.com",
34
+ )
35
+ mocker.patch(
36
+ "wbcore.contrib.workflow.models.step.reverse",
37
+ return_value="/api/process-step/123/",
38
+ )
39
+
40
+ def test_run(self, mocker: MockerFixture, mocked_process_step, mocked_email_step):
41
+ # Arrange
42
+ mocked_template_render = mocker.patch(
43
+ "django.template.Template.render",
44
+ return_value="<html>Rendered content</html>",
45
+ )
46
+ mocked_email_init = mocker.patch("django.core.mail.EmailMultiAlternatives.__init__", return_value=None)
47
+ mocked_email_send = mocker.patch("django.core.mail.EmailMultiAlternatives.send")
48
+ mocked_attach_alternative = mocker.patch("django.core.mail.EmailMultiAlternatives.attach_alternative")
49
+ mocker.patch(
50
+ "wbcore.contrib.workflow.models.step.convert_html2text",
51
+ return_value="Rendered content",
52
+ )
53
+ mocked_execute_single_next_step = mocker.patch.object(mocked_email_step, "execute_single_next_step")
54
+ # Act
55
+ EmailStep.run(mocked_email_step, mocked_process_step)
56
+ # Assert
57
+ mocked_template_render.assert_called_once_with(mocker.ANY)
58
+ mocked_email_init.assert_called_once_with(
59
+ "Test Subject",
60
+ "Rendered content",
61
+ settings.DEFAULT_FROM_EMAIL,
62
+ to=["to@example.com"],
63
+ cc=["cc1@example.com", "cc2@example.com"],
64
+ bcc=[],
65
+ )
66
+ mocked_attach_alternative.assert_called_once_with("<html>Rendered content</html>", "text/html")
67
+ mocked_email_send.assert_called_once()
68
+ mocked_execute_single_next_step.assert_called_once_with(mocked_process_step)
28
69
 
29
- @patch("wbcore.contrib.workflow.models.step.Step.set_failed")
30
- @patch("django.template.Template.render")
31
- @patch("wbcore.contrib.workflow.models.step.Step.execute_single_next_step")
32
- @patch("django.core.mail.EmailMultiAlternatives.send")
33
- def test_run_failed(
34
- self, mock_send, mock_execute, mock_render, mock_set_failed, process_step_factory, email_step_factory
35
- ):
36
- step = email_step_factory(
37
- to=[EmailContactFactory()],
38
- cc=[EmailContactFactory(), EmailContactFactory()],
70
+ def test_run_failed(self, mocker: MockerFixture, mocked_process_step, mocked_email_step):
71
+ # Arrange
72
+ mocked_template_render = mocker.patch(
73
+ "django.template.Template.render",
74
+ side_effect=TemplateSyntaxError("Invalid syntax"),
39
75
  )
40
- process_step = process_step_factory(step=step)
41
- mock_render.side_effect = TemplateSyntaxError
42
- step.run(process_step)
43
- assert not mock_send.called
44
- assert not mock_execute.called
45
- assert mock_set_failed.call_args.args[0] == process_step
76
+ mocked_email_send = mocker.patch("django.core.mail.EmailMultiAlternatives.send")
77
+ mocked_execute_single_next_step = mocker.patch.object(mocked_email_step, "execute_single_next_step")
78
+ mocked_set_failed = mocker.patch.object(mocked_email_step, "set_failed")
79
+ # Act
80
+ EmailStep.run(mocked_email_step, mocked_process_step)
81
+ # Assert
82
+ mocked_template_render.assert_called_once_with(mocker.ANY)
83
+ mocked_email_send.assert_not_called()
84
+ mocked_execute_single_next_step.assert_not_called()
85
+ mocked_set_failed.assert_called_once_with(mocked_process_step, "Error in template syntax!")
@@ -1,35 +1,59 @@
1
- from unittest.mock import patch
2
-
3
1
  import pytest
4
- from wbcore.contrib.directory.models import Person
5
- from wbcore.contrib.workflow.models import Process, ProcessStep
6
- from wbcore.contrib.workflow.sites import workflow_site
2
+ from django.utils.translation import gettext
3
+ from pytest_mock import MockerFixture
4
+ from wbcore.contrib.workflow.models import FinishStep, Process, ProcessStep, Step
7
5
 
8
6
 
9
- @pytest.mark.django_db
10
7
  class TestFinishStep:
11
- @pytest.mark.parametrize(
12
- "finished_state",
13
- [ProcessStep.StepState.FAILED, ProcessStep.StepState.CANCELED, ProcessStep.StepState.FINISHED],
14
- )
15
- @pytest.mark.parametrize("unfinished_state", [ProcessStep.StepState.ACTIVE, ProcessStep.StepState.WAITING])
16
- @patch("wbcore.contrib.workflow.models.step.Step.set_finished")
17
- def test_run(self, mock_finish, process_step_factory, finish_step_factory, unfinished_state, finished_state):
18
- step = finish_step_factory()
19
- process_step = process_step_factory(step=step)
20
- process_step_factory(process=process_step.process, state=finished_state)
21
- process_step_factory(step=step, state=unfinished_state)
22
- step.run(process_step)
23
- assert mock_finish.call_args.args == (process_step,)
24
-
25
- @pytest.mark.parametrize("unfinished_state", [ProcessStep.StepState.ACTIVE, ProcessStep.StepState.WAITING])
26
- @patch("wbcore.contrib.workflow.models.step.Step.set_failed")
27
- def test_run_failed(self, mock_failed, process_step_factory, finish_step_factory, unfinished_state):
28
- step = finish_step_factory()
29
- process_step = process_step_factory(step=step)
30
- process_step_factory(process=process_step.process, state=unfinished_state)
31
- step.run(process_step)
32
- assert mock_failed.call_args.args[0] == process_step
8
+ @pytest.fixture(autouse=True)
9
+ def patch_super_finish(self, mocker: MockerFixture):
10
+ mocker.patch("wbcore.contrib.workflow.models.step.Step.finish", autospec=True)
11
+
12
+ @pytest.fixture
13
+ def mocked_finish_step(self, mocker: MockerFixture):
14
+ return mocker.MagicMock(spec=FinishStep)
15
+
16
+ @pytest.fixture
17
+ def mocked_instance(self, mocker: MockerFixture):
18
+ return mocker.MagicMock(first_name="Original")
19
+
20
+ @pytest.fixture
21
+ def mocked_process(self, mocker: MockerFixture, mocked_instance):
22
+ mocked_process = mocker.MagicMock(
23
+ finished=None,
24
+ instance=mocked_instance,
25
+ save=mocker.MagicMock(),
26
+ spec=Process,
27
+ state=Process.ProcessState.ACTIVE,
28
+ )
29
+ return mocked_process
30
+
31
+ @pytest.fixture
32
+ def mocked_process_step(self, mocker: MockerFixture, mocked_process):
33
+ return mocker.MagicMock(spec=ProcessStep, process=mocked_process)
34
+
35
+ def test_run(self, mocker: MockerFixture, mocked_process_step, mocked_finish_step):
36
+ # Arrange
37
+ mocked_qs = mocker.patch("wbcore.contrib.workflow.models.step.ProcessStep.objects.filter")
38
+ mocked_qs.return_value.exclude.return_value.exists.return_value = False
39
+ mocked_set_finished = mocker.patch.object(mocked_finish_step, "set_finished")
40
+ # Act
41
+ FinishStep.run(mocked_finish_step, mocked_process_step)
42
+ # Assert
43
+ mocked_set_finished.assert_called_once_with(mocked_process_step)
44
+
45
+ def test_run_failed(self, mocker: MockerFixture, mocked_process_step, mocked_finish_step):
46
+ # Arrange
47
+ mocked_qs = mocker.patch("wbcore.contrib.workflow.models.step.ProcessStep.objects.filter")
48
+ mocked_qs.return_value.exclude.return_value.exists.return_value = True
49
+ mocked_set_failed = mocker.patch.object(mocked_finish_step, "set_failed")
50
+ # Act
51
+ FinishStep.run(mocked_finish_step, mocked_process_step)
52
+ # Assert
53
+ mocked_set_failed.assert_called_once_with(
54
+ mocked_process_step,
55
+ gettext("There are process steps still running for this workflow!"),
56
+ )
33
57
 
34
58
  @pytest.mark.parametrize(
35
59
  "unfinished_state",
@@ -40,66 +64,104 @@ class TestFinishStep:
40
64
  ProcessStep.StepState.FAILED,
41
65
  ],
42
66
  )
43
- @patch("wbcore.contrib.workflow.models.step.Step.finish")
44
- def test_finish_not_finished(self, mock_finish, unfinished_state, process_step_factory, finish_step_factory):
45
- step = finish_step_factory(write_preserved_instance=True)
46
- process_step = process_step_factory(
47
- process__preserved_instance={"first_name": "Test"},
48
- step=step,
49
- process__finished=None,
50
- state=unfinished_state,
51
- process__state=Process.ProcessState.ACTIVE,
52
- )
53
- step.finish(process_step)
54
- assert mock_finish.call_args.args == (process_step,)
55
- assert process_step.process.finished is None
56
- assert process_step.process.instance.first_name != "Test"
57
- assert process_step.process.state == Process.ProcessState.ACTIVE
58
-
59
- @patch("wbcore.contrib.workflow.models.step.Step.finish")
60
- def test_finish_write_preserved_instance(self, mock_finish, process_step_factory, finish_step_factory):
61
- step = finish_step_factory(write_preserved_instance=True, workflow__preserve_instance=True)
62
- process_step = process_step_factory(
63
- step=step,
64
- process__finished=None,
65
- state=ProcessStep.StepState.FINISHED,
66
- process__workflow=step.workflow,
67
- )
68
- old_name = process_step.process.instance.first_name
69
- workflow_site.registered_model_classes_serializer_map[
70
- Person
71
- ] = "wbcore.contrib.directory.serializers.PersonModelSerializer"
72
- step.finish(process_step)
73
- assert mock_finish.call_args.args == (process_step,)
74
- assert process_step.process.finished is not None
75
- assert process_step.process.instance.first_name != old_name
76
- assert process_step.process.instance.first_name == process_step.process.preserved_instance["first_name"]
77
- assert process_step.process.state == Process.ProcessState.FINISHED
78
-
79
- @patch("wbcore.contrib.workflow.models.step.Step.finish")
80
- def test_finish_do_not_write(self, mock_finish, process_step_factory, finish_step_factory):
81
- step = finish_step_factory(write_preserved_instance=False, workflow__preserve_instance=True)
82
- process_step = process_step_factory(
83
- step=step,
84
- process__finished=None,
85
- state=ProcessStep.StepState.FINISHED,
86
- )
87
- old_name = process_step.process.instance.first_name
88
- step.finish(process_step)
89
- assert mock_finish.call_args.args == (process_step,)
90
- assert process_step.process.finished is not None
91
- assert process_step.process.instance.first_name == old_name
92
- assert process_step.process.state == Process.ProcessState.FINISHED
93
-
94
- @patch("wbcore.contrib.workflow.models.step.Step.finish")
95
- def test_finish_no_preserved_instance(self, mock_finish, process_step_factory, finish_step_factory):
96
- step = finish_step_factory(write_preserved_instance=True, workflow__preserve_instance=True)
97
- process_step = process_step_factory(
98
- step=step, process__finished=None, state=ProcessStep.StepState.FINISHED, process__preserved_instance=None
67
+ def test_finish_not_finished(
68
+ self,
69
+ mocker: MockerFixture,
70
+ mocked_process,
71
+ mocked_process_step,
72
+ unfinished_state,
73
+ ):
74
+ # Arrange
75
+ mocked_process.preserved_instance = {"first_name": "Test"}
76
+ mocked_process_step.state = unfinished_state
77
+
78
+ finish_step = FinishStep()
79
+ mock_serializer = mocker.patch("wbcore.contrib.workflow.models.step.get_model_serializer_class_for_instance")
80
+ # Act
81
+ finish_step.finish(mocked_process_step)
82
+ # Assert
83
+ mock_serializer.assert_not_called()
84
+ mocked_process_step.process.save.assert_not_called()
85
+ assert mocked_process.finished is None
86
+ assert mocked_process.state == Process.ProcessState.ACTIVE
87
+
88
+ def test_finish_write_preserved_instance(
89
+ self,
90
+ mocker: MockerFixture,
91
+ mocked_process,
92
+ mocked_process_step,
93
+ ):
94
+ # Arrange
95
+ preserved_data = {"first_name": "Updated"}
96
+
97
+ mocked_serializer = mocker.MagicMock()
98
+ mocked_serializer.validated_data = preserved_data
99
+ mocked_serializer_class = mocker.MagicMock()
100
+ mocked_serializer_class.return_value = mocked_serializer
101
+ mocker.patch(
102
+ "wbcore.contrib.workflow.models.step.get_model_serializer_class_for_instance",
103
+ return_value=mocked_serializer_class,
99
104
  )
100
- old_name = process_step.process.instance.first_name
101
- step.finish(process_step)
102
- assert mock_finish.call_args.args == (process_step,)
103
- assert process_step.process.finished is not None
104
- assert process_step.process.instance.first_name == old_name
105
- assert process_step.process.state == Process.ProcessState.FINISHED
105
+ mocked_serializer.is_valid.return_value = True
106
+ mocked_serializer.update = mocker.MagicMock()
107
+
108
+ mocked_process.preserved_instance = preserved_data
109
+
110
+ mocked_step = mocker.MagicMock(spec=Step, write_preserved_instance=True)
111
+ mocked_step.workflow.preserve_instance = True
112
+
113
+ mocked_process_step.state = ProcessStep.StepState.FINISHED
114
+ mocked_process_step.step = mocked_step
115
+
116
+ finish_step = FinishStep()
117
+ mocker.patch.object(finish_step, "write_preserved_instance", True)
118
+ # Act
119
+ finish_step.finish(mocked_process_step)
120
+ # Assert
121
+ mocked_serializer.is_valid.assert_called_once()
122
+ mocked_serializer.update.assert_called_once_with(mocked_process.instance, preserved_data)
123
+ mocked_process.save.assert_called_once()
124
+ assert mocked_process.finished is not None
125
+ assert mocked_process.state == Process.ProcessState.FINISHED
126
+
127
+ def test_finish_do_not_write_mocker(self, mocker: MockerFixture, mocked_process, mocked_process_step):
128
+ # Arrange
129
+ mocked_process.preserved_instance = {"first_name": "Updated"}
130
+ mocked_process.state = Process.ProcessState.ACTIVE
131
+
132
+ mocked_process_step.state = ProcessStep.StepState.FINISHED
133
+
134
+ finish_step = FinishStep()
135
+ mocker.patch.object(finish_step, "write_preserved_instance", False)
136
+
137
+ mock_serializer = mocker.patch("wbcore.contrib.workflow.models.step.get_model_serializer_class_for_instance")
138
+
139
+ # Act
140
+ finish_step.finish(mocked_process_step)
141
+
142
+ # Assert
143
+ mock_serializer.assert_not_called()
144
+ mocked_process.save.assert_called_once()
145
+ assert mocked_process.finished is not None
146
+ assert mocked_process.state == Process.ProcessState.FINISHED
147
+
148
+ def test_finish_no_preserved_instance_mocker(self, mocker: MockerFixture, mocked_process, mocked_process_step):
149
+ # Arrange
150
+ mocked_process.preserved_instance = None
151
+ mocked_process.state = Process.ProcessState.ACTIVE
152
+
153
+ mocked_process_step.state = ProcessStep.StepState.FINISHED
154
+
155
+ finish_step = FinishStep()
156
+ mocker.patch.object(finish_step, "write_preserved_instance", True)
157
+
158
+ mock_serializer = mocker.patch("wbcore.contrib.workflow.models.step.get_model_serializer_class_for_instance")
159
+
160
+ # Act
161
+ finish_step.finish(mocked_process_step)
162
+
163
+ # Assert
164
+ mock_serializer.assert_not_called()
165
+ mocked_process.save.assert_called_once()
166
+ assert mocked_process.finished is not None
167
+ assert mocked_process.state == Process.ProcessState.FINISHED
@@ -1,127 +1,117 @@
1
- from unittest.mock import patch
2
-
3
1
  import pytest
4
- from wbcore.contrib.workflow.models import ProcessStep
2
+ from django.db import models
3
+ from pytest_mock import MockerFixture
4
+ from wbcore.contrib.workflow.models import (
5
+ JoinStep,
6
+ ProcessStep,
7
+ Step,
8
+ Transition,
9
+ )
5
10
 
6
11
 
7
- @pytest.mark.django_db
8
12
  class TestJoinStep:
9
- @patch("wbcore.contrib.workflow.models.step.Step.set_canceled")
13
+ @pytest.fixture
14
+ def join_step(self):
15
+ return JoinStep()
16
+
17
+ @pytest.fixture
18
+ def mocked_step(self, mocker: MockerFixture):
19
+ return mocker.MagicMock(spec=Step, set_canceled=mocker.MagicMock())
20
+
21
+ @pytest.fixture
22
+ def mocked_process_step(self, mocker: MockerFixture, mocked_step):
23
+ return mocker.MagicMock(spec=ProcessStep, step=mocked_step)
24
+
25
+ @pytest.fixture
26
+ def mocked_transition(self, mocker: MockerFixture):
27
+ return mocker.MagicMock(spec=Transition)
28
+
29
+ @pytest.fixture
30
+ def mocked_execute_single_next_step(self, mocker: MockerFixture, join_step):
31
+ return mocker.patch.object(join_step, "execute_single_next_step")
32
+
10
33
  def test_cancel_if_leading_to_self_transition_found(
11
- self, mock_canceled, random_child_step_factory, join_step_factory, process_step_factory, transition_factory
34
+ self,
35
+ mocker: MockerFixture,
36
+ join_step,
37
+ mocked_step,
38
+ mocked_process_step,
39
+ mocked_transition,
12
40
  ):
13
- self_step = join_step_factory()
14
- step = random_child_step_factory()
15
- process_step = process_step_factory(step=step)
16
- transition1 = transition_factory(from_step=step)
17
- transition_factory(from_step=step)
18
- transition_factory(from_step=transition1.to_step, to_step=self_step)
19
- transition_factory()
20
- self_step.cancel_if_leading_to_self(step, process_step)
21
- assert mock_canceled.call_args.args == (process_step,)
41
+ # Arrange
42
+ mocker.patch.object(join_step, "id", 1)
43
+ mocked_transition.to_step = join_step
44
+ mocked_step.outgoing_transitions.all.return_value = [mocked_transition]
45
+ # Act
46
+ join_step.cancel_if_leading_to_self(mocked_step, mocked_process_step)
47
+ # Assert
48
+ mocked_step.set_canceled.assert_called_once_with(mocked_process_step)
22
49
 
23
- @patch("wbcore.contrib.workflow.models.step.Step.set_canceled")
24
50
  def test_cancel_if_leading_to_self_no_transition_found(
25
51
  self,
26
- mock_canceled,
27
- random_child_step_factory,
28
- join_step_factory,
29
- process_step_factory,
30
- transition_factory,
52
+ mocker: MockerFixture,
53
+ join_step,
54
+ mocked_step,
55
+ mocked_process_step,
56
+ mocked_transition,
31
57
  ):
32
- self_step = join_step_factory()
33
- step = random_child_step_factory()
34
- process_step = process_step_factory(step=step)
35
- transition1 = transition_factory(from_step=step)
36
- transition_factory(from_step=step)
37
- transition_factory(from_step=transition1.to_step)
38
- transition_factory()
39
- self_step.cancel_if_leading_to_self(step, process_step)
40
- assert not mock_canceled.called
58
+ # Arrange
59
+ mocked_step.outgoing_transitions.all.return_value = [mocked_transition]
60
+ # Act
61
+ join_step.cancel_if_leading_to_self(mocked_step, mocked_process_step)
62
+ # Assert
63
+ mocked_step.set_canceled.assert_not_called()
41
64
 
42
- @patch("wbcore.contrib.workflow.models.step.JoinStep.cancel_if_leading_to_self")
43
- @patch("wbcore.contrib.workflow.models.step.Step.execute_single_next_step")
44
- def test_run_wait_for_all_transition_without_process_step(
45
- self, mock_execute, mock_cancel, process_step_factory, join_step_factory, transition_factory
65
+ @pytest.mark.parametrize("with_transition_qs", [True, False])
66
+ def test_run_with_wait_for_all(
67
+ self,
68
+ mocker: MockerFixture,
69
+ join_step,
70
+ mocked_process_step,
71
+ with_transition_qs,
72
+ mocked_execute_single_next_step,
46
73
  ):
47
- step = join_step_factory(wait_for_all=True)
48
- transition_factory(to_step=step)
49
- process_step = process_step_factory(step=step, state=ProcessStep.StepState.ACTIVE)
50
- step.run(process_step)
51
- assert process_step.state == ProcessStep.StepState.WAITING
52
- assert not mock_execute.called
53
- assert not mock_cancel.called
74
+ # Arrange
75
+ mocker.patch.object(join_step, "wait_for_all", True)
76
+ mocked_process_step.save = mocker.MagicMock()
77
+ mocked_object_manager = mocker.patch("wbcore.contrib.workflow.models.transition.Transition.objects")
78
+ mocked_qs = mocker.MagicMock(spec=models.QuerySet)
79
+ mocked_qs.exists.return_value = with_transition_qs
80
+ mocked_object_manager.filter.return_value = mocked_qs
81
+ # Act
82
+ join_step.run(mocked_process_step)
83
+ # Assert
84
+ assert mocked_process_step.state == ProcessStep.StepState.WAITING
85
+ mocked_process_step.save.assert_called_once()
86
+ if with_transition_qs:
87
+ mocked_execute_single_next_step.assert_not_called()
88
+ else:
89
+ mocked_execute_single_next_step.assert_called_once_with(mocked_process_step)
54
90
 
55
- @patch("wbcore.contrib.workflow.models.step.JoinStep.cancel_if_leading_to_self")
56
- @patch("wbcore.contrib.workflow.models.step.Step.execute_single_next_step")
57
- def test_run_wait_for_all_transition_with_process_step(
58
- self, mock_execute, mock_cancel, process_step_factory, join_step_factory, transition_factory
91
+ def test_run_without_wait_for_all(
92
+ self,
93
+ mocker: MockerFixture,
94
+ join_step,
95
+ mocked_process_step,
96
+ mocked_execute_single_next_step,
59
97
  ):
60
- step = join_step_factory(wait_for_all=True)
61
- transition = transition_factory(to_step=step)
62
- process_step = process_step_factory(step=step, state=ProcessStep.StepState.ACTIVE)
63
- process_step_factory(
64
- step=transition.from_step, process=process_step.process, state=ProcessStep.StepState.ACTIVE
65
- )
66
- step.run(process_step)
67
- assert process_step.state == ProcessStep.StepState.WAITING
68
- assert not mock_execute.called
69
- assert not mock_cancel.called
98
+ # Arrange
99
+ mocker.patch.object(join_step, "wait_for_all", False)
70
100
 
71
- @pytest.mark.parametrize("state", [ProcessStep.StepState.FINISHED, ProcessStep.StepState.CANCELED])
72
- @patch("wbcore.contrib.workflow.models.step.JoinStep.cancel_if_leading_to_self")
73
- @patch("wbcore.contrib.workflow.models.step.Step.execute_single_next_step")
74
- def test_run_wait_for_all_transition_with_finished_canceled_process_step(
75
- self, mock_execute, mock_cancel, process_step_factory, join_step_factory, transition_factory, state
76
- ):
77
- step = join_step_factory(wait_for_all=True)
78
- transition = transition_factory(to_step=step)
79
- transition_factory(from_step=step)
80
- process_step = process_step_factory(step=step, state=ProcessStep.StepState.ACTIVE)
81
- process_step_factory(step=transition.from_step, process=process_step.process, state=state)
82
- step.run(process_step)
83
- assert process_step.state == ProcessStep.StepState.WAITING
84
- assert mock_execute.call_args.args == (process_step,)
85
- assert not mock_cancel.called
101
+ mocked_cancel_if_leading = mocker.patch.object(join_step, "cancel_if_leading_to_self")
102
+ mocked_process_step.save = mocker.MagicMock()
103
+ mocked_object_manager = mocker.patch("wbcore.contrib.workflow.models.step.ProcessStep.objects")
104
+ unfinished_step_a = mocker.MagicMock(spec=ProcessStep, pk=123, step=mocker.MagicMock(spec=Step))
86
105
 
87
- @patch("wbcore.contrib.workflow.models.step.JoinStep.cancel_if_leading_to_self")
88
- @patch("wbcore.contrib.workflow.models.step.Step.execute_single_next_step")
89
- def test_run_wait_for_all_no_transition(
90
- self, mock_execute, mock_cancel, process_step_factory, join_step_factory, transition_factory
91
- ):
92
- step = join_step_factory(wait_for_all=True)
93
- transition_factory(from_step=step)
94
- process_step = process_step_factory(step=step, state=ProcessStep.StepState.ACTIVE)
95
- step.run(process_step)
96
- assert process_step.state == ProcessStep.StepState.WAITING
97
- assert mock_execute.call_args.args == (process_step,)
98
- assert not mock_cancel.called
106
+ unfinished_step_b = mocker.MagicMock(spec=ProcessStep, pk=456, step=mocker.MagicMock(spec=Step))
99
107
 
100
- @patch("wbcore.contrib.workflow.models.step.JoinStep.cancel_if_leading_to_self")
101
- @patch("wbcore.contrib.workflow.models.step.Step.execute_single_next_step")
102
- def test_run_do_not_wait(
103
- self,
104
- mock_execute,
105
- mock_cancel,
106
- process_step_factory,
107
- join_step_factory,
108
- ):
109
- step = join_step_factory(wait_for_all=False)
110
- process_step = process_step_factory(step=step, state=ProcessStep.StepState.ACTIVE)
111
- unfinished_process_step1 = process_step_factory(
112
- process=process_step.process, state=ProcessStep.StepState.ACTIVE
113
- )
114
- unfinished_process_step2 = process_step_factory(
115
- process=process_step.process, state=ProcessStep.StepState.WAITING
116
- )
117
- process_step_factory(state=ProcessStep.StepState.ACTIVE)
118
- process_step_factory(process=process_step.process, state=ProcessStep.StepState.FINISHED)
119
- process_step_factory(process=process_step.process, state=ProcessStep.StepState.CANCELED)
120
- process_step_factory(process=process_step.process, state=ProcessStep.StepState.FAILED)
121
- step.run(process_step)
122
- assert process_step.state == ProcessStep.StepState.WAITING
123
- assert mock_execute.call_args.args == (process_step,)
124
- assert set(tuple(map(lambda y: str(y.pk), x.args)) for x in mock_cancel.call_args_list) == {
125
- (str(unfinished_process_step1.step.pk), unfinished_process_step1.pk),
126
- (str(unfinished_process_step2.step.pk), unfinished_process_step2.pk),
127
- }
108
+ mocked_qs = mocker.MagicMock(spec=models.QuerySet)
109
+ mocked_qs.exclude.return_value = [unfinished_step_a, unfinished_step_b]
110
+ mocked_object_manager.filter.return_value = mocked_qs
111
+ # Act
112
+ join_step.run(mocked_process_step)
113
+ # Assert
114
+ assert mocked_process_step.state == ProcessStep.StepState.WAITING
115
+ assert mocked_cancel_if_leading.call_count == len(mocked_qs.exclude.return_value)
116
+ mocked_process_step.save.assert_called_once()
117
+ mocked_execute_single_next_step.assert_called_once()