diff options
author | Igor Scheller <igor.scheller@igorshp.de> | 2018-09-26 21:28:49 +0200 |
---|---|---|
committer | Igor Scheller <igor.scheller@igorshp.de> | 2018-09-26 21:31:18 +0200 |
commit | 6187eed3bb08f200050a3078bd762b5731dfbe78 (patch) | |
tree | 06e0b08294c66449ed5f9f046a3bfe2097c7b4ad | |
parent | bc5764b33ffc8a92dfa1d788ba2a204dd991c3d9 (diff) | |
parent | d36de2d26f5af76d5d4f34f8620694c6d0368983 (diff) |
Merge remote-tracking branch 'MyIgel/mailing'
-rw-r--r-- | composer.json | 1 | ||||
-rw-r--r-- | config/app.php | 13 | ||||
-rw-r--r-- | config/config.default.php | 40 | ||||
-rw-r--r-- | includes/helper/email_helper.php | 37 | ||||
-rw-r--r-- | src/Mail/EngelsystemMailer.php | 35 | ||||
-rw-r--r-- | src/Mail/Mailer.php | 79 | ||||
-rw-r--r-- | src/Mail/MailerServiceProvider.php | 89 | ||||
-rw-r--r-- | src/Mail/Transport/LogTransport.php | 44 | ||||
-rw-r--r-- | src/Mail/Transport/Transport.php | 103 | ||||
-rw-r--r-- | templates/emails/mail.twig | 6 | ||||
-rw-r--r-- | tests/Unit/Mail/EngelsystemMailerTest.php | 40 | ||||
-rw-r--r-- | tests/Unit/Mail/MailerServiceProviderTest.php | 177 | ||||
-rw-r--r-- | tests/Unit/Mail/MailerTest.php | 77 | ||||
-rw-r--r-- | tests/Unit/Mail/Transport/LogTransportTest.php | 60 | ||||
-rw-r--r-- | tests/Unit/Mail/Transport/Stub/TransportImplementation.php | 35 | ||||
-rw-r--r-- | tests/Unit/Mail/Transport/TransportTest.php | 83 |
16 files changed, 881 insertions, 38 deletions
diff --git a/composer.json b/composer.json index 622fbdfa..0632bbc3 100644 --- a/composer.json +++ b/composer.json @@ -29,6 +29,7 @@ "psr/container": "^1.0", "psr/http-server-middleware": "^1.0", "psr/log": "^1.0", + "swiftmailer/swiftmailer": "^6.1", "symfony/http-foundation": "^3.3", "symfony/psr-http-message-bridge": "^1.0", "twig/extensions": "^1.5", diff --git a/config/app.php b/config/app.php index e309abe4..efe745e8 100644 --- a/config/app.php +++ b/config/app.php @@ -5,9 +5,13 @@ return [ // Service providers 'providers' => [ + + // Application bootstrap \Engelsystem\Logger\LoggerServiceProvider::class, \Engelsystem\Exceptions\ExceptionsServiceProvider::class, \Engelsystem\Config\ConfigServiceProvider::class, + + // Request handling \Engelsystem\Http\UrlGeneratorServiceProvider::class, \Engelsystem\Renderer\RendererServiceProvider::class, \Engelsystem\Database\DatabaseServiceProvider::class, @@ -19,13 +23,22 @@ return [ \Engelsystem\Renderer\TwigServiceProvider::class, \Engelsystem\Middleware\RouteDispatcherServiceProvider::class, \Engelsystem\Middleware\RequestHandlerServiceProvider::class, + + // Additional services + \Engelsystem\Mail\MailerServiceProvider::class, ], // Application middleware 'middleware' => [ + + // Basic initialization \Engelsystem\Middleware\SendResponseHandler::class, \Engelsystem\Middleware\ExceptionHandler::class, + + // Changes of request/response parameters \Engelsystem\Middleware\SetLocale::class, + + // The application code \Engelsystem\Middleware\ErrorHandler::class, \Engelsystem\Middleware\RouteDispatcher::class, \Engelsystem\Middleware\RequestHandler::class, diff --git a/config/config.default.php b/config/config.default.php index dfccab0a..659a26b7 100644 --- a/config/config.default.php +++ b/config/config.default.php @@ -26,8 +26,24 @@ return [ // Contact email address, linked on every page 'contact_email' => env('CONTACT_EMAIL', 'mailto:ticket@c3heaven.de'), - // From address of all emails - 'no_reply_email' => env('NO_REPLY_EMAIL', 'noreply@engelsystem.de'), + // Email config + 'email' => [ + // Can be mail, smtp, sendmail or log + 'driver' => env('MAIL_DRIVER', 'smtp'), + 'from' => [ + // From address of all emails + 'address' => env('MAIL_FROM_ADDRESS', 'noreply@engelsystem.de'), + 'name' => env('MAIL_FROM_NAME', 'Engelsystem') + ], + + 'host' => env('MAIL_HOST', 'localhost'), + 'port' => env('MAIL_PORT', 587), + // Transport encryption like tls + 'encryption' => env('MAIL_ENCRYPTION', null), + 'username' => env('MAIL_USERNAME'), + 'password' => env('MAIL_PASSWORD'), + 'sendmail' => env('MAIL_SENDMAIL', '/usr/sbin/sendmail -bs'), + ], // Default theme, 1=style1.css 'theme' => env('THEME', 1), @@ -55,28 +71,27 @@ return [ // Only arrived angels can sign up for shifts 'signup_requires_arrival' => false, - // Anzahl Stunden bis zum Austragen eigener Schichten + // Number of hours that an angel has to sign out own shifts 'last_unsubscribe' => 3, - // Setzt den zu verwendenden Crypto-Algorithmus (entsprechend der Dokumentation von crypt()). - // Falls ein Benutzerpasswort in einem anderen Format gespeichert ist, - // wird es bei der ersten Benutzung des Klartext-Passworts in das neue Format - // konvertiert. + // Define the algorithm to use for `crypt()` of passwords + // If the user uses an old algorithm the password will be converted to the new format // MD5 '$1' // Blowfish '$2y$13' // SHA-256 '$5$rounds=5000' // SHA-512 '$6$rounds=5000' 'crypt_alg' => '$6$rounds=5000', + // The minimum length for passwords 'min_password_length' => 8, - // Wenn Engel beim Registrieren oder in ihrem Profil eine T-Shirt Größe angeben sollen, auf true setzen: + // Enables the T-Shirt configuration on signup and profile 'enable_tshirt_size' => true, // Number of shifts to freeload until angel is locked for shift signup. 'max_freeloadable_shifts' => 2, - // local timezone + // Local timezone 'timezone' => env('TIMEZONE', 'Europe/Berlin'), // Multiply 'night shifts' and freeloaded shifts (start or end between 2 and 6 exclusive) by 2 @@ -99,10 +114,11 @@ return [ 'en_US.UTF-8' => 'English', ], - 'default_locale' => env('DEFAULT_LOCALE', 'en_US.UTF-8'), + // The default locale to use + 'default_locale' => env('DEFAULT_LOCALE', 'en_US.UTF-8'), // Available T-Shirt sizes, set value to null if not available - 'tshirt_sizes' => [ + 'tshirt_sizes' => [ 'S' => 'S', 'S-G' => 'S Girl', 'M' => 'M', @@ -117,5 +133,5 @@ return [ ], // IP addresses of reverse proxies that are trusted, can be an array or a comma separated list - 'trusted_proxies' => env('TRUSTED_PROXIES', ['127.0.0.0/8', '::ffff:127.0.0.0/8', '::1/128']), + 'trusted_proxies' => env('TRUSTED_PROXIES', ['127.0.0.0/8', '::ffff:127.0.0.0/8', '::1/128']), ]; diff --git a/includes/helper/email_helper.php b/includes/helper/email_helper.php index a0631550..e9e5a323 100644 --- a/includes/helper/email_helper.php +++ b/includes/helper/email_helper.php @@ -1,5 +1,7 @@ <?php +use Engelsystem\Mail\EngelsystemMailer; + /** * @param array $recipient_user * @param string $title @@ -18,38 +20,21 @@ function engelsystem_email_to_user($recipient_user, $title, $message, $not_if_it /** @var \Engelsystem\Helpers\Translator $translator */ $translator = app()->get('translator'); $locale = $translator->getLocale(); + /** @var EngelsystemMailer $mailer */ + $mailer = app('mailer'); $translator->setLocale($recipient_user['Sprache']); - $message = sprintf(__('Hi %s,'), $recipient_user['Nick']) . "\n\n" - . __('here is a message for you from the engelsystem:') . "\n\n" - . $message . "\n\n" - . __('This email is autogenerated and has not been signed. You got this email because you are registered in the engelsystem.'); - $translator->setLocale($locale); - - return engelsystem_email($recipient_user['email'], $title, $message); -} - -/** - * @param string $address - * @param string $title - * @param string $message - * @return bool - */ -function engelsystem_email($address, $title, $message) -{ - $result = mail( - $address, + $status = $mailer->sendView( + $recipient_user['email'], $title, - $message, - sprintf( - "Content-Type: text/plain; charset=UTF-8\r\nFrom: Engelsystem <%s>", - config('no_reply_email') - ) + 'emails/mail', + ['user' => $recipient_user['Nick'], 'message' => $message] ); + $translator->setLocale($locale); - if ($result === false) { + if (!$status) { engelsystem_error('Unable to send email.'); } - return true; + return (bool)$status; } diff --git a/src/Mail/EngelsystemMailer.php b/src/Mail/EngelsystemMailer.php new file mode 100644 index 00000000..17047cc8 --- /dev/null +++ b/src/Mail/EngelsystemMailer.php @@ -0,0 +1,35 @@ +<?php + +namespace Engelsystem\Mail; + +use Engelsystem\Renderer\Renderer; +use Swift_Mailer as SwiftMailer; + +class EngelsystemMailer extends Mailer +{ + /** @var Renderer|null */ + protected $view; + + public function __construct(SwiftMailer $mailer, Renderer $view = null) + { + parent::__construct($mailer); + + $this->view = $view; + } + + /** + * Send a template + * + * @param string $to + * @param string $subject + * @param string $template + * @param array $data + * @return int + */ + public function sendView($to, $subject, $template, $data = []): int + { + $body = $this->view->render($template, $data); + + return $this->send($to, $subject, $body); + } +} diff --git a/src/Mail/Mailer.php b/src/Mail/Mailer.php new file mode 100644 index 00000000..ed800986 --- /dev/null +++ b/src/Mail/Mailer.php @@ -0,0 +1,79 @@ +<?php + +namespace Engelsystem\Mail; + +use Engelsystem\Renderer\Renderer; +use Swift_Mailer as SwiftMailer; +use Swift_Message as SwiftMessage; + +class Mailer +{ + /** @var SwiftMailer */ + protected $mailer; + + /** @var Renderer|null */ + protected $view; + + /** @var string */ + protected $fromAddress = ''; + + /** @var string */ + protected $fromName = null; + + public function __construct(SwiftMailer $mailer) + { + $this->mailer = $mailer; + } + + /** + * Send the mail + * + * @param string|string[] $to + * @param string $subject + * @param string $body + * @return int + */ + public function send($to, string $subject, string $body): int + { + /** @var SwiftMessage $message */ + $message = $this->mailer->createMessage(); + $message->setTo((array)$to) + ->setFrom($this->fromAddress, $this->fromName) + ->setSubject($subject) + ->setBody($body); + + return $this->mailer->send($message); + } + + /** + * @return string + */ + public function getFromAddress(): string + { + return $this->fromAddress; + } + + /** + * @param string $fromAddress + */ + public function setFromAddress(string $fromAddress) + { + $this->fromAddress = $fromAddress; + } + + /** + * @return string + */ + public function getFromName(): string + { + return $this->fromName; + } + + /** + * @param string $fromName + */ + public function setFromName(string $fromName) + { + $this->fromName = $fromName; + } +} diff --git a/src/Mail/MailerServiceProvider.php b/src/Mail/MailerServiceProvider.php new file mode 100644 index 00000000..989fee8f --- /dev/null +++ b/src/Mail/MailerServiceProvider.php @@ -0,0 +1,89 @@ +<?php + +namespace Engelsystem\Mail; + +use Engelsystem\Config\Config; +use Engelsystem\Container\ServiceProvider; +use Engelsystem\Mail\Transport\LogTransport; +use InvalidArgumentException; +use Swift_Mailer as SwiftMailer; +use Swift_SendmailTransport as SendmailTransport; +use Swift_SmtpTransport as SmtpTransport; +use Swift_Transport as Transport; + +class MailerServiceProvider extends ServiceProvider +{ + public function register() + { + /** @var Config $config */ + $config = $this->app->get('config'); + $mailConfig = $config->get('email'); + + $transport = $this->getTransport($mailConfig['driver'], $mailConfig); + $this->app->instance(Transport::class, $transport); + $this->app->instance('mailer.transport', $transport); + + /** @var SwiftMailer $swiftMailer */ + $swiftMailer = $this->app->make(SwiftMailer::class); + $this->app->instance(SwiftMailer::class, $swiftMailer); + $this->app->instance('mailer.swift', $swiftMailer); + + /** @var Mailer $mailer */ + $mailer = $this->app->make(EngelsystemMailer::class); + $mailer->setFromAddress($mailConfig['from']['address']); + if (!empty($mailConfig['from']['name'])) { + $mailer->setFromName($mailConfig['from']['name']); + } + + $this->app->instance(EngelsystemMailer::class, $mailer); + $this->app->instance(Mailer::class, $mailer); + $this->app->instance('mailer', $mailer); + } + + /** + * @param string $transport + * @param array $config + * @return Transport + */ + protected function getTransport($transport, $config) + { + switch ($transport) { + case 'log': + return $this->app->make(LogTransport::class); + case 'mail': + case 'sendmail': + return $this->app->make(SendmailTransport::class, ['command' => $config['sendmail']]); + case 'smtp': + return $this->getSmtpTransport($config); + } + + throw new InvalidArgumentException(sprintf('Mail driver "%s" not found', $transport)); + } + + /** + * @param array $config + * @return SmtpTransport + */ + protected function getSmtpTransport(array $config) + { + /** @var SmtpTransport $transport */ + $transport = $this->app->make(SmtpTransport::class, [ + 'host' => $config['host'], + 'port' => $config['port'], + 'encryption' => $config['encryption'], + // TODO: The security variable should be removed in the future + // https://github.com/swiftmailer/swiftmailer/commit/d3d6a98ab7dc155a04eb08273db7cd34606e7b5e#commitcomment-30462876 + 'security' => $config['encryption'], + ]); + + if ($config['username']) { + $transport->setUsername($config['username']); + } + + if ($config['password']) { + $transport->setPassword($config['password']); + } + + return $transport; + } +} diff --git a/src/Mail/Transport/LogTransport.php b/src/Mail/Transport/LogTransport.php new file mode 100644 index 00000000..6e351302 --- /dev/null +++ b/src/Mail/Transport/LogTransport.php @@ -0,0 +1,44 @@ +<?php + +namespace Engelsystem\Mail\Transport; + +use Psr\Log\LoggerInterface; +use Swift_Mime_SimpleMessage as SimpleMessage; + +class LogTransport extends Transport +{ + /** @var LoggerInterface */ + protected $logger; + + public function __construct(LoggerInterface $logger) + { + $this->logger = $logger; + } + + /** + * Send the given Message. + * + * Recipient/sender data will be retrieved from the Message API. + * The return value is the number of recipients + * + * @param SimpleMessage $message + * @param string[] $failedRecipients An array of failures by-reference + * + * @return int + */ + public function send( + SimpleMessage $message, + &$failedRecipients = null + ): int { + $this->logger->debug( + 'Mail: Send mail "{title}" to "{recipients}":' . PHP_EOL . '{content}', + [ + 'title' => $message->getSubject(), + 'recipients' => $this->getTo($message), + 'content' => (string)$message->getHeaders() . PHP_EOL . PHP_EOL . $message->toString(), + ] + ); + + return count($this->allRecipients($message)); + } +} diff --git a/src/Mail/Transport/Transport.php b/src/Mail/Transport/Transport.php new file mode 100644 index 00000000..691faf60 --- /dev/null +++ b/src/Mail/Transport/Transport.php @@ -0,0 +1,103 @@ +<?php + +namespace Engelsystem\Mail\Transport; + +use Swift_Events_EventListener; +use Swift_Mime_SimpleMessage as SimpleMessage; +use Swift_Transport as SwiftTransport; + +abstract class Transport implements SwiftTransport +{ + /** + * Test if this Transport mechanism has started. + * + * @return bool + */ + public function isStarted(): bool + { + return true; + } + + /** + * Start this Transport mechanism. + */ + public function start() { } + + /** + * Stop this Transport mechanism. + */ + public function stop() { } + + /** + * Check if this Transport mechanism is alive. + * + * If a Transport mechanism session is no longer functional, the method + * returns FALSE. It is the responsibility of the developer to handle this + * case and restart the Transport mechanism manually. + * + * @example + * + * if (!$transport->ping()) { + * $transport->stop(); + * $transport->start(); + * } + * + * The Transport mechanism will be started, if it is not already. + * + * It is undefined if the Transport mechanism attempts to restart as long as + * the return value reflects whether the mechanism is now functional. + * + * @return bool TRUE if the transport is alive + */ + public function ping(): bool + { + return true; + } + + /** + * Register a plugin in the Transport. + * + * @param Swift_Events_EventListener $plugin + */ + public function registerPlugin(Swift_Events_EventListener $plugin) { } + + /** + * Returns a unified list of all recipients + * + * @param SimpleMessage $message + * @return array + */ + protected function allRecipients(SimpleMessage $message): array + { + return array_merge( + (array)$message->getTo(), + (array)$message->getCc(), + (array)$message->getBcc() + ); + } + + /** + * Returns a concatenated list of mail recipients + * + * @param SimpleMessage $message + * @return string + */ + protected function getTo(SimpleMessage $message): string + { + return $this->formatTo($this->allRecipients($message)); + } + + /** + * @param array $recipients + * @return string + */ + protected function formatTo(array $recipients) + { + $list = []; + foreach ($recipients as $address => $name) { + $list[] = $name ? sprintf('%s <%s>', $name, $address) : $address; + } + + return implode(',', $list); + } +} diff --git a/templates/emails/mail.twig b/templates/emails/mail.twig new file mode 100644 index 00000000..f5ac5860 --- /dev/null +++ b/templates/emails/mail.twig @@ -0,0 +1,6 @@ +{{ __('Hi %s,', [user]) }} + +{{ __('here is a message for you from the engelsystem:') }} +{{ message|raw }} + +{{ __('This email is autogenerated and has not been signed. You got this email because you are registered in the engelsystem.') }} diff --git a/tests/Unit/Mail/EngelsystemMailerTest.php b/tests/Unit/Mail/EngelsystemMailerTest.php new file mode 100644 index 00000000..aae6e267 --- /dev/null +++ b/tests/Unit/Mail/EngelsystemMailerTest.php @@ -0,0 +1,40 @@ +<?php + +namespace Engelsystem\Test\Unit\Mail; + +use Engelsystem\Mail\EngelsystemMailer; +use Engelsystem\Renderer\Renderer; +use PHPUnit\Framework\MockObject\MockObject; +use PHPUnit\Framework\TestCase; +use Swift_Mailer as SwiftMailer; + +class EngelsystemMailerTest extends TestCase +{ + /** + * @covers \Engelsystem\Mail\EngelsystemMailer::__construct + * @covers \Engelsystem\Mail\EngelsystemMailer::sendView + */ + public function testSendView() + { + /** @var Renderer|MockObject $view */ + $view = $this->createMock(Renderer::class); + /** @var SwiftMailer|MockObject $swiftMailer */ + $swiftMailer = $this->createMock(SwiftMailer::class); + /** @var EngelsystemMailer|MockObject $mailer */ + $mailer = $this->getMockBuilder(EngelsystemMailer::class) + ->setConstructorArgs(['mailer' => $swiftMailer, 'view' => $view]) + ->setMethods(['send']) + ->getMock(); + $mailer->expects($this->once()) + ->method('send') + ->with('foo@bar.baz', 'Lorem dolor', 'Rendered Stuff!') + ->willReturn(1); + $view->expects($this->once()) + ->method('render') + ->with('test/template.tpl', ['dev' => true]) + ->willReturn('Rendered Stuff!'); + + $return = $mailer->sendView('foo@bar.baz', 'Lorem dolor', 'test/template.tpl', ['dev' => true]); + $this->equalTo(1, $return); + } +} diff --git a/tests/Unit/Mail/MailerServiceProviderTest.php b/tests/Unit/Mail/MailerServiceProviderTest.php new file mode 100644 index 00000000..0c841e9e --- /dev/null +++ b/tests/Unit/Mail/MailerServiceProviderTest.php @@ -0,0 +1,177 @@ +<?php + +namespace Engelsystem\Test\Unit\Middleware; + +use Engelsystem\Application; +use Engelsystem\Config\Config; +use Engelsystem\Mail\EngelsystemMailer; +use Engelsystem\Mail\Mailer; +use Engelsystem\Mail\MailerServiceProvider; +use Engelsystem\Mail\Transport\LogTransport; +use Engelsystem\Test\Unit\ServiceProviderTest; +use InvalidArgumentException; +use Psr\Log\LoggerInterface; +use Swift_Mailer as SwiftMailer; +use Swift_SendmailTransport as SendmailTransport; +use Swift_SmtpTransport as SmtpTransport; +use Swift_Transport as Transport; + +class MailerServiceProviderTest extends ServiceProviderTest +{ + /** @var array */ + protected $defaultConfig = [ + 'email' => [ + 'driver' => 'mail', + 'from' => [ + 'name' => 'Engelsystem', + 'address' => 'foo@bar.batz', + ], + 'sendmail' => '/opt/bin/sendmail -bs', + ], + ]; + + /** @var array */ + protected $smtpConfig = [ + 'email' => [ + 'driver' => 'smtp', + 'host' => 'mail.foo.bar', + 'port' => 587, + 'encryption' => 'tls', + 'username' => 'foobar', + 'password' => 'LoremIpsum123', + ], + ]; + + /** + * @covers \Engelsystem\Mail\MailerServiceProvider::register + */ + public function testRegister() + { + $app = $this->getApplication(); + + $serviceProvider = new MailerServiceProvider($app); + $serviceProvider->register(); + + $this->assertExistsInContainer(['mailer.transport', Transport::class], $app); + $this->assertExistsInContainer(['mailer.swift', SwiftMailer::class], $app); + $this->assertExistsInContainer(['mailer', EngelsystemMailer::class, Mailer::class], $app); + + /** @var EngelsystemMailer $mailer */ + $mailer = $app->get('mailer'); + $this->assertEquals('Engelsystem', $mailer->getFromName()); + $this->assertEquals('foo@bar.batz', $mailer->getFromAddress()); + + /** @var SendmailTransport $transport */ + $transport = $app->get('mailer.transport'); + $this->assertEquals($this->defaultConfig['email']['sendmail'], $transport->getCommand()); + } + + /** + * @return array + */ + public function provideTransports() + { + return [ + [LogTransport::class, ['email' => ['driver' => 'log']]], + [SendmailTransport::class, ['email' => ['driver' => 'mail']]], + [SendmailTransport::class, ['email' => ['driver' => 'sendmail']]], + [ + SmtpTransport::class, + $this->smtpConfig + ], + ]; + } + + /** + * @covers \Engelsystem\Mail\MailerServiceProvider::getTransport + * @param string $class + * @param array $emailConfig + * @dataProvider provideTransports + */ + public function testGetTransport($class, $emailConfig = []) + { + $app = $this->getApplication($emailConfig); + + $serviceProvider = new MailerServiceProvider($app); + $serviceProvider->register(); + + $transport = $app->get('mailer.transport'); + $this->assertInstanceOf($class, $transport); + } + + /** + * @covers \Engelsystem\Mail\MailerServiceProvider::getTransport + */ + public function testGetTransportNotFound() + { + $app = $this->getApplication(['email' => ['driver' => 'foo-bar-batz']]); + $this->expectException(InvalidArgumentException::class); + + $serviceProvider = new MailerServiceProvider($app); + $serviceProvider->register(); + } + + /** + * @covers \Engelsystem\Mail\MailerServiceProvider::getSmtpTransport + */ + public function testGetSmtpTransport() + { + $app = $this->getApplication($this->smtpConfig); + + $serviceProvider = new MailerServiceProvider($app); + $serviceProvider->register(); + + /** @var SmtpTransport $transport */ + $transport = $app->get('mailer.transport'); + + $this->assertEquals($this->smtpConfig['email']['host'], $transport->getHost()); + $this->assertEquals($this->smtpConfig['email']['port'], $transport->getPort()); + $this->assertEquals($this->smtpConfig['email']['encryption'], $transport->getEncryption()); + $this->assertEquals($this->smtpConfig['email']['username'], $transport->getUsername()); + $this->assertEquals($this->smtpConfig['email']['password'], $transport->getPassword()); + } + + /** + * @param array $configuration + * @return Application + */ + protected function getApplication($configuration = []): Application + { + $app = new Application(); + + $configuration = new Config(array_replace_recursive($this->defaultConfig, $configuration)); + $app->instance('config', $configuration); + + $logger = $this->getMockForAbstractClass(LoggerInterface::class); + $app->instance(LoggerInterface::class, $logger); + + return $app; + } + + /** + * @param string[] $abstracts + * @param Application $container + */ + protected function assertExistsInContainer($abstracts, $container) + { + $first = array_shift($abstracts); + $this->assertContainerHas($first, $container); + + foreach ($abstracts as $abstract) { + $this->assertContainerHas($abstract, $container); + $this->assertEquals($container->get($first), $container->get($abstract)); + } + } + + /** + * @param string $abstract + * @param Application $container + */ + protected function assertContainerHas($abstract, $container) + { + $this->assertTrue( + $container->has($abstract) || $container->hasMethodBinding($abstract), + sprintf('Container does not contain abstract %s', $abstract) + ); + } +} diff --git a/tests/Unit/Mail/MailerTest.php b/tests/Unit/Mail/MailerTest.php new file mode 100644 index 00000000..24c6f69a --- /dev/null +++ b/tests/Unit/Mail/MailerTest.php @@ -0,0 +1,77 @@ +<?php + +namespace Engelsystem\Test\Unit\Mail; + +use Engelsystem\Mail\Mailer; +use PHPUnit\Framework\MockObject\MockObject; +use PHPUnit\Framework\TestCase; +use Swift_Mailer as SwiftMailer; +use Swift_Message as SwiftMessage; + +class MailerTest extends TestCase +{ + /** + * @covers \Engelsystem\Mail\Mailer::__construct + * @covers \Engelsystem\Mail\Mailer::getFromAddress + * @covers \Engelsystem\Mail\Mailer::setFromAddress + * @covers \Engelsystem\Mail\Mailer::getFromName + * @covers \Engelsystem\Mail\Mailer::setFromName + */ + public function testInitAndSettersAndGetters() + { + /** @var SwiftMailer|MockObject $swiftMailer */ + $swiftMailer = $this->createMock(SwiftMailer::class); + + $mailer = new Mailer($swiftMailer); + + $mailer->setFromName('From Name'); + $this->assertEquals('From Name', $mailer->getFromName()); + + $mailer->setFromAddress('from@foo.bar'); + $this->assertEquals('from@foo.bar', $mailer->getFromAddress()); + } + + /** + * @covers \Engelsystem\Mail\Mailer::send + */ + public function testSend() + { + /** @var SwiftMessage|MockObject $message */ + $message = $this->createMock(SwiftMessage::class); + /** @var SwiftMailer|MockObject $swiftMailer */ + $swiftMailer = $this->createMock(SwiftMailer::class); + $swiftMailer->expects($this->once()) + ->method('createMessage') + ->willReturn($message); + $swiftMailer->expects($this->once()) + ->method('send') + ->willReturn(1); + + $message->expects($this->once()) + ->method('setTo') + ->with(['to@xam.pel']) + ->willReturn($message); + + $message->expects($this->once()) + ->method('setFrom') + ->with('foo@bar.baz', 'Lorem Ipsum') + ->willReturn($message); + + $message->expects($this->once()) + ->method('setSubject') + ->with('Foo Bar') + ->willReturn($message); + + $message->expects($this->once()) + ->method('setBody') + ->with('Lorem Ipsum!') + ->willReturn($message); + + $mailer = new Mailer($swiftMailer); + $mailer->setFromAddress('foo@bar.baz'); + $mailer->setFromName('Lorem Ipsum'); + + $return = $mailer->send('to@xam.pel', 'Foo Bar', 'Lorem Ipsum!'); + $this->equalTo(1, $return); + } +} diff --git a/tests/Unit/Mail/Transport/LogTransportTest.php b/tests/Unit/Mail/Transport/LogTransportTest.php new file mode 100644 index 00000000..5eb3a667 --- /dev/null +++ b/tests/Unit/Mail/Transport/LogTransportTest.php @@ -0,0 +1,60 @@ +<?php + +namespace Engelsystem\Test\Unit\Mail\Transport; + +use Engelsystem\Mail\Transport\LogTransport; +use PHPUnit\Framework\MockObject\MockObject; +use PHPUnit\Framework\TestCase; +use Psr\Log\LoggerInterface; +use Swift_Mime_SimpleMessage as SimpleMessage; + +class LogTransportTest extends TestCase +{ + /** + * @covers \Engelsystem\Mail\Transport\LogTransport::__construct + * @covers \Engelsystem\Mail\Transport\LogTransport::send + */ + public function testSend() + { + /** @var LoggerInterface|MockObject $logger */ + $logger = $this->getMockForAbstractClass(LoggerInterface::class); + /** @var SimpleMessage|MockObject $message */ + $message = $this->createMock(SimpleMessage::class); + + $message->expects($this->once()) + ->method('getSubject') + ->willReturn('Some subject'); + $message->expects($this->once()) + ->method('getHeaders') + ->willReturn('Head: er'); + $message->expects($this->once()) + ->method('toString') + ->willReturn('Message body'); + + $logger->expects($this->once()) + ->method('debug') + ->willReturnCallback(function ($message, $context = []) { + foreach (array_keys($context) as $key) { + $this->assertContains(sprintf('{%s}', $key), $message); + } + + $this->assertEquals('Some subject', $context['title']); + $this->assertEquals('foo@bar.batz,Lorem Ipsum <lor@em.ips>', $context['recipients']); + $this->assertContains('Head: er', $context['content']); + $this->assertContains('Message body', $context['content']); + }); + + /** @var LogTransport|MockObject $transport */ + $transport = $this->getMockBuilder(LogTransport::class) + ->setConstructorArgs(['logger' => $logger]) + ->setMethods(['allRecipients']) + ->getMock(); + $transport->expects($this->exactly(2)) + ->method('allRecipients') + ->with($message) + ->willReturn(['foo@bar.batz' => null, 'lor@em.ips' => 'Lorem Ipsum']); + + $return = $transport->send($message); + $this->equalTo(2, $return); + } +} diff --git a/tests/Unit/Mail/Transport/Stub/TransportImplementation.php b/tests/Unit/Mail/Transport/Stub/TransportImplementation.php new file mode 100644 index 00000000..e3667c6e --- /dev/null +++ b/tests/Unit/Mail/Transport/Stub/TransportImplementation.php @@ -0,0 +1,35 @@ +<?php + +namespace Engelsystem\Test\Unit\Mail\Transport\Stub; + +use Engelsystem\Mail\Transport\Transport; +use Swift_Mime_SimpleMessage as SimpleMessage; + +class TransportImplementation extends Transport +{ + /** + * {@inheritdoc} + */ + public function send(SimpleMessage $message, &$failedRecipients = null) + { + return 0; + } + + /** + * @param SimpleMessage $message + * @return array + */ + public function getAllRecipients(SimpleMessage $message) + { + return $this->allRecipients($message); + } + + /** + * @param SimpleMessage $message + * @return string + */ + public function getGetTo(SimpleMessage $message) + { + return $this->getTo($message); + } +} diff --git a/tests/Unit/Mail/Transport/TransportTest.php b/tests/Unit/Mail/Transport/TransportTest.php new file mode 100644 index 00000000..60f2079d --- /dev/null +++ b/tests/Unit/Mail/Transport/TransportTest.php @@ -0,0 +1,83 @@ +<?php + +namespace Engelsystem\Test\Unit\Mail\Transport; + +use Engelsystem\Test\Unit\Mail\Transport\Stub\TransportImplementation; +use PHPUnit\Framework\MockObject\MockObject; +use PHPUnit\Framework\TestCase; +use Swift_Mime_SimpleMessage as SimpleMessage; + +class TransportTest extends TestCase +{ + /** + * @covers \Engelsystem\Mail\Transport\Transport::isStarted + * @covers \Engelsystem\Mail\Transport\Transport::ping + */ + public function testMethods() + { + $transport = new TransportImplementation(); + + $this->assertTrue($transport->isStarted()); + $this->assertTrue($transport->ping()); + } + + /** + * @covers \Engelsystem\Mail\Transport\Transport::allRecipients + */ + public function testAllRecipients() + { + /** @var SimpleMessage|MockObject $message */ + $message = $this->createMock(SimpleMessage::class); + $transport = new TransportImplementation(); + $message->expects($this->once()) + ->method('getTo') + ->willReturn([ + 'foo@bar.batz' => 'Foo Bar', + 'lorem@ipsum.dolor' => null, + ]); + $message->expects($this->once()) + ->method('getCc') + ->willReturn([ + 'to@bar.batz' => null, + ]); + $message->expects($this->once()) + ->method('getBcc') + ->willReturn([ + 'secret@bar.batz' => 'I\'m secret!', + ]); + + $this->assertEquals( + [ + 'foo@bar.batz' => 'Foo Bar', + 'lorem@ipsum.dolor' => null, + 'to@bar.batz' => null, + 'secret@bar.batz' => 'I\'m secret!', + ], + $transport->getAllRecipients($message) + ); + } + + /** + * @covers \Engelsystem\Mail\Transport\Transport::getTo + * @covers \Engelsystem\Mail\Transport\Transport::formatTo + */ + public function testGetTo() + { + /** @var SimpleMessage|MockObject $message */ + $message = $this->createMock(SimpleMessage::class); + /** @var TransportImplementation|MockObject $transport */ + $transport = $this->getMockBuilder(TransportImplementation::class) + ->setMethods(['allRecipients']) + ->getMock(); + $transport->expects($this->once()) + ->method('allRecipients') + ->with($message) + ->willReturn([ + 'foo@bar.batz' => null, + 'lorem@ipsum.dolor' => 'Developer', + ]); + + $return = $transport->getGetTo($message); + $this->assertEquals('foo@bar.batz,Developer <lorem@ipsum.dolor>', $return); + } +} |