import { describe, it, expect, beforeAll } from '@jest/globals'; import sharp from 'sharp'; import { isImage, getImageMetadata, generateDerivatives, THUMBNAIL_SIZE, } from './image-processor'; describe('image-processor', () => { let testPng: Buffer; let testJpeg: Buffer; const txtBuffer = Buffer.from('not an image'); beforeAll(async () => { testPng = await sharp({ create: { width: 800, height: 600, channels: 3, background: { r: 255, g: 0, b: 0 }, }, }) .png() .toBuffer(); testJpeg = await sharp({ create: { width: 400, height: 300, channels: 3, background: { r: 0, g: 0, b: 255 }, }, }) .jpeg() .toBuffer(); }); describe('isImage', () => { it('returns true for common image mime types', () => { expect(isImage('image/png')).toBe(true); expect(isImage('image/jpeg')).toBe(true); expect(isImage('image/webp')).toBe(true); expect(isImage('image/avif')).toBe(true); expect(isImage('image/gif')).toBe(true); }); it('returns false for non-image mime types', () => { expect(isImage('application/pdf')).toBe(false); expect(isImage('text/plain')).toBe(false); expect(isImage('video/mp4')).toBe(false); }); }); describe('getImageMetadata', () => { it('returns width and height for a valid image buffer', async () => { const meta = await getImageMetadata(testPng); expect(meta).toEqual({ width: 800, height: 600 }); }); it('returns null for non-image buffer', async () => { const meta = await getImageMetadata(txtBuffer); expect(meta).toBeNull(); }); }); describe('generateDerivatives', () => { it('generates thumbnail, webp and avif for image/png', async () => { const derivatives = await generateDerivatives(testPng, 'image/png'); expect(derivatives.thumbnail).toBeDefined(); expect(derivatives.webp).toBeDefined(); expect(derivatives.avif).toBeDefined(); expect(derivatives.thumbnail!.width).toBeLessThanOrEqual(THUMBNAIL_SIZE); expect(derivatives.thumbnail!.height).toBeLessThanOrEqual(THUMBNAIL_SIZE); expect(derivatives.thumbnail!.path).toMatch(/-thumbnail\./); expect(derivatives.thumbnail!.url).toMatch(/-thumbnail\./); expect(derivatives.webp!.path).toMatch(/\.webp$/); expect(derivatives.webp!.url).toMatch(/\.webp$/); expect(derivatives.avif!.path).toMatch(/\.avif$/); expect(derivatives.avif!.url).toMatch(/\.avif$/); }); it('keeps original aspect ratio for thumbnail', async () => { const derivatives = await generateDerivatives(testPng, 'image/png'); const thumb = derivatives.thumbnail!; expect(thumb.width / thumb.height).toBeCloseTo(800 / 600, 1); }); it('returns empty derivatives for non-image files', async () => { const derivatives = await generateDerivatives(txtBuffer, 'text/plain'); expect(Object.keys(derivatives)).toHaveLength(0); }); it('generates webp/avif with same dimensions as original', async () => { const derivatives = await generateDerivatives(testJpeg, 'image/jpeg'); expect(derivatives.webp!.width).toBe(400); expect(derivatives.webp!.height).toBe(300); expect(derivatives.avif!.width).toBe(400); expect(derivatives.avif!.height).toBe(300); }); }); });