@dangao/bun-server 1.0.0 → 1.0.3

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 (200) hide show
  1. package/package.json +4 -2
  2. package/readme.md +163 -2
  3. package/src/auth/controller.ts +148 -0
  4. package/src/auth/decorators.ts +81 -0
  5. package/src/auth/index.ts +12 -0
  6. package/src/auth/jwt.ts +169 -0
  7. package/src/auth/oauth2.ts +244 -0
  8. package/src/auth/types.ts +248 -0
  9. package/src/cache/cache-module.ts +67 -0
  10. package/src/cache/decorators.ts +202 -0
  11. package/src/cache/index.ts +27 -0
  12. package/src/cache/service.ts +151 -0
  13. package/src/cache/types.ts +420 -0
  14. package/src/config/config-module.ts +76 -0
  15. package/src/config/index.ts +8 -0
  16. package/src/config/service.ts +93 -0
  17. package/src/config/types.ts +27 -0
  18. package/src/controller/controller.ts +251 -0
  19. package/src/controller/decorators.ts +84 -0
  20. package/src/controller/index.ts +7 -0
  21. package/src/controller/metadata.ts +27 -0
  22. package/src/controller/param-binder.ts +157 -0
  23. package/src/core/application.ts +233 -0
  24. package/src/core/context.ts +228 -0
  25. package/src/core/index.ts +4 -0
  26. package/src/core/server.ts +128 -0
  27. package/src/core/types.ts +2 -0
  28. package/src/database/connection-manager.ts +239 -0
  29. package/src/database/connection-pool.ts +322 -0
  30. package/src/database/database-extension.ts +62 -0
  31. package/src/database/database-module.ts +115 -0
  32. package/src/database/health-indicator.ts +51 -0
  33. package/src/database/index.ts +47 -0
  34. package/src/database/orm/decorators.ts +155 -0
  35. package/src/database/orm/drizzle-repository.ts +39 -0
  36. package/src/database/orm/index.ts +23 -0
  37. package/src/database/orm/repository-decorator.ts +39 -0
  38. package/src/database/orm/repository.ts +103 -0
  39. package/src/database/orm/service.ts +49 -0
  40. package/src/database/orm/transaction-decorator.ts +45 -0
  41. package/src/database/orm/transaction-interceptor.ts +243 -0
  42. package/src/database/orm/transaction-manager.ts +276 -0
  43. package/src/database/orm/transaction-types.ts +140 -0
  44. package/src/database/orm/types.ts +99 -0
  45. package/src/database/service.ts +221 -0
  46. package/src/database/types.ts +171 -0
  47. package/src/di/container.ts +398 -0
  48. package/src/di/decorators.ts +228 -0
  49. package/src/di/index.ts +4 -0
  50. package/src/di/module-registry.ts +188 -0
  51. package/src/di/module.ts +65 -0
  52. package/src/di/types.ts +67 -0
  53. package/src/error/error-codes.ts +222 -0
  54. package/src/error/filter.ts +43 -0
  55. package/src/error/handler.ts +66 -0
  56. package/src/error/http-exception.ts +115 -0
  57. package/src/error/i18n.ts +217 -0
  58. package/src/error/index.ts +16 -0
  59. package/src/extensions/index.ts +5 -0
  60. package/src/extensions/logger-extension.ts +31 -0
  61. package/src/extensions/logger-module.ts +69 -0
  62. package/src/extensions/types.ts +14 -0
  63. package/src/files/index.ts +5 -0
  64. package/src/files/static-middleware.ts +53 -0
  65. package/src/files/storage.ts +67 -0
  66. package/src/files/types.ts +33 -0
  67. package/src/files/upload-middleware.ts +45 -0
  68. package/src/health/controller.ts +76 -0
  69. package/src/health/health-module.ts +51 -0
  70. package/src/health/index.ts +12 -0
  71. package/src/health/types.ts +28 -0
  72. package/src/index.ts +270 -0
  73. package/src/metrics/collector.ts +209 -0
  74. package/src/metrics/controller.ts +40 -0
  75. package/src/metrics/index.ts +15 -0
  76. package/src/metrics/metrics-module.ts +58 -0
  77. package/src/metrics/middleware.ts +46 -0
  78. package/src/metrics/prometheus.ts +79 -0
  79. package/src/metrics/types.ts +103 -0
  80. package/src/middleware/builtin/cors.ts +60 -0
  81. package/src/middleware/builtin/error-handler.ts +90 -0
  82. package/src/middleware/builtin/file-upload.ts +42 -0
  83. package/src/middleware/builtin/index.ts +14 -0
  84. package/src/middleware/builtin/logger.ts +91 -0
  85. package/src/middleware/builtin/rate-limit.ts +252 -0
  86. package/src/middleware/builtin/static-file.ts +88 -0
  87. package/src/middleware/decorators.ts +91 -0
  88. package/src/middleware/index.ts +11 -0
  89. package/src/middleware/middleware.ts +13 -0
  90. package/src/middleware/pipeline.ts +93 -0
  91. package/src/queue/decorators.ts +110 -0
  92. package/src/queue/index.ts +26 -0
  93. package/src/queue/queue-module.ts +64 -0
  94. package/src/queue/service.ts +302 -0
  95. package/src/queue/types.ts +341 -0
  96. package/src/request/body-parser.ts +133 -0
  97. package/src/request/file-handler.ts +46 -0
  98. package/src/request/index.ts +5 -0
  99. package/src/request/request.ts +107 -0
  100. package/src/request/response.ts +150 -0
  101. package/src/router/decorators.ts +122 -0
  102. package/src/router/index.ts +6 -0
  103. package/src/router/registry.ts +98 -0
  104. package/src/router/route.ts +140 -0
  105. package/src/router/router.ts +241 -0
  106. package/src/router/types.ts +27 -0
  107. package/src/security/access-decision-manager.ts +34 -0
  108. package/src/security/authentication-manager.ts +47 -0
  109. package/src/security/context.ts +92 -0
  110. package/src/security/filter.ts +162 -0
  111. package/src/security/index.ts +8 -0
  112. package/src/security/providers/index.ts +3 -0
  113. package/src/security/providers/jwt-provider.ts +60 -0
  114. package/src/security/providers/oauth2-provider.ts +70 -0
  115. package/src/security/security-module.ts +145 -0
  116. package/src/security/types.ts +165 -0
  117. package/src/session/decorators.ts +45 -0
  118. package/src/session/index.ts +19 -0
  119. package/src/session/middleware.ts +143 -0
  120. package/src/session/service.ts +218 -0
  121. package/src/session/session-module.ts +69 -0
  122. package/src/session/types.ts +373 -0
  123. package/src/swagger/decorators.ts +133 -0
  124. package/src/swagger/generator.ts +234 -0
  125. package/src/swagger/index.ts +7 -0
  126. package/src/swagger/swagger-extension.ts +41 -0
  127. package/src/swagger/swagger-module.ts +83 -0
  128. package/src/swagger/types.ts +188 -0
  129. package/src/swagger/ui.ts +98 -0
  130. package/src/testing/harness.ts +96 -0
  131. package/src/validation/decorators.ts +95 -0
  132. package/src/validation/errors.ts +28 -0
  133. package/src/validation/index.ts +14 -0
  134. package/src/validation/types.ts +35 -0
  135. package/src/validation/validator.ts +63 -0
  136. package/src/websocket/decorators.ts +51 -0
  137. package/src/websocket/index.ts +12 -0
  138. package/src/websocket/registry.ts +133 -0
  139. package/tests/cache/cache-module.test.ts +212 -0
  140. package/tests/config/config-module.test.ts +151 -0
  141. package/tests/controller/controller.test.ts +189 -0
  142. package/tests/core/application.test.ts +57 -0
  143. package/tests/core/context-body.test.ts +44 -0
  144. package/tests/core/context.test.ts +86 -0
  145. package/tests/core/edge-cases.test.ts +432 -0
  146. package/tests/database/database-module.test.ts +385 -0
  147. package/tests/database/orm.test.ts +164 -0
  148. package/tests/database/postgres-mysql-integration.test.ts +395 -0
  149. package/tests/database/transaction.test.ts +238 -0
  150. package/tests/di/container.test.ts +264 -0
  151. package/tests/di/module.test.ts +128 -0
  152. package/tests/error/error-codes.test.ts +121 -0
  153. package/tests/error/error-handler.test.ts +68 -0
  154. package/tests/error/error-handling.test.ts +254 -0
  155. package/tests/error/http-exception.test.ts +37 -0
  156. package/tests/error/i18n-integration.test.ts +175 -0
  157. package/tests/extensions/logger-extension.test.ts +40 -0
  158. package/tests/files/static-middleware.test.ts +67 -0
  159. package/tests/files/upload-middleware.test.ts +43 -0
  160. package/tests/health/health-module.test.ts +116 -0
  161. package/tests/integration/application-router.test.ts +85 -0
  162. package/tests/integration/body-parsing.test.ts +88 -0
  163. package/tests/integration/cache-e2e.test.ts +114 -0
  164. package/tests/integration/oauth2-e2e.test.ts +615 -0
  165. package/tests/integration/session-e2e.test.ts +207 -0
  166. package/tests/metrics/metrics-module.test.ts +178 -0
  167. package/tests/middleware/builtin.test.ts +206 -0
  168. package/tests/middleware/file-upload.test.ts +41 -0
  169. package/tests/middleware/middleware.test.ts +120 -0
  170. package/tests/middleware/pipeline.test.ts +72 -0
  171. package/tests/middleware/rate-limit.test.ts +314 -0
  172. package/tests/middleware/static-file.test.ts +62 -0
  173. package/tests/perf/harness.test.ts +48 -0
  174. package/tests/perf/optimization.test.ts +183 -0
  175. package/tests/perf/regression.test.ts +120 -0
  176. package/tests/queue/queue-module.test.ts +217 -0
  177. package/tests/request/body-parser.test.ts +96 -0
  178. package/tests/request/response.test.ts +99 -0
  179. package/tests/router/decorators.test.ts +48 -0
  180. package/tests/router/registry.test.ts +51 -0
  181. package/tests/router/route.test.ts +71 -0
  182. package/tests/router/router-normalization.test.ts +106 -0
  183. package/tests/router/router.test.ts +133 -0
  184. package/tests/security/access-decision-manager.test.ts +84 -0
  185. package/tests/security/authentication-manager.test.ts +81 -0
  186. package/tests/security/context.test.ts +302 -0
  187. package/tests/security/filter.test.ts +225 -0
  188. package/tests/security/jwt-provider.test.ts +106 -0
  189. package/tests/security/oauth2-provider.test.ts +269 -0
  190. package/tests/security/security-module.test.ts +143 -0
  191. package/tests/session/session-module.test.ts +307 -0
  192. package/tests/stress/di-stress.test.ts +30 -0
  193. package/tests/swagger/decorators.test.ts +153 -0
  194. package/tests/swagger/generator.test.ts +202 -0
  195. package/tests/swagger/swagger-extension.test.ts +72 -0
  196. package/tests/swagger/swagger-module.test.ts +79 -0
  197. package/tests/utils/test-port.ts +10 -0
  198. package/tests/validation/controller-validation.test.ts +64 -0
  199. package/tests/validation/validation.test.ts +42 -0
  200. package/tests/websocket/gateway.test.ts +68 -0
@@ -0,0 +1,86 @@
1
+ import { describe, expect, test } from 'bun:test';
2
+ import { Context } from '../../src/core/context';
3
+
4
+ describe('Context', () => {
5
+ test('should create context from request', () => {
6
+ const request = new Request('http://localhost:3000/api/users?id=1');
7
+ const context = new Context(request);
8
+
9
+ expect(context.request).toBe(request);
10
+ expect(context.method).toBe('GET');
11
+ expect(context.path).toBe('/api/users');
12
+ expect(context.getQuery('id')).toBe('1');
13
+ });
14
+
15
+ test('should get query parameters', () => {
16
+ const request = new Request('http://localhost:3000/api/users?name=John&age=30');
17
+ const context = new Context(request);
18
+
19
+ expect(context.getQuery('name')).toBe('John');
20
+ expect(context.getQuery('age')).toBe('30');
21
+ expect(context.getQuery('unknown')).toBeNull();
22
+ });
23
+
24
+ test('should get all query parameters', () => {
25
+ const request = new Request('http://localhost:3000/api/users?name=John&age=30');
26
+ const context = new Context(request);
27
+
28
+ const all = context.getQueryAll();
29
+ expect(all.name).toBe('John');
30
+ expect(all.age).toBe('30');
31
+ });
32
+
33
+ test('should get and set path parameters', () => {
34
+ const request = new Request('http://localhost:3000/api/users/123');
35
+ const context = new Context(request);
36
+
37
+ context.params = { id: '123' };
38
+ expect(context.getParam('id')).toBe('123');
39
+ expect(context.getParam('unknown')).toBeUndefined();
40
+ });
41
+
42
+ test('should get headers', () => {
43
+ const request = new Request('http://localhost:3000/api/users', {
44
+ headers: { 'Content-Type': 'application/json' },
45
+ });
46
+ const context = new Context(request);
47
+
48
+ expect(context.getHeader('Content-Type')).toBe('application/json');
49
+ expect(context.getHeader('Unknown')).toBeNull();
50
+ });
51
+
52
+ test('should set response headers', () => {
53
+ const request = new Request('http://localhost:3000/api/users');
54
+ const context = new Context(request);
55
+
56
+ context.setHeader('Content-Type', 'application/json');
57
+ expect(context.responseHeaders.get('Content-Type')).toBe('application/json');
58
+ });
59
+
60
+ test('should set status code', () => {
61
+ const request = new Request('http://localhost:3000/api/users');
62
+ const context = new Context(request);
63
+
64
+ context.setStatus(404);
65
+ expect(context.statusCode).toBe(404);
66
+ });
67
+
68
+ test('should create JSON response', () => {
69
+ const request = new Request('http://localhost:3000/api/users');
70
+ const context = new Context(request);
71
+
72
+ const response = context.createResponse({ message: 'Hello' });
73
+ expect(response.status).toBe(200);
74
+ expect(response.headers.get('Content-Type')).toBe('application/json');
75
+ });
76
+
77
+ test('should create response with custom status', () => {
78
+ const request = new Request('http://localhost:3000/api/users');
79
+ const context = new Context(request);
80
+ context.setStatus(404);
81
+
82
+ const response = context.createResponse({ error: 'Not Found' });
83
+ expect(response.status).toBe(404);
84
+ });
85
+ });
86
+
@@ -0,0 +1,432 @@
1
+ import { describe, expect, test, beforeEach } from 'bun:test';
2
+ import { Container } from '../../src/di/container';
3
+ import { Injectable, Inject } from '../../src/di/decorators';
4
+ import { Router } from '../../src/router/router';
5
+ import { Context } from '../../src/core/context';
6
+ import { MiddlewarePipeline } from '../../src/middleware/pipeline';
7
+ import type { Middleware } from '../../src/middleware';
8
+
9
+ /**
10
+ * 核心模块边界情况测试
11
+ * 测试错误处理、边界值、异常情况等
12
+ */
13
+
14
+ describe('DI Container Edge Cases', () => {
15
+ let container: Container;
16
+
17
+ beforeEach(() => {
18
+ container = new Container();
19
+ });
20
+
21
+ test('should handle unregistered dependency', () => {
22
+ class UnregisteredService {
23
+ public value = 'test';
24
+ }
25
+
26
+ // 根据实现,可能抛出错误或返回 undefined
27
+ try {
28
+ const instance = container.resolve(UnregisteredService);
29
+ // 如果没有抛出错误,应该返回 undefined 或 null
30
+ expect(instance).toBeUndefined();
31
+ } catch (error) {
32
+ // 或者抛出错误
33
+ expect(error).toBeDefined();
34
+ }
35
+ });
36
+
37
+ test('should handle circular dependency detection', () => {
38
+ @Injectable()
39
+ class ServiceA {
40
+ public constructor(@Inject('ServiceB') private b: unknown) {}
41
+ }
42
+
43
+ @Injectable()
44
+ class ServiceB {
45
+ public constructor(@Inject('ServiceA') private a: unknown) {}
46
+ }
47
+
48
+ container.register('ServiceA', ServiceA);
49
+ container.register('ServiceB', ServiceB);
50
+
51
+ // 循环依赖应该被检测到或抛出错误
52
+ expect(() => {
53
+ container.resolve('ServiceA');
54
+ }).toThrow();
55
+ });
56
+
57
+ test('should handle invalid factory function', () => {
58
+ class TestService {
59
+ public value: string;
60
+ public constructor(value: string) {
61
+ this.value = value;
62
+ }
63
+ }
64
+
65
+ container.register(TestService, {
66
+ factory: () => {
67
+ throw new Error('Factory error');
68
+ },
69
+ });
70
+
71
+ expect(() => {
72
+ container.resolve(TestService);
73
+ }).toThrow('Factory error');
74
+ });
75
+
76
+ test('should handle null/undefined factory return', () => {
77
+ class TestService {
78
+ public value = 'test';
79
+ }
80
+
81
+ container.register(TestService, {
82
+ factory: () => null as unknown as TestService,
83
+ });
84
+
85
+ const instance = container.resolve(TestService);
86
+ expect(instance).toBeNull();
87
+ });
88
+
89
+ test('should handle duplicate registration', () => {
90
+ class TestService {
91
+ public value = 'first';
92
+ }
93
+
94
+ container.register(TestService);
95
+ const first = container.resolve(TestService);
96
+ expect(first.value).toBe('first');
97
+
98
+ // 重复注册应该覆盖或保持原样
99
+ container.register(TestService, {
100
+ factory: () => {
101
+ const instance = new TestService();
102
+ instance.value = 'second';
103
+ return instance;
104
+ },
105
+ });
106
+
107
+ const second = container.resolve(TestService);
108
+ // 根据实现,可能是单例(保持first)或新实例(second)
109
+ expect(second).toBeDefined();
110
+ });
111
+
112
+ test('should handle empty container resolve', () => {
113
+ expect(() => {
114
+ container.resolve('NonExistent');
115
+ }).toThrow();
116
+ });
117
+ });
118
+
119
+ describe('Router Edge Cases', () => {
120
+ let router: Router;
121
+
122
+ beforeEach(() => {
123
+ router = new Router();
124
+ });
125
+
126
+ test('should handle empty path', () => {
127
+ const handler = (ctx: Context) => ctx.createResponse({});
128
+ router.get('', handler);
129
+
130
+ const route = router.findRoute('GET', '');
131
+ expect(route).toBeDefined();
132
+ });
133
+
134
+ test('should handle root path', () => {
135
+ const handler = (ctx: Context) => ctx.createResponse({});
136
+ router.get('/', handler);
137
+
138
+ const route = router.findRoute('GET', '/');
139
+ expect(route).toBeDefined();
140
+ });
141
+
142
+ test('should handle paths with special characters', () => {
143
+ const handler = (ctx: Context) => ctx.createResponse({});
144
+ router.get('/api/users/:id/comments', handler);
145
+
146
+ const route = router.findRoute('GET', '/api/users/123/comments');
147
+ expect(route).toBeDefined();
148
+ });
149
+
150
+ test('should handle very long paths', () => {
151
+ const handler = (ctx: Context) => ctx.createResponse({});
152
+ const longPath = '/api/' + 'a'.repeat(1000);
153
+ router.get(longPath, handler);
154
+
155
+ const route = router.findRoute('GET', longPath);
156
+ expect(route).toBeDefined();
157
+ });
158
+
159
+ test('should handle paths with query strings', () => {
160
+ const handler = (ctx: Context) => ctx.createResponse({});
161
+ router.get('/api/users', handler);
162
+
163
+ // 查询字符串不应该影响路由匹配
164
+ const request = new Request('http://localhost:3000/api/users?id=1&name=test');
165
+ const context = new Context(request);
166
+ const route = router.findRoute('GET', context.path);
167
+ expect(route).toBeDefined();
168
+ });
169
+
170
+ test('should handle invalid HTTP methods', () => {
171
+ const handler = (ctx: Context) => ctx.createResponse({});
172
+ router.register('INVALID' as any, '/api/test', handler);
173
+
174
+ const route = router.findRoute('INVALID' as any, '/api/test');
175
+ // 根据实现,可能接受任意方法或返回 undefined
176
+ // 这里只验证不会崩溃
177
+ expect(route !== undefined || route === undefined).toBe(true);
178
+ });
179
+
180
+ test('should handle path parameters with special values', async () => {
181
+ const handler = (ctx: Context) => {
182
+ return ctx.createResponse({ id: ctx.getParam('id') });
183
+ };
184
+ router.get('/api/users/:id', handler);
185
+
186
+ // 测试各种边界值(跳过中文,因为URL编码/解码可能在不同阶段处理)
187
+ const testCases = [
188
+ { input: '123', expected: '123' },
189
+ { input: 'abc-def_123', expected: 'abc-def_123' },
190
+ { input: '123.456', expected: '123.456' },
191
+ ];
192
+ for (const testCase of testCases) {
193
+ const request = new Request(`http://localhost:3000/api/users/${testCase.input}`);
194
+ const context = new Context(request);
195
+ // 路径参数应该匹配路由提取的值
196
+ context.params = { id: testCase.input };
197
+
198
+ const response = await router.handle(context);
199
+ if (response) {
200
+ const data = await response.json();
201
+ expect(data.id).toBe(testCase.input);
202
+ }
203
+ }
204
+ });
205
+
206
+ test('should handle multiple path parameters', async () => {
207
+ const handler = (ctx: Context) => {
208
+ return ctx.createResponse({
209
+ userId: ctx.getParam('userId'),
210
+ postId: ctx.getParam('postId'),
211
+ });
212
+ };
213
+ router.get('/api/users/:userId/posts/:postId', handler);
214
+
215
+ const request = new Request('http://localhost:3000/api/users/123/posts/456');
216
+ const context = new Context(request);
217
+ context.params = { userId: '123', postId: '456' };
218
+
219
+ const response = await router.handle(context);
220
+ if (response) {
221
+ const data = await response.json();
222
+ expect(data.userId).toBe('123');
223
+ expect(data.postId).toBe('456');
224
+ }
225
+ });
226
+ });
227
+
228
+ describe('Middleware Pipeline Edge Cases', () => {
229
+ test('should handle middleware that throws error', async () => {
230
+ const pipeline = new MiddlewarePipeline();
231
+ const errorMiddleware: Middleware = async () => {
232
+ throw new Error('Middleware error');
233
+ };
234
+
235
+ pipeline.use(errorMiddleware);
236
+
237
+ const context = new Context(new Request('http://localhost:3000/test'));
238
+
239
+ await expect(
240
+ pipeline.run(context, async () => {
241
+ return new Response('ok');
242
+ }),
243
+ ).rejects.toThrow('Middleware error');
244
+ });
245
+
246
+ test('should handle middleware that does not call next', async () => {
247
+ const pipeline = new MiddlewarePipeline();
248
+ const calls: string[] = [];
249
+
250
+ const blocker: Middleware = async () => {
251
+ calls.push('blocker');
252
+ return new Response('blocked', { status: 403 });
253
+ };
254
+
255
+ const afterBlocker: Middleware = async (ctx, next) => {
256
+ calls.push('after:before');
257
+ const response = await next();
258
+ calls.push('after:after');
259
+ return response;
260
+ };
261
+
262
+ pipeline.use(blocker);
263
+ pipeline.use(afterBlocker);
264
+
265
+ const context = new Context(new Request('http://localhost:3000/test'));
266
+ const response = await pipeline.run(context, async () => {
267
+ calls.push('handler');
268
+ return new Response('ok');
269
+ });
270
+
271
+ expect(response.status).toBe(403);
272
+ expect(calls).toEqual(['blocker']);
273
+ expect(calls).not.toContain('after:before');
274
+ expect(calls).not.toContain('handler');
275
+ });
276
+
277
+ test('should handle empty pipeline', async () => {
278
+ const pipeline = new MiddlewarePipeline();
279
+ const context = new Context(new Request('http://localhost:3000/test'));
280
+
281
+ const response = await pipeline.run(context, async () => {
282
+ return new Response('ok');
283
+ });
284
+
285
+ expect(response.status).toBe(200);
286
+ const text = await response.text();
287
+ expect(text).toBe('ok');
288
+ });
289
+
290
+ test('should handle many middlewares', async () => {
291
+ const pipeline = new MiddlewarePipeline();
292
+ const calls: number[] = [];
293
+
294
+ // 添加100个中间件
295
+ for (let i = 0; i < 100; i++) {
296
+ const index = i;
297
+ pipeline.use(async (ctx, next) => {
298
+ calls.push(index);
299
+ return await next();
300
+ });
301
+ }
302
+
303
+ const context = new Context(new Request('http://localhost:3000/test'));
304
+ const response = await pipeline.run(context, async () => {
305
+ calls.push(100);
306
+ return new Response('ok');
307
+ });
308
+
309
+ expect(response.status).toBe(200);
310
+ expect(calls.length).toBe(101);
311
+ expect(calls[0]).toBe(0);
312
+ expect(calls[99]).toBe(99);
313
+ expect(calls[100]).toBe(100);
314
+ });
315
+
316
+ test('should handle middleware that modifies response', async () => {
317
+ const pipeline = new MiddlewarePipeline();
318
+
319
+ const modifier: Middleware = async (ctx, next) => {
320
+ const response = await next();
321
+ const newResponse = new Response('modified', {
322
+ status: response.status,
323
+ headers: response.headers,
324
+ });
325
+ newResponse.headers.set('X-Modified', 'true');
326
+ return newResponse;
327
+ };
328
+
329
+ pipeline.use(modifier);
330
+
331
+ const context = new Context(new Request('http://localhost:3000/test'));
332
+ const response = await pipeline.run(context, async () => {
333
+ return new Response('original');
334
+ });
335
+
336
+ expect(response.status).toBe(200);
337
+ const text = await response.text();
338
+ expect(text).toBe('modified');
339
+ expect(response.headers.get('X-Modified')).toBe('true');
340
+ });
341
+ });
342
+
343
+ describe('Context Edge Cases', () => {
344
+ test('should handle invalid URL', () => {
345
+ // Request 构造函数会直接抛出错误,所以我们需要捕获
346
+ expect(() => {
347
+ const invalidRequest = new Request('invalid-url');
348
+ const context = new Context(invalidRequest);
349
+ expect(context.request).toBe(invalidRequest);
350
+ }).toThrow();
351
+ });
352
+
353
+ test('should handle request with no query parameters', () => {
354
+ const request = new Request('http://localhost:3000/api/users');
355
+ const context = new Context(request);
356
+
357
+ expect(context.getQuery('id')).toBeNull();
358
+ expect(context.getQueryAll()).toEqual({});
359
+ });
360
+
361
+ test('should handle request with empty query values', () => {
362
+ const request = new Request('http://localhost:3000/api/users?id=&name=');
363
+ const context = new Context(request);
364
+
365
+ expect(context.getQuery('id')).toBe('');
366
+ expect(context.getQuery('name')).toBe('');
367
+ });
368
+
369
+ test('should handle special characters in query parameters', () => {
370
+ const request = new Request(
371
+ 'http://localhost:3000/api/users?name=John%20Doe&email=test%40example.com',
372
+ );
373
+ const context = new Context(request);
374
+
375
+ expect(context.getQuery('name')).toBe('John Doe');
376
+ expect(context.getQuery('email')).toBe('test@example.com');
377
+ });
378
+
379
+ test('should handle multiple values for same query parameter', () => {
380
+ const request = new Request('http://localhost:3000/api/users?tag=js&tag=ts&tag=node');
381
+ const context = new Context(request);
382
+
383
+ // getQuery 应该返回第一个值或所有值
384
+ const tag = context.getQuery('tag');
385
+ expect(tag).toBeDefined();
386
+ });
387
+
388
+ test('should handle very long query strings', () => {
389
+ const longValue = 'a'.repeat(10000);
390
+ const request = new Request(`http://localhost:3000/api/users?data=${longValue}`);
391
+ const context = new Context(request);
392
+
393
+ const data = context.getQuery('data');
394
+ expect(data).toBe(longValue);
395
+ });
396
+
397
+ test('should handle invalid status codes', () => {
398
+ const request = new Request('http://localhost:3000/api/users');
399
+ const context = new Context(request);
400
+
401
+ // 应该允许设置各种状态码
402
+ context.setStatus(999);
403
+ expect(context.statusCode).toBe(999);
404
+
405
+ context.setStatus(-1);
406
+ expect(context.statusCode).toBe(-1);
407
+ });
408
+
409
+ test('should handle null/undefined response data', () => {
410
+ const request = new Request('http://localhost:3000/api/users');
411
+ const context = new Context(request);
412
+
413
+ const nullResponse = context.createResponse(null);
414
+ expect(nullResponse).toBeDefined();
415
+
416
+ const undefinedResponse = context.createResponse(undefined);
417
+ expect(undefinedResponse).toBeDefined();
418
+ });
419
+
420
+ test('should handle response with circular references', () => {
421
+ const request = new Request('http://localhost:3000/api/users');
422
+ const context = new Context(request);
423
+
424
+ const circular: any = { name: 'test' };
425
+ circular.self = circular;
426
+
427
+ // JSON.stringify 应该处理循环引用(抛出错误或使用特殊处理)
428
+ expect(() => {
429
+ context.createResponse(circular);
430
+ }).toThrow();
431
+ });
432
+ });