create(['is_active' => true]); $admin->assignRole('admin'); return $admin; } test('the recipient filters cover legacy contracts and login windows', function () { $recipients = app(CampaignRecipients::class); $legacyUser = User::factory()->create(['name' => 'Legacy Lisa', 'last_login_at' => now()->subMonths(2)]); UserPaymentOption::factory()->create([ 'user_id' => $legacyUser->id, 'payment_option_id' => PaymentOption::factory()->create()->id, 'status' => UserPaymentOptionStatus::Grandfathered->value, 'grandfathered_until' => null, 'stripe_subscription_id' => null, ]); $recentUser = User::factory()->create(['name' => 'Recent Rita', 'last_login_at' => now()->subMonth()]); $dormantUser = User::factory()->create(['name' => 'Dormant Doris', 'last_login_at' => now()->subMonths(18)]); $neverLoggedIn = User::factory()->create(['name' => 'Nie Nora', 'last_login_at' => null]); $inactiveUser = User::factory()->create(['is_active' => false, 'last_login_at' => now()->subMonth()]); $unsubscribed = User::factory()->create(['name' => 'Abgemeldet Anna', 'last_login_at' => now()->subMonth()]); $unsubscribed->optOutOfEmailNotification(EmailNotificationType::Newsletter); // Grundmenge: aktiv, mit E-Mail, nicht abgemeldet. $all = $recipients->query([])->pluck('id'); expect($all)->toContain($legacyUser->id, $recentUser->id, $dormantUser->id, $neverLoggedIn->id) ->not->toContain($inactiveUser->id) ->not->toContain($unsubscribed->id); // Nur Legacy-Verträge (Bestandsschutz). expect($recipients->query(['legacy_contract' => true])->pluck('id')->all()) ->toBe([$legacyUser->id]); // In den letzten 6 Monaten eingeloggt. $within = $recipients->query(['login' => 'within', 'login_months' => 6])->pluck('id'); expect($within)->toContain($legacyUser->id, $recentUser->id) ->not->toContain($dormantUser->id) ->not->toContain($neverLoggedIn->id); // Komplement: seit 6 Monaten nicht (oder noch nie) eingeloggt. $notWithin = $recipients->query(['login' => 'not_within', 'login_months' => 6])->pluck('id'); expect($notWithin)->toContain($dormantUser->id, $neverLoggedIn->id) ->not->toContain($recentUser->id); // Sortierung nach letztem Login, älteste zuerst (NULL zuerst). $sorted = $recipients->query(['sort' => 'last_login_at', 'direction' => 'asc'])->pluck('id')->all(); expect($sorted[0])->toBe($neverLoggedIn->id) ->and(end($sorted))->toBe($recentUser->id); }); test('the campaign mail carries the body, salutation and a signed unsubscribe link', function () { $user = User::factory()->create(['name' => 'Empfang Emil']); $campaign = NewsletterCampaign::factory()->create([ 'subject' => 'Unser Portal ist umgezogen', 'body' => '
Hallo zusammen, es gibt Neuigkeiten.
', ]); $html = (new NewsletterCampaignMail($campaign, $user))->render(); expect($html) ->toContain('Hallo Empfang Emil') ->toContain('zusammen') ->not->toContain('