set('admin_access.secrets_key', 'base64:'.base64_encode(random_bytes(32))); config()->set('mail.default', 'log'); // as on a development machine Settings::set('mail.host', '127.0.0.1'); Settings::set('mail.port', 1); Settings::set('mail.encryption', 'tls'); }); /* | The brief points this beforeEach at 'mail.example.invalid' and asserts the | error mentions it. Measured by hand in this container (php artisan tinker | equivalent, three runs each): a lookup for 'mail.example.invalid' fails in | 0.7-7ms via NXDOMAIN, but that number depends on a resolver being reachable | at all — nothing here guarantees that in every environment this suite might | run in. 127.0.0.1:1 (nothing listens on port 1) refused every one of those | three runs in ~0.2ms with zero DNS involved, and it is the convention this | codebase already settled on for the identical reason — see | MailboxTransportTest's "LOAD-BEARING for the MAIL_MAILER=log guard" comment. | Using it here keeps both tests fast AND deterministic instead of merely fast | today. */ it('does NOT quietly succeed through the log mailer', function () { // The whole point: on dev the default mailer is the log, and a tester // that honoured it would report success while writing to a file — a // test that proves nothing. The log "transport" never fails, so only a // real attempt (which a closed port refuses) can make this assertion // meaningful. $result = app(MailboxTester::class)->run( Mailbox::factory()->create(['address' => 'no-reply@clupilot.com']), 'ziel@example.com', ); expect($result['ok'])->toBeFalse() ->and($result['error'])->not->toBeNull(); }); it('passes the SMTP error through verbatim, because that is the whole diagnosis', function () { $result = app(MailboxTester::class)->run( Mailbox::factory()->create(['address' => 'no-reply@clupilot.com']), 'ziel@example.com', ); // Not "Fehler beim Senden" — the operating system's own sentence, naming // the actual host:port and the actual reason. expect($result['error'])->toContain('127.0.0.1:1') ->and($result['error'])->toContain('Connection refused'); }); it('refuses a mailbox without a password rather than pretending to try', function () { $box = Mailbox::factory()->create(['password' => null]); $result = app(MailboxTester::class)->run($box, 'ziel@example.com'); expect($result['ok'])->toBeFalse() ->and($result['error'])->toContain('Passwort'); }); it('stamps last_verified_at only on success', function () { $box = Mailbox::factory()->create(['address' => 'no-reply@clupilot.com']); app(MailboxTester::class)->run($box, 'ziel@example.com'); expect($box->fresh()->last_verified_at)->toBeNull(); }); it('gives a readable message instead of throwing when SECRETS_KEY is unusable', function () { // Facts the brief could not know: SECRETS_KEY is not set on this // installation at all (the mail settings page's own banner says so). // $box->password decrypts on read, so touching it throws a // RuntimeException out of SecretCipher — Task 7's EditMailbox::save() // shipped a Critical fix for exactly this shape of bug. The tester // touches the very same cipher and needs the very same guard. $box = Mailbox::factory()->create(['address' => 'no-reply@clupilot.com']); config()->set('admin_access.secrets_key', ''); $result = app(MailboxTester::class)->run($box, 'ziel@example.com'); expect($result['ok'])->toBeFalse() ->and($result['error'])->toBe(__('mail_settings.no_key')); }); /* | LOAD-BEARING for the success path. Every test above only proves a FAILURE | is reported correctly — none of them ever reaches | `$box->forceFill(['last_verified_at' => now()])->save()`, so all of them | would stay green even if that line were deleted outright. A real success | needs something real to answer on the other end of the socket; see | FakeSmtpServer for why that has to be a subprocess rather than an in-process | fake. */ it('actually delivers and stamps last_verified_at when the server accepts the message', function () { $server = FakeSmtpServer::succeeding(); try { Settings::set('mail.host', '127.0.0.1'); Settings::set('mail.port', $server->port); $box = Mailbox::factory()->create(['address' => 'no-reply@clupilot.com']); $result = app(MailboxTester::class)->run($box, 'ziel@example.com'); expect($result['ok'])->toBeTrue() ->and($result['error'])->toBeNull() ->and($box->fresh()->last_verified_at)->not->toBeNull(); } finally { $server->stop(); } }); it('passes a real SMTP rejection through verbatim too, not only a refused connection', function () { // The strongest version of "the server's own sentence": an actual // protocol-level 5xx from an actual SMTP dialogue, not merely the // operating system's "connection refused". Proves MailboxTester's catch // block does not paraphrase or replace what Symfony Mailer reports. $server = FakeSmtpServer::rejectingAt('rcpt', '550 5.1.1 No such user ziel@example.com'); try { Settings::set('mail.host', '127.0.0.1'); Settings::set('mail.port', $server->port); $box = Mailbox::factory()->create(['address' => 'no-reply@clupilot.com']); $result = app(MailboxTester::class)->run($box, 'ziel@example.com'); expect($result['ok'])->toBeFalse() ->and($result['error'])->toContain('550 5.1.1 No such user ziel@example.com') ->and($box->fresh()->last_verified_at)->toBeNull(); } finally { $server->stop(); } }); it('actually attempts TLS when SSL is configured on a non-standard port, instead of silently going out in the clear', function () { // The exact trap this task names: Laravel 13's MailManager reads // 'scheme', not 'encryption' (MailManager.php:196). An 'encryption' key // is silently ignored, and MailManager falls back to choosing the scheme // from the port number alone — smtps only for port 465, smtp otherwise. // Configured for SSL on a non-465 port (a real EU hoster's submission // port with implicit TLS, say), that bug would connect in PLAIN TEXT // while the console still reported TLS. // // FakeSmtpServer only ever speaks plain text, so it can tell the two // apart: a correct 'scheme' => 'smtps' opens an implicit-TLS connection // this server cannot answer and the send FAILS; the buggy 'encryption' // key would default to plain 'smtp' on this non-465 port and let it // succeed instead. Verified by hand this fails in ~0.2s, not a hang — // OpenSSL rejects the server's plain-text greeting as an invalid TLS // record immediately. $server = FakeSmtpServer::succeeding(); try { Settings::set('mail.host', '127.0.0.1'); Settings::set('mail.port', $server->port); // deliberately not 465 Settings::set('mail.encryption', 'ssl'); $box = Mailbox::factory()->create(['address' => 'no-reply@clupilot.com']); $result = app(MailboxTester::class)->run($box, 'ziel@example.com'); expect($result['ok'])->toBeFalse(); } finally { $server->stop(); } });