// @vitest-environment node import { describe, expect, it } from 'vitest' import sharp from 'sharp' import { reencode } from '@/lib/files/reencode' import { detectType } from '@/lib/files/magic' /** * Die Neukodierung ist die wirksamste Massnahme der Kette: ein Bild, das durch * `sharp` gelaufen ist, besteht nur noch aus Pixeln. * * Diese Tests prüfen genau das - nicht, dass irgendetwas herauskommt, sondern * dass das, was nicht Inhalt ist, **weg** ist. */ async function testbild(): Promise { return sharp({ create: { width: 24, height: 16, channels: 3, background: { r: 200, g: 40, b: 40 } }, }) .jpeg() .withMetadata({ exif: { IFD0: { Copyright: 'GEHEIMER FOTOGRAF', Software: 'Kamera 9000' }, }, }) .toBuffer() } describe('Bilder neu kodieren', () => { it('wirft die Metadaten weg, statt sie zu filtern', async () => { const original = await testbild() // Gegenprobe: das Original trägt die Daten wirklich. expect(original.includes(Buffer.from('GEHEIMER FOTOGRAF'))).toBe(true) const ergebnis = await reencode(new Uint8Array(original), 'image/jpeg') expect(ergebnis.ok).toBe(true) if (!ergebnis.ok) return const neu = Buffer.from(ergebnis.result.content) expect(neu.includes(Buffer.from('GEHEIMER FOTOGRAF'))).toBe(false) expect(neu.includes(Buffer.from('Kamera 9000'))).toBe(false) // Und es ist noch dasselbe Bild. const info = await sharp(neu).metadata() expect(info.width).toBe(24) expect(info.height).toBe(16) expect(ergebnis.result.width).toBe(24) }) it('zerstört eine angehängte Nutzlast', async () => { // Der klassische Trick: hinter das Bildende Daten kleben. Nach der // Neukodierung ist davon nichts mehr da, weil nur die Pixel übernommen // werden. const angehaengt = Buffer.concat([ await testbild(), Buffer.from(''), ]) const ergebnis = await reencode(new Uint8Array(angehaengt), 'image/jpeg') expect(ergebnis.ok).toBe(true) if (!ergebnis.ok) return expect(Buffer.from(ergebnis.result.content).includes(Buffer.from(' { const ergebnis = await reencode(new Uint8Array(await testbild()), 'image/jpeg') expect(ergebnis.ok).toBe(true) if (!ergebnis.ok) return expect(ergebnis.result.thumb).toBeDefined() // Das Vorschaubild ist immer WebP, damit die Auslieferung es gefahrlos // inline schicken kann. expect(detectType(ergebnis.result.thumb!)).toEqual({ ok: true, type: 'image/webp' }) expect(ergebnis.result.blurhash).toMatch(/^[\w#$%*+,\-.:;=?@[\]^_{|}~]{6,}$/) }) it('lehnt ab, was kein Bild ist', async () => { const ergebnis = await reencode(new TextEncoder().encode('kein Bild'), 'image/png') expect(ergebnis.ok).toBe(false) }) }) describe('Text normalisieren', () => { it('entfernt die Stückliste am Anfang', async () => { const mitBom = new TextEncoder().encode('Hallo Welt') const ergebnis = await reencode(mitBom, 'text/plain') expect(ergebnis.ok).toBe(true) if (!ergebnis.ok) return expect(new TextDecoder().decode(ergebnis.result.content)).toBe('Hallo Welt') }) it('lehnt ungültiges UTF-8 ab', async () => { const ergebnis = await reencode(new Uint8Array([0xff, 0xfe, 0x41]), 'text/plain') expect(ergebnis.ok).toBe(false) }) })