@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.
Files changed (45) hide show
  1. package/package.json +1 -1
  2. package/tests/auth/auth-decorators.test.ts +241 -0
  3. package/tests/auth/oauth2-service.test.ts +318 -0
  4. package/tests/cache/cache-decorators-extended.test.ts +272 -0
  5. package/tests/cache/cache-interceptors.test.ts +534 -0
  6. package/tests/cache/cache-service-proxy.test.ts +246 -0
  7. package/tests/cache/memory-cache-store.test.ts +155 -0
  8. package/tests/cache/redis-cache-store.test.ts +199 -0
  9. package/tests/config/config-center-integration.test.ts +334 -0
  10. package/tests/config/config-module-extended.test.ts +165 -0
  11. package/tests/controller/param-binder.test.ts +333 -0
  12. package/tests/error/error-handler.test.ts +166 -57
  13. package/tests/error/i18n-extended.test.ts +105 -0
  14. package/tests/events/event-listener-scanner.test.ts +114 -0
  15. package/tests/events/event-module.test.ts +133 -302
  16. package/tests/extensions/logger-module.test.ts +158 -0
  17. package/tests/files/file-storage.test.ts +136 -0
  18. package/tests/interceptor/base-interceptor.test.ts +605 -0
  19. package/tests/interceptor/builtin/cache-interceptor.test.ts +233 -86
  20. package/tests/interceptor/builtin/log-interceptor.test.ts +469 -0
  21. package/tests/interceptor/builtin/permission-interceptor.test.ts +219 -120
  22. package/tests/interceptor/interceptor-chain.test.ts +241 -189
  23. package/tests/interceptor/interceptor-metadata.test.ts +221 -0
  24. package/tests/microservice/circuit-breaker.test.ts +221 -0
  25. package/tests/microservice/service-client-decorators.test.ts +86 -0
  26. package/tests/microservice/service-client-interceptors.test.ts +274 -0
  27. package/tests/microservice/service-registry-decorators.test.ts +147 -0
  28. package/tests/microservice/tracer.test.ts +213 -0
  29. package/tests/microservice/tracing-collectors.test.ts +168 -0
  30. package/tests/middleware/builtin/middleware-builtin-extended.test.ts +237 -0
  31. package/tests/middleware/builtin/rate-limit.test.ts +257 -0
  32. package/tests/middleware/middleware-decorators.test.ts +222 -0
  33. package/tests/middleware/middleware-pipeline.test.ts +160 -0
  34. package/tests/queue/queue-decorators.test.ts +139 -0
  35. package/tests/queue/queue-service.test.ts +191 -0
  36. package/tests/request/body-parser-extended.test.ts +291 -0
  37. package/tests/request/request-wrapper.test.ts +319 -0
  38. package/tests/router/router-decorators.test.ts +260 -0
  39. package/tests/router/router-extended.test.ts +298 -0
  40. package/tests/security/guards/reflector.test.ts +188 -0
  41. package/tests/security/security-filter.test.ts +182 -0
  42. package/tests/security/security-module-extended.test.ts +133 -0
  43. package/tests/session/memory-session-store.test.ts +172 -0
  44. package/tests/session/session-decorators.test.ts +163 -0
  45. 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 { Application } from '../../../src/core/application';
4
- import { Controller, ControllerRegistry } from '../../../src/controller/controller';
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
- InterceptorRegistry,
12
- INTERCEPTOR_REGISTRY_TOKEN,
13
- } from '../../../src/interceptor';
14
- import { getTestPort } from '../../utils/test-port';
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 app: Application;
18
- let port: number;
19
- let interceptorRegistry: InterceptorRegistry;
20
- let callCount = 0;
90
+ let container: Container;
91
+ let interceptor: CacheInterceptor;
21
92
 
22
93
  beforeEach(() => {
23
- port = getTestPort();
24
- app = new Application({ port });
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(async () => {
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
- @Controller('/api/test')
45
- class TestController {
46
- @GET('/')
47
- @Cache({ ttl: 1000 })
48
- public getData() {
104
+ let callCount = 0;
105
+
106
+ class TestService {
107
+ @Cache({ ttl: 60000 })
108
+ public getData(): string {
49
109
  callCount++;
50
- return { data: 'cached', count: callCount };
110
+ return 'data';
51
111
  }
52
112
  }
53
113
 
54
- app.registerController(TestController);
55
- await app.listen();
114
+ const service = new TestService();
56
115
 
57
116
  // 第一次调用
58
- const response1 = await fetch(`http://localhost:${port}/api/test`);
59
- expect(response1.status).toBe(200);
60
- const data1 = await response1.json();
61
- expect(data1.count).toBe(1);
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 response2 = await fetch(`http://localhost:${port}/api/test`);
66
- expect(response2.status).toBe(200);
67
- const data2 = await response2.json();
68
- expect(data2.count).toBe(1); // 缓存的结果
69
- expect(callCount).toBe(1); // 方法没有被再次调用
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
- @Controller('/api/test')
74
- class TestController {
75
- @GET('/')
76
- @Cache({ ttl: 1000, key: 'custom-key' })
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
- app.registerController(TestController);
84
- await app.listen();
149
+ const service = new TestService();
85
150
 
86
- await fetch(`http://localhost:${port}/api/test`);
87
- await fetch(`http://localhost:${port}/api/test`);
151
+ await interceptor.execute(
152
+ service,
153
+ 'getData',
154
+ service.getData.bind(service),
155
+ [],
156
+ container,
157
+ );
88
158
 
89
- expect(callCount).toBe(1); // 应该只调用一次(使用缓存)
159
+ const stats = CacheInterceptor.getCacheStats();
160
+ expect(stats.keys).toContain('custom-key');
90
161
  });
91
162
 
92
- test('should expire cache after TTL', async () => {
93
- @Controller('/api/test')
94
- class TestController {
95
- @GET('/')
96
- @Cache({ ttl: 100 }) // 100ms TTL
97
- public getData() {
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 { data: 'cached', count: callCount };
195
+ return 'data';
100
196
  }
101
197
  }
102
198
 
103
- app.registerController(TestController);
104
- await app.listen();
199
+ const service = new TestService();
105
200
 
106
- // 第一次调用
107
- await fetch(`http://localhost:${port}/api/test`);
108
- expect(callCount).toBe(1);
201
+ await interceptor.execute(
202
+ service,
203
+ 'normalMethod',
204
+ service.normalMethod.bind(service),
205
+ [],
206
+ container,
207
+ );
109
208
 
110
- // 等待缓存过期
111
- await new Promise((resolve) => setTimeout(resolve, 150));
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 work without cache decorator', async () => {
119
- @Controller('/api/test')
120
- class TestController {
121
- @GET('/')
122
- public getData() {
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
- app.registerController(TestController);
129
- await app.listen();
228
+ const service = new TestService();
130
229
 
131
- await fetch(`http://localhost:${port}/api/test`);
132
- await fetch(`http://localhost:${port}/api/test`);
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
- expect(callCount).toBe(2); // 没有缓存,应该调用两次
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
+ });