3 final class PhabricatorMetaMTAMailTestCase
extends PhabricatorTestCase
{
5 protected function getPhabricatorTestCaseConfiguration() {
7 self
::PHABRICATOR_TESTCONFIG_BUILD_STORAGE_FIXTURES
=> true,
11 public function testMailSendFailures() {
12 $user = $this->generateNewTestUser();
13 $phid = $user->getPHID();
16 // Normally, the send should succeed.
17 $mail = new PhabricatorMetaMTAMail();
18 $mail->addTos(array($phid));
20 $mailer = new PhabricatorMailTestAdapter();
21 $mail->sendWithMailers(array($mailer));
23 PhabricatorMailOutboundStatus
::STATUS_SENT
,
27 // When the mailer fails temporarily, the mail should remain queued.
28 $mail = new PhabricatorMetaMTAMail();
29 $mail->addTos(array($phid));
31 $mailer = new PhabricatorMailTestAdapter();
32 $mailer->setFailTemporarily(true);
34 $mail->sendWithMailers(array($mailer));
35 } catch (Exception
$ex) {
39 PhabricatorMailOutboundStatus
::STATUS_QUEUE
,
43 // When the mailer fails permanently, the mail should be failed.
44 $mail = new PhabricatorMetaMTAMail();
45 $mail->addTos(array($phid));
47 $mailer = new PhabricatorMailTestAdapter();
48 $mailer->setFailPermanently(true);
50 $mail->sendWithMailers(array($mailer));
51 } catch (Exception
$ex) {
55 PhabricatorMailOutboundStatus
::STATUS_FAIL
,
59 public function testRecipients() {
60 $user = $this->generateNewTestUser();
61 $phid = $user->getPHID();
63 $mailer = new PhabricatorMailTestAdapter();
65 $mail = new PhabricatorMetaMTAMail();
66 $mail->addTos(array($phid));
69 in_array($phid, $mail->buildRecipientList()),
70 pht('"To" is a recipient.'));
73 // Test that the "No Self Mail" and "No Mail" preferences work correctly.
74 $mail->setFrom($phid);
77 in_array($phid, $mail->buildRecipientList()),
78 pht('"From" does not exclude recipients by default.'));
80 $user = $this->writeSetting(
82 PhabricatorEmailSelfActionsSetting
::SETTINGKEY
,
86 in_array($phid, $mail->buildRecipientList()),
87 pht('"From" excludes recipients with no-self-mail set.'));
89 $user = $this->writeSetting(
91 PhabricatorEmailSelfActionsSetting
::SETTINGKEY
,
95 in_array($phid, $mail->buildRecipientList()),
96 pht('"From" does not exclude recipients by default.'));
98 $user = $this->writeSetting(
100 PhabricatorEmailNotificationsSetting
::SETTINGKEY
,
104 in_array($phid, $mail->buildRecipientList()),
105 pht('"From" excludes recipients with no-mail set.'));
107 $mail->setForceDelivery(true);
110 in_array($phid, $mail->buildRecipientList()),
111 pht('"From" includes no-mail recipients when forced.'));
113 $mail->setForceDelivery(false);
115 $user = $this->writeSetting(
117 PhabricatorEmailNotificationsSetting
::SETTINGKEY
,
121 in_array($phid, $mail->buildRecipientList()),
122 pht('"From" does not exclude recipients by default.'));
124 // Test that explicit exclusion works correctly.
125 $mail->setExcludeMailRecipientPHIDs(array($phid));
128 in_array($phid, $mail->buildRecipientList()),
129 pht('Explicit exclude excludes recipients.'));
131 $mail->setExcludeMailRecipientPHIDs(array());
134 // Test that mail tag preferences exclude recipients.
135 $user = $this->writeSetting(
137 PhabricatorEmailTagsSetting
::SETTINGKEY
,
142 $mail->setMailTags(array('test-tag'));
145 in_array($phid, $mail->buildRecipientList()),
146 pht('Tag preference excludes recipients.'));
148 $user = $this->writeSetting(
150 PhabricatorEmailTagsSetting
::SETTINGKEY
,
154 in_array($phid, $mail->buildRecipientList()),
155 'Recipients restored after tag preference removed.');
157 $email = id(new PhabricatorUserEmail())->loadOneWhere(
158 'userPHID = %s AND isPrimary = 1',
161 $email->setIsVerified(0)->save();
164 in_array($phid, $mail->buildRecipientList()),
165 pht('Mail not sent to unverified address.'));
167 $email->setIsVerified(1)->save();
170 in_array($phid, $mail->buildRecipientList()),
171 pht('Mail sent to verified address.'));
174 public function testThreadIDHeaders() {
175 $this->runThreadIDHeadersWithConfiguration(true, true);
176 $this->runThreadIDHeadersWithConfiguration(true, false);
177 $this->runThreadIDHeadersWithConfiguration(false, true);
178 $this->runThreadIDHeadersWithConfiguration(false, false);
181 private function runThreadIDHeadersWithConfiguration(
182 $supports_message_id,
185 $user = $this->generateNewTestUser();
186 $phid = $user->getPHID();
188 $mailer = new PhabricatorMailTestAdapter();
190 $mailer->setSupportsMessageID($supports_message_id);
192 $thread_id = 'somethread-12345';
194 $mail = id(new PhabricatorMetaMTAMail())
195 ->setThreadID($thread_id, $is_first_mail)
196 ->addTos(array($phid))
197 ->sendWithMailers(array($mailer));
199 $guts = $mailer->getGuts();
201 $headers = idx($guts, 'headers', array());
204 foreach ($headers as $header) {
205 list($name, $value) = $header;
206 $dict[$name] = $value;
209 if ($is_first_mail && $supports_message_id) {
210 $expect_message_id = true;
211 $expect_in_reply_to = false;
212 $expect_references = false;
214 $expect_message_id = false;
215 $expect_in_reply_to = true;
216 $expect_references = true;
219 $case = '<message-id = '.($supports_message_id ?
'Y' : 'N').', '.
220 'first = '.($is_first_mail ?
'Y' : 'N').'>';
223 isset($dict['Thread-Index']),
224 pht('Expect Thread-Index header for case %s.', $case));
227 isset($dict['Message-ID']),
229 'Expectation about existence of Message-ID header for case %s.',
233 isset($dict['In-Reply-To']),
235 'Expectation about existence of In-Reply-To header for case %s.',
239 isset($dict['References']),
241 'Expectation about existence of References header for case %s.',
245 private function writeSetting(PhabricatorUser
$user, $key, $value) {
246 $preferences = PhabricatorUserPreferences
::loadUserPreferences($user);
248 $editor = id(new PhabricatorUserPreferencesEditor())
250 ->setContentSource($this->newContentSource())
251 ->setContinueOnNoEffect(true)
252 ->setContinueOnMissingFields(true);
255 $xactions[] = $preferences->newTransaction($key, $value);
256 $editor->applyTransactions($preferences, $xactions);
258 return id(new PhabricatorPeopleQuery())
260 ->withIDs(array($user->getID()))
264 public function testMailerFailover() {
265 $user = $this->generateNewTestUser();
266 $phid = $user->getPHID();
268 $status_sent = PhabricatorMailOutboundStatus
::STATUS_SENT
;
269 $status_queue = PhabricatorMailOutboundStatus
::STATUS_QUEUE
;
270 $status_fail = PhabricatorMailOutboundStatus
::STATUS_FAIL
;
272 $mailer1 = id(new PhabricatorMailTestAdapter())
275 $mailer2 = id(new PhabricatorMailTestAdapter())
283 // Send mail with both mailers active. The first mailer should be used.
284 $mail = id(new PhabricatorMetaMTAMail())
285 ->addTos(array($phid))
286 ->sendWithMailers($mailers);
287 $this->assertEqual($status_sent, $mail->getStatus());
288 $this->assertEqual('mailer1', $mail->getMailerKey());
291 // If the first mailer fails, the mail should be sent with the second
292 // mailer. Since we transmitted the mail, this doesn't raise an exception.
293 $mailer1->setFailTemporarily(true);
295 $mail = id(new PhabricatorMetaMTAMail())
296 ->addTos(array($phid))
297 ->sendWithMailers($mailers);
298 $this->assertEqual($status_sent, $mail->getStatus());
299 $this->assertEqual('mailer2', $mail->getMailerKey());
302 // If both mailers fail, the mail should remain in queue.
303 $mailer2->setFailTemporarily(true);
305 $mail = id(new PhabricatorMetaMTAMail())
306 ->addTos(array($phid));
310 $mail->sendWithMailers($mailers);
311 } catch (Exception
$ex) {
315 $this->assertTrue($caught instanceof Exception
);
316 $this->assertEqual($status_queue, $mail->getStatus());
317 $this->assertEqual(null, $mail->getMailerKey());
319 $mailer1->setFailTemporarily(false);
320 $mailer2->setFailTemporarily(false);
323 // If the first mailer fails permanently, the mail should fail even though
324 // the second mailer isn't configured to fail.
325 $mailer1->setFailPermanently(true);
327 $mail = id(new PhabricatorMetaMTAMail())
328 ->addTos(array($phid));
332 $mail->sendWithMailers($mailers);
333 } catch (Exception
$ex) {
337 $this->assertTrue($caught instanceof Exception
);
338 $this->assertEqual($status_fail, $mail->getStatus());
339 $this->assertEqual(null, $mail->getMailerKey());
342 public function testMailSizeLimits() {
343 $env = PhabricatorEnv
::beginScopedEnv();
344 $env->overrideEnvConfig('metamta.email-body-limit', 1024 * 512);
346 $user = $this->generateNewTestUser();
347 $phid = $user->getPHID();
349 $string_1kb = str_repeat('x', 1024);
350 $html_1kb = str_repeat('y', 1024);
351 $string_1mb = str_repeat('x', 1024 * 1024);
352 $html_1mb = str_repeat('y', 1024 * 1024);
354 // First, send a mail with a small text body and a small HTML body to make
355 // sure the basics work properly.
356 $mail = id(new PhabricatorMetaMTAMail())
357 ->addTos(array($phid))
358 ->setBody($string_1kb)
359 ->setHTMLBody($html_1kb);
361 $mailer = new PhabricatorMailTestAdapter();
362 $mail->sendWithMailers(array($mailer));
364 PhabricatorMailOutboundStatus
::STATUS_SENT
,
367 $text_body = $mailer->getBody();
368 $html_body = $mailer->getHTMLBody();
370 $this->assertEqual($string_1kb, $text_body);
371 $this->assertEqual($html_1kb, $html_body);
374 // Now, send a mail with a large text body and a large HTML body. We expect
375 // the text body to be truncated and the HTML body to be dropped.
376 $mail = id(new PhabricatorMetaMTAMail())
377 ->addTos(array($phid))
378 ->setBody($string_1mb)
379 ->setHTMLBody($html_1mb);
381 $mailer = new PhabricatorMailTestAdapter();
382 $mail->sendWithMailers(array($mailer));
384 PhabricatorMailOutboundStatus
::STATUS_SENT
,
387 $text_body = $mailer->getBody();
388 $html_body = $mailer->getHTMLBody();
390 // We expect the body was truncated, because it exceeded the body limit.
392 (strlen($text_body) < strlen($string_1mb)),
393 pht('Text Body Truncated'));
395 // We expect the HTML body was dropped completely after the text body was
398 !phutil_nonempty_string($html_body),
399 pht('HTML Body Removed'));
402 // Next send a mail with a small text body and a large HTML body. We expect
403 // the text body to be intact and the HTML body to be dropped.
404 $mail = id(new PhabricatorMetaMTAMail())
405 ->addTos(array($phid))
406 ->setBody($string_1kb)
407 ->setHTMLBody($html_1mb);
409 $mailer = new PhabricatorMailTestAdapter();
410 $mail->sendWithMailers(array($mailer));
412 PhabricatorMailOutboundStatus
::STATUS_SENT
,
415 $text_body = $mailer->getBody();
416 $html_body = $mailer->getHTMLBody();
418 $this->assertEqual($string_1kb, $text_body);
419 $this->assertTrue(!phutil_nonempty_string($html_body));