@dangao/bun-server 1.7.1 → 1.8.1
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/README.md +129 -21
- package/dist/di/decorators.d.ts +37 -0
- package/dist/di/decorators.d.ts.map +1 -1
- package/dist/di/index.d.ts +1 -1
- package/dist/di/index.d.ts.map +1 -1
- package/dist/di/module-registry.d.ts +17 -0
- package/dist/di/module-registry.d.ts.map +1 -1
- package/dist/events/decorators.d.ts +52 -0
- package/dist/events/decorators.d.ts.map +1 -0
- package/dist/events/event-module.d.ts +97 -0
- package/dist/events/event-module.d.ts.map +1 -0
- package/dist/events/index.d.ts +5 -0
- package/dist/events/index.d.ts.map +1 -0
- package/dist/events/service.d.ts +76 -0
- package/dist/events/service.d.ts.map +1 -0
- package/dist/events/types.d.ts +184 -0
- package/dist/events/types.d.ts.map +1 -0
- package/dist/index.d.ts +5 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1511 -11
- package/dist/security/filter.d.ts +23 -0
- package/dist/security/filter.d.ts.map +1 -1
- package/dist/security/guards/builtin/auth-guard.d.ts +44 -0
- package/dist/security/guards/builtin/auth-guard.d.ts.map +1 -0
- package/dist/security/guards/builtin/index.d.ts +3 -0
- package/dist/security/guards/builtin/index.d.ts.map +1 -0
- package/dist/security/guards/builtin/roles-guard.d.ts +66 -0
- package/dist/security/guards/builtin/roles-guard.d.ts.map +1 -0
- package/dist/security/guards/decorators.d.ts +50 -0
- package/dist/security/guards/decorators.d.ts.map +1 -0
- package/dist/security/guards/execution-context.d.ts +56 -0
- package/dist/security/guards/execution-context.d.ts.map +1 -0
- package/dist/security/guards/guard-registry.d.ts +67 -0
- package/dist/security/guards/guard-registry.d.ts.map +1 -0
- package/dist/security/guards/index.d.ts +7 -0
- package/dist/security/guards/index.d.ts.map +1 -0
- package/dist/security/guards/reflector.d.ts +57 -0
- package/dist/security/guards/reflector.d.ts.map +1 -0
- package/dist/security/guards/types.d.ts +126 -0
- package/dist/security/guards/types.d.ts.map +1 -0
- package/dist/security/index.d.ts +1 -0
- package/dist/security/index.d.ts.map +1 -1
- package/dist/security/security-module.d.ts +20 -0
- package/dist/security/security-module.d.ts.map +1 -1
- package/dist/validation/class-validator.d.ts +108 -0
- package/dist/validation/class-validator.d.ts.map +1 -0
- package/dist/validation/custom-validator.d.ts +130 -0
- package/dist/validation/custom-validator.d.ts.map +1 -0
- package/dist/validation/errors.d.ts +22 -2
- package/dist/validation/errors.d.ts.map +1 -1
- package/dist/validation/index.d.ts +7 -1
- package/dist/validation/index.d.ts.map +1 -1
- package/dist/validation/rules/array.d.ts +33 -0
- package/dist/validation/rules/array.d.ts.map +1 -0
- package/dist/validation/rules/common.d.ts +90 -0
- package/dist/validation/rules/common.d.ts.map +1 -0
- package/dist/validation/rules/conditional.d.ts +30 -0
- package/dist/validation/rules/conditional.d.ts.map +1 -0
- package/dist/validation/rules/index.d.ts +5 -0
- package/dist/validation/rules/index.d.ts.map +1 -0
- package/dist/validation/rules/object.d.ts +30 -0
- package/dist/validation/rules/object.d.ts.map +1 -0
- package/dist/validation/types.d.ts +52 -1
- package/dist/validation/types.d.ts.map +1 -1
- package/docs/events.md +494 -0
- package/docs/guards.md +376 -0
- package/docs/guide.md +309 -1
- package/docs/request-lifecycle.md +444 -0
- package/docs/validation.md +407 -0
- package/docs/zh/events.md +494 -0
- package/docs/zh/guards.md +376 -0
- package/docs/zh/guide.md +309 -1
- package/docs/zh/request-lifecycle.md +444 -0
- package/docs/zh/validation.md +407 -0
- package/package.json +1 -1
- package/src/di/decorators.ts +46 -0
- package/src/di/index.ts +10 -1
- package/src/di/module-registry.ts +39 -0
- package/src/events/decorators.ts +103 -0
- package/src/events/event-module.ts +272 -0
- package/src/events/index.ts +32 -0
- package/src/events/service.ts +352 -0
- package/src/events/types.ts +223 -0
- package/src/index.ts +133 -1
- package/src/security/filter.ts +88 -8
- package/src/security/guards/builtin/auth-guard.ts +68 -0
- package/src/security/guards/builtin/index.ts +3 -0
- package/src/security/guards/builtin/roles-guard.ts +165 -0
- package/src/security/guards/decorators.ts +124 -0
- package/src/security/guards/execution-context.ts +152 -0
- package/src/security/guards/guard-registry.ts +164 -0
- package/src/security/guards/index.ts +7 -0
- package/src/security/guards/reflector.ts +99 -0
- package/src/security/guards/types.ts +144 -0
- package/src/security/index.ts +1 -0
- package/src/security/security-module.ts +72 -2
- package/src/validation/class-validator.ts +322 -0
- package/src/validation/custom-validator.ts +289 -0
- package/src/validation/errors.ts +50 -2
- package/src/validation/index.ts +103 -1
- package/src/validation/rules/array.ts +118 -0
- package/src/validation/rules/common.ts +286 -0
- package/src/validation/rules/conditional.ts +52 -0
- package/src/validation/rules/index.ts +51 -0
- package/src/validation/rules/object.ts +86 -0
- package/src/validation/types.ts +61 -1
- package/tests/auth/auth-decorators.test.ts +241 -0
- package/tests/auth/oauth2-service.test.ts +318 -0
- package/tests/cache/cache-decorators-extended.test.ts +272 -0
- package/tests/cache/cache-interceptors.test.ts +534 -0
- package/tests/cache/cache-service-proxy.test.ts +246 -0
- package/tests/cache/memory-cache-store.test.ts +155 -0
- package/tests/cache/redis-cache-store.test.ts +199 -0
- package/tests/config/config-center-integration.test.ts +334 -0
- package/tests/config/config-module-extended.test.ts +165 -0
- package/tests/controller/param-binder.test.ts +333 -0
- package/tests/di/global-module.test.ts +487 -0
- package/tests/error/error-handler.test.ts +166 -57
- package/tests/error/i18n-extended.test.ts +105 -0
- package/tests/events/event-decorators.test.ts +173 -0
- package/tests/events/event-emitter.test.ts +373 -0
- package/tests/events/event-listener-scanner.test.ts +114 -0
- package/tests/events/event-module.test.ts +204 -0
- package/tests/extensions/logger-module.test.ts +158 -0
- package/tests/files/file-storage.test.ts +136 -0
- package/tests/interceptor/base-interceptor.test.ts +605 -0
- package/tests/interceptor/builtin/cache-interceptor.test.ts +233 -86
- package/tests/interceptor/builtin/log-interceptor.test.ts +469 -0
- package/tests/interceptor/builtin/permission-interceptor.test.ts +219 -120
- package/tests/interceptor/interceptor-chain.test.ts +241 -189
- package/tests/interceptor/interceptor-metadata.test.ts +221 -0
- package/tests/microservice/circuit-breaker.test.ts +221 -0
- package/tests/microservice/service-client-decorators.test.ts +86 -0
- package/tests/microservice/service-client-interceptors.test.ts +274 -0
- package/tests/microservice/service-registry-decorators.test.ts +147 -0
- package/tests/microservice/tracer.test.ts +213 -0
- package/tests/microservice/tracing-collectors.test.ts +168 -0
- package/tests/middleware/builtin/middleware-builtin-extended.test.ts +237 -0
- package/tests/middleware/builtin/rate-limit.test.ts +257 -0
- package/tests/middleware/middleware-decorators.test.ts +222 -0
- package/tests/middleware/middleware-pipeline.test.ts +160 -0
- package/tests/queue/queue-decorators.test.ts +139 -0
- package/tests/queue/queue-service.test.ts +191 -0
- package/tests/request/body-parser-extended.test.ts +291 -0
- package/tests/request/request-wrapper.test.ts +319 -0
- package/tests/router/router-decorators.test.ts +260 -0
- package/tests/router/router-extended.test.ts +298 -0
- package/tests/security/guards/guards-integration.test.ts +371 -0
- package/tests/security/guards/guards.test.ts +775 -0
- package/tests/security/guards/reflector.test.ts +188 -0
- package/tests/security/security-filter.test.ts +182 -0
- package/tests/security/security-module-extended.test.ts +133 -0
- package/tests/security/security-module.test.ts +2 -2
- package/tests/session/memory-session-store.test.ts +172 -0
- package/tests/session/session-decorators.test.ts +163 -0
- package/tests/swagger/ui.test.ts +212 -0
- package/tests/validation/class-validator.test.ts +349 -0
- package/tests/validation/custom-validator.test.ts +335 -0
- package/tests/validation/rules.test.ts +543 -0
|
@@ -0,0 +1,534 @@
|
|
|
1
|
+
import { describe, expect, test, beforeEach } from 'bun:test';
|
|
2
|
+
import 'reflect-metadata';
|
|
3
|
+
|
|
4
|
+
import {
|
|
5
|
+
CacheableInterceptor,
|
|
6
|
+
CacheEvictInterceptor,
|
|
7
|
+
CachePutInterceptor,
|
|
8
|
+
} from '../../src/cache/interceptors';
|
|
9
|
+
import {
|
|
10
|
+
Cacheable,
|
|
11
|
+
CacheEvict,
|
|
12
|
+
CachePut,
|
|
13
|
+
} from '../../src/cache/decorators';
|
|
14
|
+
import { CacheService } from '../../src/cache/service';
|
|
15
|
+
import { MemoryCacheStore } from '../../src/cache/types';
|
|
16
|
+
import { CACHE_SERVICE_TOKEN } from '../../src/cache/types';
|
|
17
|
+
import { Container } from '../../src/di/container';
|
|
18
|
+
|
|
19
|
+
describe('CacheableInterceptor', () => {
|
|
20
|
+
let container: Container;
|
|
21
|
+
let cacheService: CacheService;
|
|
22
|
+
let interceptor: CacheableInterceptor;
|
|
23
|
+
|
|
24
|
+
beforeEach(() => {
|
|
25
|
+
container = new Container();
|
|
26
|
+
const store = new MemoryCacheStore();
|
|
27
|
+
cacheService = new CacheService(store);
|
|
28
|
+
container.registerInstance(CACHE_SERVICE_TOKEN, cacheService);
|
|
29
|
+
interceptor = new CacheableInterceptor();
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
test('should execute method and cache result', async () => {
|
|
33
|
+
let callCount = 0;
|
|
34
|
+
|
|
35
|
+
class TestService {
|
|
36
|
+
@Cacheable({ key: 'test:{0}' })
|
|
37
|
+
public getData(id: string): string {
|
|
38
|
+
callCount++;
|
|
39
|
+
return `data-${id}`;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
const service = new TestService();
|
|
44
|
+
|
|
45
|
+
// 第一次调用
|
|
46
|
+
const result1 = await interceptor.execute(
|
|
47
|
+
service,
|
|
48
|
+
'getData',
|
|
49
|
+
service.getData.bind(service),
|
|
50
|
+
['123'],
|
|
51
|
+
container,
|
|
52
|
+
);
|
|
53
|
+
|
|
54
|
+
expect(result1).toBe('data-123');
|
|
55
|
+
expect(callCount).toBe(1);
|
|
56
|
+
|
|
57
|
+
// 第二次调用应该从缓存返回
|
|
58
|
+
const result2 = await interceptor.execute(
|
|
59
|
+
service,
|
|
60
|
+
'getData',
|
|
61
|
+
service.getData.bind(service),
|
|
62
|
+
['123'],
|
|
63
|
+
container,
|
|
64
|
+
);
|
|
65
|
+
|
|
66
|
+
expect(result2).toBe('data-123');
|
|
67
|
+
expect(callCount).toBe(1); // 不应该增加
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
test('should generate default cache key when no key specified', async () => {
|
|
71
|
+
class TestService {
|
|
72
|
+
@Cacheable()
|
|
73
|
+
public getData(): string {
|
|
74
|
+
return 'data';
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
const service = new TestService();
|
|
79
|
+
|
|
80
|
+
const result = await interceptor.execute(
|
|
81
|
+
service,
|
|
82
|
+
'getData',
|
|
83
|
+
service.getData.bind(service),
|
|
84
|
+
[],
|
|
85
|
+
container,
|
|
86
|
+
);
|
|
87
|
+
|
|
88
|
+
expect(result).toBe('data');
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
test('should use keyPrefix in cache key', async () => {
|
|
92
|
+
class TestService {
|
|
93
|
+
@Cacheable({ keyPrefix: 'myprefix' })
|
|
94
|
+
public getData(): string {
|
|
95
|
+
return 'data';
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
const service = new TestService();
|
|
100
|
+
|
|
101
|
+
const result = await interceptor.execute(
|
|
102
|
+
service,
|
|
103
|
+
'getData',
|
|
104
|
+
service.getData.bind(service),
|
|
105
|
+
[],
|
|
106
|
+
container,
|
|
107
|
+
);
|
|
108
|
+
|
|
109
|
+
expect(result).toBe('data');
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
test('should skip caching when condition is false', async () => {
|
|
113
|
+
let callCount = 0;
|
|
114
|
+
|
|
115
|
+
class TestService {
|
|
116
|
+
@Cacheable({ key: 'test', condition: 'false' })
|
|
117
|
+
public getData(): string {
|
|
118
|
+
callCount++;
|
|
119
|
+
return 'data';
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
const service = new TestService();
|
|
124
|
+
|
|
125
|
+
await interceptor.execute(
|
|
126
|
+
service,
|
|
127
|
+
'getData',
|
|
128
|
+
service.getData.bind(service),
|
|
129
|
+
[],
|
|
130
|
+
container,
|
|
131
|
+
);
|
|
132
|
+
|
|
133
|
+
await interceptor.execute(
|
|
134
|
+
service,
|
|
135
|
+
'getData',
|
|
136
|
+
service.getData.bind(service),
|
|
137
|
+
[],
|
|
138
|
+
container,
|
|
139
|
+
);
|
|
140
|
+
|
|
141
|
+
// 每次都应该执行方法,因为 condition 为 false
|
|
142
|
+
expect(callCount).toBe(2);
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
test('should execute method without caching when no metadata', async () => {
|
|
146
|
+
class TestService {
|
|
147
|
+
public getData(): string {
|
|
148
|
+
return 'data';
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
const service = new TestService();
|
|
153
|
+
|
|
154
|
+
const result = await interceptor.execute(
|
|
155
|
+
service,
|
|
156
|
+
'getData',
|
|
157
|
+
service.getData.bind(service),
|
|
158
|
+
[],
|
|
159
|
+
container,
|
|
160
|
+
);
|
|
161
|
+
|
|
162
|
+
expect(result).toBe('data');
|
|
163
|
+
});
|
|
164
|
+
|
|
165
|
+
test('should execute method when cache service not registered', async () => {
|
|
166
|
+
const emptyContainer = new Container();
|
|
167
|
+
|
|
168
|
+
class TestService {
|
|
169
|
+
@Cacheable({ key: 'test' })
|
|
170
|
+
public getData(): string {
|
|
171
|
+
return 'data';
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
const service = new TestService();
|
|
176
|
+
|
|
177
|
+
// 应该正常执行,只是跳过缓存
|
|
178
|
+
const result = await interceptor.execute(
|
|
179
|
+
service,
|
|
180
|
+
'getData',
|
|
181
|
+
service.getData.bind(service),
|
|
182
|
+
[],
|
|
183
|
+
emptyContainer,
|
|
184
|
+
);
|
|
185
|
+
|
|
186
|
+
expect(result).toBe('data');
|
|
187
|
+
});
|
|
188
|
+
|
|
189
|
+
test('should resolve key template with positional arguments', async () => {
|
|
190
|
+
class TestService {
|
|
191
|
+
@Cacheable({ key: 'user:{0}:name:{1}' })
|
|
192
|
+
public getData(userId: string, name: string): string {
|
|
193
|
+
return `${userId}-${name}`;
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
const service = new TestService();
|
|
198
|
+
|
|
199
|
+
const result = await interceptor.execute(
|
|
200
|
+
service,
|
|
201
|
+
'getData',
|
|
202
|
+
service.getData.bind(service),
|
|
203
|
+
['123', 'alice'],
|
|
204
|
+
container,
|
|
205
|
+
);
|
|
206
|
+
|
|
207
|
+
expect(result).toBe('123-alice');
|
|
208
|
+
|
|
209
|
+
// 验证缓存
|
|
210
|
+
const cached = await cacheService.get('user:123:name:alice');
|
|
211
|
+
expect(cached).toBe('123-alice');
|
|
212
|
+
});
|
|
213
|
+
|
|
214
|
+
test('should handle TTL option', async () => {
|
|
215
|
+
class TestService {
|
|
216
|
+
@Cacheable({ key: 'test', ttl: 100 })
|
|
217
|
+
public getData(): string {
|
|
218
|
+
return 'data';
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
const service = new TestService();
|
|
223
|
+
|
|
224
|
+
const result = await interceptor.execute(
|
|
225
|
+
service,
|
|
226
|
+
'getData',
|
|
227
|
+
service.getData.bind(service),
|
|
228
|
+
[],
|
|
229
|
+
container,
|
|
230
|
+
);
|
|
231
|
+
|
|
232
|
+
expect(result).toBe('data');
|
|
233
|
+
// 立即检查应该存在
|
|
234
|
+
const cached = await cacheService.get('test');
|
|
235
|
+
expect(cached).toBe('data');
|
|
236
|
+
});
|
|
237
|
+
});
|
|
238
|
+
|
|
239
|
+
describe('CacheEvictInterceptor', () => {
|
|
240
|
+
let container: Container;
|
|
241
|
+
let cacheService: CacheService;
|
|
242
|
+
let interceptor: CacheEvictInterceptor;
|
|
243
|
+
|
|
244
|
+
beforeEach(() => {
|
|
245
|
+
container = new Container();
|
|
246
|
+
const store = new MemoryCacheStore();
|
|
247
|
+
cacheService = new CacheService(store);
|
|
248
|
+
container.registerInstance(CACHE_SERVICE_TOKEN, cacheService);
|
|
249
|
+
interceptor = new CacheEvictInterceptor();
|
|
250
|
+
});
|
|
251
|
+
|
|
252
|
+
test('should evict cache after method execution', async () => {
|
|
253
|
+
await cacheService.set('user:123', 'cached-value');
|
|
254
|
+
|
|
255
|
+
class TestService {
|
|
256
|
+
@CacheEvict({ key: 'user:{0}' })
|
|
257
|
+
public deleteUser(id: string): string {
|
|
258
|
+
return 'deleted';
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
const service = new TestService();
|
|
263
|
+
|
|
264
|
+
const result = await interceptor.execute(
|
|
265
|
+
service,
|
|
266
|
+
'deleteUser',
|
|
267
|
+
service.deleteUser.bind(service),
|
|
268
|
+
['123'],
|
|
269
|
+
container,
|
|
270
|
+
);
|
|
271
|
+
|
|
272
|
+
expect(result).toBe('deleted');
|
|
273
|
+
});
|
|
274
|
+
|
|
275
|
+
test('should handle beforeInvocation option', async () => {
|
|
276
|
+
await cacheService.set('user:123', 'cached-value');
|
|
277
|
+
|
|
278
|
+
class TestService {
|
|
279
|
+
@CacheEvict({ key: 'user:{0}', beforeInvocation: true })
|
|
280
|
+
public deleteUser(id: string): string {
|
|
281
|
+
return 'deleted';
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
const service = new TestService();
|
|
286
|
+
|
|
287
|
+
const result = await interceptor.execute(
|
|
288
|
+
service,
|
|
289
|
+
'deleteUser',
|
|
290
|
+
service.deleteUser.bind(service),
|
|
291
|
+
['123'],
|
|
292
|
+
container,
|
|
293
|
+
);
|
|
294
|
+
|
|
295
|
+
expect(result).toBe('deleted');
|
|
296
|
+
});
|
|
297
|
+
|
|
298
|
+
test('should handle allEntries option', async () => {
|
|
299
|
+
await cacheService.set('key1', 'value1');
|
|
300
|
+
await cacheService.set('key2', 'value2');
|
|
301
|
+
|
|
302
|
+
class TestService {
|
|
303
|
+
@CacheEvict({ allEntries: true })
|
|
304
|
+
public clearAll(): string {
|
|
305
|
+
return 'cleared';
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
const service = new TestService();
|
|
310
|
+
|
|
311
|
+
const result = await interceptor.execute(
|
|
312
|
+
service,
|
|
313
|
+
'clearAll',
|
|
314
|
+
service.clearAll.bind(service),
|
|
315
|
+
[],
|
|
316
|
+
container,
|
|
317
|
+
);
|
|
318
|
+
|
|
319
|
+
expect(result).toBe('cleared');
|
|
320
|
+
});
|
|
321
|
+
|
|
322
|
+
test('should execute method without eviction when no metadata', async () => {
|
|
323
|
+
await cacheService.set('test', 'value');
|
|
324
|
+
|
|
325
|
+
class TestService {
|
|
326
|
+
public normalMethod(): string {
|
|
327
|
+
return 'result';
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
const service = new TestService();
|
|
332
|
+
|
|
333
|
+
const result = await interceptor.execute(
|
|
334
|
+
service,
|
|
335
|
+
'normalMethod',
|
|
336
|
+
service.normalMethod.bind(service),
|
|
337
|
+
[],
|
|
338
|
+
container,
|
|
339
|
+
);
|
|
340
|
+
|
|
341
|
+
expect(result).toBe('result');
|
|
342
|
+
// 缓存应该仍然存在
|
|
343
|
+
const cached = await cacheService.get('test');
|
|
344
|
+
expect(cached).toBe('value');
|
|
345
|
+
});
|
|
346
|
+
|
|
347
|
+
test('should execute method when cache service not registered', async () => {
|
|
348
|
+
const emptyContainer = new Container();
|
|
349
|
+
|
|
350
|
+
class TestService {
|
|
351
|
+
@CacheEvict({ key: 'test' })
|
|
352
|
+
public deleteData(): string {
|
|
353
|
+
return 'deleted';
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
const service = new TestService();
|
|
358
|
+
|
|
359
|
+
const result = await interceptor.execute(
|
|
360
|
+
service,
|
|
361
|
+
'deleteData',
|
|
362
|
+
service.deleteData.bind(service),
|
|
363
|
+
[],
|
|
364
|
+
emptyContainer,
|
|
365
|
+
);
|
|
366
|
+
|
|
367
|
+
expect(result).toBe('deleted');
|
|
368
|
+
});
|
|
369
|
+
});
|
|
370
|
+
|
|
371
|
+
describe('CachePutInterceptor', () => {
|
|
372
|
+
let container: Container;
|
|
373
|
+
let cacheService: CacheService;
|
|
374
|
+
let interceptor: CachePutInterceptor;
|
|
375
|
+
|
|
376
|
+
beforeEach(() => {
|
|
377
|
+
container = new Container();
|
|
378
|
+
const store = new MemoryCacheStore();
|
|
379
|
+
cacheService = new CacheService(store);
|
|
380
|
+
container.registerInstance(CACHE_SERVICE_TOKEN, cacheService);
|
|
381
|
+
interceptor = new CachePutInterceptor();
|
|
382
|
+
});
|
|
383
|
+
|
|
384
|
+
test('should always execute method and update cache', async () => {
|
|
385
|
+
let callCount = 0;
|
|
386
|
+
|
|
387
|
+
class TestService {
|
|
388
|
+
@CachePut({ key: 'user:{0}' })
|
|
389
|
+
public updateUser(id: string): string {
|
|
390
|
+
callCount++;
|
|
391
|
+
return `updated-${id}-${callCount}`;
|
|
392
|
+
}
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
const service = new TestService();
|
|
396
|
+
|
|
397
|
+
// 第一次调用
|
|
398
|
+
const result1 = await interceptor.execute(
|
|
399
|
+
service,
|
|
400
|
+
'updateUser',
|
|
401
|
+
service.updateUser.bind(service),
|
|
402
|
+
['123'],
|
|
403
|
+
container,
|
|
404
|
+
);
|
|
405
|
+
|
|
406
|
+
expect(result1).toBe('updated-123-1');
|
|
407
|
+
expect(callCount).toBe(1);
|
|
408
|
+
|
|
409
|
+
// 第二次调用也应该执行方法(CachePut 总是执行)
|
|
410
|
+
const result2 = await interceptor.execute(
|
|
411
|
+
service,
|
|
412
|
+
'updateUser',
|
|
413
|
+
service.updateUser.bind(service),
|
|
414
|
+
['123'],
|
|
415
|
+
container,
|
|
416
|
+
);
|
|
417
|
+
|
|
418
|
+
expect(result2).toBe('updated-123-2');
|
|
419
|
+
expect(callCount).toBe(2);
|
|
420
|
+
|
|
421
|
+
// 缓存应该有最新值
|
|
422
|
+
const cached = await cacheService.get('user:123');
|
|
423
|
+
expect(cached).toBe('updated-123-2');
|
|
424
|
+
});
|
|
425
|
+
|
|
426
|
+
test('should skip cache update when condition is false', async () => {
|
|
427
|
+
class TestService {
|
|
428
|
+
@CachePut({ key: 'test', condition: 'false' })
|
|
429
|
+
public updateData(): string {
|
|
430
|
+
return 'data';
|
|
431
|
+
}
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
const service = new TestService();
|
|
435
|
+
|
|
436
|
+
const result = await interceptor.execute(
|
|
437
|
+
service,
|
|
438
|
+
'updateData',
|
|
439
|
+
service.updateData.bind(service),
|
|
440
|
+
[],
|
|
441
|
+
container,
|
|
442
|
+
);
|
|
443
|
+
|
|
444
|
+
expect(result).toBe('data');
|
|
445
|
+
});
|
|
446
|
+
|
|
447
|
+
test('should execute method without cache update when no metadata', async () => {
|
|
448
|
+
class TestService {
|
|
449
|
+
public normalMethod(): string {
|
|
450
|
+
return 'result';
|
|
451
|
+
}
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
const service = new TestService();
|
|
455
|
+
|
|
456
|
+
const result = await interceptor.execute(
|
|
457
|
+
service,
|
|
458
|
+
'normalMethod',
|
|
459
|
+
service.normalMethod.bind(service),
|
|
460
|
+
[],
|
|
461
|
+
container,
|
|
462
|
+
);
|
|
463
|
+
|
|
464
|
+
expect(result).toBe('result');
|
|
465
|
+
});
|
|
466
|
+
|
|
467
|
+
test('should execute method when cache service not registered', async () => {
|
|
468
|
+
const emptyContainer = new Container();
|
|
469
|
+
|
|
470
|
+
class TestService {
|
|
471
|
+
@CachePut({ key: 'test' })
|
|
472
|
+
public updateData(): string {
|
|
473
|
+
return 'updated';
|
|
474
|
+
}
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
const service = new TestService();
|
|
478
|
+
|
|
479
|
+
const result = await interceptor.execute(
|
|
480
|
+
service,
|
|
481
|
+
'updateData',
|
|
482
|
+
service.updateData.bind(service),
|
|
483
|
+
[],
|
|
484
|
+
emptyContainer,
|
|
485
|
+
);
|
|
486
|
+
|
|
487
|
+
expect(result).toBe('updated');
|
|
488
|
+
});
|
|
489
|
+
|
|
490
|
+
test('should handle TTL option', async () => {
|
|
491
|
+
class TestService {
|
|
492
|
+
@CachePut({ key: 'test', ttl: 100 })
|
|
493
|
+
public updateData(): string {
|
|
494
|
+
return 'data';
|
|
495
|
+
}
|
|
496
|
+
}
|
|
497
|
+
|
|
498
|
+
const service = new TestService();
|
|
499
|
+
|
|
500
|
+
const result = await interceptor.execute(
|
|
501
|
+
service,
|
|
502
|
+
'updateData',
|
|
503
|
+
service.updateData.bind(service),
|
|
504
|
+
[],
|
|
505
|
+
container,
|
|
506
|
+
);
|
|
507
|
+
|
|
508
|
+
expect(result).toBe('data');
|
|
509
|
+
// 立即检查应该存在
|
|
510
|
+
const cached = await cacheService.get('test');
|
|
511
|
+
expect(cached).toBe('data');
|
|
512
|
+
});
|
|
513
|
+
|
|
514
|
+
test('should generate default cache key', async () => {
|
|
515
|
+
class TestService {
|
|
516
|
+
@CachePut()
|
|
517
|
+
public updateData(): string {
|
|
518
|
+
return 'data';
|
|
519
|
+
}
|
|
520
|
+
}
|
|
521
|
+
|
|
522
|
+
const service = new TestService();
|
|
523
|
+
|
|
524
|
+
const result = await interceptor.execute(
|
|
525
|
+
service,
|
|
526
|
+
'updateData',
|
|
527
|
+
service.updateData.bind(service),
|
|
528
|
+
[],
|
|
529
|
+
container,
|
|
530
|
+
);
|
|
531
|
+
|
|
532
|
+
expect(result).toBe('data');
|
|
533
|
+
});
|
|
534
|
+
});
|