import { promises as fs } from 'node:fs' import os from 'node:os' import path from 'node:path' import sharp from 'sharp' import { afterEach, beforeEach, describe, expect, it } from 'vitest' import type { ProcessOptions } from '../../src/shared/types' import { ImageProcessor, processSingleImage, readSofHeight, } from './image-processor' describe('image processing algorithm', () => { let temporaryDirectory: string let inputPath: string beforeEach(async () => { temporaryDirectory = await fs.mkdtemp(path.join(os.tmpdir(), 'genuine-qingquwang-')) inputPath = path.join(temporaryDirectory, 'sample.jpg') await sharp({ create: { width: 18, height: 12, channels: 3, background: { r: 23, g: 105, b: 224 }, }, }).jpeg({ quality: 90 }).toFile(inputPath) }) afterEach(async () => { await fs.rm(temporaryDirectory, { recursive: true, force: true }) }) it('writes a .file without EOI while restoring original SOF height', async () => { const outputPath = await processSingleImage(inputPath, options('file', 0)) const output = await fs.readFile(outputPath) expect(outputPath).toBe(path.join(temporaryDirectory, 'sample.file')) expect(readSofHeight(output)).toBe(12) expect(output.subarray(-2).equals(Buffer.from([0xff, 0xd9]))).toBe(false) }) it('supports .file.jpg and appends the configured random byte count', async () => { const baselinePath = await processSingleImage(inputPath, options('file', 0)) const baselineSize = (await fs.stat(baselinePath)).size const outputPath = await processSingleImage(inputPath, options('file-jpg', 0.01)) const outputSize = (await fs.stat(outputPath)).size expect(outputPath).toBe(path.join(temporaryDirectory, 'sample.file.jpg')) expect(outputSize - baselineSize).toBe(Math.floor(0.01 * 1024 * 1024)) }) it('discovers and converts supported non-JPEG inputs into JPEG delivery files', async () => { const source = sharp({ create: { width: 18, height: 12, channels: 4, background: { r: 23, g: 105, b: 224, alpha: 0.5 }, }, }) const inputs = [ path.join(temporaryDirectory, 'sample.png'), path.join(temporaryDirectory, 'sample.webp'), path.join(temporaryDirectory, 'sample.gif'), path.join(temporaryDirectory, 'sample.tiff'), path.join(temporaryDirectory, 'sample.avif'), ] await Promise.all([ source.clone().png().toFile(inputs[0]), source.clone().webp().toFile(inputs[1]), source.clone().gif().toFile(inputs[2]), source.clone().tiff().toFile(inputs[3]), source.clone().avif().toFile(inputs[4]), fs.writeFile(path.join(temporaryDirectory, 'sample.svg'), ''), fs.writeFile(path.join(temporaryDirectory, 'ignored.bmp'), 'not-supported'), ]) const processor = new ImageProcessor() const inspected = await processor.inspect([temporaryDirectory]) const inspectedNames = inspected.map((item) => item.name) expect(inspectedNames).toEqual([ 'sample.avif', 'sample.gif', 'sample.jpg', 'sample.png', 'sample.svg', 'sample.tiff', 'sample.webp', ]) for (const input of [...inputs, path.join(temporaryDirectory, 'sample.svg')]) { const outputPath = await processSingleImage(input, options('file', 0)) expect(readSofHeight(await fs.readFile(outputPath))).toBe(12) } }) function options(outputFormat: 'file' | 'file-jpg', junkSizeMb: number): ProcessOptions { return { paths: [inputPath], useSourceDirectory: true, outputFormat, junkSizeMb, } } })