wbmailing 2.2.1__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.
Potentially problematic release.
This version of wbmailing might be problematic. Click here for more details.
- wbmailing/__init__.py +1 -0
- wbmailing/admin.py +74 -0
- wbmailing/apps.py +14 -0
- wbmailing/backend.py +131 -0
- wbmailing/celery.py +0 -0
- wbmailing/dynamic_preferences_registry.py +35 -0
- wbmailing/factories.py +211 -0
- wbmailing/filters/__init__.py +8 -0
- wbmailing/filters/mailing_lists.py +84 -0
- wbmailing/filters/mails.py +74 -0
- wbmailing/management/__init__.py +22 -0
- wbmailing/migrations/0001_initial_squashed_squashed_0008_alter_mail_bcc_email_alter_mail_cc_email_and_more.py +649 -0
- wbmailing/migrations/0002_delete_mailingsettings.py +16 -0
- wbmailing/migrations/0003_alter_mailinglistsubscriberchangerequest_options.py +25 -0
- wbmailing/migrations/__init__.py +0 -0
- wbmailing/models/__init__.py +6 -0
- wbmailing/models/mailing_lists.py +386 -0
- wbmailing/models/mails.py +895 -0
- wbmailing/serializers/__init__.py +19 -0
- wbmailing/serializers/mailing_lists.py +209 -0
- wbmailing/serializers/mails.py +251 -0
- wbmailing/tasks.py +37 -0
- wbmailing/templatetags/__init__.py +0 -0
- wbmailing/templatetags/mailing_tags.py +22 -0
- wbmailing/tests/__init__.py +0 -0
- wbmailing/tests/conftest.py +30 -0
- wbmailing/tests/models/__init__.py +0 -0
- wbmailing/tests/models/test_mailing_lists.py +297 -0
- wbmailing/tests/models/test_mails.py +205 -0
- wbmailing/tests/signals.py +124 -0
- wbmailing/tests/test_serializers.py +28 -0
- wbmailing/tests/test_tasks.py +49 -0
- wbmailing/tests/test_viewsets.py +216 -0
- wbmailing/tests/tests.py +142 -0
- wbmailing/urls.py +90 -0
- wbmailing/viewsets/__init__.py +32 -0
- wbmailing/viewsets/analytics.py +110 -0
- wbmailing/viewsets/buttons/__init__.py +10 -0
- wbmailing/viewsets/buttons/mailing_lists.py +91 -0
- wbmailing/viewsets/buttons/mails.py +98 -0
- wbmailing/viewsets/display/__init__.py +16 -0
- wbmailing/viewsets/display/mailing_lists.py +175 -0
- wbmailing/viewsets/display/mails.py +318 -0
- wbmailing/viewsets/endpoints/__init__.py +8 -0
- wbmailing/viewsets/endpoints/mailing_lists.py +86 -0
- wbmailing/viewsets/endpoints/mails.py +51 -0
- wbmailing/viewsets/mailing_lists.py +320 -0
- wbmailing/viewsets/mails.py +425 -0
- wbmailing/viewsets/menu/__init__.py +5 -0
- wbmailing/viewsets/menu/mailing_lists.py +37 -0
- wbmailing/viewsets/menu/mails.py +25 -0
- wbmailing/viewsets/titles/__init__.py +17 -0
- wbmailing/viewsets/titles/mailing_lists.py +63 -0
- wbmailing/viewsets/titles/mails.py +55 -0
- wbmailing-2.2.1.dist-info/METADATA +5 -0
- wbmailing-2.2.1.dist-info/RECORD +57 -0
- wbmailing-2.2.1.dist-info/WHEEL +5 -0
|
@@ -0,0 +1,297 @@
|
|
|
1
|
+
import pytest
|
|
2
|
+
from django.contrib.auth.models import Permission
|
|
3
|
+
from faker import Faker
|
|
4
|
+
from wbcore.contrib.authentication.factories import UserFactory
|
|
5
|
+
from wbmailing.models.mailing_lists import (
|
|
6
|
+
MailingList,
|
|
7
|
+
MailingListEmailContactThroughModel,
|
|
8
|
+
MailingListSubscriberChangeRequest,
|
|
9
|
+
)
|
|
10
|
+
|
|
11
|
+
fake = Faker()
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
@pytest.mark.django_db
|
|
15
|
+
class TestMailingListSubscriberChangeRequest:
|
|
16
|
+
@pytest.fixture
|
|
17
|
+
def user_admin(self):
|
|
18
|
+
user = UserFactory.create()
|
|
19
|
+
perm = Permission.objects.get(codename="administrate_mailinglistsubscriberchangerequest")
|
|
20
|
+
user.user_permissions.add(perm)
|
|
21
|
+
return user
|
|
22
|
+
|
|
23
|
+
def test_init(self, mailing_list_subscriber_change_request):
|
|
24
|
+
"""
|
|
25
|
+
Test basics creation logic:
|
|
26
|
+
- relationship creation from email contact and mailing list
|
|
27
|
+
"""
|
|
28
|
+
assert mailing_list_subscriber_change_request
|
|
29
|
+
assert mailing_list_subscriber_change_request.status == MailingListSubscriberChangeRequest.Status.PENDING
|
|
30
|
+
rel = mailing_list_subscriber_change_request.relationship
|
|
31
|
+
assert rel.email_contact == mailing_list_subscriber_change_request.email_contact
|
|
32
|
+
assert rel.mailing_list == mailing_list_subscriber_change_request.mailing_list
|
|
33
|
+
assert rel.status == MailingListEmailContactThroughModel.Status.UNSUBSCRIBED
|
|
34
|
+
|
|
35
|
+
@pytest.mark.parametrize("mailing_list__is_public", [True, False])
|
|
36
|
+
def test_init_public_mailing_list(self, mailing_list_subscriber_change_request_factory, mailing_list):
|
|
37
|
+
"""
|
|
38
|
+
Test if a request subscription creation to a public mailing list is automatically approved
|
|
39
|
+
"""
|
|
40
|
+
request = mailing_list_subscriber_change_request_factory.create(mailing_list=mailing_list)
|
|
41
|
+
assert request.type == MailingListSubscriberChangeRequest.Type.SUBSCRIBING
|
|
42
|
+
if mailing_list.is_public:
|
|
43
|
+
assert request.status == MailingListSubscriberChangeRequest.Status.APPROVED
|
|
44
|
+
else:
|
|
45
|
+
assert request.status == MailingListSubscriberChangeRequest.Status.PENDING
|
|
46
|
+
|
|
47
|
+
@pytest.mark.parametrize(
|
|
48
|
+
"mailing_list_subscriber_change_request__type, description",
|
|
49
|
+
[
|
|
50
|
+
(MailingListSubscriberChangeRequest.Type.SUBSCRIBING, fake.sentence()),
|
|
51
|
+
(MailingListSubscriberChangeRequest.Type.UNSUBSCRIBING, fake.sentence()),
|
|
52
|
+
],
|
|
53
|
+
)
|
|
54
|
+
def test_approve(self, mailing_list_subscriber_change_request, user, description):
|
|
55
|
+
mailing_list_subscriber_change_request.approve(by=user, description=description)
|
|
56
|
+
if mailing_list_subscriber_change_request.type == MailingListSubscriberChangeRequest.Type.SUBSCRIBING:
|
|
57
|
+
assert (
|
|
58
|
+
mailing_list_subscriber_change_request.relationship.status
|
|
59
|
+
== MailingListEmailContactThroughModel.Status.SUBSCRIBED
|
|
60
|
+
)
|
|
61
|
+
else:
|
|
62
|
+
assert (
|
|
63
|
+
mailing_list_subscriber_change_request.relationship.status
|
|
64
|
+
== MailingListEmailContactThroughModel.Status.UNSUBSCRIBED
|
|
65
|
+
)
|
|
66
|
+
assert mailing_list_subscriber_change_request.approver == user.profile
|
|
67
|
+
assert mailing_list_subscriber_change_request.reason == description
|
|
68
|
+
|
|
69
|
+
@pytest.mark.parametrize("description", [fake.sentence()])
|
|
70
|
+
def test_deny(self, mailing_list_subscriber_change_request, user, description):
|
|
71
|
+
"""
|
|
72
|
+
Coverage test and unit test on description and approver from action trigerer
|
|
73
|
+
"""
|
|
74
|
+
mailing_list_subscriber_change_request.deny(by=user, description=description)
|
|
75
|
+
mailing_list_subscriber_change_request.save()
|
|
76
|
+
assert mailing_list_subscriber_change_request.approver == user.profile
|
|
77
|
+
assert mailing_list_subscriber_change_request.reason == description
|
|
78
|
+
|
|
79
|
+
@pytest.mark.parametrize(
|
|
80
|
+
"mailing_list_email_contact_through_model__status, type, res",
|
|
81
|
+
[
|
|
82
|
+
(
|
|
83
|
+
MailingListEmailContactThroughModel.Status.SUBSCRIBED,
|
|
84
|
+
MailingListSubscriberChangeRequest.Type.UNSUBSCRIBING,
|
|
85
|
+
False,
|
|
86
|
+
),
|
|
87
|
+
(
|
|
88
|
+
MailingListEmailContactThroughModel.Status.SUBSCRIBED,
|
|
89
|
+
MailingListSubscriberChangeRequest.Type.SUBSCRIBING,
|
|
90
|
+
False,
|
|
91
|
+
),
|
|
92
|
+
(
|
|
93
|
+
MailingListEmailContactThroughModel.Status.UNSUBSCRIBED,
|
|
94
|
+
MailingListSubscriberChangeRequest.Type.UNSUBSCRIBING,
|
|
95
|
+
False,
|
|
96
|
+
),
|
|
97
|
+
(
|
|
98
|
+
MailingListEmailContactThroughModel.Status.UNSUBSCRIBED,
|
|
99
|
+
MailingListSubscriberChangeRequest.Type.SUBSCRIBING,
|
|
100
|
+
True,
|
|
101
|
+
),
|
|
102
|
+
],
|
|
103
|
+
)
|
|
104
|
+
def test_subscribing(
|
|
105
|
+
self, mailing_list_email_contact_through_model, mailing_list_subscriber_change_request_factory, type, res
|
|
106
|
+
):
|
|
107
|
+
"""
|
|
108
|
+
Basic property result check
|
|
109
|
+
"""
|
|
110
|
+
request = mailing_list_subscriber_change_request_factory.create(
|
|
111
|
+
type=type,
|
|
112
|
+
relationship=mailing_list_email_contact_through_model,
|
|
113
|
+
email_contact=mailing_list_email_contact_through_model.email_contact,
|
|
114
|
+
mailing_list=mailing_list_email_contact_through_model.mailing_list,
|
|
115
|
+
)
|
|
116
|
+
assert request.subscribing == res
|
|
117
|
+
|
|
118
|
+
def test_get_expired_date_subquery(
|
|
119
|
+
self, email_contact, mailing_list, mailing_list_subscriber_change_request_factory
|
|
120
|
+
):
|
|
121
|
+
"""
|
|
122
|
+
Check that expired date is the last approved and subscribing mailing change request
|
|
123
|
+
"""
|
|
124
|
+
mailing_list_subscriber_change_request_factory.create(
|
|
125
|
+
type=MailingListSubscriberChangeRequest.Type.SUBSCRIBING,
|
|
126
|
+
status=MailingListSubscriberChangeRequest.Status.APPROVED,
|
|
127
|
+
mailing_list=mailing_list,
|
|
128
|
+
email_contact=email_contact,
|
|
129
|
+
expiration_date=fake.date_object(),
|
|
130
|
+
) # oldest valid request
|
|
131
|
+
req = mailing_list_subscriber_change_request_factory.create(
|
|
132
|
+
type=MailingListSubscriberChangeRequest.Type.SUBSCRIBING,
|
|
133
|
+
status=MailingListSubscriberChangeRequest.Status.APPROVED,
|
|
134
|
+
mailing_list=mailing_list,
|
|
135
|
+
email_contact=email_contact,
|
|
136
|
+
expiration_date=fake.date_object(),
|
|
137
|
+
) # expected expiration time request
|
|
138
|
+
mailing_list_subscriber_change_request_factory.create(
|
|
139
|
+
type=MailingListSubscriberChangeRequest.Type.UNSUBSCRIBING,
|
|
140
|
+
status=MailingListSubscriberChangeRequest.Status.APPROVED,
|
|
141
|
+
mailing_list=mailing_list,
|
|
142
|
+
email_contact=email_contact,
|
|
143
|
+
expiration_date=fake.date_object(),
|
|
144
|
+
) # Unvalid request because not subscribing
|
|
145
|
+
mailing_list_subscriber_change_request_factory.create(
|
|
146
|
+
type=MailingListSubscriberChangeRequest.Type.SUBSCRIBING,
|
|
147
|
+
status=MailingListSubscriberChangeRequest.Status.PENDING,
|
|
148
|
+
mailing_list=mailing_list,
|
|
149
|
+
email_contact=email_contact,
|
|
150
|
+
expiration_date=fake.date_object(),
|
|
151
|
+
) # Unvalid request because pending
|
|
152
|
+
assert (
|
|
153
|
+
MailingListEmailContactThroughModel.objects.annotate(
|
|
154
|
+
expiration_date=MailingListEmailContactThroughModel.get_expired_date_subquery()
|
|
155
|
+
)
|
|
156
|
+
.filter(mailing_list=mailing_list, email_contact=email_contact)
|
|
157
|
+
.values_list("expiration_date", flat=True)[0]
|
|
158
|
+
== req.expiration_date
|
|
159
|
+
)
|
|
160
|
+
|
|
161
|
+
def test_get_approvers(self, user, user_admin):
|
|
162
|
+
"""
|
|
163
|
+
Test that approvers are the proper user with admin rights
|
|
164
|
+
"""
|
|
165
|
+
assert set(MailingListSubscriberChangeRequest.get_approvers()) == {user_admin}
|
|
166
|
+
assert not MailingListSubscriberChangeRequest.get_approvers().filter(id=user.id).exists()
|
|
167
|
+
|
|
168
|
+
|
|
169
|
+
@pytest.mark.django_db
|
|
170
|
+
class TestMailingListEmailContactThroughModel:
|
|
171
|
+
def test_init(self, mailing_list_email_contact_through_model):
|
|
172
|
+
assert mailing_list_email_contact_through_model
|
|
173
|
+
|
|
174
|
+
def test_change_state(self, mailing_list_email_contact_through_model):
|
|
175
|
+
initial_status = mailing_list_email_contact_through_model.status
|
|
176
|
+
mailing_list_email_contact_through_model.change_state()
|
|
177
|
+
assert mailing_list_email_contact_through_model.status == initial_status
|
|
178
|
+
assert (
|
|
179
|
+
mailing_list_email_contact_through_model.requests.filter(
|
|
180
|
+
status=MailingListSubscriberChangeRequest.Status.PENDING
|
|
181
|
+
).count()
|
|
182
|
+
== 1
|
|
183
|
+
)
|
|
184
|
+
mailing_list_email_contact_through_model.change_state(automatically_approve=True)
|
|
185
|
+
mailing_list_email_contact_through_model.refresh_from_db()
|
|
186
|
+
assert not mailing_list_email_contact_through_model.requests.filter(
|
|
187
|
+
status=MailingListSubscriberChangeRequest.Status.PENDING
|
|
188
|
+
).exists()
|
|
189
|
+
assert mailing_list_email_contact_through_model.status != initial_status
|
|
190
|
+
|
|
191
|
+
|
|
192
|
+
@pytest.mark.django_db
|
|
193
|
+
class TestMailingList:
|
|
194
|
+
def test_init(self, mailing_list):
|
|
195
|
+
assert mailing_list
|
|
196
|
+
|
|
197
|
+
@pytest.mark.parametrize(
|
|
198
|
+
"mailing_list_email_contact_through_model__status", [MailingListEmailContactThroughModel.Status.SUBSCRIBED]
|
|
199
|
+
)
|
|
200
|
+
def test_unsubscription(self, mailing_list_email_contact_through_model):
|
|
201
|
+
"""
|
|
202
|
+
Test unsubscription
|
|
203
|
+
"""
|
|
204
|
+
email_contact = mailing_list_email_contact_through_model.email_contact
|
|
205
|
+
mailing_list = mailing_list_email_contact_through_model.mailing_list
|
|
206
|
+
|
|
207
|
+
mailing_list.unsubscribe(email_contact)
|
|
208
|
+
req = mailing_list_email_contact_through_model.requests.get(
|
|
209
|
+
status=MailingListSubscriberChangeRequest.Status.PENDING
|
|
210
|
+
)
|
|
211
|
+
assert (
|
|
212
|
+
mailing_list_email_contact_through_model.status == MailingListEmailContactThroughModel.Status.SUBSCRIBED
|
|
213
|
+
) # We expect the contact to still be subscribed because the unsubscription request is still pending
|
|
214
|
+
req.approve()
|
|
215
|
+
mailing_list_email_contact_through_model.refresh_from_db()
|
|
216
|
+
assert (
|
|
217
|
+
mailing_list_email_contact_through_model.status == MailingListEmailContactThroughModel.Status.UNSUBSCRIBED
|
|
218
|
+
)
|
|
219
|
+
|
|
220
|
+
@pytest.mark.parametrize(
|
|
221
|
+
"mailing_list_email_contact_through_model__status", [MailingListEmailContactThroughModel.Status.SUBSCRIBED]
|
|
222
|
+
)
|
|
223
|
+
def test_unsubscription_automatically_approve(self, mailing_list_email_contact_through_model):
|
|
224
|
+
"""
|
|
225
|
+
Test automatically approved unsubscription change request
|
|
226
|
+
"""
|
|
227
|
+
email_contact = mailing_list_email_contact_through_model.email_contact
|
|
228
|
+
mailing_list = mailing_list_email_contact_through_model.mailing_list
|
|
229
|
+
|
|
230
|
+
mailing_list.unsubscribe(email_contact, automatically_approve=True)
|
|
231
|
+
mailing_list_email_contact_through_model.refresh_from_db()
|
|
232
|
+
assert (
|
|
233
|
+
mailing_list_email_contact_through_model.status == MailingListEmailContactThroughModel.Status.UNSUBSCRIBED
|
|
234
|
+
)
|
|
235
|
+
|
|
236
|
+
@pytest.mark.parametrize(
|
|
237
|
+
"mailing_list_email_contact_through_model__status", [MailingListEmailContactThroughModel.Status.UNSUBSCRIBED]
|
|
238
|
+
)
|
|
239
|
+
def test_subscription(self, mailing_list_email_contact_through_model):
|
|
240
|
+
"""
|
|
241
|
+
Test subscription change request
|
|
242
|
+
"""
|
|
243
|
+
email_contact = mailing_list_email_contact_through_model.email_contact
|
|
244
|
+
mailing_list = mailing_list_email_contact_through_model.mailing_list
|
|
245
|
+
|
|
246
|
+
mailing_list.subscribe(email_contact)
|
|
247
|
+
req = mailing_list_email_contact_through_model.requests.get(
|
|
248
|
+
status=MailingListSubscriberChangeRequest.Status.PENDING
|
|
249
|
+
)
|
|
250
|
+
assert (
|
|
251
|
+
mailing_list_email_contact_through_model.status == MailingListEmailContactThroughModel.Status.UNSUBSCRIBED
|
|
252
|
+
) # We expect the contact to still be subscribed because the unsubscription request is still pending
|
|
253
|
+
req.approve()
|
|
254
|
+
mailing_list_email_contact_through_model.refresh_from_db()
|
|
255
|
+
assert mailing_list_email_contact_through_model.status == MailingListEmailContactThroughModel.Status.SUBSCRIBED
|
|
256
|
+
|
|
257
|
+
@pytest.mark.parametrize(
|
|
258
|
+
"mailing_list_email_contact_through_model__status", [MailingListEmailContactThroughModel.Status.UNSUBSCRIBED]
|
|
259
|
+
)
|
|
260
|
+
def test_subscription_automatically_approve(self, mailing_list_email_contact_through_model):
|
|
261
|
+
"""
|
|
262
|
+
Test automatically approved subscription change request
|
|
263
|
+
"""
|
|
264
|
+
email_contact = mailing_list_email_contact_through_model.email_contact
|
|
265
|
+
mailing_list = mailing_list_email_contact_through_model.mailing_list
|
|
266
|
+
|
|
267
|
+
mailing_list.subscribe(email_contact, automatically_approve=True)
|
|
268
|
+
mailing_list_email_contact_through_model.refresh_from_db()
|
|
269
|
+
assert mailing_list_email_contact_through_model.status == MailingListEmailContactThroughModel.Status.SUBSCRIBED
|
|
270
|
+
|
|
271
|
+
def test_get_subscribed_mailing_lists(self, mailing_list_email_contact_through_model_factory):
|
|
272
|
+
"""
|
|
273
|
+
Test subscribed mailing list for a email contact.
|
|
274
|
+
"""
|
|
275
|
+
rel_e1_ml1_subscribed = mailing_list_email_contact_through_model_factory.create(
|
|
276
|
+
status=MailingListEmailContactThroughModel.Status.SUBSCRIBED
|
|
277
|
+
)
|
|
278
|
+
e1 = rel_e1_ml1_subscribed.email_contact
|
|
279
|
+
m1 = rel_e1_ml1_subscribed.mailing_list
|
|
280
|
+
|
|
281
|
+
rel_e1_rel_ml2_unsubscribed = mailing_list_email_contact_through_model_factory.create(
|
|
282
|
+
email_contact=e1, status=MailingListEmailContactThroughModel.Status.UNSUBSCRIBED
|
|
283
|
+
) # We expect e1 to not show up in the valid emails queryset
|
|
284
|
+
m2 = rel_e1_rel_ml2_unsubscribed.mailing_list
|
|
285
|
+
|
|
286
|
+
rel_e2_ml1_subscribed = mailing_list_email_contact_through_model_factory.create(
|
|
287
|
+
mailing_list=m1, status=MailingListEmailContactThroughModel.Status.SUBSCRIBED
|
|
288
|
+
)
|
|
289
|
+
e2 = rel_e2_ml1_subscribed.email_contact
|
|
290
|
+
mailing_list_email_contact_through_model_factory.create(
|
|
291
|
+
email_contact=e2, mailing_list=m2, status=MailingListEmailContactThroughModel.Status.SUBSCRIBED
|
|
292
|
+
)
|
|
293
|
+
|
|
294
|
+
assert set(MailingList.get_subscribed_mailing_lists(e1)) == {
|
|
295
|
+
m1,
|
|
296
|
+
}
|
|
297
|
+
assert set(MailingList.get_subscribed_mailing_lists(e2)) == {m1, m2}
|
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
from unittest.mock import patch
|
|
2
|
+
|
|
3
|
+
import pytest
|
|
4
|
+
from anymail.signals import AnymailTrackingEvent, tracking
|
|
5
|
+
from django.core import mail
|
|
6
|
+
from django.test import override_settings
|
|
7
|
+
from rest_framework.test import APIRequestFactory
|
|
8
|
+
from wbcore.contrib.directory.factories import EmailContactFactory
|
|
9
|
+
from wbcore.contrib.documents.factories import DocumentFactory
|
|
10
|
+
from wbcore.test.utils import get_or_create_superuser
|
|
11
|
+
from wbmailing.backend import SendgridEmailBackend
|
|
12
|
+
from wbmailing.factories import ToEmailMailFactory
|
|
13
|
+
from wbmailing.models import MailEvent, MassMail
|
|
14
|
+
from wbmailing.models.mails import (
|
|
15
|
+
can_administrate_mail,
|
|
16
|
+
send_mail_task,
|
|
17
|
+
send_mass_mail_as_task,
|
|
18
|
+
)
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
@pytest.mark.django_db
|
|
22
|
+
class TestSpecificModels:
|
|
23
|
+
@patch("wbmailing.models.mails.send_mass_mail_as_task.delay")
|
|
24
|
+
def test_send_mail_task(self, send_mass_mail_as_task, mass_mail_factory, mailing_list_factory):
|
|
25
|
+
ec = EmailContactFactory()
|
|
26
|
+
ml = mailing_list_factory(email_contacts=(ec,))
|
|
27
|
+
mm = mass_mail_factory(mailing_lists=(ml,))
|
|
28
|
+
|
|
29
|
+
send_mail_task(mm.id)
|
|
30
|
+
send_mass_mail_as_task.assert_called()
|
|
31
|
+
assert send_mass_mail_as_task.call_count == 1
|
|
32
|
+
|
|
33
|
+
def test_send_mass_mail_as_task(self, mass_mail_factory, mailing_list_factory):
|
|
34
|
+
ec = EmailContactFactory()
|
|
35
|
+
ml = mailing_list_factory(email_contacts=(ec,))
|
|
36
|
+
mass_mail = mass_mail_factory(mailing_lists=(ml,))
|
|
37
|
+
assert len(mail.outbox) == 0
|
|
38
|
+
for subscriber in mass_mail.get_mail_addresses():
|
|
39
|
+
send_mass_mail_as_task(mass_mail.id, subscriber["address"])
|
|
40
|
+
assert len(mail.outbox) == 1
|
|
41
|
+
|
|
42
|
+
@pytest.mark.parametrize("status, expected", [("PENDING", "DENIED")])
|
|
43
|
+
def test_deny(self, mailing_list_subscriber_change_request_factory, status, expected):
|
|
44
|
+
mlscr = mailing_list_subscriber_change_request_factory()
|
|
45
|
+
mlscr.deny(description="SPAM")
|
|
46
|
+
assert mlscr.status == expected
|
|
47
|
+
|
|
48
|
+
@pytest.mark.parametrize("status, expected", [("DRAFT", "PENDING")])
|
|
49
|
+
def test_submit(self, mass_mail_factory, status, expected):
|
|
50
|
+
ml = mass_mail_factory()
|
|
51
|
+
ml.submit()
|
|
52
|
+
assert ml.status == expected
|
|
53
|
+
|
|
54
|
+
@pytest.mark.parametrize("status, expected", [("PENDING", "DENIED")])
|
|
55
|
+
def test_deny2(self, mass_mail_factory, status, expected):
|
|
56
|
+
ml = mass_mail_factory(status=MassMail.Status.PENDING)
|
|
57
|
+
ml.deny()
|
|
58
|
+
assert ml.status == expected
|
|
59
|
+
|
|
60
|
+
@pytest.mark.parametrize("status, expected", [("PENDING", "DRAFT")])
|
|
61
|
+
def test_revise(self, mass_mail_factory, status, expected):
|
|
62
|
+
ml = mass_mail_factory(status=MassMail.Status.PENDING)
|
|
63
|
+
ml.revise()
|
|
64
|
+
assert ml.status == expected
|
|
65
|
+
|
|
66
|
+
@patch("wbmailing.models.mails.send_mail_task.delay")
|
|
67
|
+
def test_send(self, send_mail_task, mass_mail_factory):
|
|
68
|
+
mm = mass_mail_factory(status=MassMail.Status.PENDING)
|
|
69
|
+
mm.send()
|
|
70
|
+
send_mail_task.assert_called()
|
|
71
|
+
assert send_mail_task.call_count == 1
|
|
72
|
+
|
|
73
|
+
def test_create_email(self, mass_mail_factory):
|
|
74
|
+
request = APIRequestFactory().get("")
|
|
75
|
+
request.user = get_or_create_superuser()
|
|
76
|
+
mm = mass_mail_factory(attachments=(DocumentFactory(),))
|
|
77
|
+
EmailContactFactory(address=request.user.email)
|
|
78
|
+
mm.template = None
|
|
79
|
+
mm.save()
|
|
80
|
+
msg = mm.create_email(request.user.email)
|
|
81
|
+
assert msg
|
|
82
|
+
|
|
83
|
+
@patch("wbmailing.models.mails.send_mail_as_task.delay")
|
|
84
|
+
def test_resend_no_massmail(self, send_mail_as_task):
|
|
85
|
+
request = APIRequestFactory().get("")
|
|
86
|
+
request.user = get_or_create_superuser()
|
|
87
|
+
obj = ToEmailMailFactory(attachments=(DocumentFactory(),))
|
|
88
|
+
obj.mass_mail = None
|
|
89
|
+
obj.save()
|
|
90
|
+
obj.resend()
|
|
91
|
+
send_mail_as_task.assert_called()
|
|
92
|
+
assert send_mail_as_task.call_count == 1
|
|
93
|
+
|
|
94
|
+
@patch("wbmailing.models.mails.send_mail_as_task.delay")
|
|
95
|
+
def test_resend_no_template(self, send_mail_as_task):
|
|
96
|
+
request = APIRequestFactory().get("")
|
|
97
|
+
request.user = get_or_create_superuser()
|
|
98
|
+
obj = ToEmailMailFactory()
|
|
99
|
+
obj.mass_mail.template = None
|
|
100
|
+
obj.mass_mail.save()
|
|
101
|
+
obj.resend()
|
|
102
|
+
send_mail_as_task.assert_called()
|
|
103
|
+
assert send_mail_as_task.call_count == 1
|
|
104
|
+
|
|
105
|
+
@override_settings(EMAIL_BACKEND="anymail.backends.test.EmailBackend")
|
|
106
|
+
@pytest.mark.parametrize(
|
|
107
|
+
"exits_message_id, event_type, reject_reason",
|
|
108
|
+
[
|
|
109
|
+
("YES", MailEvent.EventType.SENT, None),
|
|
110
|
+
("YES", MailEvent.EventType.SENT, MailEvent.RejectReason.SPAM),
|
|
111
|
+
("YES", "Other", "Unknow"),
|
|
112
|
+
("NO", "Other", None),
|
|
113
|
+
],
|
|
114
|
+
)
|
|
115
|
+
def test_handle_mail_tracking(self, exits_message_id, event_type, reject_reason, mail_factory, mail_event_factory):
|
|
116
|
+
request = APIRequestFactory().get("")
|
|
117
|
+
request.user = get_or_create_superuser()
|
|
118
|
+
ml = ToEmailMailFactory()
|
|
119
|
+
nb_mail_send = len(mail.outbox)
|
|
120
|
+
esp_event = {}
|
|
121
|
+
if reject_reason:
|
|
122
|
+
mailevent = mail_event_factory(mail=ml, description="", reject_reason=reject_reason)
|
|
123
|
+
if reject_reason == MailEvent.RejectReason.SPAM:
|
|
124
|
+
esp_event["reason"] = "SPAM"
|
|
125
|
+
else:
|
|
126
|
+
esp_event["response"] = "Unknow"
|
|
127
|
+
else:
|
|
128
|
+
mailevent = mail_event_factory(mail=ml)
|
|
129
|
+
|
|
130
|
+
to = list(ml.to_email.values_list("address", flat=True))
|
|
131
|
+
msg = ml.get_mailmessage(ml.subject, ml.body, request.user.email, to, attachments=ml.documents.all())
|
|
132
|
+
msg.send()
|
|
133
|
+
assert len(mail.outbox) == nb_mail_send + 1
|
|
134
|
+
assert msg.anymail_status.status == {"sent"}
|
|
135
|
+
if exits_message_id == "YES":
|
|
136
|
+
message_id = mailevent.mail.message_ids[0]
|
|
137
|
+
else:
|
|
138
|
+
message_id = msg.anymail_status.message_id
|
|
139
|
+
|
|
140
|
+
esp_event["ip"] = mailevent.ip
|
|
141
|
+
esp_event["useragent"] = mailevent.user_agent
|
|
142
|
+
mevent = AnymailTrackingEvent(
|
|
143
|
+
event_type=event_type,
|
|
144
|
+
message_id=message_id,
|
|
145
|
+
timestamp=mailevent.timestamp,
|
|
146
|
+
event_id=mailevent.id,
|
|
147
|
+
recipient=mailevent.recipient,
|
|
148
|
+
reject_reason=mailevent.reject_reason,
|
|
149
|
+
description=mailevent.description,
|
|
150
|
+
user_agent=mailevent.user_agent,
|
|
151
|
+
click_url=mailevent.click_url,
|
|
152
|
+
esp_event=esp_event,
|
|
153
|
+
)
|
|
154
|
+
# sender(class) – The source of the event. (One of theanymail.webhook.*Viewclasses, but you generally won’t examine this parameter; it’s required by Django’s signalmechanism.
|
|
155
|
+
tracking.send(sender=msg, event=mevent, esp_name="SendGrid")
|
|
156
|
+
|
|
157
|
+
if exits_message_id == "YES":
|
|
158
|
+
assert MailEvent.objects.filter(mail=ml, recipient=mailevent.recipient).count() == 2
|
|
159
|
+
else:
|
|
160
|
+
assert MailEvent.objects.filter(mail=ml, recipient=mailevent.recipient).count() == 1
|
|
161
|
+
|
|
162
|
+
@override_settings(EMAIL_BACKEND="anymail.backends.test.EmailBackend", ANYMAIL_SENDGRID_API_KEY="TEST")
|
|
163
|
+
@patch("wbmailing.backend.SendgridEmailBackend._send")
|
|
164
|
+
@pytest.mark.parametrize("resend, exit_document", [(False, False), (False, True), (True, False)])
|
|
165
|
+
def test_SendgridEmailBackend_send_messages(self, mock_send, resend, exit_document):
|
|
166
|
+
request = APIRequestFactory().get("")
|
|
167
|
+
request.user = get_or_create_superuser()
|
|
168
|
+
num_sent = SendgridEmailBackend().send_messages(None)
|
|
169
|
+
assert num_sent == 0
|
|
170
|
+
doc1 = DocumentFactory()
|
|
171
|
+
ml = ToEmailMailFactory(cc_email=(EmailContactFactory(),), attachments=(doc1,))
|
|
172
|
+
to = list(ml.to_email.values_list("address", flat=True))
|
|
173
|
+
cc = list(ml.cc_email.values_list("address", flat=True))
|
|
174
|
+
bcc = ["lemissan@atonra.ch"]
|
|
175
|
+
if resend:
|
|
176
|
+
msg = ml.get_mailmessage(
|
|
177
|
+
ml.subject, ml.body, request.user.email, to, attachments=ml.documents.all(), mail=ml
|
|
178
|
+
)
|
|
179
|
+
else:
|
|
180
|
+
msg = ml.get_mailmessage(
|
|
181
|
+
ml.subject, ml.body, request.user.email, to, bcc=bcc, cc=cc, attachments=ml.documents.all()
|
|
182
|
+
)
|
|
183
|
+
msg.send()
|
|
184
|
+
|
|
185
|
+
if exit_document:
|
|
186
|
+
for name, payload, mimetype in msg.attachments:
|
|
187
|
+
doc1.name = name
|
|
188
|
+
doc1.save()
|
|
189
|
+
|
|
190
|
+
email_messages = [msg]
|
|
191
|
+
|
|
192
|
+
mock_send.return_value.status_code = 200
|
|
193
|
+
mock_send.return_value.json.return_value = msg
|
|
194
|
+
|
|
195
|
+
num_sent = SendgridEmailBackend().send_messages(email_messages)
|
|
196
|
+
mock_send.assert_called()
|
|
197
|
+
assert mock_send.call_count == 1
|
|
198
|
+
assert num_sent == 1
|
|
199
|
+
|
|
200
|
+
def test_can_administrate_mail(self, mass_mail_factory):
|
|
201
|
+
request = APIRequestFactory().get("")
|
|
202
|
+
request.user = get_or_create_superuser()
|
|
203
|
+
obj = mass_mail_factory()
|
|
204
|
+
result = can_administrate_mail(obj, request.user)
|
|
205
|
+
assert result
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
from django.dispatch import receiver
|
|
2
|
+
from wbcore.contrib.directory.models import EmailContact
|
|
3
|
+
from wbcore.test.signals import (
|
|
4
|
+
custom_update_data_from_factory,
|
|
5
|
+
custom_update_kwargs,
|
|
6
|
+
get_custom_factory,
|
|
7
|
+
)
|
|
8
|
+
from wbmailing.factories import (
|
|
9
|
+
CustomMassMailFactory,
|
|
10
|
+
EmailContactFactory,
|
|
11
|
+
MailEventFactory,
|
|
12
|
+
MailFactory,
|
|
13
|
+
MailingListFactory,
|
|
14
|
+
MailingListSubscriberChangeRequestFactory,
|
|
15
|
+
MassMailFactory,
|
|
16
|
+
ToEmailMailFactory,
|
|
17
|
+
)
|
|
18
|
+
from wbmailing.models import MailingListSubscriberChangeRequest
|
|
19
|
+
from wbmailing.viewsets import (
|
|
20
|
+
EmailContactMailingListModelViewSet,
|
|
21
|
+
MailEventMassMailMailModelViewSet,
|
|
22
|
+
MailingListEntryModelViewSet,
|
|
23
|
+
MailingListSubscriberChangeRequestModelViewSet,
|
|
24
|
+
MailingListSubscriberRequestEntryModelViewSet,
|
|
25
|
+
MailingListSubscriberRequestMailingListModelViewSet,
|
|
26
|
+
MailMailingListChartViewSet,
|
|
27
|
+
MailModelViewSet,
|
|
28
|
+
MailStatusMassMailModelViewSet,
|
|
29
|
+
MassMailModelViewSet,
|
|
30
|
+
)
|
|
31
|
+
|
|
32
|
+
# =================================================================================================================
|
|
33
|
+
# CUSTOM FACTORY
|
|
34
|
+
# =================================================================================================================
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
@receiver(get_custom_factory, sender=MassMailModelViewSet)
|
|
38
|
+
def receive_factory_mass_mail(sender, *args, **kwargs):
|
|
39
|
+
return CustomMassMailFactory
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
@receiver(get_custom_factory, sender=MailModelViewSet)
|
|
43
|
+
def receive_factory_toemail(sender, *args, **kwargs):
|
|
44
|
+
return ToEmailMailFactory
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
@receiver(get_custom_factory, sender=MailStatusMassMailModelViewSet)
|
|
48
|
+
def receive_factory_email(sender, *args, **kwargs):
|
|
49
|
+
return EmailContactFactory
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
# =================================================================================================================
|
|
53
|
+
# UPDATE DATA
|
|
54
|
+
# =================================================================================================================
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
@receiver(custom_update_data_from_factory, sender=MailingListSubscriberRequestMailingListModelViewSet)
|
|
58
|
+
@receiver(custom_update_data_from_factory, sender=MailingListSubscriberRequestEntryModelViewSet)
|
|
59
|
+
@receiver(custom_update_data_from_factory, sender=MailingListSubscriberChangeRequestModelViewSet)
|
|
60
|
+
def receive_data_mailinglist_subscriber(sender, *args, **kwargs):
|
|
61
|
+
if obj := kwargs.get("obj_factory"):
|
|
62
|
+
obj.status = MailingListSubscriberChangeRequest.Status.APPROVED
|
|
63
|
+
obj.save()
|
|
64
|
+
# return {"mailing_list": obj.mailing_list.id }
|
|
65
|
+
return {}
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
@receiver(custom_update_data_from_factory, sender=EmailContactMailingListModelViewSet)
|
|
69
|
+
def receive_data_email_contact_mailinglist(sender, *args, **kwargs):
|
|
70
|
+
if obj := kwargs.get("obj_factory"):
|
|
71
|
+
mlscr = MailingListSubscriberChangeRequestFactory(
|
|
72
|
+
status=MailingListSubscriberChangeRequest.Status.APPROVED, mailing_list=obj.mailing_list
|
|
73
|
+
)
|
|
74
|
+
return {"email_contact": mlscr.email_contact.id}
|
|
75
|
+
return {}
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
# =================================================================================================================
|
|
79
|
+
# UPDATE KWARGS
|
|
80
|
+
# =================================================================================================================
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
@receiver(custom_update_kwargs, sender=MailStatusMassMailModelViewSet)
|
|
84
|
+
def receive_kwargs_mass_mail_mail(sender, *args, **kwargs):
|
|
85
|
+
if obj := kwargs.get("obj_factory"):
|
|
86
|
+
ml = MailingListFactory.create(email_contacts=[obj])
|
|
87
|
+
mass_mail = MassMailFactory.create(mailing_lists=[ml])
|
|
88
|
+
MailEventFactory.create(recipient=obj.address)
|
|
89
|
+
MailFactory.create(mass_mail=mass_mail, to_email=[obj])
|
|
90
|
+
return {"mass_mail_id": mass_mail.id}
|
|
91
|
+
return {}
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
@receiver(custom_update_kwargs, sender=MailEventMassMailMailModelViewSet)
|
|
95
|
+
def receive_kwargs_mail_event_massmail(sender, *args, **kwargs):
|
|
96
|
+
if obj := kwargs.get("obj_factory"):
|
|
97
|
+
return {"mass_mail_id": obj.mail.mass_mail.id}
|
|
98
|
+
return {}
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
@receiver(custom_update_kwargs, sender=MailingListEntryModelViewSet)
|
|
102
|
+
def receive_kwargs_mailing_list_entry(sender, *args, **kwargs):
|
|
103
|
+
if email_id := kwargs.get("email_contact_id", None):
|
|
104
|
+
ec = EmailContact.objects.get(id=email_id)
|
|
105
|
+
return {"entry_id": ec.entry.id}
|
|
106
|
+
return {}
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
@receiver(custom_update_kwargs, sender=MailingListSubscriberRequestEntryModelViewSet)
|
|
110
|
+
def receive_kwargs_mailing_list_subscriber_entry(sender, *args, **kwargs):
|
|
111
|
+
if kwargs.get("email_contact_id"):
|
|
112
|
+
sec = kwargs.get("email_contact_id")
|
|
113
|
+
ec = EmailContact.objects.filter(id=sec).first()
|
|
114
|
+
return {"entry_id": ec.entry.id}
|
|
115
|
+
return {}
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
@receiver(custom_update_kwargs, sender=MailMailingListChartViewSet)
|
|
119
|
+
def receive_kwargs_mail_mailing_list(sender, *args, **kwargs):
|
|
120
|
+
if obj := kwargs.get("obj_factory"):
|
|
121
|
+
ml = MailingListFactory()
|
|
122
|
+
obj.mass_mail.mailing_lists.add(ml)
|
|
123
|
+
return {"mailing_list_id": ml.id}
|
|
124
|
+
return {}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import pytest
|
|
2
|
+
from django.core import mail
|
|
3
|
+
from rest_framework import status
|
|
4
|
+
from rest_framework.test import APIRequestFactory
|
|
5
|
+
from wbcore.contrib.directory.factories import EmailContactFactory
|
|
6
|
+
from wbcore.test.utils import get_kwargs
|
|
7
|
+
from wbmailing.factories import CustomMassMailFactory
|
|
8
|
+
from wbmailing.models import MassMail
|
|
9
|
+
from wbmailing.viewsets.mails import MassMailModelViewSet
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
@pytest.mark.django_db
|
|
13
|
+
class TestSpecificSerializers:
|
|
14
|
+
@pytest.mark.parametrize("mvs, factory", [(MassMailModelViewSet, CustomMassMailFactory)])
|
|
15
|
+
def test_serializers_mail(self, mvs, factory, user):
|
|
16
|
+
request = APIRequestFactory().get("")
|
|
17
|
+
request.user = user
|
|
18
|
+
obj = factory(status=MassMail.Status.SENT)
|
|
19
|
+
EmailContactFactory(address=request.user.email)
|
|
20
|
+
nb_mail_send = len(mail.outbox)
|
|
21
|
+
request.query_params = {}
|
|
22
|
+
mvs.request = request
|
|
23
|
+
mvs.kwargs = get_kwargs(obj, mvs, request)
|
|
24
|
+
mvs.kwargs["pk"] = obj.pk
|
|
25
|
+
response = mvs().sendtestmail(request)
|
|
26
|
+
assert response.status_code == status.HTTP_200_OK
|
|
27
|
+
assert response.data
|
|
28
|
+
assert len(mail.outbox) == nb_mail_send + 1
|