- HERENCIA-SIMCO.md actualizado con directivas v3.7 y v3.8 - Actualizaciones de configuracion Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
327 lines
14 KiB
JavaScript
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
|