cachette 4.0.22 → 4.0.24
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/test/RedisCache_test.ts +106 -0
package/package.json
CHANGED
package/test/RedisCache_test.ts
CHANGED
|
@@ -300,4 +300,110 @@ describe('RedisCache', () => {
|
|
|
300
300
|
expect(replicationAcknowledged).to.equal(0);
|
|
301
301
|
});
|
|
302
302
|
});
|
|
303
|
+
|
|
304
|
+
describe('lock', () => {
|
|
305
|
+
let cache: RedisCache;
|
|
306
|
+
|
|
307
|
+
beforeEach(async function () {
|
|
308
|
+
if (!process.env.TEST_REDIS_URL) {
|
|
309
|
+
this.skip();
|
|
310
|
+
}
|
|
311
|
+
cache = new RedisCache(process.env.TEST_REDIS_URL as string);
|
|
312
|
+
await cache.isReady();
|
|
313
|
+
await cache.clear();
|
|
314
|
+
});
|
|
315
|
+
|
|
316
|
+
afterEach(async function () {
|
|
317
|
+
if (cache) {
|
|
318
|
+
await cache.quit();
|
|
319
|
+
}
|
|
320
|
+
});
|
|
321
|
+
|
|
322
|
+
it('returns a Lock object with value property and unlock method', async () => {
|
|
323
|
+
const lock = await cache.lock('test-resource', 10000);
|
|
324
|
+
|
|
325
|
+
expect(lock).to.have.property('value');
|
|
326
|
+
expect(lock.value).to.be.a('string');
|
|
327
|
+
expect(lock.value!.length).to.be.greaterThan(0);
|
|
328
|
+
|
|
329
|
+
expect(lock).to.have.property('unlock');
|
|
330
|
+
expect(lock.unlock).to.be.a('function');
|
|
331
|
+
|
|
332
|
+
await cache.unlock(lock);
|
|
333
|
+
});
|
|
334
|
+
|
|
335
|
+
it('releases lock via the returned Lock object unlock method', async () => {
|
|
336
|
+
const resource = `test-resource-${Math.random()}`;
|
|
337
|
+
|
|
338
|
+
const lock = await cache.lock(resource, 10000);
|
|
339
|
+
expect(await cache.hasLock(resource)).to.be.true;
|
|
340
|
+
|
|
341
|
+
await lock.unlock();
|
|
342
|
+
expect(await cache.hasLock(resource)).to.be.false;
|
|
343
|
+
});
|
|
344
|
+
|
|
345
|
+
it('releases lock via cache.unlock()', async () => {
|
|
346
|
+
const resource = `test-resource-${Math.random()}`;
|
|
347
|
+
|
|
348
|
+
const lock = await cache.lock(resource, 10000);
|
|
349
|
+
expect(await cache.hasLock(resource)).to.be.true;
|
|
350
|
+
|
|
351
|
+
await cache.unlock(lock);
|
|
352
|
+
expect(await cache.hasLock(resource)).to.be.false;
|
|
353
|
+
});
|
|
354
|
+
|
|
355
|
+
it('with retry = true, waits for lock to become available', async () => {
|
|
356
|
+
const resource = `test-resource-${Math.random()}`;
|
|
357
|
+
|
|
358
|
+
// Acquire first lock with short TTL — intentionally let it expire
|
|
359
|
+
void (await cache.lock(resource, 100));
|
|
360
|
+
|
|
361
|
+
// Second lock should wait and succeed after first expires
|
|
362
|
+
const startTime = Date.now();
|
|
363
|
+
const secondLock = await cache.lock(resource, 10000, true);
|
|
364
|
+
const elapsed = Date.now() - startTime;
|
|
365
|
+
|
|
366
|
+
expect(elapsed).to.be.greaterThanOrEqual(50); // waited for first lock to expire
|
|
367
|
+
expect(secondLock).to.have.property('value');
|
|
368
|
+
expect(secondLock.value).to.be.a('string');
|
|
369
|
+
|
|
370
|
+
await cache.unlock(secondLock);
|
|
371
|
+
});
|
|
372
|
+
|
|
373
|
+
it('with retry = false, throws immediately if lock is held', async () => {
|
|
374
|
+
const resource = `test-resource-${Math.random()}`;
|
|
375
|
+
|
|
376
|
+
const firstLock = await cache.lock(resource, 10000);
|
|
377
|
+
expect(await cache.hasLock(resource)).to.be.true;
|
|
378
|
+
|
|
379
|
+
let error: Error | undefined;
|
|
380
|
+
try {
|
|
381
|
+
await cache.lock(resource, 10000, false);
|
|
382
|
+
} catch (err) {
|
|
383
|
+
error = err as Error;
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
expect(error).to.exist;
|
|
387
|
+
expect(error!.message).to.include('lock');
|
|
388
|
+
|
|
389
|
+
await cache.unlock(firstLock);
|
|
390
|
+
});
|
|
391
|
+
|
|
392
|
+
it('only releases its own lock (fencing token safety)', async () => {
|
|
393
|
+
const resource = `test-resource-${Math.random()}`;
|
|
394
|
+
|
|
395
|
+
// Acquire a lock, then let it expire
|
|
396
|
+
void (await cache.lock(resource, 50, false));
|
|
397
|
+
await new Promise((resolve) => setTimeout(resolve, 60));
|
|
398
|
+
|
|
399
|
+
// Acquire a new lock on the same resource (different owner)
|
|
400
|
+
const secondLock = await cache.lock(resource, 10000, false);
|
|
401
|
+
expect(await cache.hasLock(resource)).to.be.true;
|
|
402
|
+
|
|
403
|
+
// The second lock should still be held — unlocking the first (expired) lock
|
|
404
|
+
// should not have affected it. Verify the second lock can be unlocked normally.
|
|
405
|
+
await cache.unlock(secondLock);
|
|
406
|
+
expect(await cache.hasLock(resource)).to.be.false;
|
|
407
|
+
});
|
|
408
|
+
});
|
|
303
409
|
});
|