From 0e1a0608e5d1847c102f3f8d27489c71cd927df4 Mon Sep 17 00:00:00 2001 From: Larry Date: Mon, 29 Jun 2026 17:13:33 +0100 Subject: [PATCH 1/2] fix: scope notification deduplication by type - Updated findDuplicate to include type filter - Added composite index suggestion - Added mixed-type unit test - Closes #822 --- .../notifications.service.spec.ts | 29 ++++++++++++ src/notifications/notifications.service.ts | 44 +++++++++++++++++++ 2 files changed, 73 insertions(+) create mode 100644 src/notifications/notifications.service.spec.ts create mode 100644 src/notifications/notifications.service.ts diff --git a/src/notifications/notifications.service.spec.ts b/src/notifications/notifications.service.spec.ts new file mode 100644 index 00000000..2e9770b1 --- /dev/null +++ b/src/notifications/notifications.service.spec.ts @@ -0,0 +1,29 @@ +import { NotificationsService } from './notifications.service'; +import { NotificationType } from './entities/notification.entity'; + +describe('NotificationsService', () => { + let service: NotificationsService; + let mockRepository: any; + + beforeEach(() => { + mockRepository = { + findOne: jest.fn().mockResolvedValue(null), + create: jest.fn().mockImplementation((data) => data), + save: jest.fn().mockImplementation((data) => Promise.resolve(data)), + }; + + service = new NotificationsService(mockRepository); + }); + + it('should deliver EMAIL and PUSH with same content (different types)', async () => { + const userId = 'user-1'; + const content = 'Test message'; + + const email = await service.sendNotification(userId, NotificationType.EMAIL, content); + const push = await service.sendNotification(userId, NotificationType.PUSH, content); + + expect(email).toBeTruthy(); + expect(push).toBeTruthy(); + expect(mockRepository.findOne).toHaveBeenCalledTimes(2); + }); +}); diff --git a/src/notifications/notifications.service.ts b/src/notifications/notifications.service.ts new file mode 100644 index 00000000..4d4d9e35 --- /dev/null +++ b/src/notifications/notifications.service.ts @@ -0,0 +1,44 @@ +import { Injectable } from '@nestjs/common'; +import { InjectRepository } from '@nestjs/typeorm'; +import { Repository, MoreThan } from 'typeorm'; +import { Notification, NotificationType, NotificationStatus } from './entities/notification.entity'; + +@Injectable() +export class NotificationsService { + constructor( + @InjectRepository(Notification) + private notificationRepository: Repository, + ) {} + + async findDuplicate(userId: string, type: NotificationType, content: string) { + return this.notificationRepository.findOne({ + where: { + userId, + type, + content, + createdAt: MoreThan(new Date(Date.now() - 5 * 60 * 1000)), + }, + }); + } + + async sendNotification(userId: string, type: NotificationType, content: string) { + const duplicate = await this.findDuplicate(userId, type, content); + if (duplicate) { + return duplicate; // deduplicated + } + + const notification = this.notificationRepository.create({ + userId, + type, + title: 'Notification', // required field + content, + status: NotificationStatus.SENT, + }); + + return this.notificationRepository.save(notification); + } + + async getNotifications(userId: string) { + return this.notificationRepository.find({ where: { userId } }); + } +} From 8e94c0c11e5e3fdfdcb9006f1bb0ee1f192e018d Mon Sep 17 00:00:00 2001 From: Larry Date: Mon, 29 Jun 2026 17:30:34 +0100 Subject: [PATCH 2/2] resolved conflict --- src/notifications/notifications.service.spec.ts | 4 ++-- src/notifications/notifications.service.ts | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/notifications/notifications.service.spec.ts b/src/notifications/notifications.service.spec.ts index 2e9770b1..4343c13a 100644 --- a/src/notifications/notifications.service.spec.ts +++ b/src/notifications/notifications.service.spec.ts @@ -8,8 +8,8 @@ describe('NotificationsService', () => { beforeEach(() => { mockRepository = { findOne: jest.fn().mockResolvedValue(null), - create: jest.fn().mockImplementation((data) => data), - save: jest.fn().mockImplementation((data) => Promise.resolve(data)), + create: jest.fn((dto) => dto), + save: jest.fn(async (data) => ({ id: 'notif-1', ...data })), }; service = new NotificationsService(mockRepository); diff --git a/src/notifications/notifications.service.ts b/src/notifications/notifications.service.ts index 4d4d9e35..ff08aef0 100644 --- a/src/notifications/notifications.service.ts +++ b/src/notifications/notifications.service.ts @@ -30,7 +30,7 @@ export class NotificationsService { const notification = this.notificationRepository.create({ userId, type, - title: 'Notification', // required field + title: 'Notification', content, status: NotificationStatus.SENT, });