import { test } from 'node:test'; import assert from 'node:assert/strict'; import { mkdtemp, mkdir, writeFile, readFile, access, unlink, rmdir } from 'node:fs/promises'; import { join } from 'node:path'; import { tmpdir } from 'node:os'; import { cleanupPublished } from '../server/threads/cleanup.ts'; test('cleanup preserves unconfirmed images, deletes confirmed images and is repeatable', async () => { const root = await mkdtemp(join(tmpdir(), 'threads-cleanup-')); const recordPath = join(root, 'record.json'); const images = [join(root, 'one.jpg'), join(root, 'two.jpg')]; try { for (const image of images) await writeFile(image, 'image'); const record = { status: 'publishing', images: images.map(imagePath => ({ imagePath })) }; await writeFile(recordPath, JSON.stringify(record)); assert.equal(await cleanupPublished(recordPath, root), false); for (const image of images) await access(image); await writeFile(recordPath, JSON.stringify({ ...record, status: 'published', postId: 'post-123' })); assert.equal(await cleanupPublished(recordPath, root), true); for (const image of images) await assert.rejects(access(image), { code: 'ENOENT' }); const saved = JSON.parse(await readFile(recordPath, 'utf8')); assert.equal(saved.postId, 'post-123'); assert.ok(saved.imagesDeletedAt); assert.equal(await cleanupPublished(recordPath, root), false); } finally { for (const path of [...images, recordPath]) await unlink(path).catch(() => {}); await rmdir(root); } }); test('cleanup validates every target before deleting and rejects outside files', async () => { const root = await mkdtemp(join(tmpdir(), 'threads-cleanup-')); const imageDir = join(root, 'images'); await mkdir(imageDir); const inside = join(imageDir, 'one.jpg'), outside = join(root, 'outside.jpg'), recordPath = join(root, 'record.json'); try { await writeFile(inside, 'image'); await writeFile(outside, 'keep'); await writeFile(recordPath, JSON.stringify({ status: 'published', postId: 'post', images: [{ imagePath: inside }, { imagePath: outside }] })); await assert.rejects(cleanupPublished(recordPath, imageDir), /outside/); await access(inside); await access(outside); await writeFile(recordPath, JSON.stringify({ status: 'published', postId: 'post', images: [{ imagePath: inside }, { imagePath: join(imageDir, 'already-deleted.jpg') }] })); assert.equal(await cleanupPublished(recordPath, imageDir), true); } finally { for (const path of [inside, outside, recordPath]) await unlink(path).catch(() => {}); await rmdir(imageDir); await rmdir(root); } });