diff --git a/README.md b/README.md index 1f64c1c..6054211 100644 --- a/README.md +++ b/README.md @@ -38,6 +38,7 @@ If you are updating from an earlier version, you will need to update the config ```bash php artisan vendor:publish ``` + ```bash php artisan migrate ``` @@ -72,7 +73,7 @@ If you would like to use your own migrations, you can skip this library migratio ```php // In AppServiceProvider -public function boot() +public function register() { MailTracker::ignoreMigrations(); } @@ -103,15 +104,16 @@ If you do not wish to have an email tracked, then you can add the `X-No-Track` h ### Storing content of mails in filesystem -By default, the content of an e-mail is stored in the `content` column in the database so that the e-mail can be viewed after it has been sent. +By default, the content of an e-mail is stored in the `content` column in the database so that the e-mail can be viewed after it has been sent. If a lot of emails are sent, this can consume a lot of memory and slow down the database overall. It is possible to specify in the configuration that the content should be saved to a file in the file system. -````php +```php 'log-content-strategy' => 'filesystem', 'tracker-filesystem' => null 'tracker-filesystem-folder' => 'mail-tracker', -```` -To use the filesystem you need to change the `log-content-strategy` from `database` to `filesystem`. +``` + +To use the filesystem you need to change the `log-content-strategy` from `database` to `filesystem`. You can specify the disk with `tracker-filesystem` and the folder it should store the file in with `tracker-filesystem-folder`. ### Overriding models @@ -148,6 +150,7 @@ class OwnEmailSentModel extends Model implements SentEmailModel { If you have a specific email that you do not want to track, you can add the `X-No-Track` header to the email. This will prevent the email from being tracked. The header will be removed from the email prior to being sent. In laravel 9 onwards you can introduce a headers method to your Mailable class. This will stop the tracking pixel/click tracking from applying to the Mailable + ```php public function headers() { @@ -159,7 +162,7 @@ public function headers() ## Skipping Open/Click Tracking for Anti-virus/Spam Filters -Some mail servers might scan emails before they deliver which can trigger the tracking pixel, or even clicked links. You can add an event listener to the ValidActionEvent to handle this. +Some mail servers might scan emails before they deliver which can trigger the tracking pixel, or even clicked links. You can add an event listener to the ValidActionEvent to handle this. ```php class ValidUserListener { @@ -187,27 +190,27 @@ When an email is sent, viewed, or a link is clicked, its tracking information is You may want to do additional processing on these events, so an event is fired in these cases: - jdavidbakr\MailTracker\Events\EmailSentEvent - - Public attribute `sent_email` contains the `SentEmail` model + - Public attribute `sent_email` contains the `SentEmail` model - jdavidbakr\MailTracker\Events\ViewEmailEvent - - Public attribute `sent_email` contains the `SentEmail` model - - Public attribute `ip_address` contains the IP address that was used to trigger the event + - Public attribute `sent_email` contains the `SentEmail` model + - Public attribute `ip_address` contains the IP address that was used to trigger the event - jdavidbakr\MailTracker\Events\LinkClickedEvent - - Public attribute `sent_email` contains the `SentEmail` model - - Public attribute `ip_address` contains the IP address that was used to trigger the event - - Public attribute `link_url` contains the clicked URL + - Public attribute `sent_email` contains the `SentEmail` model + - Public attribute `ip_address` contains the IP address that was used to trigger the event + - Public attribute `link_url` contains the clicked URL If you are using the Amazon SNS notification system, these events are fired so you can do additional processing. - jdavidbakr\MailTracker\Events\EmailDeliveredEvent (when you received a "message delivered" event, you may want to mark the email as "good" or "delivered" in your database) - - Public attribute `sent_email` contains the `SentEmail` model - - Public attribute `email_address` contains the specific address that was used to trigger the event + - Public attribute `sent_email` contains the `SentEmail` model + - Public attribute `email_address` contains the specific address that was used to trigger the event - jdavidbakr\MailTracker\Events\ComplaintMessageEvent (when you received a complaint, ex: marked as "spam", you may want to remove the email from your database) - - Public attribute `sent_email` contains the `SentEmail` model - - Public attribute `email_address` contains the specific address that was used to trigger the event + - Public attribute `sent_email` contains the `SentEmail` model + - Public attribute `email_address` contains the specific address that was used to trigger the event - jdavidbakr\MailTracker\Events\PermanentBouncedMessageEvent (when you receive a permanent bounce, you may want to mark the email as bad or remove it from your database) - jdavidbakr\MailTracker\Events\TransientBouncedMessageEvent (when you receive a transient bounce. Check the event's public attributes for `bounce_sub_type` and `diagnostic_code` to determine if you want to do additional processing when this event is received.) - - Public attribute `sent_email` contains the `SentEmail` model - - Public attribute `email_address` contains the specific address that was used to trigger the event + jdavidbakr\MailTracker\Events\TransientBouncedMessageEvent (when you receive a transient bounce. Check the event's public attributes for `bounce_sub_type` and `diagnostic_code` to determine if you want to do additional processing when this event is received.) + - Public attribute `sent_email` contains the `SentEmail` model + - Public attribute `email_address` contains the specific address that was used to trigger the event To install an event listener, you will want to create a file like the following: diff --git a/composer.json b/composer.json index 09383ee..1bd2d58 100644 --- a/composer.json +++ b/composer.json @@ -19,14 +19,14 @@ ], "require": { "php": "^8.1", - "illuminate/support": "^10.0|^11.0|^12.0", + "illuminate/support": "^10.0|^11.0|^12.0|^13.0", "guzzlehttp/guzzle": "^7.2", "aws/aws-php-sns-message-validator": "^1.8", "aws/aws-sdk-php": "^3.258" }, "require-dev": { - "phpunit/phpunit": "^9.5.10|^10.5|^11.5.3", - "orchestra/testbench": "^8.0|^9.0|^10.0", + "phpunit/phpunit": "^9.5.10|^10.5|^11.5.3|^12.5.12", + "orchestra/testbench": "^8.0|^9.0|^10.0|^11.0", "mockery/mockery": "^1.4.4" }, "suggest": { @@ -52,4 +52,4 @@ ] } } -} \ No newline at end of file +} diff --git a/config/mail-tracker.php b/config/mail-tracker.php index 20ea150..7b40d0c 100644 --- a/config/mail-tracker.php +++ b/config/mail-tracker.php @@ -4,23 +4,23 @@ /** * To disable the pixel injection, set this to false. */ - 'inject-pixel' => true, + 'inject-pixel' => true, /** * To disable injecting tracking links, set this to false. */ - 'track-links' => true, + 'track-links' => true, /** * Optionally expire old emails, set to 0 to keep forever. */ - 'expire-days' => 60, + 'expire-days' => 60, /** * Where should the pingback URL route be? */ - 'route' => [ - 'prefix' => 'email', + 'route' => [ + 'prefix' => 'email', 'middleware' => ['api'], ], @@ -32,12 +32,12 @@ /** * Where should the admin route be? */ - 'admin-route' => [ - 'enabled' => true, // Should the admin routes be enabled? - 'prefix' => 'email-manager', + 'admin-route' => [ + 'enabled' => true, // Should the admin routes be enabled? + 'prefix' => 'email-manager', 'middleware' => [ 'web', - 'can:see-sent-emails' + 'can:see-sent-emails', ], ], @@ -48,60 +48,67 @@ * 'section' => 'content' for Default emailTraking use 'content' * 'styles_section' => 'styles' for Default emailTraking use 'styles' */ - 'admin-template' => [ - 'name' => 'emailTrakingViews::layouts.app', + 'admin-template' => [ + 'name' => 'emailTrakingViews::layouts.app', 'section' => 'content', ], /** * Number of emails per page in the admin view */ - 'emails-per-page' => 30, + 'emails-per-page' => 30, /** * Date Format */ - 'date-format' => 'm/d/Y g:i a', + 'date-format' => 'm/d/Y g:i a', /** * Default database connection name (optional - use null for default) */ - 'connection' => null, + 'connection' => null, /** * The SNS notification topic - if set, discard all notifications not in this topic. */ - 'sns-topic' => null, + 'sns-topic' => null, /** * Determines whether the body of the email is logged in the sent_emails table */ - 'log-content' => true, + 'log-content' => true, /** * Determines whether the body should be stored in a file instead of database * Can be either 'database' or 'filesystem' */ - 'log-content-strategy' => 'database', + 'log-content-strategy' => 'database', /** * What filesystem we use for storing content html files */ - 'tracker-filesystem' => null, + 'tracker-filesystem' => null, 'tracker-filesystem-folder' => 'mail-tracker', /** * What queue should we dispatch our tracking jobs to? Null will use the default queue. */ - 'tracker-queue' => null, + 'tracker-queue' => null, /** * Size limit for content length stored in database */ - 'content-max-size' => 65535, + 'content-max-size' => 65535, /** * Length of time to default past email search - if set, will set the default past limit to the amount of days below (Ex: => 356) */ - 'search-date-start' => null, + 'search-date-start' => null, + + /** + * Fallback method for when ValidateSignature has been introduced, but old links still need to be supported + */ + 'fallback-event-listeners' => [ + \jdavidbakr\MailTracker\Listener\DomainExistsInContentListener::class, + ] ]; diff --git a/src/Events/ValidActionEvent.php b/src/Events/ValidActionEvent.php index 23ed11d..7b2047c 100644 --- a/src/Events/ValidActionEvent.php +++ b/src/Events/ValidActionEvent.php @@ -11,6 +11,7 @@ class ValidActionEvent use Dispatchable; public $skip = false; + public $sent_email; public function __construct(Model|SentEmailModel $sent_email) { diff --git a/src/Events/ValidLinkEvent.php b/src/Events/ValidLinkEvent.php new file mode 100644 index 0000000..24c85ea --- /dev/null +++ b/src/Events/ValidLinkEvent.php @@ -0,0 +1,22 @@ +sent_email = $sent_email; + $this->url = $url; + } +} diff --git a/src/Listener/DomainExistsInContentListener.php b/src/Listener/DomainExistsInContentListener.php new file mode 100644 index 0000000..7082ed2 --- /dev/null +++ b/src/Listener/DomainExistsInContentListener.php @@ -0,0 +1,19 @@ +url, PHP_URL_HOST); + // If logging of content is on then + if (config('mail-tracker.log-content', true)) { + if ($event->sent_email && !empty($event->sent_email->domains_in_context) && in_array($url_host, $event->sent_email->domains_in_context)) { + $event->valid = true; + } + } + } +} diff --git a/src/MailTracker.php b/src/MailTracker.php index c3600fd..6eb4f10 100644 --- a/src/MailTracker.php +++ b/src/MailTracker.php @@ -9,6 +9,7 @@ use Illuminate\Mail\SentMessage; use Illuminate\Support\Facades\Event; use Illuminate\Support\Facades\Storage; +use Illuminate\Support\Facades\URL; use Illuminate\Support\Str; use jdavidbakr\MailTracker\Contracts\SentEmailModel; use jdavidbakr\MailTracker\Events\EmailSentEvent; @@ -214,27 +215,15 @@ protected function inject_link_callback($matches) $url = str_replace('&', '&', $matches[2]); } - return $matches[1].route( + return $matches[1].URL::signedRoute( 'mailTracker_n', [ 'l' => $url, - 'h' => $this->hash + 'h' => $this->hash, ] ); } - /** - * Legacy function - * - * @param [type] $url - * @return boolean - */ - public static function hash_url($url) - { - // Replace "/" with "$" - return str_replace("/", "$", base64_encode($url)); - } - /** * Create the trackers * diff --git a/src/MailTrackerController.php b/src/MailTrackerController.php index 12a2e53..c7a8159 100644 --- a/src/MailTrackerController.php +++ b/src/MailTrackerController.php @@ -2,12 +2,10 @@ namespace jdavidbakr\MailTracker; -use App\Http\Requests; -use Event; use Illuminate\Http\Request; use Illuminate\Routing\Controller; +use Illuminate\Support\Facades\Event; use jdavidbakr\MailTracker\Events\ValidActionEvent; -use jdavidbakr\MailTracker\Exceptions\BadUrlLink; use Response; class MailTrackerController extends Controller @@ -15,10 +13,10 @@ class MailTrackerController extends Controller public function getT($hash) { // Create a 1x1 transparent pixel and return it - $pixel = sprintf('%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c', 71, 73, 70, 56, 57, 97, 1, 0, 1, 0, 128, 255, 0, 192, 192, 192, 0, 0, 0, 33, 249, 4, 1, 0, 0, 0, 0, 44, 0, 0, 0, 0, 1, 0, 1, 0, 0, 2, 2, 68, 1, 0, 59); + $pixel = sprintf('%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c', 71, 73, 70, 56, 57, 97, 1, 0, 1, 0, 128, 255, 0, 192, 192, 192, 0, 0, 0, 33, 249, 4, 1, 0, 0, 0, 0, 44, 0, 0, 0, 0, 1, 0, 1, 0, 0, 2, 2, 68, 1, 0, 59); $response = Response::make($pixel, 200); $response->header('Content-type', 'image/gif'); - $response->header('Content-Length', 42); + $response->header('Content-Length', strlen($pixel)); $response->header('Cache-Control', 'private, no-cache, no-cache=Set-Cookie, proxy-revalidate'); $response->header('Expires', 'Wed, 11 Jan 2000 12:59:00 GMT'); $response->header('Last-Modified', 'Wed, 11 Jan 2006 12:59:00 GMT'); @@ -29,7 +27,7 @@ public function getT($hash) if ($tracker) { $event = new ValidActionEvent($tracker); - \Illuminate\Support\Facades\Event::dispatch($event); + Event::dispatch($event); if (!$event->skip) { RecordTrackingJob::dispatch($tracker, request()->ip()) @@ -44,37 +42,19 @@ public function getT($hash) return $response; } - public function getL($url, $hash) - { - $url = base64_decode(str_replace("$", "/", $url)); - if (filter_var($url, FILTER_VALIDATE_URL) === false) { - throw new BadUrlLink('Mail hash: '.$hash.', URL: '.$url); - } - return $this->linkClicked($url, $hash); - } - public function getN(Request $request) { - $url = $request->l; + $url = $request->l; $hash = $request->h; - return $this->linkClicked($url, $hash); - } - protected function linkClicked($url, $hash) - { - if (!$url) { - $url = config('mail-tracker.redirect-missing-links-to') ?: '/'; - } - - $url_host = parse_url($url, PHP_URL_HOST); - $tracker = MailTracker::sentEmailModel()->newQuery()->where('hash', $hash) - ->first(); + $tracker = MailTracker::sentEmailModel()->newQuery()->where('hash', $hash)->first(); if ($tracker) { $event = new ValidActionEvent($tracker); - \Illuminate\Support\Facades\Event::dispatch($event); + Event::dispatch($event); + // If the event does not skip the tracking then we can log that the link was clicked if (!$event->skip) { RecordLinkClickJob::dispatch($tracker, $url, request()->ip()) ->onQueue(config('mail-tracker.tracker-queue')); @@ -82,20 +62,17 @@ protected function linkClicked($url, $hash) // If no opened at but has a clicked event then we can assume that it was in fact opened, the tracking pixel may have been blocked if (config('mail-tracker.inject-pixel') && !$tracker->opened_at) { $tracker->opened_at = now(); - $tracker->save(); } if (!$tracker->clicked_at) { $tracker->clicked_at = now(); - $tracker->save(); } - } - } - if ( ! $tracker || empty($tracker->domains_in_context) || ! in_array($url_host, $tracker->domains_in_context) ){ - return redirect(config('mail-tracker.redirect-missing-links-to') ?: '/'); + $tracker->save(); + } } + // Perform the redirect as we know this is safe as it's made it through the ValidateSignature middleware or the ValidLinkEvent return redirect($url); } } diff --git a/src/MailTrackerServiceProvider.php b/src/MailTrackerServiceProvider.php index 1bf4b04..df654e6 100644 --- a/src/MailTrackerServiceProvider.php +++ b/src/MailTrackerServiceProvider.php @@ -8,20 +8,12 @@ use Illuminate\Support\Facades\Event; use Illuminate\Support\Facades\Route; use Illuminate\Support\ServiceProvider; +use jdavidbakr\MailTracker\Events\ValidLinkEvent; +use jdavidbakr\MailTracker\Listener\DomainExistsInContentListener; +use jdavidbakr\MailTracker\Middleware\ValidateSignature; class MailTrackerServiceProvider extends ServiceProvider { - /** - * Check to see if we're using lumen or laravel. - * - * @return bool - */ - public function isLumen() - { - $lumenClass = 'Laravel\Lumen\Application'; - return ($this->app instanceof $lumenClass); - } - /** * Perform post-registration booting of services. * @@ -30,7 +22,7 @@ public function isLumen() public function boot() { if (MailTracker::$runsMigrations && $this->app->runningInConsole()) { - $this->loadMigrationsFrom(__DIR__.'/../database/migrations'); + $this->loadMigrationsFrom(__DIR__ . '/../database/migrations'); } // Publish pieces @@ -41,15 +33,22 @@ public function boot() $this->registerCommands(); // Hook into the mailer - Event::listen(MessageSending::class, function(MessageSending $event) { + Event::listen(MessageSending::class, function (MessageSending $event) { $tracker = new MailTracker; $tracker->messageSending($event); }); - Event::listen(MessageSent::class, function(MessageSent $mail) { + Event::listen(MessageSent::class, function (MessageSent $mail) { $tracker = new MailTracker; $tracker->messageSent($mail); }); + foreach (config('mail-tracker.fallback-event-listeners', [ + DomainExistsInContentListener::class, + ]) as $listener) { + // This event is only fired when the ValidateSignature middleware is not able to validate the link + Event::listen(ValidLinkEvent::class, $listener); + } + // Install the routes $this->installRoutes(); } @@ -71,11 +70,9 @@ public function register() */ protected function publishConfig() { - if (!$this->isLumen()) { - $this->publishes([ - __DIR__.'/../config/mail-tracker.php' => config_path('mail-tracker.php') - ], 'config'); - } + $this->publishes([ + __DIR__ . '/../config/mail-tracker.php' => config_path('mail-tracker.php'), + ], 'config'); } /** @@ -85,12 +82,10 @@ protected function publishConfig() */ protected function publishViews() { - if (!$this->isLumen()) { - $this->loadViewsFrom(__DIR__.'/views', 'emailTrakingViews'); - $this->publishes([ - __DIR__.'/views' => base_path('resources/views/vendor/emailTrakingViews'), - ]); - } + $this->loadViewsFrom(__DIR__ . '/views', 'emailTrakingViews'); + $this->publishes([ + __DIR__ . '/views' => base_path('resources/views/vendor/emailTrakingViews'), + ]); } public function registerCommands() @@ -109,49 +104,28 @@ public function registerCommands() */ protected function installRoutes() { - $config = $this->app['config']->get('mail-tracker.route', []); + $config = $this->app['config']->get('mail-tracker.route', []); $config['namespace'] = 'jdavidbakr\MailTracker'; - if (!$this->isLumen()) { - Route::group($config, function () { - Route::get('t/{hash}', 'MailTrackerController@getT')->name('mailTracker_t'); - Route::get('l/{url}/{hash}', 'MailTrackerController@getL')->name('mailTracker_l'); - Route::get('n', 'MailTrackerController@getN')->name('mailTracker_n'); - Route::post('sns', 'SNSController@callback')->name('mailTracker_SNS'); - }); - } else { - $app = $this->app; - $app->group($config, function () use ($app) { - $app->get('t', 'MailTrackerController@getT')->name('mailTracker_t'); - $app->get('l', 'MailTrackerController@getL')->name('mailTracker_l'); - $app->post('sns', 'SNSController@callback')->name('mailTracker_SNS'); - }); - } + Route::group($config, function () { + Route::get('t/{hash}', 'MailTrackerController@getT')->name('mailTracker_t'); + Route::get('n', 'MailTrackerController@getN')->name('mailTracker_n')->middleware(ValidateSignature::class); + Route::post('sns', 'SNSController@callback')->name('mailTracker_SNS'); + }); + // Install the Admin routes - $config_admin = $this->app['config']->get('mail-tracker.admin-route', []); + $config_admin = $this->app['config']->get('mail-tracker.admin-route', []); $config_admin['namespace'] = 'jdavidbakr\MailTracker'; if (Arr::get($config_admin, 'enabled', true)) { - if (!$this->isLumen()) { - Route::group($config_admin, function () { - Route::get('/', 'AdminController@getIndex')->name('mailTracker_Index'); - Route::post('search', 'AdminController@postSearch')->name('mailTracker_Search'); - Route::get('clear-search', 'AdminController@clearSearch')->name('mailTracker_ClearSearch'); - Route::get('show-email/{id}', 'AdminController@getShowEmail')->name('mailTracker_ShowEmail'); - Route::get('url-detail/{id}', 'AdminController@getUrlDetail')->name('mailTracker_UrlDetail'); - Route::get('smtp-detail/{id}', 'AdminController@getSmtpDetail')->name('mailTracker_SmtpDetail'); - }); - } else { - $app = $this->app; - $app->group($config_admin, function () use ($app) { - $app->get('/', 'AdminController@getIndex')->name('mailTracker_Index'); - $app->post('search', 'AdminController@postSearch')->name('mailTracker_Search'); - $app->get('clear-search', 'AdminController@clearSearch')->name('mailTracker_ClearSearch'); - $app->get('show-email/{id}', 'AdminController@getShowEmail')->name('mailTracker_ShowEmail'); - $app->get('url-detail/{id}', 'AdminController@getUrlDetail')->name('mailTracker_UrlDetail'); - $app->get('smtp-detail/{id}', 'AdminController@getSmtpDetail')->name('mailTracker_SmtpDetail'); - }); - } + Route::group($config_admin, function () { + Route::get('/', 'AdminController@getIndex')->name('mailTracker_Index'); + Route::post('search', 'AdminController@postSearch')->name('mailTracker_Search'); + Route::get('clear-search', 'AdminController@clearSearch')->name('mailTracker_ClearSearch'); + Route::get('show-email/{id}', 'AdminController@getShowEmail')->name('mailTracker_ShowEmail'); + Route::get('url-detail/{id}', 'AdminController@getUrlDetail')->name('mailTracker_UrlDetail'); + Route::get('smtp-detail/{id}', 'AdminController@getSmtpDetail')->name('mailTracker_SmtpDetail'); + }); } } } diff --git a/src/Middleware/ValidateSignature.php b/src/Middleware/ValidateSignature.php new file mode 100644 index 0000000..9c8a695 --- /dev/null +++ b/src/Middleware/ValidateSignature.php @@ -0,0 +1,46 @@ +get('h'); + $url = $request->get('l'); + + if (filter_var($url, FILTER_VALIDATE_URL) === false) { + throw new BadUrlLink('Mail hash: ' . $hash . ', URL: ' . $url); + } + + [$relative, $ignore] = $this->parseArguments($args); + + // If the signature is valid then we know that it has not been tampered with so continue + if ($request->hasValidSignatureWhileIgnoring($ignore, ! $relative)) { + return $next($request); + } + + // If the signature is not valid then we need to check if the link is valid + /** @var SentEmail $tracker */ + if ($tracker = MailTracker::sentEmailModel()->newQuery()->where('hash', $hash)->first()) { + // If the link is not from a valid signed route then determine if the link is valid + $event = new ValidLinkEvent($tracker, $url); + + Event::dispatch($event); + + if ($event->valid) { + return $next($request); + } + } + + return redirect(config('mail-tracker.redirect-missing-links-to') ?: '/'); + } +} diff --git a/src/Model/SentEmail.php b/src/Model/SentEmail.php index 3f162ec..1c8f07e 100644 --- a/src/Model/SentEmail.php +++ b/src/Model/SentEmail.php @@ -17,7 +17,11 @@ * @property int $opens * @property int $clicks * @property int|null $message_id - * @property Collection $meta + * @property Collection|null $meta + * @property \Illuminate\Support\Carbon|null $created_at + * @property \Illuminate\Support\Carbon|null $updated_at + * @property \Illuminate\Support\Carbon|null $clicked_at + * @property \Illuminate\Support\Carbon|null $opened_at */ class SentEmail extends Model implements SentEmailModel { diff --git a/tests/MailTrackerControllerTest.php b/tests/MailTrackerControllerTest.php index 4b43972..b1b5a51 100644 --- a/tests/MailTrackerControllerTest.php +++ b/tests/MailTrackerControllerTest.php @@ -4,6 +4,7 @@ use Illuminate\Support\Facades\Config; use Illuminate\Support\Facades\Event; +use Illuminate\Support\Facades\URL; use Illuminate\Support\Str; use jdavidbakr\MailTracker\Events\ValidActionEvent; use jdavidbakr\MailTracker\MailTracker; @@ -87,9 +88,9 @@ public function testLinkTrackingIsSkipped() $redirect = 'http://' . Str::random(15) . '.com/' . Str::random(10) . '/' . Str::random(10) . '/' . rand(0, 100) . '/' . rand(0, 100) . '?page=' . rand(0, 100) . '&x=' . Str::random(32); - $this->get(route('mailTracker_l', [ - MailTracker::hash_url($redirect), // Replace slash with dollar sign - $email->hash, + $this->get(URL::signedRoute('mailTracker_n', [ + 'n' => $redirect, + 'h' => $email->hash, ])); $email->refresh(); @@ -115,9 +116,9 @@ public function testLinkTrackingIsNotSkipped() $redirect = 'http://' . Str::random(15) . '.com/' . Str::random(10) . '/' . Str::random(10) . '/' . rand(0, 100) . '/' . rand(0, 100) . '?page=' . rand(0, 100) . '&x=' . Str::random(32); - $this->get(route('mailTracker_l', [ - MailTracker::hash_url($redirect), // Replace slash with dollar sign - $email->hash, + $this->get(URL::signedRoute('mailTracker_n', [ + 'l' => $redirect, + 'h' => $email->hash, ])); $email->refresh(); diff --git a/tests/MailTrackerTest.php b/tests/MailTrackerTest.php index 4caffc0..0712b18 100644 --- a/tests/MailTrackerTest.php +++ b/tests/MailTrackerTest.php @@ -2,7 +2,6 @@ namespace jdavidbakr\MailTracker\Tests; -use Exception; use Faker\Factory; use Illuminate\Contracts\Debug\ExceptionHandler; use Illuminate\Database\Eloquent\Model; @@ -17,6 +16,7 @@ use Illuminate\Support\Facades\Event; use Illuminate\Support\Facades\Mail; use Illuminate\Support\Facades\Storage; +use Illuminate\Support\Facades\URL; use Illuminate\Support\Facades\View; use Illuminate\Support\Str; use jdavidbakr\MailTracker\Events\EmailSentEvent; @@ -32,7 +32,6 @@ use jdavidbakr\MailTracker\RecordTrackingJob; use Mockery; use Orchestra\Testbench\Exceptions\Handler; -use Symfony\Component\Mailer\Exception\TransportException; use Symfony\Component\Mime\Email; use Symfony\Component\Mime\Header\Headers; use Symfony\Component\Mime\Part\AbstractPart; @@ -43,9 +42,11 @@ class IgnoreExceptions extends Handler public function __construct() { } + public function report(Throwable $e) { } + public function render($request, Throwable $e) { throw $e; @@ -75,12 +76,12 @@ public function testSendMessage() Config::set('mail-tracker.track-links', 1); $old_email = MailTracker::sentEmailModel()->newQuery()->create([ - 'hash' => Str::random(32), - ]); - $old_url = MailTracker::sentEmailUrlClickedModel()->newQuery()->create([ - 'sent_email_id' => $old_email->id, - 'hash' => Str::random(32), - ]); + 'hash' => Str::random(32), + ]); + $old_url = MailTracker::sentEmailUrlClickedModel()->newQuery()->create([ + 'sent_email_id' => $old_email->id, + 'hash' => Str::random(32), + ]); // Go into the future to make sure that the old email gets removed \Carbon\Carbon::setTestNow(\Carbon\Carbon::now()->addWeek()); $str = Mockery::mock(Str::class); @@ -90,49 +91,47 @@ public function testSendMessage() ->andReturn('random-hash'); Event::fake([ - EmailSentEvent::class + EmailSentEvent::class, ]); - $faker = Factory::create(); - $email = $faker->email; + $faker = Factory::create(); + $email = $faker->email; $subject = $faker->sentence; - $name = $faker->firstName . ' ' .$faker->lastName; + $name = $faker->firstName . ' ' . $faker->lastName; View::addLocation(__DIR__); - try { - Mail::send('email.test', [], function ($message) use ($email, $subject, $name) { - $message->from('from@johndoe.com', 'From Name'); - $message->sender('sender@johndoe.com', 'Sender Name'); - $message->to($email, $name); + Mail::send('email.test', [], function ($message) use ($email, $subject, $name) { + $message->from('from@johndoe.com', 'From Name'); + $message->sender('sender@johndoe.com', 'Sender Name'); + + $message->to($email, $name); - $message->cc('cc@johndoe.com', 'CC Name'); - $message->bcc('bcc@johndoe.com', 'BCC Name'); + $message->cc('cc@johndoe.com', 'CC Name'); + $message->bcc('bcc@johndoe.com', 'BCC Name'); - $message->replyTo('reply-to@johndoe.com', 'Reply-To Name'); + $message->replyTo('reply-to@johndoe.com', 'Reply-To Name'); - $message->subject($subject); + $message->subject($subject); - $message->priority(3); - }); - } catch (TransportException $e) { - } + $message->priority(3); + }); Event::assertDispatched(EmailSentEvent::class); $this->assertDatabaseHas('sent_emails', [ - 'hash' => 'random-hash', - 'recipient_name' => $name, - 'recipient_email' => $email, - 'sender_name' => 'From Name', - 'sender_email' => 'from@johndoe.com', - 'subject' => $subject, - 'opened_at' => null, - 'clicked_at' => null, - ]); + 'hash' => 'random-hash', + 'recipient_name' => $name, + 'recipient_email' => $email, + 'sender_name' => 'From Name', + 'sender_email' => 'from@johndoe.com', + 'subject' => $subject, + 'opened_at' => null, + 'clicked_at' => null, + ]); $sent_email = MailTracker::sentEmailModel()->newQuery()->where([ 'hash' => 'random-hash', ])->first(); - $this->assertEquals($name.' <'.$email.'>', $sent_email->recipient); + $this->assertEquals($name . ' <' . $email . '>', $sent_email->recipient); $this->assertEquals('From Name ', $sent_email->sender); $this->assertNull($old_email->fresh()); $this->assertNull($old_url->fresh()); @@ -140,9 +139,9 @@ public function testSendMessage() public function testSendMessageWithMailRaw() { - $faker = Factory::create(); - $email = $faker->email; - $name = $faker->firstName . ' ' .$faker->lastName; + $faker = Factory::create(); + $email = $faker->email; + $name = $faker->firstName . ' ' . $faker->lastName; $content = 'Text to e-mail'; View::addLocation(__DIR__); $str = Mockery::mock(Str::class); @@ -151,22 +150,19 @@ public function testSendMessageWithMailRaw() ->once() ->andReturn('random-hash'); - try { - Mail::raw($content, function ($message) use ($email, $name) { - $message->from('from@johndoe.com', 'From Name'); + Mail::raw($content, function ($message) use ($email, $name) { + $message->from('from@johndoe.com', 'From Name'); - $message->to($email, $name); - }); - } catch (Exception $e) { - } + $message->to($email, $name); + }); $this->assertDatabaseHas('sent_emails', [ - 'hash' => 'random-hash', - 'sender_name' => 'From Name', - 'sender_email' => 'from@johndoe.com', - 'recipient_name' => $name, + 'hash' => 'random-hash', + 'sender_name' => 'From Name', + 'sender_email' => 'from@johndoe.com', + 'recipient_name' => $name, 'recipient_email' => $email, - 'content' => $content + 'content' => $content, ]); } @@ -174,7 +170,7 @@ public function testSendMessageWithMultiPart() { $faker = Factory::create(); $email = $faker->email; - $name = $faker->firstName . ' ' .$faker->lastName; + $name = $faker->firstName . ' ' . $faker->lastName; View::addLocation(__DIR__); $str = Mockery::mock(Str::class); app()->instance(Str::class, $str); @@ -184,14 +180,10 @@ public function testSendMessageWithMultiPart() $mailable = new TestMailable(); $mailable->subject('this is the message subject.'); - try { - Mail::to($email)->send($mailable); - } catch (Exception $e) { - // dd($e); - } + Mail::to($email)->send($mailable); $this->assertDatabaseHas('sent_emails', [ - 'hash' => 'random-hash', + 'hash' => 'random-hash', 'recipient_email' => $email, ]); } @@ -200,7 +192,7 @@ public function testSendMessageWithMixedPart() { $faker = Factory::create(); $email = $faker->email; - $name = $faker->firstName . ' ' .$faker->lastName; + $name = $faker->firstName . ' ' . $faker->lastName; View::addLocation(__DIR__); $str = Mockery::mock(Str::class); app()->instance(Str::class, $str); @@ -209,16 +201,12 @@ public function testSendMessageWithMixedPart() ->andReturn('random-hash'); $mailable = new TestMailable(); $mailable->subject('this is the message subject.'); - $mailable->attach(__DIR__.'/email/test.blade.php'); + $mailable->attach(__DIR__ . '/email/test.blade.php'); - try { - Mail::to($email)->send($mailable); - } catch (Exception $e) { - // dd($e); - } + Mail::to($email)->send($mailable); $this->assertDatabaseHas('sent_emails', [ - 'hash' => 'random-hash', + 'hash' => 'random-hash', 'recipient_email' => $email, ]); } @@ -227,7 +215,7 @@ public function testSendMessageWithRelatedPart() { $faker = Factory::create(); $email = $faker->email; - $name = $faker->firstName . ' ' .$faker->lastName; + $name = $faker->firstName . ' ' . $faker->lastName; View::addLocation(__DIR__); $str = Mockery::mock(Str::class); app()->instance(Str::class, $str); @@ -235,28 +223,24 @@ public function testSendMessageWithRelatedPart() ->once() ->andReturn('random-hash'); - try { - Mail::send('email.embed-test', ['imagePath' => __DIR__ . '/email/example.png'], function ($message) use ($email, $name) { - $message->from('from@johndoe.com', 'From Name'); - $message->sender('sender@johndoe.com', 'Sender Name'); - - $message->to($email, $name); + Mail::send('email.embed-test', ['imagePath' => __DIR__ . '/email/example.png'], function ($message) use ($email, $name) { + $message->from('from@johndoe.com', 'From Name'); + $message->sender('sender@johndoe.com', 'Sender Name'); - $message->cc('cc@johndoe.com', 'CC Name'); - $message->bcc('bcc@johndoe.com', 'BCC Name'); + $message->to($email, $name); - $message->replyTo('reply-to@johndoe.com', 'Reply-To Name'); + $message->cc('cc@johndoe.com', 'CC Name'); + $message->bcc('bcc@johndoe.com', 'BCC Name'); - $message->subject('This is the test subject'); + $message->replyTo('reply-to@johndoe.com', 'Reply-To Name'); - $message->priority(3); - }); - } catch (TransportException $e) { + $message->subject('This is the test subject'); - } + $message->priority(3); + }); $this->assertDatabaseHas('sent_emails', [ - 'hash' => 'random-hash', + 'hash' => 'random-hash', 'recipient_email' => $email, ]); } @@ -274,19 +258,15 @@ public function testSendMessageWithAttachment() $mailable = new TestMailable(); $mailable->subject('this is the message subject.'); - $mailable->attach(__DIR__.'/email/example.pdf', [ - 'as' => 'invoice.pdf', + $mailable->attach(__DIR__ . '/email/example.pdf', [ + 'as' => 'invoice.pdf', 'mime' => 'application/pdf', ]); - try { - Mail::to($email)->send($mailable); - } catch (Exception $e) { - // dd($e); - } + Mail::to($email)->send($mailable); $this->assertDatabaseHas('sent_emails', [ - 'hash' => 'random-hash', + 'hash' => 'random-hash', 'recipient_email' => $email, ]); } @@ -296,47 +276,45 @@ public function testSendMessageWithAttachment() */ public function it_doesnt_track_if_told_not_to() { - $faker = Factory::create(); - $email = $faker->email; + $faker = Factory::create(); + $email = $faker->email; $anotherEmail = $faker->email; - $subject = $faker->sentence; - $name = $faker->firstName . ' ' .$faker->lastName; + $subject = $faker->sentence; + $name = $faker->firstName . ' ' . $faker->lastName; View::addLocation(__DIR__); - try { - Mail::send('email.test', [], function ($message) use ($email, $anotherEmail, $subject, $name) { - $message->from('from@johndoe.com', 'From Name'); - $message->sender('sender@johndoe.com', 'Sender Name'); - $message->to($email, $name); - $message->to($anotherEmail, $name); + Mail::send('email.test', [], function ($message) use ($email, $anotherEmail, $subject, $name) { + $message->from('from@johndoe.com', 'From Name'); + $message->sender('sender@johndoe.com', 'Sender Name'); + + $message->to($email, $name); + $message->to($anotherEmail, $name); - $message->cc('cc@johndoe.com', 'CC Name'); - $message->bcc('bcc@johndoe.com', 'BCC Name'); + $message->cc('cc@johndoe.com', 'CC Name'); + $message->bcc('bcc@johndoe.com', 'BCC Name'); - $message->replyTo('reply-to@johndoe.com', 'Reply-To Name'); + $message->replyTo('reply-to@johndoe.com', 'Reply-To Name'); - $message->subject($subject); + $message->subject($subject); - $message->priority(3); + $message->priority(3); - $message->getHeaders()->addTextHeader('X-No-Track', Str::random(10)); - }); - } catch (TransportException $e) { - } + $message->getHeaders()->addTextHeader('X-No-Track', Str::random(10)); + }); $this->assertDatabaseMissing('sent_emails', [ - 'subject' => $subject, - 'sender_name' => 'From Name', - 'sender_email' => 'from@johndoe.com', - 'recipient_name' => $name, + 'subject' => $subject, + 'sender_name' => 'From Name', + 'sender_email' => 'from@johndoe.com', + 'recipient_name' => $name, 'recipient_email' => $email, ]); $this->assertDatabaseMissing('sent_emails', [ - 'subject' => $subject, - 'sender_name' => 'From Name', - 'sender_email' => 'from@johndoe.com', - 'recipient_name' => $name, + 'subject' => $subject, + 'sender_name' => 'From Name', + 'sender_email' => 'from@johndoe.com', + 'recipient_name' => $name, 'recipient_email' => $anotherEmail, ]); } @@ -349,23 +327,24 @@ public function testPing() Carbon::setTestNow(now()); Config::set('mail-tracker.tracker-queue', 'alt-queue'); Bus::fake(); + $track = MailTracker::sentEmailModel()->newQuery()->create([ - 'hash' => Str::random(32), - ]); - $pings = $track->opens; - $pings++; + 'hash' => Str::random(32), + ]); + $url = route('mailTracker_t', [$track->hash]); - $response = $this->get($url); + $this->get($url) + ->assertSuccessful(); - $response->assertSuccessful(); Bus::assertDispatched(RecordTrackingJob::class, function ($e) use ($track) { return $e->sentEmail->id == $track->id && $e->ipAddress == '127.0.0.1' && $e->queue == 'alt-queue'; }); + $this->assertDatabaseHas('sent_emails', [ - 'id' => $track->id, + 'id' => $track->id, 'opened_at' => now()->format("Y-m-d H:i:s"), ]); } @@ -379,11 +358,10 @@ public function it_leaves_existing_opened_at_value() Config::set('mail-tracker.tracker-queue', 'alt-queue'); Bus::fake(); $track = MailTracker::sentEmailModel()->newQuery()->create([ - 'hash' => Str::random(32), - 'opened_at' => now()->subDays(10), - ]); - $pings = $track->opens; - $pings++; + 'hash' => Str::random(32), + 'opened_at' => now()->subDays(10), + ]); + $url = route('mailTracker_t', [$track->hash]); $response = $this->get($url); @@ -395,58 +373,26 @@ public function it_leaves_existing_opened_at_value() $e->queue == 'alt-queue'; }); $this->assertDatabaseHas('sent_emails', [ - 'id' => $track->id, + 'id' => $track->id, 'opened_at' => $track->opened_at, ]); } - public function testLegacyLink() - { - Carbon::setTestNow(now()); - Config::set('mail-tracker.tracker-queue', 'alt-queue'); - Bus::fake(); - $redirect = 'http://'.Str::random(15).'.com/'.Str::random(10).'/'.Str::random(10).'/'.rand(0, 100).'/'.rand(0, 100).'?page='.rand(0, 100).'&x='.Str::random(32); - - $track = MailTracker::sentEmailModel()->newQuery()->create([ - 'hash' => Str::random(32), - 'content' => 'Hello, visit my website '.$redirect.'', - ]); - $clicks = $track->clicks; - $clicks++; - $url = route('mailTracker_l', [ - MailTracker::hash_url($redirect), // Replace slash with dollar sign - $track->hash - ]); - $response = $this->get($url); - - $response->assertRedirect($redirect); - Bus::assertDispatched(RecordLinkClickJob::class, function ($job) use ($track, $redirect) { - return $job->sentEmail->id == $track->id && - $job->url == $redirect && - $job->ipAddress == '127.0.0.1' && - $job->queue == 'alt-queue'; - }); - $this->assertDatabaseHas('sent_emails', [ - 'id' => $track->id, - 'clicked_at' => now()->format("Y-m-d H:i:s"), - ]); - } - public function testLink() { Carbon::setTestNow(now()); Config::set('mail-tracker.inject-pixel', true); Config::set('mail-tracker.tracker-queue', 'alt-queue'); Bus::fake(); - $redirect = 'http://'.Str::random(15).'.com/'.Str::random(10).'/'.Str::random(10).'/'.rand(0, 100).'/'.rand(0, 100).'?page='.rand(0, 100).'&x='.Str::random(32); - $track = MailTracker::sentEmailModel()->newQuery()->create([ - 'hash' => Str::random(32), - 'content' => 'Hello, visit my website '.$redirect.'', - ]); - $url = route('mailTracker_n', [ - 'l' => $redirect, - 'h' => $track->hash - ]); + $redirect = 'http://' . Str::random(15) . '.com/' . Str::random(10) . '/' . Str::random(10) . '/' . rand(0, 100) . '/' . rand(0, 100) . '?page=' . rand(0, 100) . '&x=' . Str::random(32); + $track = MailTracker::sentEmailModel()->newQuery()->create([ + 'hash' => Str::random(32), + 'content' => 'Hello, visit my website ' . $redirect . '', + ]); + $url = URL::signedRoute('mailTracker_n', [ + 'l' => $redirect, + 'h' => $track->hash, + ]); $response = $this->get($url); @@ -458,9 +404,9 @@ public function testLink() $job->queue == 'alt-queue'; }); $this->assertDatabaseHas('sent_emails', [ - 'id' => $track->id, + 'id' => $track->id, 'clicked_at' => now()->format("Y-m-d H:i:s"), - 'opened_at' => now()->format("Y-m-d H:i:s"), + 'opened_at' => now()->format("Y-m-d H:i:s"), ]); } @@ -469,81 +415,68 @@ public function testLink() */ public function it_redirects_to_fallback_if_the_sent_email_does_not_exists() { - $track = MailTracker::sentEmailModel()->newQuery()->create([ - 'hash' => Str::random(32), - ]); - - $clicks = $track->clicks; - $clicks++; + MailTracker::sentEmailModel()->newQuery()->create([ + 'hash' => Str::random(32), + ]); Config::set('mail-tracker.redirect-missing-links-to', '/home'); - $redirect = 'http://'.Str::random(15).'.com/'.Str::random(10).'/'.Str::random(10).'/'.rand(0, 100).'/'.rand(0, 100).'?page='.rand(0, 100).'&x='.Str::random(32); + $redirect = 'http://' . Str::random(15) . '.com/' . Str::random(10) . '/' . Str::random(10) . '/' . rand(0, 100) . '/' . rand(0, 100) . '?page=' . rand(0, 100) . '&x=' . Str::random(32); - // Do it with an invalid hash - $url = route('mailTracker_n', [ - 'l' => $redirect, - 'h' => 'bad-hash' - ]); - $response = $this->get($url); + // Do it with an invalid hash and an unsigned route + $url = URL::route('mailTracker_n', [ + 'l' => $redirect, + 'h' => 'bad-hash', + ]); - $response->assertRedirect('/home'); + $this->get($url) + ->assertRedirect('/home'); } - /** * @test */ - public function it_redirects_to_fallback_for_invalid_domain() + public function it_redirects_to_valid_domain_based_on_email_content() { - Event::fake(); $track = MailTracker::sentEmailModel()->newQuery()->create([ - 'hash' => Str::random(32), - 'content' => 'This is some content with a link to Good website', + 'hash' => Str::random(32), + 'content' => 'This is some content with a link to Good website', ]); Config::set('mail-tracker.redirect-missing-links-to', '/home'); - $invalidUrl = 'http://evil.com'; // Domain not present in email content - - $response = $this->get(route('mailTracker_l', [MailTracker::hash_url($invalidUrl), $track->hash])); - - $response->assertRedirect('/home'); + // Use a NON signed route to test the fallback event + $this->get(URL::route('mailTracker_n', ['l' => 'https://goodwebsite.com/test.html', 'h' => $track->hash])) + ->assertRedirect('https://goodwebsite.com/test.html'); } /** * @test */ - public function it_redirects_to_config_page_if_no_url_in_request() + public function it_redirects_to_fallback_for_invalid_domain() { - Config::set('mail-tracker.redirect-missing-links-to', '/home'); - - $url = route('mailTracker_n'); - $response = $this->get($url); + $track = MailTracker::sentEmailModel()->newQuery()->create([ + 'hash' => Str::random(32), + 'content' => 'This is some content with a link to Good website', + ]); - $response->assertRedirect('/home'); - } + Config::set('mail-tracker.redirect-missing-links-to', '/home'); - /** - * @test - */ - public function it_redirects_to_home_page_if_no_url_in_request() - { - $url = route('mailTracker_n'); - $response = $this->get($url); + $invalidUrl = 'http://evil.com'; // Domain not present in email content - $response->assertRedirect('/'); + $this->get(URL::route('mailTracker_n', ['l' => $invalidUrl, 'h' => $track->hash])) + ->assertRedirect('/home'); } /** * @test */ - public function random_string_in_link_does_not_crash(Type $var = null) + public function random_string_in_link_does_not_crash() { $this->disableExceptionHandling(); $this->expectException(BadUrlLink::class); - $url = route('mailTracker_l', [ - Str::random(32), - 'the-mail-hash', + $url = URL::signedRoute('mailTracker_n', [ + 'l' => Str::random(32), + 'h' => 'the-mail-hash', ]); $this->get($url); @@ -552,119 +485,119 @@ public function random_string_in_link_does_not_crash(Type $var = null) /** * @test */ - public function it_retrieves_the_mesage_id_from_laravel_mailer() + public function it_retrieves_the_message_id_from_laravel_mailer() { - $sent = MailTracker::sentEmailModel()->newQuery()->create([ - 'hash'=>'the-hash', - 'message_id'=>'to-be-replaced', + $sent = MailTracker::sentEmailModel()->newQuery()->create([ + 'hash' => 'the-hash', + 'message_id' => 'to-be-replaced', ]); $headers = new Headers; $headers->addHeader('X-Mailer-Hash', $sent->hash); - $sendingEvent = Mockery::mock(MessageSending::class); + $sendingEvent = Mockery::mock(MessageSending::class); $sendingEvent->message = Mockery::mock(Email::class, [ - 'getTo' => [ - Mockery::mock([ - 'getAddress'=>'destination@example.com', - 'getName'=>'Destination Person' - ]) - ], - 'getFrom' => [ - Mockery::mock([ - 'getAddress'=>'from@example.com', - 'getName'=>'From Name' - ]) - ], - 'getHeaders' => $headers, - 'getSubject' => 'The message subject', - 'getBody' => Mockery::mock(AbstractPart::class, [ - 'getBody'=>'The body', - 'getMediaType'=>'text', - 'getMediaSubtype'=>'html', + 'getTo' => [ + Mockery::mock([ + 'getAddress' => 'destination@example.com', + 'getName' => 'Destination Person', ]), - 'setBody' => Mockery::Mock(Email::class), - 'getChildren' => [], - 'getId' => 'message-id', - 'getHtmlCharset' => 'utf-8', - ]); - $sentEvent = Mockery::mock(MessageSent::class); - $sentEvent->sent = Mockery::mock(SentMessage::class, [ - 'getOriginalMessage'=>Mockery::mock([ - 'getHeaders'=>$headers + ], + 'getFrom' => [ + Mockery::mock([ + 'getAddress' => 'from@example.com', + 'getName' => 'From Name', + ]), + ], + 'getHeaders' => $headers, + 'getSubject' => 'The message subject', + 'getBody' => Mockery::mock(AbstractPart::class, [ + 'getBody' => 'The body', + 'getMediaType' => 'text', + 'getMediaSubtype' => 'html', + ]), + 'setBody' => Mockery::Mock(Email::class), + 'getChildren' => [], + 'getId' => 'message-id', + 'getHtmlCharset' => 'utf-8', + ]); + $sentEvent = Mockery::mock(MessageSent::class); + $sentEvent->sent = Mockery::mock(SentMessage::class, [ + 'getOriginalMessage' => Mockery::mock([ + 'getHeaders' => $headers, ]), - 'getMessageId'=>'native-id', + 'getMessageId' => 'native-id', ]); - $tracker = new MailTracker(); + $tracker = new MailTracker(); $tracker->messageSending($sendingEvent); $tracker->messageSent($sentEvent); $this->assertDatabaseHas('sent_emails', [ - 'id'=>$sent->id, - 'message_id'=>'native-id' + 'id' => $sent->id, + 'message_id' => 'native-id', ]); } /** * @test */ - public function it_retrieves_the_mesage_id_from_ses_mail_default() + public function it_retrieves_the_message_id_from_ses_mail_default() { Config::set('mail.default', 'ses'); Config::set('mail.driver', null); - $sent = MailTracker::sentEmailModel()->newQuery()->create([ - 'hash'=>'the-hash', - 'message_id'=>'to-be-replaced', + $sent = MailTracker::sentEmailModel()->newQuery()->create([ + 'hash' => 'the-hash', + 'message_id' => 'to-be-replaced', ]); $headers = new Headers; $headers->addHeader('X-Mailer-Hash', $sent->hash); $headers->addHeader('X-SES-Message-ID', 'aws-mailer-hash'); - $sendingEvent = Mockery::mock(MessageSending::class); + $sendingEvent = Mockery::mock(MessageSending::class); $sendingEvent->message = Mockery::mock(Email::class, [ - 'getTo' => [ - Mockery::mock([ - 'getAddress'=>'destination@example.com', - 'getName'=>'Destination Person' - ]) - ], - 'getFrom' => [ - Mockery::mock([ - 'getAddress'=>'from@example.com', - 'getName'=>'From Name' - ]) - ], - 'getHeaders' => $headers, - 'getSubject' => 'The message subject', - 'getBody' => Mockery::mock(AbstractPart::class, 'content', [ - 'getBody'=>'The body', - 'getMediaType'=>'text', - 'getMediaSubtype'=>'html', + 'getTo' => [ + Mockery::mock([ + 'getAddress' => 'destination@example.com', + 'getName' => 'Destination Person', + ]), + ], + 'getFrom' => [ + Mockery::mock([ + 'getAddress' => 'from@example.com', + 'getName' => 'From Name', ]), - 'setBody' => Mockery::mock(Email::class), - 'getChildren' => [], - 'getId' => 'message-id', - 'getHtmlCharset' => 'utf-8', - ]); - $sentEvent = Mockery::mock(MessageSent::class); - $sentEvent->sent = Mockery::mock(SentMessage::class, [ - 'getOriginalMessage'=>Mockery::mock([ - 'getHeaders'=>$headers + ], + 'getHeaders' => $headers, + 'getSubject' => 'The message subject', + 'getBody' => Mockery::mock(AbstractPart::class, 'content', [ + 'getBody' => 'The body', + 'getMediaType' => 'text', + 'getMediaSubtype' => 'html', ]), + 'setBody' => Mockery::mock(Email::class), + 'getChildren' => [], + 'getId' => 'message-id', + 'getHtmlCharset' => 'utf-8', ]); - $tracker = new MailTracker(); + $sentEvent = Mockery::mock(MessageSent::class); + $sentEvent->sent = Mockery::mock(SentMessage::class, [ + 'getOriginalMessage' => Mockery::mock([ + 'getHeaders' => $headers, + ]), + ]); + $tracker = new MailTracker(); $tracker->messageSending($sendingEvent); $tracker->messageSent($sentEvent); $this->assertDatabaseHas('sent_emails', [ - 'id'=>$sent->id, - 'message_id'=>'aws-mailer-hash' + 'id' => $sent->id, + 'message_id' => 'aws-mailer-hash', ]); } /** * @test */ - public function it_retrieves_the_mesage_id_from_ses_mail_driver() + public function it_retrieves_the_message_id_from_ses_mail_driver() { $str = Mockery::mock(Str::class); app()->instance(Str::class, $str); @@ -673,53 +606,53 @@ public function it_retrieves_the_mesage_id_from_ses_mail_driver() ->andReturn('random-hash'); Config::set('mail.driver', 'ses'); Config::set('mail.default', null); - $sent = MailTracker::sentEmailModel()->newQuery()->create([ - 'hash'=>'the-hash', - 'message_id'=>'to-be-replaced', + $sent = MailTracker::sentEmailModel()->newQuery()->create([ + 'hash' => 'the-hash', + 'message_id' => 'to-be-replaced', ]); $headers = new Headers; $headers->addHeader('X-Mailer-Hash', $sent->hash); $headers->addHeader('X-SES-Message-ID', 'aws-mailer-hash'); - $sendingEvent = Mockery::mock(MessageSending::class); + $sendingEvent = Mockery::mock(MessageSending::class); $sendingEvent->message = Mockery::mock(Email::class, [ - 'getTo' => [ - Mockery::mock([ - 'getAddress'=>'destination@example.com', - 'getName'=>'Destination Person' - ]) - ], - 'getFrom' => [ - Mockery::mock([ - 'getAddress'=>'from@example.com', - 'getName'=>'From Name' - ]) - ], - 'getHeaders' => $headers, - 'getSubject' => 'The message subject', - 'getBody' => Mockery::mock(AbstractPart::class, 'content', [ - 'getBody'=>'The body', - 'getMediaType'=>'text', - 'getMediaSubtype'=>'html', + 'getTo' => [ + Mockery::mock([ + 'getAddress' => 'destination@example.com', + 'getName' => 'Destination Person', ]), - 'setBody' => Mockery::mock(Email::class), - 'getChildren' => [], - 'getId' => 'message-id', - 'getHtmlCharset' => 'utf-8', - ]); - $sentEvent = Mockery::mock(MessageSent::class); - $sentEvent->sent = Mockery::mock(SentMessage::class, [ - 'getOriginalMessage'=>Mockery::mock([ - 'getHeaders'=>$headers + ], + 'getFrom' => [ + Mockery::mock([ + 'getAddress' => 'from@example.com', + 'getName' => 'From Name', + ]), + ], + 'getHeaders' => $headers, + 'getSubject' => 'The message subject', + 'getBody' => Mockery::mock(AbstractPart::class, 'content', [ + 'getBody' => 'The body', + 'getMediaType' => 'text', + 'getMediaSubtype' => 'html', ]), + 'setBody' => Mockery::mock(Email::class), + 'getChildren' => [], + 'getId' => 'message-id', + 'getHtmlCharset' => 'utf-8', ]); - $tracker = new MailTracker(); + $sentEvent = Mockery::mock(MessageSent::class); + $sentEvent->sent = Mockery::mock(SentMessage::class, [ + 'getOriginalMessage' => Mockery::mock([ + 'getHeaders' => $headers, + ]), + ]); + $tracker = new MailTracker(); $tracker->messageSending($sendingEvent); $tracker->messageSent($sentEvent); $this->assertDatabaseHas('sent_emails', [ - 'id'=>$sent->id, - 'message_id'=>'aws-mailer-hash' + 'id' => $sent->id, + 'message_id' => 'aws-mailer-hash', ]); } @@ -732,23 +665,23 @@ public function it_retrieves_the_mesage_id_from_ses_mail_driver() */ public function it_confirms_a_subscription() { - $url = action('\jdavidbakr\MailTracker\SNSController@callback'); + $url = action('\jdavidbakr\MailTracker\SNSController@callback'); $response = $this->post($url, [ - 'message' => json_encode([ - // Required - 'Message' => 'test subscription message', - 'MessageId' => Str::random(10), - 'Timestamp' => \Carbon\Carbon::now()->timestamp, - 'TopicArn' => Str::random(10), - 'Type' => 'SubscriptionConfirmation', - 'Signature' => Str::random(32), - 'SigningCertURL' => Str::random(32), - 'SignatureVersion' => 1, - // Request-specific - 'SubscribeURL' => 'http://google.com', - 'Token' => Str::random(10), - ]) - ]); + 'message' => json_encode([ + // Required + 'Message' => 'test subscription message', + 'MessageId' => Str::random(10), + 'Timestamp' => \Carbon\Carbon::now()->timestamp, + 'TopicArn' => Str::random(10), + 'Type' => 'SubscriptionConfirmation', + 'Signature' => Str::random(32), + 'SigningCertURL' => Str::random(32), + 'SignatureVersion' => 1, + // Request-specific + 'SubscribeURL' => 'http://google.com', + 'Token' => Str::random(10), + ]), + ]); $response->assertSee('subscription confirmed'); } @@ -759,23 +692,23 @@ public function it_processes_with_registered_topic() { $topic = Str::random(32); Config::set('mail-tracker.sns-topic', $topic); - $url = action('\jdavidbakr\MailTracker\SNSController@callback'); + $url = action('\jdavidbakr\MailTracker\SNSController@callback'); $response = $this->post($url, [ - 'message' => json_encode([ - // Required - 'Message' => 'test subscription message', - 'MessageId' => Str::random(10), - 'Timestamp' => \Carbon\Carbon::now()->timestamp, - 'TopicArn' => $topic, - 'Type' => 'SubscriptionConfirmation', - 'Signature' => Str::random(32), - 'SigningCertURL' => Str::random(32), - 'SignatureVersion' => 1, - // Request-specific - 'SubscribeURL' => 'http://google.com', - 'Token' => Str::random(10), - ]) - ]); + 'message' => json_encode([ + // Required + 'Message' => 'test subscription message', + 'MessageId' => Str::random(10), + 'Timestamp' => \Carbon\Carbon::now()->timestamp, + 'TopicArn' => $topic, + 'Type' => 'SubscriptionConfirmation', + 'Signature' => Str::random(32), + 'SigningCertURL' => Str::random(32), + 'SignatureVersion' => 1, + // Request-specific + 'SubscribeURL' => 'http://google.com', + 'Token' => Str::random(10), + ]), + ]); $response->assertSee('subscription confirmed'); } @@ -786,23 +719,23 @@ public function it_ignores_invalid_topic() { $topic = Str::random(32); Config::set('mail-tracker.sns-topic', $topic); - $url = action('\jdavidbakr\MailTracker\SNSController@callback'); + $url = action('\jdavidbakr\MailTracker\SNSController@callback'); $response = $this->post($url, [ - 'message' => json_encode([ - // Required - 'Message' => 'test subscription message', - 'MessageId' => Str::random(10), - 'Timestamp' => \Carbon\Carbon::now()->timestamp, - 'TopicArn' => Str::random(32), - 'Type' => 'SubscriptionConfirmation', - 'Signature' => Str::random(32), - 'SigningCertURL' => Str::random(32), - 'SignatureVersion' => 1, - // Request-specific - 'SubscribeURL' => 'http://google.com', - 'Token' => Str::random(10), - ]) - ]); + 'message' => json_encode([ + // Required + 'Message' => 'test subscription message', + 'MessageId' => Str::random(10), + 'Timestamp' => \Carbon\Carbon::now()->timestamp, + 'TopicArn' => Str::random(32), + 'Type' => 'SubscriptionConfirmation', + 'Signature' => Str::random(32), + 'SigningCertURL' => Str::random(32), + 'SignatureVersion' => 1, + // Request-specific + 'SubscribeURL' => 'http://google.com', + 'Token' => Str::random(10), + ]), + ]); $response->assertSee('invalid topic ARN'); } @@ -818,17 +751,17 @@ public function it_processes_a_delivery() ]; $response = $this->post(action('\jdavidbakr\MailTracker\SNSController@callback'), [ - 'message' => json_encode([ - 'Message' => json_encode($message), - 'MessageId' => Str::uuid(), - 'Timestamp' => Carbon::now()->timestamp, - 'TopicArn' => Str::uuid(), - 'Type' => 'Notification', - 'Signature' => Str::uuid(), - 'SigningCertURL' => Str::uuid(), - 'SignatureVersion' => Str::uuid(), - ]) - ]); + 'message' => json_encode([ + 'Message' => json_encode($message), + 'MessageId' => Str::uuid(), + 'Timestamp' => Carbon::now()->timestamp, + 'TopicArn' => Str::uuid(), + 'Type' => 'Notification', + 'Signature' => Str::uuid(), + 'SigningCertURL' => Str::uuid(), + 'SignatureVersion' => Str::uuid(), + ]), + ]); $response->assertSee('notification processed'); Bus::assertDispatched(RecordDeliveryJob::class, function ($job) use ($message) { @@ -849,17 +782,17 @@ public function it_processes_a_bounce() ]; $response = $this->post(action('\jdavidbakr\MailTracker\SNSController@callback'), [ - 'message' => json_encode([ - 'Message' => json_encode($message), - 'MessageId' => Str::uuid(), - 'Timestamp' => Carbon::now()->timestamp, - 'TopicArn' => Str::uuid(), - 'Type' => 'Notification', - 'Signature' => Str::uuid(), - 'SigningCertURL' => Str::uuid(), - 'SignatureVersion' => Str::uuid(), - ]) - ]); + 'message' => json_encode([ + 'Message' => json_encode($message), + 'MessageId' => Str::uuid(), + 'Timestamp' => Carbon::now()->timestamp, + 'TopicArn' => Str::uuid(), + 'Type' => 'Notification', + 'Signature' => Str::uuid(), + 'SigningCertURL' => Str::uuid(), + 'SignatureVersion' => Str::uuid(), + ]), + ]); $response->assertSee('notification processed'); Bus::assertDispatched(RecordBounceJob::class, function ($job) use ($message) { @@ -880,17 +813,17 @@ public function it_processes_a_complaint() ]; $response = $this->post(action('\jdavidbakr\MailTracker\SNSController@callback'), [ - 'message' => json_encode([ - 'Message' => json_encode($message), - 'MessageId' => Str::uuid(), - 'Timestamp' => Carbon::now()->timestamp, - 'TopicArn' => Str::uuid(), - 'Type' => 'Notification', - 'Signature' => Str::uuid(), - 'SigningCertURL' => Str::uuid(), - 'SignatureVersion' => Str::uuid(), - ]) - ]); + 'message' => json_encode([ + 'Message' => json_encode($message), + 'MessageId' => Str::uuid(), + 'Timestamp' => Carbon::now()->timestamp, + 'TopicArn' => Str::uuid(), + 'Type' => 'Notification', + 'Signature' => Str::uuid(), + 'SigningCertURL' => Str::uuid(), + 'SignatureVersion' => Str::uuid(), + ]), + ]); $response->assertSee('notification processed'); Bus::assertDispatched(RecordComplaintJob::class, function ($job) use ($message) { @@ -910,10 +843,10 @@ public function it_handles_ampersands_in_links() Config::set('mail.driver', 'array'); (new MailServiceProvider(app()))->register(); - $faker = Factory::create(); - $email = $faker->email; + $faker = Factory::create(); + $email = $faker->email; $subject = $faker->sentence; - $name = $faker->firstName . ' ' .$faker->lastName; + $name = $faker->firstName . ' ' . $faker->lastName; View::addLocation(__DIR__); Mail::send('email.testAmpersand', [], function ($message) use ($email, $subject, $name) { @@ -934,7 +867,7 @@ public function it_handles_ampersands_in_links() $driver = app('mailer')->getSymfonyTransport(); $this->assertEquals(1, count($driver->messages())); - $mes = $driver->messages()[0]; + $mes = $driver->messages()[0]; $body = $mes->getOriginalMessage()->getBody()->getBody(); $hash = $mes->getOriginalMessage()->getHeaders()->get('X-Mailer-Hash')->getValue(); @@ -953,7 +886,7 @@ public function it_handles_ampersands_in_links() Event::assertDispatched(LinkClickedEvent::class); $this->assertDatabaseHas('sent_emails_url_clicked', [ - 'url' => $expected_url, + 'url' => $expected_url, 'clicks' => 1, ]); @@ -973,10 +906,10 @@ public function it_handles_apostrophes_in_links() Config::set('mail.driver', 'array'); (new MailServiceProvider(app()))->register(); - $faker = Factory::create(); - $email = $faker->email; + $faker = Factory::create(); + $email = $faker->email; $subject = $faker->sentence; - $name = $faker->firstName . ' ' . $faker->lastName; + $name = $faker->firstName . ' ' . $faker->lastName; View::addLocation(__DIR__); Mail::send('email.testApostrophe', [], function ($message) use ($email, $subject, $name) { @@ -992,7 +925,7 @@ public function it_handles_apostrophes_in_links() $driver = app('mailer')->getSymfonyTransport(); $this->assertEquals(1, count($driver->messages())); - $mes = $driver->messages()[0]; + $mes = $driver->messages()[0]; $body = $mes->getOriginalMessage()->getBody()->getBody(); $hash = $mes->getOriginalMessage()->getHeaders()->get('X-Mailer-Hash')->getValue(); @@ -1011,7 +944,7 @@ public function it_handles_apostrophes_in_links() Event::assertDispatched(LinkClickedEvent::class); $this->assertDatabaseHas('sent_emails_url_clicked', [ - 'url' => $expected_url, + 'url' => $expected_url, 'clicks' => 1, ]); @@ -1026,32 +959,30 @@ public function it_handles_apostrophes_in_links() */ public function it_retrieves_header_data() { - $faker = Factory::create(); - $email = $faker->email; - $subject = $faker->sentence; - $name = $faker->firstName . ' ' .$faker->lastName; + $faker = Factory::create(); + $email = $faker->email; + $subject = $faker->sentence; + $name = $faker->firstName . ' ' . $faker->lastName; $header_test = Str::random(10); \View::addLocation(__DIR__); - try { - \Mail::send('email.test', [], function ($message) use ($email, $subject, $name, $header_test) { - $message->from('from@johndoe.com', 'From Name'); - $message->sender('sender@johndoe.com', 'Sender Name'); - $message->to($email, $name); + \Mail::send('email.test', [], function ($message) use ($email, $subject, $name, $header_test) { + $message->from('from@johndoe.com', 'From Name'); + $message->sender('sender@johndoe.com', 'Sender Name'); + + $message->to($email, $name); - $message->cc('cc@johndoe.com', 'CC Name'); - $message->bcc('bcc@johndoe.com', 'BCC Name'); + $message->cc('cc@johndoe.com', 'CC Name'); + $message->bcc('bcc@johndoe.com', 'BCC Name'); - $message->replyTo('reply-to@johndoe.com', 'Reply-To Name'); + $message->replyTo('reply-to@johndoe.com', 'Reply-To Name'); - $message->subject($subject); + $message->subject($subject); - $message->priority(3); + $message->priority(3); - $message->getHeaders()->addTextHeader('X-Header-Test', $header_test); - }); - } catch (TransportException $e) { - } + $message->getHeaders()->addTextHeader('X-Header-Test', $header_test); + }); $track = MailTracker::sentEmailModel()->newQuery()->orderBy('id', 'desc')->first(); $this->assertEquals($header_test, $track->getHeader('X-Header-Test')); @@ -1062,32 +993,30 @@ public function it_retrieves_header_data() */ public function it_retrieves_long_header_data() { - $faker = Factory::create(); - $email = $faker->email; - $subject = $faker->sentence; - $name = $faker->firstName . ' ' .$faker->lastName; - $header_test = Str::random(100) .', ' . Str::random(100) .', '. Str::random(100); + $faker = Factory::create(); + $email = $faker->email; + $subject = $faker->sentence; + $name = $faker->firstName . ' ' . $faker->lastName; + $header_test = Str::random(100) . ', ' . Str::random(100) . ', ' . Str::random(100); View::addLocation(__DIR__); - try { - Mail::send('email.test', [], function ($message) use ($email, $subject, $name, $header_test) { - $message->from('from@johndoe.com', 'From Name'); - $message->sender('sender@johndoe.com', 'Sender Name'); - $message->to($email, $name); + Mail::send('email.test', [], function ($message) use ($email, $subject, $name, $header_test) { + $message->from('from@johndoe.com', 'From Name'); + $message->sender('sender@johndoe.com', 'Sender Name'); + + $message->to($email, $name); - $message->cc('cc@johndoe.com', 'CC Name'); - $message->bcc('bcc@johndoe.com', 'BCC Name'); + $message->cc('cc@johndoe.com', 'CC Name'); + $message->bcc('bcc@johndoe.com', 'BCC Name'); - $message->replyTo('reply-to@johndoe.com', 'Reply-To Name'); + $message->replyTo('reply-to@johndoe.com', 'Reply-To Name'); - $message->subject($subject); + $message->subject($subject); - $message->priority(3); + $message->priority(3); - $message->getHeaders()->addTextHeader('X-Header-Test', $header_test); - }); - } catch (TransportException $e) { - } + $message->getHeaders()->addTextHeader('X-Header-Test', $header_test); + }); $track = MailTracker::sentEmailModel()->newQuery()->orderBy('id', 'desc')->first(); $this->assertEquals($header_test, $track->getHeader('X-Header-Test')); @@ -1098,37 +1027,35 @@ public function it_retrieves_long_header_data() */ public function it_retrieves_multiple_cc_recipients_from_header_data() { - $faker = Factory::create(); - $email = $faker->email; + $faker = Factory::create(); + $email = $faker->email; $subject = $faker->sentence; - $name = $faker->firstName . ' ' .$faker->lastName; + $name = $faker->firstName . ' ' . $faker->lastName; View::addLocation(__DIR__); - try { - Mail::send('email.test', [], function ($message) use ($email, $subject, $name) { - $message->from('from@johndoe.com', 'From Name'); - $message->sender('sender@johndoe.com', 'Sender Name'); - $message->to($email, $name); + Mail::send('email.test', [], function ($message) use ($email, $subject, $name) { + $message->from('from@johndoe.com', 'From Name'); + $message->sender('sender@johndoe.com', 'Sender Name'); - $message->cc('cc.averylongemail1@johndoe.com', 'CC This Person With a Long Name 1'); - $message->cc('cc.averylongemail2@johndoe.com', 'CC This Person With a Long Name 2'); - $message->cc('cc.averylongemail3@johndoe.com', 'CC This Person With a Long Name 3'); - $message->cc('cc.averylongemail4@johndoe.com', 'CC This Person With a Long Name 4'); - $message->cc('cc.averylongemail5@johndoe.com', 'CC This Person With a Long Name 5'); - $message->cc('cc.averylongemail6@johndoe.com', 'CC This Person With a Long Name 6'); - $message->cc('cc.averylongemail7@johndoe.com', 'CC This Person With a Long Name 7'); - $message->cc('cc.averylongemail8@johndoe.com', 'CC This Person With a Long Name 8'); - $message->cc('cc.averylongemail9@johndoe.com', 'CC This Person With a Long Name 9'); - $message->bcc('bcc@johndoe.com', 'BCC Name'); + $message->to($email, $name); - $message->replyTo('reply-to@johndoe.com', 'Reply-To Name'); + $message->cc('cc.averylongemail1@johndoe.com', 'CC This Person With a Long Name 1'); + $message->cc('cc.averylongemail2@johndoe.com', 'CC This Person With a Long Name 2'); + $message->cc('cc.averylongemail3@johndoe.com', 'CC This Person With a Long Name 3'); + $message->cc('cc.averylongemail4@johndoe.com', 'CC This Person With a Long Name 4'); + $message->cc('cc.averylongemail5@johndoe.com', 'CC This Person With a Long Name 5'); + $message->cc('cc.averylongemail6@johndoe.com', 'CC This Person With a Long Name 6'); + $message->cc('cc.averylongemail7@johndoe.com', 'CC This Person With a Long Name 7'); + $message->cc('cc.averylongemail8@johndoe.com', 'CC This Person With a Long Name 8'); + $message->cc('cc.averylongemail9@johndoe.com', 'CC This Person With a Long Name 9'); + $message->bcc('bcc@johndoe.com', 'BCC Name'); - $message->subject($subject); + $message->replyTo('reply-to@johndoe.com', 'Reply-To Name'); - $message->priority(3); - }); - } catch (TransportException $e) { - } + $message->subject($subject); + + $message->priority(3); + }); $track = MailTracker::sentEmailModel()->newQuery()->orderBy('id', 'desc')->first(); @@ -1161,49 +1088,47 @@ public function it_handles_secondary_connection() $old_email = MailTracker::sentEmailModel()->newQuery()->create([ 'hash' => Str::random(32), ]); - $old_url = MailTracker::sentEmailUrlClickedModel()->newQuery()->create([ + $old_url = MailTracker::sentEmailUrlClickedModel()->newQuery()->create([ 'sent_email_id' => $old_email->id, - 'hash' => Str::random(32), + 'hash' => Str::random(32), ]); // Go into the future to make sure that the old email gets removed \Carbon\Carbon::setTestNow(\Carbon\Carbon::now()->addWeek()); Event::fake([ - EmailSentEvent::class + EmailSentEvent::class, ]); - $faker = Factory::create(); - $email = $faker->email; + $faker = Factory::create(); + $email = $faker->email; $subject = $faker->sentence; - $name = $faker->firstName . ' ' .$faker->lastName; + $name = $faker->firstName . ' ' . $faker->lastName; \View::addLocation(__DIR__); - try { - \Mail::send('email.test', [], function ($message) use ($email, $subject, $name) { - $message->from('from@johndoe.com', 'From Name'); - $message->sender('sender@johndoe.com', 'Sender Name'); - $message->to($email, $name); + \Mail::send('email.test', [], function ($message) use ($email, $subject, $name) { + $message->from('from@johndoe.com', 'From Name'); + $message->sender('sender@johndoe.com', 'Sender Name'); - $message->cc('cc@johndoe.com', 'CC Name'); - $message->bcc('bcc@johndoe.com', 'BCC Name'); + $message->to($email, $name); - $message->replyTo('reply-to@johndoe.com', 'Reply-To Name'); + $message->cc('cc@johndoe.com', 'CC Name'); + $message->bcc('bcc@johndoe.com', 'BCC Name'); - $message->subject($subject); + $message->replyTo('reply-to@johndoe.com', 'Reply-To Name'); - $message->priority(3); - }); - } catch (TransportException $e) { - } + $message->subject($subject); + + $message->priority(3); + }); Event::assertDispatched(EmailSentEvent::class); $this->assertDatabaseHas('sent_emails', [ - 'recipient_name' => $name, + 'recipient_name' => $name, 'recipient_email' => $email, - 'sender_name' => 'From Name', - 'sender_email' => 'from@johndoe.com', - 'subject' => $subject, + 'sender_name' => 'From Name', + 'sender_email' => 'from@johndoe.com', + 'subject' => $subject, ], 'secondary'); $this->assertNull($old_email->fresh()); $this->assertNull($old_url->fresh()); @@ -1215,17 +1140,17 @@ public function it_handles_secondary_connection() public function it_can_retrieve_url_clicks_from_eloquent() { Event::fake(); - $track = MailTracker::sentEmailModel()->newQuery()->create([ + $track = MailTracker::sentEmailModel()->newQuery()->create([ 'hash' => Str::random(32), ]); - $message_id = Str::random(32); + $message_id = Str::random(32); $track->message_id = $message_id; $track->save(); $urlClick = MailTracker::sentEmailUrlClickedModel()->newQuery()->create([ 'sent_email_id' => $track->id, - 'url' => 'https://example.com', - 'hash' => Str::random(32) + 'url' => 'https://example.com', + 'hash' => Str::random(32), ]); $urlClick->save(); $this->assertTrue($track->urlClicks->count() === 1); @@ -1239,9 +1164,9 @@ public function it_can_retrieve_url_clicks_from_eloquent() public function it_handles_headers_with_colons() { $headerData = '{"some_id":2,"some_othger_id":"0dd75231-31bb-4e67-8ab7-a83315f75a44","some_field":"A Field Value"}'; - $track = MailTracker::sentEmailModel()->newQuery()->create([ - 'hash' => Str::random(32), - 'headers' => 'X-MyHeader: '.$headerData, + $track = MailTracker::sentEmailModel()->newQuery()->create([ + 'hash' => Str::random(32), + 'headers' => 'X-MyHeader: ' . $headerData, ]); $retrieval = $track->getHeader('X-MyHeader'); @@ -1251,9 +1176,9 @@ public function it_handles_headers_with_colons() public function testLogContentInFilesystem() { - $faker = Factory::create(); - $email = $faker->email; - $name = $faker->firstName . ' ' .$faker->lastName; + $faker = Factory::create(); + $email = $faker->email; + $name = $faker->firstName . ' ' . $faker->lastName; $content = 'Text to e-mail'; View::addLocation(__DIR__); $str = Mockery::mock(Str::class); @@ -1267,34 +1192,31 @@ public function testLogContentInFilesystem() config()->set('mail-tracker.tracker-filesystem', 'filesystem'); config()->set('mail-tracker.tracker-filesystem-folder', 'mail-tracker'); config()->set('filesystems.disks.testing.driver', 'local'); - config()->set('filesystems.disks.testing.root', realpath(__DIR__.'/../storage')); + config()->set('filesystems.disks.testing.root', realpath(__DIR__ . '/../storage')); config()->set('filesystems.default', 'testing'); Storage::fake(config('mail-tracker.tracker-filesystem')); - try { - Mail::raw($content, function ($message) use ($email, $name) { - $message->from('from@johndoe.com', 'From Name'); + Mail::raw($content, function ($message) use ($email, $name) { + $message->from('from@johndoe.com', 'From Name'); - $message->to($email, $name); - }); - } catch (Exception $e) { - } + $message->to($email, $name); + }); $this->assertDatabaseHas('sent_emails', [ - 'hash' => 'random-hash', - 'sender_name' => 'From Name', - 'sender_email' => 'from@johndoe.com', - 'recipient_name' => $name, + 'hash' => 'random-hash', + 'sender_name' => 'From Name', + 'sender_email' => 'from@johndoe.com', + 'recipient_name' => $name, 'recipient_email' => $email, - 'content' => null + 'content' => null, ]); - $tracker = MailTracker::sentEmailModel()->newQuery()->where('hash', '=','random-hash')->first(); + $tracker = MailTracker::sentEmailModel()->newQuery()->where('hash', '=', 'random-hash')->first(); $this->assertNotNull($tracker); $this->assertEquals($content, $tracker->content); - $folder = config('mail-tracker.tracker-filesystem-folder', 'mail-tracker'); - $filePath = $tracker->meta->get('content_file_path'); + $folder = config('mail-tracker.tracker-filesystem-folder', 'mail-tracker'); + $filePath = $tracker->meta->get('content_file_path'); $expectedPath = "{$folder}/random-hash.html"; $this->assertEquals($expectedPath, $filePath); Storage::disk(config('mail-tracker.tracker-filesystem'))->assertExists($filePath); diff --git a/tests/SetUpTest.php b/tests/SetUpTest.php index 868371d..5a7e9ef 100644 --- a/tests/SetUpTest.php +++ b/tests/SetUpTest.php @@ -59,6 +59,7 @@ protected function getEnvironmentSetUp($app) 'secret' => 'aws-secret', ]); $app['config']->set('mail.from.address', 'test@example.com'); + $app['config']->set('mail.default', 'log'); $app['config']->set('app.debug', true); } }