test(management): cover plugin layout sync lock

This commit is contained in:
Tam Nhu Tran
2026-03-18 08:22:03 -04:00
parent 36e8ed5d87
commit fab05011f1
2 changed files with 97 additions and 1 deletions
+3 -1
View File
@@ -234,7 +234,9 @@ class ProfileContextSyncLock {
const until = Date.now() + retryDelayMs;
while (Date.now() < until) {
// Busy wait only during rare lock contention.
// Sync callers need a synchronous retry path here.
// This lock only guards short local filesystem normalization work, so
// contention should be brief and limited to profile/bootstrap edges.
}
}
}
@@ -0,0 +1,94 @@
import { afterEach, beforeEach, describe, expect, it } from 'bun:test';
import { createHash } from 'crypto';
import * as fs from 'fs';
import * as os from 'os';
import * as path from 'path';
import ProfileContextSyncLock from '../../src/management/profile-context-sync-lock';
describe('ProfileContextSyncLock', () => {
let tempRoot = '';
let instancesDir = '';
const getLockPath = (lockName: string): string => {
const safeName = lockName.replace(/[^a-zA-Z0-9_-]/g, '-').toLowerCase();
const profileHash = createHash('sha1').update(lockName).digest('hex').slice(0, 8);
return path.join(instancesDir, '.locks', `${safeName}-${profileHash}.lock`);
};
beforeEach(() => {
tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), 'ccs-context-lock-test-'));
instancesDir = path.join(tempRoot, 'instances');
fs.mkdirSync(instancesDir, { recursive: true });
});
afterEach(() => {
if (tempRoot && fs.existsSync(tempRoot)) {
fs.rmSync(tempRoot, { recursive: true, force: true });
}
});
it('acquires and releases synchronous named locks', () => {
const lock = new ProfileContextSyncLock(instancesDir);
const lockPath = getLockPath('__plugin-layout__');
let sawLockInsideCallback = false;
const result = lock.withNamedLockSync('__plugin-layout__', () => {
sawLockInsideCallback = fs.existsSync(lockPath);
expect(fs.readFileSync(lockPath, 'utf8')).toContain(`"pid":${process.pid}`);
return 'ok';
});
expect(result).toBe('ok');
expect(sawLockInsideCallback).toBe(true);
expect(fs.existsSync(lockPath)).toBe(false);
});
it('releases synchronous named locks when the callback throws', () => {
const lock = new ProfileContextSyncLock(instancesDir);
const lockPath = getLockPath('__plugin-layout__');
expect(() =>
lock.withNamedLockSync('__plugin-layout__', () => {
throw new Error('boom');
})
).toThrow('boom');
expect(fs.existsSync(lockPath)).toBe(false);
});
it('reclaims dead-owner locks before entering the callback', () => {
const lock = new ProfileContextSyncLock(instancesDir);
const lockPath = getLockPath('__plugin-layout__');
fs.mkdirSync(path.dirname(lockPath), { recursive: true });
fs.writeFileSync(
lockPath,
JSON.stringify({
version: 1,
pid: 999999,
nonce: 'dead-owner',
acquiredAtMs: Date.now() - 1000,
}),
'utf8'
);
const result = lock.withNamedLockSync('__plugin-layout__', () => 'reclaimed');
expect(result).toBe('reclaimed');
expect(fs.existsSync(lockPath)).toBe(false);
});
it('reclaims malformed stale locks before entering the callback', () => {
const lock = new ProfileContextSyncLock(instancesDir);
const lockPath = getLockPath('__plugin-layout__');
const staleDate = new Date(Date.now() - 60_000);
fs.mkdirSync(path.dirname(lockPath), { recursive: true });
fs.writeFileSync(lockPath, 'not-json', 'utf8');
fs.utimesSync(lockPath, staleDate, staleDate);
const result = lock.withNamedLockSync('__plugin-layout__', () => 'stale-reclaimed');
expect(result).toBe('stale-reclaimed');
expect(fs.existsSync(lockPath)).toBe(false);
});
});