"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const testing_1 = require("@nestjs/testing"); const typeorm_1 = require("@nestjs/typeorm"); const common_1 = require("@nestjs/common"); const notifications_service_1 = require("../services/notifications.service"); const entities_1 = require("../entities"); const email_1 = require("@modules/email"); describe('NotificationsService', () => { let service; let notificationRepository; let templateRepository; let preferenceRepository; let emailService; const mockTenantId = '550e8400-e29b-41d4-a716-446655440001'; const mockUserId = '550e8400-e29b-41d4-a716-446655440000'; const mockNotification = { id: 'notif-001', tenant_id: mockTenantId, user_id: mockUserId, type: 'info', channel: 'in_app', title: 'Test Notification', message: 'This is a test notification', is_read: false, delivery_status: 'pending', created_at: new Date(), }; const mockTemplate = { id: 'template-001', code: 'welcome_email', name: 'Welcome Email', channel: 'email', subject: 'Welcome {{userName}}!', body: 'Hello {{userName}}, welcome to our platform!', is_active: true, }; const mockPreferences = { id: 'pref-001', user_id: mockUserId, tenant_id: mockTenantId, email_enabled: true, push_enabled: true, in_app_enabled: true, sms_enabled: false, marketing_emails: true, product_updates: true, security_alerts: true, }; beforeEach(async () => { const mockNotificationRepo = { create: jest.fn(), save: jest.fn(), findOne: jest.fn(), findAndCount: jest.fn(), count: jest.fn(), update: jest.fn(), delete: jest.fn(), }; const mockTemplateRepo = { find: jest.fn(), findOne: jest.fn(), }; const mockPreferenceRepo = { findOne: jest.fn(), create: jest.fn(), save: jest.fn(), }; const mockEmailService = { sendEmail: jest.fn(), isEnabled: jest.fn(), }; const module = await testing_1.Test.createTestingModule({ providers: [ notifications_service_1.NotificationsService, { provide: (0, typeorm_1.getRepositoryToken)(entities_1.Notification), useValue: mockNotificationRepo }, { provide: (0, typeorm_1.getRepositoryToken)(entities_1.NotificationTemplate), useValue: mockTemplateRepo }, { provide: (0, typeorm_1.getRepositoryToken)(entities_1.UserNotificationPreference), useValue: mockPreferenceRepo }, { provide: email_1.EmailService, useValue: mockEmailService }, ], }).compile(); service = module.get(notifications_service_1.NotificationsService); notificationRepository = module.get((0, typeorm_1.getRepositoryToken)(entities_1.Notification)); templateRepository = module.get((0, typeorm_1.getRepositoryToken)(entities_1.NotificationTemplate)); preferenceRepository = module.get((0, typeorm_1.getRepositoryToken)(entities_1.UserNotificationPreference)); emailService = module.get(email_1.EmailService); }); afterEach(() => { jest.clearAllMocks(); }); describe('create', () => { const createDto = { userId: mockUserId, title: 'Test Notification', message: 'This is a test', type: 'info', channel: 'in_app', }; it('should create an in_app notification and mark as delivered', async () => { notificationRepository.create.mockReturnValue(mockNotification); notificationRepository.save.mockResolvedValue({ ...mockNotification, delivery_status: 'delivered', sent_at: new Date(), }); const result = await service.create(createDto, mockTenantId); expect(result).toHaveProperty('id'); expect(notificationRepository.create).toHaveBeenCalled(); expect(notificationRepository.save).toHaveBeenCalled(); }); it('should create an email notification and send via EmailService', async () => { const emailDto = { ...createDto, channel: 'email', email: 'user@example.com', userName: 'Test User', }; const emailNotification = { ...mockNotification, channel: 'email', delivery_status: 'pending', }; notificationRepository.create.mockReturnValue(emailNotification); notificationRepository.save.mockResolvedValue(emailNotification); emailService.sendEmail.mockResolvedValue({ success: true, messageId: 'msg-123', provider: 'sendgrid', }); const result = await service.create(emailDto, mockTenantId); expect(result).toHaveProperty('id'); expect(emailService.sendEmail).toHaveBeenCalledWith(expect.objectContaining({ to: { email: 'user@example.com', name: 'Test User' }, subject: emailDto.title, })); }); it('should handle email delivery failure gracefully', async () => { const emailDto = { ...createDto, channel: 'email', email: 'user@example.com', }; const emailNotification = { ...mockNotification, channel: 'email', delivery_status: 'pending', }; notificationRepository.create.mockReturnValue(emailNotification); notificationRepository.save.mockResolvedValue(emailNotification); emailService.sendEmail.mockResolvedValue({ success: false, provider: 'sendgrid', error: 'API error', }); const result = await service.create(emailDto, mockTenantId); expect(result).toHaveProperty('id'); expect(notificationRepository.save).toHaveBeenLastCalledWith(expect.objectContaining({ delivery_status: 'failed' })); }); }); describe('sendFromTemplate', () => { const templateDto = { userId: mockUserId, templateCode: 'welcome_email', variables: { userName: 'John' }, }; it('should send notification from template', async () => { templateRepository.findOne.mockResolvedValue(mockTemplate); notificationRepository.create.mockReturnValue(mockNotification); notificationRepository.save.mockResolvedValue(mockNotification); const result = await service.sendFromTemplate(templateDto, mockTenantId); expect(result).toHaveProperty('id'); expect(templateRepository.findOne).toHaveBeenCalledWith({ where: { code: 'welcome_email', is_active: true }, }); }); it('should throw NotFoundException for invalid template', async () => { templateRepository.findOne.mockResolvedValue(null); await expect(service.sendFromTemplate(templateDto, mockTenantId)).rejects.toThrow(common_1.NotFoundException); }); }); describe('findAllForUser', () => { it('should return paginated notifications with unread count', async () => { const notifications = [mockNotification]; notificationRepository.findAndCount.mockResolvedValue([notifications, 1]); notificationRepository.count.mockResolvedValue(1); const result = await service.findAllForUser(mockUserId, mockTenantId, { page: 1, limit: 20, }); expect(result).toHaveProperty('data'); expect(result).toHaveProperty('total', 1); expect(result).toHaveProperty('unread', 1); expect(notificationRepository.findAndCount).toHaveBeenCalled(); }); it('should filter unread only when specified', async () => { notificationRepository.findAndCount.mockResolvedValue([[], 0]); notificationRepository.count.mockResolvedValue(0); await service.findAllForUser(mockUserId, mockTenantId, { unreadOnly: true, }); expect(notificationRepository.findAndCount).toHaveBeenCalledWith(expect.objectContaining({ where: expect.objectContaining({ is_read: false }), })); }); }); describe('markAsRead', () => { it('should mark notification as read', async () => { notificationRepository.findOne.mockResolvedValue(mockNotification); notificationRepository.save.mockResolvedValue({ ...mockNotification, is_read: true, read_at: new Date(), }); const result = await service.markAsRead(mockNotification.id, mockUserId, mockTenantId); expect(result.is_read).toBe(true); expect(result.read_at).toBeDefined(); }); it('should throw NotFoundException for invalid notification', async () => { notificationRepository.findOne.mockResolvedValue(null); await expect(service.markAsRead('invalid-id', mockUserId, mockTenantId)).rejects.toThrow(common_1.NotFoundException); }); }); describe('markAllAsRead', () => { it('should mark all notifications as read', async () => { notificationRepository.update.mockResolvedValue({ affected: 5 }); const result = await service.markAllAsRead(mockUserId, mockTenantId); expect(result).toBe(5); expect(notificationRepository.update).toHaveBeenCalledWith({ user_id: mockUserId, tenant_id: mockTenantId, is_read: false }, expect.objectContaining({ is_read: true })); }); }); describe('delete', () => { it('should delete notification successfully', async () => { notificationRepository.delete.mockResolvedValue({ affected: 1 }); await service.delete(mockNotification.id, mockUserId, mockTenantId); expect(notificationRepository.delete).toHaveBeenCalledWith({ id: mockNotification.id, user_id: mockUserId, tenant_id: mockTenantId, }); }); it('should throw NotFoundException for invalid notification', async () => { notificationRepository.delete.mockResolvedValue({ affected: 0 }); await expect(service.delete('invalid-id', mockUserId, mockTenantId)).rejects.toThrow(common_1.NotFoundException); }); }); describe('getUnreadCount', () => { it('should return unread notification count', async () => { notificationRepository.count.mockResolvedValue(3); const result = await service.getUnreadCount(mockUserId, mockTenantId); expect(result).toBe(3); expect(notificationRepository.count).toHaveBeenCalledWith({ where: { user_id: mockUserId, tenant_id: mockTenantId, channel: 'in_app', is_read: false, }, }); }); }); describe('findAllTemplates', () => { it('should return all active templates', async () => { templateRepository.find.mockResolvedValue([mockTemplate]); const result = await service.findAllTemplates(); expect(result).toHaveLength(1); expect(templateRepository.find).toHaveBeenCalledWith({ where: { is_active: true }, order: { category: 'ASC', code: 'ASC' }, }); }); }); describe('findTemplateByCode', () => { it('should return template by code', async () => { templateRepository.findOne.mockResolvedValue(mockTemplate); const result = await service.findTemplateByCode('welcome_email'); expect(result).toHaveProperty('code', 'welcome_email'); }); it('should throw NotFoundException for invalid template', async () => { templateRepository.findOne.mockResolvedValue(null); await expect(service.findTemplateByCode('invalid_code')).rejects.toThrow(common_1.NotFoundException); }); }); describe('getPreferences', () => { it('should return existing preferences', async () => { preferenceRepository.findOne.mockResolvedValue(mockPreferences); const result = await service.getPreferences(mockUserId, mockTenantId); expect(result).toHaveProperty('email_enabled', true); }); it('should create default preferences if not exist', async () => { preferenceRepository.findOne.mockResolvedValue(null); preferenceRepository.create.mockReturnValue(mockPreferences); preferenceRepository.save.mockResolvedValue(mockPreferences); const result = await service.getPreferences(mockUserId, mockTenantId); expect(result).toHaveProperty('email_enabled', true); expect(preferenceRepository.create).toHaveBeenCalled(); expect(preferenceRepository.save).toHaveBeenCalled(); }); }); describe('updatePreferences', () => { const updateDto = { emailEnabled: false, pushEnabled: false, }; it('should update preferences', async () => { preferenceRepository.findOne.mockResolvedValue(mockPreferences); preferenceRepository.save.mockResolvedValue({ ...mockPreferences, email_enabled: false, push_enabled: false, }); const result = await service.updatePreferences(mockUserId, mockTenantId, updateDto); expect(result.email_enabled).toBe(false); expect(result.push_enabled).toBe(false); }); }); describe('cleanupOldNotifications', () => { it('should delete old read notifications', async () => { notificationRepository.delete.mockResolvedValue({ affected: 10 }); const result = await service.cleanupOldNotifications(30); expect(result).toBe(10); expect(notificationRepository.delete).toHaveBeenCalled(); }); }); }); //# sourceMappingURL=notifications.service.spec.js.map