updating unit and integration tests
This commit is contained in:
328
backend/tests/unit/routes/conditionChecks.test.js
Normal file
328
backend/tests/unit/routes/conditionChecks.test.js
Normal file
@@ -0,0 +1,328 @@
|
||||
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(),
|
||||
getConditionChecks: jest.fn(),
|
||||
getConditionCheckTimeline: 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/:rentalId', () => {
|
||||
it('should return condition checks for a rental', async () => {
|
||||
const mockChecks = [
|
||||
{
|
||||
id: 'check-1',
|
||||
checkType: 'pre_rental',
|
||||
notes: 'Good condition',
|
||||
createdAt: '2024-01-01T00:00:00Z',
|
||||
},
|
||||
{
|
||||
id: 'check-2',
|
||||
checkType: 'post_rental',
|
||||
notes: 'Minor wear',
|
||||
createdAt: '2024-01-15T00:00:00Z',
|
||||
},
|
||||
];
|
||||
|
||||
ConditionCheckService.getConditionChecks.mockResolvedValue(mockChecks);
|
||||
|
||||
const response = await request(app)
|
||||
.get('/condition-checks/rental-123');
|
||||
|
||||
expect(response.status).toBe(200);
|
||||
expect(response.body.success).toBe(true);
|
||||
expect(response.body.conditionChecks).toHaveLength(2);
|
||||
expect(response.body.conditionChecks[0].checkType).toBe('pre_rental');
|
||||
expect(ConditionCheckService.getConditionChecks).toHaveBeenCalledWith('rental-123');
|
||||
});
|
||||
|
||||
it('should return empty array when no checks exist', async () => {
|
||||
ConditionCheckService.getConditionChecks.mockResolvedValue([]);
|
||||
|
||||
const response = await request(app)
|
||||
.get('/condition-checks/rental-456');
|
||||
|
||||
expect(response.status).toBe(200);
|
||||
expect(response.body.success).toBe(true);
|
||||
expect(response.body.conditionChecks).toHaveLength(0);
|
||||
});
|
||||
|
||||
it('should handle service errors', async () => {
|
||||
ConditionCheckService.getConditionChecks.mockRejectedValue(
|
||||
new Error('Database error')
|
||||
);
|
||||
|
||||
const response = await request(app)
|
||||
.get('/condition-checks/rental-123');
|
||||
|
||||
expect(response.status).toBe(500);
|
||||
expect(response.body.success).toBe(false);
|
||||
expect(response.body.error).toBe('Failed to fetch condition checks');
|
||||
});
|
||||
});
|
||||
|
||||
describe('GET /condition-checks/:rentalId/timeline', () => {
|
||||
it('should return condition check timeline', async () => {
|
||||
const mockTimeline = {
|
||||
rental: { id: 'rental-123', status: 'completed' },
|
||||
checks: [
|
||||
{ type: 'pre_rental', status: 'completed', completedAt: '2024-01-01' },
|
||||
{ type: 'post_rental', status: 'pending', completedAt: null },
|
||||
],
|
||||
};
|
||||
|
||||
ConditionCheckService.getConditionCheckTimeline.mockResolvedValue(mockTimeline);
|
||||
|
||||
const response = await request(app)
|
||||
.get('/condition-checks/rental-123/timeline');
|
||||
|
||||
expect(response.status).toBe(200);
|
||||
expect(response.body.success).toBe(true);
|
||||
expect(response.body.timeline).toMatchObject(mockTimeline);
|
||||
expect(ConditionCheckService.getConditionCheckTimeline).toHaveBeenCalledWith('rental-123');
|
||||
});
|
||||
|
||||
it('should handle service errors', async () => {
|
||||
ConditionCheckService.getConditionCheckTimeline.mockRejectedValue(
|
||||
new Error('Rental not found')
|
||||
);
|
||||
|
||||
const response = await request(app)
|
||||
.get('/condition-checks/rental-123/timeline');
|
||||
|
||||
expect(response.status).toBe(500);
|
||||
expect(response.body.success).toBe(false);
|
||||
expect(response.body.error).toBe('Rental not found');
|
||||
});
|
||||
});
|
||||
|
||||
describe('GET /condition-checks', () => {
|
||||
it('should return available checks for current user', 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');
|
||||
|
||||
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');
|
||||
});
|
||||
|
||||
it('should return empty array when no checks available', 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);
|
||||
});
|
||||
|
||||
it('should handle service errors', async () => {
|
||||
ConditionCheckService.getAvailableChecks.mockRejectedValue(
|
||||
new Error('Database error')
|
||||
);
|
||||
|
||||
const response = await request(app)
|
||||
.get('/condition-checks');
|
||||
|
||||
expect(response.status).toBe(500);
|
||||
expect(response.body.success).toBe(false);
|
||||
expect(response.body.error).toBe('Failed to fetch available checks');
|
||||
});
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user