Waechtertest sucht rekursiv und nur echte Mailables

glob('Mail/*.php') sah nur die oberste Ebene von app/Mail. Der Ordner hat
mit Concerns/ und Transport/ schon zwei Unterordner - eine kuenftige
Mailklasse dort waere dem Waechter nie aufgefallen, und die Mail liefe
still auf MailLane::CALM statt auf eine bewusst gewaehlte Spur.

File::allFiles() sucht rekursiv; is_subclass_of(..., Mailable::class)
schraenkt danach auf echte Mailables ein, statt auf "liegt irgendwo unter
app/Mail" - ein Trait wie RidesALane selbst oder eine kuenftige
Basisklasse macht den Test damit nicht mehr faelschlich rot.

Falsifizierbarkeit erneut geprueft, diesmal an OrderConfirmationMail statt
an der zuvor benutzten VerifyEmailMail: Trait entfernt, Test rot mit genau
dieser Klasse benannt, Trait zurueckgesetzt, Test wieder gruen.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
feat/versandtakt
nexxo 2026-08-03 16:00:44 +02:00
parent 86477ad265
commit ec0e83e69b
1 changed files with 21 additions and 3 deletions

View File

@ -1,5 +1,6 @@
<?php
use App\Mail\Concerns\RidesALane;
use App\Mail\InvoiceMail;
use App\Mail\ResetPasswordMail;
use App\Models\Customer;
@ -9,6 +10,8 @@ use App\Models\User;
use App\Services\Billing\IssueInvoice;
use App\Services\Mail\MailLane;
use App\Support\CompanyProfile;
use Illuminate\Mail\Mailable;
use Illuminate\Support\Facades\File;
use Illuminate\Support\Facades\Mail;
use Illuminate\Support\Facades\Queue;
@ -71,10 +74,25 @@ it('folgt einer verschobenen Zuordnung', function () {
Queue::assertPushedOn(MailLane::URGENT, Illuminate\Mail\SendQueuedMailable::class);
});
/**
* Rekursiv statt `glob('Mail/*.php')`: `app/Mail` hat mit `Concerns/` und
* `Transport/` schon zwei Unterordner und wird eher mehr als weniger. Eine
* fünfzehnte Mailklasse in einem Unterordner sähe der alte `glob()`-Aufruf
* nie der Wächter schwiege genau in dem Moment, in dem er gebraucht wird.
*
* Eingeschränkt auf echte Mailables (Unterklassen von
* `Illuminate\Mail\Mailable`) statt auf „liegt irgendwo unter app/Mail":
* ein Trait wie `RidesALane` selbst, oder eine künftige Basisklasse, ist
* keine versendbare Mail, braucht keine Spur und soll den Test nicht rot
* machen, nur weil sie im selben Ordner liegt.
*/
it('lässt keine Mailklasse ohne Spur', function () {
$missing = collect(glob(app_path('Mail/*.php')))
->map(fn (string $path) => 'App\\Mail\\'.basename($path, '.php'))
->reject(fn (string $class) => in_array(App\Mail\Concerns\RidesALane::class, class_uses_recursive($class), true))
$missing = collect(File::allFiles(app_path('Mail')))
->map(fn ($file) => 'App\\Mail\\'.str_replace(
[DIRECTORY_SEPARATOR, '.php'], ['\\', ''], $file->getRelativePathname()
))
->filter(fn (string $class) => is_subclass_of($class, Mailable::class))
->reject(fn (string $class) => in_array(RidesALane::class, class_uses_recursive($class), true))
->values()
->all();