template-saas/apps/backend/dist/modules/notifications/__tests__/notifications.service.spec.js
rckrdmrd 50a821a415
Some checks failed
CI / Backend CI (push) Has been cancelled
CI / Frontend CI (push) Has been cancelled
CI / Security Scan (push) Has been cancelled
CI / CI Summary (push) Has been cancelled
[SIMCO-V38] feat: Actualizar a SIMCO v3.8.0
- HERENCIA-SIMCO.md actualizado con directivas v3.7 y v3.8
- Actualizaciones de configuracion

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-10 08:53:08 -06:00

325 lines
14 KiB
JavaScript

"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