import { EmailService, emailService } from '../email'; // Mock console methods const mockConsoleWarn = jest.fn(); const mockConsoleLog = jest.fn(); describe('EmailService', () => { let service: EmailService; beforeEach(() => { jest.clearAllMocks(); console.warn = mockConsoleWarn; console.log = mockConsoleLog; service = new EmailService(); }); afterEach(() => { jest.restoreAllMocks(); }); describe('constructor', () => { test('should create a new EmailService instance', () => { expect(service).toBeInstanceOf(EmailService); }); }); describe('sendVerificationEmail', () => { test('should log verification email details', async () => { const email = 'test@example.com'; const token = 'test-token-123'; await service.sendVerificationEmail(email, token); expect(mockConsoleWarn).toHaveBeenCalledWith( `📧 Sending verification email to ${email} with token: ${token}` ); expect(mockConsoleWarn).toHaveBeenCalledWith( `🔗 Verification link: /verify-email?token=${token}` ); expect(mockConsoleWarn).toHaveBeenCalledTimes(2); }); test('should handle empty email gracefully', async () => { const email = ''; const token = 'test-token-123'; await service.sendVerificationEmail(email, token); expect(mockConsoleWarn).toHaveBeenCalledWith( `📧 Sending verification email to ${email} with token: ${token}` ); expect(mockConsoleWarn).toHaveBeenCalledWith( `🔗 Verification link: /verify-email?token=${token}` ); }); test('should handle empty token gracefully', async () => { const email = 'test@example.com'; const token = ''; await service.sendVerificationEmail(email, token); expect(mockConsoleWarn).toHaveBeenCalledWith( `📧 Sending verification email to ${email} with token: ${token}` ); expect(mockConsoleWarn).toHaveBeenCalledWith( `🔗 Verification link: /verify-email?token=${token}` ); }); test('should handle special characters in email and token', async () => { const email = 'test+user@example-domain.com'; const token = 'abc123!@#$%^&*()_+-={}[]|\\:";\'<>?,./'; await service.sendVerificationEmail(email, token); expect(mockConsoleWarn).toHaveBeenCalledWith( `📧 Sending verification email to ${email} with token: ${token}` ); expect(mockConsoleWarn).toHaveBeenCalledWith( `🔗 Verification link: /verify-email?token=${token}` ); }); test('should simulate network delay', async () => { const email = 'test@example.com'; const token = 'test-token-123'; const startTime = Date.now(); await service.sendVerificationEmail(email, token); const endTime = Date.now(); // Should take at least 500ms due to the simulated delay expect(endTime - startTime).toBeGreaterThanOrEqual(450); // Allow some margin for test execution }); test('should complete successfully without throwing errors', async () => { const email = 'test@example.com'; const token = 'test-token-123'; await expect( service.sendVerificationEmail(email, token) ).resolves.toBeUndefined(); }); test('should handle very long email addresses', async () => { const email = 'a'.repeat(100) + '@' + 'b'.repeat(100) + '.com'; const token = 'test-token-123'; await service.sendVerificationEmail(email, token); expect(mockConsoleWarn).toHaveBeenCalledWith( `📧 Sending verification email to ${email} with token: ${token}` ); }); test('should handle very long tokens', async () => { const email = 'test@example.com'; const token = 'a'.repeat(1000); await service.sendVerificationEmail(email, token); expect(mockConsoleWarn).toHaveBeenCalledWith( `🔗 Verification link: /verify-email?token=${token}` ); }); test('should handle null and undefined inputs gracefully', async () => { // Test with null values await service.sendVerificationEmail(null as any, null as any); expect(mockConsoleWarn).toHaveBeenCalled(); jest.clearAllMocks(); // Test with undefined values await service.sendVerificationEmail(undefined as any, undefined as any); expect(mockConsoleWarn).toHaveBeenCalled(); }); }); describe('emailService singleton', () => { test('should export a singleton instance', () => { expect(emailService).toBeInstanceOf(EmailService); }); test('should be the same instance when imported multiple times', () => { // This test verifies that the exported emailService is a singleton const { emailService: secondImport } = require('../email'); expect(emailService).toBe(secondImport); }); test('should have the sendVerificationEmail method', () => { expect(typeof emailService.sendVerificationEmail).toBe('function'); }); }); describe('method return values', () => { test('sendVerificationEmail should return a Promise that resolves to void', async () => { const result = await service.sendVerificationEmail( 'test@example.com', 'token' ); expect(result).toBeUndefined(); }); }); describe('console output format', () => { test('should use emoji prefixes for visual identification', async () => { const email = 'test@example.com'; const token = 'test-token-123'; await service.sendVerificationEmail(email, token); const [firstCall, secondCall] = mockConsoleWarn.mock.calls; expect(firstCall[0]).toMatch(/^📧/); expect(secondCall[0]).toMatch(/^🔗/); }); test('should format verification link consistently', async () => { const email = 'test@example.com'; const token = 'test-token-123'; await service.sendVerificationEmail(email, token); const secondCall = mockConsoleWarn.mock.calls[1]; expect(secondCall[0]).toBe( `🔗 Verification link: /verify-email?token=${token}` ); }); }); describe('error handling', () => { test('should not throw errors even with malformed inputs', async () => { const testCases = [ ['', ''], [null, null], [undefined, undefined], ['invalid-email', 'token'], ['test@example.com', null], [null, 'token'], ]; for (const [email, token] of testCases) { await expect( service.sendVerificationEmail(email as any, token as any) ).resolves.not.toThrow(); } }); }); });