Files
rentall-app/backend/tests/unit/services/damageAssessmentService.test.js
jackiettran 9a9e96d007 tests
2025-10-06 16:05:29 -04:00

219 lines
6.5 KiB
JavaScript

// 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');
});
});
});