create(); BillingAddress::factory()->create(['user_id' => $user->id]); return UserPaymentOption::factory()->create([ 'user_id' => $user->id, 'payment_option_id' => PaymentOption::factory()->create()->id, 'status' => UserPaymentOptionStatus::Grandfathered->value, 'stripe_subscription_id' => null, 'grandfathered_until' => $until, 'cancelled_at' => null, ...$overrides, ]); } test('grandfathering past its contract end is ended and the customer is told once', function () { /** @var TestCase $this */ Mail::fake(); Carbon::setTestNow('2026-09-18 04:15:00'); $expired = grandfatheredAgreement('2026-09-17'); $endsToday = grandfatheredAgreement('2026-09-18'); $open = grandfatheredAgreement(null); $alreadyCancelled = grandfatheredAgreement('2026-09-01', ['status' => UserPaymentOptionStatus::Cancelled->value, 'cancelled_at' => now()->subDay()]); $this->artisan(ExpireGrandfathering::class)->assertSuccessful(); expect($expired->fresh()->status)->toBe(UserPaymentOptionStatus::Cancelled) ->and($expired->fresh()->cancelled_at)->not->toBeNull() ->and($endsToday->fresh()->status)->toBe(UserPaymentOptionStatus::Grandfathered) ->and($open->fresh()->status)->toBe(UserPaymentOptionStatus::Grandfathered); Mail::assertQueued(GrandfatheringExpired::class, fn (GrandfatheringExpired $mail) => $mail->user->is($expired->user)); Mail::assertQueuedCount(1); // Wiederholung ist wirkungslos. $this->artisan(ExpireGrandfathering::class)->assertSuccessful(); Mail::assertQueuedCount(1); expect($alreadyCancelled->fresh()->cancelled_at->toDateString())->toBe('2026-09-17'); Carbon::setTestNow(); });