Files
rentall-app/backend/tests/unit/routes/conditionChecks.test.js
2026-01-06 17:28:20 -05:00

239 lines
7.1 KiB
JavaScript

const request = require('supertest');
const express = require('express');
// Mock dependencies
jest.mock('../../../middleware/auth', () => ({
authenticateToken: (req, res, next) => {
req.user = { id: 'user-123' };
next();
},
}));
jest.mock('../../../services/conditionCheckService', () => ({
submitConditionCheck: jest.fn(),
getAvailableChecks: jest.fn(),
}));
jest.mock('../../../utils/logger', () => ({
info: jest.fn(),
error: jest.fn(),
warn: jest.fn(),
withRequestId: jest.fn(() => ({
info: jest.fn(),
error: jest.fn(),
warn: jest.fn(),
})),
}));
jest.mock('../../../utils/s3KeyValidator', () => ({
validateS3Keys: jest.fn().mockReturnValue({ valid: true }),
}));
jest.mock('../../../config/imageLimits', () => ({
IMAGE_LIMITS: { conditionChecks: 10 },
}));
const ConditionCheckService = require('../../../services/conditionCheckService');
const { validateS3Keys } = require('../../../utils/s3KeyValidator');
const conditionCheckRoutes = require('../../../routes/conditionChecks');
const app = express();
app.use(express.json());
app.use('/condition-checks', conditionCheckRoutes);
// Error handler
app.use((err, req, res, next) => {
res.status(500).json({ error: err.message });
});
describe('Condition Check Routes', () => {
beforeEach(() => {
jest.clearAllMocks();
});
describe('POST /condition-checks/:rentalId', () => {
const validConditionCheck = {
checkType: 'pre_rental',
notes: 'Item in good condition',
imageFilenames: ['condition-checks/uuid1.jpg', 'condition-checks/uuid2.jpg'],
};
it('should submit a condition check successfully', async () => {
const mockConditionCheck = {
id: 'check-1',
rentalId: 'rental-123',
checkType: 'pre_rental',
notes: 'Item in good condition',
imageFilenames: validConditionCheck.imageFilenames,
submittedBy: 'user-123',
createdAt: new Date().toISOString(),
};
ConditionCheckService.submitConditionCheck.mockResolvedValue(mockConditionCheck);
const response = await request(app)
.post('/condition-checks/rental-123')
.send(validConditionCheck);
expect(response.status).toBe(201);
expect(response.body.success).toBe(true);
expect(response.body.conditionCheck).toMatchObject({
id: 'check-1',
checkType: 'pre_rental',
});
expect(ConditionCheckService.submitConditionCheck).toHaveBeenCalledWith(
'rental-123',
'pre_rental',
'user-123',
validConditionCheck.imageFilenames,
'Item in good condition'
);
});
it('should handle empty image array', async () => {
const mockConditionCheck = {
id: 'check-1',
rentalId: 'rental-123',
checkType: 'post_rental',
imageFilenames: [],
};
ConditionCheckService.submitConditionCheck.mockResolvedValue(mockConditionCheck);
const response = await request(app)
.post('/condition-checks/rental-123')
.send({
checkType: 'post_rental',
notes: 'No photos',
});
expect(response.status).toBe(201);
expect(response.body.success).toBe(true);
expect(ConditionCheckService.submitConditionCheck).toHaveBeenCalledWith(
'rental-123',
'post_rental',
'user-123',
[],
'No photos'
);
});
it('should reject invalid S3 keys', async () => {
validateS3Keys.mockReturnValueOnce({
valid: false,
error: 'Invalid S3 key format',
invalidKeys: ['invalid-key'],
});
const response = await request(app)
.post('/condition-checks/rental-123')
.send({
checkType: 'pre_rental',
imageFilenames: ['invalid-key'],
});
expect(response.status).toBe(400);
expect(response.body.success).toBe(false);
expect(response.body.error).toBe('Invalid S3 key format');
expect(response.body.details).toContain('invalid-key');
});
it('should handle service errors', async () => {
ConditionCheckService.submitConditionCheck.mockRejectedValue(
new Error('Rental not found')
);
const response = await request(app)
.post('/condition-checks/rental-123')
.send(validConditionCheck);
expect(response.status).toBe(400);
expect(response.body.success).toBe(false);
expect(response.body.error).toBe('Rental not found');
});
it('should handle non-array imageFilenames gracefully', async () => {
const mockConditionCheck = {
id: 'check-1',
rentalId: 'rental-123',
checkType: 'pre_rental',
imageFilenames: [],
};
ConditionCheckService.submitConditionCheck.mockResolvedValue(mockConditionCheck);
const response = await request(app)
.post('/condition-checks/rental-123')
.send({
checkType: 'pre_rental',
imageFilenames: 'not-an-array',
});
expect(response.status).toBe(201);
// Should convert to empty array
expect(ConditionCheckService.submitConditionCheck).toHaveBeenCalledWith(
'rental-123',
'pre_rental',
'user-123',
[],
undefined
);
});
});
describe('GET /condition-checks', () => {
it('should return available checks for specified rentals', async () => {
const mockAvailableChecks = [
{
rentalId: 'rental-1',
itemName: 'Camera',
checkType: 'pre_rental',
dueDate: '2024-01-10',
},
{
rentalId: 'rental-2',
itemName: 'Laptop',
checkType: 'post_rental',
dueDate: '2024-01-15',
},
];
ConditionCheckService.getAvailableChecks.mockResolvedValue(mockAvailableChecks);
const response = await request(app)
.get('/condition-checks?rentalIds=rental-1,rental-2');
expect(response.status).toBe(200);
expect(response.body.success).toBe(true);
expect(response.body.availableChecks).toHaveLength(2);
expect(response.body.availableChecks[0].itemName).toBe('Camera');
expect(ConditionCheckService.getAvailableChecks).toHaveBeenCalledWith('user-123', ['rental-1', 'rental-2']);
});
it('should return empty array when no rentalIds provided', async () => {
ConditionCheckService.getAvailableChecks.mockResolvedValue([]);
const response = await request(app)
.get('/condition-checks');
expect(response.status).toBe(200);
expect(response.body.success).toBe(true);
expect(response.body.availableChecks).toHaveLength(0);
expect(ConditionCheckService.getAvailableChecks).toHaveBeenCalledWith('user-123', []);
});
it('should handle service errors', async () => {
ConditionCheckService.getAvailableChecks.mockRejectedValue(
new Error('Database error')
);
const response = await request(app)
.get('/condition-checks?rentalIds=rental-1');
expect(response.status).toBe(500);
expect(response.body.success).toBe(false);
expect(response.body.error).toBe('Failed to fetch available checks');
});
});
});