Files
flyer-crawler.projectium.com/src/tests/e2e/flyer-upload.e2e.test.ts
Torben Sorensen b777430ff7
Some checks failed
Deploy to Test Environment / deploy-to-test (push) Has been cancelled
integration test fixes - claude for the win? try 4 - i have a good feeling
2026-01-09 05:18:19 -08:00

113 lines
4.5 KiB
TypeScript

// src/tests/e2e/flyer-upload.e2e.test.ts
import { describe, it, expect, afterAll } from 'vitest';
import crypto from 'crypto';
import * as apiClient from '../../services/apiClient';
import { getPool } from '../../services/db/connection.db';
import path from 'path';
import fs from 'fs';
import { cleanupDb } from '../utils/cleanup';
import { poll } from '../utils/poll';
/**
* @vitest-environment node
*/
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;
let storeId: number | null = null;
afterAll(async () => {
// Use the centralized cleanup utility for robustness.
await cleanupDb({
userIds: [userId],
flyerIds: [flyerId],
storeIds: [storeId],
});
});
it('should allow a user to upload a flyer and wait for processing to complete', async () => {
// 1. Register a new user
const registerResponse = await apiClient.registerUser(userEmail, userPassword, 'E2E Flyer Uploader');
expect(registerResponse.status).toBe(201);
// 2. Login to get the access token
const loginResponse = await apiClient.loginUser(userEmail, userPassword, false);
expect(loginResponse.status).toBe(200);
const loginData = await loginResponse.json();
authToken = loginData.token;
userId = loginData.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())
]);
}
// Create a File object for the apiClient
// FIX: The Node.js `Buffer` type can be incompatible with the web `File` API's
// expected `BlobPart` type in some TypeScript configurations. Explicitly creating
// a `Uint8Array` from the buffer ensures compatibility and resolves the type error.
// `Uint8Array` is a valid `BufferSource`, which is a valid `BlobPart`.
const flyerFile = new File([new Uint8Array(fileBuffer)], fileName, { type: 'image/jpeg' });
// Calculate checksum (required by the API)
const checksum = crypto.createHash('sha256').update(fileBuffer).digest('hex');
// 4. Upload the flyer
const uploadResponse = await apiClient.uploadAndProcessFlyer(flyerFile, checksum, authToken);
expect(uploadResponse.status).toBe(202);
const uploadData = await uploadResponse.json();
const jobId = uploadData.jobId;
expect(jobId).toBeDefined();
// 5. Poll for job completion using the new utility
const jobStatus = await poll(
async () => {
const statusResponse = await apiClient.getJobStatus(jobId, authToken);
return statusResponse.json();
},
(status) => status.state === 'completed' || status.state === 'failed',
{ timeout: 180000, interval: 3000, description: 'flyer processing job completion' },
);
if (jobStatus.state === 'failed') {
// Log the failure reason for easier debugging in CI/CD environments.
console.error('E2E flyer processing job failed. Reason:', jobStatus.failedReason);
}
expect(jobStatus.state).toBe('completed');
flyerId = jobStatus.returnValue?.flyerId;
expect(flyerId).toBeTypeOf('number');
// Fetch the store_id associated with the created flyer for robust cleanup
if (flyerId) {
const flyerRes = await getPool().query('SELECT store_id FROM public.flyers WHERE flyer_id = $1', [flyerId]);
if (flyerRes.rows.length > 0) {
storeId = flyerRes.rows[0].store_id;
}
}
}, 240000); // Extended timeout for AI processing
});