// Mock dependencies BEFORE requiring modules jest.mock('../../../models'); jest.mock('../../../services/lateReturnService'); jest.mock('../../../services/emailService'); jest.mock('../../../config/aws', () => ({ getAWSConfig: jest.fn(() => ({ region: 'us-east-1' })), getAWSCredentials: jest.fn() })); const DamageAssessmentService = require('../../../services/damageAssessmentService'); const { Rental, Item } = require('../../../models'); const LateReturnService = require('../../../services/lateReturnService'); const emailService = require('../../../services/emailService'); describe('DamageAssessmentService', () => { beforeEach(() => { jest.clearAllMocks(); }); describe('processDamageAssessment', () => { let mockRental; let mockDamageInfo; beforeEach(() => { // Reset mockRental for each test to avoid state pollution mockRental = { id: 'rental-123', ownerId: 'owner-789', renterId: 'renter-456', status: 'active', item: { name: 'Test Camera', dailyRate: 100 }, update: jest.fn().mockResolvedValue({ id: 'rental-123', status: 'damaged', damageFees: 500 }) }; mockDamageInfo = { description: 'Camera lens is cracked and unusable', canBeFixed: false, needsReplacement: true, replacementCost: 500, photos: ['photo1.jpg', 'photo2.jpg'], proofOfOwnership: ['receipt.pdf'] }; Rental.findByPk.mockResolvedValue(mockRental); LateReturnService.processLateReturn.mockResolvedValue({ lateCalculation: { lateFee: 0, isLate: false } }); emailService.sendDamageReportToCustomerService.mockResolvedValue(); }); it('should process damage assessment for replacement', async () => { const result = await DamageAssessmentService.processDamageAssessment( 'rental-123', mockDamageInfo, 'owner-789' ); expect(mockRental.update).toHaveBeenCalledWith({ status: 'damaged', damageFees: 500, damageAssessment: expect.objectContaining({ description: 'Camera lens is cracked and unusable', canBeFixed: false, needsReplacement: true, replacementCost: 500, feeCalculation: expect.objectContaining({ type: 'replacement', amount: 500 }) }) }); expect(emailService.sendDamageReportToCustomerService).toHaveBeenCalled(); expect(result.totalAdditionalFees).toBe(500); }); it('should process damage assessment for repair', async () => { const repairInfo = { description: 'Lens needs professional cleaning and adjustment', canBeFixed: true, needsReplacement: false, repairCost: 150 }; mockRental.update.mockResolvedValue({ ...mockRental, status: 'damaged', damageFees: 150 }); const result = await DamageAssessmentService.processDamageAssessment( 'rental-123', repairInfo, 'owner-789' ); expect(mockRental.update).toHaveBeenCalledWith({ status: 'damaged', damageFees: 150, damageAssessment: expect.objectContaining({ canBeFixed: true, needsReplacement: false, repairCost: 150, feeCalculation: expect.objectContaining({ type: 'repair', amount: 150 }) }) }); expect(result.totalAdditionalFees).toBe(150); }); it('should include late fees when provided', async () => { const actualReturnDateTime = new Date('2023-06-01T14:00:00Z'); LateReturnService.processLateReturn.mockResolvedValue({ lateCalculation: { lateFee: 50, isLate: true } }); mockRental.update.mockResolvedValue({ ...mockRental, status: 'damaged', damageFees: 500, lateFees: 50 }); const result = await DamageAssessmentService.processDamageAssessment( 'rental-123', { ...mockDamageInfo, actualReturnDateTime }, 'owner-789' ); expect(LateReturnService.processLateReturn).toHaveBeenCalledWith( 'rental-123', actualReturnDateTime, 'Item returned damaged: Camera lens is cracked and unusable' ); expect(result.totalAdditionalFees).toBe(550); // 500 damage + 50 late fee }); it('should throw error when rental not found', async () => { Rental.findByPk.mockResolvedValue(null); await expect( DamageAssessmentService.processDamageAssessment( 'nonexistent', mockDamageInfo, 'owner-789' ) ).rejects.toThrow('Rental not found'); }); it('should validate authorization - only owner can report', async () => { await expect( DamageAssessmentService.processDamageAssessment( 'rental-123', mockDamageInfo, 'renter-456' ) ).rejects.toThrow('Only the item owner can report damage'); }); it('should validate rental status - only active rentals', async () => { mockRental.status = 'completed'; await expect( DamageAssessmentService.processDamageAssessment( 'rental-123', mockDamageInfo, 'owner-789' ) ).rejects.toThrow('Can only assess damage for active rentals'); }); it('should validate required fields - description', async () => { await expect( DamageAssessmentService.processDamageAssessment( 'rental-123', { ...mockDamageInfo, description: '' }, 'owner-789' ) ).rejects.toThrow('Damage description is required'); }); it('should validate required fields - repair cost', async () => { await expect( DamageAssessmentService.processDamageAssessment( 'rental-123', { description: 'Needs repair', canBeFixed: true, needsReplacement: false }, 'owner-789' ) ).rejects.toThrow('Repair cost is required when item can be fixed'); }); it('should validate required fields - replacement cost', async () => { await expect( DamageAssessmentService.processDamageAssessment( 'rental-123', { description: 'Needs replacement', canBeFixed: false, needsReplacement: true }, 'owner-789' ) ).rejects.toThrow('Replacement cost is required when item needs replacement'); }); }); });