@dangao/bun-server 1.8.0 → 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/package.json +1 -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/error/error-handler.test.ts +166 -57
- package/tests/error/i18n-extended.test.ts +105 -0
- package/tests/events/event-listener-scanner.test.ts +114 -0
- package/tests/events/event-module.test.ts +133 -302
- 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/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/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
|
@@ -1,137 +1,284 @@
|
|
|
1
|
-
import 'reflect-metadata';
|
|
2
1
|
import { describe, expect, test, beforeEach, afterEach } from 'bun:test';
|
|
3
|
-
import
|
|
4
|
-
|
|
5
|
-
import { GET } from '../../../src/router/decorators';
|
|
6
|
-
import { RouteRegistry } from '../../../src/router/registry';
|
|
2
|
+
import 'reflect-metadata';
|
|
3
|
+
|
|
7
4
|
import {
|
|
8
5
|
Cache,
|
|
6
|
+
getCacheMetadata,
|
|
9
7
|
CacheInterceptor,
|
|
10
8
|
CACHE_METADATA_KEY,
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
} from '../../../src/
|
|
14
|
-
|
|
9
|
+
type CacheOptions,
|
|
10
|
+
} from '../../../src/interceptor/builtin/cache-interceptor';
|
|
11
|
+
import { Container } from '../../../src/di/container';
|
|
12
|
+
|
|
13
|
+
describe('Cache Decorator', () => {
|
|
14
|
+
test('should set cache metadata with default ttl', () => {
|
|
15
|
+
class TestService {
|
|
16
|
+
@Cache()
|
|
17
|
+
public getData(): string {
|
|
18
|
+
return 'data';
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
const metadata = getCacheMetadata(TestService.prototype, 'getData');
|
|
23
|
+
expect(metadata).toBeDefined();
|
|
24
|
+
expect(metadata?.ttl).toBe(60000);
|
|
25
|
+
expect(metadata?.key).toBeUndefined();
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
test('should set cache metadata with custom ttl', () => {
|
|
29
|
+
class TestService {
|
|
30
|
+
@Cache({ ttl: 30000 })
|
|
31
|
+
public getData(): string {
|
|
32
|
+
return 'data';
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const metadata = getCacheMetadata(TestService.prototype, 'getData');
|
|
37
|
+
expect(metadata?.ttl).toBe(30000);
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
test('should set cache metadata with custom key', () => {
|
|
41
|
+
class TestService {
|
|
42
|
+
@Cache({ key: 'my-custom-key' })
|
|
43
|
+
public getData(): string {
|
|
44
|
+
return 'data';
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
const metadata = getCacheMetadata(TestService.prototype, 'getData');
|
|
49
|
+
expect(metadata?.key).toBe('my-custom-key');
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
test('should set cache metadata with all options', () => {
|
|
53
|
+
class TestService {
|
|
54
|
+
@Cache({ ttl: 120000, key: 'full-options' })
|
|
55
|
+
public getData(): string {
|
|
56
|
+
return 'data';
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
const metadata = getCacheMetadata(TestService.prototype, 'getData');
|
|
61
|
+
expect(metadata?.ttl).toBe(120000);
|
|
62
|
+
expect(metadata?.key).toBe('full-options');
|
|
63
|
+
});
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
describe('getCacheMetadata', () => {
|
|
67
|
+
test('should return undefined for non-decorated method', () => {
|
|
68
|
+
class TestService {
|
|
69
|
+
public normalMethod(): string {
|
|
70
|
+
return 'data';
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
const metadata = getCacheMetadata(TestService.prototype, 'normalMethod');
|
|
75
|
+
expect(metadata).toBeUndefined();
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
test('should return undefined for null target', () => {
|
|
79
|
+
const metadata = getCacheMetadata(null, 'method');
|
|
80
|
+
expect(metadata).toBeUndefined();
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
test('should return undefined for non-object target', () => {
|
|
84
|
+
const metadata = getCacheMetadata('string', 'method');
|
|
85
|
+
expect(metadata).toBeUndefined();
|
|
86
|
+
});
|
|
87
|
+
});
|
|
15
88
|
|
|
16
89
|
describe('CacheInterceptor', () => {
|
|
17
|
-
let
|
|
18
|
-
let
|
|
19
|
-
let interceptorRegistry: InterceptorRegistry;
|
|
20
|
-
let callCount = 0;
|
|
90
|
+
let container: Container;
|
|
91
|
+
let interceptor: CacheInterceptor;
|
|
21
92
|
|
|
22
93
|
beforeEach(() => {
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
interceptorRegistry = app.getContainer().resolve<InterceptorRegistry>(
|
|
26
|
-
INTERCEPTOR_REGISTRY_TOKEN,
|
|
27
|
-
);
|
|
28
|
-
interceptorRegistry.register(CACHE_METADATA_KEY, new CacheInterceptor());
|
|
29
|
-
callCount = 0;
|
|
94
|
+
container = new Container();
|
|
95
|
+
interceptor = new CacheInterceptor();
|
|
30
96
|
CacheInterceptor.clearCache();
|
|
31
97
|
});
|
|
32
98
|
|
|
33
|
-
afterEach(
|
|
34
|
-
if (app) {
|
|
35
|
-
await app.stop();
|
|
36
|
-
}
|
|
37
|
-
RouteRegistry.getInstance().clear();
|
|
38
|
-
ControllerRegistry.getInstance().clear();
|
|
39
|
-
interceptorRegistry.clear();
|
|
99
|
+
afterEach(() => {
|
|
40
100
|
CacheInterceptor.clearCache();
|
|
41
101
|
});
|
|
42
102
|
|
|
43
103
|
test('should cache method result', async () => {
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
@Cache({ ttl:
|
|
48
|
-
public getData() {
|
|
104
|
+
let callCount = 0;
|
|
105
|
+
|
|
106
|
+
class TestService {
|
|
107
|
+
@Cache({ ttl: 60000 })
|
|
108
|
+
public getData(): string {
|
|
49
109
|
callCount++;
|
|
50
|
-
return
|
|
110
|
+
return 'data';
|
|
51
111
|
}
|
|
52
112
|
}
|
|
53
113
|
|
|
54
|
-
|
|
55
|
-
await app.listen();
|
|
114
|
+
const service = new TestService();
|
|
56
115
|
|
|
57
116
|
// 第一次调用
|
|
58
|
-
const
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
117
|
+
const result1 = await interceptor.execute(
|
|
118
|
+
service,
|
|
119
|
+
'getData',
|
|
120
|
+
service.getData.bind(service),
|
|
121
|
+
[],
|
|
122
|
+
container,
|
|
123
|
+
);
|
|
124
|
+
|
|
125
|
+
expect(result1).toBe('data');
|
|
62
126
|
expect(callCount).toBe(1);
|
|
63
127
|
|
|
64
|
-
//
|
|
65
|
-
const
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
128
|
+
// 第二次调用应该从缓存返回
|
|
129
|
+
const result2 = await interceptor.execute(
|
|
130
|
+
service,
|
|
131
|
+
'getData',
|
|
132
|
+
service.getData.bind(service),
|
|
133
|
+
[],
|
|
134
|
+
container,
|
|
135
|
+
);
|
|
136
|
+
|
|
137
|
+
expect(result2).toBe('data');
|
|
138
|
+
expect(callCount).toBe(1);
|
|
70
139
|
});
|
|
71
140
|
|
|
72
141
|
test('should use custom cache key', async () => {
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
public getData() {
|
|
78
|
-
callCount++;
|
|
79
|
-
return { data: 'cached' };
|
|
142
|
+
class TestService {
|
|
143
|
+
@Cache({ key: 'custom-key' })
|
|
144
|
+
public getData(): string {
|
|
145
|
+
return 'data';
|
|
80
146
|
}
|
|
81
147
|
}
|
|
82
148
|
|
|
83
|
-
|
|
84
|
-
await app.listen();
|
|
149
|
+
const service = new TestService();
|
|
85
150
|
|
|
86
|
-
await
|
|
87
|
-
|
|
151
|
+
await interceptor.execute(
|
|
152
|
+
service,
|
|
153
|
+
'getData',
|
|
154
|
+
service.getData.bind(service),
|
|
155
|
+
[],
|
|
156
|
+
container,
|
|
157
|
+
);
|
|
88
158
|
|
|
89
|
-
|
|
159
|
+
const stats = CacheInterceptor.getCacheStats();
|
|
160
|
+
expect(stats.keys).toContain('custom-key');
|
|
90
161
|
});
|
|
91
162
|
|
|
92
|
-
test('should
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
163
|
+
test('should generate cache key from class, method and args', async () => {
|
|
164
|
+
class TestService {
|
|
165
|
+
@Cache()
|
|
166
|
+
public getData(id: string): string {
|
|
167
|
+
return `data-${id}`;
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
const service = new TestService();
|
|
172
|
+
|
|
173
|
+
await interceptor.execute(
|
|
174
|
+
service,
|
|
175
|
+
'getData',
|
|
176
|
+
service.getData.bind(service),
|
|
177
|
+
['123'],
|
|
178
|
+
container,
|
|
179
|
+
);
|
|
180
|
+
|
|
181
|
+
const stats = CacheInterceptor.getCacheStats();
|
|
182
|
+
expect(stats.size).toBe(1);
|
|
183
|
+
// 键应该包含类名、方法名和参数
|
|
184
|
+
expect(stats.keys[0]).toContain('TestService');
|
|
185
|
+
expect(stats.keys[0]).toContain('getData');
|
|
186
|
+
expect(stats.keys[0]).toContain('123');
|
|
187
|
+
});
|
|
188
|
+
|
|
189
|
+
test('should execute method without caching when no metadata', async () => {
|
|
190
|
+
let callCount = 0;
|
|
191
|
+
|
|
192
|
+
class TestService {
|
|
193
|
+
public normalMethod(): string {
|
|
98
194
|
callCount++;
|
|
99
|
-
return
|
|
195
|
+
return 'data';
|
|
100
196
|
}
|
|
101
197
|
}
|
|
102
198
|
|
|
103
|
-
|
|
104
|
-
await app.listen();
|
|
199
|
+
const service = new TestService();
|
|
105
200
|
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
201
|
+
await interceptor.execute(
|
|
202
|
+
service,
|
|
203
|
+
'normalMethod',
|
|
204
|
+
service.normalMethod.bind(service),
|
|
205
|
+
[],
|
|
206
|
+
container,
|
|
207
|
+
);
|
|
109
208
|
|
|
110
|
-
|
|
111
|
-
|
|
209
|
+
await interceptor.execute(
|
|
210
|
+
service,
|
|
211
|
+
'normalMethod',
|
|
212
|
+
service.normalMethod.bind(service),
|
|
213
|
+
[],
|
|
214
|
+
container,
|
|
215
|
+
);
|
|
112
216
|
|
|
113
|
-
// 第二次调用(缓存已过期)
|
|
114
|
-
await fetch(`http://localhost:${port}/api/test`);
|
|
115
217
|
expect(callCount).toBe(2);
|
|
116
218
|
});
|
|
117
219
|
|
|
118
|
-
test('should
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
callCount++;
|
|
124
|
-
return { data: 'no-cache' };
|
|
220
|
+
test('should clear all cache', async () => {
|
|
221
|
+
class TestService {
|
|
222
|
+
@Cache()
|
|
223
|
+
public getData(): string {
|
|
224
|
+
return 'data';
|
|
125
225
|
}
|
|
126
226
|
}
|
|
127
227
|
|
|
128
|
-
|
|
129
|
-
await app.listen();
|
|
228
|
+
const service = new TestService();
|
|
130
229
|
|
|
131
|
-
await
|
|
132
|
-
|
|
230
|
+
await interceptor.execute(
|
|
231
|
+
service,
|
|
232
|
+
'getData',
|
|
233
|
+
service.getData.bind(service),
|
|
234
|
+
[],
|
|
235
|
+
container,
|
|
236
|
+
);
|
|
237
|
+
|
|
238
|
+
expect(CacheInterceptor.getCacheStats().size).toBe(1);
|
|
133
239
|
|
|
134
|
-
|
|
240
|
+
CacheInterceptor.clearCache();
|
|
241
|
+
|
|
242
|
+
expect(CacheInterceptor.getCacheStats().size).toBe(0);
|
|
135
243
|
});
|
|
136
|
-
});
|
|
137
244
|
|
|
245
|
+
test('should clear specific cache key', async () => {
|
|
246
|
+
class TestService {
|
|
247
|
+
@Cache({ key: 'key1' })
|
|
248
|
+
public getData1(): string {
|
|
249
|
+
return 'data1';
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
@Cache({ key: 'key2' })
|
|
253
|
+
public getData2(): string {
|
|
254
|
+
return 'data2';
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
const service = new TestService();
|
|
259
|
+
|
|
260
|
+
await interceptor.execute(
|
|
261
|
+
service,
|
|
262
|
+
'getData1',
|
|
263
|
+
service.getData1.bind(service),
|
|
264
|
+
[],
|
|
265
|
+
container,
|
|
266
|
+
);
|
|
267
|
+
|
|
268
|
+
await interceptor.execute(
|
|
269
|
+
service,
|
|
270
|
+
'getData2',
|
|
271
|
+
service.getData2.bind(service),
|
|
272
|
+
[],
|
|
273
|
+
container,
|
|
274
|
+
);
|
|
275
|
+
|
|
276
|
+
expect(CacheInterceptor.getCacheStats().size).toBe(2);
|
|
277
|
+
|
|
278
|
+
CacheInterceptor.clearCacheKey('key1');
|
|
279
|
+
|
|
280
|
+
const stats = CacheInterceptor.getCacheStats();
|
|
281
|
+
expect(stats.size).toBe(1);
|
|
282
|
+
expect(stats.keys).toContain('key2');
|
|
283
|
+
});
|
|
284
|
+
});
|