-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathtest_membership_notifications.py
More file actions
177 lines (142 loc) · 7.24 KB
/
test_membership_notifications.py
File metadata and controls
177 lines (142 loc) · 7.24 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
from __future__ import annotations
from datetime import timedelta
from unittest.mock import MagicMock, patch
from django.test import TestCase
from apps.accounts.models import UserProfile
from apps.notifications.models import NotificationKind, ScheduledNotification
from apps.notifications.services.membership import notify_new_membership
from apps.notifications.tests.factories import SectionNotificationPreferencesFactory
from apps.sections.models import SectionMembership
from apps.utils.factories.accounts import UserFactory
from apps.utils.factories.sections import KnownSectionFactory, SectionMembershipWithUserFactory
def _create_user_profile(user):
"""Ensure user has a UserProfile attached."""
UserProfile.objects.get_or_create(user=user)
def _make_sections_config(
notify_member=True,
notify_on_new_member=True,
digest_interval=timedelta(hours=24),
):
"""Return a mock SectionsConfiguration object with the fields used by the service."""
config = MagicMock()
config.email_notify_member_on_received = notify_member
config.email_notify_on_new_member = notify_on_new_member
config.email_digest_interval = digest_interval
return config
class NotifyNewMembershipTestCase(TestCase):
def setUp(self):
self.section = KnownSectionFactory()
self.applicant = UserFactory(profile=None)
_create_user_profile(self.applicant)
self.membership = SectionMembershipWithUserFactory(
section=self.section,
user=self.applicant,
role=SectionMembership.Role.MEMBER,
state=SectionMembership.State.UNCONFIRMED,
)
@patch("apps.notifications.services.membership.send_notification_email")
def test_sends_received_email_to_applicant(self, mock_send):
"""Applicant receives a confirmation email when their membership application is registered."""
config = _make_sections_config()
self.section.sections_plugin_configuration = config # type: ignore[attr-defined]
notify_new_membership(self.membership)
mock_send.assert_called_once()
call_kwargs = mock_send.call_args.kwargs
self.assertIn("membership_received", call_kwargs["template_prefix"])
self.assertEqual(call_kwargs["recipient_email"], self.applicant.email)
@patch("apps.notifications.services.scheduler.enqueue_delayed_notification")
@patch("apps.notifications.services.membership.send_notification_email")
def test_enqueues_digest_for_editors(self, mock_send, mock_enqueue):
"""Active editors and admins each get a digest ScheduledNotification enqueued."""
editor = UserFactory(profile=None)
SectionMembershipWithUserFactory(
section=self.section,
user=editor,
role=SectionMembership.Role.EDITOR,
state=SectionMembership.State.ACTIVE,
)
admin_user = UserFactory(profile=None)
SectionMembershipWithUserFactory(
section=self.section,
user=admin_user,
role=SectionMembership.Role.ADMIN,
state=SectionMembership.State.ACTIVE,
)
config = _make_sections_config()
self.section.sections_plugin_configuration = config # type: ignore[attr-defined]
notify_new_membership(self.membership)
# Both editor and admin should have enqueue called
self.assertEqual(mock_enqueue.call_count, 2)
for call in mock_enqueue.call_args_list:
self.assertEqual(call.kwargs["kind"], NotificationKind.MEMBER_WAITING_DIGEST)
self.assertEqual(call.kwargs["related_object"], self.membership)
@patch("apps.notifications.services.scheduler.enqueue_delayed_notification")
@patch("apps.notifications.services.membership.send_notification_email")
def test_skips_non_editor_members(self, mock_send, mock_enqueue):
"""Regular members and international students do NOT get a digest notification."""
regular_member = UserFactory(profile=None)
SectionMembershipWithUserFactory(
section=self.section,
user=regular_member,
role=SectionMembership.Role.MEMBER,
state=SectionMembership.State.ACTIVE,
)
international = UserFactory(profile=None)
SectionMembershipWithUserFactory(
section=self.section,
user=international,
role=SectionMembership.Role.INTERNATIONAL,
state=SectionMembership.State.ACTIVE,
)
config = _make_sections_config()
self.section.sections_plugin_configuration = config # type: ignore[attr-defined]
notify_new_membership(self.membership)
mock_enqueue.assert_not_called()
@patch("apps.notifications.services.membership.send_notification_email")
def test_config_disabled_skips_applicant_email(self, mock_send):
"""If config.email_notify_member_on_received=False, no confirmation email is sent to applicant."""
config = _make_sections_config(notify_member=False)
self.section.sections_plugin_configuration = config # type: ignore[attr-defined]
notify_new_membership(self.membership)
mock_send.assert_not_called()
@patch("apps.notifications.services.scheduler.enqueue_delayed_notification")
@patch("apps.notifications.services.membership.send_notification_email")
def test_config_disabled_skips_editor_digest(self, mock_send, mock_enqueue):
"""If config.email_notify_on_new_member=False, no digest notifications are enqueued for editors."""
editor = UserFactory(profile=None)
SectionMembershipWithUserFactory(
section=self.section,
user=editor,
role=SectionMembership.Role.EDITOR,
state=SectionMembership.State.ACTIVE,
)
config = _make_sections_config(notify_on_new_member=False)
self.section.sections_plugin_configuration = config # type: ignore[attr-defined]
notify_new_membership(self.membership)
mock_enqueue.assert_not_called()
@patch("apps.notifications.services.scheduler.enqueue_delayed_notification")
@patch("apps.notifications.services.membership.send_notification_email")
def test_editor_opted_out_not_enqueued(self, mock_send, mock_enqueue):
"""Editor with notify_on_new_member_waiting=False does not receive a digest notification."""
editor = UserFactory(profile=None)
SectionMembershipWithUserFactory(
section=self.section,
user=editor,
role=SectionMembership.Role.EDITOR,
state=SectionMembership.State.ACTIVE,
)
SectionNotificationPreferencesFactory(
user=editor,
section=self.section,
notify_on_new_member_waiting=False,
)
config = _make_sections_config()
self.section.sections_plugin_configuration = config # type: ignore[attr-defined]
notify_new_membership(self.membership)
mock_enqueue.assert_not_called()
@patch("apps.notifications.services.membership.send_notification_email")
def test_no_config_skips_all(self, mock_send):
"""If section has no SectionsConfiguration plugin, nothing is sent."""
notify_new_membership(self.membership)
mock_send.assert_not_called()
self.assertEqual(ScheduledNotification.objects.count(), 0)