@dangao/bun-server 1.0.1 → 1.1.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.
- package/dist/controller/controller.d.ts +1 -1
- package/dist/controller/controller.d.ts.map +1 -1
- package/dist/core/application.d.ts.map +1 -1
- package/dist/database/database-extension.d.ts.map +1 -1
- package/dist/database/database-module.d.ts.map +1 -1
- package/dist/database/orm/transaction-decorator.d.ts +1 -0
- package/dist/database/orm/transaction-decorator.d.ts.map +1 -1
- package/dist/database/orm/transaction-interceptor.d.ts +12 -3
- package/dist/database/orm/transaction-interceptor.d.ts.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +678 -310
- package/dist/interceptor/base-interceptor.d.ts +94 -0
- package/dist/interceptor/base-interceptor.d.ts.map +1 -0
- package/dist/interceptor/builtin/cache-interceptor.d.ts +69 -0
- package/dist/interceptor/builtin/cache-interceptor.d.ts.map +1 -0
- package/dist/interceptor/builtin/index.d.ts +4 -0
- package/dist/interceptor/builtin/index.d.ts.map +1 -0
- package/dist/interceptor/builtin/log-interceptor.d.ts +56 -0
- package/dist/interceptor/builtin/log-interceptor.d.ts.map +1 -0
- package/dist/interceptor/builtin/permission-interceptor.d.ts +70 -0
- package/dist/interceptor/builtin/permission-interceptor.d.ts.map +1 -0
- package/dist/interceptor/index.d.ts +7 -0
- package/dist/interceptor/index.d.ts.map +1 -0
- package/dist/interceptor/interceptor-chain.d.ts +22 -0
- package/dist/interceptor/interceptor-chain.d.ts.map +1 -0
- package/dist/interceptor/interceptor-registry.d.ts +59 -0
- package/dist/interceptor/interceptor-registry.d.ts.map +1 -0
- package/dist/interceptor/metadata.d.ts +12 -0
- package/dist/interceptor/metadata.d.ts.map +1 -0
- package/dist/interceptor/types.d.ts +42 -0
- package/dist/interceptor/types.d.ts.map +1 -0
- package/dist/middleware/decorators.d.ts +2 -1
- package/dist/middleware/decorators.d.ts.map +1 -1
- package/dist/router/decorators.d.ts.map +1 -1
- package/dist/router/registry.d.ts +2 -1
- package/dist/router/registry.d.ts.map +1 -1
- package/dist/router/route.d.ts +3 -2
- package/dist/router/route.d.ts.map +1 -1
- package/dist/router/router.d.ts +2 -1
- package/dist/router/router.d.ts.map +1 -1
- package/dist/websocket/decorators.d.ts +2 -1
- package/dist/websocket/decorators.d.ts.map +1 -1
- package/package.json +5 -3
- package/readme.md +163 -2
- package/src/auth/controller.ts +148 -0
- package/src/auth/decorators.ts +81 -0
- package/src/auth/index.ts +12 -0
- package/src/auth/jwt.ts +169 -0
- package/src/auth/oauth2.ts +244 -0
- package/src/auth/types.ts +248 -0
- package/src/cache/cache-module.ts +67 -0
- package/src/cache/decorators.ts +202 -0
- package/src/cache/index.ts +27 -0
- package/src/cache/service.ts +151 -0
- package/src/cache/types.ts +420 -0
- package/src/config/config-module.ts +76 -0
- package/src/config/index.ts +8 -0
- package/src/config/service.ts +93 -0
- package/src/config/types.ts +27 -0
- package/src/controller/controller.ts +278 -0
- package/src/controller/decorators.ts +84 -0
- package/src/controller/index.ts +7 -0
- package/src/controller/metadata.ts +27 -0
- package/src/controller/param-binder.ts +157 -0
- package/src/core/application.ts +239 -0
- package/src/core/context.ts +228 -0
- package/src/core/index.ts +4 -0
- package/src/core/server.ts +128 -0
- package/src/core/types.ts +2 -0
- package/src/database/connection-manager.ts +239 -0
- package/src/database/connection-pool.ts +322 -0
- package/src/database/database-extension.ts +83 -0
- package/src/database/database-module.ts +121 -0
- package/src/database/health-indicator.ts +51 -0
- package/src/database/index.ts +47 -0
- package/src/database/orm/decorators.ts +155 -0
- package/src/database/orm/drizzle-repository.ts +39 -0
- package/src/database/orm/index.ts +23 -0
- package/src/database/orm/repository-decorator.ts +39 -0
- package/src/database/orm/repository.ts +103 -0
- package/src/database/orm/service.ts +49 -0
- package/src/database/orm/transaction-decorator.ts +76 -0
- package/src/database/orm/transaction-interceptor.ts +263 -0
- package/src/database/orm/transaction-manager.ts +276 -0
- package/src/database/orm/transaction-types.ts +140 -0
- package/src/database/orm/types.ts +99 -0
- package/src/database/service.ts +221 -0
- package/src/database/types.ts +171 -0
- package/src/di/container.ts +398 -0
- package/src/di/decorators.ts +228 -0
- package/src/di/index.ts +4 -0
- package/src/di/module-registry.ts +188 -0
- package/src/di/module.ts +65 -0
- package/src/di/types.ts +67 -0
- package/src/error/error-codes.ts +222 -0
- package/src/error/filter.ts +43 -0
- package/src/error/handler.ts +66 -0
- package/src/error/http-exception.ts +115 -0
- package/src/error/i18n.ts +217 -0
- package/src/error/index.ts +16 -0
- package/src/extensions/index.ts +5 -0
- package/src/extensions/logger-extension.ts +31 -0
- package/src/extensions/logger-module.ts +69 -0
- package/src/extensions/types.ts +14 -0
- package/src/files/index.ts +5 -0
- package/src/files/static-middleware.ts +53 -0
- package/src/files/storage.ts +67 -0
- package/src/files/types.ts +33 -0
- package/src/files/upload-middleware.ts +45 -0
- package/src/health/controller.ts +76 -0
- package/src/health/health-module.ts +51 -0
- package/src/health/index.ts +12 -0
- package/src/health/types.ts +28 -0
- package/src/index.ts +292 -0
- package/src/interceptor/base-interceptor.ts +203 -0
- package/src/interceptor/builtin/cache-interceptor.ts +169 -0
- package/src/interceptor/builtin/index.ts +28 -0
- package/src/interceptor/builtin/log-interceptor.ts +178 -0
- package/src/interceptor/builtin/permission-interceptor.ts +173 -0
- package/src/interceptor/index.ts +26 -0
- package/src/interceptor/interceptor-chain.ts +79 -0
- package/src/interceptor/interceptor-registry.ts +132 -0
- package/src/interceptor/metadata.ts +40 -0
- package/src/interceptor/types.ts +52 -0
- package/src/metrics/collector.ts +209 -0
- package/src/metrics/controller.ts +40 -0
- package/src/metrics/index.ts +15 -0
- package/src/metrics/metrics-module.ts +58 -0
- package/src/metrics/middleware.ts +46 -0
- package/src/metrics/prometheus.ts +79 -0
- package/src/metrics/types.ts +103 -0
- package/src/middleware/builtin/cors.ts +60 -0
- package/src/middleware/builtin/error-handler.ts +90 -0
- package/src/middleware/builtin/file-upload.ts +42 -0
- package/src/middleware/builtin/index.ts +14 -0
- package/src/middleware/builtin/logger.ts +91 -0
- package/src/middleware/builtin/rate-limit.ts +252 -0
- package/src/middleware/builtin/static-file.ts +88 -0
- package/src/middleware/decorators.ts +92 -0
- package/src/middleware/index.ts +11 -0
- package/src/middleware/middleware.ts +13 -0
- package/src/middleware/pipeline.ts +93 -0
- package/src/queue/decorators.ts +110 -0
- package/src/queue/index.ts +26 -0
- package/src/queue/queue-module.ts +64 -0
- package/src/queue/service.ts +302 -0
- package/src/queue/types.ts +341 -0
- package/src/request/body-parser.ts +133 -0
- package/src/request/file-handler.ts +46 -0
- package/src/request/index.ts +5 -0
- package/src/request/request.ts +107 -0
- package/src/request/response.ts +150 -0
- package/src/router/decorators.ts +123 -0
- package/src/router/index.ts +6 -0
- package/src/router/registry.ts +99 -0
- package/src/router/route.ts +141 -0
- package/src/router/router.ts +242 -0
- package/src/router/types.ts +27 -0
- package/src/security/access-decision-manager.ts +34 -0
- package/src/security/authentication-manager.ts +47 -0
- package/src/security/context.ts +92 -0
- package/src/security/filter.ts +162 -0
- package/src/security/index.ts +8 -0
- package/src/security/providers/index.ts +3 -0
- package/src/security/providers/jwt-provider.ts +60 -0
- package/src/security/providers/oauth2-provider.ts +70 -0
- package/src/security/security-module.ts +145 -0
- package/src/security/types.ts +165 -0
- package/src/session/decorators.ts +45 -0
- package/src/session/index.ts +19 -0
- package/src/session/middleware.ts +143 -0
- package/src/session/service.ts +218 -0
- package/src/session/session-module.ts +69 -0
- package/src/session/types.ts +373 -0
- package/src/swagger/decorators.ts +133 -0
- package/src/swagger/generator.ts +234 -0
- package/src/swagger/index.ts +7 -0
- package/src/swagger/swagger-extension.ts +41 -0
- package/src/swagger/swagger-module.ts +83 -0
- package/src/swagger/types.ts +188 -0
- package/src/swagger/ui.ts +98 -0
- package/src/testing/harness.ts +96 -0
- package/src/validation/decorators.ts +95 -0
- package/src/validation/errors.ts +28 -0
- package/src/validation/index.ts +14 -0
- package/src/validation/types.ts +35 -0
- package/src/validation/validator.ts +63 -0
- package/src/websocket/decorators.ts +53 -0
- package/src/websocket/index.ts +12 -0
- package/src/websocket/registry.ts +133 -0
- package/tests/cache/cache-module.test.ts +212 -0
- package/tests/config/config-module.test.ts +151 -0
- package/tests/controller/controller.test.ts +189 -0
- package/tests/controller/path-combination.test.ts +207 -0
- package/tests/core/application.test.ts +57 -0
- package/tests/core/context-body.test.ts +44 -0
- package/tests/core/context.test.ts +86 -0
- package/tests/core/edge-cases.test.ts +432 -0
- package/tests/database/database-module.test.ts +385 -0
- package/tests/database/orm.test.ts +164 -0
- package/tests/database/postgres-mysql-integration.test.ts +395 -0
- package/tests/database/transaction.test.ts +238 -0
- package/tests/di/container.test.ts +264 -0
- package/tests/di/module.test.ts +128 -0
- package/tests/error/error-codes.test.ts +121 -0
- package/tests/error/error-handler.test.ts +68 -0
- package/tests/error/error-handling.test.ts +254 -0
- package/tests/error/http-exception.test.ts +37 -0
- package/tests/error/i18n-integration.test.ts +175 -0
- package/tests/extensions/logger-extension.test.ts +40 -0
- package/tests/files/static-middleware.test.ts +67 -0
- package/tests/files/upload-middleware.test.ts +43 -0
- package/tests/health/health-module.test.ts +116 -0
- package/tests/integration/application-router.test.ts +85 -0
- package/tests/integration/body-parsing.test.ts +88 -0
- package/tests/integration/cache-e2e.test.ts +114 -0
- package/tests/integration/oauth2-e2e.test.ts +615 -0
- package/tests/integration/session-e2e.test.ts +207 -0
- package/tests/interceptor/builtin/cache-interceptor.test.ts +137 -0
- package/tests/interceptor/builtin/permission-interceptor.test.ts +182 -0
- package/tests/interceptor/interceptor-advanced-integration.test.ts +592 -0
- package/tests/interceptor/interceptor-arg-modification.test.ts +76 -0
- package/tests/interceptor/interceptor-chain.test.ts +199 -0
- package/tests/interceptor/interceptor-integration.test.ts +230 -0
- package/tests/interceptor/interceptor-registry.test.ts +200 -0
- package/tests/interceptor/perf/interceptor-performance.test.ts +341 -0
- package/tests/metrics/metrics-module.test.ts +178 -0
- package/tests/middleware/builtin.test.ts +206 -0
- package/tests/middleware/file-upload.test.ts +41 -0
- package/tests/middleware/middleware.test.ts +120 -0
- package/tests/middleware/pipeline.test.ts +72 -0
- package/tests/middleware/rate-limit.test.ts +314 -0
- package/tests/middleware/static-file.test.ts +62 -0
- package/tests/perf/harness.test.ts +48 -0
- package/tests/perf/optimization.test.ts +183 -0
- package/tests/perf/regression.test.ts +120 -0
- package/tests/queue/queue-module.test.ts +217 -0
- package/tests/request/body-parser.test.ts +96 -0
- package/tests/request/response.test.ts +99 -0
- package/tests/router/decorators.test.ts +46 -0
- package/tests/router/registry.test.ts +51 -0
- package/tests/router/route.test.ts +71 -0
- package/tests/router/router-normalization.test.ts +106 -0
- package/tests/router/router.test.ts +133 -0
- package/tests/security/access-decision-manager.test.ts +84 -0
- package/tests/security/authentication-manager.test.ts +81 -0
- package/tests/security/context.test.ts +302 -0
- package/tests/security/filter.test.ts +225 -0
- package/tests/security/jwt-provider.test.ts +106 -0
- package/tests/security/oauth2-provider.test.ts +269 -0
- package/tests/security/security-module.test.ts +143 -0
- package/tests/session/session-module.test.ts +307 -0
- package/tests/stress/di-stress.test.ts +30 -0
- package/tests/swagger/decorators.test.ts +153 -0
- package/tests/swagger/generator.test.ts +202 -0
- package/tests/swagger/swagger-extension.test.ts +72 -0
- package/tests/swagger/swagger-module.test.ts +79 -0
- package/tests/utils/test-port.ts +10 -0
- package/tests/validation/controller-validation.test.ts +64 -0
- package/tests/validation/validation.test.ts +42 -0
- package/tests/websocket/gateway.test.ts +68 -0
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
import 'reflect-metadata';
|
|
2
|
+
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, POST } from '../../src/router/decorators';
|
|
6
|
+
import { Param } from '../../src/controller/decorators';
|
|
7
|
+
import { RouteRegistry } from '../../src/router/registry';
|
|
8
|
+
import { getTestPort } from '../utils/test-port';
|
|
9
|
+
|
|
10
|
+
describe('Controller Path Combination', () => {
|
|
11
|
+
let app: Application;
|
|
12
|
+
let port: number;
|
|
13
|
+
|
|
14
|
+
beforeEach(() => {
|
|
15
|
+
port = getTestPort();
|
|
16
|
+
app = new Application({ port });
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
afterEach(async () => {
|
|
20
|
+
if (app) {
|
|
21
|
+
await app.stop();
|
|
22
|
+
}
|
|
23
|
+
RouteRegistry.getInstance().clear();
|
|
24
|
+
ControllerRegistry.getInstance().clear();
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
test('should correctly combine base path with method path starting with /', async () => {
|
|
28
|
+
@Controller('/api/products')
|
|
29
|
+
class ProductController {
|
|
30
|
+
@GET('/')
|
|
31
|
+
public listProducts() {
|
|
32
|
+
return { products: ['product1', 'product2'] };
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
@GET('/:id')
|
|
36
|
+
public getProduct(@Param('id') id: string) {
|
|
37
|
+
return { id, name: `Product ${id}` };
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
app.registerController(ProductController);
|
|
42
|
+
await app.listen();
|
|
43
|
+
|
|
44
|
+
// 应该访问 /api/products,而不是 /
|
|
45
|
+
const response = await fetch(`http://localhost:${port}/api/products`);
|
|
46
|
+
expect(response.status).toBe(200);
|
|
47
|
+
const data = await response.json();
|
|
48
|
+
expect(data.products).toBeDefined();
|
|
49
|
+
|
|
50
|
+
// 访问 / 应该返回 404,而不是访问到 listProducts
|
|
51
|
+
const rootResponse = await fetch(`http://localhost:${port}/`);
|
|
52
|
+
expect(rootResponse.status).toBe(404);
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
test('should correctly combine base path with empty method path', async () => {
|
|
56
|
+
@Controller('/api/users')
|
|
57
|
+
class UserController {
|
|
58
|
+
@GET('')
|
|
59
|
+
public listUsers() {
|
|
60
|
+
return { users: ['user1', 'user2'] };
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
app.registerController(UserController);
|
|
65
|
+
await app.listen();
|
|
66
|
+
|
|
67
|
+
const response = await fetch(`http://localhost:${port}/api/users`);
|
|
68
|
+
expect(response.status).toBe(200);
|
|
69
|
+
const data = await response.json();
|
|
70
|
+
expect(data.users).toBeDefined();
|
|
71
|
+
|
|
72
|
+
// 访问 / 应该返回 404
|
|
73
|
+
const rootResponse = await fetch(`http://localhost:${port}/`);
|
|
74
|
+
expect(rootResponse.status).toBe(404);
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
test('should correctly combine base path with method path without leading /', async () => {
|
|
78
|
+
@Controller('/api/orders')
|
|
79
|
+
class OrderController {
|
|
80
|
+
@GET('list')
|
|
81
|
+
public listOrders() {
|
|
82
|
+
return { orders: ['order1', 'order2'] };
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
app.registerController(OrderController);
|
|
87
|
+
await app.listen();
|
|
88
|
+
|
|
89
|
+
const response = await fetch(`http://localhost:${port}/api/orders/list`);
|
|
90
|
+
expect(response.status).toBe(200);
|
|
91
|
+
const data = await response.json();
|
|
92
|
+
expect(data.orders).toBeDefined();
|
|
93
|
+
|
|
94
|
+
// 访问 /api/orders 应该返回 404
|
|
95
|
+
const baseResponse = await fetch(`http://localhost:${port}/api/orders`);
|
|
96
|
+
expect(baseResponse.status).toBe(404);
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
test('should handle root controller with method path /', async () => {
|
|
100
|
+
@Controller('')
|
|
101
|
+
class RootController {
|
|
102
|
+
@GET('/')
|
|
103
|
+
public root() {
|
|
104
|
+
return { message: 'root' };
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
app.registerController(RootController);
|
|
109
|
+
await app.listen();
|
|
110
|
+
|
|
111
|
+
const response = await fetch(`http://localhost:${port}/`);
|
|
112
|
+
expect(response.status).toBe(200);
|
|
113
|
+
const data = await response.json();
|
|
114
|
+
expect(data.message).toBe('root');
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
test('should handle root controller with empty method path', async () => {
|
|
118
|
+
@Controller('/')
|
|
119
|
+
class RootController {
|
|
120
|
+
@GET('')
|
|
121
|
+
public root() {
|
|
122
|
+
return { message: 'root' };
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
app.registerController(RootController);
|
|
127
|
+
await app.listen();
|
|
128
|
+
|
|
129
|
+
const response = await fetch(`http://localhost:${port}/`);
|
|
130
|
+
expect(response.status).toBe(200);
|
|
131
|
+
const data = await response.json();
|
|
132
|
+
expect(data.message).toBe('root');
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
test('should not allow / to match when controller has base path', async () => {
|
|
136
|
+
@Controller('/api/products')
|
|
137
|
+
class ProductController {
|
|
138
|
+
@GET('/')
|
|
139
|
+
public listProducts() {
|
|
140
|
+
return { products: ['product1'] };
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
@Controller('/')
|
|
145
|
+
class RootController {
|
|
146
|
+
@GET('/')
|
|
147
|
+
public root() {
|
|
148
|
+
return { message: 'root' };
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
app.registerController(ProductController);
|
|
153
|
+
app.registerController(RootController);
|
|
154
|
+
await app.listen();
|
|
155
|
+
|
|
156
|
+
// /api/products 应该访问 ProductController
|
|
157
|
+
const productsResponse = await fetch(`http://localhost:${port}/api/products`);
|
|
158
|
+
expect(productsResponse.status).toBe(200);
|
|
159
|
+
const productsData = await productsResponse.json();
|
|
160
|
+
expect(productsData.products).toBeDefined();
|
|
161
|
+
|
|
162
|
+
// / 应该访问 RootController
|
|
163
|
+
const rootResponse = await fetch(`http://localhost:${port}/`);
|
|
164
|
+
expect(rootResponse.status).toBe(200);
|
|
165
|
+
const rootData = await rootResponse.json();
|
|
166
|
+
expect(rootData.message).toBe('root');
|
|
167
|
+
});
|
|
168
|
+
|
|
169
|
+
test('should handle multiple controllers with different base paths', async () => {
|
|
170
|
+
@Controller('/api/products')
|
|
171
|
+
class ProductController {
|
|
172
|
+
@GET('/')
|
|
173
|
+
public listProducts() {
|
|
174
|
+
return { source: 'products' };
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
@Controller('/api/users')
|
|
179
|
+
class UserController {
|
|
180
|
+
@GET('/')
|
|
181
|
+
public listUsers() {
|
|
182
|
+
return { source: 'users' };
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
app.registerController(ProductController);
|
|
187
|
+
app.registerController(UserController);
|
|
188
|
+
await app.listen();
|
|
189
|
+
|
|
190
|
+
// 访问 /api/products 应该返回 products
|
|
191
|
+
const productsResponse = await fetch(`http://localhost:${port}/api/products`);
|
|
192
|
+
expect(productsResponse.status).toBe(200);
|
|
193
|
+
const productsData = await productsResponse.json();
|
|
194
|
+
expect(productsData.source).toBe('products');
|
|
195
|
+
|
|
196
|
+
// 访问 /api/users 应该返回 users
|
|
197
|
+
const usersResponse = await fetch(`http://localhost:${port}/api/users`);
|
|
198
|
+
expect(usersResponse.status).toBe(200);
|
|
199
|
+
const usersData = await usersResponse.json();
|
|
200
|
+
expect(usersData.source).toBe('users');
|
|
201
|
+
|
|
202
|
+
// 访问 / 应该返回 404
|
|
203
|
+
const rootResponse = await fetch(`http://localhost:${port}/`);
|
|
204
|
+
expect(rootResponse.status).toBe(404);
|
|
205
|
+
});
|
|
206
|
+
});
|
|
207
|
+
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { describe, expect, test, afterEach } from 'bun:test';
|
|
2
|
+
import { Application } from '../../src/core/application';
|
|
3
|
+
import { getTestPort } from '../utils/test-port';
|
|
4
|
+
|
|
5
|
+
describe('Application', () => {
|
|
6
|
+
let app: Application;
|
|
7
|
+
|
|
8
|
+
afterEach(async () => {
|
|
9
|
+
if (app) {
|
|
10
|
+
await app.stop();
|
|
11
|
+
}
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
test('should create application instance', () => {
|
|
15
|
+
app = new Application();
|
|
16
|
+
expect(app).toBeInstanceOf(Application);
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
test('should create application with options', () => {
|
|
20
|
+
const port = getTestPort();
|
|
21
|
+
app = new Application({ port });
|
|
22
|
+
expect(app).toBeInstanceOf(Application);
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
test('should start server', async () => {
|
|
26
|
+
const port = getTestPort();
|
|
27
|
+
app = new Application({ port });
|
|
28
|
+
await app.listen();
|
|
29
|
+
|
|
30
|
+
const server = app.getServer();
|
|
31
|
+
expect(server).toBeDefined();
|
|
32
|
+
expect(server?.isRunning()).toBe(true);
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
test('should stop server', async () => {
|
|
36
|
+
const port = getTestPort();
|
|
37
|
+
app = new Application({ port });
|
|
38
|
+
await app.listen();
|
|
39
|
+
await app.stop();
|
|
40
|
+
|
|
41
|
+
const server = app.getServer();
|
|
42
|
+
expect(server?.isRunning()).toBe(false);
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
test('should handle request with 404 by default', async () => {
|
|
46
|
+
const port = getTestPort();
|
|
47
|
+
app = new Application({ port });
|
|
48
|
+
await app.listen();
|
|
49
|
+
|
|
50
|
+
const response = await fetch(`http://localhost:${port}/api/users`);
|
|
51
|
+
expect(response.status).toBe(404);
|
|
52
|
+
|
|
53
|
+
const data = await response.json();
|
|
54
|
+
expect(data.error).toBe('Not Found');
|
|
55
|
+
});
|
|
56
|
+
});
|
|
57
|
+
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { describe, expect, test } from 'bun:test';
|
|
2
|
+
import { Context } from '../../src/core/context';
|
|
3
|
+
|
|
4
|
+
describe('Context Body Parsing', () => {
|
|
5
|
+
test('should parse JSON body', async () => {
|
|
6
|
+
const request = new Request('http://localhost:3000/api/users', {
|
|
7
|
+
method: 'POST',
|
|
8
|
+
headers: { 'Content-Type': 'application/json' },
|
|
9
|
+
body: JSON.stringify({ name: 'John', age: 30 }),
|
|
10
|
+
});
|
|
11
|
+
|
|
12
|
+
const context = new Context(request);
|
|
13
|
+
const body = await context.getBody();
|
|
14
|
+
|
|
15
|
+
expect(body).toEqual({ name: 'John', age: 30 });
|
|
16
|
+
expect(context.body).toEqual({ name: 'John', age: 30 });
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
test('should parse body only once', async () => {
|
|
20
|
+
const request = new Request('http://localhost:3000/api/users', {
|
|
21
|
+
method: 'POST',
|
|
22
|
+
headers: { 'Content-Type': 'application/json' },
|
|
23
|
+
body: JSON.stringify({ name: 'John' }),
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
const context = new Context(request);
|
|
27
|
+
const body1 = await context.getBody();
|
|
28
|
+
const body2 = await context.getBody();
|
|
29
|
+
|
|
30
|
+
expect(body1).toEqual(body2);
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
test('should return undefined for GET request', async () => {
|
|
34
|
+
const request = new Request('http://localhost:3000/api/users', {
|
|
35
|
+
method: 'GET',
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
const context = new Context(request);
|
|
39
|
+
const body = await context.getBody();
|
|
40
|
+
|
|
41
|
+
expect(body).toBeUndefined();
|
|
42
|
+
});
|
|
43
|
+
});
|
|
44
|
+
|
|
@@ -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
|
+
|