visual-journal / src /app /api /image-delete /route.test.ts
Visual Journal deploy
Deploy 5554830 to Docker Space
e4e0afe
Raw
History Blame Contribute Delete
6.72 kB
import { POST } from './route';
import { getWebuiImageRetentionStore, resetWebuiImageRetentionStoresForTests } from '@/lib/webui-image-retention-store';
import { NextRequest } from 'next/server';
import assert from 'node:assert/strict';
import { access, mkdtemp, mkdir, rm, writeFile } from 'node:fs/promises';
import os from 'node:os';
import path from 'node:path';
import { afterEach, beforeEach, describe, it } from 'node:test';
const validFilename = '1781567999000-aaaaaaaaaaaaaaaa-0.png';
const missingFilename = '1781567999001-bbbbbbbbbbbbbbbb-1.webp';
let originalEnv: NodeJS.ProcessEnv;
let originalCwd = '';
let tempDir = '';
beforeEach(async () => {
originalEnv = { ...process.env };
originalCwd = process.cwd();
tempDir = await mkdtemp(path.join(os.tmpdir(), 'image-delete-route-'));
process.chdir(tempDir);
delete process.env.APP_PASSWORD;
resetWebuiImageRetentionStoresForTests();
});
afterEach(async () => {
resetWebuiImageRetentionStoresForTests();
process.chdir(originalCwd);
await rm(tempDir, { recursive: true, force: true });
restoreProcessEnv(originalEnv);
});
describe('POST /api/image-delete', { concurrency: false }, () => {
it('removes automatic-cleanup protection after deleting an image successfully', async () => {
const filepath = await writeOutputFile(validFilename);
const store = await getWebuiImageRetentionStore();
await store.preserve([validFilename]);
const response = await POST(jsonRequest({ filenames: [validFilename] }));
const body = (await response.json()) as {
results: Array<{ filename: string; success: boolean; error?: string }>;
};
assert.equal(response.status, 200);
assert.deepEqual(body.results, [{ filename: validFilename, success: true }]);
await assert.rejects(() => access(filepath));
assert.deepEqual(await store.listPermanentFilenames(), []);
});
it('reports a missing file as absent and releases stale automatic-cleanup protection', async () => {
const store = await getWebuiImageRetentionStore();
await store.preserve([missingFilename]);
const response = await POST(jsonRequest({ filenames: [missingFilename] }));
const body = (await response.json()) as {
results: Array<{
filename: string;
success: boolean;
fileDeleted?: boolean;
fileAbsent?: boolean;
markerRemoved?: boolean;
error?: string;
}>;
};
assert.equal(response.status, 207);
assert.deepEqual(body.results, [
{
filename: missingFilename,
success: false,
fileAbsent: true,
markerRemoved: true,
error: '文件不存在。'
}
]);
assert.deepEqual(await store.listPermanentFilenames(), []);
});
it('reports a retention-state cleanup failure after deleting the file', async () => {
const filepath = await writeOutputFile(validFilename);
const store = await getWebuiImageRetentionStore();
await store.preserve([validFilename]);
const originalRemove = store.remove.bind(store);
store.remove = async () => {
throw new Error('expected retention state failure');
};
try {
const response = await POST(jsonRequest({ filenames: [validFilename] }));
const body = (await response.json()) as {
results: Array<{
filename: string;
success: boolean;
fileDeleted?: boolean;
fileAbsent?: boolean;
markerRemoved?: boolean;
error?: string;
}>;
};
assert.equal(response.status, 207);
assert.deepEqual(body.results, [
{
filename: validFilename,
success: false,
fileDeleted: true,
markerRemoved: false,
error: '图片已删除,但自动清理保护未能清理。'
}
]);
await assert.rejects(() => access(filepath));
assert.deepEqual(await store.listPermanentFilenames(), [validFilename]);
} finally {
store.remove = originalRemove;
}
});
it('does not report a stale marker as released when its cleanup fails for an absent file', async () => {
const store = await getWebuiImageRetentionStore();
await store.preserve([missingFilename]);
const originalRemove = store.remove.bind(store);
store.remove = async () => {
throw new Error('expected retention state failure');
};
try {
const response = await POST(jsonRequest({ filenames: [missingFilename] }));
const body = (await response.json()) as {
results: Array<{
filename: string;
success: boolean;
fileDeleted?: boolean;
fileAbsent?: boolean;
markerRemoved?: boolean;
error?: string;
}>;
};
assert.equal(response.status, 207);
assert.deepEqual(body.results, [
{
filename: missingFilename,
success: false,
fileAbsent: true,
markerRemoved: false,
error: '图片已不存在,但自动清理保护未能清理。'
}
]);
assert.deepEqual(await store.listPermanentFilenames(), [missingFilename]);
} finally {
store.remove = originalRemove;
}
});
});
function jsonRequest(body: unknown): NextRequest {
return new NextRequest('http://localhost/api/image-delete', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(body)
});
}
async function writeOutputFile(filename: string): Promise<string> {
const outputDir = path.join(tempDir, 'generated-images');
await mkdir(outputDir, { recursive: true });
const filepath = path.join(outputDir, filename);
await writeFile(filepath, 'image');
return filepath;
}
function restoreProcessEnv(snapshot: NodeJS.ProcessEnv): void {
for (const key of Object.keys(process.env)) {
if (!(key in snapshot)) delete process.env[key];
}
for (const [key, value] of Object.entries(snapshot)) {
process.env[key] = value;
}
}