├── .styleci.yml
├── .gitignore
├── .editorconfig
├── sonar-project.properties
├── .github
└── dependabot.yml
├── composer.json
├── LICENSE
├── phpunit.xml
├── .appveyor.yml
├── src
└── MailFailer.php
├── tests
└── MailFailureTest.php
└── README.md
/.styleci.yml:
--------------------------------------------------------------------------------
1 | preset: psr2
2 |
3 | finder:
4 | exclude:
5 | - "vendor"
6 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | composer.lock
2 | vendor
3 | junit-logfile.xml
4 | clover.xml
5 | .scannerwork
6 | *.cache
7 |
--------------------------------------------------------------------------------
/.editorconfig:
--------------------------------------------------------------------------------
1 | root = true
2 |
3 | [*]
4 | charset = utf-8
5 | end_of_line = lf
6 | insert_final_newline = true
7 | indent_style = space
8 | indent_size = 4
9 | trim_trailing_whitespace = true
10 |
11 | [*.md]
12 | trim_trailing_whitespace = false
13 |
14 | [*.yml]
15 | indent_style = space
16 | indent_size = 2
17 |
--------------------------------------------------------------------------------
/sonar-project.properties:
--------------------------------------------------------------------------------
1 | sonar.projectKey=rogervila_laravel-email-failer
2 | sonar.organization=rogervila-github
3 | sonar.host.url=https://sonarcloud.io
4 | sonar.sources=.
5 | sonar.exclusions=vendor/**, tests/**
6 | sonar.coverage.exclusions=vendor/**, tests/**
7 | sonar.php.tests.reportPath=junit-logfile.xml
8 | sonar.php.coverage.reportPaths=clover.xml
9 |
--------------------------------------------------------------------------------
/.github/dependabot.yml:
--------------------------------------------------------------------------------
1 | # To get started with Dependabot version updates, you'll need to specify which
2 | # package ecosystems to update and where the package manifests are located.
3 | # Please see the documentation for all configuration options:
4 | # https://help.github.com/github/administering-a-repository/configuration-options-for-dependency-updates
5 |
6 | version: 2
7 | updates:
8 | # Maintain dependencies for GitHub Actions
9 | - package-ecosystem: github-actions
10 | directory: "/"
11 | schedule:
12 | interval: "daily"
13 | # Maintain dependencies for composer
14 | - package-ecosystem: composer
15 | directory: "/"
16 | schedule:
17 | interval: "daily"
18 |
--------------------------------------------------------------------------------
/composer.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "rogervila/laravel-email-failer",
3 | "description": "Helper class for testing Laravel Mail Failures",
4 | "keywords": [
5 | "laravel mail failure",
6 | "laravel mail error"
7 | ],
8 | "type": "library",
9 | "license": "MIT",
10 | "authors": [
11 | {
12 | "name": "Roger Vilà",
13 | "email": "rogervila@me.com"
14 | }
15 | ],
16 | "config": {
17 | "optimize-autoloader": true,
18 | "preferred-install": "dist",
19 | "sort-packages": true
20 | },
21 | "autoload": {
22 | "psr-4": {
23 | "LaravelEmailFailer\\": "src/"
24 | }
25 | },
26 | "autoload-dev": {
27 | "psr-4": {
28 | "Tests\\": "tests/"
29 | }
30 | },
31 | "require": {
32 | "laravel/framework": "^11.0"
33 | },
34 | "require-dev": {
35 | "laravel/pint": "^1.16",
36 | "orchestra/testbench": "^9.0",
37 | "phpunit/phpunit": "^10.0 || ^11.0"
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2019 Roger Vilà
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/phpunit.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 | tests
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 | ./src
31 |
32 |
33 |
34 |
--------------------------------------------------------------------------------
/.appveyor.yml:
--------------------------------------------------------------------------------
1 | build: false
2 | version: appveyor-{branch}-{build}
3 | shallow_clone: false
4 | clone_folder: C:\projects\app
5 |
6 | environment:
7 | matrix:
8 | - php_ver: 8.2.0
9 | - php_ver: 8.3.0
10 |
11 | init:
12 | - SET PATH=C:\tools\php;%PATH%
13 |
14 | install:
15 | - ps: Set-Service wuauserv -StartupType Manual
16 | - IF NOT EXIST C:\tools\php (choco install --yes --allow-empty-checksums php --version %php_ver% --params '/InstallDir:C:\tools\php')
17 | - cd C:\tools\php
18 | - copy php.ini-production php.ini
19 | - echo date.timezone="UTC" >> php.ini
20 | - echo memory_limit=512M >> php.ini
21 | - echo extension_dir=ext >> php.ini
22 | - echo extension=php_curl.dll >> php.ini
23 | - echo extension=php_fileinfo.dll >> php.ini
24 | - echo extension=php_openssl.dll >> php.ini
25 | - echo extension=php_mbstring.dll >> php.ini
26 | - IF NOT EXIST C:\tools\composer.phar (cd C:\tools && appveyor DownloadFile https://getcomposer.org/composer.phar)
27 | - php C:\tools\composer.phar --version
28 | - cd C:\projects\app
29 |
30 | before_test:
31 | - cd C:\projects\app
32 | - php C:\tools\composer.phar update --optimize-autoloader --no-interaction --no-progress --prefer-stable --no-ansi
33 | - php C:\tools\composer.phar info -D | sort
34 |
35 | test_script:
36 | - cd C:\projects\app
37 | - vendor\bin\pint --test
38 | - vendor\bin\phpunit --no-coverage
39 |
--------------------------------------------------------------------------------
/src/MailFailer.php:
--------------------------------------------------------------------------------
1 | make(MailManager::class));
27 | }
28 |
29 | /**
30 | * Send a new message using a view.
31 | *
32 | * @param Mailable|string|array $view
33 | * @param \Closure|string|null $callback
34 | * @return void
35 | *
36 | * @throws TransportException
37 | */
38 | public function send($view, array $data = [], $callback = null)
39 | {
40 | try {
41 | if ($view instanceof Mailable) {
42 | foreach ($view->to as $to) {
43 | array_push($this->failedRecipients, $to['address']);
44 | }
45 | }
46 |
47 | throw new TransportException('Connection could not be established with host "1.2.3.4:1234": stream_socket_client(): Unable to connect to 1.2.3.4:1234 (Connection refused)');
48 | } catch (Throwable $e) {
49 | throw new TransportException($e->getMessage());
50 | }
51 | }
52 |
53 | /**
54 | * Get the array of failed recipients.
55 | *
56 | * @return array
57 | */
58 | public function failures()
59 | {
60 | return $this->failedRecipients;
61 | }
62 |
63 | public static function bind(?Application $app = null): self
64 | {
65 | Mail::swap($instance = new self($app));
66 |
67 | return $instance;
68 | }
69 | }
70 |
--------------------------------------------------------------------------------
/tests/MailFailureTest.php:
--------------------------------------------------------------------------------
1 | expectException(TransportException::class);
19 | $this->expectExceptionMessage('Connection could not be established with host "1.2.3.4:1234": stream_socket_client(): Unable to connect to 1.2.3.4:1234 (Connection refused)');
20 |
21 | $address = 'foo@foo.foo';
22 | $mailer = MailFailer::bind();
23 |
24 | $mailable = new FakeMailable;
25 | $mailable->subject('test')->to($address);
26 |
27 | $mailer->send($mailable);
28 | }
29 |
30 | public function testMailFailuresContainsAddress()
31 | {
32 | try {
33 | $address = 'foo@foo.foo';
34 | $mailer = MailFailer::bind();
35 |
36 | $mailable = new FakeMailable;
37 | $mailable->subject('test')->to($address);
38 |
39 | $mailer->send($mailable);
40 | } catch (TransportException) {
41 | $this->assertTrue(in_array($address, $mailer->failures()));
42 | }
43 | }
44 |
45 | public function testMailFailuresContainsMultipleAddresses()
46 | {
47 | try {
48 | $addresses = ['foo@foo.foo', 'bar@bar.bar'];
49 | $mailer = MailFailer::bind();
50 |
51 | $mailable = new FakeMailable;
52 | $mailable->subject('test')->to($addresses);
53 |
54 | $mailer->send($mailable);
55 | } catch (TransportException) {
56 | $this->assertCount(0, array_diff($addresses, $mailer->failures()));
57 | }
58 | }
59 |
60 | public function testFacadeSwap()
61 | {
62 | MailFailer::bind();
63 |
64 | $addresses = ['foo@foo.foo', 'bar@bar.bar'];
65 | $mailable = new FakeMailable;
66 | $mailable->subject('test')->to($addresses);
67 |
68 | try {
69 | Mail::send($mailable);
70 | } catch (TransportException) {
71 | $this->assertCount(0, array_diff($addresses, Mail::failures()));
72 | }
73 | }
74 | }
75 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |

2 |
3 | [](https://travis-ci.org/rogervila/laravel-email-failer)
4 | [](https://ci.appveyor.com/project/roger-vila/laravel-email-failer/branch/master)
5 | [](https://github.styleci.io/repos/195772522)
6 | [](https://packagist.org/packages/rogervila/laravel-email-failer)
7 | [](https://packagist.org/packages/rogervila/laravel-email-failer)
8 | [](https://packagist.org/packages/rogervila/laravel-email-failer)
9 | [](https://madewithlaravel.com/p/laravel-email-failer/shield-link)
10 |
11 | # Laravel Email Failer
12 |
13 | ```sh
14 | composer require --dev rogervila/laravel-email-failer
15 | ```
16 |
17 | ## About
18 |
19 | Trigger email failures to assert what happens on your Laravel Application when an email fails to send
20 |
21 | ## Usage
22 |
23 | Once MailFailer instance is binded, all emails will fail. This helps to assert that your application Mail exceptions are handled correctly (ie: mark the email address as invalid)
24 |
25 | ```php
26 | class MyService
27 | {
28 | public static function sendEmail()
29 | {
30 | \Illuminate\Support\Facades\Mail::send(...);
31 | }
32 | }
33 |
34 | public function test_happy_path()
35 | {
36 | Mail::fake();
37 |
38 | MyService::sendEmail();
39 |
40 | Mail::assertSent(MyMailable::class);
41 | }
42 |
43 | public function test_email_failures()
44 | {
45 | $this->expectException(TransportException::class);
46 |
47 | \LaravelEmailFailer\MailFailer::bind();
48 |
49 | MyService::sendEmail();
50 |
51 | Mail::assertNotSent(MyMailable::class);
52 |
53 | dump(Mail::failures());
54 | // Assert here what happens when the email has failed
55 | }
56 |
57 | ```
58 |
59 |
60 | ## License
61 |
62 | Laravel Email Failer is open-sourced software licensed under the [MIT license](https://opensource.org/licenses/MIT).
63 |
64 |
65 | Icon made by Darius Dan from www.flaticon.com is licensed by CC 3.0 BY
66 |
--------------------------------------------------------------------------------