@@ -226,6 +226,36 @@ def test_spam(self):
226226 )
227227 event = kwargs ["event" ]
228228 self .assertEqual (event .event_type , "complained" )
229+ self .assertEqual (event .reject_reason , "spam" )
230+
231+ def test_complaint (self ):
232+ # Sadly, this is not well documented in the official Brevo API documentation.
233+ raw_event = {
234+ "event" : "complaint" ,
235+ 236+ "id" : "xxxxx" ,
237+ "date" : "2020-10-09 00:00:00" ,
238+ "ts" : 1604933619 ,
239+ "message-id" :
"[email protected] " ,
240+ "ts_event" : 1604933654 ,
241+ "X-Mailin-custom" : '{"meta": "data"}' ,
242+ "tags" : ["transac_messages" ],
243+ }
244+ response = self .client .post (
245+ "/anymail/brevo/tracking/" ,
246+ content_type = "application/json" ,
247+ data = json .dumps (raw_event ),
248+ )
249+ self .assertEqual (response .status_code , 200 )
250+ kwargs = self .assert_handler_called_once_with (
251+ self .tracking_handler ,
252+ sender = BrevoTrackingWebhookView ,
253+ event = ANY ,
254+ esp_name = "Brevo" ,
255+ )
256+ event = kwargs ["event" ]
257+ self .assertEqual (event .event_type , "complained" )
258+ self .assertEqual (event .reject_reason , "spam" )
229259
230260 def test_invalid_email (self ):
231261 # "If a ISP again indicated us that the email is not valid or if we discovered
@@ -258,6 +288,38 @@ def test_invalid_email(self):
258288 event .mta_response , "(guessing invalid_email includes a reason)"
259289 )
260290
291+ def test_error_email (self ):
292+ # Sadly, this is not well documented in the official Brevo API documentation.
293+ raw_event = {
294+ "event" : "error" ,
295+ 296+ "id" : "xxxxx" ,
297+ "date" : "2020-10-09 00:00:00" ,
298+ "ts" : 1604933619 ,
299+ "message-id" :
"[email protected] " ,
300+ "ts_event" : 1604933654 ,
301+ "subject" : "My first Transactional" ,
302+ "X-Mailin-custom" : '{"meta": "data"}' ,
303+ "template_id" : 22 ,
304+ "tags" : ["transac_messages" ],
305+ "ts_epoch" : 1604933623 ,
306+ }
307+ response = self .client .post (
308+ "/anymail/brevo/tracking/" ,
309+ content_type = "application/json" ,
310+ data = json .dumps (raw_event ),
311+ )
312+ self .assertEqual (response .status_code , 200 )
313+ kwargs = self .assert_handler_called_once_with (
314+ self .tracking_handler ,
315+ sender = BrevoTrackingWebhookView ,
316+ event = ANY ,
317+ esp_name = "Brevo" ,
318+ )
319+ event = kwargs ["event" ]
320+ self .assertEqual (event .event_type , "failed" )
321+ self .assertEqual (event .reject_reason , None )
322+
261323 def test_deferred_event (self ):
262324 # Note: the example below is an actual event capture (with 'example.com'
263325 # substituted for the real receiving domain). It's pretty clearly a bounce, not
@@ -341,6 +403,71 @@ def test_unique_opened_event(self):
341403 event = kwargs ["event" ]
342404 self .assertEqual (event .event_type , "opened" )
343405
406+ def test_proxy_open_event (self ):
407+ # Equivalent to "Loaded via Proxy" in the Brevo UI.
408+ # This is sent when a tracking pixel is loaded via a 'privacy proxy server'.
409+ # This technique is used by Apple Mail, for example, to protect user's IP
410+ # addresses.
411+ raw_event = {
412+ "event" : "proxy_open" ,
413+ 414+ "id" : 1 ,
415+ "date" : "2020-10-09 00:00:00" ,
416+ "message-id" :
"[email protected] " ,
417+ "subject" : "My first Transactional" ,
418+ "tag" : ["transactionalTag" ],
419+ "sending_ip" : "xxx.xxx.xxx.xxx" ,
420+ "s_epoch" : 1534486682000 ,
421+ "template_id" : 1 ,
422+ }
423+ response = self .client .post (
424+ "/anymail/brevo/tracking/" ,
425+ content_type = "application/json" ,
426+ data = json .dumps (raw_event ),
427+ )
428+ self .assertEqual (response .status_code , 200 )
429+ kwargs = self .assert_handler_called_once_with (
430+ self .tracking_handler ,
431+ sender = BrevoTrackingWebhookView ,
432+ event = ANY ,
433+ esp_name = "Brevo" ,
434+ )
435+ event = kwargs ["event" ]
436+ self .assertEqual (event .event_type , "opened" )
437+
438+ def test_unique_proxy_open_event (self ):
439+ # Sadly, undocumented in Brevo.
440+ # Equivalent to "First Open but loaded via Proxy".
441+ # This is sent when a tracking pixel is loaded via a 'privacy proxy server'.
442+ # This technique is used by Apple Mail, for example, to protect user's IP
443+ # addresses.
444+ raw_event = {
445+ "event" : "unique_proxy_open" ,
446+ 447+ "id" : 1 ,
448+ "date" : "2020-10-09 00:00:00" ,
449+ "message-id" :
"[email protected] " ,
450+ "subject" : "My first Transactional" ,
451+ "tag" : ["transactionalTag" ],
452+ "sending_ip" : "xxx.xxx.xxx.xxx" ,
453+ "s_epoch" : 1534486682000 ,
454+ "template_id" : 1 ,
455+ }
456+ response = self .client .post (
457+ "/anymail/brevo/tracking/" ,
458+ content_type = "application/json" ,
459+ data = json .dumps (raw_event ),
460+ )
461+ self .assertEqual (response .status_code , 200 )
462+ kwargs = self .assert_handler_called_once_with (
463+ self .tracking_handler ,
464+ sender = BrevoTrackingWebhookView ,
465+ event = ANY ,
466+ esp_name = "Brevo" ,
467+ )
468+ event = kwargs ["event" ]
469+ self .assertEqual (event .event_type , "opened" )
470+
344471 def test_clicked_event (self ):
345472 raw_event = {
346473 "event" : "click" ,
0 commit comments