137 lines
3.6 KiB
JavaScript
137 lines
3.6 KiB
JavaScript
const AsyncLock = require('../async-lock');
|
|
|
|
describe('AsyncLock', () => {
|
|
let lock;
|
|
|
|
beforeEach(() => {
|
|
lock = new AsyncLock();
|
|
});
|
|
|
|
describe('acquire and release', () => {
|
|
test('should acquire lock without waiting when not locked', async () => {
|
|
const startTime = Date.now();
|
|
await lock.acquire();
|
|
const endTime = Date.now();
|
|
|
|
// Should resolve almost immediately (no waiting)
|
|
expect(endTime - startTime).toBeLessThan(10);
|
|
|
|
lock.release();
|
|
});
|
|
|
|
test('should protect async function without waiting when not locked', async () => {
|
|
let protectedFunctionCalled = false;
|
|
|
|
const protectedFunction = async () => {
|
|
await lock.acquire();
|
|
protectedFunctionCalled = true;
|
|
// Simulate some async work
|
|
await new Promise(resolve => setTimeout(resolve, 10));
|
|
lock.release();
|
|
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();
|
|
expect(lock.locked).toBe(true);
|
|
lock.release();
|
|
|
|
// Second acquisition
|
|
await lock.acquire();
|
|
expect(lock.locked).toBe(true);
|
|
lock.release();
|
|
|
|
// Third acquisition
|
|
await lock.acquire();
|
|
expect(lock.locked).toBe(true);
|
|
lock.release();
|
|
});
|
|
|
|
test('should handle multiple concurrent acquisitions correctly', async () => {
|
|
let counter = 0;
|
|
|
|
const incrementCounter = async () => {
|
|
await lock.acquire();
|
|
const temp = counter;
|
|
// Simulate some async work that could cause race condition
|
|
await new Promise(resolve => setTimeout(resolve, 1));
|
|
counter = temp + 1;
|
|
lock.release();
|
|
};
|
|
|
|
// 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', () => {
|
|
// Initially unlocked
|
|
expect(lock.locked).toBe(false);
|
|
|
|
// Calling release on unlocked lock should not error
|
|
lock.release();
|
|
expect(lock.locked).toBe(false);
|
|
});
|
|
|
|
test('should properly queue multiple concurrent requests', async () => {
|
|
let executionOrder = [];
|
|
|
|
const task = async (id) => {
|
|
await lock.acquire();
|
|
executionOrder.push(id);
|
|
// Simulate work
|
|
await new Promise(resolve => setTimeout(resolve, 10));
|
|
lock.release();
|
|
return id;
|
|
};
|
|
|
|
// Start multiple concurrent tasks
|
|
const tasks = [
|
|
task(1),
|
|
task(2),
|
|
task(3)
|
|
];
|
|
|
|
const r = await Promise.all(tasks);
|
|
|
|
// All tasks should execute in order
|
|
expect(executionOrder).toEqual([1, 2, 3]);
|
|
expect(r).toEqual([1, 2, 3]);
|
|
});
|
|
|
|
test('no await for async function',async()=>{
|
|
const results= []
|
|
async function task(prefix, count) {
|
|
await lock.acquire()
|
|
let i = 0
|
|
const h = setInterval(() => {
|
|
results.push(`${prefix}${i}`)
|
|
i++
|
|
if (i >= count) {
|
|
lock.release()
|
|
clearInterval(h)
|
|
}
|
|
},10)
|
|
}
|
|
|
|
task('a', 5)
|
|
task('b', 5)
|
|
task('c', 5)
|
|
await lock.acquire()
|
|
const r = results.join('')
|
|
expect(r.includes('a0a1a2a3a4')).toBe(true)
|
|
})
|
|
});
|
|
}); |