ai: validate flyer payload, prevent DB crashes, and add tests for /api/ai/flyers/process
All checks were successful
Deploy to Web Server flyer-crawler.projectium.com / deploy (push) Successful in 4m29s
All checks were successful
Deploy to Web Server flyer-crawler.projectium.com / deploy (push) Successful in 4m29s
This commit is contained in:
@@ -156,6 +156,66 @@ describe('AI Routes (/api/ai)', () => {
|
||||
expect(response.status).toBe(400);
|
||||
expect(response.body.message).toBe('Flyer image file is required.');
|
||||
});
|
||||
|
||||
it('should return 400 if extractedData is missing from payload', async () => {
|
||||
const badPayload = { checksum: 'c2', originalFileName: 'noflyer.jpg' }; // no extractedData
|
||||
|
||||
const response = await supertest(app)
|
||||
.post('/api/ai/flyers/process')
|
||||
.field('data', JSON.stringify(badPayload))
|
||||
.attach('flyerImage', imagePath);
|
||||
|
||||
expect(response.status).toBe(400);
|
||||
expect(response.body.message).toBe('Invalid request: extractedData is required.');
|
||||
expect(mockedDb.createFlyerAndItems).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should accept payload when extractedData.items is missing and save with empty items', async () => {
|
||||
// Arrange: extractedData present but items missing
|
||||
const partialPayload = {
|
||||
checksum: 'test-checksum-2',
|
||||
originalFileName: 'flyer2.jpg',
|
||||
extractedData: { store_name: 'Partial Store' } // no items key
|
||||
};
|
||||
|
||||
mockedDb.findFlyerByChecksum.mockResolvedValue(undefined);
|
||||
mockedDb.createFlyerAndItems.mockResolvedValue({ flyer_id: 2, created_at: new Date().toISOString(), file_name: partialPayload.originalFileName, image_url: '/flyer-images/flyer2.jpg' } as any);
|
||||
|
||||
const response = await supertest(app)
|
||||
.post('/api/ai/flyers/process')
|
||||
.field('data', JSON.stringify(partialPayload))
|
||||
.attach('flyerImage', imagePath);
|
||||
|
||||
expect(response.status).toBe(201);
|
||||
expect(mockedDb.createFlyerAndItems).toHaveBeenCalledTimes(1);
|
||||
// verify the items array passed to DB was an empty array
|
||||
const callArgs = mockedDb.createFlyerAndItems.mock.calls[0]?.[1];
|
||||
expect(callArgs).toBeDefined();
|
||||
expect(Array.isArray(callArgs)).toBe(true);
|
||||
expect(callArgs.length).toBe(0);
|
||||
});
|
||||
|
||||
it('should fallback to a safe store name when store_name is missing', async () => {
|
||||
const payloadNoStore = {
|
||||
checksum: 'test-checksum-3',
|
||||
originalFileName: 'flyer3.jpg',
|
||||
extractedData: { items: [] } // store_name missing
|
||||
};
|
||||
|
||||
mockedDb.findFlyerByChecksum.mockResolvedValue(undefined);
|
||||
mockedDb.createFlyerAndItems.mockResolvedValue({ flyer_id: 3, created_at: new Date().toISOString(), file_name: payloadNoStore.originalFileName, image_url: '/flyer-images/flyer3.jpg' } as any);
|
||||
|
||||
const response = await supertest(app)
|
||||
.post('/api/ai/flyers/process')
|
||||
.field('data', JSON.stringify(payloadNoStore))
|
||||
.attach('flyerImage', imagePath);
|
||||
|
||||
expect(response.status).toBe(201);
|
||||
expect(mockedDb.createFlyerAndItems).toHaveBeenCalledTimes(1);
|
||||
// verify the flyerData.store_name passed to DB was the fallback string
|
||||
const flyerDataArg = mockedDb.createFlyerAndItems.mock.calls[0][0];
|
||||
expect(flyerDataArg.store_name).toContain('Unknown Store');
|
||||
});
|
||||
});
|
||||
|
||||
describe('when user is authenticated', () => {
|
||||
|
||||
Reference in New Issue
Block a user