more icon image work
All checks were successful
Deploy to Web Server flyer-crawler.projectium.com / deploy (push) Successful in 4m24s
All checks were successful
Deploy to Web Server flyer-crawler.projectium.com / deploy (push) Successful in 4m24s
This commit is contained in:
@@ -4,7 +4,7 @@ import supertest from 'supertest';
|
||||
import express, { Request, Response, NextFunction } from 'express';
|
||||
import path from 'node:path';
|
||||
import fs from 'node:fs/promises';
|
||||
import adminRouter from './admin';
|
||||
import adminRouter from './admin'; // Correctly imported
|
||||
import * as db from '../services/db';
|
||||
import { UserProfile } from '../types';
|
||||
|
||||
@@ -378,6 +378,18 @@ describe('Admin Routes (/api/admin)', () => {
|
||||
await fs.unlink(dummyFilePath);
|
||||
});
|
||||
|
||||
// Add a cleanup hook to remove the file created on the server by multer
|
||||
afterAll(async () => {
|
||||
const uploadDir = path.resolve(__dirname, '../../../flyer-images');
|
||||
try {
|
||||
const files = await fs.readdir(uploadDir);
|
||||
const testFiles = files.filter(f => f.startsWith('logoImage-'));
|
||||
for (const file of testFiles) {
|
||||
await fs.unlink(path.join(uploadDir, file));
|
||||
}
|
||||
} catch (error) { console.error('Error during admin test file cleanup:', error); }
|
||||
});
|
||||
|
||||
it('should return a 400 error if no logo image is provided', async () => {
|
||||
// Arrange
|
||||
const brandId = 56;
|
||||
|
||||
@@ -18,7 +18,7 @@ import { flyerQueue, emailQueue, analyticsQueue } from '../services/queueService
|
||||
const router = Router();
|
||||
|
||||
// --- Multer Configuration for File Uploads ---
|
||||
const storagePath = process.env.STORAGE_PATH || '/var/www/flyer-crawler.projectium.com/assets';
|
||||
const storagePath = process.env.STORAGE_PATH || '/var/www/flyer-crawler.projectium.com/flyer-images';
|
||||
const storage = multer.diskStorage({
|
||||
destination: function (req, file, cb) {
|
||||
cb(null, storagePath);
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
// src/tests/integration/ai.integration.test.ts
|
||||
import { describe, it, expect, beforeAll } from 'vitest';
|
||||
import { describe, it, expect, beforeAll, afterAll } from 'vitest';
|
||||
import * as apiClient from '../../services/apiClient';
|
||||
import * as aiApiClient from '../../services/aiApiClient';
|
||||
import fs from 'fs/promises';
|
||||
import path from 'path';
|
||||
|
||||
/**
|
||||
* @vitest-environment node
|
||||
@@ -32,6 +34,21 @@ describe('AI API Routes Integration Tests', () => {
|
||||
authToken = loginToken;
|
||||
});
|
||||
|
||||
afterAll(async () => {
|
||||
// Clean up any files created in the flyer-images directory during these tests.
|
||||
const uploadDir = path.resolve(__dirname, '../../../flyer-images');
|
||||
try {
|
||||
const files = await fs.readdir(uploadDir);
|
||||
// Target files created by the 'image' and 'images' multer instances.
|
||||
const testFiles = files.filter(f => f.startsWith('image-') || f.startsWith('images-'));
|
||||
for (const file of testFiles) {
|
||||
await fs.unlink(path.join(uploadDir, file));
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error during AI integration test file cleanup:', error);
|
||||
}
|
||||
});
|
||||
|
||||
it('POST /api/ai/check-flyer should return a boolean', async () => {
|
||||
const mockImageFile = new File(['content'], 'test.jpg', { type: 'image/jpeg' });
|
||||
const response = await aiApiClient.isImageAFlyer(mockImageFile, authToken);
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// src/tests/integration/flyer-processing.integration.test.ts
|
||||
import { describe, it, expect, beforeAll, afterAll } from 'vitest';
|
||||
import fs from 'fs';
|
||||
import fs from 'fs/promises';
|
||||
import path from 'path';
|
||||
import * as apiClient from '../../services/apiClient';
|
||||
import * as aiApiClient from '../../services/aiApiClient';
|
||||
@@ -46,6 +46,21 @@ describe('Flyer Processing Background Job Integration Test', () => {
|
||||
if (createdUserIds.length > 0) {
|
||||
await getPool().query('DELETE FROM public.users WHERE user_id = ANY($1::uuid[])', [createdUserIds]);
|
||||
}
|
||||
|
||||
// Clean up any files created in the flyer-images directory during tests.
|
||||
const uploadDir = path.resolve(__dirname, '../../../flyer-images');
|
||||
try {
|
||||
const files = await fs.readdir(uploadDir);
|
||||
// Use a more specific filter to only target files created by this test suite.
|
||||
const testFiles = files.filter(f => f.includes('test-flyer-image'));
|
||||
for (const file of testFiles) {
|
||||
await fs.unlink(path.join(uploadDir, file));
|
||||
// Also try to remove from the icons subdirectory
|
||||
await fs.unlink(path.join(uploadDir, 'icons', `icon-${file}`)).catch(() => {});
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error during test file cleanup:', error);
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
@@ -55,7 +70,7 @@ describe('Flyer Processing Background Job Integration Test', () => {
|
||||
const runBackgroundProcessingTest = async (user?: User, token?: string) => {
|
||||
// Arrange: Load a mock flyer PDF.
|
||||
const imagePath = path.resolve(__dirname, '../assets/test-flyer-image.jpg');
|
||||
const imageBuffer = fs.readFileSync(imagePath);
|
||||
const imageBuffer = await fs.readFile(imagePath);
|
||||
const mockImageFile = new File([imageBuffer], 'test-flyer-image.jpg', { type: 'image/jpeg' });
|
||||
const checksum = await generateFileChecksum(mockImageFile);
|
||||
|
||||
|
||||
@@ -13,16 +13,24 @@ import { logger } from '../services/logger.server';
|
||||
*/
|
||||
export async function generateFlyerIcon(sourceImagePath: string, iconsDirectory: string): Promise<string> {
|
||||
try {
|
||||
const originalFileName = path.basename(sourceImagePath);
|
||||
const iconFileName = `icon-${originalFileName}`;
|
||||
// 1. Create a new filename, standardizing the extension to .webp for consistency and performance.
|
||||
// We parse the original filename to remove its extension before adding the new one.
|
||||
const originalFileName = path.basename(sourceImagePath, path.extname(sourceImagePath));
|
||||
const iconFileName = `icon-${originalFileName}.webp`;
|
||||
const iconOutputPath = path.join(iconsDirectory, iconFileName);
|
||||
|
||||
// Ensure the icons subdirectory exists.
|
||||
await fs.mkdir(iconsDirectory, { recursive: true });
|
||||
|
||||
// Use sharp to resize the image to 64x64.
|
||||
// 2. Use sharp to process the image.
|
||||
await sharp(sourceImagePath)
|
||||
.resize(64, 64)
|
||||
// Use `resize` with a `fit` strategy to prevent distortion.
|
||||
// `sharp.fit.cover` will resize to fill 64x64 and crop any excess,
|
||||
// ensuring the icon is always a non-distorted square.
|
||||
.resize(64, 64, { fit: sharp.fit.cover })
|
||||
// 3. Convert the output to WebP format.
|
||||
// The `quality` option is a good balance between size and clarity.
|
||||
.webp({ quality: 80 })
|
||||
.toFile(iconOutputPath);
|
||||
|
||||
logger.info(`Generated 64x64 icon: ${iconFileName}`);
|
||||
|
||||
Reference in New Issue
Block a user