negyedik gyakorlat + megoldasok
This commit is contained in:
@@ -0,0 +1,240 @@
|
||||
const EmailService = require('../../../src/application/services/EmailService');
|
||||
const nodemailer = require('nodemailer');
|
||||
|
||||
// Mock nodemailer
|
||||
jest.mock('nodemailer');
|
||||
|
||||
describe('EmailService', () => {
|
||||
let emailService;
|
||||
let mockTransporter;
|
||||
|
||||
beforeEach(() => {
|
||||
// Reset mocks
|
||||
jest.clearAllMocks();
|
||||
|
||||
// Mock transporter
|
||||
mockTransporter = {
|
||||
sendMail: jest.fn().mockResolvedValue({
|
||||
messageId: 'test-message-id',
|
||||
response: '250 OK',
|
||||
}),
|
||||
};
|
||||
|
||||
// Mock nodemailer.createTransport
|
||||
nodemailer.createTransport.mockReturnValue(mockTransporter);
|
||||
|
||||
// Create EmailService instance
|
||||
emailService = new EmailService();
|
||||
});
|
||||
|
||||
describe('constructor', () => {
|
||||
test('should create transporter with correct configuration', () => {
|
||||
expect(nodemailer.createTransport).toHaveBeenCalled();
|
||||
|
||||
const config = nodemailer.createTransport.mock.calls[0][0];
|
||||
expect(config).toHaveProperty('host');
|
||||
expect(config).toHaveProperty('port');
|
||||
expect(config).toHaveProperty('auth');
|
||||
expect(config.auth).toHaveProperty('user');
|
||||
expect(config.auth).toHaveProperty('pass');
|
||||
});
|
||||
|
||||
test('should initialize transporter instance', () => {
|
||||
expect(emailService.transporter).toBeDefined();
|
||||
expect(emailService.transporter).toBe(mockTransporter);
|
||||
});
|
||||
});
|
||||
|
||||
describe('sendWelcomeEmail', () => {
|
||||
test('should send welcome email with correct parameters', async () => {
|
||||
const userEmail = 'test@example.com';
|
||||
const userName = 'John Doe';
|
||||
|
||||
await emailService.sendWelcomeEmail(userEmail, userName);
|
||||
|
||||
expect(mockTransporter.sendMail).toHaveBeenCalledTimes(1);
|
||||
|
||||
const emailOptions = mockTransporter.sendMail.mock.calls[0][0];
|
||||
expect(emailOptions.to).toBe(userEmail);
|
||||
expect(emailOptions.subject).toContain('Üdvözlünk');
|
||||
expect(emailOptions).toHaveProperty('html');
|
||||
});
|
||||
|
||||
test('should include user name in email content', async () => {
|
||||
const userEmail = 'jane@example.com';
|
||||
const userName = 'Jane Smith';
|
||||
|
||||
await emailService.sendWelcomeEmail(userEmail, userName);
|
||||
|
||||
const emailOptions = mockTransporter.sendMail.mock.calls[0][0];
|
||||
expect(emailOptions.html).toContain(userName);
|
||||
});
|
||||
|
||||
test('should include user email in email content', async () => {
|
||||
const userEmail = 'contact@test.com';
|
||||
const userName = 'Test User';
|
||||
|
||||
await emailService.sendWelcomeEmail(userEmail, userName);
|
||||
|
||||
const emailOptions = mockTransporter.sendMail.mock.calls[0][0];
|
||||
expect(emailOptions.html).toContain(userEmail);
|
||||
});
|
||||
|
||||
test('should return messageId on successful send', async () => {
|
||||
const result = await emailService.sendWelcomeEmail('user@test.com', 'User');
|
||||
|
||||
expect(result).toBeDefined();
|
||||
expect(result.messageId).toBe('test-message-id');
|
||||
});
|
||||
|
||||
test('should use correct from address', async () => {
|
||||
await emailService.sendWelcomeEmail('recipient@test.com', 'Recipient');
|
||||
|
||||
const emailOptions = mockTransporter.sendMail.mock.calls[0][0];
|
||||
expect(emailOptions).toHaveProperty('from');
|
||||
expect(emailOptions.from).toBeTruthy();
|
||||
});
|
||||
|
||||
test('should handle multiple recipients', async () => {
|
||||
await emailService.sendWelcomeEmail('user1@test.com', 'User 1');
|
||||
await emailService.sendWelcomeEmail('user2@test.com', 'User 2');
|
||||
await emailService.sendWelcomeEmail('user3@test.com', 'User 3');
|
||||
|
||||
expect(mockTransporter.sendMail).toHaveBeenCalledTimes(3);
|
||||
});
|
||||
});
|
||||
|
||||
describe('error handling', () => {
|
||||
test('should throw error when email sending fails', async () => {
|
||||
mockTransporter.sendMail.mockRejectedValue(new Error('SMTP connection failed'));
|
||||
|
||||
await expect(
|
||||
emailService.sendWelcomeEmail('user@test.com', 'User')
|
||||
).rejects.toThrow('SMTP connection failed');
|
||||
});
|
||||
|
||||
test('should throw error for invalid email address', async () => {
|
||||
mockTransporter.sendMail.mockRejectedValue(new Error('Invalid recipient'));
|
||||
|
||||
await expect(
|
||||
emailService.sendWelcomeEmail('invalid-email', 'User')
|
||||
).rejects.toThrow();
|
||||
});
|
||||
|
||||
test('should throw error when transporter is not configured', async () => {
|
||||
mockTransporter.sendMail.mockRejectedValue(new Error('Transporter not configured'));
|
||||
|
||||
await expect(
|
||||
emailService.sendWelcomeEmail('user@test.com', 'User')
|
||||
).rejects.toThrow('Transporter not configured');
|
||||
});
|
||||
});
|
||||
|
||||
describe('template rendering', () => {
|
||||
test('should render HTML template with Handlebars', async () => {
|
||||
await emailService.sendWelcomeEmail('template@test.com', 'Template User');
|
||||
|
||||
const emailOptions = mockTransporter.sendMail.mock.calls[0][0];
|
||||
|
||||
// Check that HTML is rendered (not raw Handlebars template)
|
||||
expect(emailOptions.html).not.toContain('{{userName}}');
|
||||
expect(emailOptions.html).not.toContain('{{userEmail}}');
|
||||
expect(emailOptions.html).toContain('Template User');
|
||||
expect(emailOptions.html).toContain('template@test.com');
|
||||
});
|
||||
|
||||
test('should handle special characters in user name', async () => {
|
||||
const specialName = "O'Reilly & Sons <script>alert('xss')</script>";
|
||||
|
||||
await emailService.sendWelcomeEmail('user@test.com', specialName);
|
||||
|
||||
const emailOptions = mockTransporter.sendMail.mock.calls[0][0];
|
||||
|
||||
// Handlebars should escape HTML by default
|
||||
expect(emailOptions.html).toBeDefined();
|
||||
});
|
||||
|
||||
test('should use correct template file path', async () => {
|
||||
await emailService.sendWelcomeEmail('user@test.com', 'User');
|
||||
|
||||
// Verify that email was sent (which means template was loaded successfully)
|
||||
expect(mockTransporter.sendMail).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
describe('real-world scenarios', () => {
|
||||
test('should send welcome email after user registration', async () => {
|
||||
const newUser = {
|
||||
email: 'newuser@example.com',
|
||||
name: 'New User',
|
||||
};
|
||||
|
||||
const result = await emailService.sendWelcomeEmail(newUser.email, newUser.name);
|
||||
|
||||
expect(result).toBeDefined();
|
||||
expect(mockTransporter.sendMail).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
to: newUser.email,
|
||||
html: expect.stringContaining(newUser.name),
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
test('should handle concurrent email sends', async () => {
|
||||
const users = [
|
||||
{ email: 'user1@test.com', name: 'User 1' },
|
||||
{ email: 'user2@test.com', name: 'User 2' },
|
||||
{ email: 'user3@test.com', name: 'User 3' },
|
||||
];
|
||||
|
||||
await Promise.all(
|
||||
users.map(user => emailService.sendWelcomeEmail(user.email, user.name))
|
||||
);
|
||||
|
||||
expect(mockTransporter.sendMail).toHaveBeenCalledTimes(3);
|
||||
});
|
||||
|
||||
test('should work with Ethereal Email test account', async () => {
|
||||
// Simulate Ethereal Email configuration
|
||||
const etherealTransporter = {
|
||||
sendMail: jest.fn().mockResolvedValue({
|
||||
messageId: '<ethereal-id@ethereal.email>',
|
||||
response: '250 Accepted',
|
||||
}),
|
||||
};
|
||||
|
||||
nodemailer.createTransport.mockReturnValue(etherealTransporter);
|
||||
const etherealEmailService = new EmailService();
|
||||
|
||||
const result = await etherealEmailService.sendWelcomeEmail('test@ethereal.email', 'Test User');
|
||||
|
||||
expect(result.messageId).toContain('ethereal');
|
||||
expect(etherealTransporter.sendMail).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
describe('email content validation', () => {
|
||||
test('should include welcome message', async () => {
|
||||
await emailService.sendWelcomeEmail('user@test.com', 'User');
|
||||
|
||||
const emailOptions = mockTransporter.sendMail.mock.calls[0][0];
|
||||
expect(emailOptions.html.toLowerCase()).toMatch(/üdvözl|welcome/i);
|
||||
});
|
||||
|
||||
test('should be HTML formatted', async () => {
|
||||
await emailService.sendWelcomeEmail('user@test.com', 'User');
|
||||
|
||||
const emailOptions = mockTransporter.sendMail.mock.calls[0][0];
|
||||
expect(emailOptions.html).toContain('<html');
|
||||
expect(emailOptions.html).toContain('</html>');
|
||||
});
|
||||
|
||||
test('should have valid subject line', async () => {
|
||||
await emailService.sendWelcomeEmail('user@test.com', 'User');
|
||||
|
||||
const emailOptions = mockTransporter.sendMail.mock.calls[0][0];
|
||||
expect(emailOptions.subject).toBeTruthy();
|
||||
expect(emailOptions.subject.length).toBeGreaterThan(0);
|
||||
});
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user