UNPKG

cache-manager-redis-yet

Version:
313 lines (265 loc) 9.91 kB
import { describe, expect, it, beforeEach } from 'vitest'; import cacheManager from 'cache-manager'; import { redisStore, RedisCache, redisInsStore } from '../src'; import { createClient, RedisClientType } from 'redis'; let redisCache: RedisCache; let customRedisCache: RedisCache; const config = { url: 'redis://localhost:6379', ttl: 0, }; beforeEach(async () => { redisCache = cacheManager.caching({ store: await redisStore(config), ...config, }) as RedisCache; const conf = { ...config, isCacheableValue: (val: unknown) => { if (val === undefined) { // allow undefined return true; } else if (val === 'FooBarString') { // disallow FooBarString return false; } return redisCache.store.isCacheableValue(val); }, }; customRedisCache = cacheManager.caching({ store: await redisStore(conf), ...conf, }) as RedisCache; }); describe('instance', () => { it('should be constructed', async () => { const instance: RedisClientType = await createClient(config); await instance.connect(); const cache = cacheManager.caching({ store: redisInsStore(instance, config), ...config, }) as RedisCache; await cache.set('fooll', 'bar'); await expect(cache.get('fooll')).resolves.toEqual('bar'); }); }); describe('set', () => { it('should store a value without ttl', () => expect(redisCache.set('foo', 'bar')).resolves.toBeUndefined()); it('should store a value with a specific ttl', () => expect(redisCache.set('foo', 'bar', config.ttl)).resolves.toBeUndefined()); it('should store a value with a infinite ttl', () => expect(redisCache.set('foo', 'bar', { ttl: 0 })).resolves.toBeUndefined()); it('should not be able to store a null value (not cacheable)', () => expect(redisCache.set('foo2', null)).rejects.toBeDefined()); it('should store a value without callback', async () => { const value = 'baz'; await redisCache.set('foo', value); await expect(redisCache.get('foo')).resolves.toEqual(value); }); it('should not store an invalid value', () => expect(redisCache.set('foo1', undefined)).rejects.toStrictEqual( new Error('"undefined" is not a cacheable value'), )); it('should store an undefined value if permitted by isCacheableValue', async () => { expect(customRedisCache.store.isCacheableValue(undefined)).toBe(true); await customRedisCache.set('foo3', undefined); }); it('should not store a value disallowed by isCacheableValue', async () => { expect(customRedisCache.store.isCacheableValue('FooBarString')).toBe(false); await expect( customRedisCache.set('foobar', 'FooBarString'), ).rejects.toBeDefined(); }); it('should return an error if there is an error acquiring a connection', async () => { await redisCache.store.getClient.disconnect(); await expect(redisCache.set('foo', 'bar')).rejects.toBeDefined(); }); }); describe('mset', () => { it('should store a value without ttl', () => redisCache.store.mset([ ['foo', 'bar'], ['foo2', 'bar2'], ])); it( 'should store a value with a specific ttl', () => redisCache.store.mset( [ ['foo', 'bar'], ['foo2', 'bar2'], ], 60, ), 100000, ); it('should store a value with a infinite ttl', async () => { await redisCache.store.mset([ ['foo', 'bar'], ['foo2', 'bar2'], ]); await expect(redisCache.store.ttl('foo')).resolves.toEqual(-1); }); it('should not be able to store a null value (not cacheable)', () => expect(redisCache.store.mset([['foo2', null]])).rejects.toBeDefined()); it('should store a value without callback', async () => { await redisCache.store.mset([ ['foo', 'baz'], ['foo2', 'baz2'], ]); await expect(redisCache.store.mget('foo', 'foo2')).resolves.toStrictEqual([ 'baz', 'baz2', ]); }); it('should not store an invalid value', () => expect(redisCache.store.mset([['foo1', undefined]])).rejects.toBeDefined()); it('should store an undefined value if permitted by isCacheableValue', async () => { expect(customRedisCache.store.isCacheableValue(undefined)).toBe(true); await customRedisCache.store.mset([ ['foo3', undefined], ['foo4', undefined], ]); await expect( customRedisCache.store.mget('foo3', 'foo4'), ).resolves.toStrictEqual(['undefined', 'undefined']); }); it('should not store a value disallowed by isCacheableValue', async () => { expect(customRedisCache.store.isCacheableValue('FooBarString')).toBe(false); await expect( customRedisCache.store.mset([['foobar', 'FooBarString']]), ).rejects.toBeDefined(); }); it('should return an error if there is an error acquiring a connection', async () => { await redisCache.store.getClient.disconnect(); await expect(redisCache.store.mset([['foo', 'bar']])).rejects.toBeDefined(); }); }); describe('mget', () => { it('should retrieve a value for a given key', async () => { const value = 'bar'; const value2 = 'bar2'; await redisCache.store.mset([ ['foo', value], ['foo2', value2], ]); await expect(redisCache.store.mget('foo', 'foo2')).resolves.toStrictEqual([ value, value2, ]); }); it('should return null when the key is invalid', () => expect( redisCache.store.mget('invalidKey', 'otherInvalidKey'), ).resolves.toStrictEqual([null, null])); it('should return an error if there is an error acquiring a connection', async () => { await redisCache.store.getClient.disconnect(); await expect(redisCache.store.mget('foo')).rejects.toBeDefined(); }); }); describe('del', () => { it('should delete a value for a given key', async () => { await redisCache.set('foo', 'bar'); await expect(redisCache.del('foo')).resolves.toBeUndefined(); }); it('should delete a unlimited number of keys', async () => { await redisCache.store.mset([ ['foo', 'bar'], ['foo2', 'bar2'], ]); await expect( redisCache.store.del(['foo', 'foo2']), ).resolves.toBeUndefined(); }); it('should return an error if there is an error acquiring a connection', async () => { await redisCache.store.getClient.disconnect(); await expect(redisCache.del('foo')).rejects.toBeDefined(); }); }); describe('reset', () => { it('should flush underlying db', () => redisCache.reset()); it('should return an error if there is an error acquiring a connection', async () => { await redisCache.store.getClient.disconnect(); await expect(redisCache.reset()).rejects.toBeDefined(); }); }); describe('ttl', () => { it('should retrieve ttl for a given key', async () => { const ttl = 100; await redisCache.set('foo', 'bar', ttl); await expect(redisCache.store.ttl('foo')).resolves.toEqual(ttl); await redisCache.set('foo', 'bar', 0); await expect(redisCache.store.ttl('foo')).resolves.toEqual(-1); }); it('should retrieve ttl for an invalid key', () => expect(redisCache.store.ttl('invalidKey')).resolves.toEqual(-2)); it('should return an error if there is an error acquiring a connection', async () => { await redisCache.store.getClient.disconnect(); await expect(redisCache.store.ttl('foo')).rejects.toBeDefined(); }); }); describe('keys', () => { it('should return an array of keys for the given pattern', async () => { await redisCache.set('foo', 'bar'); await expect(redisCache.store.keys('f*')).resolves.toStrictEqual(['foo']); }); it('should return an array of all keys if called without a pattern', async () => { await redisCache.store.mset([ ['foo', 'bar'], ['foo2', 'bar2'], ['foo3', 'bar3'], ]); await expect( redisCache.store .keys('f*') .then((x) => x.sort((a, b) => a.localeCompare(b))), ).resolves.toStrictEqual(['foo', 'foo2', 'foo3']); }); it('should return an array of keys without pattern', async () => { await redisCache.reset(); await redisCache.set('foo', 'bar'); await expect(redisCache.store.keys()).resolves.toStrictEqual(['foo']); }); it('should return an error if there is an error acquiring a connection', async () => { await redisCache.store.getClient.disconnect(); await expect(redisCache.store.keys()).rejects.toBeDefined(); }); }); describe('isCacheableValue', () => { it('should return true when the value is not undefined', () => { expect(redisCache.store.isCacheableValue(0)).toBeTruthy(); expect(redisCache.store.isCacheableValue(100)).toBeTruthy(); expect(redisCache.store.isCacheableValue('')).toBeTruthy(); expect(redisCache.store.isCacheableValue('test')).toBeTruthy(); }); it('should return false when the value is undefined', () => { expect(redisCache.store.isCacheableValue(undefined)).toBeFalsy(); }); it('should return false when the value is null', () => { expect(redisCache.store.isCacheableValue(null)).toBeFalsy(); }); }); describe('redis error event', () => { it('should return an error when the redis server is unavailable', async () => { await new Promise<void>((resolve) => { redisCache.store.getClient.on('error', (err) => { expect(err).not.toEqual(null); resolve(); }); redisCache.store.getClient.emit('error', 'Something unexpected'); }); }); }); describe('wrap function', () => { // Simulate retrieving a user from a database const getUser = (id: number) => Promise.resolve({ id }); it('should work', async () => { const id = 123; await redisCache.wrap('wrap-promise', () => getUser(id)); // Second call to wrap should retrieve from cache await expect( redisCache.wrap('wrap-promise', () => getUser(id + 1)), ).resolves.toStrictEqual({ id }); }); });