4 * Class CRM_Utils_Mail_EmailProcessorTest
8 class CRM_Utils_Mail_EmailProcessorTest
extends CiviUnitTestCase
{
15 protected $eventQueue = array();
18 * ID of our sample contact.
24 public function setUp() {
26 CRM_Utils_File
::cleanDir(__DIR__
. '/data/mail');
27 mkdir(__DIR__
. '/data/mail');
28 $this->callAPISuccess('MailSettings', 'get', array(
29 'api.MailSettings.create' => array(
31 'protocol' => 'Localdir',
32 'source' => __DIR__
. '/data/mail',
33 'domain' => 'example.com',
38 public function tearDown() {
39 CRM_Utils_File
::cleanDir(__DIR__
. '/data/mail');
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'));
45 * Test the job processing function works and processes a bounce.
47 public function testBounceProcessing() {
48 $this->setUpMailing();
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);
58 * Test the job processing function can handle invalid characters.
60 public function testBounceProcessingInvalidCharacter() {
61 $this->setUpMailing();
62 $mail = 'test_invalid_character.eml';
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);
71 * Test that the job processing function can handle incoming utf8mb4 characters.
73 public function testBounceProcessingUTF8mb4() {
74 $this->setUpMailing();
75 $mail = 'test_utf8mb4_character.txt';
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);
84 * Tests that a multipart related email does not cause pain & misery & fatal errors.
86 * Sample taken from https://www.phpclasses.org/browse/file/14672.html
88 public function testProcessingMultipartRelatedEmail() {
89 $this->setUpMailing();
90 $mail = 'test_sample_message.eml';
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);
99 * Tests that a nested multipart email does not cause pain & misery & fatal errors.
101 * Sample anonymized from an email that broke bounce processing at Wikimedia
103 public function testProcessingNestedMultipartEmail() {
104 $this->setUpMailing();
105 $mail = 'test_nested_message.eml';
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);
114 * Test that a deleted email does not cause a hard fail.
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.
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.
126 * For not however, we are testing absence of mysql error in conjunction with CRM-20016.
128 public function testBounceProcessingDeletedEmail() {
129 $this->setUpMailing();
130 $this->callAPISuccess('Email', 'get', array(
131 'contact_id' => $this->contactID
,
132 'api.email.delete' => 1,
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);
144 * Wrapper to check for mailing bounces.
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.
149 * @param int $expectedCount
151 public function checkMailingBounces($expectedCount) {
152 $this->assertEquals($expectedCount, CRM_Core_DAO
::singleValueQuery(
153 "SELECT count(*) FROM civicrm_mailing_event_bounce"
160 public function setUpMailing() {
161 $this->contactID
= $this->individualCreate(array('email' => 'undeliverable@example.com'));
162 $groupID = $this->callAPISuccess('Group', 'create', array(
163 'title' => 'Mailing group',
164 'api.GroupContact.create' => array(
165 'contact_id' => $this->contactID
,
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')));