artisan('db:seed', ['--class' => 'PlanSeeder']); }); // ─── DispatchUserNotificationJob — sent logging ─────────────────────────────── it('logs a sent entry for each allowed channel', function (): void { // Free tier allows email (weekly_digest). User has email pref enabled. $user = User::factory()->create(); UserNotificationPreference::factory()->create([ 'user_id' => $user->id, 'channel' => 'email', 'fuel_type' => FuelType::E10->value, 'enabled' => true, ]); (new DispatchUserNotificationJob($user, 'price_threshold', FuelType::E10->value, price: 143.9))->handle(); $log = NotificationLog::where('user_id', $user->id)->where('channel', 'email')->first(); expect($log)->not->toBeNull() ->and($log->sent)->toBeTrue() ->and($log->trigger_type)->toBe('price_threshold') ->and($log->fuel_type)->toBe(FuelType::E10->value); }); it('actually dispatches FuelPriceAlert with the allowed channels', function (): void { Notification::fake(); $user = User::factory()->create(); UserNotificationPreference::factory()->create([ 'user_id' => $user->id, 'channel' => 'email', 'fuel_type' => FuelType::E10->value, 'enabled' => true, ]); (new DispatchUserNotificationJob($user, 'price_threshold', FuelType::E10->value, price: 143.9))->handle(); Notification::assertSentTo($user, FuelPriceAlert::class, function (FuelPriceAlert $n) { return $n->triggerType === 'price_threshold' && $n->fuelType === FuelType::E10->value && $n->price === 143.9 && in_array('email', $n->channels, true); }); }); it('does not dispatch FuelPriceAlert when no channels are allowed', function (): void { Notification::fake(); // Free user with no preferences — channelsFor returns [] $user = User::factory()->create(); (new DispatchUserNotificationJob($user, 'price_threshold', FuelType::E10->value))->handle(); Notification::assertNothingSent(); }); // ─── DispatchUserNotificationJob — tier_restricted logging ─────────────────── it('logs tier_restricted for channels the user wants but their tier forbids', function (): void { // Free tier: sms is disabled. User has sms pref enabled. $user = User::factory()->create(); UserNotificationPreference::factory()->create([ 'user_id' => $user->id, 'channel' => 'sms', 'fuel_type' => FuelType::E10->value, 'enabled' => true, ]); (new DispatchUserNotificationJob($user, 'price_threshold', FuelType::E10->value))->handle(); $log = NotificationLog::where('user_id', $user->id) ->where('channel', 'sms') ->first(); expect($log)->not->toBeNull() ->and($log->sent)->toBeFalse() ->and($log->missed_reason)->toBe('tier_restricted'); }); // ─── DispatchUserNotificationJob — daily_limit logging ─────────────────────── it('logs daily_limit when the channel is allowed but the limit is exhausted', function (): void { $user = User::factory()->create(); // Patch the free plan to allow sms with limit 1 Plan::where('name', 'free')->first()->update([ 'sms_enabled' => true, 'sms_daily_limit' => 1, ]); UserNotificationPreference::factory()->create([ 'user_id' => $user->id, 'channel' => 'sms', 'fuel_type' => FuelType::E10->value, 'enabled' => true, ]); // Pre-log one sent SMS to hit the daily limit NotificationLog::factory()->create([ 'user_id' => $user->id, 'channel' => 'sms', 'sent' => true, 'created_at' => now(), ]); (new DispatchUserNotificationJob($user, 'price_threshold', FuelType::E10->value))->handle(); $missed = NotificationLog::where('user_id', $user->id) ->where('channel', 'sms') ->where('sent', false) ->first(); expect($missed)->not->toBeNull() ->and($missed->missed_reason)->toBe('daily_limit'); }); // ─── DispatchUserNotificationJob — does not log user-disabled channels ──────── it('does not log channels the user has explicitly disabled', function (): void { $user = User::factory()->create(); Plan::where('name', 'free')->first()->update([ 'sms_enabled' => true, 'sms_daily_limit' => 3, ]); UserNotificationPreference::factory()->create([ 'user_id' => $user->id, 'channel' => 'sms', 'fuel_type' => FuelType::E10->value, 'enabled' => false, ]); (new DispatchUserNotificationJob($user, 'price_threshold', FuelType::E10->value))->handle(); expect(NotificationLog::where('user_id', $user->id)->where('channel', 'sms')->count())->toBe(0); }); // ─── DispatchUserNotificationJob — queued on notifications queue ────────────── it('is dispatched on the notifications queue', function (): void { Queue::fake(); $user = User::factory()->create(); DispatchUserNotificationJob::dispatch($user, 'price_threshold', FuelType::E10->value); Queue::assertPushedOn('notifications', DispatchUserNotificationJob::class); }); // ─── SendScheduledWhatsAppJob — dispatches per eligible user ───────────────── it('dispatches DispatchUserNotificationJob for eligible whatsapp users', function (): void { Queue::fake(); $user = User::factory()->create(); Plan::where('name', 'free')->first()->update([ 'whatsapp_enabled' => true, 'whatsapp_daily_limit' => 5, 'whatsapp_scheduled_updates' => 2, ]); UserNotificationPreference::factory()->create([ 'user_id' => $user->id, 'channel' => 'whatsapp', 'fuel_type' => FuelType::E10->value, 'enabled' => true, ]); (new SendScheduledWhatsAppJob('morning'))->handle(); Queue::assertPushedOn('notifications', DispatchUserNotificationJob::class); }); // ─── SendScheduledWhatsAppJob — skips users over daily limit ───────────────── it('skips users who have hit their whatsapp daily limit', function (): void { Queue::fake(); $user = User::factory()->create(); Plan::where('name', 'free')->first()->update([ 'whatsapp_enabled' => true, 'whatsapp_daily_limit' => 1, 'whatsapp_scheduled_updates' => 2, ]); UserNotificationPreference::factory()->create([ 'user_id' => $user->id, 'channel' => 'whatsapp', 'fuel_type' => FuelType::E10->value, 'enabled' => true, ]); NotificationLog::factory()->create([ 'user_id' => $user->id, 'channel' => 'whatsapp', 'sent' => true, 'created_at' => now(), ]); (new SendScheduledWhatsAppJob('evening'))->handle(); Queue::assertNothingPushed(); }); // ─── SendScheduledWhatsAppJob — correct trigger type per period ─────────────── it('passes scheduled_morning trigger for morning period', function (): void { Queue::fake(); $user = User::factory()->create(); Plan::where('name', 'free')->first()->update([ 'whatsapp_enabled' => true, 'whatsapp_daily_limit' => 5, 'whatsapp_scheduled_updates' => 2, ]); UserNotificationPreference::factory()->create([ 'user_id' => $user->id, 'channel' => 'whatsapp', 'fuel_type' => FuelType::E10->value, 'enabled' => true, ]); (new SendScheduledWhatsAppJob('morning'))->handle(); Queue::assertPushed(DispatchUserNotificationJob::class, function (DispatchUserNotificationJob $job): bool { return $job->triggerType === 'scheduled_morning'; }); });