import { describe, it, expect, vi, beforeEach } from 'vitest'; import { SmtpEmailProvider } from '../../src/providers/email/smtp.provider'; import { AppError } from '../../src/utils/errors'; // Mock dependencies vi.mock('nodemailer', () => { const sendMailMock = vi.fn().mockResolvedValue({ messageId: 'test-id' }); const closeMock = vi.fn(); return { default: { createTransport: vi.fn().mockReturnValue({ sendMail: sendMailMock, close: closeMock, }), }, }; }); vi.mock('../../src/utils/logger', () => ({ default: { info: vi.fn(), error: vi.fn(), warn: vi.fn(), }, })); const mockSmtpConfig = { enabled: true, host: 'smtp.example.com', port: 465, username: 'testuser', password: 'testpass', senderEmail: 'noreply@example.com', senderName: 'Test App', minIntervalSeconds: 60, }; const mockTemplate = { id: '11111111-1111-4111-8111-111111111111', templateType: 'email-verification-code', subject: 'Verify your email', bodyHtml: '

Your code is: {{ token }}

Email: {{ email }}

', createdAt: '2026-01-01T00:00:00Z', updatedAt: '2026-01-01T00:00:00Z', }; const getRawSmtpConfigMock = vi.fn().mockResolvedValue(mockSmtpConfig); const getTemplateMock = vi.fn().mockResolvedValue(mockTemplate); vi.mock('../../src/services/email/smtp-config.service', () => ({ SmtpConfigService: { getInstance: () => ({ getRawSmtpConfig: getRawSmtpConfigMock, }), }, })); vi.mock('../../src/services/email/email-template.service', () => ({ EmailTemplateService: { getInstance: () => ({ getTemplate: getTemplateMock, }), }, })); describe('SmtpEmailProvider', () => { let provider: SmtpEmailProvider; beforeEach(() => { vi.clearAllMocks(); provider = new SmtpEmailProvider(); }); describe('supportsTemplates', () => { it('returns true', () => { expect(provider.supportsTemplates()).toBe(true); }); }); describe('sendWithTemplate', () => { it('sends email with rendered template', async () => { const nodemailer = await import('nodemailer'); const transport = nodemailer.default.createTransport(); await provider.sendWithTemplate('user@example.com', 'Test App', 'email-verification-code', { token: '123456', }); expect(transport.sendMail).toHaveBeenCalledWith( expect.objectContaining({ from: '"Test App" ', to: 'user@example.com', subject: 'Verify your email', html: expect.stringContaining('123456'), }) ); }); it('HTML-escapes placeholder values to prevent XSS', async () => { const nodemailer = await import('nodemailer'); const transport = nodemailer.default.createTransport(); await provider.sendWithTemplate( '@evil.com', 'Test App', 'email-verification-code', { token: '' } ); expect(transport.sendMail).toHaveBeenCalledWith( expect.objectContaining({ html: expect.not.stringContaining('