Skip to content

Commit 7762898

Browse files
committed
chore: Update npm dependencies and add cache manager functions and tests
1 parent f8eee76 commit 7762898

File tree

4 files changed

+87
-124
lines changed

4 files changed

+87
-124
lines changed

bun.lockb

95.9 KB
Binary file not shown.

package.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@
1313
"@semantic-release/release-notes-generator": "^14.0.1",
1414
"@types/lodash": "^4.17.7",
1515
"bun-types": "latest",
16+
"jest": "^29.7.0",
17+
"vitest": "^2.0.5",
1618
"xo": "^0.59.3"
1719
},
1820
"peerDependencies": {
@@ -23,6 +25,7 @@
2325
},
2426
"dependencies": {
2527
"cache-manager": "^5.7.6",
28+
"cache-manager-ioredis-yet": "^2.1.1",
2629
"cache-manager-redis-store": "^3.0.1",
2730
"eslint-plugin-th-rules": "^1.13.4",
2831
"lodash": "^4.17.21",

src/index.ts

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -2,42 +2,42 @@
22
// eslint-disable-next-line import/no-unassigned-import
33
import 'reflect-metadata';
44
import cacheManager, {type Cache} from 'cache-manager';
5-
import {redisStore} from 'cache-manager-redis-store';
5+
import {redisStore} from 'cache-manager-ioredis-yet';
66
import {get} from 'lodash';
77

8-
type CacheInitializationConfig = {
8+
export type CacheInitializationConfig = {
99
host?: string;
1010
port?: number;
1111
password?: string;
1212
ttl: number;
1313
};
1414

15-
type KeySelector = ((...arguments_: unknown[]) => unknown[]) | string | string[];
15+
export type KeySelector = ((...arguments_: unknown[]) => unknown[]) | string | string[];
1616

17-
type CacheOptions = {
17+
export type CacheOptions = {
1818
ttl: number;
1919
keySelector: KeySelector;
2020
} & Partial<CacheInitializationConfig>;
2121

2222
let cacheManagerInstance: Cache | undefined;
2323

2424
/**
25-
* Initializes the cache manager with Redis store by default.
25+
* Initializes the cache manager with ioredis store by default.
2626
*/
27-
async function initializeCache(config: CacheInitializationConfig): Promise<void> {
28-
cacheManagerInstance = await cacheManager.caching({
29-
store: redisStore,
27+
export async function initializeCache(config: CacheInitializationConfig): Promise<void> {
28+
const store = async () => redisStore({
3029
host: config.host ?? 'localhost',
3130
port: config.port ?? 6379,
3231
password: config.password,
33-
ttl: config.ttl,
3432
});
33+
34+
cacheManagerInstance = await cacheManager.caching(store, {ttl: config.ttl});
3535
}
3636

3737
/**
3838
* Automatically initializes cache if not already initialized and initialization data is present.
3939
*/
40-
async function ensureCacheInitialized(config?: CacheInitializationConfig): Promise<void> {
40+
export async function ensureCacheInitialized(config?: CacheInitializationConfig): Promise<void> {
4141
if (!cacheManagerInstance && config) {
4242
await initializeCache(config);
4343
}
@@ -47,7 +47,7 @@ async function ensureCacheInitialized(config?: CacheInitializationConfig): Promi
4747
* Decorator to provide metadata for caching with TTL and key selection.
4848
*/
4949
export function cacheMeta(options: CacheOptions) {
50-
return async (target: unknown, propertyKey: string): Promise<void> => {
50+
return async (target: Record<string, unknown>, propertyKey: string): Promise<void> => {
5151
await ensureCacheInitialized(options);
5252
Reflect.defineMetadata('cacheKeySelector', options.keySelector, target, propertyKey);
5353
Reflect.defineMetadata('cacheTtl', options.ttl, target, propertyKey);
@@ -86,7 +86,7 @@ export function cache(options: CacheOptions) {
8686
/**
8787
* Creates a cache key based on the key selector function or paths.
8888
*/
89-
function createCacheKey(arguments_: unknown[], keySelector: KeySelector): string {
89+
export function createCacheKey(arguments_: unknown[], keySelector: KeySelector): string {
9090
if (typeof keySelector === 'function') {
9191
const result = keySelector(...arguments_);
9292
return Array.isArray(result) ? result.join(':') : String(result);

tests/index.test.ts

Lines changed: 72 additions & 112 deletions
Original file line numberDiff line numberDiff line change
@@ -1,136 +1,96 @@
1-
import 'reflect-metadata'; // eslint-disable-line import/no-unassigned-import
2-
import {
3-
expect, describe, it, afterEach,
4-
mock,
5-
} from 'bun:test';
6-
import cacheManager, {type Cache} from 'cache-manager';
7-
import {redisStore} from 'cache-manager-redis-store';
1+
2+
import {test, expect} from 'bun:test';
83
import {
9-
cache, cacheMeta, cacheFunction, initializeCache, ensureCacheInitialized,
4+
initializeCache,
5+
ensureCacheInitialized,
6+
cacheMeta,
7+
cache,
8+
createCacheKey,
9+
cacheFunction,
10+
type CacheInitializationConfig,
1011
} from '../src/index';
1112

12-
mock(cacheManager.caching, function_ => ({
13-
async caching(config) {
14-
return mockCacheManagerInstance;
15-
},
16-
}));
17-
mock(redisStore, () => jest.fn());
18-
19-
const mockCacheManagerInstance: Partial<Cache> = {
20-
get: mock(async () => null),
21-
set: mock(async () => undefined),
13+
const testConfig: CacheInitializationConfig = {
14+
host: '127.0.0.1',
15+
port: 6379,
16+
ttl: 300,
2217
};
2318

24-
describe('Cache Manager Functions', () => {
25-
afterEach(() => {
26-
mockCacheManagerInstance.get.mockClear();
27-
mockCacheManagerInstance.set.mockClear();
28-
});
29-
30-
describe('initializeCache', () => {
31-
it('should initialize cache with given config', async () => {
32-
await initializeCache({host: 'localhost', port: 6379, ttl: 100});
33-
34-
expect(cacheManager.caching).toHaveBeenCalledWith({
35-
store: redisStore,
36-
host: 'localhost',
37-
port: 6379,
38-
password: undefined,
39-
ttl: 100,
40-
});
41-
});
42-
});
43-
44-
describe('ensureCacheInitialized', () => {
45-
it('should initialize cache if not already initialized', async () => {
46-
await ensureCacheInitialized({ttl: 200});
47-
48-
expect(cacheManager.caching).toHaveBeenCalled();
49-
});
50-
51-
it('should not initialize cache if already initialized', async () => {
52-
await initializeCache({ttl: 200});
53-
await ensureCacheInitialized({ttl: 200});
54-
55-
expect(cacheManager.caching).toHaveBeenCalledTimes(1);
56-
});
57-
});
58-
59-
describe('cacheMeta', () => {
60-
it('should define cache metadata correctly', async () => {
61-
const target = {};
62-
const propertyKey = 'testFunction';
19+
test('initializeCache initializes cache manager with correct configuration', async () => {
20+
await initializeCache(testConfig);
6321

64-
await cacheMeta({ttl: 300, keySelector: 'key.path'})(target, propertyKey);
65-
66-
expect(Reflect.getMetadata('cacheKeySelector', target, propertyKey)).toEqual('key.path');
67-
expect(Reflect.getMetadata('cacheTtl', target, propertyKey)).toEqual(300);
68-
});
69-
});
70-
71-
describe('cache', () => {
72-
it('should cache the result of the decorated function', async () => {
73-
const target = {};
74-
const propertyKey = 'cachedMethod';
75-
const originalMethod = mock(async () => 'result');
76-
const descriptor = {
77-
value: originalMethod,
78-
};
22+
expect(globalThis.cacheManagerInstance).toBeDefined();
23+
});
7924

80-
cache({ttl: 400, keySelector: 'id'})(target, propertyKey, descriptor);
25+
test('ensureCacheInitialized initializes cache if not initialized', async () => {
26+
globalThis.cacheManagerInstance = undefined;
27+
await ensureCacheInitialized(testConfig);
28+
expect(globalThis.cacheManagerInstance).toBeDefined();
29+
});
8130

82-
mockCacheManagerInstance.get.mockResolvedValueOnce(null);
31+
test('ensureCacheInitialized does not initialize cache if already initialized', async () => {
32+
globalThis.cacheManagerInstance = {get: async () => 'mocked'} as any;
33+
await ensureCacheInitialized(testConfig);
34+
expect(globalThis.cacheManagerInstance.get).toBeDefined();
35+
});
8336

84-
const result = await descriptor.value.apply({}, [{id: 1}]);
37+
test('createCacheKey generates key correctly based on keySelector function', () => {
38+
const keySelector = (a: number, b: number) => [a, b];
39+
const key = createCacheKey([1, 2], keySelector);
40+
expect(key).toBe('1:2');
41+
});
8542

86-
expect(originalMethod).toHaveBeenCalledWith({id: 1});
87-
expect(mockCacheManagerInstance.set).toHaveBeenCalledWith(expect.any(String), 'result', 400);
88-
expect(result).toEqual('result');
89-
});
43+
test('createCacheKey generates key correctly based on string selector', () => {
44+
const key = createCacheKey([{name: 'test'}], 'name');
45+
expect(key).toBe('test');
46+
});
9047

91-
it('should return cached result if available', async () => {
92-
const target = {};
93-
const propertyKey = 'cachedMethod';
94-
const originalMethod = mock(async () => 'should not be called');
95-
const descriptor = {
96-
value: originalMethod,
97-
};
48+
test('createCacheKey generates key correctly based on array of selectors', () => {
49+
const key = createCacheKey([{name: 'test', age: 25}], ['name', 'age']);
50+
expect(key).toBe('test:25');
51+
});
9852

99-
cache({ttl: 400, keySelector: 'id'})(target, propertyKey, descriptor);
53+
test('cacheMeta sets metadata correctly', async () => {
54+
const options = {ttl: 300, keySelector: 'name'};
55+
const target = {};
56+
const propertyKey = 'testMethod';
10057

101-
mockCacheManagerInstance.get.mockResolvedValueOnce('cachedResult');
58+
await cacheMeta(options)(target, propertyKey);
10259

103-
const result = await descriptor.value.apply({}, [{id: 1}]);
60+
expect(Reflect.getMetadata('cacheKeySelector', target, propertyKey)).toBe('name');
61+
expect(Reflect.getMetadata('cacheTtl', target, propertyKey)).toBe(300);
62+
});
10463

105-
expect(originalMethod).not.toHaveBeenCalled();
106-
expect(result).toEqual('cachedResult');
107-
});
108-
});
64+
test('cache decorator caches function results', async () => {
65+
await initializeCache(testConfig);
66+
const options = {ttl: 300, keySelector: 'name'};
67+
const target = {};
68+
const descriptor = {
69+
async value() {
70+
return 'result';
71+
},
72+
} as PropertyDescriptor;
10973

110-
describe('cacheFunction', () => {
111-
it('should cache the result of the function', async () => {
112-
const function_ = mock(async () => 'computedValue');
113-
const wrappedFunction = cacheFunction(function_, {ttl: 500, keySelector: 'name'});
74+
cache(options)(target, 'testMethod', descriptor);
11475

115-
mockCacheManagerInstance.get.mockResolvedValueOnce(null);
76+
const result = await descriptor.value({name: 'test'});
11677

117-
const result = await wrappedFunction({name: 'test'});
78+
const cachedResult = await globalThis.cacheManagerInstance!.get('test');
11879

119-
expect(function_).toHaveBeenCalledWith({name: 'test'});
120-
expect(mockCacheManagerInstance.set).toHaveBeenCalledWith(expect.any(String), 'computedValue', 500);
121-
expect(result).toEqual('computedValue');
122-
});
80+
expect(result).toBe('result');
81+
expect(cachedResult).toBe('result');
82+
});
12383

124-
it('should return cached result if available', async () => {
125-
const function_ = mock(async () => 'should not be called');
126-
const wrappedFunction = cacheFunction(function_, {ttl: 500, keySelector: 'name'});
84+
test('cacheFunction caches results correctly', async () => {
85+
await initializeCache(testConfig);
86+
const options = {ttl: 300, keySelector: 'name'};
87+
const originalFunction = async () => 'result';
12788

128-
mockCacheManagerInstance.get.mockResolvedValueOnce('cachedValue');
89+
const wrappedFunction = cacheFunction(originalFunction, options);
90+
const result = await wrappedFunction({name: 'test'});
12991

130-
const result = await wrappedFunction({name: 'test'});
92+
const cachedResult = await globalThis.cacheManagerInstance!.get('test');
13193

132-
expect(function_).not.toHaveBeenCalled();
133-
expect(result).toEqual('cachedValue');
134-
});
135-
});
94+
expect(result).toBe('result');
95+
expect(cachedResult).toBe('result');
13696
});

0 commit comments

Comments
 (0)