const ConditionCheckService = require('../../../services/conditionCheckService'); const { ConditionCheck, Rental, User } = require('../../../models'); jest.mock('../../../models'); describe('ConditionCheckService', () => { beforeEach(() => { jest.clearAllMocks(); }); describe('submitConditionCheck', () => { // Set rental dates relative to current time for valid time window const now = new Date(); const mockRental = { id: 'rental-123', ownerId: 'owner-456', renterId: 'renter-789', startDateTime: new Date(now.getTime() - 1000 * 60 * 60), // 1 hour ago endDateTime: new Date(now.getTime() + 1000 * 60 * 60 * 24), // 24 hours from now status: 'active' }; const mockPhotos = ['/uploads/photo1.jpg', '/uploads/photo2.jpg']; beforeEach(() => { Rental.findByPk.mockResolvedValue(mockRental); ConditionCheck.findOne.mockResolvedValue(null); // No existing check ConditionCheck.create.mockResolvedValue({ id: 'check-123', rentalId: 'rental-123', checkType: 'rental_start_renter', photos: mockPhotos, notes: 'Item received in good condition', submittedBy: 'renter-789' }); }); it('should submit condition check with photos and notes', async () => { const result = await ConditionCheckService.submitConditionCheck( 'rental-123', 'rental_start_renter', 'renter-789', mockPhotos, 'Item received in good condition' ); expect(ConditionCheck.create).toHaveBeenCalledWith( expect.objectContaining({ rentalId: 'rental-123', checkType: 'rental_start_renter', submittedBy: 'renter-789', photos: mockPhotos, notes: 'Item received in good condition', }) ); expect(result).toBeTruthy(); expect(result.id).toBe('check-123'); }); it('should validate user authorization - owner checks', async () => { // Renter trying to submit pre-rental owner check await expect( ConditionCheckService.submitConditionCheck( 'rental-123', 'pre_rental_owner', 'renter-789', mockPhotos ) ).rejects.toThrow('Only the item owner can submit owner condition checks'); }); it('should validate user authorization - renter checks', async () => { // Owner trying to submit rental start renter check await expect( ConditionCheckService.submitConditionCheck( 'rental-123', 'rental_start_renter', 'owner-456', mockPhotos ) ).rejects.toThrow('Only the renter can submit renter condition checks'); }); it('should prevent duplicate condition checks', async () => { ConditionCheck.findOne.mockResolvedValue({ id: 'existing-check' }); await expect( ConditionCheckService.submitConditionCheck( 'rental-123', 'rental_start_renter', 'renter-789', mockPhotos ) ).rejects.toThrow('Condition check already submitted for this type'); }); it('should limit number of photos to 20', async () => { const tooManyPhotos = Array(21).fill('/uploads/photo.jpg'); await expect( ConditionCheckService.submitConditionCheck( 'rental-123', 'rental_start_renter', 'renter-789', tooManyPhotos ) ).rejects.toThrow('Maximum 20 photos allowed per condition check'); }); it('should handle rental not found', async () => { Rental.findByPk.mockResolvedValue(null); await expect( ConditionCheckService.submitConditionCheck( 'nonexistent-rental', 'rental_start_renter', 'renter-789', mockPhotos ) ).rejects.toThrow('Rental not found'); }); }); describe('getConditionChecks', () => { it('should retrieve condition checks for rental', async () => { const mockChecks = [ { id: 'check-1', checkType: 'pre_rental_owner', submittedBy: 'owner-456', submittedAt: '2023-05-31T12:00:00Z', photos: ['/uploads/photo1.jpg'], notes: 'Item ready' }, { id: 'check-2', checkType: 'rental_start_renter', submittedBy: 'renter-789', submittedAt: '2023-06-01T11:00:00Z', photos: ['/uploads/photo2.jpg'], notes: 'Item received' } ]; ConditionCheck.findAll.mockResolvedValue(mockChecks); const result = await ConditionCheckService.getConditionChecks('rental-123'); expect(ConditionCheck.findAll).toHaveBeenCalledWith({ where: { rentalId: 'rental-123' }, include: [{ model: User, as: 'submittedByUser', attributes: ['id', 'firstName', 'lastName'] }], order: [['submittedAt', 'ASC']] }); expect(result).toEqual(mockChecks); expect(result.length).toBe(2); }); }); });