From ce5692cb3036ec47c4f82ae57aaadfd58e6c14a4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?V=C3=ADctor=20Falc=C3=B3n?= Date: Mon, 6 Apr 2026 11:16:47 +0100 Subject: [PATCH] fix: split drip and default email senders (#263) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Summary - route drip mailables through `Álvaro and Víctor ` and send the rest from `Whisper Money ` - remove legacy per-mailable `Victor` sender overrides so non-drip mail falls back to the default sender consistently - add focused sender coverage for drip, non-drip, and verification mail paths ## Testing - `php artisan test --compact tests/Feature/MailSenderTest.php tests/Feature/Jobs/Drip/SendWelcomeEmailJobTest.php` --- .env.example | 4 +- .env.production.example | 4 +- app/Mail/BankTransactionsSyncedEmail.php | 2 +- app/Mail/BankingConnectionAuthFailedEmail.php | 2 +- app/Mail/BrokenBankLogosReportEmail.php | 2 +- app/Mail/Drip/FeedbackEmail.php | 7 +- app/Mail/Drip/ImportHelpEmail.php | 7 +- app/Mail/Drip/OnboardingReminderEmail.php | 7 +- app/Mail/Drip/PromoCodeEmail.php | 9 +- app/Mail/Drip/SubscriptionCancelledEmail.php | 7 +- app/Mail/Drip/WelcomeEmail.php | 7 +- app/Mail/UpdateEmail.php | 2 +- app/Mail/WaitlistOvertaken.php | 5 +- app/Mail/WaitlistReferralNotification.php | 5 +- app/Mail/WaitlistWelcome.php | 5 +- app/Notifications/VerifyEmailNotification.php | 1 - config/mail.php | 9 +- templates/coolify/whisper-money.yaml | 128 +++++++++------- .../Jobs/Drip/SendWelcomeEmailJobTest.php | 3 +- tests/Feature/MailSenderTest.php | 141 ++++++++++++++++++ 20 files changed, 279 insertions(+), 78 deletions(-) create mode 100644 tests/Feature/MailSenderTest.php diff --git a/.env.example b/.env.example index 13e91b84..bef3de4f 100644 --- a/.env.example +++ b/.env.example @@ -51,8 +51,10 @@ MAIL_HOST=127.0.0.1 MAIL_PORT=1025 MAIL_USERNAME=null MAIL_PASSWORD=null -MAIL_FROM_ADDRESS="hi@whisper.money" +MAIL_FROM_ADDRESS="no-reply@whisper.money" MAIL_FROM_NAME="Whisper Money" +MAIL_DRIP_FROM_ADDRESS="hi@whisper.money" +MAIL_DRIP_FROM_NAME="Álvaro and Víctor" ADMIN_EMAIL= # Resend Email Service (set MAIL_MAILER=resend to use in production) diff --git a/.env.production.example b/.env.production.example index 1778e1ac..62acb956 100644 --- a/.env.production.example +++ b/.env.production.example @@ -43,8 +43,10 @@ REDIS_PORT=6379 # Email - Resend (Recommended for production) MAIL_MAILER=resend RESEND_API_KEY=your-resend-api-key -MAIL_FROM_ADDRESS=hi@your-domain.com +MAIL_FROM_ADDRESS=no-reply@your-domain.com MAIL_FROM_NAME="Whisper Money" +MAIL_DRIP_FROM_ADDRESS=hi@your-domain.com +MAIL_DRIP_FROM_NAME="Álvaro and Víctor" # Stripe (Optional - for subscriptions) STRIPE_KEY= diff --git a/app/Mail/BankTransactionsSyncedEmail.php b/app/Mail/BankTransactionsSyncedEmail.php index 43bd1b04..4e85c8bd 100644 --- a/app/Mail/BankTransactionsSyncedEmail.php +++ b/app/Mail/BankTransactionsSyncedEmail.php @@ -44,7 +44,7 @@ class BankTransactionsSyncedEmail extends Mailable implements ShouldQueue { return new Envelope( subject: __(':count new transactions synced on Whisper Money', ['count' => $this->totalTransactions]), - )->from(config('mail.from.address', 'hello@example.com'), 'Victor'); + ); } public function content(): Content diff --git a/app/Mail/BankingConnectionAuthFailedEmail.php b/app/Mail/BankingConnectionAuthFailedEmail.php index bdabf3d0..ab0787f7 100644 --- a/app/Mail/BankingConnectionAuthFailedEmail.php +++ b/app/Mail/BankingConnectionAuthFailedEmail.php @@ -43,7 +43,7 @@ class BankingConnectionAuthFailedEmail extends Mailable implements ShouldQueue subject: __('Action required: :provider connection needs attention', [ 'provider' => $this->bankingConnection->aspsp_name, ]), - )->from(config('mail.from.address', 'hello@example.com'), 'Victor'); + ); } public function content(): Content diff --git a/app/Mail/BrokenBankLogosReportEmail.php b/app/Mail/BrokenBankLogosReportEmail.php index 277db385..bc149c3a 100644 --- a/app/Mail/BrokenBankLogosReportEmail.php +++ b/app/Mail/BrokenBankLogosReportEmail.php @@ -25,7 +25,7 @@ class BrokenBankLogosReportEmail extends Mailable return new Envelope( subject: trans_choice('Weekly bank logo audit: :count broken logo|Weekly bank logo audit: :count broken logos', $updatedCount, ['count' => $updatedCount]), - )->from(config('mail.from.address', 'hello@example.com'), 'Victor'); + ); } public function content(): Content diff --git a/app/Mail/Drip/FeedbackEmail.php b/app/Mail/Drip/FeedbackEmail.php index aaad53e0..37c7f2ee 100644 --- a/app/Mail/Drip/FeedbackEmail.php +++ b/app/Mail/Drip/FeedbackEmail.php @@ -6,6 +6,7 @@ use App\Models\User; use Illuminate\Bus\Queueable; use Illuminate\Contracts\Queue\ShouldQueue; use Illuminate\Mail\Mailable; +use Illuminate\Mail\Mailables\Address; use Illuminate\Mail\Mailables\Content; use Illuminate\Mail\Mailables\Envelope; use Illuminate\Queue\Middleware\RateLimited; @@ -37,8 +38,12 @@ class FeedbackEmail extends Mailable implements ShouldQueue public function envelope(): Envelope { return new Envelope( + from: new Address( + config('mail.drip_from.address', 'hi@whisper.money'), + config('mail.drip_from.name', 'Álvaro and Víctor'), + ), subject: __("How's Your Experience So Far?"), - )->from(config('mail.from.address', 'hello@example.com'), 'Victor'); + ); } public function content(): Content diff --git a/app/Mail/Drip/ImportHelpEmail.php b/app/Mail/Drip/ImportHelpEmail.php index 34c4e250..f4825f50 100644 --- a/app/Mail/Drip/ImportHelpEmail.php +++ b/app/Mail/Drip/ImportHelpEmail.php @@ -6,6 +6,7 @@ use App\Models\User; use Illuminate\Bus\Queueable; use Illuminate\Contracts\Queue\ShouldQueue; use Illuminate\Mail\Mailable; +use Illuminate\Mail\Mailables\Address; use Illuminate\Mail\Mailables\Content; use Illuminate\Mail\Mailables\Envelope; use Illuminate\Queue\Middleware\RateLimited; @@ -37,8 +38,12 @@ class ImportHelpEmail extends Mailable implements ShouldQueue public function envelope(): Envelope { return new Envelope( + from: new Address( + config('mail.drip_from.address', 'hi@whisper.money'), + config('mail.drip_from.name', 'Álvaro and Víctor'), + ), subject: __("Let's Import Your Transactions"), - )->from(config('mail.from.address', 'hello@example.com'), 'Victor'); + ); } public function content(): Content diff --git a/app/Mail/Drip/OnboardingReminderEmail.php b/app/Mail/Drip/OnboardingReminderEmail.php index d2beea99..f444d891 100644 --- a/app/Mail/Drip/OnboardingReminderEmail.php +++ b/app/Mail/Drip/OnboardingReminderEmail.php @@ -6,6 +6,7 @@ use App\Models\User; use Illuminate\Bus\Queueable; use Illuminate\Contracts\Queue\ShouldQueue; use Illuminate\Mail\Mailable; +use Illuminate\Mail\Mailables\Address; use Illuminate\Mail\Mailables\Content; use Illuminate\Mail\Mailables\Envelope; use Illuminate\Queue\Middleware\RateLimited; @@ -37,8 +38,12 @@ class OnboardingReminderEmail extends Mailable implements ShouldQueue public function envelope(): Envelope { return new Envelope( + from: new Address( + config('mail.drip_from.address', 'hi@whisper.money'), + config('mail.drip_from.name', 'Álvaro and Víctor'), + ), subject: __('Need Help Getting Started?'), - )->from(config('mail.from.address', 'hello@example.com'), 'Victor'); + ); } public function content(): Content diff --git a/app/Mail/Drip/PromoCodeEmail.php b/app/Mail/Drip/PromoCodeEmail.php index 14cbb635..e2090472 100644 --- a/app/Mail/Drip/PromoCodeEmail.php +++ b/app/Mail/Drip/PromoCodeEmail.php @@ -6,6 +6,7 @@ use App\Models\User; use Illuminate\Bus\Queueable; use Illuminate\Contracts\Queue\ShouldQueue; use Illuminate\Mail\Mailable; +use Illuminate\Mail\Mailables\Address; use Illuminate\Mail\Mailables\Content; use Illuminate\Mail\Mailables\Envelope; use Illuminate\Queue\Middleware\RateLimited; @@ -36,9 +37,13 @@ class PromoCodeEmail extends Mailable implements ShouldQueue public function envelope(): Envelope { - return (new Envelope( + return new Envelope( + from: new Address( + config('mail.drip_from.address', 'hi@whisper.money'), + config('mail.drip_from.name', 'Álvaro and Víctor'), + ), subject: __('Your Founder Discount - 80% Off First Period') - ))->from(config('mail.from.address', 'hello@example.com'), 'Victor'); + ); } public function content(): Content diff --git a/app/Mail/Drip/SubscriptionCancelledEmail.php b/app/Mail/Drip/SubscriptionCancelledEmail.php index 8dd96b92..d1637c63 100644 --- a/app/Mail/Drip/SubscriptionCancelledEmail.php +++ b/app/Mail/Drip/SubscriptionCancelledEmail.php @@ -6,6 +6,7 @@ use App\Models\User; use Illuminate\Bus\Queueable; use Illuminate\Contracts\Queue\ShouldQueue; use Illuminate\Mail\Mailable; +use Illuminate\Mail\Mailables\Address; use Illuminate\Mail\Mailables\Content; use Illuminate\Mail\Mailables\Envelope; use Illuminate\Queue\Middleware\RateLimited; @@ -37,8 +38,12 @@ class SubscriptionCancelledEmail extends Mailable implements ShouldQueue public function envelope(): Envelope { return new Envelope( + from: new Address( + config('mail.drip_from.address', 'hi@whisper.money'), + config('mail.drip_from.name', 'Álvaro and Víctor'), + ), subject: __("We're sorry to see you go"), - )->from(config('mail.from.address', 'hello@example.com'), 'Victor'); + ); } public function content(): Content diff --git a/app/Mail/Drip/WelcomeEmail.php b/app/Mail/Drip/WelcomeEmail.php index 3de9f5f4..bea10763 100644 --- a/app/Mail/Drip/WelcomeEmail.php +++ b/app/Mail/Drip/WelcomeEmail.php @@ -6,6 +6,7 @@ use App\Models\User; use Illuminate\Bus\Queueable; use Illuminate\Contracts\Queue\ShouldQueue; use Illuminate\Mail\Mailable; +use Illuminate\Mail\Mailables\Address; use Illuminate\Mail\Mailables\Content; use Illuminate\Mail\Mailables\Envelope; use Illuminate\Queue\Middleware\RateLimited; @@ -37,8 +38,12 @@ class WelcomeEmail extends Mailable implements ShouldQueue public function envelope(): Envelope { return new Envelope( + from: new Address( + config('mail.drip_from.address', 'hi@whisper.money'), + config('mail.drip_from.name', 'Álvaro and Víctor'), + ), subject: __('Welcome to Whisper Money - Your Privacy-First Finance App'), - )->from(config('mail.from.address', 'hello@example.com'), 'Victor'); + ); } public function content(): Content diff --git a/app/Mail/UpdateEmail.php b/app/Mail/UpdateEmail.php index 01633f9e..e908481b 100644 --- a/app/Mail/UpdateEmail.php +++ b/app/Mail/UpdateEmail.php @@ -41,7 +41,7 @@ class UpdateEmail extends Mailable implements ShouldQueue { return new Envelope( subject: $this->emailSubject, - )->from(config('mail.from.address', 'hello@example.com'), 'Victor'); + ); } public function content(): Content diff --git a/app/Mail/WaitlistOvertaken.php b/app/Mail/WaitlistOvertaken.php index 4ba7cdd0..ab99992b 100644 --- a/app/Mail/WaitlistOvertaken.php +++ b/app/Mail/WaitlistOvertaken.php @@ -45,7 +45,10 @@ class WaitlistOvertaken extends Mailable implements ShouldQueue public function envelope(): Envelope { return new Envelope( - from: new Address('victor@whisper.money', 'Victor & Alvaro from Whisper Money'), + from: new Address( + config('mail.from.address', 'no-reply@whisper.money'), + config('mail.from.name', 'Whisper Money'), + ), subject: __('Someone just overtook you in the Whisper Money queue!'), ); } diff --git a/app/Mail/WaitlistReferralNotification.php b/app/Mail/WaitlistReferralNotification.php index f95b442f..82fc420a 100644 --- a/app/Mail/WaitlistReferralNotification.php +++ b/app/Mail/WaitlistReferralNotification.php @@ -45,7 +45,10 @@ class WaitlistReferralNotification extends Mailable implements ShouldQueue public function envelope(): Envelope { return new Envelope( - from: new Address('victor@whisper.money', 'Victor & Alvaro from Whisper Money'), + from: new Address( + config('mail.from.address', 'no-reply@whisper.money'), + config('mail.from.name', 'Whisper Money'), + ), subject: __('Someone just joined Whisper Money with your link!'), ); } diff --git a/app/Mail/WaitlistWelcome.php b/app/Mail/WaitlistWelcome.php index b5c4e66b..3c2edcf1 100644 --- a/app/Mail/WaitlistWelcome.php +++ b/app/Mail/WaitlistWelcome.php @@ -46,7 +46,10 @@ class WaitlistWelcome extends Mailable implements ShouldQueue public function envelope(): Envelope { return new Envelope( - from: new Address('victor@whisper.money', 'Victor from Whisper Money'), + from: new Address( + config('mail.from.address', 'no-reply@whisper.money'), + config('mail.from.name', 'Whisper Money'), + ), subject: __("You're on the Whisper Money waiting list!"), ); } diff --git a/app/Notifications/VerifyEmailNotification.php b/app/Notifications/VerifyEmailNotification.php index a0aef5e7..08e0cd50 100644 --- a/app/Notifications/VerifyEmailNotification.php +++ b/app/Notifications/VerifyEmailNotification.php @@ -12,7 +12,6 @@ class VerifyEmailNotification extends VerifyEmail $verificationUrl = $this->verificationUrl($notifiable); return (new MailMessage) - ->from(config('mail.from.address', 'hello@example.com'), 'Victor') ->subject(__('Verify Your Email - Whisper Money')) ->markdown('mail.verify-email', [ 'userName' => $notifiable->name, diff --git a/config/mail.php b/config/mail.php index f76c4557..ed53f95f 100644 --- a/config/mail.php +++ b/config/mail.php @@ -139,8 +139,13 @@ return [ */ 'from' => [ - 'address' => env('MAIL_FROM_ADDRESS', 'hello@example.com'), - 'name' => env('MAIL_FROM_NAME', 'Example'), + 'address' => env('MAIL_FROM_ADDRESS', 'no-reply@whisper.money'), + 'name' => env('MAIL_FROM_NAME', 'Whisper Money'), + ], + + 'drip_from' => [ + 'address' => env('MAIL_DRIP_FROM_ADDRESS', 'hi@whisper.money'), + 'name' => env('MAIL_DRIP_FROM_NAME', 'Álvaro and Víctor'), ], ]; diff --git a/templates/coolify/whisper-money.yaml b/templates/coolify/whisper-money.yaml index 42a11150..e5e53561 100644 --- a/templates/coolify/whisper-money.yaml +++ b/templates/coolify/whisper-money.yaml @@ -5,64 +5,76 @@ # port: 80 services: - whisper-money: - image: ghcr.io/whisper-money/whisper-money:latest - environment: - - SERVICE_FQDN_WHISPERMONEY_80 - - APP_NAME=Whisper Money - - APP_ENV=production - - APP_DEBUG=false - - APP_URL=${SERVICE_FQDN_WHISPERMONEY_80} - # APP_KEY is auto-generated on first startup if not provided - - DB_CONNECTION=mysql - - DB_HOST=mysql - - DB_PORT=3306 - - DB_DATABASE=${MYSQL_DATABASE:-whisper_money} - - DB_USERNAME=${SERVICE_USER_MYSQL} - - DB_PASSWORD=${SERVICE_PASSWORD_MYSQL} - - CACHE_STORE=redis - - SESSION_DRIVER=redis - - QUEUE_CONNECTION=database - - REDIS_HOST=127.0.0.1 - - HIDE_AUTH_BUTTONS=${HIDE_AUTH_BUTTONS:-false} - - MAIL_MAILER=${MAIL_MAILER:-log} - - MAIL_HOST=${MAIL_HOST:-} - - MAIL_PORT=${MAIL_PORT:-587} - - MAIL_USERNAME=${MAIL_USERNAME:-} - - MAIL_PASSWORD=${MAIL_PASSWORD:-} - - MAIL_FROM_ADDRESS=${MAIL_FROM_ADDRESS:-hi@whisper.money} - - MAIL_FROM_NAME=${MAIL_FROM_NAME:-Whisper Money} - - RESEND_API_KEY=${RESEND_API_KEY:-} - - STRIPE_KEY=${STRIPE_KEY:-} - - STRIPE_SECRET=${STRIPE_SECRET:-} - - STRIPE_WEBHOOK_SECRET=${STRIPE_WEBHOOK_SECRET:-} - - SUBSCRIPTIONS_ENABLED=${SUBSCRIPTIONS_ENABLED:-false} - volumes: - - whisper-money-storage:/app/storage - depends_on: - mysql: - condition: service_healthy - healthcheck: - test: ["CMD", "curl", "-f", "http://localhost/up"] - interval: 30s - timeout: 10s - retries: 3 + whisper-money: + image: ghcr.io/whisper-money/whisper-money:latest + environment: + - SERVICE_FQDN_WHISPERMONEY_80 + - APP_NAME=Whisper Money + - APP_ENV=production + - APP_DEBUG=false + - APP_URL=${SERVICE_FQDN_WHISPERMONEY_80} + # APP_KEY is auto-generated on first startup if not provided + - DB_CONNECTION=mysql + - DB_HOST=mysql + - DB_PORT=3306 + - DB_DATABASE=${MYSQL_DATABASE:-whisper_money} + - DB_USERNAME=${SERVICE_USER_MYSQL} + - DB_PASSWORD=${SERVICE_PASSWORD_MYSQL} + - CACHE_STORE=redis + - SESSION_DRIVER=redis + - QUEUE_CONNECTION=database + - REDIS_HOST=127.0.0.1 + - HIDE_AUTH_BUTTONS=${HIDE_AUTH_BUTTONS:-false} + - MAIL_MAILER=${MAIL_MAILER:-log} + - MAIL_HOST=${MAIL_HOST:-} + - MAIL_PORT=${MAIL_PORT:-587} + - MAIL_USERNAME=${MAIL_USERNAME:-} + - MAIL_PASSWORD=${MAIL_PASSWORD:-} + - MAIL_FROM_ADDRESS=${MAIL_FROM_ADDRESS:-no-reply@whisper.money} + - MAIL_FROM_NAME=${MAIL_FROM_NAME:-Whisper Money} + - MAIL_DRIP_FROM_ADDRESS=${MAIL_DRIP_FROM_ADDRESS:-hi@whisper.money} + - MAIL_DRIP_FROM_NAME=${MAIL_DRIP_FROM_NAME:-Álvaro and Víctor} + - RESEND_API_KEY=${RESEND_API_KEY:-} + - STRIPE_KEY=${STRIPE_KEY:-} + - STRIPE_SECRET=${STRIPE_SECRET:-} + - STRIPE_WEBHOOK_SECRET=${STRIPE_WEBHOOK_SECRET:-} + - SUBSCRIPTIONS_ENABLED=${SUBSCRIPTIONS_ENABLED:-false} + volumes: + - whisper-money-storage:/app/storage + depends_on: + mysql: + condition: service_healthy + healthcheck: + test: ['CMD', 'curl', '-f', 'http://localhost/up'] + interval: 30s + timeout: 10s + retries: 3 - mysql: - image: mysql:8.0 - environment: - - MYSQL_ROOT_PASSWORD=${SERVICE_PASSWORD_MYSQLROOT} - - MYSQL_DATABASE=${MYSQL_DATABASE:-whisper_money} - - MYSQL_USER=${SERVICE_USER_MYSQL} - - MYSQL_PASSWORD=${SERVICE_PASSWORD_MYSQL} - volumes: - - whisper-money-mysql:/var/lib/mysql - healthcheck: - test: ["CMD", "mysqladmin", "ping", "-h", "localhost", "-u", "root", "-p${SERVICE_PASSWORD_MYSQLROOT}"] - interval: 5s - timeout: 5s - retries: 10 + mysql: + image: mysql:8.0 + environment: + - MYSQL_ROOT_PASSWORD=${SERVICE_PASSWORD_MYSQLROOT} + - MYSQL_DATABASE=${MYSQL_DATABASE:-whisper_money} + - MYSQL_USER=${SERVICE_USER_MYSQL} + - MYSQL_PASSWORD=${SERVICE_PASSWORD_MYSQL} + volumes: + - whisper-money-mysql:/var/lib/mysql + healthcheck: + test: + [ + 'CMD', + 'mysqladmin', + 'ping', + '-h', + 'localhost', + '-u', + 'root', + '-p${SERVICE_PASSWORD_MYSQLROOT}', + ] + interval: 5s + timeout: 5s + retries: 10 volumes: - whisper-money-storage: - whisper-money-mysql: + whisper-money-storage: + whisper-money-mysql: diff --git a/tests/Feature/Jobs/Drip/SendWelcomeEmailJobTest.php b/tests/Feature/Jobs/Drip/SendWelcomeEmailJobTest.php index bdb409df..a9f83fd1 100644 --- a/tests/Feature/Jobs/Drip/SendWelcomeEmailJobTest.php +++ b/tests/Feature/Jobs/Drip/SendWelcomeEmailJobTest.php @@ -17,7 +17,8 @@ test('welcome email is sent and logged', function () { SendWelcomeEmailJob::dispatchSync($user); Mail::assertQueued(WelcomeEmail::class, function ($mail) use ($user) { - return $mail->hasTo($user->email); + return $mail->hasTo($user->email) + && $mail->hasFrom('hi@whisper.money', 'Álvaro and Víctor'); }); $this->assertDatabaseHas('user_mail_logs', [ diff --git a/tests/Feature/MailSenderTest.php b/tests/Feature/MailSenderTest.php new file mode 100644 index 00000000..e25ff737 --- /dev/null +++ b/tests/Feature/MailSenderTest.php @@ -0,0 +1,141 @@ + 'array', + 'mail.from.address' => 'no-reply@whisper.money', + 'mail.from.name' => 'Whisper Money', + 'mail.drip_from.address' => 'hi@whisper.money', + 'mail.drip_from.name' => 'Álvaro and Víctor', + ]); + + $viewPath = resource_path('views/mail/updates'); + + if (! File::exists($viewPath)) { + File::makeDirectory($viewPath, 0755, true); + } + + File::put(resource_path('views/mail/updates/test-update.blade.php'), <<<'BLADE' + +# Test Update + +Hello {{ $user->name }}, + +Test update. + +BLADE); +}); + +afterEach(function () { + $testViewPath = resource_path('views/mail/updates/test-update.blade.php'); + + if (File::exists($testViewPath)) { + File::delete($testViewPath); + } +}); + +function lastSentMailMessage(): object +{ + /** @var Mailer $mailer */ + $mailer = Mail::mailer('array'); + /** @var ArrayTransport $transport */ + $transport = $mailer->getSymfonyTransport(); + + return $transport->messages()->last(); +} + +function sendWithArrayMailer($mailable): void +{ + /** @var Mailer $mailer */ + $mailer = Mail::mailer('array'); + /** @var ArrayTransport $transport */ + $transport = $mailer->getSymfonyTransport(); + $transport->flush(); + + $mailer->to('recipient@example.com')->sendNow($mailable); +} + +test('drip mailables use the drip sender', function (string $mailableClass) { + $user = User::factory()->create(); + + $mailable = match ($mailableClass) { + WelcomeEmail::class => new WelcomeEmail($user), + FeedbackEmail::class => new FeedbackEmail($user), + ImportHelpEmail::class => new ImportHelpEmail($user), + OnboardingReminderEmail::class => new OnboardingReminderEmail($user), + PromoCodeEmail::class => new PromoCodeEmail($user), + SubscriptionCancelledEmail::class => new SubscriptionCancelledEmail($user), + }; + + expect($mailable->envelope()->from)->toEqual(new Address('hi@whisper.money', 'Álvaro and Víctor')); +})->with([ + WelcomeEmail::class, + FeedbackEmail::class, + ImportHelpEmail::class, + OnboardingReminderEmail::class, + PromoCodeEmail::class, + SubscriptionCancelledEmail::class, +]); + +test('default sender is used for active non-drip mailables', function (string $mailableClass) { + $user = User::factory()->create(); + + $mailable = match ($mailableClass) { + UpdateEmail::class => new UpdateEmail($user, 'test-update'), + BankTransactionsSyncedEmail::class => new BankTransactionsSyncedEmail($user, 3, ['Test Bank' => 3]), + BankingConnectionAuthFailedEmail::class => new BankingConnectionAuthFailedEmail($user, BankingConnection::factory()->for($user)->create(['aspsp_name' => 'Test Bank'])), + BrokenBankLogosReportEmail::class => new BrokenBankLogosReportEmail([['id' => 'bank-1', 'name' => 'Test Bank', 'previous_logo' => 'https://example.com/logo.png']]), + WaitlistWelcome::class => new WaitlistWelcome(UserLead::factory()->create()), + WaitlistReferralNotification::class => new WaitlistReferralNotification(UserLead::factory()->create()), + WaitlistOvertaken::class => new WaitlistOvertaken(UserLead::factory()->create()), + }; + + sendWithArrayMailer($mailable); + + $from = lastSentMailMessage()->getOriginalMessage()->getFrom()[0]; + + expect($from->getAddress())->toBe('no-reply@whisper.money') + ->and($from->getName())->toBe('Whisper Money'); +})->with([ + UpdateEmail::class, + BankTransactionsSyncedEmail::class, + BankingConnectionAuthFailedEmail::class, + BrokenBankLogosReportEmail::class, + WaitlistWelcome::class, + WaitlistReferralNotification::class, + WaitlistOvertaken::class, +]); + +test('verification notification uses the default sender', function () { + $user = User::factory()->unverified()->create(); + + $user->notify(new VerifyEmailNotification); + + $from = lastSentMailMessage()->getOriginalMessage()->getFrom()[0]; + + expect($from->getAddress())->toBe('no-reply@whisper.money') + ->and($from->getName())->toBe('Whisper Money'); +});