diff --git a/lib/API/JSON/v1/Mailer.php b/lib/API/JSON/v1/Mailer.php index 872ac860ff..7e4736e27d 100644 --- a/lib/API/JSON/v1/Mailer.php +++ b/lib/API/JSON/v1/Mailer.php @@ -34,7 +34,7 @@ class Mailer extends APIEndpoint { if($result['response'] === false) { $error = sprintf( __('The email could not be sent: %s', 'mailpoet'), - $result['error_message'] + $result['error']->getMessage() ); return $this->errorResponse(array(APIError::BAD_REQUEST => $error)); } else { @@ -46,4 +46,4 @@ class Mailer extends APIEndpoint { MailerLog::resumeSending(); return $this->successResponse(null); } -} \ No newline at end of file +} diff --git a/lib/Cron/Workers/SendingQueue/SendingQueue.php b/lib/Cron/Workers/SendingQueue/SendingQueue.php index b06536c40f..7d3c363c09 100644 --- a/lib/Cron/Workers/SendingQueue/SendingQueue.php +++ b/lib/Cron/Workers/SendingQueue/SendingQueue.php @@ -5,6 +5,7 @@ use MailPoet\Cron\CronHelper; use MailPoet\Cron\Workers\SendingQueue\Tasks\Links; use MailPoet\Cron\Workers\SendingQueue\Tasks\Mailer as MailerTask; use MailPoet\Cron\Workers\SendingQueue\Tasks\Newsletter as NewsletterTask; +use MailPoet\Mailer\MailerError; use MailPoet\Mailer\MailerLog; use MailPoet\Models\ScheduledTask as ScheduledTaskModel; use MailPoet\Models\StatisticsNewsletters as StatisticsNewslettersModel; @@ -164,10 +165,12 @@ class SendingQueue { ); // log error message and schedule retry/pause sending if($send_result['response'] === false) { - if(isset($send_result['retry_interval'])) { - MailerLog::processNonBlockingError($send_result['operation'], $send_result['error_message'], $send_result['retry_interval']); + $error = $send_result['error']; + assert($error instanceof MailerError); + if($error->getRetryInterval() !== null) { + MailerLog::processNonBlockingError($error->getOperation(), $error->getMessage(), $error->getRetryInterval()); } else { - MailerLog::processError($send_result['operation'], $send_result['error_message']); + MailerLog::processError($error->getOperation(), $error->getMessage()); } } // update processed/to process list diff --git a/lib/Mailer/Mailer.php b/lib/Mailer/Mailer.php index 645b9f982a..e5ff82ecf4 100644 --- a/lib/Mailer/Mailer.php +++ b/lib/Mailer/Mailer.php @@ -1,6 +1,11 @@ mailer_config['secret_key'], $this->sender, $this->reply_to, - $this->return_path + $this->return_path, + new AmazonSESMapper() ); break; case self::METHOD_MAILPOET: $mailer_instance = new $this->mailer_config['class']( $this->mailer_config['mailpoet_api_key'], $this->sender, - $this->reply_to + $this->reply_to, + new MailPoetMapper() ); break; case self::METHOD_SENDGRID: $mailer_instance = new $this->mailer_config['class']( $this->mailer_config['api_key'], $this->sender, - $this->reply_to + $this->reply_to, + new SendGridMapper() ); break; case self::METHOD_PHPMAIL: $mailer_instance = new $this->mailer_config['class']( $this->sender, $this->reply_to, - $this->return_path + $this->return_path, + new PHPMailMapper() ); break; case self::METHOD_SMTP: @@ -76,7 +85,8 @@ class Mailer { $this->mailer_config['encryption'], $this->sender, $this->reply_to, - $this->return_path + $this->return_path, + new SMTPMapper() ); break; default: @@ -166,25 +176,16 @@ class Mailer { return sprintf('=?utf-8?B?%s?=', base64_encode($name)); } - static function formatMailerConnectionErrorResult($error_message) { - return array( + static function formatMailerErrorResult(MailerError $error) { + return [ 'response' => false, - 'operation' => 'connect', - 'error_message' => $error_message - ); - } - - static function formatMailerSendErrorResult($error_message) { - return array( - 'response' => false, - 'operation' => 'send', - 'error_message' => $error_message - ); + 'error' => $error, + ]; } static function formatMailerSendSuccessResult() { - return array( + return [ 'response' => true - ); + ]; } } diff --git a/lib/Mailer/MailerError.php b/lib/Mailer/MailerError.php new file mode 100644 index 0000000000..c6b45f0226 --- /dev/null +++ b/lib/Mailer/MailerError.php @@ -0,0 +1,63 @@ +operation = $operation; + $this->level = $level; + $this->message = $message; + $this->retry_interval = $retry_interval; + } + + /** + * @return string + */ + function getOperation() { + return $this->operation; + } + + /** + * @return string + */ + function getLevel() { + return $this->level; + } + + /** + * @return null|string + */ + function getMessage() { + return $this->message; + } + + /** + * @return int|null + */ + function getRetryInterval() { + return $this->retry_interval; + } +} diff --git a/lib/Mailer/Methods/AmazonSES.php b/lib/Mailer/Methods/AmazonSES.php index 9a1fb5311c..96df2d1382 100644 --- a/lib/Mailer/Methods/AmazonSES.php +++ b/lib/Mailer/Methods/AmazonSES.php @@ -2,6 +2,7 @@ namespace MailPoet\Mailer\Methods; use MailPoet\Mailer\Mailer; +use MailPoet\Mailer\Methods\ErrorMappers\AmazonSESMapper; use MailPoet\WP\Functions as WPFunctions; if(!defined('ABSPATH')) exit; @@ -28,7 +29,18 @@ class AmazonSES { 'EU (Ireland)' => 'eu-west-1' ); - function __construct($region, $access_key, $secret_key, $sender, $reply_to, $return_path) { + /** @var AmazonSESMapper */ + private $error_mapper; + + function __construct( + $region, + $access_key, + $secret_key, + $sender, + $reply_to, + $return_path, + AmazonSESMapper $error_mapper + ) { $this->aws_access_key = $access_key; $this->aws_secret_key = $secret_key; $this->aws_region = (in_array($region, $this->available_regions)) ? $region : false; @@ -48,6 +60,7 @@ class AmazonSES { $this->sender['from_email']; $this->date = gmdate('Ymd\THis\Z'); $this->date_without_time = gmdate('Ymd'); + $this->error_mapper = $error_mapper; } function send($newsletter, $subscriber, $extra_params = array()) { @@ -57,20 +70,17 @@ class AmazonSES { $this->request($newsletter, $subscriber, $extra_params) ); } catch(\Exception $e) { - return Mailer::formatMailerSendErrorResult($e->getMessage()); + $error = $this->error_mapper->getErrorFromException($e); + return Mailer::formatMailerErrorResult($error); } if(is_wp_error($result)) { - return Mailer::formatMailerConnectionErrorResult($result->get_error_message()); + $error = $this->error_mapper->getConnectionError($result->get_error_message()); + return Mailer::formatMailerErrorResult($error); } if(WPFunctions::wpRemoteRetrieveResponseCode($result) !== 200) { $response = simplexml_load_string(WPFunctions::wpRemoteRetrieveBody($result)); - $response = ($response) ? - $response->Error->Message->__toString() : - sprintf(__('%s has returned an unknown error.', 'mailpoet'), Mailer::METHOD_AMAZONSES); - if(empty($extra_params['test_email'])) { - $response .= sprintf(' %s: %s', __('Unprocessed subscriber', 'mailpoet'), $subscriber); - } - return Mailer::formatMailerSendErrorResult($response); + $error = $this->error_mapper->getErrorFromResponse($response, $subscriber, $extra_params); + return Mailer::formatMailerErrorResult($error); } return Mailer::formatMailerSendSuccessResult(); } diff --git a/lib/Mailer/Methods/ErrorMappers/AmazonSESMapper.php b/lib/Mailer/Methods/ErrorMappers/AmazonSESMapper.php new file mode 100644 index 0000000000..721e7978dc --- /dev/null +++ b/lib/Mailer/Methods/ErrorMappers/AmazonSESMapper.php @@ -0,0 +1,23 @@ +getMessage()); + } + + function getErrorFromResponse($response, $subscriber, $extra_params) { + $response = ($response) ? + $response->Error->Message->__toString() : + sprintf(__('%s has returned an unknown error.', 'mailpoet'), Mailer::METHOD_AMAZONSES); + if(empty($extra_params['test_email'])) { + $response .= sprintf(' %s: %s', __('Unprocessed subscriber', 'mailpoet'), $subscriber); + } + return new MailerError(MailerError::OPERATION_SEND, MailerError::LEVEL_HARD, $response); + } +} diff --git a/lib/Mailer/Methods/ErrorMappers/ConnectionErrorMapperTrait.php b/lib/Mailer/Methods/ErrorMappers/ConnectionErrorMapperTrait.php new file mode 100644 index 0000000000..f64f7b8005 --- /dev/null +++ b/lib/Mailer/Methods/ErrorMappers/ConnectionErrorMapperTrait.php @@ -0,0 +1,14 @@ +parseErrorResponse($result['message'], $subscribers); + break; + case API::RESPONSE_CODE_TEMPORARY_UNAVAILABLE: + $message = __('Email service is temporarily not available, please try again in a few minutes.', 'mailpoet'); + $retry_interval = self::TEMPORARY_UNAVAILABLE_RETRY_INTERVAL; + break; + case API::RESPONSE_CODE_KEY_INVALID: + case API::RESPONSE_CODE_PAYLOAD_TOO_BIG: + default: + $message = $result['message']; + } + } + return new MailerError(MailerError::OPERATION_SEND, $level, $message, $retry_interval); + } + + private function parseErrorResponse($result, $subscriber) { + $result_parsed = json_decode($result, true); + $errors = []; + if(is_array($result_parsed)) { + foreach($result_parsed as $result_error) { + $errors[] = $this->processSingleSubscriberError($result_error, $subscriber); + } + } + if(!empty($errors)) { + return __('Error while sending: ', 'mailpoet') . join(', ', $errors); + } else { + return __('Error while sending newsletters. ', 'mailpoet') . $result; + } + } + + private function processSingleSubscriberError($result_error, $subscriber) { + $error = ''; + if(is_array($result_error)) { + $subscriber_errors = []; + if(isset($result_error['errors']) && is_array($result_error['errors'])) { + array_walk_recursive($result_error['errors'], function($item) use (&$subscriber_errors) { + $subscriber_errors[] = $item; + }); + } + $error .= join(', ', $subscriber_errors); + + if(isset($result_error['index']) && isset($subscriber[$result_error['index']])) { + $error = '(' . $subscriber[$result_error['index']] . ': ' . $error . ')'; + } + } + return $error; + } +} diff --git a/lib/Mailer/Methods/ErrorMappers/PHPMailMapper.php b/lib/Mailer/Methods/ErrorMappers/PHPMailMapper.php new file mode 100644 index 0000000000..466d6fb3b7 --- /dev/null +++ b/lib/Mailer/Methods/ErrorMappers/PHPMailMapper.php @@ -0,0 +1,21 @@ +getMessage()); + } + + function getErrorForSubscriber($subscriber, $extra_params) { + $message = sprintf(__('%s has returned an unknown error.', 'mailpoet'), Mailer::METHOD_PHPMAIL); + if(empty($extra_params['test_email'])) { + $message .= sprintf(' %s: %s', __('Unprocessed subscriber', 'mailpoet'), $subscriber); + } + return new MailerError(MailerError::OPERATION_SEND, MailerError::LEVEL_HARD, $message); + } +} diff --git a/lib/Mailer/Methods/ErrorMappers/SMTPMapper.php b/lib/Mailer/Methods/ErrorMappers/SMTPMapper.php new file mode 100644 index 0000000000..61ad82b898 --- /dev/null +++ b/lib/Mailer/Methods/ErrorMappers/SMTPMapper.php @@ -0,0 +1,31 @@ +getMessage()); + return new MailerError(MailerError::OPERATION_SEND, MailerError::LEVEL_HARD, $message[0]); + } + + function getErrorFromLog($log, $subscriber, $extra_params = []) { + // extract error message from log + preg_match('/!! (.*?)>>/ism', $log, $message); + if(!empty($message[1])) { + $message = $message[1]; + // remove line breaks from the message due to how logger's dump() method works + $message = preg_replace('/\r|\n/', '', $message); + } else { + $message = sprintf(__('%s has returned an unknown error.', 'mailpoet'), Mailer::METHOD_SMTP); + } + if(empty($extra_params['test_email'])) { + $message .= sprintf(' %s: %s', __('Unprocessed subscriber', 'mailpoet'), $subscriber); + } + return new MailerError(MailerError::OPERATION_SEND, MailerError::LEVEL_HARD, $message); + } +} diff --git a/lib/Mailer/Methods/ErrorMappers/SendGridMapper.php b/lib/Mailer/Methods/ErrorMappers/SendGridMapper.php new file mode 100644 index 0000000000..88972e14f6 --- /dev/null +++ b/lib/Mailer/Methods/ErrorMappers/SendGridMapper.php @@ -0,0 +1,19 @@ +api = new API($api_key); $this->sender = $sender; $this->reply_to = $reply_to; - $this->services_checker = new ServicesChecker(false); + $this->services_checker = new ServicesChecker(); + $this->error_mapper = $error_mapper; } function send($newsletter, $subscriber, $extra_params = array()) { if($this->services_checker->isMailPoetAPIKeyValid() === false) { - $response = __('MailPoet API key is invalid!', 'mailpoet'); - return Mailer::formatMailerSendErrorResult($response); + return Mailer::formatMailerErrorResult($this->error_mapper->getInvalidApiKeyError()); } $message_body = $this->getBody($newsletter, $subscriber, $extra_params); @@ -35,9 +36,11 @@ class MailPoet { switch($result['status']) { case API::SENDING_STATUS_CONNECTION_ERROR: - return Mailer::formatMailerConnectionErrorResult($result['message']); + $error = $this->error_mapper->getConnectionError($result['message']); + return Mailer::formatMailerErrorResult($error); case API::SENDING_STATUS_SEND_ERROR: - return $this->processSendError($result, $subscriber); + $error = $this->processSendError($result, $subscriber); + return Mailer::formatMailerErrorResult($error); case API::SENDING_STATUS_OK: default: return Mailer::formatMailerSendSuccessResult(); @@ -45,27 +48,10 @@ class MailPoet { } function processSendError($result, $subscriber) { - if(!empty($result['code'])) { - switch($result['code']) { - case API::RESPONSE_CODE_NOT_ARRAY: - return Mailer::formatMailerSendErrorResult(__('JSON input is not an array', 'mailpoet')); - case API::RESPONSE_CODE_PAYLOAD_TOO_BIG: - return Mailer::formatMailerSendErrorResult($result['message']); - case API::RESPONSE_CODE_PAYLOAD_ERROR: - $error = $this->parseErrorResponse($result['message'], $subscriber); - return Mailer::formatMailerSendErrorResult($error); - case API::RESPONSE_CODE_TEMPORARY_UNAVAILABLE: - $error = Mailer::formatMailerSendErrorResult(__('Email service is temporarily not available, please try again in a few minutes.', 'mailpoet')); - $error['retry_interval'] = self::TEMPORARY_UNAVAILABLE_RETRY_INTERVAL; - return $error; - case API::RESPONSE_CODE_KEY_INVALID: - Bridge::invalidateKey(); - break; - default: - return Mailer::formatMailerSendErrorResult($result['message']); - } + if(!empty($result['code']) && $result['code'] === API::RESPONSE_CODE_KEY_INVALID) { + Bridge::invalidateKey(); } - return Mailer::formatMailerSendErrorResult($result['message']); + return $this->error_mapper->getErrorForResult($result, $subscriber); } function processSubscriber($subscriber) { @@ -128,37 +114,4 @@ class MailPoet { } return $body; } - - private function parseErrorResponse($result, $subscriber) { - $result_parsed = json_decode($result, true); - $errors = []; - if(is_array($result_parsed)) { - foreach($result_parsed as $result_error) { - $errors[] = $this->processSingleSubscriberError($result_error, $subscriber); - } - } - if(!empty($errors)) { - return __('Error while sending: ', 'mailpoet') . join(', ', $errors); - } else { - return __('Error while sending newsletters. ', 'mailpoet') . $result; - } - } - - private function processSingleSubscriberError($result_error, $subscriber) { - $error = ''; - if(is_array($result_error)) { - $subscriber_errors = []; - if(isset($result_error['errors']) && is_array($result_error['errors'])) { - array_walk_recursive($result_error['errors'], function($item) use (&$subscriber_errors) { - $subscriber_errors[] = $item; - }); - } - $error .= join(', ', $subscriber_errors); - - if(isset($result_error['index']) && isset($subscriber[$result_error['index']])) { - $error = '(' . $subscriber[$result_error['index']] . ': ' . $error . ')'; - } - } - return $error; - } } diff --git a/lib/Mailer/Methods/PHPMail.php b/lib/Mailer/Methods/PHPMail.php index 349a191e77..c02df9f1d1 100644 --- a/lib/Mailer/Methods/PHPMail.php +++ b/lib/Mailer/Methods/PHPMail.php @@ -3,6 +3,7 @@ namespace MailPoet\Mailer\Methods; use MailPoet\Mailer\Mailer; +use MailPoet\Mailer\Methods\ErrorMappers\PHPMailMapper; if(!defined('ABSPATH')) exit; @@ -14,13 +15,17 @@ class PHPMail { public $return_path; public $mailer; - function __construct($sender, $reply_to, $return_path) { + /** @var PHPMailMapper */ + private $error_mapper; + + function __construct($sender, $reply_to, $return_path, PHPMailMapper $error_mapper) { $this->sender = $sender; $this->reply_to = $reply_to; $this->return_path = ($return_path) ? $return_path : $this->sender['from_email']; $this->mailer = $this->buildMailer(); + $this->error_mapper = $error_mapper; } function send($newsletter, $subscriber, $extra_params = array()) { @@ -28,16 +33,13 @@ class PHPMail { $mailer = $this->configureMailerWithMessage($newsletter, $subscriber, $extra_params); $result = $mailer->send(); } catch(\Exception $e) { - return Mailer::formatMailerSendErrorResult($e->getMessage()); + return Mailer::formatMailerErrorResult($this->error_mapper->getErrorFromException($e)); } if($result === true) { return Mailer::formatMailerSendSuccessResult(); } else { - $result = sprintf(__('%s has returned an unknown error.', 'mailpoet'), Mailer::METHOD_PHPMAIL); - if(empty($extra_params['test_email'])) { - $result .= sprintf(' %s: %s', __('Unprocessed subscriber', 'mailpoet'), $subscriber); - } - return Mailer::formatMailerSendErrorResult($result); + $error = $this->error_mapper->getErrorForSubscriber($subscriber, $extra_params); + return Mailer::formatMailerErrorResult($error); } } @@ -80,4 +82,4 @@ class PHPMail { 'name' => (isset($subscriber_data['name'])) ? $subscriber_data['name'] : '' ); } -} \ No newline at end of file +} diff --git a/lib/Mailer/Methods/SMTP.php b/lib/Mailer/Methods/SMTP.php index 68117dcaf9..cb6945f83a 100644 --- a/lib/Mailer/Methods/SMTP.php +++ b/lib/Mailer/Methods/SMTP.php @@ -2,6 +2,7 @@ namespace MailPoet\Mailer\Methods; use MailPoet\Mailer\Mailer; +use MailPoet\Mailer\Methods\ErrorMappers\SMTPMapper; use MailPoet\WP\Hooks; if(!defined('ABSPATH')) exit; @@ -19,9 +20,12 @@ class SMTP { public $mailer; const SMTP_CONNECTION_TIMEOUT = 15; // seconds + /** @var SMTPMapper */ + private $error_mapper; + function __construct( $host, $port, $authentication, $login = null, $password = null, $encryption, - $sender, $reply_to, $return_path) { + $sender, $reply_to, $return_path, SMTPMapper $error_mapper) { $this->host = $host; $this->port = $port; $this->authentication = $authentication; @@ -36,6 +40,7 @@ class SMTP { $this->mailer = $this->buildMailer(); $this->mailer_logger = new \Swift_Plugins_Loggers_ArrayLogger(); $this->mailer->registerPlugin(new \Swift_Plugins_LoggerPlugin($this->mailer_logger)); + $this->error_mapper = $error_mapper; } function send($newsletter, $subscriber, $extra_params = array()) { @@ -43,13 +48,16 @@ class SMTP { $message = $this->createMessage($newsletter, $subscriber, $extra_params); $result = $this->mailer->send($message); } catch(\Exception $e) { - return Mailer::formatMailerSendErrorResult( - $this->processExceptionMessage($e->getMessage()) + return Mailer::formatMailerErrorResult( + $this->error_mapper->getErrorFromException($e) ); } - return ($result === 1) ? - Mailer::formatMailerSendSuccessResult() : - Mailer::formatMailerSendErrorResult($this->processLogMessage($subscriber, $extra_params)); + if($result === 1) { + return Mailer::formatMailerSendSuccessResult(); + } else { + $error = $this->error_mapper->getErrorFromLog($this->mailer_logger->dump(), $subscriber, $extra_params); + return Mailer::formatMailerErrorResult($error); + } } function buildMailer() { @@ -107,27 +115,4 @@ class SMTP { (isset($subscriber_data['name'])) ? $subscriber_data['name'] : '' ); } - - function processLogMessage($subscriber, $extra_params = array(), $log = false) { - $log = ($log) ? $log : $this->mailer_logger->dump(); - // extract error message from log - preg_match('/!! (.*?)>>/ism', $log, $message); - if(!empty($message[1])) { - $message = $message[1]; - // remove line breaks from the message due to how logger's dump() method works - $message = preg_replace('/\r|\n/', '', $message); - } else { - $message = sprintf(__('%s has returned an unknown error.', 'mailpoet'), Mailer::METHOD_SMTP); - } - if(empty($extra_params['test_email'])) { - $message .= sprintf(' %s: %s', __('Unprocessed subscriber', 'mailpoet'), $subscriber); - } - return $message; - } - - function processExceptionMessage($message) { - // remove redundant information appended by Swift logger to exception messages - $message = explode(PHP_EOL, $message); - return $message[0]; - } } diff --git a/lib/Mailer/Methods/SendGrid.php b/lib/Mailer/Methods/SendGrid.php index fb936582eb..9ad738c6aa 100644 --- a/lib/Mailer/Methods/SendGrid.php +++ b/lib/Mailer/Methods/SendGrid.php @@ -3,6 +3,7 @@ namespace MailPoet\Mailer\Methods; use MailPoet\Mailer\Mailer; +use MailPoet\Mailer\Methods\ErrorMappers\SendGridMapper; use MailPoet\WP\Functions as WPFunctions; if(!defined('ABSPATH')) exit; @@ -13,10 +14,14 @@ class SendGrid { public $sender; public $reply_to; - function __construct($api_key, $sender, $reply_to) { + /** @var SendGridMapper */ + private $error_mapper; + + function __construct($api_key, $sender, $reply_to, SendGridMapper $error_mapper) { $this->api_key = $api_key; $this->sender = $sender; $this->reply_to = $reply_to; + $this->error_mapper = $error_mapper; } function send($newsletter, $subscriber, $extra_params = array()) { @@ -25,17 +30,13 @@ class SendGrid { $this->request($newsletter, $subscriber, $extra_params) ); if(is_wp_error($result)) { - return Mailer::formatMailerConnectionErrorResult($result->get_error_message()); + $error = $this->error_mapper->getConnectionError($result->get_error_message()); + return Mailer::formatMailerErrorResult($error); } if(WPFunctions::wpRemoteRetrieveResponseCode($result) !== 200) { $response = json_decode($result['body'], true); - $response = (!empty($response['errors'][0])) ? - $response['errors'][0] : - sprintf(__('%s has returned an unknown error.', 'mailpoet'), Mailer::METHOD_SENDGRID); - if(empty($extra_params['test_email'])) { - $response .= sprintf(' %s: %s', __('Unprocessed subscriber', 'mailpoet'), $subscriber); - } - return Mailer::formatMailerSendErrorResult($response); + $error = $this->error_mapper->getErrorFromResponse($response, $subscriber, $extra_params); + return Mailer::formatMailerErrorResult($error); } return Mailer::formatMailerSendSuccessResult(); } @@ -80,4 +81,4 @@ class SendGrid { 'body' => http_build_query($body, null, '&') ); } -} \ No newline at end of file +} diff --git a/tests/unit/Mailer/Methods/AmazonSESTest.php b/tests/unit/Mailer/Methods/AmazonSESTest.php index dc1bde9be1..4a645f9dcd 100644 --- a/tests/unit/Mailer/Methods/AmazonSESTest.php +++ b/tests/unit/Mailer/Methods/AmazonSESTest.php @@ -1,7 +1,9 @@ settings['secret_key'], $this->sender, $this->reply_to, - $this->return_path + $this->return_path, + new AmazonSESMapper() ); $this->subscriber = 'Recipient '; $this->newsletter = array( @@ -69,7 +72,8 @@ class AmazonSESTest extends \MailPoetTest { $this->settings['secret_key'], $this->sender, $this->reply_to, - $return_path = false + $return_path = false, + new AmazonSESMapper() ); expect($mailer->return_path)->equals($this->sender['from_email']); } @@ -82,7 +86,8 @@ class AmazonSESTest extends \MailPoetTest { $this->settings['secret_key'], $this->sender, $this->reply_to, - $this->return_path + $this->return_path, + new AmazonSESMapper() ); $this->fail('Unsupported region exception was not thrown'); } catch(\Exception $e) { @@ -223,7 +228,8 @@ class AmazonSESTest extends \MailPoetTest { $invalid_subscriber ); expect($result['response'])->false(); - expect($result['error_message'])->contains('does not comply with RFC 2822'); + expect($result['error'])->isInstanceOf(MailerError::class); + expect($result['error']->getMessage())->contains('does not comply with RFC 2822'); } function testItCanSend() { diff --git a/tests/unit/Mailer/Methods/ErrorMappers/MailPoetMapperTest.php b/tests/unit/Mailer/Methods/ErrorMappers/MailPoetMapperTest.php new file mode 100644 index 0000000000..24e92fcd1e --- /dev/null +++ b/tests/unit/Mailer/Methods/ErrorMappers/MailPoetMapperTest.php @@ -0,0 +1,80 @@ +mapper = new MailPoetMapper(); + $this->subscribers = ['a@example.com', 'c d ']; + } + + function testCreateConnectionError() { + $error = $this->mapper->getConnectionError('connection error'); + expect($error)->isInstanceOf(MailerError::class); + expect($error->getOperation())->equals(MailerError::OPERATION_CONNECT); + expect($error->getLevel())->equals(MailerError::LEVEL_HARD); + expect($error->getMessage())->equals('connection error'); + } + + function testGetErrorNotArray() { + $api_result = [ + 'code' => API::RESPONSE_CODE_NOT_ARRAY, + 'status' => API::SENDING_STATUS_SEND_ERROR, + 'message' => 'error not array', + ]; + $error = $this->mapper->getErrorForResult($api_result, $this->subscribers); + + expect($error)->isInstanceOf(MailerError::class); + expect($error->getOperation())->equals(MailerError::OPERATION_SEND); + expect($error->getLevel())->equals(MailerError::LEVEL_HARD); + expect($error->getMessage())->equals('JSON input is not an array'); + } + + function testGetErrorPayloadTooBig() { + $api_result = [ + 'code' => API::RESPONSE_CODE_PAYLOAD_TOO_BIG, + 'status' => API::SENDING_STATUS_SEND_ERROR, + 'message' => 'error too big', + ]; + $error = $this->mapper->getErrorForResult($api_result, $this->subscribers); + expect($error)->isInstanceOf(MailerError::class); + expect($error->getOperation())->equals(MailerError::OPERATION_SEND); + expect($error->getLevel())->equals(MailerError::LEVEL_HARD); + expect($error->getMessage())->equals('error too big'); + } + + function testGetPayloadError() { + $api_result = [ + 'code' => API::RESPONSE_CODE_PAYLOAD_ERROR, + 'status' => API::SENDING_STATUS_SEND_ERROR, + 'message' => 'Api Error', + ]; + $error = $this->mapper->getErrorForResult($api_result, $this->subscribers); + expect($error)->isInstanceOf(MailerError::class); + expect($error->getOperation())->equals(MailerError::OPERATION_SEND); + expect($error->getLevel())->equals(MailerError::LEVEL_HARD); + expect($error->getMessage())->equals('Error while sending newsletters. Api Error'); + } + + function testGetPayloadErrorWithErrorMessage() { + $api_result = [ + 'code' => API::RESPONSE_CODE_PAYLOAD_ERROR, + 'status' => API::SENDING_STATUS_SEND_ERROR, + 'message' => '[{"index":0,"errors":{"subject":"subject is missing"}},{"index":1,"errors":{"subject":"subject is missing"}}]' + ]; + $error = $this->mapper->getErrorForResult($api_result, $this->subscribers); + expect($error)->isInstanceOf(MailerError::class); + expect($error->getOperation())->equals(MailerError::OPERATION_SEND); + expect($error->getLevel())->equals(MailerError::LEVEL_HARD); + expect($error->getMessage())->equals('Error while sending: (a@example.com: subject is missing), (c d : subject is missing)'); + } +} diff --git a/tests/unit/Mailer/Methods/ErrorMappers/SMTPMapperTest.php b/tests/unit/Mailer/Methods/ErrorMappers/SMTPMapperTest.php new file mode 100644 index 0000000000..fe3821a3c6 --- /dev/null +++ b/tests/unit/Mailer/Methods/ErrorMappers/SMTPMapperTest.php @@ -0,0 +1,46 @@ +mapper = new SMTPMapper(); + } + + function testItCanProcessExceptionMessage() { + $message = 'Connection could not be established with host localhost [Connection refused #111]' . PHP_EOL + . 'Log data:' . PHP_EOL + . '++ Starting Swift_SmtpTransport' . PHP_EOL + . '!! Connection could not be established with host localhost [Connection refused #111] (code: 0)'; + $error = $this->mapper->getErrorFromException(new \Exception($message)); + expect($error->getMessage()) + ->equals('Connection could not be established with host localhost [Connection refused #111]'); + } + + function testItCanProcessLogMessageWhenOneExists() { + $log = '++ Swift_SmtpTransport started' . PHP_EOL + . '>> MAIL FROM:' . PHP_EOL + . '<< 250 OK' . PHP_EOL + . '>> RCPT TO:' . PHP_EOL + . '<< 550 No such recipient here' . PHP_EOL + . '!! Expected response code 250/251/252 but got code "550", with message "550 No such recipient here' . PHP_EOL + . '" (code: 550)' . PHP_EOL + . '>> RSET' . PHP_EOL + . '<< 250 Reset OK' . PHP_EOL; + $error = $this->mapper->getErrorFromLog($log, 'test@example.com', []); + expect($error->getMessage()) + ->equals('Expected response code 250/251/252 but got code "550", with message "550 No such recipient here" (code: 550) Unprocessed subscriber: test@example.com'); + } + + function testItReturnsGenericMessageWhenLogMessageDoesNotExist() { + $error = $this->mapper->getErrorFromLog(null, 'test@example.com'); + expect($error->getMessage()) + ->equals(Mailer::METHOD_SMTP . ' has returned an unknown error. Unprocessed subscriber: test@example.com'); + } +} diff --git a/tests/unit/Mailer/Methods/MailPoetAPITest.php b/tests/unit/Mailer/Methods/MailPoetAPITest.php index 3ce64a90a9..e1db2009fe 100644 --- a/tests/unit/Mailer/Methods/MailPoetAPITest.php +++ b/tests/unit/Mailer/Methods/MailPoetAPITest.php @@ -3,6 +3,8 @@ namespace MailPoet\Test\Mailer\Methods; use Codeception\Util\Stub; use MailPoet\Config\ServicesChecker; +use MailPoet\Mailer\MailerError; +use MailPoet\Mailer\Methods\ErrorMappers\MailPoetMapper; use MailPoet\Mailer\Methods\MailPoet; use MailPoet\Services\Bridge\API; @@ -27,7 +29,8 @@ class MailPoetAPITest extends \MailPoetTest { $this->mailer = new MailPoet( $this->settings['api_key'], $this->sender, - $this->reply_to + $this->reply_to, + new MailPoetMapper() ); $this->subscriber = 'Recipient '; $this->newsletter = array( @@ -161,11 +164,9 @@ class MailPoetAPITest extends \MailPoetTest { $this ); $result = $this->mailer->send($this->newsletter, $this->subscriber); - expect($result)->equals([ - 'response' => false, - 'operation' => 'connect', - 'error_message' => 'connection error', - ]); + expect($result['response'])->false(); + expect($result['error'])->isInstanceOf(MailerError::class); + expect($result['error']->getOperation())->equals(MailerError::OPERATION_CONNECT); } function testFormatErrorNotArray() { @@ -179,11 +180,9 @@ class MailPoetAPITest extends \MailPoetTest { $this ); $result = $this->mailer->send($this->newsletter, $this->subscriber); - expect($result)->equals([ - 'response' => false, - 'operation' => 'send', - 'error_message' => 'JSON input is not an array', - ]); + expect($result['response'])->false(); + expect($result['error'])->isInstanceOf(MailerError::class); + expect($result['error']->getOperation())->equals(MailerError::OPERATION_SEND); } function testFormatErrorTooBig() { @@ -197,11 +196,8 @@ class MailPoetAPITest extends \MailPoetTest { $this ); $result = $this->mailer->send($this->newsletter, $this->subscriber); - expect($result)->equals([ - 'response' => false, - 'operation' => 'send', - 'error_message' => 'error too big', - ]); + expect($result['response'])->false(); + expect($result['error'])->isInstanceOf(MailerError::class); } function testFormatPayloadError() { @@ -215,11 +211,9 @@ class MailPoetAPITest extends \MailPoetTest { $this ); $result = $this->mailer->send([$this->newsletter, $this->newsletter], ['a@example.com', 'c d ']); - expect($result)->equals([ - 'response' => false, - 'operation' => 'send', - 'error_message' => 'Error while sending newsletters. Api Error', - ]); + expect($result['response'])->false(); + expect($result['error'])->isInstanceOf(MailerError::class); + expect($result['error']->getOperation())->equals(MailerError::OPERATION_SEND); } function testFormatPayloadErrorWithErrorMessage() { @@ -233,12 +227,8 @@ class MailPoetAPITest extends \MailPoetTest { $this ); $result = $this->mailer->send([$this->newsletter, $this->newsletter], ['a@example.com', 'c d ']); - expect($result)->equals([ - 'response' => false, - 'operation' => 'send', - 'error_message' => 'Error while sending: (a@example.com: subject is missing), (c d : subject is missing)', - ]); + expect($result['response'])->false(); + expect($result['error'])->isInstanceOf(MailerError::class); + expect($result['error']->getOperation())->equals(MailerError::OPERATION_SEND); } - - } diff --git a/tests/unit/Mailer/Methods/PHPMailTest.php b/tests/unit/Mailer/Methods/PHPMailTest.php index ca3d7c93b3..b57a90363b 100644 --- a/tests/unit/Mailer/Methods/PHPMailTest.php +++ b/tests/unit/Mailer/Methods/PHPMailTest.php @@ -1,6 +1,7 @@ mailer = new PHPMail( $this->sender, $this->reply_to, - $this->return_path + $this->return_path, + new PHPMailMapper() ); $this->subscriber = 'Recipient '; $this->newsletter = array( @@ -44,7 +46,8 @@ class PHPMailTest extends \MailPoetTest { $mailer = new PHPMail( $this->sender, $this->reply_to, - $return_path = false + $return_path = false, + new PHPMailMapper() ); expect($mailer->return_path)->equals($this->sender['from_email']); } diff --git a/tests/unit/Mailer/Methods/SMTPTest.php b/tests/unit/Mailer/Methods/SMTPTest.php index e5032d8ae7..cb92f167c0 100644 --- a/tests/unit/Mailer/Methods/SMTPTest.php +++ b/tests/unit/Mailer/Methods/SMTPTest.php @@ -3,6 +3,7 @@ namespace MailPoet\Test\Mailer\Methods; use Helper\WordPressHooks as WPHooksHelper; use MailPoet\Mailer\Mailer; +use MailPoet\Mailer\Methods\ErrorMappers\SMTPMapper; use MailPoet\Mailer\Methods\SMTP; use MailPoet\WP\Hooks; @@ -43,7 +44,8 @@ class SMTPTest extends \MailPoetTest { $this->settings['encryption'], $this->sender, $this->reply_to, - $this->return_path + $this->return_path, + new SMTPMapper() ); $this->subscriber = 'Recipient '; $this->newsletter = array( @@ -82,7 +84,8 @@ class SMTPTest extends \MailPoetTest { $this->settings['encryption'], $this->sender, $this->reply_to, - $return_path = false + $return_path = false, + new SMTPMapper() ); expect($mailer->return_path)->equals($this->sender['from_email']); } @@ -128,36 +131,6 @@ class SMTPTest extends \MailPoetTest { expect($result['response'])->false(); } - function testItCanProcessExceptionMessage() { - $message = 'Connection could not be established with host localhost [Connection refused #111]' . PHP_EOL - . 'Log data:' . PHP_EOL - . '++ Starting Swift_SmtpTransport' . PHP_EOL - . '!! Connection could not be established with host localhost [Connection refused #111] (code: 0)'; - expect($this->mailer->processExceptionMessage($message)) - ->equals('Connection could not be established with host localhost [Connection refused #111]'); - } - - function testItCanProcessLogMessageWhenOneExists() { - $message = '++ Swift_SmtpTransport started' . PHP_EOL - . '>> MAIL FROM:' . PHP_EOL - . '<< 250 OK' . PHP_EOL - . '>> RCPT TO:' . PHP_EOL - . '<< 550 No such recipient here' . PHP_EOL - . '!! Expected response code 250/251/252 but got code "550", with message "550 No such recipient here' . PHP_EOL - . '" (code: 550)' . PHP_EOL - . '>> RSET' . PHP_EOL - . '<< 250 Reset OK' . PHP_EOL; - expect($this->mailer->processLogMessage('test@example.com', $extra_params = array(), $message)) - ->equals('Expected response code 250/251/252 but got code "550", with message "550 No such recipient here" (code: 550) Unprocessed subscriber: test@example.com'); - expect($this->mailer->processLogMessage('test@example.com', $extra_params = array(), $message)) - ->equals('Expected response code 250/251/252 but got code "550", with message "550 No such recipient here" (code: 550) Unprocessed subscriber: test@example.com'); - } - - function testItReturnsGenericMessageWhenLogMessageDoesNotExist() { - expect($this->mailer->processLogMessage('test@example.com')) - ->equals(Mailer::METHOD_SMTP . ' has returned an unknown error. Unprocessed subscriber: test@example.com'); - } - function testItAppliesTransportFilter() { $mailer = $this->mailer->buildMailer(); expect($mailer->getTransport()->getStreamOptions())->isEmpty(); diff --git a/tests/unit/Mailer/Methods/SendGridTest.php b/tests/unit/Mailer/Methods/SendGridTest.php index 89cf1c433c..6061196994 100644 --- a/tests/unit/Mailer/Methods/SendGridTest.php +++ b/tests/unit/Mailer/Methods/SendGridTest.php @@ -1,6 +1,7 @@ mailer = new SendGrid( $this->settings['api_key'], $this->sender, - $this->reply_to + $this->reply_to, + new SendGridMapper() ); $this->subscriber = 'Recipient '; $this->newsletter = array(