imean-service-engine 2.0.0 → 2.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.
Files changed (72) hide show
  1. package/dist/index.d.mts +77 -52
  2. package/dist/index.d.ts +77 -52
  3. package/dist/index.js +2078 -1945
  4. package/dist/index.mjs +2076 -1944
  5. package/package.json +9 -2
  6. package/.vscode/settings.json +0 -8
  7. package/src/core/checker.ts +0 -33
  8. package/src/core/decorators.test.ts +0 -96
  9. package/src/core/decorators.ts +0 -68
  10. package/src/core/engine.test.ts +0 -218
  11. package/src/core/engine.ts +0 -635
  12. package/src/core/errors.ts +0 -28
  13. package/src/core/factory.test.ts +0 -73
  14. package/src/core/factory.ts +0 -92
  15. package/src/core/logger.ts +0 -65
  16. package/src/core/testing.ts +0 -73
  17. package/src/core/types.ts +0 -191
  18. package/src/index.ts +0 -49
  19. package/src/metadata/README.md +0 -422
  20. package/src/metadata/metadata.test.ts +0 -369
  21. package/src/metadata/metadata.ts +0 -512
  22. package/src/plugins/action/action-plugin.test.ts +0 -660
  23. package/src/plugins/action/decorator.ts +0 -14
  24. package/src/plugins/action/index.ts +0 -4
  25. package/src/plugins/action/plugin.ts +0 -349
  26. package/src/plugins/action/types.ts +0 -49
  27. package/src/plugins/action/utils.test.ts +0 -196
  28. package/src/plugins/action/utils.ts +0 -111
  29. package/src/plugins/cache/adapter.test.ts +0 -689
  30. package/src/plugins/cache/adapter.ts +0 -324
  31. package/src/plugins/cache/cache-plugin.test.ts +0 -269
  32. package/src/plugins/cache/decorator.ts +0 -26
  33. package/src/plugins/cache/index.ts +0 -20
  34. package/src/plugins/cache/plugin.ts +0 -299
  35. package/src/plugins/cache/types.ts +0 -69
  36. package/src/plugins/client-code/client-code-plugin.test.ts +0 -511
  37. package/src/plugins/client-code/format.ts +0 -9
  38. package/src/plugins/client-code/generator.test.ts +0 -52
  39. package/src/plugins/client-code/generator.ts +0 -263
  40. package/src/plugins/client-code/index.ts +0 -15
  41. package/src/plugins/client-code/plugin.ts +0 -158
  42. package/src/plugins/client-code/types.ts +0 -52
  43. package/src/plugins/client-code/utils.ts +0 -164
  44. package/src/plugins/graceful-shutdown/graceful-shutdown-plugin.test.ts +0 -401
  45. package/src/plugins/graceful-shutdown/index.ts +0 -3
  46. package/src/plugins/graceful-shutdown/plugin.ts +0 -279
  47. package/src/plugins/graceful-shutdown/types.ts +0 -17
  48. package/src/plugins/rate-limit/rate-limit-plugin.example.ts +0 -171
  49. package/src/plugins/route/components/Layout.tsx +0 -42
  50. package/src/plugins/route/components/ServiceStatusPage.tsx +0 -141
  51. package/src/plugins/route/decorator.ts +0 -50
  52. package/src/plugins/route/index.ts +0 -16
  53. package/src/plugins/route/plugin.ts +0 -218
  54. package/src/plugins/route/route-plugin.test.ts +0 -759
  55. package/src/plugins/route/types.ts +0 -72
  56. package/src/plugins/schedule/README.md +0 -309
  57. package/src/plugins/schedule/decorator.ts +0 -25
  58. package/src/plugins/schedule/index.ts +0 -12
  59. package/src/plugins/schedule/mock-etcd.ts +0 -145
  60. package/src/plugins/schedule/plugin.ts +0 -164
  61. package/src/plugins/schedule/schedule-plugin.test.ts +0 -312
  62. package/src/plugins/schedule/scheduler.ts +0 -164
  63. package/src/plugins/schedule/types.ts +0 -94
  64. package/src/plugins/schedule/utils.test.ts +0 -163
  65. package/src/plugins/schedule/utils.ts +0 -41
  66. package/tests/integration/client.test.ts +0 -203
  67. package/tests/integration/dev-service.ts +0 -301
  68. package/tests/integration/generated/client.ts +0 -123
  69. package/tests/integration/start-service.ts +0 -21
  70. package/tsconfig.json +0 -27
  71. package/tsup.config.ts +0 -16
  72. package/vitest.config.ts +0 -19
@@ -1,759 +0,0 @@
1
- import { beforeEach, describe, expect, it, vi } from "vitest";
2
- import { Context } from "hono";
3
- import { Testing } from "../../core/testing";
4
- import { Page, Route, RoutePlugin } from "./index";
5
-
6
- describe("RoutePlugin", () => {
7
- let engine: ReturnType<typeof Testing.createTestEngine>["engine"];
8
- let Module: ReturnType<typeof Testing.createTestEngine>["Module"];
9
- let routePlugin: RoutePlugin;
10
-
11
- beforeEach(() => {
12
- routePlugin = new RoutePlugin();
13
- const testEngine = Testing.createTestEngine({ plugins: [routePlugin] });
14
- engine = testEngine.engine;
15
- Module = testEngine.Module;
16
- });
17
-
18
- describe("插件配置", () => {
19
- it("应该有正确的插件名称", () => {
20
- expect(routePlugin.name).toBe("route-plugin");
21
- });
22
-
23
- it("应该声明Module配置Schema", () => {
24
- const schema = routePlugin.getModuleOptionsSchema?.();
25
- expect(schema).toBeDefined();
26
- expect(schema?.validate).toBeDefined();
27
- });
28
-
29
- it("应该校验Module配置", () => {
30
- const schema = routePlugin.getModuleOptionsSchema?.();
31
- if (!schema?.validate) return;
32
-
33
- // 有效的配置
34
- expect(schema.validate({ routePrefix: "/api" })).toBe(true);
35
-
36
- // 无效的配置(routePrefix不以/开头)
37
- const result = schema.validate({ routePrefix: "api" });
38
- expect(result).not.toBe(true);
39
- expect(typeof result).toBe("string");
40
- });
41
- });
42
-
43
- describe("生命周期钩子", () => {
44
- it("应该在onHandlerLoad中注册路由", async () => {
45
- routePlugin.onInit?.(engine);
46
-
47
- @Module("test-module", {
48
- routePrefix: "/api",
49
- routeMiddlewares: [],
50
- })
51
- class TestService {
52
- @Route({ method: "GET", path: "/user/:id" })
53
- getUser() {
54
- return { id: 1 };
55
- }
56
- }
57
-
58
- // 手动触发Handler加载(模拟引擎流程)
59
- const { getAllHandlerMetadata } = await import("../../core/decorators");
60
- const handlers = Array.from(
61
- getAllHandlerMetadata(TestService).entries()
62
- ).flatMap(([methodName, metadataList]) =>
63
- metadataList.map((meta) => ({
64
- ...meta,
65
- method: (TestService.prototype as any)[methodName],
66
- methodName: String(methodName),
67
- module: TestService,
68
- }))
69
- );
70
-
71
- routePlugin.onHandlerLoad?.(handlers);
72
- });
73
- });
74
-
75
- describe("路由注册", () => {
76
- it("应该支持模块级路由前缀", async () => {
77
- routePlugin.onInit?.(engine);
78
-
79
- @Module("test-module", {
80
- routePrefix: "/api/v1",
81
- })
82
- class TestService {
83
- @Route({ method: "GET", path: "/user" })
84
- getUser() {}
85
- }
86
-
87
- // 这里简化测试,实际应该通过引擎启动流程
88
- // 验证路由路径应该是 /api/v1/user
89
- });
90
-
91
- it("应该支持模块级中间件", async () => {
92
- const middleware = vi.fn(async (ctx: any, next: () => Promise<void>) => {
93
- await next();
94
- });
95
-
96
- routePlugin.onInit?.(engine);
97
-
98
- @Module("test-module", {
99
- routeMiddlewares: [middleware],
100
- })
101
- class TestService {
102
- @Route({ method: "GET", path: "/user" })
103
- getUser() {}
104
- }
105
-
106
- // 验证中间件应该被注册
107
- });
108
-
109
- it("应该支持多个路径", async () => {
110
- @Module("test-module")
111
- class TestService {
112
- @Route({
113
- path: ["/path1", "/path2", "/path3"],
114
- description: "多路径测试",
115
- })
116
- multiPath() {
117
- return { success: true };
118
- }
119
- }
120
-
121
- await engine.start(3000);
122
- const port = engine.getPort();
123
-
124
- // 测试所有路径都应该响应
125
- const response1 = await fetch(`http://127.0.0.1:${port}/path1`);
126
- expect(response1.ok).toBe(true);
127
-
128
- const response2 = await fetch(`http://127.0.0.1:${port}/path2`);
129
- expect(response2.ok).toBe(true);
130
-
131
- const response3 = await fetch(`http://127.0.0.1:${port}/path3`);
132
- expect(response3.ok).toBe(true);
133
-
134
- await engine.stop();
135
- });
136
-
137
- it("应该支持 Page 装饰器(默认 GET 方法)", async () => {
138
- @Module("test-module")
139
- class TestService {
140
- @Page({
141
- path: ["/", "/home"],
142
- description: "首页",
143
- })
144
- homePage() {
145
- return { page: "home" };
146
- }
147
- }
148
-
149
- await engine.start(3000);
150
- const port = engine.getPort();
151
-
152
- // Page 装饰器应该默认使用 GET 方法
153
- const response1 = await fetch(`http://127.0.0.1:${port}/`);
154
- expect(response1.ok).toBe(true);
155
-
156
- const response2 = await fetch(`http://127.0.0.1:${port}/home`);
157
- expect(response2.ok).toBe(true);
158
-
159
- await engine.stop();
160
- });
161
-
162
- it("应该支持路由级中间件", async () => {
163
- const routeMiddleware = vi.fn(
164
- async (ctx: Context, next: () => Promise<void>) => {
165
- ctx.set("X-Custom-Header", "test-value");
166
- await next();
167
- }
168
- );
169
-
170
- @Module("test-module")
171
- class TestService {
172
- @Route({
173
- method: "GET",
174
- path: "/middleware-test",
175
- middlewares: [routeMiddleware],
176
- })
177
- middlewareTest(ctx: Context) {
178
- return { header: ctx.get("X-Custom-Header") };
179
- }
180
- }
181
-
182
- await engine.start(3000);
183
- const port = engine.getPort();
184
-
185
- const response = await fetch(`http://127.0.0.1:${port}/middleware-test`);
186
- expect(response.ok).toBe(true);
187
- expect(routeMiddleware).toHaveBeenCalled();
188
-
189
- await engine.stop();
190
- });
191
-
192
- it("应该支持 description 字段", async () => {
193
- @Module("test-module")
194
- class TestService {
195
- @Route({
196
- method: "GET",
197
- path: "/described",
198
- description: "这是一个带描述的路由",
199
- })
200
- describedRoute() {
201
- return { success: true };
202
- }
203
- }
204
-
205
- await engine.start(3000);
206
- const port = engine.getPort();
207
-
208
- // description 主要用于日志,这里验证路由可以正常访问
209
- const response = await fetch(`http://127.0.0.1:${port}/described`);
210
- expect(response.ok).toBe(true);
211
-
212
- await engine.stop();
213
- });
214
- });
215
-
216
- describe("中间件功能", () => {
217
- it("应该支持从 header 中获取 token 的授权中间件", async () => {
218
- // 模拟授权中间件:从 header 中获取 token,验证授权
219
- const authMiddleware = vi.fn(
220
- async (ctx: Context, next: () => Promise<void>) => {
221
- const token = ctx.req.header("Authorization");
222
- if (!token || !token.startsWith("Bearer ")) {
223
- return ctx.json({ error: "Unauthorized: Missing or invalid token" }, 401);
224
- }
225
- const actualToken = token.replace("Bearer ", "");
226
- if (actualToken !== "valid-token-123") {
227
- return ctx.json({ error: "Unauthorized: Invalid token" }, 401);
228
- }
229
- // 将用户信息注入到 context
230
- ctx.set("user", { id: "123", name: "Test User" });
231
- await next();
232
- }
233
- );
234
-
235
- @Module("test-module")
236
- class TestService {
237
- @Route({
238
- method: "GET",
239
- path: "/protected",
240
- middlewares: [authMiddleware],
241
- })
242
- protectedRoute(ctx: Context) {
243
- const user = ctx.get("user") as { id: string; name: string } | undefined;
244
- return { success: true, user };
245
- }
246
- }
247
-
248
- await engine.start(3000);
249
- const port = engine.getPort();
250
-
251
- // 测试:没有 token 的请求应该被拦截
252
- const response1 = await fetch(`http://127.0.0.1:${port}/protected`);
253
- expect(response1.status).toBe(401);
254
- const error1 = (await response1.json()) as { error: string };
255
- expect(error1.error).toContain("Unauthorized");
256
- expect(authMiddleware).toHaveBeenCalled();
257
-
258
- // 测试:无效 token 的请求应该被拦截
259
- const response2 = await fetch(`http://127.0.0.1:${port}/protected`, {
260
- headers: { Authorization: "Bearer invalid-token" },
261
- });
262
- expect(response2.status).toBe(401);
263
- const error2 = (await response2.json()) as { error: string };
264
- expect(error2.error).toContain("Invalid token");
265
-
266
- // 测试:有效 token 的请求应该通过
267
- const response3 = await fetch(`http://127.0.0.1:${port}/protected`, {
268
- headers: { Authorization: "Bearer valid-token-123" },
269
- });
270
- expect(response3.status).toBe(200);
271
- const result = (await response3.json()) as {
272
- success: boolean;
273
- user: { id: string; name: string };
274
- };
275
- expect(result.success).toBe(true);
276
- expect(result.user).toEqual({ id: "123", name: "Test User" });
277
-
278
- await engine.stop();
279
- });
280
-
281
- it("应该支持模块级中间件和路由级中间件的组合", async () => {
282
- const moduleMiddleware = vi.fn(
283
- async (ctx: Context, next: () => Promise<void>) => {
284
- ctx.set("module-processed", true);
285
- await next();
286
- }
287
- );
288
-
289
- const routeMiddleware = vi.fn(
290
- async (ctx: Context, next: () => Promise<void>) => {
291
- ctx.set("route-processed", true);
292
- await next();
293
- }
294
- );
295
-
296
- @Module("test-module", {
297
- routeMiddlewares: [moduleMiddleware],
298
- })
299
- class TestService {
300
- @Route({
301
- method: "GET",
302
- path: "/combined",
303
- middlewares: [routeMiddleware],
304
- })
305
- combinedRoute(ctx: Context) {
306
- return {
307
- moduleProcessed: ctx.get("module-processed"),
308
- routeProcessed: ctx.get("route-processed"),
309
- };
310
- }
311
- }
312
-
313
- await engine.start(3000);
314
- const port = engine.getPort();
315
-
316
- const response = await fetch(`http://127.0.0.1:${port}/combined`);
317
- expect(response.status).toBe(200);
318
- const result = (await response.json()) as {
319
- moduleProcessed: boolean;
320
- routeProcessed: boolean;
321
- };
322
- expect(result.moduleProcessed).toBe(true);
323
- expect(result.routeProcessed).toBe(true);
324
- expect(moduleMiddleware).toHaveBeenCalled();
325
- expect(routeMiddleware).toHaveBeenCalled();
326
-
327
- await engine.stop();
328
- });
329
-
330
- it("应该支持中间件链式执行顺序", async () => {
331
- const executionOrder: string[] = [];
332
-
333
- const middleware1 = vi.fn(async (ctx: Context, next: () => Promise<void>) => {
334
- executionOrder.push("middleware1-before");
335
- await next();
336
- executionOrder.push("middleware1-after");
337
- });
338
-
339
- const middleware2 = vi.fn(async (ctx: Context, next: () => Promise<void>) => {
340
- executionOrder.push("middleware2-before");
341
- await next();
342
- executionOrder.push("middleware2-after");
343
- });
344
-
345
- const middleware3 = vi.fn(async (ctx: Context, next: () => Promise<void>) => {
346
- executionOrder.push("middleware3-before");
347
- await next();
348
- executionOrder.push("middleware3-after");
349
- });
350
-
351
- @Module("test-module", {
352
- routeMiddlewares: [middleware1],
353
- })
354
- class TestService {
355
- @Route({
356
- method: "GET",
357
- path: "/chain",
358
- middlewares: [middleware2, middleware3],
359
- })
360
- chainRoute() {
361
- executionOrder.push("handler");
362
- return { order: [...executionOrder] };
363
- }
364
- }
365
-
366
- await engine.start(3000);
367
- const port = engine.getPort();
368
-
369
- const response = await fetch(`http://127.0.0.1:${port}/chain`);
370
- expect(response.status).toBe(200);
371
- const result = (await response.json()) as { order: string[] };
372
-
373
- // 验证执行顺序:模块中间件 -> 路由中间件1 -> 路由中间件2 -> 处理器 -> 路由中间件2 -> 路由中间件1 -> 模块中间件
374
- // Hono 的中间件执行顺序:先执行模块级中间件,然后执行路由级中间件(按数组顺序)
375
- // 注意:由于中间件是异步的,后置处理可能在响应返回后才执行,所以这里只验证前置顺序
376
- expect(result.order).toContain("middleware1-before");
377
- expect(result.order).toContain("middleware2-before");
378
- expect(result.order).toContain("middleware3-before");
379
- expect(result.order).toContain("handler");
380
-
381
- // 验证前置执行顺序(在 handler 之前)
382
- const handlerIndex = result.order.indexOf("handler");
383
- expect(handlerIndex).toBeGreaterThan(-1);
384
- expect(result.order.indexOf("middleware1-before")).toBeLessThan(handlerIndex);
385
- expect(result.order.indexOf("middleware2-before")).toBeLessThan(handlerIndex);
386
- expect(result.order.indexOf("middleware3-before")).toBeLessThan(handlerIndex);
387
-
388
- // 验证所有中间件都被调用了
389
- expect(middleware1).toHaveBeenCalled();
390
- expect(middleware2).toHaveBeenCalled();
391
- expect(middleware3).toHaveBeenCalled();
392
-
393
- await engine.stop();
394
- });
395
-
396
- it("应该支持中间件提前返回响应(不调用 next)", async () => {
397
- const blockingMiddleware = vi.fn(async (ctx: Context, next: () => Promise<void>) => {
398
- // 中间件直接返回响应,不调用 next
399
- return ctx.json({ blocked: true, reason: "Access denied" }, 403);
400
- });
401
-
402
- @Module("test-module")
403
- class TestService {
404
- @Route({
405
- method: "GET",
406
- path: "/blocked",
407
- middlewares: [blockingMiddleware],
408
- })
409
- blockedRoute() {
410
- // 这个方法不应该被执行
411
- return { success: true };
412
- }
413
- }
414
-
415
- await engine.start(3000);
416
- const port = engine.getPort();
417
-
418
- const response = await fetch(`http://127.0.0.1:${port}/blocked`);
419
- expect(response.status).toBe(403);
420
- const result = (await response.json()) as { blocked: boolean; reason: string };
421
- expect(result.blocked).toBe(true);
422
- expect(result.reason).toBe("Access denied");
423
-
424
- await engine.stop();
425
- });
426
-
427
- it("应该支持多个路径共享相同的中间件", async () => {
428
- const sharedMiddleware = vi.fn(
429
- async (ctx: Context, next: () => Promise<void>) => {
430
- ctx.set("shared-processed", true);
431
- await next();
432
- }
433
- );
434
-
435
- @Module("test-module")
436
- class TestService {
437
- @Route({
438
- method: "GET",
439
- path: ["/shared1", "/shared2", "/shared3"],
440
- middlewares: [sharedMiddleware],
441
- })
442
- sharedRoute(ctx: Context) {
443
- return {
444
- path: ctx.req.path,
445
- processed: ctx.get("shared-processed"),
446
- };
447
- }
448
- }
449
-
450
- await engine.start(3000);
451
- const port = engine.getPort();
452
-
453
- // 测试所有路径都应该应用中间件
454
- const response1 = await fetch(`http://127.0.0.1:${port}/shared1`);
455
- expect(response1.status).toBe(200);
456
- const result1 = (await response1.json()) as {
457
- path: string;
458
- processed: boolean;
459
- };
460
- expect(result1.processed).toBe(true);
461
-
462
- const response2 = await fetch(`http://127.0.0.1:${port}/shared2`);
463
- expect(response2.status).toBe(200);
464
- const result2 = (await response2.json()) as {
465
- path: string;
466
- processed: boolean;
467
- };
468
- expect(result2.processed).toBe(true);
469
-
470
- const response3 = await fetch(`http://127.0.0.1:${port}/shared3`);
471
- expect(response3.status).toBe(200);
472
- const result3 = (await response3.json()) as {
473
- path: string;
474
- processed: boolean;
475
- };
476
- expect(result3.processed).toBe(true);
477
-
478
- // 中间件应该被调用 3 次(每个路径一次)
479
- expect(sharedMiddleware).toHaveBeenCalledTimes(3);
480
-
481
- await engine.stop();
482
- });
483
- });
484
-
485
- describe("全局中间件功能", () => {
486
- it("应该支持全局中间件应用于所有路由", async () => {
487
- const globalMiddleware = vi.fn(
488
- async (ctx: Context, next: () => Promise<void>) => {
489
- ctx.set("global-processed", true);
490
- await next();
491
- }
492
- );
493
-
494
- const routePluginWithGlobal = new RoutePlugin({
495
- globalMiddlewares: [globalMiddleware],
496
- });
497
- const testEngine = Testing.createTestEngine({
498
- plugins: [routePluginWithGlobal],
499
- });
500
- const testEngineInstance = testEngine.engine;
501
- const TestModule = testEngine.Module;
502
-
503
- @TestModule("test-module-1")
504
- class TestService1 {
505
- @Route({ method: "GET", path: "/route1" })
506
- route1(ctx: Context) {
507
- return { processed: ctx.get("global-processed") };
508
- }
509
- }
510
-
511
- @TestModule("test-module-2")
512
- class TestService2 {
513
- @Route({ method: "GET", path: "/route2" })
514
- route2(ctx: Context) {
515
- return { processed: ctx.get("global-processed") };
516
- }
517
- }
518
-
519
- await testEngineInstance.start(3000);
520
- const port = testEngineInstance.getPort();
521
-
522
- // 测试所有路由都应该应用全局中间件
523
- const response1 = await fetch(`http://127.0.0.1:${port}/route1`);
524
- expect(response1.status).toBe(200);
525
- const result1 = (await response1.json()) as { processed: boolean };
526
- expect(result1.processed).toBe(true);
527
-
528
- const response2 = await fetch(`http://127.0.0.1:${port}/route2`);
529
- expect(response2.status).toBe(200);
530
- const result2 = (await response2.json()) as { processed: boolean };
531
- expect(result2.processed).toBe(true);
532
-
533
- // 全局中间件应该被调用 2 次(每个路由一次)
534
- expect(globalMiddleware).toHaveBeenCalledTimes(2);
535
-
536
- await testEngineInstance.stop();
537
- });
538
-
539
- it("应该支持全局中间件、模块级中间件和路由级中间件的执行顺序", async () => {
540
- const executionOrder: string[] = [];
541
-
542
- const globalMiddleware = vi.fn(
543
- async (ctx: Context, next: () => Promise<void>) => {
544
- executionOrder.push("global-before");
545
- await next();
546
- executionOrder.push("global-after");
547
- }
548
- );
549
-
550
- const moduleMiddleware = vi.fn(
551
- async (ctx: Context, next: () => Promise<void>) => {
552
- executionOrder.push("module-before");
553
- await next();
554
- executionOrder.push("module-after");
555
- }
556
- );
557
-
558
- const routeMiddleware = vi.fn(
559
- async (ctx: Context, next: () => Promise<void>) => {
560
- executionOrder.push("route-before");
561
- await next();
562
- executionOrder.push("route-after");
563
- }
564
- );
565
-
566
- const routePluginWithGlobal = new RoutePlugin({
567
- globalMiddlewares: [globalMiddleware],
568
- });
569
- const testEngine = Testing.createTestEngine({
570
- plugins: [routePluginWithGlobal],
571
- });
572
- const testEngineInstance = testEngine.engine;
573
- const TestModule = testEngine.Module;
574
-
575
- @TestModule("test-module", {
576
- routeMiddlewares: [moduleMiddleware],
577
- })
578
- class TestService {
579
- @Route({
580
- method: "GET",
581
- path: "/ordered",
582
- middlewares: [routeMiddleware],
583
- })
584
- orderedRoute() {
585
- executionOrder.push("handler");
586
- return { order: [...executionOrder] };
587
- }
588
- }
589
-
590
- await testEngineInstance.start(3000);
591
- const port = testEngineInstance.getPort();
592
-
593
- const response = await fetch(`http://127.0.0.1:${port}/ordered`);
594
- expect(response.status).toBe(200);
595
- const result = (await response.json()) as { order: string[] };
596
-
597
- // 验证执行顺序:全局 -> 模块 -> 路由 -> 处理器
598
- // 注意:后置处理(after)可能在响应返回后才执行,所以这里只验证前置顺序
599
- const handlerIndex = result.order.indexOf("handler");
600
- expect(handlerIndex).toBeGreaterThan(-1);
601
-
602
- // 验证前置执行顺序(在 handler 之前)
603
- const globalBeforeIndex = result.order.indexOf("global-before");
604
- const moduleBeforeIndex = result.order.indexOf("module-before");
605
- const routeBeforeIndex = result.order.indexOf("route-before");
606
-
607
- expect(globalBeforeIndex).toBeGreaterThan(-1);
608
- expect(moduleBeforeIndex).toBeGreaterThan(-1);
609
- expect(routeBeforeIndex).toBeGreaterThan(-1);
610
-
611
- expect(globalBeforeIndex).toBeLessThan(moduleBeforeIndex);
612
- expect(moduleBeforeIndex).toBeLessThan(routeBeforeIndex);
613
- expect(routeBeforeIndex).toBeLessThan(handlerIndex);
614
-
615
- // 验证所有中间件都被调用了
616
- expect(globalMiddleware).toHaveBeenCalled();
617
- expect(moduleMiddleware).toHaveBeenCalled();
618
- expect(routeMiddleware).toHaveBeenCalled();
619
-
620
- await testEngineInstance.stop();
621
- });
622
-
623
- it("应该支持全局鉴权中间件拦截未授权请求", async () => {
624
- const authMiddleware = vi.fn(
625
- async (ctx: Context, next: () => Promise<void>) => {
626
- const token = ctx.req.header("Authorization");
627
- if (!token || !token.startsWith("Bearer ")) {
628
- return ctx.json({ error: "Unauthorized" }, 401);
629
- }
630
- const actualToken = token.replace("Bearer ", "");
631
- if (actualToken !== "valid-token") {
632
- return ctx.json({ error: "Invalid token" }, 401);
633
- }
634
- ctx.set("user", { id: "123", name: "Test User" });
635
- await next();
636
- }
637
- );
638
-
639
- const routePluginWithAuth = new RoutePlugin({
640
- globalMiddlewares: [authMiddleware],
641
- });
642
- const testEngine = Testing.createTestEngine({
643
- plugins: [routePluginWithAuth],
644
- });
645
- const testEngineInstance = testEngine.engine;
646
- const TestModule = testEngine.Module;
647
-
648
- @TestModule("test-module-1")
649
- class TestService1 {
650
- @Route({ method: "GET", path: "/public" })
651
- publicRoute() {
652
- return { message: "public" };
653
- }
654
- }
655
-
656
- @TestModule("test-module-2")
657
- class TestService2 {
658
- @Route({ method: "GET", path: "/private" })
659
- privateRoute(ctx: Context) {
660
- const user = ctx.get("user") as { id: string; name: string } | undefined;
661
- return { message: "private", user };
662
- }
663
- }
664
-
665
- await testEngineInstance.start(3000);
666
- const port = testEngineInstance.getPort();
667
-
668
- // 测试:没有 token 的请求应该被拦截
669
- const response1 = await fetch(`http://127.0.0.1:${port}/public`);
670
- expect(response1.status).toBe(401);
671
- const error1 = (await response1.json()) as { error: string };
672
- expect(error1.error).toBe("Unauthorized");
673
-
674
- const response2 = await fetch(`http://127.0.0.1:${port}/private`);
675
- expect(response2.status).toBe(401);
676
-
677
- // 测试:无效 token 的请求应该被拦截
678
- const response3 = await fetch(`http://127.0.0.1:${port}/public`, {
679
- headers: { Authorization: "Bearer invalid-token" },
680
- });
681
- expect(response3.status).toBe(401);
682
- const error3 = (await response3.json()) as { error: string };
683
- expect(error3.error).toBe("Invalid token");
684
-
685
- // 测试:有效 token 的请求应该通过
686
- const response4 = await fetch(`http://127.0.0.1:${port}/public`, {
687
- headers: { Authorization: "Bearer valid-token" },
688
- });
689
- expect(response4.status).toBe(200);
690
- const result4 = (await response4.json()) as { message: string };
691
- expect(result4.message).toBe("public");
692
-
693
- const response5 = await fetch(`http://127.0.0.1:${port}/private`, {
694
- headers: { Authorization: "Bearer valid-token" },
695
- });
696
- expect(response5.status).toBe(200);
697
- const result5 = (await response5.json()) as {
698
- message: string;
699
- user: { id: string; name: string };
700
- };
701
- expect(result5.message).toBe("private");
702
- expect(result5.user).toEqual({ id: "123", name: "Test User" });
703
-
704
- await testEngineInstance.stop();
705
- });
706
-
707
- it("应该支持多个全局中间件", async () => {
708
- const middleware1 = vi.fn(
709
- async (ctx: Context, next: () => Promise<void>) => {
710
- ctx.set("m1-processed", true);
711
- await next();
712
- }
713
- );
714
-
715
- const middleware2 = vi.fn(
716
- async (ctx: Context, next: () => Promise<void>) => {
717
- ctx.set("m2-processed", true);
718
- await next();
719
- }
720
- );
721
-
722
- const routePluginWithMultiple = new RoutePlugin({
723
- globalMiddlewares: [middleware1, middleware2],
724
- });
725
- const testEngine = Testing.createTestEngine({
726
- plugins: [routePluginWithMultiple],
727
- });
728
- const testEngineInstance = testEngine.engine;
729
- const TestModule = testEngine.Module;
730
-
731
- @TestModule("test-module")
732
- class TestService {
733
- @Route({ method: "GET", path: "/multi" })
734
- multiRoute(ctx: Context) {
735
- return {
736
- m1: ctx.get("m1-processed"),
737
- m2: ctx.get("m2-processed"),
738
- };
739
- }
740
- }
741
-
742
- await testEngineInstance.start(3000);
743
- const port = testEngineInstance.getPort();
744
-
745
- const response = await fetch(`http://127.0.0.1:${port}/multi`);
746
- expect(response.status).toBe(200);
747
- const result = (await response.json()) as {
748
- m1: boolean;
749
- m2: boolean;
750
- };
751
- expect(result.m1).toBe(true);
752
- expect(result.m2).toBe(true);
753
- expect(middleware1).toHaveBeenCalled();
754
- expect(middleware2).toHaveBeenCalled();
755
-
756
- await testEngineInstance.stop();
757
- });
758
- });
759
- });