template-saas/apps/backend/dist/modules/email/__tests__/email.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

327 lines
14 KiB
JavaScript

"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const testing_1 = require("@nestjs/testing");
const config_1 = require("@nestjs/config");
const email_service_1 = require("../services/email.service");
const mockFetch = jest.fn();
global.fetch = mockFetch;
describe('EmailService', () => {
let service;
let configService;
const defaultEmailConfig = {
provider: 'sendgrid',
from: 'noreply@example.com',
fromName: 'Test App',
replyTo: 'support@example.com',
sendgridApiKey: 'SG.test-api-key',
sesRegion: 'us-east-1',
sesAccessKeyId: '',
sesSecretAccessKey: '',
smtpHost: '',
smtpPort: 587,
smtpUser: '',
smtpPassword: '',
smtpSecure: false,
};
beforeEach(async () => {
mockFetch.mockReset();
const mockConfigService = {
get: jest.fn().mockReturnValue(defaultEmailConfig),
};
const module = await testing_1.Test.createTestingModule({
providers: [
email_service_1.EmailService,
{ provide: config_1.ConfigService, useValue: mockConfigService },
],
}).compile();
service = module.get(email_service_1.EmailService);
configService = module.get(config_1.ConfigService);
service.onModuleInit();
});
afterEach(() => {
jest.clearAllMocks();
});
describe('initialization', () => {
it('should initialize with sendgrid provider', () => {
expect(service.getProvider()).toBe('sendgrid');
expect(service.isEnabled()).toBe(true);
});
it('should not be enabled when no API key is configured', async () => {
configService.get.mockReturnValue({
...defaultEmailConfig,
sendgridApiKey: '',
});
const module = await testing_1.Test.createTestingModule({
providers: [
email_service_1.EmailService,
{ provide: config_1.ConfigService, useValue: configService },
],
}).compile();
const unconfiguredService = module.get(email_service_1.EmailService);
unconfiguredService.onModuleInit();
expect(unconfiguredService.isEnabled()).toBe(false);
});
});
describe('sendEmail', () => {
const sendEmailDto = {
to: { email: 'user@example.com', name: 'Test User' },
subject: 'Test Subject',
html: '<h1>Hello</h1>',
text: 'Hello',
};
describe('SendGrid provider', () => {
it('should send email successfully via SendGrid', async () => {
mockFetch.mockResolvedValue({
ok: true,
headers: {
get: jest.fn().mockReturnValue('sg-msg-123'),
},
});
const result = await service.sendEmail(sendEmailDto);
expect(result.success).toBe(true);
expect(result.provider).toBe('sendgrid');
expect(result.messageId).toBeDefined();
expect(mockFetch).toHaveBeenCalledWith('https://api.sendgrid.com/v3/mail/send', expect.objectContaining({
method: 'POST',
headers: expect.objectContaining({
'Authorization': 'Bearer SG.test-api-key',
}),
}));
});
it('should handle SendGrid API error', async () => {
mockFetch.mockResolvedValue({
ok: false,
status: 401,
text: jest.fn().mockResolvedValue('Unauthorized'),
});
const result = await service.sendEmail(sendEmailDto);
expect(result.success).toBe(false);
expect(result.error).toContain('SendGrid error');
});
it('should include CC and BCC recipients', async () => {
mockFetch.mockResolvedValue({
ok: true,
headers: { get: jest.fn().mockReturnValue('msg-123') },
});
const dtoWithCcBcc = {
...sendEmailDto,
cc: [{ email: 'cc@example.com' }],
bcc: [{ email: 'bcc@example.com' }],
};
await service.sendEmail(dtoWithCcBcc);
expect(mockFetch).toHaveBeenCalledWith(expect.any(String), expect.objectContaining({
body: expect.stringContaining('cc@example.com'),
}));
});
it('should include attachments', async () => {
mockFetch.mockResolvedValue({
ok: true,
headers: { get: jest.fn().mockReturnValue('msg-123') },
});
const dtoWithAttachment = {
...sendEmailDto,
attachments: [{
filename: 'test.pdf',
content: 'base64content',
contentType: 'application/pdf',
}],
};
await service.sendEmail(dtoWithAttachment);
expect(mockFetch).toHaveBeenCalledWith(expect.any(String), expect.objectContaining({
body: expect.stringContaining('test.pdf'),
}));
});
});
describe('when not configured', () => {
it('should log email instead of sending', async () => {
configService.get.mockReturnValue({
...defaultEmailConfig,
sendgridApiKey: '',
});
const module = await testing_1.Test.createTestingModule({
providers: [
email_service_1.EmailService,
{ provide: config_1.ConfigService, useValue: configService },
],
}).compile();
const unconfiguredService = module.get(email_service_1.EmailService);
unconfiguredService.onModuleInit();
const result = await unconfiguredService.sendEmail(sendEmailDto);
expect(result.success).toBe(true);
expect(result.messageId).toMatch(/^mock-/);
expect(mockFetch).not.toHaveBeenCalled();
});
});
});
describe('sendTemplateEmail', () => {
it('should send email from welcome template', async () => {
mockFetch.mockResolvedValue({
ok: true,
headers: { get: jest.fn().mockReturnValue('msg-123') },
});
const result = await service.sendTemplateEmail({
to: { email: 'user@example.com' },
templateKey: 'welcome',
variables: {
userName: 'John',
appName: 'Test App',
},
});
expect(result.success).toBe(true);
expect(mockFetch).toHaveBeenCalledWith(expect.any(String), expect.objectContaining({
body: expect.stringContaining('Welcome'),
}));
});
it('should send email from password_reset template', async () => {
mockFetch.mockResolvedValue({
ok: true,
headers: { get: jest.fn().mockReturnValue('msg-123') },
});
const result = await service.sendTemplateEmail({
to: { email: 'user@example.com' },
templateKey: 'password_reset',
variables: {
userName: 'John',
resetLink: 'https://app.com/reset?token=abc',
expiresIn: '1 hour',
},
});
expect(result.success).toBe(true);
});
it('should send email from invitation template', async () => {
mockFetch.mockResolvedValue({
ok: true,
headers: { get: jest.fn().mockReturnValue('msg-123') },
});
const result = await service.sendTemplateEmail({
to: { email: 'user@example.com' },
templateKey: 'invitation',
variables: {
inviterName: 'Admin',
tenantName: 'Acme Corp',
appName: 'Test App',
inviteLink: 'https://app.com/invite?code=xyz',
expiresIn: '7 days',
},
});
expect(result.success).toBe(true);
});
it('should return error for unknown template', async () => {
const result = await service.sendTemplateEmail({
to: { email: 'user@example.com' },
templateKey: 'unknown_template',
variables: {},
});
expect(result.success).toBe(false);
expect(result.error).toContain('Template not found');
});
it('should handle conditional blocks in templates', async () => {
mockFetch.mockResolvedValue({
ok: true,
headers: { get: jest.fn().mockReturnValue('msg-123') },
});
await service.sendTemplateEmail({
to: { email: 'user@example.com' },
templateKey: 'notification',
variables: {
title: 'New Message',
message: 'You have a new message',
actionUrl: 'https://app.com/messages',
actionText: 'View Message',
},
});
expect(mockFetch).toHaveBeenCalledWith(expect.any(String), expect.objectContaining({
body: expect.stringContaining('View Message'),
}));
});
});
describe('sendBulkEmails', () => {
it('should send multiple emails in batches', async () => {
mockFetch.mockResolvedValue({
ok: true,
headers: { get: jest.fn().mockReturnValue('msg-123') },
});
const emails = Array(15).fill(null).map((_, i) => ({
to: { email: `user${i}@example.com` },
subject: `Email ${i}`,
text: `Content ${i}`,
}));
const results = await service.sendBulkEmails(emails);
expect(results).toHaveLength(15);
expect(results.every(r => r.success)).toBe(true);
expect(mockFetch).toHaveBeenCalledTimes(15);
});
it('should handle partial failures in bulk send', async () => {
mockFetch
.mockResolvedValueOnce({ ok: true, headers: { get: () => 'msg-1' } })
.mockResolvedValueOnce({ ok: false, status: 500, text: () => 'Error' })
.mockResolvedValueOnce({ ok: true, headers: { get: () => 'msg-3' } });
const emails = [
{ to: { email: 'user1@example.com' }, subject: 'Test 1', text: 'Content' },
{ to: { email: 'user2@example.com' }, subject: 'Test 2', text: 'Content' },
{ to: { email: 'user3@example.com' }, subject: 'Test 3', text: 'Content' },
];
const results = await service.sendBulkEmails(emails);
expect(results[0].success).toBe(true);
expect(results[1].success).toBe(false);
expect(results[2].success).toBe(true);
});
});
describe('AWS SES provider', () => {
beforeEach(async () => {
configService.get.mockReturnValue({
...defaultEmailConfig,
provider: 'ses',
sendgridApiKey: '',
sesAccessKeyId: 'AKIATEST',
sesSecretAccessKey: 'secret',
});
const module = await testing_1.Test.createTestingModule({
providers: [
email_service_1.EmailService,
{ provide: config_1.ConfigService, useValue: configService },
],
}).compile();
service = module.get(email_service_1.EmailService);
service.onModuleInit();
});
it('should use SES provider when configured', () => {
expect(service.getProvider()).toBe('ses');
expect(service.isEnabled()).toBe(true);
});
});
describe('SMTP provider', () => {
beforeEach(async () => {
configService.get.mockReturnValue({
...defaultEmailConfig,
provider: 'smtp',
sendgridApiKey: '',
smtpHost: 'smtp.example.com',
smtpUser: 'user',
smtpPassword: 'password',
});
const module = await testing_1.Test.createTestingModule({
providers: [
email_service_1.EmailService,
{ provide: config_1.ConfigService, useValue: configService },
],
}).compile();
service = module.get(email_service_1.EmailService);
service.onModuleInit();
});
it('should use SMTP provider when configured', () => {
expect(service.getProvider()).toBe('smtp');
expect(service.isEnabled()).toBe(true);
});
it('should fallback to logging for SMTP (nodemailer not implemented)', async () => {
const result = await service.sendEmail({
to: { email: 'user@example.com' },
subject: 'Test',
text: 'Hello',
});
expect(result.success).toBe(true);
expect(result.messageId).toMatch(/^smtp-/);
});
});
});
//# sourceMappingURL=email.service.spec.js.map