Files
SerpentRace/SerpentRace_Backend/tests/Application/Services/EmailService.test.ts
T
2025-11-25 00:07:54 +01:00

227 lines
6.0 KiB
TypeScript

import { EmailService, EmailOptions } from '../../../src/Application/Services/EmailService';
import * as nodemailer from 'nodemailer';
import * as fs from 'fs';
// Mock nodemailer
jest.mock('nodemailer');
jest.mock('fs');
// Mock logger
jest.mock('../../../src/Application/Services/Logger', () => ({
logError: jest.fn(),
logAuth: jest.fn(),
logStartup: jest.fn(),
}));
describe('EmailService', () => {
let emailService: EmailService;
let mockTransporter: jest.Mocked<nodemailer.Transporter>;
let mockCreateTransporter: jest.MockedFunction<typeof nodemailer.createTransport>;
beforeEach(() => {
jest.clearAllMocks();
// Mock nodemailer.createTransporter
mockTransporter = {
sendMail: jest.fn(),
} as any;
mockCreateTransporter = nodemailer.createTransport as jest.MockedFunction<typeof nodemailer.createTransport>;
mockCreateTransporter.mockReturnValue(mockTransporter);
// Mock fs
(fs.readFileSync as jest.Mock).mockImplementation((filePath: string) => {
if (filePath.includes('html')) {
return 'HTML template: {{name}}';
}
return 'Text template: {{name}}';
});
(fs.existsSync as jest.Mock).mockReturnValue(true);
emailService = new EmailService();
});
describe('sendEmail', () => {
it('should send email successfully', async () => {
// Arrange
const emailOptions: EmailOptions = {
to: 'test@example.com',
subject: 'Test Subject',
html: '<p>Test HTML</p>',
text: 'Test Text',
};
mockTransporter.sendMail.mockResolvedValue({ messageId: 'test-id' });
// Act
const result = await emailService.sendEmail(emailOptions);
// Assert
expect(result).toBe(true);
expect(mockTransporter.sendMail).toHaveBeenCalledWith({
from: process.env.EMAIL_FROM || 'noreply@serpentrace.com',
to: emailOptions.to,
subject: emailOptions.subject,
html: emailOptions.html,
text: emailOptions.text,
attachments: expect.any(Array),
});
});
it('should send email with template', async () => {
// Arrange
const emailOptions: EmailOptions = {
to: 'test@example.com',
subject: 'Test Subject',
template: 'verification',
templateData: { name: 'John', token: 'abc123' },
};
mockTransporter.sendMail.mockResolvedValue({ messageId: 'test-id' });
// Act
const result = await emailService.sendEmail(emailOptions);
// Assert
expect(result).toBe(true);
expect(mockTransporter.sendMail).toHaveBeenCalledWith({
from: process.env.EMAIL_FROM || 'noreply@serpentrace.com',
to: emailOptions.to,
subject: emailOptions.subject,
html: expect.any(String),
text: expect.any(String),
attachments: expect.any(Array),
});
});
it('should handle email send failure', async () => {
// Arrange
const emailOptions: EmailOptions = {
to: 'test@example.com',
subject: 'Test Subject',
text: 'Test Text',
};
mockTransporter.sendMail.mockRejectedValue(new Error('SMTP Error'));
// Act
const result = await emailService.sendEmail(emailOptions);
// Assert
expect(result).toBe(false);
});
it('should handle missing template files', async () => {
// Arrange
const emailOptions: EmailOptions = {
to: 'test@example.com',
subject: 'Test Subject',
template: 'nonexistent',
templateData: { name: 'John' },
};
(fs.existsSync as jest.Mock).mockReturnValue(false);
// Act
const result = await emailService.sendEmail(emailOptions);
// Assert
expect(result).toBe(false);
});
it('should handle template processing errors', async () => {
// Arrange
const emailOptions: EmailOptions = {
to: 'test@example.com',
subject: 'Test Subject',
template: 'verification',
templateData: { name: 'John' },
};
(fs.readFileSync as jest.Mock).mockImplementation(() => {
throw new Error('File read error');
});
// Act
const result = await emailService.sendEmail(emailOptions);
// Assert
expect(result).toBe(false);
});
it('should use fallback content when template data is missing', async () => {
// Arrange
const emailOptions: EmailOptions = {
to: 'test@example.com',
subject: 'Test Subject',
template: 'verification',
};
mockTransporter.sendMail.mockResolvedValue({ messageId: 'test-id' });
// Act
const result = await emailService.sendEmail(emailOptions);
// Assert
expect(result).toBe(true);
});
});
describe('constructor', () => {
it('should initialize with environment variables', () => {
// Arrange
const originalEnv = process.env;
process.env = {
...originalEnv,
EMAIL_HOST: 'test-smtp.com',
EMAIL_PORT: '465',
EMAIL_SECURE: 'true',
EMAIL_USER: 'test@example.com',
EMAIL_PASS: 'testpass',
EMAIL_FROM: 'sender@example.com',
};
// Act
const service = new EmailService();
// Assert
expect(mockCreateTransporter).toHaveBeenCalledWith({
host: 'test-smtp.com',
port: 465,
secure: true,
auth: {
user: 'test@example.com',
pass: 'testpass',
},
});
// Restore environment
process.env = originalEnv;
});
it('should use default values when environment variables are missing', () => {
// Arrange
const originalEnv = process.env;
process.env = {};
// Act
const service = new EmailService();
// Assert
expect(mockCreateTransporter).toHaveBeenCalledWith({
host: 'smtp.gmail.com',
port: 587,
secure: false,
auth: {
user: '',
pass: '',
},
});
// Restore environment
process.env = originalEnv;
});
});
});