wbcore 2.2.1__py2.py3-none-any.whl → 2.2.4__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.
- wbcore/contrib/agenda/locale/de/LC_MESSAGES/django.po +113 -0
- wbcore/contrib/agenda/static/agenda/markdown/documentation/building.md +11 -0
- wbcore/contrib/agenda/static/agenda/markdown/documentation/conferenceroom.md +20 -0
- wbcore/contrib/authentication/fixtures/authentication.json +62 -0
- wbcore/contrib/authentication/locale/de/LC_MESSAGES/django.po +610 -0
- wbcore/contrib/authentication/templates/activate_confirm.html +12 -0
- wbcore/contrib/authentication/templates/base.html +135 -0
- wbcore/contrib/authentication/templates/email_base_template.html +335 -0
- wbcore/contrib/authentication/templates/password_reset_done.html +13 -0
- wbcore/contrib/authentication/templates/password_reset_email.html +11 -0
- wbcore/contrib/authentication/templates/password_reset_email_html.html +43 -0
- wbcore/contrib/authentication/templates/password_reset_form.html +23 -0
- wbcore/contrib/authentication/templates/password_reset_sent.html +11 -0
- wbcore/contrib/authentication/templates/reset_password.html +15 -0
- wbcore/contrib/authentication/templates/user_registration_email.html +37 -0
- wbcore/contrib/color/admin.py +28 -0
- wbcore/contrib/color/apps.py +5 -0
- wbcore/contrib/color/enums.py +17 -0
- wbcore/contrib/color/factories.py +10 -0
- wbcore/contrib/color/fields.py +29 -0
- wbcore/contrib/color/forms.py +13 -0
- wbcore/contrib/color/models.py +62 -0
- wbcore/contrib/color/tests/conftest.py +10 -0
- wbcore/contrib/color/tests/test_color_models.py +61 -0
- wbcore/contrib/color/tests/test_fields.py +25 -0
- wbcore/contrib/currency/fixtures/currency.yaml +1014 -0
- wbcore/contrib/currency/fixtures/currency_fx_rate.yaml +73585 -0
- wbcore/contrib/directory/fixtures/directory.json +3924 -0
- wbcore/contrib/directory/locale/de/LC_MESSAGES/django.po +1686 -0
- wbcore/contrib/directory/static/directory/markdown/documentation/address.md +38 -0
- wbcore/contrib/directory/static/directory/markdown/documentation/banking.md +54 -0
- wbcore/contrib/directory/static/directory/markdown/documentation/bankingentry.md +38 -0
- wbcore/contrib/directory/static/directory/markdown/documentation/clientmanagerrelationship.md +42 -0
- wbcore/contrib/directory/static/directory/markdown/documentation/company.md +52 -0
- wbcore/contrib/directory/static/directory/markdown/documentation/companytype.md +2 -0
- wbcore/contrib/directory/static/directory/markdown/documentation/customerstatus.md +2 -0
- wbcore/contrib/directory/static/directory/markdown/documentation/email.md +20 -0
- wbcore/contrib/directory/static/directory/markdown/documentation/employeecompany.md +34 -0
- wbcore/contrib/directory/static/directory/markdown/documentation/employerperson.md +43 -0
- wbcore/contrib/directory/static/directory/markdown/documentation/person.md +61 -0
- wbcore/contrib/directory/static/directory/markdown/documentation/position.md +2 -0
- wbcore/contrib/directory/static/directory/markdown/documentation/relationshiptype.md +2 -0
- wbcore/contrib/directory/static/directory/markdown/documentation/socialmedia.md +23 -0
- wbcore/contrib/directory/static/directory/markdown/documentation/specialization.md +2 -0
- wbcore/contrib/directory/static/directory/markdown/documentation/systememployee.md +31 -0
- wbcore/contrib/directory/static/directory/markdown/documentation/telephone.md +23 -0
- wbcore/contrib/directory/static/directory/markdown/documentation/telephonesearch.md +26 -0
- wbcore/contrib/directory/static/directory/markdown/documentation/userisclient.md +14 -0
- wbcore/contrib/directory/static/directory/markdown/documentation/userismanager.md +28 -0
- wbcore/contrib/directory/static/directory/markdown/documentation/website.md +20 -0
- wbcore/contrib/documents/fixtures/docments.json +135 -0
- wbcore/contrib/documents/locale/de/LC_MESSAGES/django.po +272 -0
- wbcore/contrib/documents/static/documents/markdown/documentation/document_types.md +21 -0
- wbcore/contrib/documents/static/documents/markdown/documentation/documents.md +18 -0
- wbcore/contrib/documents/static/documents/markdown/documentation/shareablelink.md +28 -0
- wbcore/contrib/documents/static/documents/markdown/documentation/shareablelinkaccess.md +20 -0
- wbcore/contrib/documents/tests/conftest.py +30 -0
- wbcore/contrib/documents/tests/test_models.py +144 -0
- wbcore/contrib/example_app/fixtures/example_app.json +7425 -0
- wbcore/contrib/example_app/tests/test_models/test_event.py +87 -0
- wbcore/contrib/example_app/tests/test_models/test_match.py +210 -0
- wbcore/contrib/example_app/tests/test_models/test_others.py +159 -0
- wbcore/contrib/example_app/tests/test_serializers/test_league_serializer.py +34 -0
- wbcore/contrib/example_app/tests/test_serializers/test_match_serializer.py +134 -0
- wbcore/contrib/example_app/tests/test_serializers/test_role_serializer.py +13 -0
- wbcore/contrib/example_app/tests/test_serializers/test_sport_serializer.py +14 -0
- wbcore/contrib/example_app/tests/test_serializers/test_stadium_serializer.py +14 -0
- wbcore/contrib/example_app/tests/test_serializers/test_team_result_serializer.py +30 -0
- wbcore/contrib/example_app/tests/test_serializers/test_team_serializer.py +70 -0
- wbcore/contrib/example_app/tests/test_viewsets/test_event_viewset.py +162 -0
- wbcore/contrib/example_app/tests/test_viewsets/test_league_viewset.py +84 -0
- wbcore/contrib/example_app/tests/test_viewsets/test_match_viewset.py +65 -0
- wbcore/contrib/example_app/tests/test_viewsets/test_person_viewset.py +166 -0
- wbcore/contrib/example_app/tests/test_viewsets/test_role_viewset.py +75 -0
- wbcore/contrib/example_app/tests/test_viewsets/test_sport_viewset.py +75 -0
- wbcore/contrib/example_app/tests/test_viewsets/test_stadium_viewset.py +75 -0
- wbcore/contrib/example_app/tests/test_viewsets/test_team_viewset.py +92 -0
- wbcore/contrib/example_app/tests/test_viewsets/test_teamresult_viewset.py +58 -0
- wbcore/contrib/example_app/tests/test_viewsets/test_utils_viewsets.py +124 -0
- wbcore/contrib/geography/fixtures/geography.json +13454 -0
- wbcore/contrib/geography/static/geography/markdown/documentation/geography.md +16 -0
- wbcore/contrib/guardian/apps.py +6 -0
- wbcore/contrib/guardian/configurations.py +3 -0
- wbcore/contrib/guardian/filters.py +21 -0
- wbcore/contrib/guardian/tasks.py +10 -0
- wbcore/contrib/guardian/urls.py +12 -0
- wbcore/contrib/guardian/utils.py +124 -0
- wbcore/contrib/io/fixtures/io.json +145 -0
- wbcore/contrib/io/locale/de/LC_MESSAGES/django.po +52 -0
- wbcore/contrib/notifications/locale/de/LC_MESSAGES/django.po +60 -0
- wbcore/contrib/notifications/static/notifications/service-worker.js +1 -0
- wbcore/contrib/notifications/templates/notifications/notification_template.html +43 -0
- wbcore/contrib/notifications/viewsets/configs/notification_types.py +27 -0
- wbcore/contrib/notifications/viewsets/configs/notifications.py +85 -0
- wbcore/contrib/workflow/fixtures/workflow.json +612 -0
- wbcore/contrib/workflow/locale/de/LC_MESSAGES/django.po +1289 -0
- wbcore/contrib/workflow/static/workflow/markdown/documentation/assignedprocessstep.md +33 -0
- wbcore/contrib/workflow/static/workflow/markdown/documentation/condition.md +24 -0
- wbcore/contrib/workflow/static/workflow/markdown/documentation/decisionstep.md +30 -0
- wbcore/contrib/workflow/static/workflow/markdown/documentation/emailstep.md +45 -0
- wbcore/contrib/workflow/static/workflow/markdown/documentation/finishstep.md +33 -0
- wbcore/contrib/workflow/static/workflow/markdown/documentation/joinstep.md +33 -0
- wbcore/contrib/workflow/static/workflow/markdown/documentation/process.md +33 -0
- wbcore/contrib/workflow/static/workflow/markdown/documentation/processstep.md +51 -0
- wbcore/contrib/workflow/static/workflow/markdown/documentation/scriptstep.md +33 -0
- wbcore/contrib/workflow/static/workflow/markdown/documentation/splitstep.md +30 -0
- wbcore/contrib/workflow/static/workflow/markdown/documentation/startstep.md +27 -0
- wbcore/contrib/workflow/static/workflow/markdown/documentation/transition.md +27 -0
- wbcore/contrib/workflow/static/workflow/markdown/documentation/userstep.md +42 -0
- wbcore/contrib/workflow/static/workflow/markdown/documentation/workflow.md +32 -0
- wbcore/contrib/workflow/templates/Test_Templates.txt +25 -0
- wbcore/contrib/workflow/tests/test_models/step/test_decision_step.py +79 -0
- wbcore/contrib/workflow/tests/test_models/step/test_email_step.py +45 -0
- wbcore/contrib/workflow/tests/test_models/step/test_finish_step.py +105 -0
- wbcore/contrib/workflow/tests/test_models/step/test_join_step.py +127 -0
- wbcore/contrib/workflow/tests/test_models/step/test_script_step.py +24 -0
- wbcore/contrib/workflow/tests/test_models/step/test_split_step.py +49 -0
- wbcore/contrib/workflow/tests/test_models/step/test_step.py +621 -0
- wbcore/contrib/workflow/tests/test_models/step/test_user_step.py +225 -0
- wbcore/contrib/workflow/tests/test_models/test_condition.py +103 -0
- wbcore/contrib/workflow/tests/test_models/test_data.py +134 -0
- wbcore/contrib/workflow/tests/test_models/test_process.py +98 -0
- wbcore/contrib/workflow/tests/test_models/test_transition.py +128 -0
- wbcore/contrib/workflow/tests/test_models/test_workflow.py +358 -0
- wbcore/locale/de/LC_MESSAGES/django.po +667 -0
- wbcore/templates/errors/404.html +134 -0
- wbcore/templates/errors/500.html +138 -0
- wbcore/templates/errors/503.html +132 -0
- wbcore/templates/errors/custom.html +132 -0
- wbcore/templates/forms.py +0 -0
- wbcore/templates/notifications/email_template.html +43 -0
- wbcore/templates/wbcore/admin/change_list.html +8 -0
- wbcore/templates/wbcore/admin/csv_form.html +15 -0
- wbcore/templates/wbcore/dynamic_color_array.html +29 -0
- wbcore/templates/wbcore/email_base_template.html +335 -0
- wbcore/templates/wbcore/email_notification_template.html +10 -0
- wbcore/templates/wbcore/frontend.html +51 -0
- wbcore/test/e2e_helpers_methods/e2e_checks.py +121 -0
- wbcore/test/e2e_helpers_methods/e2e_helper_methods.py +395 -0
- wbcore/tests/test_permissions/test_backend.py +29 -0
- {wbcore-2.2.1.dist-info → wbcore-2.2.4.dist-info}/METADATA +1 -1
- {wbcore-2.2.1.dist-info → wbcore-2.2.4.dist-info}/RECORD +143 -3
- {wbcore-2.2.1.dist-info → wbcore-2.2.4.dist-info}/WHEEL +0 -0
|
@@ -0,0 +1,225 @@
|
|
|
1
|
+
from unittest.mock import patch
|
|
2
|
+
|
|
3
|
+
import pytest
|
|
4
|
+
from rest_framework.reverse import reverse
|
|
5
|
+
from wbcore.contrib.authentication.factories import UserFactory
|
|
6
|
+
from wbcore.contrib.directory.factories import BankingContactFactory, PersonFactory
|
|
7
|
+
from wbcore.contrib.workflow.models import ProcessStep
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
@pytest.mark.django_db
|
|
11
|
+
class TestUserStep:
|
|
12
|
+
def test_get_assigned_group(self, user_step_factory):
|
|
13
|
+
user_step = user_step_factory()
|
|
14
|
+
assert user_step.get_assigned_group() == user_step.group
|
|
15
|
+
|
|
16
|
+
def test_get_assigned_user(self, user_step_factory):
|
|
17
|
+
user = UserFactory()
|
|
18
|
+
user_step = user_step_factory(assignee=user, group=None)
|
|
19
|
+
assert user_step.get_assigned_user() == user
|
|
20
|
+
|
|
21
|
+
def test_execute_assignee_method(self, process_step_factory, user_step_factory):
|
|
22
|
+
step = user_step_factory()
|
|
23
|
+
manager = PersonFactory()
|
|
24
|
+
manager_account = UserFactory(profile=manager)
|
|
25
|
+
assignee = PersonFactory(relationship_managers=[manager])
|
|
26
|
+
instance = BankingContactFactory(entry=assignee)
|
|
27
|
+
process_step = process_step_factory(step=step, process__instance=instance)
|
|
28
|
+
process_step.step.execute_assignee_method(
|
|
29
|
+
process_step, "manager_of_instance_assignee", assignee_field="entry", assignee_type="entry"
|
|
30
|
+
)
|
|
31
|
+
assert process_step.assignee == manager_account
|
|
32
|
+
|
|
33
|
+
@patch("wbcore.contrib.workflow.models.step.UserStep.execute_assignee_method")
|
|
34
|
+
@patch("wbcore.contrib.workflow.models.step.UserStep.notify_assignees")
|
|
35
|
+
def test_run(self, mock_notify, mock_execute, process_step_factory, transition_factory, user_step_factory):
|
|
36
|
+
kwargs = {"test": "Test"}
|
|
37
|
+
step = user_step_factory(notify_user=True, kwargs=kwargs)
|
|
38
|
+
user = UserFactory(groups=[step.group])
|
|
39
|
+
process_step = process_step_factory(state=ProcessStep.StepState.ACTIVE, step=step)
|
|
40
|
+
transition_factory(from_step=step)
|
|
41
|
+
step.run(process_step)
|
|
42
|
+
assert mock_notify.call_args.args == ([user], process_step)
|
|
43
|
+
assert mock_execute.call_args.args == (process_step, step.assignee_method)
|
|
44
|
+
assert mock_execute.call_args.kwargs == kwargs
|
|
45
|
+
|
|
46
|
+
@patch("wbcore.contrib.workflow.models.step.UserStep.execute_assignee_method")
|
|
47
|
+
@patch("wbcore.contrib.workflow.models.step.UserStep.notify_assignees")
|
|
48
|
+
def test_run_no_kwargs(
|
|
49
|
+
self, mock_notify, mock_execute, process_step_factory, transition_factory, user_step_factory
|
|
50
|
+
):
|
|
51
|
+
step = user_step_factory(notify_user=True)
|
|
52
|
+
user = UserFactory(groups=[step.group])
|
|
53
|
+
process_step = process_step_factory(state=ProcessStep.StepState.ACTIVE, step=step)
|
|
54
|
+
transition_factory(from_step=step)
|
|
55
|
+
step.run(process_step)
|
|
56
|
+
assert mock_notify.call_args.args == ([user], process_step)
|
|
57
|
+
assert mock_execute.call_args.args == (process_step, step.assignee_method)
|
|
58
|
+
assert mock_execute.call_args.kwargs == {}
|
|
59
|
+
|
|
60
|
+
@patch("wbcore.contrib.workflow.models.step.UserStep.execute_assignee_method")
|
|
61
|
+
@patch("wbcore.contrib.workflow.models.step.UserStep.notify_assignees")
|
|
62
|
+
def test_run_no_notification(
|
|
63
|
+
self, mock_notify, mock_execute, process_step_factory, transition_factory, user_step_factory
|
|
64
|
+
):
|
|
65
|
+
kwargs = {"test": "Test"}
|
|
66
|
+
step = user_step_factory(notify_user=False, kwargs=kwargs)
|
|
67
|
+
UserFactory(groups=[step.group])
|
|
68
|
+
process_step = process_step_factory(state=ProcessStep.StepState.ACTIVE, step=step)
|
|
69
|
+
transition_factory(from_step=step)
|
|
70
|
+
step.run(process_step)
|
|
71
|
+
assert not mock_notify.called
|
|
72
|
+
assert mock_execute.call_args.args == (process_step, step.assignee_method)
|
|
73
|
+
assert mock_execute.call_args.kwargs == kwargs
|
|
74
|
+
|
|
75
|
+
@patch("wbcore.contrib.workflow.models.step.UserStep.execute_assignee_method")
|
|
76
|
+
@patch("wbcore.contrib.workflow.models.step.UserStep.notify_assignees")
|
|
77
|
+
def test_run_assignee_method_failed(
|
|
78
|
+
self, mock_notify, mock_execute, process_step_factory, transition_factory, user_step_factory
|
|
79
|
+
):
|
|
80
|
+
kwargs = {"test": "Test"}
|
|
81
|
+
step = user_step_factory(notify_user=True, kwargs=kwargs)
|
|
82
|
+
UserFactory(groups=[step.group])
|
|
83
|
+
process_step = process_step_factory(state=ProcessStep.StepState.FAILED, step=step)
|
|
84
|
+
transition_factory(from_step=step)
|
|
85
|
+
step.run(process_step)
|
|
86
|
+
assert not mock_notify.called
|
|
87
|
+
assert mock_execute.call_args.args == (process_step, step.assignee_method)
|
|
88
|
+
assert mock_execute.call_args.kwargs == kwargs
|
|
89
|
+
|
|
90
|
+
@patch("wbcore.contrib.workflow.models.step.UserStep.set_failed")
|
|
91
|
+
@patch("wbcore.contrib.workflow.models.step.UserStep.execute_assignee_method")
|
|
92
|
+
@patch("wbcore.contrib.workflow.models.step.UserStep.notify_assignees")
|
|
93
|
+
def test_run_no_assignee(
|
|
94
|
+
self, mock_notify, mock_execute, mock_failed, process_step_factory, transition_factory, user_step_factory
|
|
95
|
+
):
|
|
96
|
+
kwargs = {"test": "Test"}
|
|
97
|
+
step = user_step_factory(notify_user=True, kwargs=kwargs)
|
|
98
|
+
process_step = process_step_factory(state=ProcessStep.StepState.ACTIVE, step=step)
|
|
99
|
+
transition_factory(from_step=step)
|
|
100
|
+
step.run(process_step)
|
|
101
|
+
assert not mock_notify.called
|
|
102
|
+
assert mock_execute.call_args.args == (process_step, step.assignee_method)
|
|
103
|
+
assert mock_execute.call_args.kwargs == kwargs
|
|
104
|
+
assert mock_failed.call_args.args[0] == process_step
|
|
105
|
+
|
|
106
|
+
@patch("wbcore.contrib.workflow.models.step.UserStep.set_failed")
|
|
107
|
+
@patch("wbcore.contrib.workflow.models.step.UserStep.execute_assignee_method")
|
|
108
|
+
@patch("wbcore.contrib.workflow.models.step.UserStep.notify_assignees")
|
|
109
|
+
def test_run_no_transition(
|
|
110
|
+
self, mock_notify, mock_execute, mock_failed, process_step_factory, transition_factory, user_step_factory
|
|
111
|
+
):
|
|
112
|
+
kwargs = {"test": "Test"}
|
|
113
|
+
step = user_step_factory(notify_user=True, kwargs=kwargs)
|
|
114
|
+
UserFactory(groups=[step.group])
|
|
115
|
+
process_step = process_step_factory(state=ProcessStep.StepState.ACTIVE, step=step)
|
|
116
|
+
step.run(process_step)
|
|
117
|
+
assert not mock_notify.called
|
|
118
|
+
assert mock_execute.call_args.args == (process_step, step.assignee_method)
|
|
119
|
+
assert mock_execute.call_args.kwargs == kwargs
|
|
120
|
+
assert mock_failed.call_args.args[0] == process_step
|
|
121
|
+
|
|
122
|
+
@patch("wbcore.contrib.workflow.models.step.process_can_finish.delay")
|
|
123
|
+
@patch("wbcore.contrib.workflow.models.step.UserStep.notify_assignees")
|
|
124
|
+
def test_set_failed_user_step_notify_user(
|
|
125
|
+
self, mock_notify, mock_can_finish, process_step_factory, user_step_factory
|
|
126
|
+
):
|
|
127
|
+
step = user_step_factory(notify_user=True)
|
|
128
|
+
UserFactory(groups=[step.group])
|
|
129
|
+
process_step = process_step_factory(step=step, error_message=None)
|
|
130
|
+
error_message = "Error message"
|
|
131
|
+
step.set_failed(process_step, error_message)
|
|
132
|
+
assert process_step.state == ProcessStep.StepState.FAILED
|
|
133
|
+
assert process_step.error_message == error_message
|
|
134
|
+
assert mock_notify.call_args.args == (process_step.get_all_assignees(), process_step, error_message)
|
|
135
|
+
assert mock_can_finish.call_args.args == (process_step.process.pk,)
|
|
136
|
+
|
|
137
|
+
@patch("wbcore.contrib.workflow.models.step.process_can_finish.delay")
|
|
138
|
+
@patch("wbcore.contrib.workflow.models.step.UserStep.notify_assignees")
|
|
139
|
+
def test_set_failed_user_step_not_notify(
|
|
140
|
+
self, mock_notify, mock_can_finish, process_step_factory, user_step_factory
|
|
141
|
+
):
|
|
142
|
+
step = user_step_factory(notify_user=False)
|
|
143
|
+
UserFactory(groups=[step.group])
|
|
144
|
+
process_step = process_step_factory(step=step, error_message=None)
|
|
145
|
+
error_message = "Error message"
|
|
146
|
+
step.set_failed(process_step, error_message)
|
|
147
|
+
assert process_step.state == ProcessStep.StepState.FAILED
|
|
148
|
+
assert process_step.error_message == error_message
|
|
149
|
+
assert not mock_notify.called
|
|
150
|
+
assert mock_can_finish.call_args.args == (process_step.process.pk,)
|
|
151
|
+
|
|
152
|
+
@patch("wbcore.contrib.workflow.models.step.process_can_finish.delay")
|
|
153
|
+
@patch("wbcore.contrib.workflow.models.step.UserStep.notify_assignees")
|
|
154
|
+
def test_set_failed_user_step_no_assignees(
|
|
155
|
+
self, mock_notify, mock_can_finish, process_step_factory, user_step_factory
|
|
156
|
+
):
|
|
157
|
+
step = user_step_factory(notify_user=True)
|
|
158
|
+
process_step = process_step_factory(step=step, error_message=None)
|
|
159
|
+
error_message = "Error message"
|
|
160
|
+
step.set_failed(process_step, error_message)
|
|
161
|
+
assert process_step.state == ProcessStep.StepState.FAILED
|
|
162
|
+
assert process_step.error_message == error_message
|
|
163
|
+
assert not mock_notify.called
|
|
164
|
+
assert mock_can_finish.call_args.args == (process_step.process.pk,)
|
|
165
|
+
|
|
166
|
+
@patch("wbcore.contrib.workflow.models.step.send_notification")
|
|
167
|
+
def test_notify_single_assignee(self, mock_notification, process_step_factory, user_step_factory):
|
|
168
|
+
step = user_step_factory()
|
|
169
|
+
process_step = process_step_factory(step=step)
|
|
170
|
+
assignee_list = [UserFactory()]
|
|
171
|
+
step.notify_assignees(assignee_list, process_step)
|
|
172
|
+
assert mock_notification.call_args.kwargs == {
|
|
173
|
+
"code": "workflow.userstep.notify_next_step",
|
|
174
|
+
"title": "Workflow Step Awaiting Your Decision",
|
|
175
|
+
"body": "You were assigned to a workflow step. Please select the next step.",
|
|
176
|
+
"user": assignee_list[0],
|
|
177
|
+
"endpoint": reverse(f"{process_step.get_endpoint_basename()}-detail", args=[process_step.id]),
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
@patch("wbcore.contrib.workflow.models.step.send_notification")
|
|
181
|
+
def test_notify_assignee_error(self, mock_notification, process_step_factory, user_step_factory):
|
|
182
|
+
step = user_step_factory()
|
|
183
|
+
process_step = process_step_factory(step=step)
|
|
184
|
+
assignee_list = [UserFactory()]
|
|
185
|
+
error_message = "Test error"
|
|
186
|
+
step.notify_assignees(assignee_list, process_step, error_message)
|
|
187
|
+
assert mock_notification.call_args.kwargs == {
|
|
188
|
+
"code": "workflow.userstep.notify_failed_step",
|
|
189
|
+
"title": "Assigned Workflow Step Failed",
|
|
190
|
+
"body": f"A workflow step you were assigned to just failed with the error message '{error_message}'. Please take appropriate action.",
|
|
191
|
+
"user": assignee_list[0],
|
|
192
|
+
"endpoint": reverse(f"{process_step.get_endpoint_basename()}-detail", args=[process_step.id]),
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
@patch("wbcore.contrib.workflow.models.step.send_notification")
|
|
196
|
+
def test_notify_assignee_list(self, mock_notification, process_step_factory, user_step_factory):
|
|
197
|
+
step = user_step_factory()
|
|
198
|
+
process_step = process_step_factory(step=step)
|
|
199
|
+
assignee_list = UserFactory.create_batch(3)
|
|
200
|
+
step.notify_assignees(assignee_list, process_step)
|
|
201
|
+
assert mock_notification.call_count == 3
|
|
202
|
+
|
|
203
|
+
@patch("wbcore.contrib.workflow.models.step.send_notification")
|
|
204
|
+
def test_notify_assignee_inactive_user(self, mock_notification, process_step_factory, user_step_factory):
|
|
205
|
+
step = user_step_factory()
|
|
206
|
+
process_step = process_step_factory(step=step)
|
|
207
|
+
assignee_list = UserFactory.create_batch(3)
|
|
208
|
+
inactive_user = UserFactory(is_active=False)
|
|
209
|
+
assignee_list.append(inactive_user)
|
|
210
|
+
step.notify_assignees(assignee_list, process_step)
|
|
211
|
+
assert mock_notification.call_count == 3
|
|
212
|
+
for call in mock_notification.call_args_list:
|
|
213
|
+
assert call[1]["user"] != inactive_user
|
|
214
|
+
|
|
215
|
+
@patch("wbcore.contrib.workflow.models.step.send_notification")
|
|
216
|
+
@patch("wbcore.contrib.workflow.models.step.Step.user_can_see_step")
|
|
217
|
+
def test_notify_assignee_user_cant_see(
|
|
218
|
+
self, mock_can_see, mock_notification, process_step_factory, user_step_factory
|
|
219
|
+
):
|
|
220
|
+
step = user_step_factory()
|
|
221
|
+
process_step = process_step_factory(step=step)
|
|
222
|
+
assignee_list = [UserFactory()]
|
|
223
|
+
mock_can_see.return_value = False
|
|
224
|
+
step.notify_assignees(assignee_list, process_step)
|
|
225
|
+
assert not mock_notification.called
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
from unittest.mock import patch
|
|
2
|
+
|
|
3
|
+
import pytest
|
|
4
|
+
from wbcore.contrib.directory.factories import PersonFactory
|
|
5
|
+
from wbcore.contrib.workflow.models import Condition
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
@pytest.mark.django_db
|
|
9
|
+
class TestCondition:
|
|
10
|
+
def test_errors_satisfied_not_called(self, condition_factory):
|
|
11
|
+
condition = condition_factory()
|
|
12
|
+
with pytest.raises(ValueError):
|
|
13
|
+
condition.errors
|
|
14
|
+
|
|
15
|
+
def test_errors(self, condition_factory):
|
|
16
|
+
condition = condition_factory()
|
|
17
|
+
with patch.object(condition, "_errors", create=True):
|
|
18
|
+
assert condition.errors
|
|
19
|
+
|
|
20
|
+
def test_satisfied_attribute_error(self, condition_factory):
|
|
21
|
+
instance = PersonFactory()
|
|
22
|
+
condition = condition_factory(attribute_name="Test")
|
|
23
|
+
assert not condition.satisfied(instance)
|
|
24
|
+
assert len(condition.errors) == 1
|
|
25
|
+
|
|
26
|
+
@patch("wbcore.contrib.workflow.models.data.Data.cast_value_from_target_object")
|
|
27
|
+
def test_satisfied_value_error(self, mock_cast, condition_factory):
|
|
28
|
+
instance = PersonFactory()
|
|
29
|
+
condition = condition_factory()
|
|
30
|
+
mock_cast.side_effect = ValueError
|
|
31
|
+
assert not condition.satisfied(instance)
|
|
32
|
+
assert len(condition.errors) == 1
|
|
33
|
+
|
|
34
|
+
@patch("wbcore.contrib.workflow.models.data.Data.cast_value_from_target_object")
|
|
35
|
+
def test_satisfied_eq(self, mock_cast, condition_factory):
|
|
36
|
+
instance = PersonFactory()
|
|
37
|
+
condition1 = condition_factory(operator=Condition.Operator.EQ, negate_operator=False)
|
|
38
|
+
condition2 = condition_factory(operator=Condition.Operator.EQ, negate_operator=True)
|
|
39
|
+
mock_cast.return_value = instance.first_name
|
|
40
|
+
assert condition1.satisfied(instance)
|
|
41
|
+
assert not condition1.errors
|
|
42
|
+
assert not condition2.satisfied(instance)
|
|
43
|
+
assert not condition2.errors
|
|
44
|
+
|
|
45
|
+
@patch("wbcore.contrib.workflow.models.data.Data.cast_value_from_target_object")
|
|
46
|
+
def test_satisfied_gt(self, mock_cast, condition_factory):
|
|
47
|
+
instance = PersonFactory(personality_profile_red=5)
|
|
48
|
+
condition1 = condition_factory(
|
|
49
|
+
attribute_name="personality_profile_red", operator=Condition.Operator.GT, negate_operator=False
|
|
50
|
+
)
|
|
51
|
+
condition2 = condition_factory(
|
|
52
|
+
attribute_name="personality_profile_red", operator=Condition.Operator.GT, negate_operator=True
|
|
53
|
+
)
|
|
54
|
+
mock_cast.return_value = 3
|
|
55
|
+
assert condition1.satisfied(instance)
|
|
56
|
+
assert not condition1.errors
|
|
57
|
+
assert not condition2.satisfied(instance)
|
|
58
|
+
assert not condition2.errors
|
|
59
|
+
|
|
60
|
+
@patch("wbcore.contrib.workflow.models.data.Data.cast_value_from_target_object")
|
|
61
|
+
def test_satisfied_gte(self, mock_cast, condition_factory):
|
|
62
|
+
instance = PersonFactory(personality_profile_red=5)
|
|
63
|
+
condition1 = condition_factory(
|
|
64
|
+
attribute_name="personality_profile_red", operator=Condition.Operator.GTE, negate_operator=False
|
|
65
|
+
)
|
|
66
|
+
condition2 = condition_factory(
|
|
67
|
+
attribute_name="personality_profile_red", operator=Condition.Operator.GTE, negate_operator=True
|
|
68
|
+
)
|
|
69
|
+
mock_cast.return_value = 5
|
|
70
|
+
assert condition1.satisfied(instance)
|
|
71
|
+
assert not condition1.errors
|
|
72
|
+
assert not condition2.satisfied(instance)
|
|
73
|
+
assert not condition2.errors
|
|
74
|
+
|
|
75
|
+
@patch("wbcore.contrib.workflow.models.data.Data.cast_value_from_target_object")
|
|
76
|
+
def test_satisfied_lt(self, mock_cast, condition_factory):
|
|
77
|
+
instance = PersonFactory(personality_profile_red=2)
|
|
78
|
+
condition1 = condition_factory(
|
|
79
|
+
attribute_name="personality_profile_red", operator=Condition.Operator.LT, negate_operator=False
|
|
80
|
+
)
|
|
81
|
+
condition2 = condition_factory(
|
|
82
|
+
attribute_name="personality_profile_red", operator=Condition.Operator.LT, negate_operator=True
|
|
83
|
+
)
|
|
84
|
+
mock_cast.return_value = 5
|
|
85
|
+
assert condition1.satisfied(instance)
|
|
86
|
+
assert not condition1.errors
|
|
87
|
+
assert not condition2.satisfied(instance)
|
|
88
|
+
assert not condition2.errors
|
|
89
|
+
|
|
90
|
+
@patch("wbcore.contrib.workflow.models.data.Data.cast_value_from_target_object")
|
|
91
|
+
def test_satisfied_lte(self, mock_cast, condition_factory):
|
|
92
|
+
instance = PersonFactory(personality_profile_red=2)
|
|
93
|
+
condition1 = condition_factory(
|
|
94
|
+
attribute_name="personality_profile_red", operator=Condition.Operator.LTE, negate_operator=False
|
|
95
|
+
)
|
|
96
|
+
condition2 = condition_factory(
|
|
97
|
+
attribute_name="personality_profile_red", operator=Condition.Operator.LTE, negate_operator=True
|
|
98
|
+
)
|
|
99
|
+
mock_cast.return_value = 2
|
|
100
|
+
assert condition1.satisfied(instance)
|
|
101
|
+
assert not condition1.errors
|
|
102
|
+
assert not condition2.satisfied(instance)
|
|
103
|
+
assert not condition2.errors
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
from datetime import date, datetime
|
|
2
|
+
from unittest.mock import patch
|
|
3
|
+
|
|
4
|
+
import pytest
|
|
5
|
+
from wbcore.contrib.workflow.models import Data
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
@pytest.mark.django_db
|
|
9
|
+
class TestData:
|
|
10
|
+
@pytest.mark.parametrize(
|
|
11
|
+
("type", "field_name"),
|
|
12
|
+
[
|
|
13
|
+
(Data.DataType.CHAR, "CharField"),
|
|
14
|
+
(Data.DataType.BOOL, "BooleanField"),
|
|
15
|
+
(Data.DataType.DATE, "DateField"),
|
|
16
|
+
(Data.DataType.DATETIME, "DateTimeField"),
|
|
17
|
+
(Data.DataType.INT, "IntegerField"),
|
|
18
|
+
],
|
|
19
|
+
)
|
|
20
|
+
@patch("wbcore.contrib.workflow.models.data.Data.cast_value_to_datatype")
|
|
21
|
+
def test_get_serializer_field(self, mock_cast, data_factory, type, field_name):
|
|
22
|
+
data = data_factory(help_text=None, data_type=type)
|
|
23
|
+
field = data.get_serializer_field()
|
|
24
|
+
assert field.__class__.__name__ == field_name
|
|
25
|
+
assert field.label == data.label
|
|
26
|
+
assert field.required == data.required
|
|
27
|
+
assert "default" not in field._kwargs
|
|
28
|
+
assert "help_text" not in field._kwargs
|
|
29
|
+
assert not mock_cast.called
|
|
30
|
+
|
|
31
|
+
@patch("wbcore.contrib.workflow.models.data.Data.cast_value_to_datatype")
|
|
32
|
+
def test_get_serializer_field_help_text(self, mock_cast, data_factory):
|
|
33
|
+
data = data_factory()
|
|
34
|
+
field = data.get_serializer_field()
|
|
35
|
+
assert field.label == data.label
|
|
36
|
+
assert field.required == data.required
|
|
37
|
+
assert "default" not in field._kwargs
|
|
38
|
+
assert field.help_text == data.help_text
|
|
39
|
+
assert not mock_cast.called
|
|
40
|
+
|
|
41
|
+
@patch("wbcore.contrib.workflow.models.data.Data.cast_value_to_datatype")
|
|
42
|
+
def test_get_serializer_field_default(self, mock_cast, data_factory):
|
|
43
|
+
data_type = Data.DataType.INT
|
|
44
|
+
default_value = "50"
|
|
45
|
+
casted_default_value = 50
|
|
46
|
+
data = data_factory(default=default_value, data_type=data_type)
|
|
47
|
+
mock_cast.return_value = casted_default_value
|
|
48
|
+
field = data.get_serializer_field()
|
|
49
|
+
assert mock_cast.call_args.args == (data_type, default_value)
|
|
50
|
+
assert field.label == data.label
|
|
51
|
+
assert field.required == data.required
|
|
52
|
+
assert field.default == casted_default_value
|
|
53
|
+
assert field.help_text == data.help_text
|
|
54
|
+
|
|
55
|
+
@pytest.mark.parametrize(
|
|
56
|
+
("data_type", "value", "expected"),
|
|
57
|
+
[
|
|
58
|
+
(Data.DataType.BOOL, "True", True),
|
|
59
|
+
(Data.DataType.BOOL, "true", True),
|
|
60
|
+
(Data.DataType.BOOL, "False", False),
|
|
61
|
+
(Data.DataType.BOOL, "false", False),
|
|
62
|
+
(Data.DataType.DATE, "12.07.1054", date(1054, 7, 12)),
|
|
63
|
+
(Data.DataType.DATETIME, "12.07.1054 13:47:09", datetime(1054, 7, 12, 13, 47, 9)),
|
|
64
|
+
(Data.DataType.CHAR, "123456", "123456"),
|
|
65
|
+
(Data.DataType.INT, "123456", 123456),
|
|
66
|
+
],
|
|
67
|
+
)
|
|
68
|
+
def test_cast_value_to_datatype(self, data_type, value, expected):
|
|
69
|
+
assert Data.cast_value_to_datatype(data_type, value) == expected
|
|
70
|
+
|
|
71
|
+
@pytest.mark.parametrize(
|
|
72
|
+
("data_type", "value"),
|
|
73
|
+
[
|
|
74
|
+
(Data.DataType.BOOL, "Test"),
|
|
75
|
+
(Data.DataType.BOOL, "1"),
|
|
76
|
+
(Data.DataType.BOOL, "0"),
|
|
77
|
+
(Data.DataType.DATE, "07/12/1054"),
|
|
78
|
+
(Data.DataType.DATETIME, "07/12/1054 13:47:09"),
|
|
79
|
+
(Data.DataType.INT, "Test"),
|
|
80
|
+
],
|
|
81
|
+
)
|
|
82
|
+
def test_cast_value_to_datatype_error(self, data_type, value):
|
|
83
|
+
with pytest.raises(ValueError):
|
|
84
|
+
Data.cast_value_to_datatype(data_type, value)
|
|
85
|
+
|
|
86
|
+
@pytest.mark.parametrize(
|
|
87
|
+
("target", "data_type"),
|
|
88
|
+
[
|
|
89
|
+
(datetime(2003, 8, 28, 7, 18, 4), Data.DataType.DATETIME),
|
|
90
|
+
(date(2003, 8, 28), Data.DataType.DATE),
|
|
91
|
+
(True, Data.DataType.BOOL),
|
|
92
|
+
(False, Data.DataType.BOOL),
|
|
93
|
+
(69, Data.DataType.INT),
|
|
94
|
+
("Test", Data.DataType.CHAR),
|
|
95
|
+
],
|
|
96
|
+
)
|
|
97
|
+
@patch("wbcore.contrib.workflow.models.data.Data.cast_value_to_datatype")
|
|
98
|
+
def test_cast_value_from_target_object(self, mock_cast, target, data_type):
|
|
99
|
+
Data.cast_value_from_target_object(target, "Test")
|
|
100
|
+
assert mock_cast.call_args.args == (data_type, "Test")
|
|
101
|
+
|
|
102
|
+
@pytest.mark.parametrize(
|
|
103
|
+
"target",
|
|
104
|
+
[None, Data, str, {}, []],
|
|
105
|
+
)
|
|
106
|
+
def test_cast_value_from_target_object_error(self, target):
|
|
107
|
+
with pytest.raises(ValueError):
|
|
108
|
+
Data.cast_value_from_target_object(target, "Test")
|
|
109
|
+
|
|
110
|
+
@pytest.mark.parametrize(
|
|
111
|
+
("data_object", "data_type", "expected"),
|
|
112
|
+
[
|
|
113
|
+
(datetime(2003, 8, 28, 7, 18, 4), Data.DataType.DATETIME, "28.08.2003 07:18:04"),
|
|
114
|
+
(date(2003, 8, 28), Data.DataType.DATE, "28.08.2003"),
|
|
115
|
+
(True, Data.DataType.BOOL, "True"),
|
|
116
|
+
(False, Data.DataType.BOOL, "False"),
|
|
117
|
+
(69, Data.DataType.INT, "69"),
|
|
118
|
+
("Test", Data.DataType.CHAR, "Test"),
|
|
119
|
+
],
|
|
120
|
+
)
|
|
121
|
+
def test_cast_datatype_to_string(self, data_type, data_object, expected):
|
|
122
|
+
assert Data.cast_datatype_to_string(data_type, data_object) == expected
|
|
123
|
+
|
|
124
|
+
@pytest.mark.parametrize(
|
|
125
|
+
("data_object", "data_type"),
|
|
126
|
+
[
|
|
127
|
+
(69, Data.DataType.DATETIME),
|
|
128
|
+
("Test", Data.DataType.DATE),
|
|
129
|
+
(datetime(2003, 8, 28, 7, 18, 4), Data.DataType.BOOL),
|
|
130
|
+
],
|
|
131
|
+
)
|
|
132
|
+
def test_cast_datatype_to_string_error(self, data_type, data_object):
|
|
133
|
+
with pytest.raises(ValueError):
|
|
134
|
+
Data.cast_datatype_to_string(data_type, data_object)
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
from unittest.mock import patch
|
|
2
|
+
|
|
3
|
+
import pytest
|
|
4
|
+
from wbcore.contrib.authentication.factories import GroupFactory, UserFactory
|
|
5
|
+
from wbcore.contrib.directory.factories import PersonFactory
|
|
6
|
+
from wbcore.contrib.workflow.factories import UserStepFactory
|
|
7
|
+
from wbcore.contrib.workflow.models import Process, ProcessStep
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
@pytest.mark.django_db
|
|
11
|
+
class TestProcess:
|
|
12
|
+
@patch("wbcore.contrib.workflow.models.process.ProcessStep.get_all_assignees")
|
|
13
|
+
@patch("wbcore.contrib.workflow.models.step.UserStep.notify_assignees")
|
|
14
|
+
def test_set_failed(
|
|
15
|
+
self,
|
|
16
|
+
mock_notify,
|
|
17
|
+
mock_assignees,
|
|
18
|
+
process_step_factory,
|
|
19
|
+
process_factory,
|
|
20
|
+
random_child_step_factory,
|
|
21
|
+
user_step_factory,
|
|
22
|
+
):
|
|
23
|
+
step = random_child_step_factory(exclude_factories=[UserStepFactory])
|
|
24
|
+
user_step_no_notify = user_step_factory(notify_user=False)
|
|
25
|
+
user_step_notify = user_step_factory(notify_user=True)
|
|
26
|
+
process = process_factory(state=Process.ProcessState.ACTIVE)
|
|
27
|
+
process_step1 = process_step_factory(
|
|
28
|
+
state=ProcessStep.StepState.ACTIVE, process=process, step=step, error_message=None
|
|
29
|
+
)
|
|
30
|
+
process_step2 = process_step_factory(
|
|
31
|
+
state=ProcessStep.StepState.WAITING, process=process, step=step, error_message=None
|
|
32
|
+
)
|
|
33
|
+
process_step3 = process_step_factory(
|
|
34
|
+
state=ProcessStep.StepState.CANCELED, process=process, step=step, error_message=None
|
|
35
|
+
)
|
|
36
|
+
process_step4 = process_step_factory(
|
|
37
|
+
state=ProcessStep.StepState.FINISHED, process=process, step=step, error_message=None
|
|
38
|
+
)
|
|
39
|
+
process_step5 = process_step_factory(state=ProcessStep.StepState.ACTIVE, step=step, error_message=None)
|
|
40
|
+
process_step6 = process_step_factory(
|
|
41
|
+
state=ProcessStep.StepState.ACTIVE, process=process, step=user_step_no_notify, error_message=None
|
|
42
|
+
)
|
|
43
|
+
process_step7 = process_step_factory(
|
|
44
|
+
state=ProcessStep.StepState.ACTIVE, process=process, step=user_step_notify, error_message=None
|
|
45
|
+
)
|
|
46
|
+
user = UserFactory()
|
|
47
|
+
mock_assignees.return_value = [user]
|
|
48
|
+
process.set_failed()
|
|
49
|
+
assert mock_notify.call_count == 1
|
|
50
|
+
assert mock_notify.call_args.args[0] == [user]
|
|
51
|
+
assert str(mock_notify.call_args.args[1].pk) == process_step7.pk
|
|
52
|
+
assert mock_assignees.call_count == 1
|
|
53
|
+
for process_step in ProcessStep.objects.filter(
|
|
54
|
+
id__in=[process_step1.pk, process_step2.pk, process_step6.pk, process_step7.pk]
|
|
55
|
+
):
|
|
56
|
+
assert process_step.state == ProcessStep.StepState.FAILED
|
|
57
|
+
assert process_step.error_message
|
|
58
|
+
for process_step in ProcessStep.objects.filter(id__in=[process_step3.pk, process_step4.pk, process_step5.pk]):
|
|
59
|
+
assert process_step.state != ProcessStep.StepState.FAILED
|
|
60
|
+
assert process_step.error_message is None
|
|
61
|
+
assert Process.objects.get(id=process.pk).state == Process.ProcessState.FAILED
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
@pytest.mark.django_db
|
|
65
|
+
class TestProcessStep:
|
|
66
|
+
def test_get_instance(self, process_step_factory):
|
|
67
|
+
attached_instance = PersonFactory()
|
|
68
|
+
process_step = process_step_factory(process__instance=attached_instance)
|
|
69
|
+
assert process_step.get_instance() == attached_instance
|
|
70
|
+
|
|
71
|
+
def test_get_instance_no_attached(self, process_step_factory):
|
|
72
|
+
process_step = process_step_factory(process__instance=None, process__instance_id=None)
|
|
73
|
+
assert process_step.get_instance() is None
|
|
74
|
+
|
|
75
|
+
def test_get_all_assignees_assignee(self, process_step_factory):
|
|
76
|
+
user = UserFactory()
|
|
77
|
+
group = GroupFactory()
|
|
78
|
+
process_step = process_step_factory(assignee=user, group=group)
|
|
79
|
+
assert process_step.get_all_assignees() == [user]
|
|
80
|
+
|
|
81
|
+
def test_get_all_assignees_group(self, process_step_factory):
|
|
82
|
+
group = GroupFactory()
|
|
83
|
+
user1 = UserFactory(groups=[group])
|
|
84
|
+
user2 = UserFactory(groups=[group])
|
|
85
|
+
UserFactory()
|
|
86
|
+
process_step = process_step_factory(group=group, assignee=None)
|
|
87
|
+
assert set(process_step.get_all_assignees()) == {user1, user2}
|
|
88
|
+
|
|
89
|
+
def test_get_all_assignees_empty_group(self, process_step_factory):
|
|
90
|
+
group = GroupFactory()
|
|
91
|
+
process_step = process_step_factory(group=group, assignee=None)
|
|
92
|
+
assert process_step.get_all_assignees() == []
|
|
93
|
+
|
|
94
|
+
def test_get_all_assignees_none(self, process_step_factory):
|
|
95
|
+
group = GroupFactory()
|
|
96
|
+
UserFactory(groups=[group])
|
|
97
|
+
process_step = process_step_factory(group=None, assignee=None)
|
|
98
|
+
assert process_step.get_all_assignees() == []
|