diff --git a/.github/workflows/run-tests.yml b/.github/workflows/run-tests.yml index 2680a49..e1b6ddf 100644 --- a/.github/workflows/run-tests.yml +++ b/.github/workflows/run-tests.yml @@ -13,39 +13,39 @@ jobs: fail-fast: false matrix: os: [ubuntu-latest] - php: [8.0, 8.1, 8.2] - laravel: ['8.*', '9.*', '10.*', '11.*', '12.*'] + php: ['8.1', '8.2', '8.3', '8.4', '8.5'] + laravel: ['10.*', '11.*', '12.*', '13.*'] stability: [prefer-stable] exclude: - - php: 8.0 - laravel: 10.* - - php: 8.2 - laravel: 8.* - - laravel: 11.* - php: 8.0 - - laravel: 11.* - php: 8.1 - - laravel: 12.* - php: 8.0 - - laravel: 12.* - php: 8.1 + - laravel: '10.*' + php: '8.4' + - laravel: '10.*' + php: '8.5' + - laravel: '11.*' + php: '8.1' + - laravel: '11.*' + php: '8.5' + - laravel: '12.*' + php: '8.1' + - laravel: '13.*' + php: '8.1' + - laravel: '13.*' + php: '8.2' include: - - laravel: 10.* - testbench: 8.* - - laravel: 9.* - testbench: 7.* - - laravel: 8.* - testbench: 6.23 - - laravel: 11.* - testbench: 9.* - - laravel: 12.* - testbench: 10.* + - laravel: '10.*' + testbench: '8.*' + - laravel: '11.*' + testbench: '9.*' + - laravel: '12.*' + testbench: '10.*' + - laravel: '13.*' + testbench: '11.*' name: P${{ matrix.php }} - L${{ matrix.laravel }} - ${{ matrix.stability }} - ${{ matrix.os }} steps: - name: Checkout code - uses: actions/checkout@v2 + uses: actions/checkout@v4 - name: Setup PHP uses: shivammathur/setup-php@v2 diff --git a/README.md b/README.md index e50c4c4..0c29d89 100644 --- a/README.md +++ b/README.md @@ -36,14 +36,15 @@ The email service organizes emails into distinct inboxes for each application, e ![image](https://github.com/user-attachments/assets/6417907c-119d-43ac-9cf6-5638bafae24f) +## Testing -### Testing +Inbound messages are parsed with [zbateson/mail-mime-parser](https://mail-mime-parser.org) (see the docs on [handling inbound email](https://docs.beyondco.de/laravel-mailbox/basic-usage/handling)). Run the package test suite with: -``` bash +```bash composer test ``` -### Changelog +## Changelog Please see [CHANGELOG](CHANGELOG.md) for more information what has changed recently. diff --git a/composer.json b/composer.json index 44a1f50..a904a75 100644 --- a/composer.json +++ b/composer.json @@ -16,20 +16,19 @@ } ], "require": { - "php": "^8.0", - "illuminate/container": "^6.0|^7.0|^8.0|^9.0|^10.0|^11.0|^12.0", - "illuminate/database": "^6.0|^7.0|^8.0|^9.0|^10.0|^11.0|^12.0", - "illuminate/log": "^6.0|^7.0|^8.0|^9.0|^10.0|^11.0|^12.0", - "illuminate/routing": "^6.0|^7.0|^8.0|^9.0|^10.0|^11.0|^12.0", - "illuminate/support": "^6.0|^7.0|^8.0|^9.0|^10.0|^11.0|^12.0", + "php": "^8.1", + "illuminate/container": "^10.0|^11.0|^12.0|^13.0", + "illuminate/database": "^10.0|^11.0|^12.0|^13.0", + "illuminate/log": "^10.0|^11.0|^12.0|^13.0", + "illuminate/routing": "^10.0|^11.0|^12.0|^13.0", + "illuminate/support": "^10.0|^11.0|^12.0|^13.0", "willdurand/email-reply-parser": "^2.8", - "zbateson/mail-mime-parser": "^1.1|^2.4|^3.0" + "zbateson/mail-mime-parser": "^2.4|^3.0|^4.0" }, "require-dev": { - "laminas/laminas-mail": "^2.13", "mockery/mockery": "^1.2", - "orchestra/testbench": "^4.0|^5.0|^7.0|^8.0|^9.0|^10.0", - "phpunit/phpunit": "^7.0|^8.0|^9.3|^10.5|^11.5.3" + "orchestra/testbench": "^8.0|^9.0|^10.0|^11.0", + "phpunit/phpunit": "^10.5|^11.0|^12.0" }, "autoload": { "psr-4": { diff --git a/tests/Console/CleanEmailsTest.php b/tests/Console/CleanEmailsTest.php index 5f18272..4c994da 100644 --- a/tests/Console/CleanEmailsTest.php +++ b/tests/Console/CleanEmailsTest.php @@ -8,6 +8,7 @@ use Carbon\Carbon; use Illuminate\Support\Collection; use Illuminate\Support\Str; +use PHPUnit\Framework\Attributes\Test; class CleanEmailsTest extends TestCase { @@ -20,7 +21,7 @@ public function setUp(): void $this->app['config']->set('mailbox.store_incoming_emails_for_days', 31); } - /** @test */ + #[Test] public function it_can_clean_the_statistics() { Collection::times(200)->each(function (int $index) { @@ -41,7 +42,7 @@ public function it_can_clean_the_statistics() $this->assertCount(0, InboundEmail::where('created_at', '<', $cutOffDate)->get()); } - /** @test */ + #[Test] public function it_errors_if_max_age_inf() { $this->app['config']->set('mailbox.store_incoming_emails_for_days', INF); diff --git a/tests/Controllers/MailgunTest.php b/tests/Controllers/MailgunTest.php index af10733..0571b79 100644 --- a/tests/Controllers/MailgunTest.php +++ b/tests/Controllers/MailgunTest.php @@ -3,6 +3,7 @@ namespace BeyondCode\Mailbox\Tests\Controllers; use BeyondCode\Mailbox\Tests\TestCase; +use PHPUnit\Framework\Attributes\Test; class MailgunTest extends TestCase { @@ -13,7 +14,7 @@ protected function getEnvironmentSetUp($app) $app['config']['mailbox.driver'] = 'mailgun'; } - /** @test */ + #[Test] public function it_verifies_mailgun_signatures() { $this->post('/laravel-mailbox/mailgun/mime', [ @@ -38,7 +39,7 @@ public function it_verifies_mailgun_signatures() ])->assertStatus(200); } - /** @test */ + #[Test] public function it_verifies_fresh_timestamps() { $timestamp = now()->subMinutes(5)->timestamp; diff --git a/tests/Controllers/PostmarkTest.php b/tests/Controllers/PostmarkTest.php index 2e7d4f8..a0fa3fd 100644 --- a/tests/Controllers/PostmarkTest.php +++ b/tests/Controllers/PostmarkTest.php @@ -3,6 +3,7 @@ namespace BeyondCode\Mailbox\Tests\Controllers; use BeyondCode\Mailbox\Tests\TestCase; +use PHPUnit\Framework\Attributes\Test; class PostmarkTest extends TestCase { @@ -13,7 +14,7 @@ protected function getEnvironmentSetUp($app) $app['config']['mailbox.driver'] = 'postmark'; } - /** @test */ + #[Test] public function it_expects_to_receive_raw_email_field() { $this->withoutMiddleware(); diff --git a/tests/Drivers/LogTest.php b/tests/Drivers/LogTest.php index 4cdb5a2..b39f954 100644 --- a/tests/Drivers/LogTest.php +++ b/tests/Drivers/LogTest.php @@ -7,6 +7,7 @@ use BeyondCode\Mailbox\Tests\TestCase; use Illuminate\Mail\Mailable; use Illuminate\Support\Facades\Mail; +use PHPUnit\Framework\Attributes\Test; class LogTest extends TestCase { @@ -18,7 +19,7 @@ protected function getEnvironmentSetUp($app) $app['config']['mailbox.driver'] = 'log'; } - /** @test */ + #[Test] public function it_catches_logged_mails() { Mailbox::from('{name}@beyondco.de', function (InboundEmail $email, $name) { diff --git a/tests/InboundEmailTest.php b/tests/InboundEmailTest.php index 8a54b9a..ef8156b 100644 --- a/tests/InboundEmailTest.php +++ b/tests/InboundEmailTest.php @@ -6,6 +6,7 @@ use BeyondCode\Mailbox\InboundEmail; use Illuminate\Mail\Mailable; use Illuminate\Support\Facades\Mail; +use PHPUnit\Framework\Attributes\Test; class InboundEmailTest extends TestCase { @@ -17,7 +18,7 @@ protected function getEnvironmentSetUp($app) $app['config']['mailbox.driver'] = 'log'; } - /** @test */ + #[Test] public function it_stores_inbound_emails() { Mailbox::to('someone@beyondco.de', function ($email) { @@ -29,7 +30,7 @@ public function it_stores_inbound_emails() $this->assertSame(1, InboundEmail::query()->count()); } - /** @test */ + #[Test] public function it_stores_all_inbound_emails() { $this->app['config']['mailbox.only_store_matching_emails'] = false; @@ -43,7 +44,7 @@ public function it_stores_all_inbound_emails() $this->assertSame(2, InboundEmail::query()->count()); } - /** @test */ + #[Test] public function it_can_use_fallbacks() { Mailbox::fallback(function (InboundEmail $email) { @@ -57,7 +58,7 @@ public function it_can_use_fallbacks() Mail::assertSent(ReplyMail::class); } - /** @test */ + #[Test] public function it_can_use_catchall() { Mailbox::to('someone@beyondco.de', function ($email) { @@ -74,7 +75,7 @@ public function it_can_use_catchall() Mail::assertSent(ReplyMail::class); } - /** @test */ + #[Test] public function it_stores_inbound_emails_with_catchall() { Mailbox::catchAll(function ($email) { @@ -86,7 +87,7 @@ public function it_stores_inbound_emails_with_catchall() $this->assertSame(2, InboundEmail::query()->count()); } - /** @test */ + #[Test] public function it_stores_inbound_emails_with_fallback() { Mailbox::fallback(function ($email) { @@ -98,7 +99,7 @@ public function it_stores_inbound_emails_with_fallback() $this->assertSame(2, InboundEmail::query()->count()); } - /** @test */ + #[Test] public function it_stores_inbound_emails_with_fallback_and_catchall_only_once() { Mailbox::fallback(function ($email) { @@ -113,7 +114,7 @@ public function it_stores_inbound_emails_with_fallback_and_catchall_only_once() $this->assertSame(2, InboundEmail::query()->count()); } - /** @test */ + #[Test] public function it_does_not_store_inbound_emails_if_configured() { $this->app['config']['mailbox.store_incoming_emails_for_days'] = 0; @@ -127,7 +128,7 @@ public function it_does_not_store_inbound_emails_if_configured() $this->assertSame(0, InboundEmail::query()->count()); } - /** @test */ + #[Test] public function it_can_reply_to_mails() { Mailbox::from('example@beyondco.de', function (InboundEmail $email) { @@ -141,7 +142,7 @@ public function it_can_reply_to_mails() Mail::assertSent(ReplyMail::class); } - /** @test */ + #[Test] public function it_uses_the_configured_model() { $this->app['config']['mailbox.model'] = ExtendedInboundEmail::class; diff --git a/tests/MailboxRouteCollectionTest.php b/tests/MailboxRouteCollectionTest.php index fa3c027..d8d5fde 100644 --- a/tests/MailboxRouteCollectionTest.php +++ b/tests/MailboxRouteCollectionTest.php @@ -5,11 +5,11 @@ use BeyondCode\Mailbox\InboundEmail; use BeyondCode\Mailbox\Routing\Route; use BeyondCode\Mailbox\Routing\RouteCollection; -use Laminas\Mail\Message as TestMail; +use PHPUnit\Framework\Attributes\Test; class MailboxRouteCollectionTest extends TestCase { - /** @test */ + #[Test] public function it_returns_all_matching_mailbox_routes() { $collection = new RouteCollection(); @@ -18,7 +18,7 @@ public function it_returns_all_matching_mailbox_routes() $collection->add(new Route(Route::FROM, '{from}@beyondco.de', '')); $collection->add(new Route(Route::FROM, 'different@laravel.com', '')); - $testMail = (new TestMail()) + $testMail = (new TestMimeMessage()) ->setFrom('hello@beyondco.de'); $message = new InboundEmail(['message' => $testMail->toString()]); diff --git a/tests/MailboxRouteTest.php b/tests/MailboxRouteTest.php index 5adfcd0..3d4b3c1 100644 --- a/tests/MailboxRouteTest.php +++ b/tests/MailboxRouteTest.php @@ -4,7 +4,8 @@ use BeyondCode\Mailbox\InboundEmail; use BeyondCode\Mailbox\Routing\Route; -use Laminas\Mail\Message as TestMail; +use PHPUnit\Framework\Attributes\DataProvider; +use PHPUnit\Framework\Attributes\Test; class MailboxRouteTest extends TestCase { @@ -16,14 +17,11 @@ public static function emailDataProvider() ]; } - /** - * @test - * - * @dataProvider emailDataProvider - */ + #[Test] + #[DataProvider('emailDataProvider')] public function it_matches_from_mails($fromMail, $successfulPattern, $failingPattern) { - $testMail = (new TestMail()) + $testMail = (new TestMimeMessage()) ->setFrom($fromMail); $message = new InboundEmail(['message' => $testMail->toString()]); @@ -35,14 +33,11 @@ public function it_matches_from_mails($fromMail, $successfulPattern, $failingPat $this->assertFalse($route->matches($message)); } - /** - * @test - * - * @dataProvider emailDataProvider - */ + #[Test] + #[DataProvider('emailDataProvider')] public function it_matches_to_mails($toMail, $successfulPattern, $failingPattern) { - $testMail = (new TestMail()) + $testMail = (new TestMimeMessage()) ->setTo($toMail); $message = new InboundEmail(['message' => $testMail->toString()]); @@ -54,14 +49,11 @@ public function it_matches_to_mails($toMail, $successfulPattern, $failingPattern $this->assertFalse($route->matches($message)); } - /** - * @test - * - * @dataProvider emailDataProvider - */ + #[Test] + #[DataProvider('emailDataProvider')] public function it_matches_cc_mails($ccMail, $successfulPattern, $failingPattern) { - $testMail = (new TestMail()) + $testMail = (new TestMimeMessage()) ->setCc($ccMail); $message = new InboundEmail(['message' => $testMail->toString()]); @@ -73,14 +65,11 @@ public function it_matches_cc_mails($ccMail, $successfulPattern, $failingPattern $this->assertFalse($route->matches($message)); } - /** - * @test - * - * @dataProvider emailDataProvider - */ + #[Test] + #[DataProvider('emailDataProvider')] public function it_matches_bcc_mails($bccMail, $successfulPattern, $failingPattern) { - $testMail = (new TestMail()) + $testMail = (new TestMimeMessage()) ->setBcc($bccMail); $message = new InboundEmail(['message' => $testMail->toString()]); @@ -92,14 +81,11 @@ public function it_matches_bcc_mails($bccMail, $successfulPattern, $failingPatte $this->assertFalse($route->matches($message)); } - /** - * @test - * - * @dataProvider subjectDataProvider - */ + #[Test] + #[DataProvider('subjectDataProvider')] public function it_matches_subjects($subject, $successfulPattern, $failingPattern) { - $testMail = (new TestMail()) + $testMail = (new TestMimeMessage()) ->setSubject($subject); $message = new InboundEmail(['message' => $testMail->toString()]); @@ -111,10 +97,10 @@ public function it_matches_subjects($subject, $successfulPattern, $failingPatter $this->assertFalse($route->matches($message)); } - /** @test */ + #[Test] public function it_matches_requirements() { - $testMail = (new TestMail()) + $testMail = (new TestMimeMessage()) ->setFrom('abc@domain.com'); $message = new InboundEmail(['message' => $testMail->toString()]); @@ -138,7 +124,7 @@ public static function subjectDataProvider() ]; } - /** @test */ + #[Test] public function it_returns_parameter_names() { $route = new Route(Route::FROM, 'someone@domain.com', 'SomeAction@handle'); @@ -160,10 +146,10 @@ public function it_returns_parameter_names() ], $route->parameterNames()); } - /** @test */ + #[Test] public function it_returns_parameter_values() { - $testMail = (new TestMail()) + $testMail = (new TestMimeMessage()) ->setFrom('my-email@foo.com') ->setSubject('ABC/DEF/GEH'); @@ -200,10 +186,10 @@ public function it_returns_parameter_values() ], $route->parameters()); } - /** @test */ + #[Test] public function it_runs_callables() { - $testMail = (new TestMail()) + $testMail = (new TestMimeMessage()) ->setFrom('marcel@beyondco.de'); $message = new InboundEmail(['message' => $testMail->toString()]); @@ -217,10 +203,10 @@ public function it_runs_callables() $route->run($message); } - /** @test */ + #[Test] public function it_passes_parameters_to_callables() { - $testMail = (new TestMail()) + $testMail = (new TestMimeMessage()) ->setFrom('marcel@beyondco.de'); $message = new InboundEmail(['message' => $testMail->toString()]); diff --git a/tests/TestMimeMessage.php b/tests/TestMimeMessage.php new file mode 100644 index 0000000..858ffdb --- /dev/null +++ b/tests/TestMimeMessage.php @@ -0,0 +1,61 @@ + */ + private array $headers = []; + + public function setFrom(string $email): self + { + $this->headers['From'] = $email; + + return $this; + } + + public function setTo(string $email): self + { + $this->headers['To'] = $email; + + return $this; + } + + public function setCc(string $email): self + { + $this->headers['Cc'] = $email; + + return $this; + } + + public function setBcc(string $email): self + { + $this->headers['Bcc'] = $email; + + return $this; + } + + public function setSubject(string $subject): self + { + $this->headers['Subject'] = $subject; + + return $this; + } + + public function toString(): string + { + $lines = []; + foreach (['From', 'To', 'Cc', 'Bcc', 'Subject'] as $name) { + if (isset($this->headers[$name])) { + $lines[] = $name.': '.$this->headers[$name]; + } + } + $lines[] = 'MIME-Version: 1.0'; + $lines[] = 'Content-Type: text/plain; charset=UTF-8'; + $lines[] = ''; + $lines[] = ''; + + return implode("\r\n", $lines); + } +}