@jintianxiayu/cache-decorator 1.0.1 → 1.0.2
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/CHANGELOG.md +8 -0
- package/README.md +11 -5
- package/dist/decorators/cache-evict.d.ts.map +1 -1
- package/dist/decorators/cache-evict.js +15 -10
- package/dist/decorators/cache-evict.js.map +1 -1
- package/dist/decorators/cache.d.ts.map +1 -1
- package/dist/decorators/cache.js +23 -29
- package/dist/decorators/cache.js.map +1 -1
- package/package.json +1 -1
- package/src/decorators/cache-evict.ts +19 -11
- package/src/decorators/cache.ts +34 -30
- package/test/cache-evict-logging.test.ts +47 -11
- package/test/cache-logger.integration.test.ts +30 -0
- package/test/cache-logging.test.ts +95 -18
- package/test/cache.test.ts +84 -6
- package/test/fixtures/cache-logger-child.mjs +108 -108
- package/test/fixtures/cache-provider-failure-child.mjs +70 -0
- package/test/redis-cache-decorator.test.ts +65 -4
|
@@ -12,6 +12,9 @@ interface SharedRedisState {
|
|
|
12
12
|
readonly values: Map<string, string>;
|
|
13
13
|
readonly writes: Array<{ readonly key: string; readonly value: string; readonly ttlSeconds?: number }>;
|
|
14
14
|
getError?: unknown;
|
|
15
|
+
setError?: unknown;
|
|
16
|
+
deleteError?: unknown;
|
|
17
|
+
scanError?: unknown;
|
|
15
18
|
}
|
|
16
19
|
|
|
17
20
|
function createSharedRedisState(): SharedRedisState {
|
|
@@ -30,24 +33,36 @@ function createSharedIoredisSource(state: SharedRedisState, keyPrefix: string):
|
|
|
30
33
|
return Promise.resolve(state.values.get(physicalKey(key)) ?? null);
|
|
31
34
|
},
|
|
32
35
|
set(key: string, value: string): Promise<unknown> {
|
|
36
|
+
if (state.setError !== undefined) {
|
|
37
|
+
return Promise.reject(state.setError);
|
|
38
|
+
}
|
|
33
39
|
const resolvedKey = physicalKey(key);
|
|
34
40
|
state.values.set(resolvedKey, value);
|
|
35
41
|
state.writes.push({ key: resolvedKey, value });
|
|
36
42
|
return Promise.resolve('OK');
|
|
37
43
|
},
|
|
38
44
|
setex(key: string, ttlSeconds: number, value: string): Promise<unknown> {
|
|
45
|
+
if (state.setError !== undefined) {
|
|
46
|
+
return Promise.reject(state.setError);
|
|
47
|
+
}
|
|
39
48
|
const resolvedKey = physicalKey(key);
|
|
40
49
|
state.values.set(resolvedKey, value);
|
|
41
50
|
state.writes.push({ key: resolvedKey, value, ttlSeconds });
|
|
42
51
|
return Promise.resolve('OK');
|
|
43
52
|
},
|
|
44
53
|
del(...keys: string[]): Promise<unknown> {
|
|
54
|
+
if (state.deleteError !== undefined) {
|
|
55
|
+
return Promise.reject(state.deleteError);
|
|
56
|
+
}
|
|
45
57
|
for (const key of keys) {
|
|
46
58
|
state.values.delete(physicalKey(key));
|
|
47
59
|
}
|
|
48
60
|
return Promise.resolve(keys.length);
|
|
49
61
|
},
|
|
50
62
|
scan(..._args: Parameters<IoredisCacheClientSource['scan']>): Promise<unknown> {
|
|
63
|
+
if (state.scanError !== undefined) {
|
|
64
|
+
return Promise.reject(state.scanError);
|
|
65
|
+
}
|
|
51
66
|
return Promise.resolve(['0', []]);
|
|
52
67
|
},
|
|
53
68
|
flushdb(): Promise<unknown> {
|
|
@@ -67,16 +82,25 @@ function createSharedNodeRedisSource(state: SharedRedisState): NodeRedisCacheCli
|
|
|
67
82
|
return Promise.resolve(state.values.get(key) ?? null);
|
|
68
83
|
},
|
|
69
84
|
set(key: string, value: string): Promise<unknown> {
|
|
85
|
+
if (state.setError !== undefined) {
|
|
86
|
+
return Promise.reject(state.setError);
|
|
87
|
+
}
|
|
70
88
|
state.values.set(key, value);
|
|
71
89
|
state.writes.push({ key, value });
|
|
72
90
|
return Promise.resolve('OK');
|
|
73
91
|
},
|
|
74
92
|
setEx(key: string, ttlSeconds: number, value: string): Promise<unknown> {
|
|
93
|
+
if (state.setError !== undefined) {
|
|
94
|
+
return Promise.reject(state.setError);
|
|
95
|
+
}
|
|
75
96
|
state.values.set(key, value);
|
|
76
97
|
state.writes.push({ key, value, ttlSeconds });
|
|
77
98
|
return Promise.resolve('OK');
|
|
78
99
|
},
|
|
79
100
|
del(keys: string | string[]): Promise<unknown> {
|
|
101
|
+
if (state.deleteError !== undefined) {
|
|
102
|
+
return Promise.reject(state.deleteError);
|
|
103
|
+
}
|
|
80
104
|
const deletedKeys = typeof keys === 'string' ? [keys] : keys;
|
|
81
105
|
for (const key of deletedKeys) {
|
|
82
106
|
state.values.delete(key);
|
|
@@ -84,6 +108,9 @@ function createSharedNodeRedisSource(state: SharedRedisState): NodeRedisCacheCli
|
|
|
84
108
|
return Promise.resolve(deletedKeys.length);
|
|
85
109
|
},
|
|
86
110
|
scan(): Promise<unknown> {
|
|
111
|
+
if (state.scanError !== undefined) {
|
|
112
|
+
return Promise.reject(state.scanError);
|
|
113
|
+
}
|
|
87
114
|
return Promise.resolve({ cursor: '0', keys: [] });
|
|
88
115
|
},
|
|
89
116
|
flushDb(): Promise<unknown> {
|
|
@@ -510,7 +537,7 @@ it('不可序列化异常 payload 不向 Redis 发送 SET', async () => {
|
|
|
510
537
|
expect(state.values).toEqual(new Map());
|
|
511
538
|
});
|
|
512
539
|
|
|
513
|
-
it('Redis GET
|
|
540
|
+
it('Redis GET 失败旁路缓存且不调用异常策略', async () => {
|
|
514
541
|
const state = createSharedRedisState();
|
|
515
542
|
const readError = new Error('GET unavailable');
|
|
516
543
|
state.getError = readError;
|
|
@@ -519,7 +546,8 @@ it('Redis GET 失败传播基础设施错误且不调用策略或业务方法',
|
|
|
519
546
|
const shouldCache = jest.fn((_error: unknown): boolean => true);
|
|
520
547
|
const encode = jest.fn((error: unknown): unknown => error);
|
|
521
548
|
const decode = jest.fn((payload: unknown): unknown => payload);
|
|
522
|
-
const
|
|
549
|
+
const businessResult = { id: 22 };
|
|
550
|
+
const businessMethod = jest.fn(() => businessResult);
|
|
523
551
|
|
|
524
552
|
class UserService {
|
|
525
553
|
@Cache('failed-get-errors', {
|
|
@@ -531,10 +559,43 @@ it('Redis GET 失败传播基础设施错误且不调用策略或业务方法',
|
|
|
531
559
|
}
|
|
532
560
|
}
|
|
533
561
|
|
|
534
|
-
await expect(new UserService().getUser()).
|
|
535
|
-
expect(businessMethod).
|
|
562
|
+
await expect(new UserService().getUser()).resolves.toBe(businessResult);
|
|
563
|
+
expect(businessMethod).toHaveBeenCalledTimes(1);
|
|
536
564
|
expect(shouldCache).not.toHaveBeenCalled();
|
|
537
565
|
expect(encode).not.toHaveBeenCalled();
|
|
538
566
|
expect(decode).not.toHaveBeenCalled();
|
|
539
567
|
expect(state.writes).toEqual([]);
|
|
540
568
|
});
|
|
569
|
+
|
|
570
|
+
it('redis-cache-client/E07 Redis 写入与淘汰命令失败不改变装饰器业务结果', async () => {
|
|
571
|
+
const state = createSharedRedisState();
|
|
572
|
+
state.setError = new Error('SET unavailable');
|
|
573
|
+
state.deleteError = new Error('DEL unavailable');
|
|
574
|
+
state.scanError = new Error('SCAN unavailable');
|
|
575
|
+
const provider = new RedisCacheProvider(createNodeRedisCacheClient(createSharedNodeRedisSource(state)));
|
|
576
|
+
CacheProviderRegistry.register('failed-commands-node', provider);
|
|
577
|
+
|
|
578
|
+
class UserService {
|
|
579
|
+
@Cache('failed-set-users', { providerName: 'failed-commands-node' })
|
|
580
|
+
getUser(): string {
|
|
581
|
+
return 'business-read';
|
|
582
|
+
}
|
|
583
|
+
|
|
584
|
+
@CacheEvict('failed-delete-users', { providerName: 'failed-commands-node', key: 'user-24' })
|
|
585
|
+
updateUser(): string {
|
|
586
|
+
return 'business-update';
|
|
587
|
+
}
|
|
588
|
+
|
|
589
|
+
@CacheEvict('failed-scan-users', { providerName: 'failed-commands-node', allEntries: true })
|
|
590
|
+
clearUsers(): string {
|
|
591
|
+
return 'business-clear';
|
|
592
|
+
}
|
|
593
|
+
}
|
|
594
|
+
|
|
595
|
+
const service = new UserService();
|
|
596
|
+
await expect(service.getUser()).resolves.toBe('business-read');
|
|
597
|
+
await expect(service.updateUser()).resolves.toBe('business-update');
|
|
598
|
+
await expect(service.clearUsers()).resolves.toBe('business-clear');
|
|
599
|
+
await Promise.resolve();
|
|
600
|
+
expect(state.writes).toEqual([]);
|
|
601
|
+
});
|