negyedik gyakorlat + megoldasok

This commit is contained in:
magdo
2026-03-04 20:02:39 +01:00
parent afc3777ac9
commit 388aa908de
217 changed files with 19791 additions and 0 deletions
@@ -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);
});
});
});
@@ -0,0 +1,218 @@
const JwtService = require('../../../src/application/services/JwtService');
const jwt = require('jsonwebtoken');
// Mock jsonwebtoken
jest.mock('jsonwebtoken');
describe('JwtService', () => {
let jwtService;
const mockSecret = 'test-secret';
const mockExpiresIn = '1h';
beforeEach(() => {
// Setup environment variables
process.env.JWT_SECRET = mockSecret;
process.env.JWT_EXPIRES_IN = mockExpiresIn;
jwtService = new JwtService();
// Reset all mocks
jest.clearAllMocks();
});
describe('generateToken', () => {
it('should generate a JWT token with payload', () => {
// Arrange
const payload = { userId: 1, email: 'john@example.com' };
const mockToken = 'mock_jwt_token_abc123';
jwt.sign.mockReturnValue(mockToken);
// Act
const result = jwtService.generateToken(payload);
// Assert
expect(jwt.sign).toHaveBeenCalledWith(payload, mockSecret, { expiresIn: mockExpiresIn });
expect(result).toBe(mockToken);
});
it('should use default secret if JWT_SECRET not set', () => {
// Arrange
delete process.env.JWT_SECRET;
jwtService = new JwtService();
const payload = { userId: 1, email: 'john@example.com' };
jwt.sign.mockReturnValue('token');
// Act
jwtService.generateToken(payload);
// Assert
expect(jwt.sign).toHaveBeenCalledWith(payload, 'default-secret-change-me', expect.any(Object));
});
});
describe('verifyToken', () => {
it('should verify and return decoded token', () => {
// Arrange
const token = 'valid_token';
const mockDecoded = { userId: 1, email: 'john@example.com' };
jwt.verify.mockReturnValue(mockDecoded);
// Act
const result = jwtService.verifyToken(token);
// Assert
expect(jwt.verify).toHaveBeenCalledWith(token, mockSecret);
expect(result).toEqual(mockDecoded);
});
it('should throw error for invalid token', () => {
// Arrange
const token = 'invalid_token';
jwt.verify.mockImplementation(() => {
throw new Error('jwt malformed');
});
// Act & Assert
expect(() => jwtService.verifyToken(token)).toThrow('Invalid or expired token');
});
it('should throw error for expired token', () => {
// Arrange
const token = 'expired_token';
jwt.verify.mockImplementation(() => {
throw new Error('jwt expired');
});
// Act & Assert
expect(() => jwtService.verifyToken(token)).toThrow('Invalid or expired token');
});
});
describe('extractTokenFromCookies', () => {
it('should extract token from cookies object', () => {
// Arrange
const cookies = { auth_token: 'abc123xyz' };
// Act
const result = jwtService.extractTokenFromCookies(cookies);
// Assert
expect(result).toBe('abc123xyz');
});
it('should return null if cookies object is empty', () => {
// Arrange
const cookies = {};
// Act
const result = jwtService.extractTokenFromCookies(cookies);
// Assert
expect(result).toBeNull();
});
it('should return null if cookies is null', () => {
// Arrange
const cookies = null;
// Act
const result = jwtService.extractTokenFromCookies(cookies);
// Assert
expect(result).toBeNull();
});
it('should return null if cookies is undefined', () => {
// Arrange
const cookies = undefined;
// Act
const result = jwtService.extractTokenFromCookies(cookies);
// Assert
expect(result).toBeNull();
});
});
describe('getCookieOptions', () => {
it('should return secure cookie options in production', () => {
// Arrange
process.env.NODE_ENV = 'production';
jwtService = new JwtService();
// Act
const options = jwtService.getCookieOptions();
// Assert
expect(options.httpOnly).toBe(true);
expect(options.secure).toBe(true);
expect(options.sameSite).toBe('strict');
expect(options.path).toBe('/');
expect(options.maxAge).toBeGreaterThan(0);
});
it('should return non-secure cookie options in development', () => {
// Arrange
process.env.NODE_ENV = 'development';
jwtService = new JwtService();
// Act
const options = jwtService.getCookieOptions();
// Assert
expect(options.httpOnly).toBe(true);
expect(options.secure).toBe(false);
expect(options.sameSite).toBe('strict');
});
});
describe('getCookieName', () => {
it('should return the cookie name', () => {
// Act
const name = jwtService.getCookieName();
// Assert
expect(name).toBe('auth_token');
});
});
describe('extractTokenFromHeader (legacy)', () => {
it('should extract token from valid Authorization header', () => {
// Arrange
const authHeader = 'Bearer abc123xyz';
// Act
const result = jwtService.extractTokenFromHeader(authHeader);
// Assert
expect(result).toBe('abc123xyz');
});
it('should return null if Authorization header is missing', () => {
// Arrange
const authHeader = null;
// Act
const result = jwtService.extractTokenFromHeader(authHeader);
// Assert
expect(result).toBeNull();
});
it('should return null if Authorization header does not start with Bearer', () => {
// Arrange
const authHeader = 'Basic abc123xyz';
// Act
const result = jwtService.extractTokenFromHeader(authHeader);
// Assert
expect(result).toBeNull();
});
});
});