Files
2026-07-13 19:57:46 +08:00

69 lines
2.0 KiB
JavaScript

const test = require('node:test');
const assert = require('node:assert/strict');
const fs = require('node:fs/promises');
const os = require('node:os');
const path = require('node:path');
const lock = require('../erp_task_lock');
function delay(ms) {
return new Promise((resolve) => setTimeout(resolve, ms));
}
test('withFileLock serializes concurrent tasks', async () => {
const root = await fs.mkdtemp(path.join(os.tmpdir(), 'erp-lock-'));
const lockDir = path.join(root, 'erp-task.lock');
const events = [];
const first = lock.withFileLock(lockDir, async () => {
events.push('a-start');
await delay(20);
events.push('a-end');
}, { pollMs: 1, timeoutMs: 1000 });
await delay(2);
const second = lock.withFileLock(lockDir, async () => {
events.push('b-start');
events.push('b-end');
}, { pollMs: 1, timeoutMs: 1000 });
await Promise.all([first, second]);
assert.deepEqual(events, ['a-start', 'a-end', 'b-start', 'b-end']);
});
test('withFileLock removes the lock after a failed task', async () => {
const root = await fs.mkdtemp(path.join(os.tmpdir(), 'erp-lock-'));
const lockDir = path.join(root, 'erp-task.lock');
await assert.rejects(
() => lock.withFileLock(lockDir, async () => {
throw new Error('boom');
}, { pollMs: 1, timeoutMs: 1000 }),
/boom/
);
await lock.withFileLock(lockDir, async () => 'ok', { pollMs: 1, timeoutMs: 1000 });
});
test('withFileLock recovers an orphaned lock whose owner process no longer exists', async () => {
const root = await fs.mkdtemp(path.join(os.tmpdir(), 'erp-lock-'));
const lockDir = path.join(root, 'erp-task.lock');
await fs.mkdir(lockDir);
await fs.writeFile(
path.join(lockDir, 'owner.json'),
JSON.stringify({ pid: 99999999, acquiredAt: '2026-06-26T16:18:21.576Z' }),
'utf8'
);
let ran = false;
await lock.withFileLock(lockDir, async () => {
ran = true;
}, { pollMs: 1, timeoutMs: 1000 });
assert.equal(ran, true);
await assert.rejects(() => fs.stat(lockDir), /ENOENT/);
});