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