Bundle extension (upload hook + API endpoint + admin module) that warms Directus image transform cache in-process with an explicit acceptFormat, so the generated file matches the browser's cache key byte-for-byte. - resumable, window-aware backfill with progress + cancel - system + custom presets, effective-format reporting, shadow warnings - storage breakdown (previews vs originals), human-readable API fields - diagnostics: resolve-plan prediction, per-file generate, ops ring buffer - isolated dev/staging compose stack + deploy/rollback scripts Verified on Directus 11.17.4: 40/40 tests, hash-match gate passed, idempotent backfill, custom fixed-format presets, error surfacing.
95 lines
2.6 KiB
TypeScript
95 lines
2.6 KiB
TypeScript
import assert from 'node:assert/strict';
|
|
import { appendFile, mkdtemp, rm } from 'node:fs/promises';
|
|
import { tmpdir } from 'node:os';
|
|
import { join } from 'node:path';
|
|
import { test } from 'node:test';
|
|
import {
|
|
acquireLock,
|
|
appendCompleted,
|
|
completedKeyOf,
|
|
isCancelled,
|
|
loadCompleted,
|
|
readRun,
|
|
releaseLock,
|
|
requestCancel,
|
|
writeRun,
|
|
} from './state';
|
|
import type { RunState } from './types';
|
|
|
|
async function withTempEnv(run: (env: Record<string, string>) => Promise<void>): Promise<void> {
|
|
const dir = await mkdtemp(join(tmpdir(), 'image-previews-'));
|
|
|
|
try {
|
|
await run({ IMAGE_PREVIEWS_STATE_DIR: dir });
|
|
} finally {
|
|
await rm(dir, { recursive: true, force: true });
|
|
}
|
|
}
|
|
|
|
test('appendCompleted + loadCompleted round-trips and resumes', async () => {
|
|
await withTempEnv(async (env) => {
|
|
const empty = await loadCompleted(env);
|
|
assert.equal(empty.size, 0);
|
|
|
|
await appendCompleted({ file_id: 'f1', preset_key: 'system-small-cover', format: 'avif', ts: 1 }, env);
|
|
await appendCompleted({ file_id: 'f1', preset_key: 'system-small-cover', format: 'webp', ts: 2 }, env);
|
|
|
|
const completed = await loadCompleted(env);
|
|
assert.equal(completed.size, 2);
|
|
assert.ok(completed.has(completedKeyOf('f1', 'system-small-cover', 'avif')));
|
|
assert.ok(completed.has(completedKeyOf('f1', 'system-small-cover', 'webp')));
|
|
});
|
|
});
|
|
|
|
test('loadCompleted tolerates corrupt lines', async () => {
|
|
await withTempEnv(async (env) => {
|
|
await appendFile(join(env['IMAGE_PREVIEWS_STATE_DIR'] as string, 'state.jsonl'), 'not-json\n', 'utf8');
|
|
await appendCompleted({ file_id: 'f2', preset_key: 'k', format: null, ts: 1 }, env);
|
|
|
|
const completed = await loadCompleted(env);
|
|
assert.equal(completed.size, 1);
|
|
});
|
|
});
|
|
|
|
test('run state round-trips', async () => {
|
|
await withTempEnv(async (env) => {
|
|
assert.equal(await readRun(env), null);
|
|
|
|
const run: RunState = {
|
|
running: true,
|
|
startedAt: 1,
|
|
finishedAt: null,
|
|
total: 10,
|
|
done: 2,
|
|
generated: 2,
|
|
hits: 0,
|
|
failed: 0,
|
|
rate: 1,
|
|
eta: 8,
|
|
cancelled: false,
|
|
};
|
|
|
|
await writeRun(run, env);
|
|
const loaded = await readRun(env);
|
|
assert.equal(loaded?.done, 2);
|
|
assert.equal(loaded?.total, 10);
|
|
});
|
|
});
|
|
|
|
test('lock is exclusive and releasable', async () => {
|
|
await withTempEnv(async (env) => {
|
|
assert.equal(await acquireLock(env), true);
|
|
assert.equal(await acquireLock(env), false);
|
|
await releaseLock(env);
|
|
assert.equal(await acquireLock(env), true);
|
|
await releaseLock(env);
|
|
});
|
|
});
|
|
|
|
test('cancel flag toggles', async () => {
|
|
await withTempEnv(async (env) => {
|
|
assert.equal(await isCancelled(env), false);
|
|
await requestCancel(env);
|
|
assert.equal(await isCancelled(env), true);
|
|
});
|
|
});
|