more icon image work
All checks were successful
Deploy to Web Server flyer-crawler.projectium.com / deploy (push) Successful in 4m24s

This commit is contained in:
2025-12-03 13:28:59 -08:00
parent 29484b61c2
commit ba778a20d7
5 changed files with 61 additions and 9 deletions

View File

@@ -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;

View File

@@ -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);

View File

@@ -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);

View File

@@ -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);

View File

@@ -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}`);