News: Fotos verkleinern und Metadaten entfernen (sharp)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
8374a570aa
commit
ac7375e889
5 files changed
+815
No files matched your search
@@ -0,0 +1,86 @@
|
||||
import test from 'node:test'
|
||||
import assert from 'node:assert/strict'
|
||||
import os from 'node:os'
|
||||
import path from 'node:path'
|
||||
import { mkdtempSync, existsSync } from 'node:fs'
|
||||
import { readFile, writeFile } from 'node:fs/promises'
|
||||
import sharp from 'sharp'
|
||||
|
||||
process.env.NEWS_UPLOAD_DIR = mkdtempSync(path.join(os.tmpdir(), 'news-images-'))
|
||||
const { storeNewsImage, claimNewsImage, newsImagePath, purgeUnclaimedNewsImages, newsImageRateLimited, NEWS_IMAGE_MAX_BYTES } =
|
||||
await import('../server/services/newsImages.js')
|
||||
|
||||
const solid = (width, height) => sharp({ create: { width, height, channels: 3, background: '#c33' } })
|
||||
|
||||
test('exif_gps_entfernt_und_gedreht', async () => {
|
||||
const input = await solid(1200, 800)
|
||||
.withMetadata({ orientation: 6 })
|
||||
.withExif({ IFD3: { GPSLatitudeRef: 'N', GPSLatitude: '48/1 30/1 0/1' } })
|
||||
.jpeg().toBuffer()
|
||||
const inMeta = await sharp(input).metadata()
|
||||
assert.ok(inMeta.exif, 'Fixture hat EXIF')
|
||||
assert.equal(inMeta.orientation, 6)
|
||||
const { imageId } = await storeNewsImage({ customerId: 'c1', projectId: 'p1', buffer: input })
|
||||
for (const size of ['l', 's']) {
|
||||
const meta = await sharp(newsImagePath('p1', imageId, size)).metadata()
|
||||
assert.equal(meta.exif, undefined)
|
||||
assert.ok(meta.width < meta.height, `${size}: gedreht`)
|
||||
}
|
||||
})
|
||||
|
||||
test('verkleinert_auf_1600_und_480', async () => {
|
||||
const input = await solid(4000, 3000).png().toBuffer()
|
||||
const { imageId, w, h } = await storeNewsImage({ customerId: 'c1', projectId: 'p1', buffer: input })
|
||||
assert.deepEqual([w, h], [1600, 1200])
|
||||
const l = await sharp(newsImagePath('p1', imageId, 'l')).metadata()
|
||||
const s = await sharp(newsImagePath('p1', imageId, 's')).metadata()
|
||||
assert.deepEqual([l.format, l.width, l.height], ['webp', 1600, 1200])
|
||||
assert.deepEqual([s.format, s.width, s.height], ['webp', 480, 360])
|
||||
})
|
||||
|
||||
test('zu_gross_413', async () => {
|
||||
await assert.rejects(
|
||||
storeNewsImage({ customerId: 'c1', projectId: 'p1', buffer: Buffer.alloc(NEWS_IMAGE_MAX_BYTES + 1) }),
|
||||
{ status: 413 },
|
||||
)
|
||||
})
|
||||
|
||||
test('falscher_typ_415', async () => {
|
||||
await assert.rejects(
|
||||
storeNewsImage({ customerId: 'c1', projectId: 'p1', buffer: Buffer.from('%PDF-1.4\n1 0 obj\n<<>>\nendobj\ntrailer\n<<>>\n%%EOF\n') }),
|
||||
{ status: 415 },
|
||||
)
|
||||
})
|
||||
|
||||
test('pfad_nur_saubere_ids', () => {
|
||||
assert.equal(newsImagePath('p1', '../x', 'l'), null)
|
||||
assert.equal(newsImagePath('../p', 'abc-1', 'l'), null)
|
||||
assert.ok(newsImagePath('p1', 'abc-1', 's').endsWith(path.join('p1', 'abc-1-s.webp')))
|
||||
})
|
||||
|
||||
test('unbenutzte_nach_24h_weg', async () => {
|
||||
const input = await solid(100, 100).png().toBuffer()
|
||||
const old = await storeNewsImage({ customerId: 'c1', projectId: 'p2', buffer: input })
|
||||
const kept = await storeNewsImage({ customerId: 'c1', projectId: 'p2', buffer: input })
|
||||
const fresh = await storeNewsImage({ customerId: 'c1', projectId: 'p2', buffer: input })
|
||||
await claimNewsImage('p2', kept.imageId)
|
||||
const backdate = async (id) => {
|
||||
const f = path.join(process.env.NEWS_UPLOAD_DIR, 'p2', `${id}.json`)
|
||||
const meta = JSON.parse(await readFile(f, 'utf8'))
|
||||
meta.createdAt = new Date(Date.now() - 25 * 3600 * 1000).toISOString()
|
||||
await writeFile(f, JSON.stringify(meta))
|
||||
}
|
||||
await backdate(old.imageId)
|
||||
await backdate(kept.imageId)
|
||||
assert.equal(await purgeUnclaimedNewsImages(), 1)
|
||||
assert.equal(existsSync(newsImagePath('p2', old.imageId, 'l')), false)
|
||||
assert.equal(existsSync(newsImagePath('p2', old.imageId, 's')), false)
|
||||
assert.equal(existsSync(newsImagePath('p2', kept.imageId, 'l')), true)
|
||||
assert.equal(existsSync(newsImagePath('p2', fresh.imageId, 'l')), true)
|
||||
})
|
||||
|
||||
test('rate_limit_20_pro_stunde', () => {
|
||||
for (let i = 0; i < 20; i++) assert.equal(newsImageRateLimited('rl'), false)
|
||||
assert.equal(newsImageRateLimited('rl'), true)
|
||||
assert.equal(newsImageRateLimited('other'), false)
|
||||
})
|
||||
Reference in new issue
Block a user