Commit | Line | Data |
---|---|---|
cdc5c450 | 1 | <?php |
2 | ||
3 | /** | |
67991a1a | 4 | * Class CRM_Utils_Mail_EmailProcessorTest |
cdc5c450 | 5 | * @group headless |
6 | */ | |
7 | ||
67991a1a | 8 | class CRM_Utils_Mail_EmailProcessorTest extends CiviUnitTestCase { |
cdc5c450 | 9 | |
10 | /** | |
11 | * Event queue record. | |
12 | * | |
13 | * @var array | |
14 | */ | |
15 | protected $eventQueue = array(); | |
16 | ||
17 | /** | |
18 | * ID of our sample contact. | |
19 | * | |
20 | * @var int | |
21 | */ | |
22 | protected $contactID; | |
23 | ||
24 | public function setUp() { | |
25 | parent::setUp(); | |
e7693ddd | 26 | CRM_Utils_File::cleanDir(__DIR__ . '/data/mail'); |
27 | mkdir(__DIR__ . '/data/mail'); | |
cdc5c450 | 28 | $this->callAPISuccess('MailSettings', 'get', array( |
29 | 'api.MailSettings.create' => array( | |
30 | 'name' => 'local', | |
31 | 'protocol' => 'Localdir', | |
32 | 'source' => __DIR__ . '/data/mail', | |
33 | 'domain' => 'example.com', | |
34 | ), | |
35 | )); | |
36 | } | |
37 | ||
e7693ddd | 38 | public function tearDown() { |
39 | CRM_Utils_File::cleanDir(__DIR__ . '/data/mail'); | |
40 | parent::tearDown(); | |
41 | $this->quickCleanup(array('civicrm_group', 'civicrm_group_contact', 'civicrm_mailing', 'civicrm_mailing_job', 'civicrm_mailing_event_bounce', 'civicrm_mailing_event_queue', 'civicrm_mailing_group', 'civicrm_mailing_recipients', 'civicrm_contact', 'civicrm_email')); | |
42 | } | |
43 | ||
cdc5c450 | 44 | /** |
45 | * Test the job processing function works and processes a bounce. | |
46 | */ | |
47 | public function testBounceProcessing() { | |
48 | $this->setUpMailing(); | |
49 | ||
50 | copy(__DIR__ . '/data/bounces/bounce_no_verp.txt', __DIR__ . '/data/mail/bounce_no_verp.txt'); | |
51 | $this->assertTrue(file_exists(__DIR__ . '/data/mail/bounce_no_verp.txt')); | |
52 | $this->callAPISuccess('job', 'fetch_bounces', array()); | |
53 | $this->assertFalse(file_exists(__DIR__ . '/data/mail/bounce_no_verp.txt')); | |
54 | $this->checkMailingBounces(1); | |
55 | } | |
56 | ||
8c4991d2 TS |
57 | /** |
58 | * Test the job processing function can handle invalid characters. | |
59 | */ | |
60 | public function testBounceProcessingInvalidCharacter() { | |
61 | $this->setUpMailing(); | |
62 | $mail = 'test_invalid_character.eml'; | |
63 | ||
64 | copy(__DIR__ . '/data/bounces/' . $mail, __DIR__ . '/data/mail/' . $mail); | |
65 | $this->callAPISuccess('job', 'fetch_bounces', array()); | |
66 | $this->assertFalse(file_exists(__DIR__ . '/data/mail/' . $mail)); | |
67 | $this->checkMailingBounces(1); | |
68 | } | |
69 | ||
ebe10b73 TS |
70 | /** |
71 | * Test that the job processing function can handle incoming utf8mb4 characters. | |
72 | */ | |
73 | public function testBounceProcessingUTF8mb4() { | |
74 | $this->setUpMailing(); | |
75 | $mail = 'test_utf8mb4_character.txt'; | |
76 | ||
77 | copy(__DIR__ . '/data/bounces/' . $mail, __DIR__ . '/data/mail/' . $mail); | |
78 | $this->callAPISuccess('job', 'fetch_bounces', array()); | |
79 | $this->assertFalse(file_exists(__DIR__ . '/data/mail/' . $mail)); | |
80 | $this->checkMailingBounces(1); | |
81 | } | |
82 | ||
e7693ddd | 83 | /** |
84 | * Tests that a multipart related email does not cause pain & misery & fatal errors. | |
85 | * | |
86 | * Sample taken from https://www.phpclasses.org/browse/file/14672.html | |
87 | */ | |
88 | public function testProcessingMultipartRelatedEmail() { | |
89 | $this->setUpMailing(); | |
90 | $mail = 'test_sample_message.eml'; | |
91 | ||
92 | copy(__DIR__ . '/data/bounces/' . $mail, __DIR__ . '/data/mail/' . $mail); | |
93 | $this->callAPISuccess('job', 'fetch_bounces', array()); | |
94 | $this->assertFalse(file_exists(__DIR__ . '/data/mail/' . $mail)); | |
95 | $this->checkMailingBounces(1); | |
96 | } | |
97 | ||
01467aec EE |
98 | /** |
99 | * Tests that a nested multipart email does not cause pain & misery & fatal errors. | |
100 | * | |
101 | * Sample anonymized from an email that broke bounce processing at Wikimedia | |
102 | */ | |
103 | public function testProcessingNestedMultipartEmail() { | |
104 | $this->setUpMailing(); | |
105 | $mail = 'test_nested_message.eml'; | |
106 | ||
107 | copy(__DIR__ . '/data/bounces/' . $mail, __DIR__ . '/data/mail/' . $mail); | |
108 | $this->callAPISuccess('job', 'fetch_bounces', array()); | |
109 | $this->assertFalse(file_exists(__DIR__ . '/data/mail/' . $mail)); | |
110 | $this->checkMailingBounces(1); | |
111 | } | |
112 | ||
cdc5c450 | 113 | /** |
114 | * Test that a deleted email does not cause a hard fail. | |
115 | * | |
116 | * The civicrm_mailing_event_queue table tracks email ids to represent an | |
117 | * email address. The id may not represent the same email by the time the bounce may | |
118 | * come in - a weakness of storing the id not the email. Relevant here | |
119 | * is that it might have been deleted altogether, in which case the bounce should be | |
120 | * silently ignored. This ignoring is also at the expense of the contact | |
121 | * having the same email address with a different id. | |
122 | * | |
123 | * Longer term it would make sense to track the email address & track bounces back to that | |
124 | * rather than an id that may not reflect the email used. Issue logged CRM-20021. | |
125 | * | |
126 | * For not however, we are testing absence of mysql error in conjunction with CRM-20016. | |
127 | */ | |
128 | public function testBounceProcessingDeletedEmail() { | |
129 | $this->setUpMailing(); | |
130 | $this->callAPISuccess('Email', 'get', array( | |
131 | 'contact_id' => $this->contactID, | |
132 | 'api.email.delete' => 1, | |
133 | )); | |
134 | ||
135 | copy(__DIR__ . '/data/bounces/bounce_no_verp.txt', __DIR__ . '/data/mail/bounce_no_verp.txt'); | |
136 | $this->assertTrue(file_exists(__DIR__ . '/data/mail/bounce_no_verp.txt')); | |
137 | $this->callAPISuccess('job', 'fetch_bounces', array()); | |
138 | $this->assertFalse(file_exists(__DIR__ . '/data/mail/bounce_no_verp.txt')); | |
139 | $this->checkMailingBounces(1); | |
140 | } | |
141 | ||
142 | /** | |
e7693ddd | 143 | * |
cdc5c450 | 144 | * Wrapper to check for mailing bounces. |
145 | * | |
146 | * Normally we would call $this->callAPISuccessGetCount but there is not one & there is resistance to | |
147 | * adding apis for 'convenience' so just adding a hacky function to get past the impasse. | |
148 | * | |
149 | * @param int $expectedCount | |
150 | */ | |
151 | public function checkMailingBounces($expectedCount) { | |
152 | $this->assertEquals($expectedCount, CRM_Core_DAO::singleValueQuery( | |
e7693ddd | 153 | "SELECT count(*) FROM civicrm_mailing_event_bounce" |
cdc5c450 | 154 | )); |
155 | } | |
156 | ||
157 | /** | |
158 | * Set up a mailing. | |
159 | */ | |
160 | public function setUpMailing() { | |
161 | $this->contactID = $this->individualCreate(array('email' => 'undeliverable@example.com')); | |
d15a97f4 | 162 | $groupID = $this->callAPISuccess('Group', 'create', array( |
cdc5c450 | 163 | 'title' => 'Mailing group', |
164 | 'api.GroupContact.create' => array( | |
165 | 'contact_id' => $this->contactID, | |
d15a97f4 | 166 | ), |
167 | )); | |
cdc5c450 | 168 | $this->createMailing(array('scheduled_date' => 'now', 'groups' => array('include' => array($groupID)))); |
169 | $this->callAPISuccess('job', 'process_mailing', array()); | |
170 | $this->eventQueue = $this->callAPISuccess('MailingEventQueue', 'get', array('api.MailingEventQueue.create' => array('hash' => 'aaaaaaaaaaaaaaaa'))); | |
171 | } | |
172 | ||
173 | } |