// Mock dependencies jest.mock('../../../../../services/email/core/EmailClient', () => { return jest.fn().mockImplementation(() => ({ initialize: jest.fn().mockResolvedValue(), sendEmail: jest.fn().mockResolvedValue({ success: true, messageId: 'msg-123' }), })); }); jest.mock('../../../../../services/email/core/TemplateManager', () => { return jest.fn().mockImplementation(() => ({ initialize: jest.fn().mockResolvedValue(), renderTemplate: jest.fn().mockResolvedValue('Test'), })); }); jest.mock('../../../../../utils/logger', () => ({ info: jest.fn(), error: jest.fn(), warn: jest.fn(), })); const CustomerServiceEmailService = require('../../../../../services/email/domain/CustomerServiceEmailService'); describe('CustomerServiceEmailService', () => { let service; const originalEnv = process.env; beforeEach(() => { jest.clearAllMocks(); process.env = { ...originalEnv, CUSTOMER_SUPPORT_EMAIL: 'support@example.com', }; service = new CustomerServiceEmailService(); }); afterEach(() => { process.env = originalEnv; }); describe('initialize', () => { it('should initialize only once', async () => { await service.initialize(); await service.initialize(); expect(service.emailClient.initialize).toHaveBeenCalledTimes(1); expect(service.templateManager.initialize).toHaveBeenCalledTimes(1); }); }); describe('sendLateReturnToCustomerService', () => { const rental = { id: 123, endDateTime: '2024-01-16T10:00:00Z', actualReturnDateTime: '2024-01-16T15:00:00Z', item: { name: 'Power Drill' }, }; const owner = { firstName: 'John', lastName: 'Doe', email: 'john@example.com', }; const renter = { firstName: 'Jane', lastName: 'Smith', email: 'jane@example.com', }; const lateCalculation = { lateHours: 5, lateFee: 25.00, }; it('should send late return notification with correct variables', async () => { const result = await service.sendLateReturnToCustomerService( rental, owner, renter, lateCalculation ); expect(result.success).toBe(true); expect(service.templateManager.renderTemplate).toHaveBeenCalledWith( 'lateReturnToCS', expect.objectContaining({ rentalId: 123, itemName: 'Power Drill', ownerName: 'John Doe', ownerEmail: 'john@example.com', renterName: 'Jane Smith', renterEmail: 'jane@example.com', hoursLate: '5.0', lateFee: '25.00', }) ); expect(service.emailClient.sendEmail).toHaveBeenCalledWith( 'support@example.com', 'Late Return Detected - Action Required', expect.any(String) ); }); it('should return error when customer service email is not configured', async () => { delete process.env.CUSTOMER_SUPPORT_EMAIL; const result = await service.sendLateReturnToCustomerService( rental, owner, renter, lateCalculation ); expect(result.success).toBe(false); expect(result.error).toBe('No customer service email configured'); expect(service.emailClient.sendEmail).not.toHaveBeenCalled(); }); it('should handle errors gracefully', async () => { service.templateManager.renderTemplate.mockRejectedValueOnce(new Error('Template error')); const result = await service.sendLateReturnToCustomerService( rental, owner, renter, lateCalculation ); expect(result.success).toBe(false); expect(result.error).toBe('Template error'); }); }); describe('sendDamageReportToCustomerService', () => { const rental = { id: 123, item: { name: 'Power Drill' }, }; const owner = { firstName: 'John', lastName: 'Doe', email: 'john@example.com', }; const renter = { firstName: 'Jane', lastName: 'Smith', email: 'jane@example.com', }; const damageAssessment = { description: 'Drill bit broken', canBeFixed: true, repairCost: 30, needsReplacement: false, replacementCost: null, feeCalculation: { type: 'repair', amount: 30, }, proofOfOwnership: ['receipt.jpg'], }; it('should send damage report with correct variables', async () => { const result = await service.sendDamageReportToCustomerService( rental, owner, renter, damageAssessment ); expect(result.success).toBe(true); expect(service.templateManager.renderTemplate).toHaveBeenCalledWith( 'damageReportToCS', expect.objectContaining({ rentalId: 123, itemName: 'Power Drill', ownerName: 'John Doe', ownerEmail: 'john@example.com', renterName: 'Jane Smith', renterEmail: 'jane@example.com', damageDescription: 'Drill bit broken', canBeFixed: 'Yes', repairCost: '30.00', needsReplacement: 'No', feeTypeDescription: 'Repair Cost', damageFee: '30.00', lateFee: '0.00', totalFees: '30.00', hasProofOfOwnership: 'Yes', }) ); expect(service.emailClient.sendEmail).toHaveBeenCalledWith( 'support@example.com', 'Damage Report Filed - Action Required', expect.any(String) ); }); it('should include late fee when provided', async () => { const lateCalculation = { lateFee: 15 }; await service.sendDamageReportToCustomerService( rental, owner, renter, damageAssessment, lateCalculation ); expect(service.templateManager.renderTemplate).toHaveBeenCalledWith( 'damageReportToCS', expect.objectContaining({ lateFee: '15.00', totalFees: '45.00', }) ); }); it('should handle replacement fee type', async () => { const replacementAssessment = { ...damageAssessment, canBeFixed: false, needsReplacement: true, replacementCost: 150, feeCalculation: { type: 'replacement', amount: 150, }, }; await service.sendDamageReportToCustomerService( rental, owner, renter, replacementAssessment ); expect(service.templateManager.renderTemplate).toHaveBeenCalledWith( 'damageReportToCS', expect.objectContaining({ feeTypeDescription: 'Replacement Cost', needsReplacement: 'Yes', replacementCost: '150.00', }) ); }); it('should handle unknown fee type', async () => { const unknownTypeAssessment = { ...damageAssessment, feeCalculation: { type: 'other', amount: 50, }, }; await service.sendDamageReportToCustomerService( rental, owner, renter, unknownTypeAssessment ); expect(service.templateManager.renderTemplate).toHaveBeenCalledWith( 'damageReportToCS', expect.objectContaining({ feeTypeDescription: 'Damage Assessment Fee', }) ); }); it('should indicate no proof of ownership when not provided', async () => { const noProofAssessment = { ...damageAssessment, proofOfOwnership: [], }; await service.sendDamageReportToCustomerService( rental, owner, renter, noProofAssessment ); expect(service.templateManager.renderTemplate).toHaveBeenCalledWith( 'damageReportToCS', expect.objectContaining({ hasProofOfOwnership: 'No', }) ); }); it('should return error when customer service email is not configured', async () => { delete process.env.CUSTOMER_SUPPORT_EMAIL; const result = await service.sendDamageReportToCustomerService( rental, owner, renter, damageAssessment ); expect(result.success).toBe(false); expect(result.error).toBe('No customer service email configured'); }); it('should handle errors gracefully', async () => { service.emailClient.sendEmail.mockRejectedValueOnce(new Error('Send error')); const result = await service.sendDamageReportToCustomerService( rental, owner, renter, damageAssessment ); expect(result.success).toBe(false); expect(result.error).toBe('Send error'); }); }); describe('sendLostItemToCustomerService', () => { const rental = { id: 123, endDateTime: '2024-01-16T10:00:00Z', itemLostReportedAt: '2024-01-17T10:00:00Z', item: { name: 'Power Drill', replacementCost: 200, }, }; const owner = { firstName: 'John', lastName: 'Doe', email: 'john@example.com', }; const renter = { firstName: 'Jane', lastName: 'Smith', email: 'jane@example.com', }; it('should send lost item notification with correct variables', async () => { const result = await service.sendLostItemToCustomerService(rental, owner, renter); expect(result.success).toBe(true); expect(service.templateManager.renderTemplate).toHaveBeenCalledWith( 'lostItemToCS', expect.objectContaining({ rentalId: 123, itemName: 'Power Drill', ownerName: 'John Doe', ownerEmail: 'john@example.com', renterName: 'Jane Smith', renterEmail: 'jane@example.com', replacementCost: '200.00', }) ); expect(service.emailClient.sendEmail).toHaveBeenCalledWith( 'support@example.com', 'Lost Item Claim Filed - Action Required', expect.any(String) ); }); it('should return error when customer service email is not configured', async () => { delete process.env.CUSTOMER_SUPPORT_EMAIL; const result = await service.sendLostItemToCustomerService(rental, owner, renter); expect(result.success).toBe(false); expect(result.error).toBe('No customer service email configured'); expect(service.emailClient.sendEmail).not.toHaveBeenCalled(); }); it('should handle errors gracefully', async () => { service.templateManager.renderTemplate.mockRejectedValueOnce(new Error('Template error')); const result = await service.sendLostItemToCustomerService(rental, owner, renter); expect(result.success).toBe(false); expect(result.error).toBe('Template error'); }); }); });