@@ -98,3 +98,102 @@ def test_email_template_display_name():
9898 visible_name = admin .email_template_display_name (sent_email )
9999
100100 assert visible_name == sent_email .email_template .get_identifier_display ()
101+
102+
103+ def test_send_email_action (rf , admin_user , django_capture_on_commit_callbacks , mocker ):
104+ mock_send_pending_email = mocker .patch (
105+ "notifications.admin.admins.send_pending_email.delay"
106+ )
107+ admin = SentEmailAdmin (
108+ model = SentEmail ,
109+ admin_site = AdminSite (),
110+ )
111+ admin .message_user = mocker .Mock ()
112+
113+ request = rf .post ("/" )
114+ request .user = admin_user
115+
116+ draft_email_1 = SentEmailFactory (status = SentEmail .Status .draft )
117+ draft_email_2 = SentEmailFactory (status = SentEmail .Status .draft )
118+ pending_email = SentEmailFactory (status = SentEmail .Status .pending )
119+ sent_email = SentEmailFactory (status = SentEmail .Status .sent )
120+ failed_email = SentEmailFactory (status = SentEmail .Status .failed )
121+
122+ with django_capture_on_commit_callbacks (execute = True ):
123+ admin .send_email (request , SentEmail .objects .all ())
124+
125+ # drafts, pending and failed emails are all (re)queued for sending
126+ queued_ids = {call .args [0 ] for call in mock_send_pending_email .call_args_list }
127+ assert queued_ids == {
128+ draft_email_1 .id ,
129+ draft_email_2 .id ,
130+ pending_email .id ,
131+ failed_email .id ,
132+ }
133+ assert mock_send_pending_email .call_count == 4
134+
135+ for email in (draft_email_1 , draft_email_2 , pending_email , failed_email ):
136+ email .refresh_from_db ()
137+ assert email .status == SentEmail .Status .pending
138+
139+ # already sent emails are never touched
140+ sent_email .refresh_from_db ()
141+ assert sent_email .status == SentEmail .Status .sent
142+
143+ admin .message_user .assert_called_once_with (request , "Emails queued for sending: 4" )
144+
145+
146+ def test_send_email_action_with_a_status_filtered_queryset (
147+ rf , admin_user , django_capture_on_commit_callbacks , mocker
148+ ):
149+ """The changelist queryset carries the active list_filter, so the action
150+ receives a queryset already narrowed to a single status."""
151+ mock_send_pending_email = mocker .patch (
152+ "notifications.admin.admins.send_pending_email.delay"
153+ )
154+ admin = SentEmailAdmin (
155+ model = SentEmail ,
156+ admin_site = AdminSite (),
157+ )
158+ admin .message_user = mocker .Mock ()
159+
160+ request = rf .post ("/" )
161+ request .user = admin_user
162+
163+ draft_email = SentEmailFactory (status = SentEmail .Status .draft )
164+
165+ with django_capture_on_commit_callbacks (execute = True ):
166+ admin .send_email (
167+ request , SentEmail .objects .filter (status = SentEmail .Status .draft )
168+ )
169+
170+ mock_send_pending_email .assert_called_once_with (draft_email .id )
171+
172+ draft_email .refresh_from_db ()
173+ assert draft_email .status == SentEmail .Status .pending
174+
175+
176+ def test_send_email_action_keeps_queueing_after_a_broker_failure (
177+ rf , admin_user , django_capture_on_commit_callbacks , mocker
178+ ):
179+ mock_send_pending_email = mocker .patch (
180+ "notifications.admin.admins.send_pending_email.delay" ,
181+ side_effect = [Exception ("broker is down" ), None ],
182+ )
183+ admin = SentEmailAdmin (
184+ model = SentEmail ,
185+ admin_site = AdminSite (),
186+ )
187+ admin .message_user = mocker .Mock ()
188+
189+ request = rf .post ("/" )
190+ request .user = admin_user
191+
192+ SentEmailFactory (status = SentEmail .Status .draft )
193+ SentEmailFactory (status = SentEmail .Status .draft )
194+
195+ with django_capture_on_commit_callbacks (execute = True ):
196+ admin .send_email (request , SentEmail .objects .all ())
197+
198+ # the first publish blowing up must not strand the remaining emails
199+ assert mock_send_pending_email .call_count == 2
0 commit comments