96 lines
2.6 KiB
TypeScript
96 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);
|
||
|
|
});
|
||
|
|
});
|