60 lines
2.0 KiB
TypeScript
60 lines
2.0 KiB
TypeScript
import { describe, it, expect, afterEach } from 'vitest';
|
|
import { mkdtempSync, writeFileSync, existsSync, rmSync } from 'fs';
|
|
import { tmpdir, hostname } from 'os';
|
|
import { join } from 'path';
|
|
import { acquireWorkerLock } from './instance-lock.js';
|
|
|
|
const dirs: string[] = [];
|
|
function tmpDb(): string {
|
|
const d = mkdtempSync(join(tmpdir(), 'maestro-lock-'));
|
|
dirs.push(d);
|
|
return join(d, 'maestro.db');
|
|
}
|
|
|
|
afterEach(() => {
|
|
for (const d of dirs) rmSync(d, { recursive: true, force: true });
|
|
dirs.length = 0;
|
|
delete process.env['MAESTRO_FORCE_WORKER_LOCK'];
|
|
});
|
|
|
|
describe('acquireWorkerLock', () => {
|
|
it('writes a lock file and release() removes it', () => {
|
|
const db = tmpDb();
|
|
const lock = acquireWorkerLock(db);
|
|
expect(existsSync(lock.path)).toBe(true);
|
|
lock.release();
|
|
expect(existsSync(lock.path)).toBe(false);
|
|
lock.release(); // idempotent
|
|
});
|
|
|
|
it('throws when a live process on this host already holds the lock', () => {
|
|
const db = tmpDb();
|
|
// process.ppid is alive (it spawned us) and is not our pid.
|
|
writeFileSync(`${db}.worker.lock`, JSON.stringify({
|
|
pid: process.ppid, host: hostname(), startedAt: new Date().toISOString(),
|
|
}));
|
|
expect(() => acquireWorkerLock(db)).toThrow(/already running/);
|
|
});
|
|
|
|
it('reclaims a stale lock from a dead pid', () => {
|
|
const db = tmpDb();
|
|
writeFileSync(`${db}.worker.lock`, JSON.stringify({
|
|
pid: 2147483646, host: hostname(), startedAt: new Date().toISOString(),
|
|
}));
|
|
const lock = acquireWorkerLock(db); // must not throw
|
|
expect(existsSync(lock.path)).toBe(true);
|
|
lock.release();
|
|
});
|
|
|
|
it('MAESTRO_FORCE_WORKER_LOCK=1 overrides a live lock', () => {
|
|
const db = tmpDb();
|
|
writeFileSync(`${db}.worker.lock`, JSON.stringify({
|
|
pid: process.ppid, host: hostname(), startedAt: new Date().toISOString(),
|
|
}));
|
|
process.env['MAESTRO_FORCE_WORKER_LOCK'] = '1';
|
|
const lock = acquireWorkerLock(db);
|
|
expect(existsSync(lock.path)).toBe(true);
|
|
lock.release();
|
|
});
|
|
});
|