219 lines
6.5 KiB
JavaScript
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');
|
|
});
|
|
});
|
|
}); |