import { rmSync, mkdirSync, existsSync, unlinkSync } from 'fs'; import { resolve } from 'path'; import sharp from 'sharp'; const root = resolve('.'); const dbPath = resolve(root, 'db', 'vendoo.db'); const uploadRoot = resolve(root, 'uploads'); rmSync(dbPath, { force: true }); rmSync(`${dbPath}-wal`, { force: true }); rmSync(`${dbPath}-shm`, { force: true }); rmSync(uploadRoot, { recursive: true, force: true }); mkdirSync(resolve(uploadRoot, 'test'), { recursive: true }); await sharp({ create: { width: 900, height: 1200, channels: 3, background: '#d8c1a7' } }).png().toFile(resolve(uploadRoot, 'test', 'produkt.png')); const batch = await import('../lib/image-batch-jobs.mjs'); const { db } = await import('../lib/db.mjs'); const profiles = batch.listImageBatchProfiles(); if (profiles.length < 8) throw new Error(`Nur ${profiles.length} integrierte Profile gefunden.`); const custom = batch.createImageBatchProfile({ name: 'Test WebP', description: 'Verifikation', filename_pattern: '{name}-test-{index}', operations: { resize: { width: 640, height: 640, fit: 'contain', background: '#ffffff' }, format: 'webp', quality: 82 }, }); if (!custom?.id || custom.is_builtin) throw new Error('Eigenes Profil wurde nicht korrekt angelegt.'); const job = batch.createImageBatchJob({ source_paths: ['test/produkt.png'], profile_ids: [custom.id], name: 'Batch-Verifikation' }); batch.startImageBatchJobLoop(); const waitFor = async (id, accepted, timeout = 15000) => { const started = Date.now(); while (Date.now() - started < timeout) { const current = batch.getImageBatchJob(id); if (accepted.includes(current?.status)) return current; await new Promise(resolveWait => setTimeout(resolveWait, 100)); } throw new Error(`Timeout für Auftrag ${id}`); }; const completed = await waitFor(job.id, ['completed','partial','failed']); if (completed.status !== 'completed') throw new Error(`Batch-Auftrag endete mit ${completed.status}: ${completed.items?.[0]?.error_message || ''}`); const output = completed.items[0]?.output_path; if (!output || !existsSync(resolve(uploadRoot, output))) throw new Error('Batch-Ausgabedatei fehlt.'); const meta = await sharp(resolve(uploadRoot, output)).metadata(); if (meta.width !== 640 || meta.height !== 640 || meta.format !== 'webp') throw new Error(`Falsche Ausgabe: ${meta.width}x${meta.height} ${meta.format}`); const version = db.prepare("SELECT * FROM image_edit_versions WHERE output_path = ? AND context = 'batch'").get(output); if (!version) throw new Error('Nicht destruktive Batch-Version fehlt in image_edit_versions.'); await sharp({ create: { width: 700, height: 500, channels: 3, background: '#eeeeee' } }).jpeg().toFile(resolve(uploadRoot, 'test', 'pause.jpg')); const pausedJob = batch.createImageBatchJob({ source_paths: ['test/pause.jpg'], profile_ids: [profiles[0].id], name: 'Pause-Verifikation' }); batch.pauseImageBatchJob(pausedJob.id); await new Promise(resolveWait => setTimeout(resolveWait, 300)); if (batch.getImageBatchJob(pausedJob.id).status !== 'paused') throw new Error('Pause wurde nicht persistiert.'); batch.resumeImageBatchJob(pausedJob.id); const resumed = await waitFor(pausedJob.id, ['completed','partial','failed']); if (resumed.status !== 'completed') throw new Error('Fortgesetzter Auftrag wurde nicht fertig.'); await sharp({ create: { width: 400, height: 400, channels: 3, background: '#ffffff' } }).png().toFile(resolve(uploadRoot, 'test', 'fail.png')); const retryJob = batch.createImageBatchJob({ source_paths: ['test/fail.png'], profile_ids: [custom.id], name: 'Retry-Verifikation' }); unlinkSync(resolve(uploadRoot, 'test', 'fail.png')); const failed = await waitFor(retryJob.id, ['failed','partial']); if (failed.status !== 'failed') throw new Error('Fehlersimulation wurde nicht als fehlgeschlagen erkannt.'); await sharp({ create: { width: 400, height: 400, channels: 3, background: '#ffffff' } }).png().toFile(resolve(uploadRoot, 'test', 'fail.png')); batch.retryImageBatchJob(retryJob.id); const retried = await waitFor(retryJob.id, ['completed','partial','failed']); if (retried.status !== 'completed') throw new Error('Wiederholung war nicht erfolgreich.'); const outputs = batch.getImageBatchOutputPaths(job.id); if (outputs.length !== 1) throw new Error('ZIP-Ausgabeliste ist inkorrekt.'); console.log(JSON.stringify({ ok: true, profiles: profiles.length, output, dimensions: `${meta.width}x${meta.height}`, pause_resume: true, retry: true }, null, 2)); db.close(); rmSync(dbPath, { force: true }); rmSync(`${dbPath}-wal`, { force: true }); rmSync(`${dbPath}-shm`, { force: true }); rmSync(uploadRoot, { recursive: true, force: true });