notifRepo = $this->createMock(NotificationRepositoryInterface::class); $this->service = new NotificationService($this->notifRepo); } public function testCreateForUserInsertsNotification(): void { $this->notifRepo->expects($this->once()) ->method('create') ->with($this->callback(function (array $data): bool { return $data['recipient_user_id'] === 5 && $data['tenant_id'] === 1 && $data['type'] === 'user.created' && $data['title'] === 'New user: Alice'; })) ->willReturn(42); $result = $this->service->createForUser(5, 1, 'user.created', 'New user: Alice', null, 'admin/users/edit/abc', []); $this->assertSame(42, $result); } public function testCreateForUserRejectsMissingFields(): void { $this->notifRepo->expects($this->never())->method('create'); $this->assertFalse($this->service->createForUser(0, 1, 'user.created', 'Test', null, null, [])); $this->assertFalse($this->service->createForUser(1, 1, '', 'Test', null, null, [])); $this->assertFalse($this->service->createForUser(1, 1, 'user.created', '', null, null, [])); } public function testUnreadCountReturnsCorrectCount(): void { $this->notifRepo->method('countUnreadByUser')->with(3)->willReturn(7); $this->assertSame(7, $this->service->unreadCount(3)); } public function testUnreadCountReturnsZeroForInvalidUser(): void { $this->notifRepo->expects($this->never())->method('countUnreadByUser'); $this->assertSame(0, $this->service->unreadCount(0)); } public function testMarkAsReadUpdatesState(): void { $this->notifRepo->expects($this->once()) ->method('markRead') ->with(10, 3) ->willReturn(true); $this->assertTrue($this->service->markAsRead(3, 10)); } public function testMarkAsReadRejectsInvalidIds(): void { $this->notifRepo->expects($this->never())->method('markRead'); $this->assertFalse($this->service->markAsRead(0, 10)); $this->assertFalse($this->service->markAsRead(3, 0)); } public function testMarkAllAsReadAffectsAllUserNotifications(): void { $this->notifRepo->expects($this->once()) ->method('markAllReadByUser') ->with(3) ->willReturn(5); $this->assertSame(5, $this->service->markAllAsRead(3)); } public function testDismissDeletesNotification(): void { $this->notifRepo->expects($this->once()) ->method('delete') ->with(10, 3) ->willReturn(true); $this->assertTrue($this->service->dismiss(3, 10)); } public function testPurgeOldReadRemovesExpiredOnly(): void { $this->notifRepo->expects($this->once()) ->method('purgeReadOlderThanDays') ->with(90) ->willReturn(12); $this->assertSame(12, $this->service->purgeOldRead(90)); } public function testDeleteAllForUserDelegates(): void { $this->notifRepo->expects($this->once()) ->method('deleteAllByUser') ->with(5) ->willReturn(3); $this->assertSame(3, $this->service->deleteAllForUser(5)); } public function testListForUserReturnsNotifications(): void { $expected = [ ['id' => 1, 'title' => 'Test', 'is_read' => 0], ['id' => 2, 'title' => 'Test 2', 'is_read' => 1], ]; $this->notifRepo->method('listByUser')->with(3, 50, 0)->willReturn($expected); $this->assertSame($expected, $this->service->listForUser(3)); } }