Replace invalid characters in bounce reason with unicode replacement character
[civicrm-core.git] / tests / phpunit / CRM / Utils / Mail / EmailProcessorTest.php
CommitLineData
cdc5c450 1<?php
2
3/**
4 * Class CRM_Utils_EmailProcessorTest
5 * @group headless
6 */
7
8class CRM_Utils_EmailProcessorTest extends CiviUnitTestCase {
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
e7693ddd 70 /**
71 * Tests that a multipart related email does not cause pain & misery & fatal errors.
72 *
73 * Sample taken from https://www.phpclasses.org/browse/file/14672.html
74 */
75 public function testProcessingMultipartRelatedEmail() {
76 $this->setUpMailing();
77 $mail = 'test_sample_message.eml';
78
79 copy(__DIR__ . '/data/bounces/' . $mail, __DIR__ . '/data/mail/' . $mail);
80 $this->callAPISuccess('job', 'fetch_bounces', array());
81 $this->assertFalse(file_exists(__DIR__ . '/data/mail/' . $mail));
82 $this->checkMailingBounces(1);
83 }
84
01467aec
EE
85 /**
86 * Tests that a nested multipart email does not cause pain & misery & fatal errors.
87 *
88 * Sample anonymized from an email that broke bounce processing at Wikimedia
89 */
90 public function testProcessingNestedMultipartEmail() {
91 $this->setUpMailing();
92 $mail = 'test_nested_message.eml';
93
94 copy(__DIR__ . '/data/bounces/' . $mail, __DIR__ . '/data/mail/' . $mail);
95 $this->callAPISuccess('job', 'fetch_bounces', array());
96 $this->assertFalse(file_exists(__DIR__ . '/data/mail/' . $mail));
97 $this->checkMailingBounces(1);
98 }
99
cdc5c450 100 /**
101 * Test that a deleted email does not cause a hard fail.
102 *
103 * The civicrm_mailing_event_queue table tracks email ids to represent an
104 * email address. The id may not represent the same email by the time the bounce may
105 * come in - a weakness of storing the id not the email. Relevant here
106 * is that it might have been deleted altogether, in which case the bounce should be
107 * silently ignored. This ignoring is also at the expense of the contact
108 * having the same email address with a different id.
109 *
110 * Longer term it would make sense to track the email address & track bounces back to that
111 * rather than an id that may not reflect the email used. Issue logged CRM-20021.
112 *
113 * For not however, we are testing absence of mysql error in conjunction with CRM-20016.
114 */
115 public function testBounceProcessingDeletedEmail() {
116 $this->setUpMailing();
117 $this->callAPISuccess('Email', 'get', array(
118 'contact_id' => $this->contactID,
119 'api.email.delete' => 1,
120 ));
121
122 copy(__DIR__ . '/data/bounces/bounce_no_verp.txt', __DIR__ . '/data/mail/bounce_no_verp.txt');
123 $this->assertTrue(file_exists(__DIR__ . '/data/mail/bounce_no_verp.txt'));
124 $this->callAPISuccess('job', 'fetch_bounces', array());
125 $this->assertFalse(file_exists(__DIR__ . '/data/mail/bounce_no_verp.txt'));
126 $this->checkMailingBounces(1);
127 }
128
129 /**
e7693ddd 130 *
cdc5c450 131 * Wrapper to check for mailing bounces.
132 *
133 * Normally we would call $this->callAPISuccessGetCount but there is not one & there is resistance to
134 * adding apis for 'convenience' so just adding a hacky function to get past the impasse.
135 *
136 * @param int $expectedCount
137 */
138 public function checkMailingBounces($expectedCount) {
139 $this->assertEquals($expectedCount, CRM_Core_DAO::singleValueQuery(
e7693ddd 140 "SELECT count(*) FROM civicrm_mailing_event_bounce"
cdc5c450 141 ));
142 }
143
144 /**
145 * Set up a mailing.
146 */
147 public function setUpMailing() {
148 $this->contactID = $this->individualCreate(array('email' => 'undeliverable@example.com'));
d15a97f4 149 $groupID = $this->callAPISuccess('Group', 'create', array(
cdc5c450 150 'title' => 'Mailing group',
151 'api.GroupContact.create' => array(
152 'contact_id' => $this->contactID,
d15a97f4 153 ),
154 ));
cdc5c450 155 $this->createMailing(array('scheduled_date' => 'now', 'groups' => array('include' => array($groupID))));
156 $this->callAPISuccess('job', 'process_mailing', array());
157 $this->eventQueue = $this->callAPISuccess('MailingEventQueue', 'get', array('api.MailingEventQueue.create' => array('hash' => 'aaaaaaaaaaaaaaaa')));
158 }
159
160}