@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
|
@@ -0,0 +1,260 @@
|
|
|
1
|
+
import { describe, expect, test, beforeEach } from 'bun:test';
|
|
2
|
+
import 'reflect-metadata';
|
|
3
|
+
|
|
4
|
+
import {
|
|
5
|
+
GET,
|
|
6
|
+
POST,
|
|
7
|
+
PUT,
|
|
8
|
+
DELETE,
|
|
9
|
+
PATCH,
|
|
10
|
+
ROUTE_METADATA_KEY,
|
|
11
|
+
type RouteMetadata,
|
|
12
|
+
} from '../../src/router/decorators';
|
|
13
|
+
import { Controller } from '../../src/controller/controller';
|
|
14
|
+
|
|
15
|
+
describe('Router Decorators', () => {
|
|
16
|
+
describe('@GET', () => {
|
|
17
|
+
test('should set route metadata for GET method', () => {
|
|
18
|
+
class TestController {
|
|
19
|
+
@GET('/users')
|
|
20
|
+
public getUsers(): string[] {
|
|
21
|
+
return [];
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
const routes = Reflect.getMetadata(
|
|
26
|
+
ROUTE_METADATA_KEY,
|
|
27
|
+
TestController.prototype,
|
|
28
|
+
) as RouteMetadata[];
|
|
29
|
+
|
|
30
|
+
expect(routes).toBeDefined();
|
|
31
|
+
expect(routes.length).toBe(1);
|
|
32
|
+
expect(routes[0].method).toBe('GET');
|
|
33
|
+
expect(routes[0].path).toBe('/users');
|
|
34
|
+
expect(routes[0].propertyKey).toBe('getUsers');
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
test('should handle path with parameters', () => {
|
|
38
|
+
class TestController {
|
|
39
|
+
@GET('/users/:id')
|
|
40
|
+
public getUser(id: string): void {}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
const routes = Reflect.getMetadata(
|
|
44
|
+
ROUTE_METADATA_KEY,
|
|
45
|
+
TestController.prototype,
|
|
46
|
+
) as RouteMetadata[];
|
|
47
|
+
|
|
48
|
+
expect(routes[0].path).toBe('/users/:id');
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
test('should handle root path', () => {
|
|
52
|
+
class TestController {
|
|
53
|
+
@GET('/')
|
|
54
|
+
public getRoot(): void {}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
const routes = Reflect.getMetadata(
|
|
58
|
+
ROUTE_METADATA_KEY,
|
|
59
|
+
TestController.prototype,
|
|
60
|
+
) as RouteMetadata[];
|
|
61
|
+
|
|
62
|
+
expect(routes[0].path).toBe('/');
|
|
63
|
+
});
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
describe('@POST', () => {
|
|
67
|
+
test('should set route metadata for POST method', () => {
|
|
68
|
+
class TestController {
|
|
69
|
+
@POST('/users')
|
|
70
|
+
public createUser(): void {}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
const routes = Reflect.getMetadata(
|
|
74
|
+
ROUTE_METADATA_KEY,
|
|
75
|
+
TestController.prototype,
|
|
76
|
+
) as RouteMetadata[];
|
|
77
|
+
|
|
78
|
+
expect(routes).toBeDefined();
|
|
79
|
+
expect(routes.length).toBe(1);
|
|
80
|
+
expect(routes[0].method).toBe('POST');
|
|
81
|
+
expect(routes[0].path).toBe('/users');
|
|
82
|
+
});
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
describe('@PUT', () => {
|
|
86
|
+
test('should set route metadata for PUT method', () => {
|
|
87
|
+
class TestController {
|
|
88
|
+
@PUT('/users/:id')
|
|
89
|
+
public updateUser(id: string): void {}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
const routes = Reflect.getMetadata(
|
|
93
|
+
ROUTE_METADATA_KEY,
|
|
94
|
+
TestController.prototype,
|
|
95
|
+
) as RouteMetadata[];
|
|
96
|
+
|
|
97
|
+
expect(routes).toBeDefined();
|
|
98
|
+
expect(routes[0].method).toBe('PUT');
|
|
99
|
+
expect(routes[0].path).toBe('/users/:id');
|
|
100
|
+
});
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
describe('@DELETE', () => {
|
|
104
|
+
test('should set route metadata for DELETE method', () => {
|
|
105
|
+
class TestController {
|
|
106
|
+
@DELETE('/users/:id')
|
|
107
|
+
public deleteUser(id: string): void {}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
const routes = Reflect.getMetadata(
|
|
111
|
+
ROUTE_METADATA_KEY,
|
|
112
|
+
TestController.prototype,
|
|
113
|
+
) as RouteMetadata[];
|
|
114
|
+
|
|
115
|
+
expect(routes).toBeDefined();
|
|
116
|
+
expect(routes[0].method).toBe('DELETE');
|
|
117
|
+
expect(routes[0].path).toBe('/users/:id');
|
|
118
|
+
});
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
describe('@PATCH', () => {
|
|
122
|
+
test('should set route metadata for PATCH method', () => {
|
|
123
|
+
class TestController {
|
|
124
|
+
@PATCH('/users/:id')
|
|
125
|
+
public patchUser(id: string): void {}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
const routes = Reflect.getMetadata(
|
|
129
|
+
ROUTE_METADATA_KEY,
|
|
130
|
+
TestController.prototype,
|
|
131
|
+
) as RouteMetadata[];
|
|
132
|
+
|
|
133
|
+
expect(routes).toBeDefined();
|
|
134
|
+
expect(routes[0].method).toBe('PATCH');
|
|
135
|
+
expect(routes[0].path).toBe('/users/:id');
|
|
136
|
+
});
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
describe('Multiple routes', () => {
|
|
140
|
+
test('should accumulate multiple routes on same controller', () => {
|
|
141
|
+
class TestController {
|
|
142
|
+
@GET('/items')
|
|
143
|
+
public getItems(): void {}
|
|
144
|
+
|
|
145
|
+
@POST('/items')
|
|
146
|
+
public createItem(): void {}
|
|
147
|
+
|
|
148
|
+
@GET('/items/:id')
|
|
149
|
+
public getItem(id: string): void {}
|
|
150
|
+
|
|
151
|
+
@PUT('/items/:id')
|
|
152
|
+
public updateItem(id: string): void {}
|
|
153
|
+
|
|
154
|
+
@DELETE('/items/:id')
|
|
155
|
+
public deleteItem(id: string): void {}
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
const routes = Reflect.getMetadata(
|
|
159
|
+
ROUTE_METADATA_KEY,
|
|
160
|
+
TestController.prototype,
|
|
161
|
+
) as RouteMetadata[];
|
|
162
|
+
|
|
163
|
+
expect(routes).toBeDefined();
|
|
164
|
+
expect(routes.length).toBe(5);
|
|
165
|
+
|
|
166
|
+
const methods = routes.map((r) => r.method);
|
|
167
|
+
expect(methods).toContain('GET');
|
|
168
|
+
expect(methods).toContain('POST');
|
|
169
|
+
expect(methods).toContain('PUT');
|
|
170
|
+
expect(methods).toContain('DELETE');
|
|
171
|
+
});
|
|
172
|
+
|
|
173
|
+
test('should preserve route order', () => {
|
|
174
|
+
class TestController {
|
|
175
|
+
@GET('/first')
|
|
176
|
+
public first(): void {}
|
|
177
|
+
|
|
178
|
+
@GET('/second')
|
|
179
|
+
public second(): void {}
|
|
180
|
+
|
|
181
|
+
@GET('/third')
|
|
182
|
+
public third(): void {}
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
const routes = Reflect.getMetadata(
|
|
186
|
+
ROUTE_METADATA_KEY,
|
|
187
|
+
TestController.prototype,
|
|
188
|
+
) as RouteMetadata[];
|
|
189
|
+
|
|
190
|
+
expect(routes[0].path).toBe('/first');
|
|
191
|
+
expect(routes[1].path).toBe('/second');
|
|
192
|
+
expect(routes[2].path).toBe('/third');
|
|
193
|
+
});
|
|
194
|
+
});
|
|
195
|
+
|
|
196
|
+
describe('With @Controller', () => {
|
|
197
|
+
test('should work with @Controller decorator', () => {
|
|
198
|
+
@Controller('/api')
|
|
199
|
+
class ApiController {
|
|
200
|
+
@GET('/data')
|
|
201
|
+
public getData(): void {}
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
const routes = Reflect.getMetadata(
|
|
205
|
+
ROUTE_METADATA_KEY,
|
|
206
|
+
ApiController.prototype,
|
|
207
|
+
) as RouteMetadata[];
|
|
208
|
+
|
|
209
|
+
expect(routes).toBeDefined();
|
|
210
|
+
expect(routes.length).toBe(1);
|
|
211
|
+
expect(routes[0].path).toBe('/data');
|
|
212
|
+
});
|
|
213
|
+
});
|
|
214
|
+
|
|
215
|
+
describe('Edge cases', () => {
|
|
216
|
+
test('should handle empty path', () => {
|
|
217
|
+
class TestController {
|
|
218
|
+
@GET('')
|
|
219
|
+
public getEmpty(): void {}
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
const routes = Reflect.getMetadata(
|
|
223
|
+
ROUTE_METADATA_KEY,
|
|
224
|
+
TestController.prototype,
|
|
225
|
+
) as RouteMetadata[];
|
|
226
|
+
|
|
227
|
+
expect(routes[0].path).toBe('');
|
|
228
|
+
});
|
|
229
|
+
|
|
230
|
+
test('should handle paths with query string patterns', () => {
|
|
231
|
+
class TestController {
|
|
232
|
+
@GET('/search')
|
|
233
|
+
public search(): void {}
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
const routes = Reflect.getMetadata(
|
|
237
|
+
ROUTE_METADATA_KEY,
|
|
238
|
+
TestController.prototype,
|
|
239
|
+
) as RouteMetadata[];
|
|
240
|
+
|
|
241
|
+
expect(routes[0].path).toBe('/search');
|
|
242
|
+
});
|
|
243
|
+
|
|
244
|
+
test('should store handler reference', () => {
|
|
245
|
+
class TestController {
|
|
246
|
+
@GET('/test')
|
|
247
|
+
public testMethod(): string {
|
|
248
|
+
return 'test';
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
const routes = Reflect.getMetadata(
|
|
253
|
+
ROUTE_METADATA_KEY,
|
|
254
|
+
TestController.prototype,
|
|
255
|
+
) as RouteMetadata[];
|
|
256
|
+
|
|
257
|
+
expect(routes[0].handler).toBe(TestController.prototype.testMethod);
|
|
258
|
+
});
|
|
259
|
+
});
|
|
260
|
+
});
|
|
@@ -0,0 +1,298 @@
|
|
|
1
|
+
import { describe, expect, test, beforeEach } from 'bun:test';
|
|
2
|
+
import 'reflect-metadata';
|
|
3
|
+
|
|
4
|
+
import { Router } from '../../src/router/router';
|
|
5
|
+
import { RouteRegistry } from '../../src/router/registry';
|
|
6
|
+
import { Context } from '../../src/core/context';
|
|
7
|
+
import { Container } from '../../src/di/container';
|
|
8
|
+
|
|
9
|
+
describe('Router', () => {
|
|
10
|
+
let router: Router;
|
|
11
|
+
|
|
12
|
+
beforeEach(() => {
|
|
13
|
+
router = new Router();
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
describe('register', () => {
|
|
17
|
+
test('should register GET route', () => {
|
|
18
|
+
router.get('/users', async () => new Response('users'));
|
|
19
|
+
const route = router.findRoute('GET', '/users');
|
|
20
|
+
expect(route).toBeDefined();
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
test('should register POST route', () => {
|
|
24
|
+
router.post('/users', async () => new Response('created'));
|
|
25
|
+
const route = router.findRoute('POST', '/users');
|
|
26
|
+
expect(route).toBeDefined();
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
test('should register PUT route', () => {
|
|
30
|
+
router.put('/users/:id', async () => new Response('updated'));
|
|
31
|
+
const route = router.findRoute('PUT', '/users/123');
|
|
32
|
+
expect(route).toBeDefined();
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
test('should register DELETE route', () => {
|
|
36
|
+
router.delete('/users/:id', async () => new Response('deleted'));
|
|
37
|
+
const route = router.findRoute('DELETE', '/users/456');
|
|
38
|
+
expect(route).toBeDefined();
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
test('should register PATCH route', () => {
|
|
42
|
+
router.patch('/users/:id', async () => new Response('patched'));
|
|
43
|
+
const route = router.findRoute('PATCH', '/users/789');
|
|
44
|
+
expect(route).toBeDefined();
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
test('should normalize path by removing trailing slash', () => {
|
|
48
|
+
router.get('/users/', async () => new Response('users'));
|
|
49
|
+
const route = router.findRoute('GET', '/users');
|
|
50
|
+
expect(route).toBeDefined();
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
test('should not remove trailing slash from root path', () => {
|
|
54
|
+
router.get('/', async () => new Response('root'));
|
|
55
|
+
const route = router.findRoute('GET', '/');
|
|
56
|
+
expect(route).toBeDefined();
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
test('should register route with middlewares', () => {
|
|
60
|
+
const middleware = async (ctx: Context, next: () => Promise<Response>) => next();
|
|
61
|
+
router.get('/protected', async () => new Response('ok'), [middleware]);
|
|
62
|
+
const route = router.findRoute('GET', '/protected');
|
|
63
|
+
expect(route).toBeDefined();
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
test('should register route with controller info', () => {
|
|
67
|
+
class TestController {}
|
|
68
|
+
router.register('GET', '/test', async () => new Response('test'), [], TestController, 'testMethod');
|
|
69
|
+
const route = router.findRoute('GET', '/test');
|
|
70
|
+
expect(route?.controllerClass).toBe(TestController);
|
|
71
|
+
expect(route?.methodName).toBe('testMethod');
|
|
72
|
+
});
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
describe('findRoute', () => {
|
|
76
|
+
test('should find static route', () => {
|
|
77
|
+
router.get('/api/health', async () => new Response('ok'));
|
|
78
|
+
const route = router.findRoute('GET', '/api/health');
|
|
79
|
+
expect(route).toBeDefined();
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
test('should find dynamic route with params', () => {
|
|
83
|
+
router.get('/users/:id', async () => new Response('user'));
|
|
84
|
+
const route = router.findRoute('GET', '/users/123');
|
|
85
|
+
expect(route).toBeDefined();
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
test('should return undefined for non-existent route', () => {
|
|
89
|
+
const route = router.findRoute('GET', '/not-found');
|
|
90
|
+
expect(route).toBeUndefined();
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
test('should return undefined for wrong method', () => {
|
|
94
|
+
router.get('/users', async () => new Response('users'));
|
|
95
|
+
const route = router.findRoute('POST', '/users');
|
|
96
|
+
expect(route).toBeUndefined();
|
|
97
|
+
});
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
describe('findRouteWithMatch', () => {
|
|
101
|
+
test('should return route and match for static route', () => {
|
|
102
|
+
router.get('/api/test', async () => new Response('test'));
|
|
103
|
+
const result = router.findRouteWithMatch('GET', '/api/test');
|
|
104
|
+
expect(result).toBeDefined();
|
|
105
|
+
expect(result?.match.matched).toBe(true);
|
|
106
|
+
expect(result?.match.params).toEqual({});
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
test('should return route and match with params', () => {
|
|
110
|
+
router.get('/users/:id/posts/:postId', async () => new Response('post'));
|
|
111
|
+
const result = router.findRouteWithMatch('GET', '/users/123/posts/456');
|
|
112
|
+
expect(result).toBeDefined();
|
|
113
|
+
expect(result?.match.matched).toBe(true);
|
|
114
|
+
expect(result?.match.params.id).toBe('123');
|
|
115
|
+
expect(result?.match.params.postId).toBe('456');
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
test('should cache match results', () => {
|
|
119
|
+
router.get('/cached', async () => new Response('cached'));
|
|
120
|
+
|
|
121
|
+
const result1 = router.findRouteWithMatch('GET', '/cached');
|
|
122
|
+
const result2 = router.findRouteWithMatch('GET', '/cached');
|
|
123
|
+
|
|
124
|
+
// Should return same cached result
|
|
125
|
+
expect(result1).toBe(result2);
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
test('should clear cache when new route is registered', () => {
|
|
129
|
+
router.get('/first', async () => new Response('first'));
|
|
130
|
+
router.findRouteWithMatch('GET', '/first'); // Populate cache
|
|
131
|
+
|
|
132
|
+
router.get('/second', async () => new Response('second'));
|
|
133
|
+
|
|
134
|
+
// Cache should be cleared, so this should work
|
|
135
|
+
const result = router.findRouteWithMatch('GET', '/second');
|
|
136
|
+
expect(result).toBeDefined();
|
|
137
|
+
});
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
describe('handle', () => {
|
|
141
|
+
test('should handle request and return response', async () => {
|
|
142
|
+
router.get('/api/data', async () => new Response('data'));
|
|
143
|
+
|
|
144
|
+
const request = new Request('http://localhost/api/data');
|
|
145
|
+
const context = new Context(request, new Container());
|
|
146
|
+
|
|
147
|
+
const response = await router.handle(context);
|
|
148
|
+
expect(response).toBeDefined();
|
|
149
|
+
expect(await response?.text()).toBe('data');
|
|
150
|
+
});
|
|
151
|
+
|
|
152
|
+
test('should set params on context', async () => {
|
|
153
|
+
router.get('/users/:id', async (ctx) => new Response(`User ${ctx.params.id}`));
|
|
154
|
+
|
|
155
|
+
const request = new Request('http://localhost/users/42');
|
|
156
|
+
const context = new Context(request, new Container());
|
|
157
|
+
|
|
158
|
+
const response = await router.handle(context);
|
|
159
|
+
expect(await response?.text()).toBe('User 42');
|
|
160
|
+
});
|
|
161
|
+
|
|
162
|
+
test('should return undefined for no match', async () => {
|
|
163
|
+
const request = new Request('http://localhost/not-found');
|
|
164
|
+
const context = new Context(request, new Container());
|
|
165
|
+
|
|
166
|
+
const response = await router.handle(context);
|
|
167
|
+
expect(response).toBeUndefined();
|
|
168
|
+
});
|
|
169
|
+
|
|
170
|
+
test('should set routeHandler on context', async () => {
|
|
171
|
+
class MyController {}
|
|
172
|
+
router.register('GET', '/test', async () => new Response('test'), [], MyController, 'testMethod');
|
|
173
|
+
|
|
174
|
+
const request = new Request('http://localhost/test');
|
|
175
|
+
const context = new Context(request, new Container());
|
|
176
|
+
|
|
177
|
+
await router.handle(context);
|
|
178
|
+
expect((context as any).routeHandler).toBeDefined();
|
|
179
|
+
expect((context as any).routeHandler.controller).toBe(MyController);
|
|
180
|
+
expect((context as any).routeHandler.method).toBe('testMethod');
|
|
181
|
+
});
|
|
182
|
+
});
|
|
183
|
+
|
|
184
|
+
describe('preHandle', () => {
|
|
185
|
+
test('should set params without executing handler', async () => {
|
|
186
|
+
let handlerCalled = false;
|
|
187
|
+
router.get('/users/:id', async () => {
|
|
188
|
+
handlerCalled = true;
|
|
189
|
+
return new Response('user');
|
|
190
|
+
});
|
|
191
|
+
|
|
192
|
+
const request = new Request('http://localhost/users/123');
|
|
193
|
+
const context = new Context(request, new Container());
|
|
194
|
+
|
|
195
|
+
await router.preHandle(context);
|
|
196
|
+
|
|
197
|
+
expect(context.params.id).toBe('123');
|
|
198
|
+
expect(handlerCalled).toBe(false);
|
|
199
|
+
});
|
|
200
|
+
|
|
201
|
+
test('should set routeHandler on context', async () => {
|
|
202
|
+
class TestController {}
|
|
203
|
+
router.register('GET', '/api/test', async () => new Response('test'), [], TestController, 'handle');
|
|
204
|
+
|
|
205
|
+
const request = new Request('http://localhost/api/test');
|
|
206
|
+
const context = new Context(request, new Container());
|
|
207
|
+
|
|
208
|
+
await router.preHandle(context);
|
|
209
|
+
|
|
210
|
+
expect((context as any).routeHandler).toBeDefined();
|
|
211
|
+
expect((context as any).routeHandler.controller).toBe(TestController);
|
|
212
|
+
});
|
|
213
|
+
|
|
214
|
+
test('should not set routeHandler for route without controller', async () => {
|
|
215
|
+
router.get('/simple', async () => new Response('simple'));
|
|
216
|
+
|
|
217
|
+
const request = new Request('http://localhost/simple');
|
|
218
|
+
const context = new Context(request, new Container());
|
|
219
|
+
|
|
220
|
+
await router.preHandle(context);
|
|
221
|
+
|
|
222
|
+
expect((context as any).routeHandler).toBeUndefined();
|
|
223
|
+
});
|
|
224
|
+
});
|
|
225
|
+
|
|
226
|
+
describe('getRoutes', () => {
|
|
227
|
+
test('should return all registered routes', () => {
|
|
228
|
+
router.get('/a', async () => new Response('a'));
|
|
229
|
+
router.post('/b', async () => new Response('b'));
|
|
230
|
+
router.put('/c', async () => new Response('c'));
|
|
231
|
+
|
|
232
|
+
const routes = router.getRoutes();
|
|
233
|
+
expect(routes.length).toBe(3);
|
|
234
|
+
});
|
|
235
|
+
});
|
|
236
|
+
|
|
237
|
+
describe('clear', () => {
|
|
238
|
+
test('should clear all routes', () => {
|
|
239
|
+
router.get('/a', async () => new Response('a'));
|
|
240
|
+
router.get('/b', async () => new Response('b'));
|
|
241
|
+
|
|
242
|
+
router.clear();
|
|
243
|
+
|
|
244
|
+
expect(router.getRoutes().length).toBe(0);
|
|
245
|
+
expect(router.findRoute('GET', '/a')).toBeUndefined();
|
|
246
|
+
});
|
|
247
|
+
});
|
|
248
|
+
});
|
|
249
|
+
|
|
250
|
+
describe('RouteRegistry', () => {
|
|
251
|
+
let registry: RouteRegistry;
|
|
252
|
+
|
|
253
|
+
beforeEach(() => {
|
|
254
|
+
registry = RouteRegistry.getInstance();
|
|
255
|
+
registry.clear();
|
|
256
|
+
});
|
|
257
|
+
|
|
258
|
+
test('should be a singleton', () => {
|
|
259
|
+
const instance1 = RouteRegistry.getInstance();
|
|
260
|
+
const instance2 = RouteRegistry.getInstance();
|
|
261
|
+
expect(instance1).toBe(instance2);
|
|
262
|
+
});
|
|
263
|
+
|
|
264
|
+
test('should register routes via convenience methods', () => {
|
|
265
|
+
registry.get('/get', async () => new Response('get'));
|
|
266
|
+
registry.post('/post', async () => new Response('post'));
|
|
267
|
+
registry.put('/put', async () => new Response('put'));
|
|
268
|
+
registry.delete('/delete', async () => new Response('delete'));
|
|
269
|
+
registry.patch('/patch', async () => new Response('patch'));
|
|
270
|
+
|
|
271
|
+
const router = registry.getRouter();
|
|
272
|
+
expect(router.findRoute('GET', '/get')).toBeDefined();
|
|
273
|
+
expect(router.findRoute('POST', '/post')).toBeDefined();
|
|
274
|
+
expect(router.findRoute('PUT', '/put')).toBeDefined();
|
|
275
|
+
expect(router.findRoute('DELETE', '/delete')).toBeDefined();
|
|
276
|
+
expect(router.findRoute('PATCH', '/patch')).toBeDefined();
|
|
277
|
+
});
|
|
278
|
+
|
|
279
|
+
test('should register route with full options', () => {
|
|
280
|
+
class TestController {}
|
|
281
|
+
const middleware = async (ctx: Context, next: () => Promise<Response>) => next();
|
|
282
|
+
|
|
283
|
+
registry.register('GET', '/full', async () => new Response('full'), [middleware], TestController, 'method');
|
|
284
|
+
|
|
285
|
+
const router = registry.getRouter();
|
|
286
|
+
const route = router.findRoute('GET', '/full');
|
|
287
|
+
expect(route).toBeDefined();
|
|
288
|
+
expect(route?.controllerClass).toBe(TestController);
|
|
289
|
+
});
|
|
290
|
+
|
|
291
|
+
test('should clear all routes', () => {
|
|
292
|
+
registry.get('/test', async () => new Response('test'));
|
|
293
|
+
registry.clear();
|
|
294
|
+
|
|
295
|
+
const router = registry.getRouter();
|
|
296
|
+
expect(router.findRoute('GET', '/test')).toBeUndefined();
|
|
297
|
+
});
|
|
298
|
+
});
|