110 lines
3.9 KiB
TypeScript
110 lines
3.9 KiB
TypeScript
// src/tests/e2e/flyer-upload.e2e.test.ts
|
|
import { describe, it, expect, afterAll } from 'vitest';
|
|
import supertest from 'supertest';
|
|
import app from '../../../server';
|
|
import { getPool } from '../../services/db/connection.db';
|
|
import crypto from 'crypto';
|
|
import path from 'path';
|
|
import fs from 'fs';
|
|
|
|
/**
|
|
* @vitest-environment node
|
|
*/
|
|
|
|
const request = supertest(app);
|
|
|
|
describe('E2E Flyer Upload and Processing Workflow', () => {
|
|
const uniqueId = Date.now();
|
|
const userEmail = `e2e-uploader-${uniqueId}@example.com`;
|
|
const userPassword = 'StrongPassword123!';
|
|
|
|
let authToken: string;
|
|
let userId: string | null = null;
|
|
let flyerId: number | null = null;
|
|
|
|
afterAll(async () => {
|
|
// Cleanup: Delete the flyer and user created during the test
|
|
const pool = getPool();
|
|
if (flyerId) {
|
|
await pool.query('DELETE FROM public.flyers WHERE flyer_id = $1', [flyerId]);
|
|
}
|
|
if (userId) {
|
|
await pool.query('DELETE FROM public.users WHERE user_id = $1', [userId]);
|
|
}
|
|
});
|
|
|
|
it('should allow a user to upload a flyer and wait for processing to complete', async () => {
|
|
// 1. Register a new user
|
|
const registerResponse = await request.post('/api/auth/register').send({
|
|
email: userEmail,
|
|
password: userPassword,
|
|
full_name: 'E2E Flyer Uploader',
|
|
});
|
|
expect(registerResponse.status).toBe(201);
|
|
|
|
// 2. Login to get the access token
|
|
const loginResponse = await request.post('/api/auth/login').send({
|
|
email: userEmail,
|
|
password: userPassword,
|
|
});
|
|
expect(loginResponse.status).toBe(200);
|
|
authToken = loginResponse.body.token;
|
|
userId = loginResponse.body.userprofile.user.user_id;
|
|
expect(authToken).toBeDefined();
|
|
|
|
// 3. Prepare the flyer file
|
|
// We try to use the existing test asset if available, otherwise create a dummy buffer.
|
|
// Note: In a real E2E scenario against a live AI service, a valid image is required.
|
|
// If the AI service is mocked or stubbed in this environment, a dummy buffer might suffice.
|
|
let fileBuffer: Buffer;
|
|
let fileName = `e2e-test-flyer-${uniqueId}.jpg`;
|
|
|
|
const assetPath = path.resolve(__dirname, '../assets/test-flyer-image.jpg');
|
|
if (fs.existsSync(assetPath)) {
|
|
const rawBuffer = fs.readFileSync(assetPath);
|
|
// Append unique ID to ensure unique checksum for every test run
|
|
fileBuffer = Buffer.concat([rawBuffer, Buffer.from(uniqueId.toString())]);
|
|
} else {
|
|
// Fallback to a minimal valid JPEG header + random data if asset is missing
|
|
// (This might fail if the backend does strict image validation/processing)
|
|
fileBuffer = Buffer.concat([
|
|
Buffer.from([0xff, 0xd8, 0xff, 0xe0]), // JPEG Start of Image
|
|
Buffer.from(uniqueId.toString())
|
|
]);
|
|
}
|
|
|
|
// Calculate checksum (required by the API)
|
|
const checksum = crypto.createHash('sha256').update(fileBuffer).digest('hex');
|
|
|
|
// 4. Upload the flyer
|
|
const uploadResponse = await request
|
|
.post('/api/ai/upload-and-process')
|
|
.set('Authorization', `Bearer ${authToken}`)
|
|
.field('checksum', checksum)
|
|
.attach('flyerFile', fileBuffer, fileName);
|
|
|
|
expect(uploadResponse.status).toBe(202);
|
|
const jobId = uploadResponse.body.jobId;
|
|
expect(jobId).toBeDefined();
|
|
|
|
// 5. Poll for job completion
|
|
let jobStatus;
|
|
const maxRetries = 30; // Poll for up to 90 seconds
|
|
for (let i = 0; i < maxRetries; i++) {
|
|
await new Promise((resolve) => setTimeout(resolve, 3000)); // Wait 3s
|
|
|
|
const statusResponse = await request
|
|
.get(`/api/ai/jobs/${jobId}/status`)
|
|
.set('Authorization', `Bearer ${authToken}`);
|
|
|
|
jobStatus = statusResponse.body;
|
|
if (jobStatus.state === 'completed' || jobStatus.state === 'failed') {
|
|
break;
|
|
}
|
|
}
|
|
|
|
expect(jobStatus.state).toBe('completed');
|
|
flyerId = jobStatus.returnValue?.flyerId;
|
|
expect(flyerId).toBeTypeOf('number');
|
|
}, 120000); // Extended timeout for AI processing
|
|
}); |