create(); $workspace = Workspace::factory()->create(); $workspace->users()->attach($user->id, [ 'role' => $role, 'permissions' => [], ]); session(['current_workspace_id' => $workspace->id]); return [$user, $workspace]; } // ── NotificationType Enum ────────────────────────────────── test('notification type enum has all expected values', function () { expect(NotificationType::getValues())->toContain('nudge') ->toContain('declaration_overdue') ->toContain('document_uploaded') ->toContain('bulk_notification') ->toContain('status_changed'); expect(NotificationType::getValues())->toHaveCount(5); }); // ── NudgeNotification ────────────────────────────────────── test('nudge notification sends via database and mail channels', function () { Notification::fake(); [$user, $workspace] = setupNotificationUser(); $declaration = Declaration::factory()->forWorkspace($workspace)->create(); $recipient = User::factory()->create(); $workspace->users()->attach($recipient->id, ['role' => 'worker', 'permissions' => []]); $recipient->notify(new NudgeNotification($declaration, $user)); Notification::assertSentTo($recipient, NudgeNotification::class, function ($notification, $channels) { return in_array('database', $channels) && in_array('mail', $channels); }); }); test('nudge notification payload includes workspace_id and notification_type', function () { [$user, $workspace] = setupNotificationUser(); $declaration = Declaration::factory()->forWorkspace($workspace)->create(); $notification = new NudgeNotification($declaration, $user); $payload = $notification->toDatabase($user); expect($payload)->toHaveKey('workspace_id', $workspace->id) ->toHaveKey('declaration_id', $declaration->id) ->toHaveKey('sender_id', $user->id) ->toHaveKey('notification_type', NotificationType::Nudge); }); test('nudge notification toMail returns NudgeNotificationMail', function () { [$user, $workspace] = setupNotificationUser(); $declaration = Declaration::factory()->forWorkspace($workspace)->create(); $notification = new NudgeNotification($declaration, $user); $mailable = $notification->toMail($user); expect($mailable)->toBeInstanceOf(NudgeNotificationMail::class); }); // ── DocumentUploadedNotification ──────────────────────────── test('document uploaded notification sends via database channel only', function () { Notification::fake(); [$user, $workspace] = setupNotificationUser(); $declaration = Declaration::factory()->forWorkspace($workspace)->create(); $user->notify(new DocumentUploadedNotification($declaration, $declaration->client_id)); Notification::assertSentTo($user, DocumentUploadedNotification::class, function ($notification, $channels) { return $channels === ['database']; }); }); test('document uploaded notification payload includes workspace_id and notification_type', function () { [$user, $workspace] = setupNotificationUser(); $declaration = Declaration::factory()->forWorkspace($workspace)->create(); $notification = new DocumentUploadedNotification($declaration, $declaration->client_id); $payload = $notification->toDatabase($user); expect($payload)->toHaveKey('workspace_id', $workspace->id) ->toHaveKey('declaration_id', $declaration->id) ->toHaveKey('client_id', $declaration->client_id) ->toHaveKey('notification_type', NotificationType::DocumentUploaded); }); // ── DeclarationOverdueNotification ────────────────────────── test('declaration overdue notification sends via database and mail channels', function () { Notification::fake(); [$user, $workspace] = setupNotificationUser(); $declaration = Declaration::factory()->forWorkspace($workspace)->create(); $user->notify(new DeclarationOverdueNotification($declaration)); Notification::assertSentTo($user, DeclarationOverdueNotification::class, function ($notification, $channels) { return in_array('database', $channels) && in_array('mail', $channels); }); }); test('declaration overdue notification payload includes workspace_id and notification_type', function () { [$user, $workspace] = setupNotificationUser(); $declaration = Declaration::factory()->forWorkspace($workspace)->create(); $notification = new DeclarationOverdueNotification($declaration); $payload = $notification->toDatabase($user); expect($payload)->toHaveKey('workspace_id', $workspace->id) ->toHaveKey('declaration_id', $declaration->id) ->toHaveKey('notification_type', NotificationType::DeclarationOverdue); }); test('declaration overdue notification has retry configuration', function () { $declaration = Declaration::factory()->make(); $notification = new DeclarationOverdueNotification($declaration); expect($notification->tries)->toBe(3); }); test('nudge notification has retry configuration', function () { $declaration = Declaration::factory()->make(); $sender = User::factory()->make(); $notification = new NudgeNotification($declaration, $sender); expect($notification->tries)->toBe(3); }); // ── NotificationController ────────────────────────────────── test('notification controller index returns paginated workspace-scoped notifications', function () { [$user, $workspace] = setupNotificationUser(); $declaration = Declaration::factory()->forWorkspace($workspace)->create(); // Create a notification in the database for this user $user->notify(new DocumentUploadedNotification($declaration, $declaration->client_id)); $response = $this->actingAs($user)->get(route('notifications.index')); $response->assertOk(); $response->assertInertia(fn (Assert $page) => $page ->component('notifications/Index') ->has('notifications') ->has('markAllReadUrl') ); }); test('notification controller markAsRead marks notification as read', function () { [$user, $workspace] = setupNotificationUser(); $declaration = Declaration::factory()->forWorkspace($workspace)->create(); $user->notify(new DocumentUploadedNotification($declaration, $declaration->client_id)); $notification = $user->notifications()->first(); expect($notification->read_at)->toBeNull(); $response = $this->actingAs($user)->post(route('notifications.read', $notification->id)); $response->assertRedirect(); expect($notification->fresh()->read_at)->not->toBeNull(); }); test('notification controller markAllAsRead marks all as read', function () { [$user, $workspace] = setupNotificationUser(); $declaration = Declaration::factory()->forWorkspace($workspace)->create(); $user->notify(new DocumentUploadedNotification($declaration, $declaration->client_id)); $user->notify(new DocumentUploadedNotification($declaration, $declaration->client_id)); expect($user->unreadNotifications)->toHaveCount(2); $response = $this->actingAs($user)->post(route('notifications.readAll')); $response->assertRedirect(); $user->refresh(); expect($user->unreadNotifications)->toHaveCount(0); }); test('unauthenticated users cannot access notification routes', function () { $this->get(route('notifications.index'))->assertRedirect(route('login')); $this->post(route('notifications.readAll'))->assertRedirect(route('login')); });