@tsed/platform-cache 8.0.0 → 8.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/package.json +12 -11
- package/src/decorators/useCache.ts +8 -0
- package/src/index.ts +11 -0
- package/src/interceptors/PlatformCacheInterceptor.spec.ts +650 -0
- package/src/interceptors/PlatformCacheInterceptor.ts +235 -0
- package/src/interfaces/PlatformCacheOptions.ts +27 -0
- package/src/interfaces/PlatformCachedObject.ts +7 -0
- package/src/interfaces/interfaces.ts +34 -0
- package/src/services/PlatformCache.spec.ts +363 -0
- package/src/services/PlatformCache.ts +204 -0
- package/src/utils/getPrefix.ts +13 -0
- package/src/utils/isEndpoint.ts +6 -0
|
@@ -0,0 +1,650 @@
|
|
|
1
|
+
import {isClass} from "@tsed/core";
|
|
2
|
+
import {serialize} from "@tsed/json-mapper";
|
|
3
|
+
import {PlatformTest} from "@tsed/platform-http/testing";
|
|
4
|
+
|
|
5
|
+
import {UseCache} from "../decorators/useCache.js";
|
|
6
|
+
import {PlatformCache} from "../services/PlatformCache.js";
|
|
7
|
+
import {isEndpoint} from "../utils/isEndpoint.js";
|
|
8
|
+
import {PlatformCacheInterceptor} from "./PlatformCacheInterceptor.js";
|
|
9
|
+
|
|
10
|
+
const defaultKeyResolver = (args: any[]) => {
|
|
11
|
+
return args.map((arg: any) => (isClass(arg) ? JSON.stringify(serialize(arg)) : arg)).join(":");
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
async function getInterceptorFixture(opts: any = {}) {
|
|
15
|
+
const cache: any = {
|
|
16
|
+
get: vi.fn().mockResolvedValue(false),
|
|
17
|
+
set: vi.fn().mockResolvedValue(false),
|
|
18
|
+
del: vi.fn().mockResolvedValue(true),
|
|
19
|
+
ttl: vi.fn().mockResolvedValue(opts.ttl || 6999),
|
|
20
|
+
calculateTTL: vi.fn().mockImplementation((result: any, ttl: any) => ttl),
|
|
21
|
+
isForceRefresh: vi.fn().mockReturnValue(opts.forceRefresh)
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
const interceptor = await PlatformTest.invoke<PlatformCacheInterceptor>(PlatformCacheInterceptor, [
|
|
25
|
+
{
|
|
26
|
+
token: PlatformCache,
|
|
27
|
+
use: cache
|
|
28
|
+
}
|
|
29
|
+
]);
|
|
30
|
+
|
|
31
|
+
return {cache, interceptor};
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
vi.mock("../utils/isEndpoint");
|
|
35
|
+
|
|
36
|
+
describe("PlatformCacheInterceptor", () => {
|
|
37
|
+
beforeEach(() => PlatformTest.create());
|
|
38
|
+
afterEach(() => PlatformTest.reset());
|
|
39
|
+
|
|
40
|
+
describe("intercept()", () => {
|
|
41
|
+
it("should intercept the method and call endpoint cache method", async () => {
|
|
42
|
+
const interceptor = await PlatformTest.invoke<PlatformCacheInterceptor>(PlatformCacheInterceptor, [
|
|
43
|
+
{
|
|
44
|
+
token: PlatformCache,
|
|
45
|
+
use: {
|
|
46
|
+
disabled: vi.fn().mockReturnValue(false)
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
]);
|
|
50
|
+
vi.spyOn(interceptor as any, "cacheMethod").mockResolvedValue("test");
|
|
51
|
+
vi.spyOn(interceptor as any, "cacheResponse").mockResolvedValue("test");
|
|
52
|
+
(isEndpoint as any).mockReturnValue(true);
|
|
53
|
+
|
|
54
|
+
const context: any = {};
|
|
55
|
+
const next: any = vi.fn();
|
|
56
|
+
|
|
57
|
+
await interceptor.intercept(context, next);
|
|
58
|
+
|
|
59
|
+
expect((interceptor as any).cacheResponse).toHaveBeenCalledWith(context, next);
|
|
60
|
+
});
|
|
61
|
+
it("should intercept the method and call service cache method", async () => {
|
|
62
|
+
const interceptor = await PlatformTest.invoke<PlatformCacheInterceptor>(PlatformCacheInterceptor, [
|
|
63
|
+
{
|
|
64
|
+
token: PlatformCache,
|
|
65
|
+
use: {
|
|
66
|
+
disabled: vi.fn().mockReturnValue(false)
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
]);
|
|
70
|
+
|
|
71
|
+
vi.spyOn(interceptor as any, "cacheMethod").mockResolvedValue("test");
|
|
72
|
+
vi.spyOn(interceptor as any, "cacheResponse").mockResolvedValue("test");
|
|
73
|
+
(isEndpoint as any).mockReturnValue(false);
|
|
74
|
+
|
|
75
|
+
const context: any = {};
|
|
76
|
+
const next: any = vi.fn();
|
|
77
|
+
|
|
78
|
+
await interceptor.intercept(context, next);
|
|
79
|
+
|
|
80
|
+
expect((interceptor as any).cacheMethod).toHaveBeenCalledWith(context, next);
|
|
81
|
+
});
|
|
82
|
+
it("should do nothing when the cache is disabled", async () => {
|
|
83
|
+
const interceptor = await PlatformTest.invoke<PlatformCacheInterceptor>(PlatformCacheInterceptor, [
|
|
84
|
+
{
|
|
85
|
+
token: PlatformCache,
|
|
86
|
+
use: {
|
|
87
|
+
disabled: vi.fn().mockReturnValue(true)
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
]);
|
|
91
|
+
|
|
92
|
+
vi.spyOn(interceptor as any, "cacheMethod").mockResolvedValue("test");
|
|
93
|
+
vi.spyOn(interceptor as any, "cacheResponse").mockResolvedValue("test");
|
|
94
|
+
(isEndpoint as any).mockReturnValue(false);
|
|
95
|
+
|
|
96
|
+
const context: any = {};
|
|
97
|
+
const next: any = vi.fn();
|
|
98
|
+
|
|
99
|
+
await interceptor.intercept(context, next);
|
|
100
|
+
|
|
101
|
+
expect((interceptor as any).cacheResponse).not.toHaveBeenCalled();
|
|
102
|
+
expect((interceptor as any).cacheMethod).not.toHaveBeenCalled();
|
|
103
|
+
});
|
|
104
|
+
});
|
|
105
|
+
describe("canRefreshInBackground()", () => {
|
|
106
|
+
it("should refresh key in background", async () => {
|
|
107
|
+
const {cache, interceptor} = await getInterceptorFixture();
|
|
108
|
+
|
|
109
|
+
const next = vi.fn();
|
|
110
|
+
|
|
111
|
+
await interceptor.canRefreshInBackground("key", {refreshThreshold: 300, ttl: 10000}, next);
|
|
112
|
+
|
|
113
|
+
expect(cache.get).toHaveBeenCalledWith("$$queue:key");
|
|
114
|
+
expect(cache.set).toHaveBeenCalledWith("$$queue:key", true, {ttl: 120});
|
|
115
|
+
expect(cache.del).toHaveBeenCalledWith("$$queue:key");
|
|
116
|
+
expect(cache.ttl).toHaveBeenCalledWith("key");
|
|
117
|
+
expect(next).toHaveBeenCalledWith();
|
|
118
|
+
});
|
|
119
|
+
it("should not refresh key in background", async () => {
|
|
120
|
+
const {cache, interceptor} = await getInterceptorFixture({
|
|
121
|
+
ttl: 9700
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
const next = vi.fn();
|
|
125
|
+
|
|
126
|
+
await interceptor.canRefreshInBackground("key", {refreshThreshold: 300, ttl: 10000}, next);
|
|
127
|
+
|
|
128
|
+
expect(cache.get).toHaveBeenCalledWith("$$queue:key");
|
|
129
|
+
expect(cache.ttl).toHaveBeenCalledWith("key");
|
|
130
|
+
expect(cache.set).toHaveBeenCalledWith("$$queue:key", true, {ttl: 120});
|
|
131
|
+
expect(cache.del).toHaveBeenCalledWith("$$queue:key");
|
|
132
|
+
expect(next).not.toHaveBeenCalled();
|
|
133
|
+
});
|
|
134
|
+
});
|
|
135
|
+
describe("cacheMethod()", () => {
|
|
136
|
+
it("should return the cached response", async () => {
|
|
137
|
+
const {cache, interceptor} = await getInterceptorFixture();
|
|
138
|
+
cache.getCachedObject = vi.fn().mockResolvedValue({
|
|
139
|
+
data: JSON.stringify({data: "data"})
|
|
140
|
+
});
|
|
141
|
+
cache.setCachedObject = vi.fn().mockResolvedValue("test");
|
|
142
|
+
cache.defaultKeyResolver = () => defaultKeyResolver;
|
|
143
|
+
|
|
144
|
+
class Test {
|
|
145
|
+
@UseCache({
|
|
146
|
+
ttl: 10000,
|
|
147
|
+
refreshThreshold: 1000
|
|
148
|
+
})
|
|
149
|
+
test(arg: string) {
|
|
150
|
+
return "";
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
const next = vi.fn().mockReturnValue({data: "refreshed"});
|
|
155
|
+
const context: any = {
|
|
156
|
+
target: Test,
|
|
157
|
+
propertyKey: "test",
|
|
158
|
+
args: ["value"],
|
|
159
|
+
options: {
|
|
160
|
+
ttl: 10000,
|
|
161
|
+
refreshThreshold: 1000
|
|
162
|
+
}
|
|
163
|
+
};
|
|
164
|
+
|
|
165
|
+
vi.spyOn(interceptor, "canRefreshInBackground").mockResolvedValue();
|
|
166
|
+
|
|
167
|
+
const result = await interceptor.cacheMethod(context, next);
|
|
168
|
+
|
|
169
|
+
expect(cache.getCachedObject).toHaveBeenCalledWith("Test:test:value");
|
|
170
|
+
expect(result).toEqual({
|
|
171
|
+
data: "data"
|
|
172
|
+
});
|
|
173
|
+
|
|
174
|
+
(interceptor.canRefreshInBackground as any).mock.calls[0][2]();
|
|
175
|
+
|
|
176
|
+
await new Promise((resolve) => setTimeout(resolve, 100));
|
|
177
|
+
|
|
178
|
+
expect(cache.setCachedObject).toHaveBeenCalledWith(
|
|
179
|
+
"Test:test:value",
|
|
180
|
+
{data: "refreshed"},
|
|
181
|
+
{
|
|
182
|
+
args: ["value"],
|
|
183
|
+
ttl: 10000
|
|
184
|
+
}
|
|
185
|
+
);
|
|
186
|
+
});
|
|
187
|
+
it("should return the cached response (prefix)", async () => {
|
|
188
|
+
const {cache, interceptor} = await getInterceptorFixture();
|
|
189
|
+
cache.getCachedObject = vi.fn().mockResolvedValue({
|
|
190
|
+
data: JSON.stringify({data: "data"})
|
|
191
|
+
});
|
|
192
|
+
cache.setCachedObject = vi.fn().mockResolvedValue("test");
|
|
193
|
+
cache.defaultKeyResolver = () => defaultKeyResolver;
|
|
194
|
+
|
|
195
|
+
class Test {
|
|
196
|
+
@UseCache({
|
|
197
|
+
prefix: "TEST",
|
|
198
|
+
ttl: 10000,
|
|
199
|
+
refreshThreshold: 1000
|
|
200
|
+
})
|
|
201
|
+
test(arg: string) {
|
|
202
|
+
return "";
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
const next = vi.fn().mockReturnValue({data: "refreshed"});
|
|
207
|
+
const context: any = {
|
|
208
|
+
target: Test,
|
|
209
|
+
propertyKey: "test",
|
|
210
|
+
args: ["value"],
|
|
211
|
+
options: {
|
|
212
|
+
prefix: "TEST",
|
|
213
|
+
ttl: 10000,
|
|
214
|
+
refreshThreshold: 1000
|
|
215
|
+
}
|
|
216
|
+
};
|
|
217
|
+
|
|
218
|
+
vi.spyOn(interceptor, "canRefreshInBackground").mockResolvedValue();
|
|
219
|
+
|
|
220
|
+
const result = await interceptor.cacheMethod(context, next);
|
|
221
|
+
|
|
222
|
+
expect(cache.getCachedObject).toHaveBeenCalledWith("TEST:value");
|
|
223
|
+
expect(result).toEqual({
|
|
224
|
+
data: "data"
|
|
225
|
+
});
|
|
226
|
+
|
|
227
|
+
(interceptor.canRefreshInBackground as any).mock.calls[0][2]();
|
|
228
|
+
|
|
229
|
+
await new Promise((resolve) => setTimeout(resolve, 100));
|
|
230
|
+
|
|
231
|
+
expect(cache.setCachedObject).toHaveBeenCalledWith(
|
|
232
|
+
"TEST:value",
|
|
233
|
+
{data: "refreshed"},
|
|
234
|
+
{
|
|
235
|
+
args: ["value"],
|
|
236
|
+
ttl: 10000
|
|
237
|
+
}
|
|
238
|
+
);
|
|
239
|
+
});
|
|
240
|
+
it("should force the refresh data", async () => {
|
|
241
|
+
const {cache, interceptor} = await getInterceptorFixture({
|
|
242
|
+
forceRefresh: true
|
|
243
|
+
});
|
|
244
|
+
cache.getCachedObject = vi.fn().mockResolvedValue({
|
|
245
|
+
data: JSON.stringify({data: "data"})
|
|
246
|
+
});
|
|
247
|
+
cache.setCachedObject = vi.fn().mockResolvedValue("test");
|
|
248
|
+
cache.defaultKeyResolver = () => defaultKeyResolver;
|
|
249
|
+
|
|
250
|
+
class Test {
|
|
251
|
+
@UseCache({
|
|
252
|
+
ttl: 10000,
|
|
253
|
+
refreshThreshold: 1000
|
|
254
|
+
})
|
|
255
|
+
test(arg: string) {
|
|
256
|
+
return "";
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
const next = vi.fn().mockReturnValue({data: "refreshed"});
|
|
261
|
+
const context: any = {
|
|
262
|
+
target: Test,
|
|
263
|
+
propertyKey: "test",
|
|
264
|
+
args: ["value"],
|
|
265
|
+
options: {
|
|
266
|
+
ttl: 10000,
|
|
267
|
+
refreshThreshold: 1000
|
|
268
|
+
}
|
|
269
|
+
};
|
|
270
|
+
|
|
271
|
+
vi.spyOn(interceptor, "canRefreshInBackground").mockResolvedValue();
|
|
272
|
+
|
|
273
|
+
const result = await interceptor.cacheMethod(context, next);
|
|
274
|
+
|
|
275
|
+
expect(cache.getCachedObject).toHaveBeenCalledWith("Test:test:value");
|
|
276
|
+
expect(result).toEqual({
|
|
277
|
+
data: "refreshed"
|
|
278
|
+
});
|
|
279
|
+
expect(interceptor.canRefreshInBackground).not.toHaveBeenCalled();
|
|
280
|
+
expect(cache.setCachedObject).toHaveBeenCalledWith(
|
|
281
|
+
"Test:test:value",
|
|
282
|
+
{data: "refreshed"},
|
|
283
|
+
{
|
|
284
|
+
args: ["value"],
|
|
285
|
+
ttl: 10000
|
|
286
|
+
}
|
|
287
|
+
);
|
|
288
|
+
});
|
|
289
|
+
it("should call the endpoint and cache the response", async () => {
|
|
290
|
+
const {cache, interceptor} = await getInterceptorFixture();
|
|
291
|
+
cache.getCachedObject = vi.fn().mockResolvedValue(undefined);
|
|
292
|
+
cache.setCachedObject = vi.fn().mockResolvedValue("test");
|
|
293
|
+
cache.defaultKeyResolver = () => defaultKeyResolver;
|
|
294
|
+
|
|
295
|
+
class Test {
|
|
296
|
+
@UseCache({
|
|
297
|
+
ttl: 10000,
|
|
298
|
+
refreshThreshold: 1000
|
|
299
|
+
})
|
|
300
|
+
test(arg: string) {
|
|
301
|
+
return "";
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
const next = vi.fn().mockResolvedValue({
|
|
306
|
+
data: "data"
|
|
307
|
+
});
|
|
308
|
+
const context: any = {
|
|
309
|
+
target: Test,
|
|
310
|
+
propertyKey: "test",
|
|
311
|
+
args: ["value"],
|
|
312
|
+
options: {
|
|
313
|
+
ttl: 10000,
|
|
314
|
+
refreshThreshold: 1000
|
|
315
|
+
}
|
|
316
|
+
};
|
|
317
|
+
|
|
318
|
+
vi.spyOn(interceptor, "canRefreshInBackground").mockResolvedValue();
|
|
319
|
+
|
|
320
|
+
const result = await interceptor.cacheMethod(context, next);
|
|
321
|
+
|
|
322
|
+
await new Promise((resolve) => setTimeout(resolve, 100));
|
|
323
|
+
|
|
324
|
+
expect(interceptor.canRefreshInBackground).not.toHaveBeenCalled();
|
|
325
|
+
expect(cache.getCachedObject).toHaveBeenCalledWith("Test:test:value");
|
|
326
|
+
expect(cache.setCachedObject).toHaveBeenCalledWith(
|
|
327
|
+
"Test:test:value",
|
|
328
|
+
{data: "data"},
|
|
329
|
+
{
|
|
330
|
+
args: ["value"],
|
|
331
|
+
ttl: 10000
|
|
332
|
+
}
|
|
333
|
+
);
|
|
334
|
+
expect(result).toEqual({
|
|
335
|
+
data: "data"
|
|
336
|
+
});
|
|
337
|
+
});
|
|
338
|
+
it("should doesn't cache nullish result", async () => {
|
|
339
|
+
const {cache, interceptor} = await getInterceptorFixture();
|
|
340
|
+
cache.getCachedObject = vi.fn().mockResolvedValue(undefined);
|
|
341
|
+
cache.setCachedObject = vi.fn().mockResolvedValue("test");
|
|
342
|
+
cache.defaultKeyResolver = () => defaultKeyResolver;
|
|
343
|
+
|
|
344
|
+
class Test {
|
|
345
|
+
@UseCache({
|
|
346
|
+
ttl: 10000,
|
|
347
|
+
canCache: "no-nullish",
|
|
348
|
+
refreshThreshold: 1000
|
|
349
|
+
})
|
|
350
|
+
test(arg: string) {
|
|
351
|
+
return null;
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
const next = vi.fn().mockResolvedValue(null);
|
|
356
|
+
const context: any = {
|
|
357
|
+
target: Test,
|
|
358
|
+
propertyKey: "test",
|
|
359
|
+
args: ["value"],
|
|
360
|
+
options: {
|
|
361
|
+
ttl: 10000,
|
|
362
|
+
canCache: "no-nullish",
|
|
363
|
+
refreshThreshold: 1000
|
|
364
|
+
}
|
|
365
|
+
};
|
|
366
|
+
|
|
367
|
+
vi.spyOn(interceptor, "canRefreshInBackground").mockResolvedValue();
|
|
368
|
+
|
|
369
|
+
const result = await interceptor.cacheMethod(context, next);
|
|
370
|
+
|
|
371
|
+
await new Promise((resolve) => setTimeout(resolve, 100));
|
|
372
|
+
|
|
373
|
+
expect(interceptor.canRefreshInBackground).not.toHaveBeenCalled();
|
|
374
|
+
expect(cache.getCachedObject).toHaveBeenCalledWith("Test:test:value");
|
|
375
|
+
expect(cache.setCachedObject).not.toHaveBeenCalled();
|
|
376
|
+
expect(result).toEqual(null);
|
|
377
|
+
});
|
|
378
|
+
it("should catch and log error", async () => {
|
|
379
|
+
const {cache, interceptor} = await getInterceptorFixture();
|
|
380
|
+
cache.getCachedObject = vi.fn().mockResolvedValue({data: JSON.stringify({})});
|
|
381
|
+
cache.setCachedObject = vi.fn().mockResolvedValue("test");
|
|
382
|
+
cache.defaultKeyResolver = () => defaultKeyResolver;
|
|
383
|
+
|
|
384
|
+
class Test {
|
|
385
|
+
@UseCache({
|
|
386
|
+
ttl: 10000,
|
|
387
|
+
refreshThreshold: 1000
|
|
388
|
+
})
|
|
389
|
+
test(arg: string) {
|
|
390
|
+
return "";
|
|
391
|
+
}
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
const next = vi.fn().mockResolvedValue({
|
|
395
|
+
data: "data"
|
|
396
|
+
});
|
|
397
|
+
const context: any = {
|
|
398
|
+
target: Test,
|
|
399
|
+
propertyKey: "test",
|
|
400
|
+
args: ["value"],
|
|
401
|
+
options: {
|
|
402
|
+
ttl: 10000,
|
|
403
|
+
refreshThreshold: 1000
|
|
404
|
+
}
|
|
405
|
+
};
|
|
406
|
+
|
|
407
|
+
const error = new Error("error");
|
|
408
|
+
vi.spyOn(interceptor, "canRefreshInBackground").mockRejectedValue(error);
|
|
409
|
+
vi.spyOn((interceptor as any).logger, "error");
|
|
410
|
+
|
|
411
|
+
await interceptor.cacheMethod(context, next);
|
|
412
|
+
|
|
413
|
+
await new Promise((resolve) => setTimeout(resolve, 100));
|
|
414
|
+
|
|
415
|
+
expect((interceptor as any).logger.error).toHaveBeenCalledWith({
|
|
416
|
+
event: "CACHE_ERROR",
|
|
417
|
+
method: "cacheMethod",
|
|
418
|
+
class_name: "Test",
|
|
419
|
+
property_key: "test",
|
|
420
|
+
concerned_key: "Test:test:value",
|
|
421
|
+
error_description: "error",
|
|
422
|
+
stack: error.stack
|
|
423
|
+
});
|
|
424
|
+
});
|
|
425
|
+
});
|
|
426
|
+
describe("cacheResponse()", () => {
|
|
427
|
+
it("should return the cached response", async () => {
|
|
428
|
+
const cache = {
|
|
429
|
+
get: vi.fn().mockResolvedValue(false),
|
|
430
|
+
set: vi.fn().mockResolvedValue(false),
|
|
431
|
+
del: vi.fn().mockResolvedValue(true),
|
|
432
|
+
calculateTTL: vi.fn().mockReturnValue(10000),
|
|
433
|
+
getCachedObject: vi.fn().mockResolvedValue({
|
|
434
|
+
data: JSON.stringify({data: "data"})
|
|
435
|
+
}),
|
|
436
|
+
setCachedObject: vi.fn().mockResolvedValue("test"),
|
|
437
|
+
defaultKeyResolver: () => defaultKeyResolver
|
|
438
|
+
};
|
|
439
|
+
const interceptor = await PlatformTest.invoke<PlatformCacheInterceptor>(PlatformCacheInterceptor, [
|
|
440
|
+
{
|
|
441
|
+
token: PlatformCache,
|
|
442
|
+
use: cache
|
|
443
|
+
}
|
|
444
|
+
]);
|
|
445
|
+
|
|
446
|
+
class Test {
|
|
447
|
+
@UseCache({
|
|
448
|
+
ttl: 10000,
|
|
449
|
+
refreshThreshold: 1000
|
|
450
|
+
})
|
|
451
|
+
test(arg: string) {
|
|
452
|
+
return "";
|
|
453
|
+
}
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
const next = vi.fn();
|
|
457
|
+
const $ctx = {
|
|
458
|
+
request: {
|
|
459
|
+
method: "GET",
|
|
460
|
+
url: "/",
|
|
461
|
+
get: vi.fn()
|
|
462
|
+
},
|
|
463
|
+
response: {
|
|
464
|
+
getBody: vi.fn().mockReturnValue({
|
|
465
|
+
data: "data"
|
|
466
|
+
}),
|
|
467
|
+
setHeaders: vi.fn(),
|
|
468
|
+
onEnd: vi.fn()
|
|
469
|
+
}
|
|
470
|
+
};
|
|
471
|
+
const context: any = {
|
|
472
|
+
target: Test,
|
|
473
|
+
propertyKey: "test",
|
|
474
|
+
args: ["value", $ctx],
|
|
475
|
+
options: {
|
|
476
|
+
ttl: 10000,
|
|
477
|
+
refreshThreshold: 1000
|
|
478
|
+
}
|
|
479
|
+
};
|
|
480
|
+
|
|
481
|
+
vi.spyOn(interceptor, "canRefreshInBackground").mockResolvedValue();
|
|
482
|
+
vi.spyOn(interceptor as any, "sendResponse").mockResolvedValue(undefined);
|
|
483
|
+
|
|
484
|
+
const result = await interceptor.cacheResponse(context, next);
|
|
485
|
+
|
|
486
|
+
expect(cache.getCachedObject).toHaveBeenCalledWith('Test:test:value:{"request":{"method":"GET","url":"/"},"response":{}}');
|
|
487
|
+
expect(result).toEqual(undefined);
|
|
488
|
+
expect((interceptor as any).sendResponse).toHaveBeenCalledWith({data: '{"data":"data"}'}, $ctx);
|
|
489
|
+
expect($ctx.request.get).toHaveBeenCalledWith("cache-control");
|
|
490
|
+
|
|
491
|
+
/*
|
|
492
|
+
console.log('$ctx.response.onEnd.mock.calls[0]', $ctx.response.onEnd.mock.calls[0])
|
|
493
|
+
await $ctx.response.onEnd.mock.calls[0][0]()
|
|
494
|
+
|
|
495
|
+
expect(cache.setCachedObject).toHaveBeenCalledWith()
|
|
496
|
+
*/
|
|
497
|
+
});
|
|
498
|
+
it("should return the cached response (prefix)", async () => {
|
|
499
|
+
const cache = {
|
|
500
|
+
get: vi.fn().mockResolvedValue(false),
|
|
501
|
+
set: vi.fn().mockResolvedValue(false),
|
|
502
|
+
del: vi.fn().mockResolvedValue(true),
|
|
503
|
+
calculateTTL: vi.fn().mockReturnValue(10000),
|
|
504
|
+
getCachedObject: vi.fn().mockResolvedValue({
|
|
505
|
+
data: JSON.stringify({data: "data"})
|
|
506
|
+
}),
|
|
507
|
+
setCachedObject: vi.fn().mockResolvedValue("test"),
|
|
508
|
+
defaultKeyResolver: () => defaultKeyResolver
|
|
509
|
+
};
|
|
510
|
+
const interceptor = await PlatformTest.invoke<PlatformCacheInterceptor>(PlatformCacheInterceptor, [
|
|
511
|
+
{
|
|
512
|
+
token: PlatformCache,
|
|
513
|
+
use: cache
|
|
514
|
+
}
|
|
515
|
+
]);
|
|
516
|
+
|
|
517
|
+
class Test {
|
|
518
|
+
@UseCache({
|
|
519
|
+
ttl: 10000,
|
|
520
|
+
prefix: "TEST",
|
|
521
|
+
refreshThreshold: 1000
|
|
522
|
+
})
|
|
523
|
+
test(arg: string) {
|
|
524
|
+
return "";
|
|
525
|
+
}
|
|
526
|
+
}
|
|
527
|
+
|
|
528
|
+
const next = vi.fn();
|
|
529
|
+
const $ctx = {
|
|
530
|
+
request: {
|
|
531
|
+
method: "GET",
|
|
532
|
+
url: "/",
|
|
533
|
+
get: vi.fn()
|
|
534
|
+
},
|
|
535
|
+
response: {
|
|
536
|
+
getBody: vi.fn().mockReturnValue({
|
|
537
|
+
data: "data"
|
|
538
|
+
}),
|
|
539
|
+
setHeaders: vi.fn(),
|
|
540
|
+
onEnd: vi.fn()
|
|
541
|
+
}
|
|
542
|
+
};
|
|
543
|
+
const context: any = {
|
|
544
|
+
target: Test,
|
|
545
|
+
propertyKey: "test",
|
|
546
|
+
args: ["value", $ctx],
|
|
547
|
+
options: {
|
|
548
|
+
ttl: 10000,
|
|
549
|
+
prefix: "TEST",
|
|
550
|
+
refreshThreshold: 1000
|
|
551
|
+
}
|
|
552
|
+
};
|
|
553
|
+
|
|
554
|
+
vi.spyOn(interceptor, "canRefreshInBackground").mockResolvedValue();
|
|
555
|
+
vi.spyOn(interceptor as any, "sendResponse").mockResolvedValue(undefined);
|
|
556
|
+
|
|
557
|
+
const result = await interceptor.cacheResponse(context, next);
|
|
558
|
+
|
|
559
|
+
expect(cache.getCachedObject).toHaveBeenCalledWith('TEST:value:{"request":{"method":"GET","url":"/"},"response":{}}');
|
|
560
|
+
expect(result).toEqual(undefined);
|
|
561
|
+
expect((interceptor as any).sendResponse).toHaveBeenCalledWith({data: '{"data":"data"}'}, $ctx);
|
|
562
|
+
expect($ctx.request.get).toHaveBeenCalledWith("cache-control");
|
|
563
|
+
|
|
564
|
+
/*
|
|
565
|
+
console.log('$ctx.response.onEnd.mock.calls[0]', $ctx.response.onEnd.mock.calls[0])
|
|
566
|
+
await $ctx.response.onEnd.mock.calls[0][0]()
|
|
567
|
+
|
|
568
|
+
expect(cache.setCachedObject).toHaveBeenCalledWith()
|
|
569
|
+
*/
|
|
570
|
+
});
|
|
571
|
+
it("should call the method and set the cache", async () => {
|
|
572
|
+
const cache = {
|
|
573
|
+
get: vi.fn().mockResolvedValue(false),
|
|
574
|
+
set: vi.fn().mockResolvedValue(false),
|
|
575
|
+
del: vi.fn().mockResolvedValue(true),
|
|
576
|
+
calculateTTL: vi.fn().mockReturnValue(10000),
|
|
577
|
+
getCachedObject: vi.fn().mockResolvedValue(undefined),
|
|
578
|
+
setCachedObject: vi.fn().mockResolvedValue("test"),
|
|
579
|
+
defaultKeyResolver: () => defaultKeyResolver
|
|
580
|
+
};
|
|
581
|
+
const interceptor = await PlatformTest.invoke<PlatformCacheInterceptor>(PlatformCacheInterceptor, [
|
|
582
|
+
{
|
|
583
|
+
token: PlatformCache,
|
|
584
|
+
use: cache
|
|
585
|
+
}
|
|
586
|
+
]);
|
|
587
|
+
|
|
588
|
+
class Test {
|
|
589
|
+
@UseCache({
|
|
590
|
+
ttl: 10000,
|
|
591
|
+
refreshThreshold: 1000
|
|
592
|
+
})
|
|
593
|
+
test(arg: string) {
|
|
594
|
+
return "";
|
|
595
|
+
}
|
|
596
|
+
}
|
|
597
|
+
|
|
598
|
+
const next = vi.fn();
|
|
599
|
+
const $ctx = {
|
|
600
|
+
request: {
|
|
601
|
+
method: "GET",
|
|
602
|
+
url: "/",
|
|
603
|
+
get: vi.fn()
|
|
604
|
+
},
|
|
605
|
+
response: {
|
|
606
|
+
getBody: vi.fn().mockReturnValue({
|
|
607
|
+
data: "data"
|
|
608
|
+
}),
|
|
609
|
+
getHeaders: vi.fn().mockReturnValue({
|
|
610
|
+
"x-key": "key"
|
|
611
|
+
}),
|
|
612
|
+
setHeaders: vi.fn(),
|
|
613
|
+
onEnd: vi.fn()
|
|
614
|
+
}
|
|
615
|
+
};
|
|
616
|
+
const context: any = {
|
|
617
|
+
target: Test,
|
|
618
|
+
propertyKey: "test",
|
|
619
|
+
args: ["value", $ctx],
|
|
620
|
+
options: {
|
|
621
|
+
ttl: 10000,
|
|
622
|
+
refreshThreshold: 1000
|
|
623
|
+
}
|
|
624
|
+
};
|
|
625
|
+
|
|
626
|
+
vi.spyOn(interceptor, "canRefreshInBackground").mockResolvedValue();
|
|
627
|
+
vi.spyOn(interceptor as any, "sendResponse").mockResolvedValue(undefined);
|
|
628
|
+
|
|
629
|
+
const result = await interceptor.cacheResponse(context, next);
|
|
630
|
+
|
|
631
|
+
expect(cache.getCachedObject).toHaveBeenCalledWith('Test:test:value:{"request":{"method":"GET","url":"/"},"response":{}}');
|
|
632
|
+
expect(result).toEqual(undefined);
|
|
633
|
+
expect($ctx.response.setHeaders).toHaveBeenCalledWith({
|
|
634
|
+
"cache-control": `max-age=10000`
|
|
635
|
+
});
|
|
636
|
+
|
|
637
|
+
await $ctx.response.onEnd.mock.calls[0][0]();
|
|
638
|
+
|
|
639
|
+
expect(cache.setCachedObject).toHaveBeenCalledWith(
|
|
640
|
+
'Test:test:value:{"request":{"method":"GET","url":"/"},"response":{}}',
|
|
641
|
+
{data: "data"},
|
|
642
|
+
{
|
|
643
|
+
args: ["value", {request: {method: "GET", url: "/"}, response: {}}],
|
|
644
|
+
headers: {"x-key": "key"},
|
|
645
|
+
ttl: 10000
|
|
646
|
+
}
|
|
647
|
+
);
|
|
648
|
+
});
|
|
649
|
+
});
|
|
650
|
+
});
|