146 lines
4.3 KiB
JavaScript
146 lines
4.3 KiB
JavaScript
const FileLock = require('../file-lock');
|
|
const fs = require('fs');
|
|
const path = require('path');
|
|
|
|
describe('FileLock', () => {
|
|
let lock;
|
|
const testFilePath = path.join(__dirname, 'test-file.txt');
|
|
|
|
beforeEach(() => {
|
|
lock = new FileLock();
|
|
// Clean up any leftover lock files
|
|
const lockFilePath = `${testFilePath}.lock`;
|
|
if (fs.existsSync(lockFilePath)) {
|
|
fs.unlinkSync(lockFilePath);
|
|
}
|
|
// Create test file
|
|
fs.writeFileSync(testFilePath, 'test content');
|
|
});
|
|
|
|
afterEach(() => {
|
|
// Clean up files
|
|
const lockFilePath = `${testFilePath}.lock`;
|
|
if (fs.existsSync(lockFilePath)) {
|
|
fs.unlinkSync(lockFilePath);
|
|
}
|
|
if (fs.existsSync(testFilePath)) {
|
|
fs.unlinkSync(testFilePath);
|
|
}
|
|
});
|
|
|
|
describe('acquire and release', () => {
|
|
test('should acquire lock without waiting when not locked', async () => {
|
|
const startTime = Date.now();
|
|
await lock.acquire(testFilePath);
|
|
const endTime = Date.now();
|
|
|
|
// Should resolve almost immediately (no waiting)
|
|
expect(endTime - startTime).toBeLessThan(100);
|
|
|
|
lock.release(testFilePath);
|
|
});
|
|
|
|
test('should protect file access without waiting when not locked', async () => {
|
|
let protectedFunctionCalled = false;
|
|
|
|
const protectedFunction = async () => {
|
|
await lock.acquire(testFilePath);
|
|
protectedFunctionCalled = true;
|
|
// Simulate some async work
|
|
await new Promise(resolve => setTimeout(resolve, 10));
|
|
lock.release(testFilePath);
|
|
return 'done';
|
|
};
|
|
|
|
const result = await protectedFunction();
|
|
|
|
expect(protectedFunctionCalled).toBe(true);
|
|
expect(result).toBe('done');
|
|
});
|
|
|
|
test('should allow consecutive acquisitions and releases without queueing', async () => {
|
|
// First acquisition
|
|
await lock.acquire(testFilePath);
|
|
lock.release(testFilePath);
|
|
|
|
// Second acquisition
|
|
await lock.acquire(testFilePath);
|
|
lock.release(testFilePath);
|
|
|
|
// Third acquisition
|
|
await lock.acquire(testFilePath);
|
|
lock.release(testFilePath);
|
|
});
|
|
|
|
test('should handle multiple concurrent acquisitions correctly', async () => {
|
|
let counter = 0;
|
|
|
|
const incrementCounter = async () => {
|
|
await lock.acquire(testFilePath);
|
|
const temp = counter;
|
|
// Simulate some async work that could cause race condition
|
|
await new Promise(resolve => setTimeout(resolve, 1));
|
|
counter = temp + 1;
|
|
lock.release(testFilePath);
|
|
};
|
|
|
|
// Run multiple concurrent operations
|
|
await Promise.all([
|
|
incrementCounter(),
|
|
incrementCounter(),
|
|
incrementCounter()
|
|
]);
|
|
|
|
expect(counter).toBe(3);
|
|
});
|
|
|
|
test('should handle case where release is called without any pending acquirers', () => {
|
|
// Calling release on unlocked lock should not error
|
|
expect(() => lock.release(testFilePath)).not.toThrow();
|
|
});
|
|
|
|
test('should properly queue multiple concurrent requests', async () => {
|
|
let executionOrder = [];
|
|
|
|
const task = async (id) => {
|
|
await lock.acquire(testFilePath);
|
|
executionOrder.push(id);
|
|
// Simulate work
|
|
await new Promise(resolve => setTimeout(resolve, 10));
|
|
lock.release(testFilePath);
|
|
};
|
|
|
|
// Start multiple concurrent tasks
|
|
const tasks = [
|
|
task(1),
|
|
task(2),
|
|
task(3)
|
|
];
|
|
|
|
await Promise.all(tasks);
|
|
|
|
// All tasks should execute in order
|
|
expect(executionOrder).toEqual([1, 2, 3]);
|
|
});
|
|
|
|
test('should create lock file when acquiring lock', async () => {
|
|
await lock.acquire(testFilePath);
|
|
const lockFilePath = `${testFilePath}.lock`;
|
|
|
|
expect(fs.existsSync(lockFilePath)).toBe(true);
|
|
expect(fs.readFileSync(lockFilePath, 'utf8')).toBe(process.pid.toString());
|
|
|
|
lock.release(testFilePath);
|
|
});
|
|
|
|
test('should remove lock file when releasing lock', async () => {
|
|
await lock.acquire(testFilePath);
|
|
const lockFilePath = `${testFilePath}.lock`;
|
|
|
|
expect(fs.existsSync(lockFilePath)).toBe(true);
|
|
|
|
lock.release(testFilePath);
|
|
expect(fs.existsSync(lockFilePath)).toBe(false);
|
|
});
|
|
});
|
|
}); |