@stacksjs/bun-router 0.0.5 → 0.0.6
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/auth.d.ts +134 -0
- package/dist/cache/lru-cache.d.ts +139 -0
- package/dist/cache/middleware-memoization.d.ts +151 -0
- package/dist/cache/route-cache-warmer.d.ts +161 -0
- package/dist/cache/sqlite-cache.d.ts +209 -0
- package/dist/cache/streaming-cache.d.ts +132 -0
- package/dist/cli/colors.d.ts +15 -0
- package/dist/cli/index.d.ts +10 -0
- package/dist/cli/middleware.d.ts +31 -0
- package/dist/cli/openapi.d.ts +17 -0
- package/dist/cli/router.d.ts +15 -0
- package/dist/cli/routes.d.ts +29 -0
- package/dist/cli/utils.d.ts +74 -0
- package/dist/cli.d.ts +1 -1
- package/dist/cli.js +1 -1
- package/dist/config.d.ts +7 -0
- package/dist/container/container.d.ts +273 -0
- package/dist/container/contextual-binding.d.ts +240 -0
- package/dist/container/decorators.d.ts +141 -0
- package/dist/container/service-provider.d.ts +285 -0
- package/dist/development/hot-reload.d.ts +187 -0
- package/dist/development/index.d.ts +167 -0
- package/dist/development/performance-profiler.d.ts +217 -0
- package/dist/development/route-debugger.d.ts +154 -0
- package/dist/development/route-inspector.d.ts +211 -0
- package/dist/development/typescript-utilities.d.ts +209 -0
- package/dist/docs.d.ts +11 -0
- package/dist/errors/circuit-breaker.d.ts +195 -0
- package/dist/errors/error-handler.d.ts +88 -0
- package/dist/errors/error-reporting.d.ts +144 -0
- package/dist/errors/exceptions.d.ts +184 -0
- package/dist/errors/graceful-degradation.d.ts +154 -0
- package/dist/errors/index.d.ts +9 -0
- package/dist/errors/router-errors.d.ts +110 -0
- package/dist/file-serving/static-files.d.ts +143 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +319 -205
- package/dist/middleware/auth.d.ts +63 -0
- package/dist/middleware/content_security_policy.d.ts +53 -0
- package/dist/middleware/cors.d.ts +5 -0
- package/dist/middleware/csrf.d.ts +7 -0
- package/dist/middleware/ddos_protection.d.ts +39 -0
- package/dist/middleware/file_security.d.ts +26 -0
- package/dist/middleware/file_upload.d.ts +38 -0
- package/dist/middleware/helmet.d.ts +54 -0
- package/dist/middleware/index.d.ts +44 -0
- package/dist/middleware/input_validation.d.ts +45 -0
- package/dist/middleware/json_body.d.ts +4 -0
- package/dist/middleware/performance_alerting.d.ts +87 -0
- package/dist/middleware/performance_dashboard.d.ts +87 -0
- package/dist/middleware/performance_monitor.d.ts +209 -0
- package/dist/middleware/pipeline.d.ts +131 -0
- package/dist/middleware/rate_limit.d.ts +36 -0
- package/dist/middleware/request_id.d.ts +4 -0
- package/dist/middleware/request_signing.d.ts +153 -0
- package/dist/middleware/request_tracer.d.ts +72 -0
- package/dist/middleware/response_cache.d.ts +97 -0
- package/dist/middleware/security.d.ts +69 -0
- package/dist/middleware/security_suite.d.ts +46 -0
- package/dist/middleware/session.d.ts +23 -0
- package/dist/model-binding/index.d.ts +2 -0
- package/dist/model-binding/model-middleware.d.ts +118 -0
- package/dist/model-binding/model-registry.d.ts +164 -0
- package/dist/model-binding.d.ts +185 -0
- package/dist/model-resolver-factory.d.ts +31 -0
- package/dist/observability/correlation.d.ts +187 -0
- package/dist/observability/health-checks.d.ts +186 -0
- package/dist/observability/index.d.ts +60 -0
- package/dist/observability/integration.d.ts +147 -0
- package/dist/observability/metrics.d.ts +184 -0
- package/dist/observability/tracing.d.ts +187 -0
- package/dist/optimization/bun-utilities.d.ts +224 -0
- package/dist/query-builder-integration.d.ts +36 -0
- package/dist/request/context.d.ts +34 -0
- package/dist/request/enhanced-request.d.ts +213 -0
- package/dist/request/macros.d.ts +300 -0
- package/dist/response/macros.d.ts +245 -0
- package/dist/response/response-factory.d.ts +133 -0
- package/dist/router/api-routes.d.ts +5 -0
- package/dist/router/file-based-routing.d.ts +118 -0
- package/dist/router/file-streaming.d.ts +5 -0
- package/dist/router/fluent-router.d.ts +315 -0
- package/dist/router/fluent-routing.d.ts +271 -0
- package/dist/router/group-organization.d.ts +5 -0
- package/dist/router/handler-resolver.d.ts +24 -0
- package/dist/router/http-methods.d.ts +10 -0
- package/dist/router/index.d.ts +113 -0
- package/dist/router/middleware-groups.d.ts +94 -0
- package/dist/router/middleware-integration.d.ts +112 -0
- package/dist/router/middleware.d.ts +5 -0
- package/dist/router/model-binding.d.ts +5 -0
- package/dist/router/optimized-route-matching.d.ts +6 -0
- package/dist/router/route-building.d.ts +5 -0
- package/dist/router/route-compiler.d.ts +153 -0
- package/dist/router/route-matching.d.ts +5 -0
- package/dist/router/route-trie.d.ts +114 -0
- package/dist/router/router.d.ts +276 -0
- package/dist/router/server.d.ts +5 -0
- package/dist/router/validation-integration.d.ts +170 -0
- package/dist/router/view-rendering.d.ts +5 -0
- package/dist/router/websocket.d.ts +5 -0
- package/dist/routing/route-caching.d.ts +129 -0
- package/dist/routing/route-throttling.d.ts +150 -0
- package/dist/routing/subdomain-routing.d.ts +206 -0
- package/dist/session/database-store.d.ts +51 -0
- package/dist/session/file-store.d.ts +22 -0
- package/dist/session/index.d.ts +106 -0
- package/dist/session/memory-store.d.ts +21 -0
- package/dist/session/redis-store.d.ts +31 -0
- package/dist/streaming/index.d.ts +3 -0
- package/dist/streaming/sse-handler.d.ts +138 -0
- package/dist/streaming/stream-handler.d.ts +114 -0
- package/dist/testing/auth-testing.d.ts +156 -0
- package/dist/testing/file-upload-testing.d.ts +190 -0
- package/dist/testing/index.d.ts +10 -0
- package/dist/testing/middleware-testing.d.ts +138 -0
- package/dist/testing/model-binding-testing.d.ts +186 -0
- package/dist/testing/performance-testing.d.ts +235 -0
- package/dist/testing/test-client.d.ts +117 -0
- package/dist/testing/test-request.d.ts +85 -0
- package/dist/testing/test-response.d.ts +90 -0
- package/dist/testing/types.d.ts +207 -0
- package/dist/testing/websocket-testing.d.ts +227 -0
- package/dist/types/controller-types.d.ts +208 -0
- package/dist/types/core.d.ts +543 -0
- package/dist/types/middleware-types.d.ts +227 -0
- package/dist/types/request-response-augmentation.d.ts +261 -0
- package/dist/types/route-inference.d.ts +168 -0
- package/dist/types.d.ts +1776 -0
- package/dist/url.d.ts +56 -0
- package/dist/utils/index.d.ts +1 -0
- package/dist/utils/query-preservation.d.ts +48 -0
- package/dist/utils.d.ts +69 -0
- package/dist/validation/validator.d.ts +140 -0
- package/dist/websocket/clustering.d.ts +185 -0
- package/package.json +3 -2
- package/src/auth.ts +469 -0
- package/src/cache/lru-cache.ts +457 -0
- package/src/cache/middleware-memoization.ts +531 -0
- package/src/cache/route-cache-warmer.ts +486 -0
- package/src/cache/sqlite-cache.ts +783 -0
- package/src/cache/streaming-cache.ts +572 -0
- package/src/cli/colors.ts +29 -0
- package/src/cli/index.ts +287 -0
- package/src/cli/middleware.ts +291 -0
- package/src/cli/openapi.ts +407 -0
- package/src/cli/router.ts +188 -0
- package/src/cli/routes.ts +265 -0
- package/src/cli/utils.ts +531 -0
- package/src/cli.ts +5 -0
- package/src/config.ts +322 -0
- package/src/container/container.ts +740 -0
- package/src/container/contextual-binding.ts +603 -0
- package/src/container/decorators.ts +359 -0
- package/src/container/service-provider.ts +596 -0
- package/src/development/hot-reload.ts +673 -0
- package/src/development/index.ts +499 -0
- package/src/development/performance-profiler.ts +717 -0
- package/src/development/route-debugger.ts +527 -0
- package/src/development/route-inspector.ts +749 -0
- package/src/development/typescript-utilities.ts +678 -0
- package/src/docs.ts +397 -0
- package/src/errors/circuit-breaker.ts +732 -0
- package/src/errors/error-handler.ts +569 -0
- package/src/errors/error-reporting.ts +672 -0
- package/src/errors/exceptions.ts +536 -0
- package/src/errors/graceful-degradation.ts +621 -0
- package/src/errors/index.ts +21 -0
- package/src/errors/router-errors.ts +632 -0
- package/src/file-serving/static-files.ts +581 -0
- package/src/index.ts +14 -0
- package/src/middleware/auth.ts +219 -0
- package/src/middleware/content_security_policy.ts +215 -0
- package/src/middleware/cors.ts +74 -0
- package/src/middleware/csrf.ts +108 -0
- package/src/middleware/ddos_protection.ts +255 -0
- package/src/middleware/file_security.ts +191 -0
- package/src/middleware/file_upload.ts +264 -0
- package/src/middleware/helmet.ts +268 -0
- package/src/middleware/index.ts +117 -0
- package/src/middleware/input_validation.ts +449 -0
- package/src/middleware/json_body.ts +37 -0
- package/src/middleware/performance_alerting.ts +538 -0
- package/src/middleware/performance_dashboard.ts +661 -0
- package/src/middleware/performance_monitor.ts +943 -0
- package/src/middleware/pipeline.ts +489 -0
- package/src/middleware/rate_limit.ts +245 -0
- package/src/middleware/request_id.ts +36 -0
- package/src/middleware/request_signing.ts +636 -0
- package/src/middleware/request_tracer.ts +636 -0
- package/src/middleware/response_cache.ts +743 -0
- package/src/middleware/security.ts +482 -0
- package/src/middleware/security_suite.ts +257 -0
- package/src/middleware/session.ts +91 -0
- package/src/model-binding/index.ts +18 -0
- package/src/model-binding/model-middleware.ts +425 -0
- package/src/model-binding/model-registry.ts +550 -0
- package/src/model-binding.ts +370 -0
- package/src/model-resolver-factory.ts +106 -0
- package/src/observability/correlation.ts +691 -0
- package/src/observability/health-checks.ts +729 -0
- package/src/observability/index.ts +184 -0
- package/src/observability/integration.ts +548 -0
- package/src/observability/metrics.ts +753 -0
- package/src/observability/tracing.ts +638 -0
- package/src/optimization/bun-utilities.ts +778 -0
- package/src/query-builder-integration.ts +137 -0
- package/src/request/context.ts +62 -0
- package/src/request/enhanced-request.ts +857 -0
- package/src/request/macros.ts +688 -0
- package/src/response/macros.ts +612 -0
- package/src/response/response-factory.ts +588 -0
- package/src/router/api-routes.ts +243 -0
- package/src/router/file-based-routing.ts +670 -0
- package/src/router/file-streaming.ts +372 -0
- package/src/router/fluent-router.ts +927 -0
- package/src/router/fluent-routing.ts +797 -0
- package/src/router/group-organization.ts +213 -0
- package/src/router/handler-resolver.ts +291 -0
- package/src/router/http-methods.ts +377 -0
- package/src/router/index.ts +185 -0
- package/src/router/middleware-groups.ts +222 -0
- package/src/router/middleware-integration.ts +399 -0
- package/src/router/middleware.ts +231 -0
- package/src/router/model-binding.ts +215 -0
- package/src/router/optimized-route-matching.ts +253 -0
- package/src/router/route-building.ts +212 -0
- package/src/router/route-compiler.ts +702 -0
- package/src/router/route-matching.ts +349 -0
- package/src/router/route-trie.ts +464 -0
- package/src/router/router.ts +1508 -0
- package/src/router/server.ts +406 -0
- package/src/router/validation-integration.ts +443 -0
- package/src/router/view-rendering.ts +233 -0
- package/src/router/websocket.ts +100 -0
- package/src/routing/route-caching.ts +402 -0
- package/src/routing/route-throttling.ts +469 -0
- package/src/routing/subdomain-routing.ts +492 -0
- package/src/session/database-store.ts +109 -0
- package/src/session/file-store.ts +148 -0
- package/src/session/index.ts +244 -0
- package/src/session/memory-store.ts +88 -0
- package/src/session/redis-store.ts +93 -0
- package/src/streaming/index.ts +17 -0
- package/src/streaming/sse-handler.ts +482 -0
- package/src/streaming/stream-handler.ts +552 -0
- package/src/testing/auth-testing.ts +446 -0
- package/src/testing/file-upload-testing.ts +543 -0
- package/src/testing/index.ts +10 -0
- package/src/testing/middleware-testing.ts +322 -0
- package/src/testing/model-binding-testing.ts +645 -0
- package/src/testing/performance-testing.ts +738 -0
- package/src/testing/test-client.ts +310 -0
- package/src/testing/test-request.ts +315 -0
- package/src/testing/test-response.ts +332 -0
- package/src/testing/types.ts +224 -0
- package/src/testing/websocket-testing.ts +590 -0
- package/src/types/controller-types.ts +385 -0
- package/src/types/core.ts +683 -0
- package/src/types/middleware-types.ts +419 -0
- package/src/types/request-response-augmentation.ts +489 -0
- package/src/types/route-inference.ts +357 -0
- package/src/types.ts +2049 -0
- package/src/url.ts +126 -0
- package/src/utils/index.ts +1 -0
- package/src/utils/query-preservation.ts +201 -0
- package/src/utils.ts +327 -0
- package/src/validation/validator.ts +685 -0
- package/src/websocket/clustering.ts +762 -0
|
@@ -0,0 +1,332 @@
|
|
|
1
|
+
import type { TestResponse } from './types'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Test response wrapper with assertion helpers
|
|
5
|
+
*/
|
|
6
|
+
export class TestResponseWrapper implements TestResponse {
|
|
7
|
+
public status: number
|
|
8
|
+
public statusText: string
|
|
9
|
+
public headers: Record<string, string>
|
|
10
|
+
public body: any
|
|
11
|
+
public text: string
|
|
12
|
+
public json: any
|
|
13
|
+
public cookies: Record<string, string>
|
|
14
|
+
public redirected: boolean
|
|
15
|
+
public url: string
|
|
16
|
+
public ok: boolean
|
|
17
|
+
public type: string
|
|
18
|
+
|
|
19
|
+
private originalResponse: Response
|
|
20
|
+
|
|
21
|
+
constructor(response: Response, body?: any) {
|
|
22
|
+
this.originalResponse = response
|
|
23
|
+
this.status = response.status
|
|
24
|
+
this.statusText = response.statusText
|
|
25
|
+
this.headers = this.parseHeaders(response.headers)
|
|
26
|
+
this.cookies = this.parseCookies(response.headers.get('set-cookie'))
|
|
27
|
+
this.redirected = response.redirected
|
|
28
|
+
this.url = response.url
|
|
29
|
+
this.ok = response.ok
|
|
30
|
+
this.type = response.type
|
|
31
|
+
this.body = body
|
|
32
|
+
this.text = typeof body === 'string' ? body : JSON.stringify(body)
|
|
33
|
+
|
|
34
|
+
try {
|
|
35
|
+
this.json = typeof body === 'string' ? JSON.parse(body) : body
|
|
36
|
+
}
|
|
37
|
+
catch {
|
|
38
|
+
this.json = null
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
private parseHeaders(headers: Headers): Record<string, string> {
|
|
43
|
+
const result: Record<string, string> = {}
|
|
44
|
+
headers.forEach((value, key) => {
|
|
45
|
+
result[key.toLowerCase()] = value
|
|
46
|
+
})
|
|
47
|
+
return result
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
private parseCookies(cookieHeader: string | null): Record<string, string> {
|
|
51
|
+
if (!cookieHeader)
|
|
52
|
+
return {}
|
|
53
|
+
|
|
54
|
+
const cookies: Record<string, string> = {}
|
|
55
|
+
cookieHeader.split(',').forEach((cookie) => {
|
|
56
|
+
const [nameValue] = cookie.trim().split(';')
|
|
57
|
+
const [name, value] = nameValue.split('=')
|
|
58
|
+
if (name && value) {
|
|
59
|
+
cookies[name.trim()] = value.trim()
|
|
60
|
+
}
|
|
61
|
+
})
|
|
62
|
+
return cookies
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Assert response status
|
|
67
|
+
*/
|
|
68
|
+
expectStatus(status: number): TestResponseWrapper {
|
|
69
|
+
if (this.status !== status) {
|
|
70
|
+
throw new Error(`Expected status ${status}, got ${this.status}`)
|
|
71
|
+
}
|
|
72
|
+
return this
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Assert response is successful (2xx)
|
|
77
|
+
*/
|
|
78
|
+
expectSuccess(): TestResponseWrapper {
|
|
79
|
+
if (!this.ok) {
|
|
80
|
+
throw new Error(`Expected successful response, got ${this.status}`)
|
|
81
|
+
}
|
|
82
|
+
return this
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Assert response has specific header
|
|
87
|
+
*/
|
|
88
|
+
expectHeader(name: string, value?: string): TestResponseWrapper {
|
|
89
|
+
const headerValue = this.headers[name.toLowerCase()]
|
|
90
|
+
if (headerValue === undefined) {
|
|
91
|
+
throw new Error(`Expected header '${name}' to be present`)
|
|
92
|
+
}
|
|
93
|
+
if (value !== undefined && headerValue !== value) {
|
|
94
|
+
throw new Error(`Expected header '${name}' to be '${value}', got '${headerValue}'`)
|
|
95
|
+
}
|
|
96
|
+
return this
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* Assert response body matches
|
|
101
|
+
*/
|
|
102
|
+
expectBody(expectedBody: any): TestResponseWrapper {
|
|
103
|
+
if (typeof expectedBody === 'string') {
|
|
104
|
+
if (this.text !== expectedBody) {
|
|
105
|
+
throw new Error(`Expected body '${expectedBody}', got '${this.text}'`)
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
else {
|
|
109
|
+
if (JSON.stringify(this.json) !== JSON.stringify(expectedBody)) {
|
|
110
|
+
throw new Error(`Expected body ${JSON.stringify(expectedBody)}, got ${JSON.stringify(this.json)}`)
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
return this
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* Assert response JSON matches
|
|
118
|
+
*/
|
|
119
|
+
expectJson(expectedJson: any): TestResponseWrapper {
|
|
120
|
+
if (JSON.stringify(this.json) !== JSON.stringify(expectedJson)) {
|
|
121
|
+
throw new Error(`Expected JSON ${JSON.stringify(expectedJson)}, got ${JSON.stringify(this.json)}`)
|
|
122
|
+
}
|
|
123
|
+
return this
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* Assert response JSON contains specific properties
|
|
128
|
+
*/
|
|
129
|
+
expectJsonContains(properties: Record<string, any>): TestResponseWrapper {
|
|
130
|
+
if (typeof this.json !== 'object' || this.json === null) {
|
|
131
|
+
throw new Error('Response is not a JSON object')
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
Object.entries(properties).forEach(([key, expectedValue]) => {
|
|
135
|
+
const actualValue = this.json[key]
|
|
136
|
+
|
|
137
|
+
// Deep comparison for objects
|
|
138
|
+
if (typeof expectedValue === 'object' && expectedValue !== null
|
|
139
|
+
&& typeof actualValue === 'object' && actualValue !== null) {
|
|
140
|
+
const expectedStr = JSON.stringify(expectedValue)
|
|
141
|
+
const actualStr = JSON.stringify(actualValue)
|
|
142
|
+
if (expectedStr !== actualStr) {
|
|
143
|
+
throw new Error(`Expected JSON property '${key}' to be ${expectedStr}, got ${actualStr}`)
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
else if (actualValue !== expectedValue) {
|
|
147
|
+
throw new Error(`Expected JSON property '${key}' to be '${expectedValue}', got '${actualValue}'`)
|
|
148
|
+
}
|
|
149
|
+
})
|
|
150
|
+
|
|
151
|
+
return this
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
/**
|
|
155
|
+
* Assert response has specific cookie
|
|
156
|
+
*/
|
|
157
|
+
expectCookie(name: string, value?: string): TestResponseWrapper {
|
|
158
|
+
const cookieValue = this.cookies[name]
|
|
159
|
+
if (cookieValue === undefined) {
|
|
160
|
+
throw new Error(`Expected cookie '${name}' to be present`)
|
|
161
|
+
}
|
|
162
|
+
if (value !== undefined && cookieValue !== value) {
|
|
163
|
+
throw new Error(`Expected cookie '${name}' to be '${value}', got '${cookieValue}'`)
|
|
164
|
+
}
|
|
165
|
+
return this
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
/**
|
|
169
|
+
* Assert response is a redirect
|
|
170
|
+
*/
|
|
171
|
+
expectRedirect(url?: string): TestResponseWrapper {
|
|
172
|
+
if (this.status < 300 || this.status >= 400) {
|
|
173
|
+
throw new Error(`Expected redirect status (3xx), got ${this.status}`)
|
|
174
|
+
}
|
|
175
|
+
if (url) {
|
|
176
|
+
const location = this.headers.location
|
|
177
|
+
if (location !== url) {
|
|
178
|
+
throw new Error(`Expected redirect to '${url}', got '${location}'`)
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
return this
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
/**
|
|
185
|
+
* Assert response contains validation errors
|
|
186
|
+
*/
|
|
187
|
+
expectValidationError(field?: string, message?: string): TestResponseWrapper {
|
|
188
|
+
if (this.status !== 422) {
|
|
189
|
+
throw new Error(`Expected validation error status (422), got ${this.status}`)
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
if (field) {
|
|
193
|
+
const errors = this.json?.errors || this.json?.message
|
|
194
|
+
if (!errors || !errors[field]) {
|
|
195
|
+
throw new Error(`Expected validation error for field '${field}'`)
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
if (message) {
|
|
199
|
+
const fieldErrors = Array.isArray(errors[field]) ? errors[field] : [errors[field]]
|
|
200
|
+
if (!fieldErrors.includes(message)) {
|
|
201
|
+
throw new Error(`Expected validation error message '${message}' for field '${field}'`)
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
return this
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
/**
|
|
209
|
+
* Assert response content type
|
|
210
|
+
*/
|
|
211
|
+
expectContentType(contentType: string): TestResponseWrapper {
|
|
212
|
+
const actualContentType = this.headers['content-type']
|
|
213
|
+
if (!actualContentType || !actualContentType.includes(contentType)) {
|
|
214
|
+
throw new Error(`Expected content type '${contentType}', got '${actualContentType}'`)
|
|
215
|
+
}
|
|
216
|
+
return this
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
/**
|
|
220
|
+
* Assert response body contains text
|
|
221
|
+
*/
|
|
222
|
+
expectBodyContains(text: string): TestResponseWrapper {
|
|
223
|
+
if (!this.text.includes(text)) {
|
|
224
|
+
throw new Error(`Expected body to contain '${text}'`)
|
|
225
|
+
}
|
|
226
|
+
return this
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
/**
|
|
230
|
+
* Assert response body matches regex
|
|
231
|
+
*/
|
|
232
|
+
expectBodyMatches(pattern: RegExp): TestResponseWrapper {
|
|
233
|
+
if (!pattern.test(this.text)) {
|
|
234
|
+
throw new Error(`Expected body to match pattern ${pattern}`)
|
|
235
|
+
}
|
|
236
|
+
return this
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
/**
|
|
240
|
+
* Assert response JSON has specific structure
|
|
241
|
+
*/
|
|
242
|
+
expectJsonStructure(structure: any): TestResponseWrapper {
|
|
243
|
+
this.validateStructure(this.json, structure, 'root')
|
|
244
|
+
return this
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
private validateStructure(data: any, structure: any, path: string): void {
|
|
248
|
+
// Handle expect.any() matchers - they have a $$typeof property
|
|
249
|
+
if (structure && typeof structure === 'object' && structure.$$typeof) {
|
|
250
|
+
// This is an expect.any() matcher, skip validation
|
|
251
|
+
return
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
if (Array.isArray(structure)) {
|
|
255
|
+
if (!Array.isArray(data)) {
|
|
256
|
+
throw new TypeError(`Expected ${path} to be an array`)
|
|
257
|
+
}
|
|
258
|
+
if (structure.length > 0 && data.length > 0) {
|
|
259
|
+
this.validateStructure(data[0], structure[0], `${path}[0]`)
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
else if (typeof structure === 'object' && structure !== null) {
|
|
263
|
+
if (typeof data !== 'object' || data === null) {
|
|
264
|
+
throw new Error(`Expected ${path} to be an object`)
|
|
265
|
+
}
|
|
266
|
+
Object.keys(structure).forEach((key) => {
|
|
267
|
+
if (!(key in data)) {
|
|
268
|
+
throw new Error(`Expected ${path} to have property '${key}'`)
|
|
269
|
+
}
|
|
270
|
+
this.validateStructure(data[key], structure[key], `${path}.${key}`)
|
|
271
|
+
})
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
/**
|
|
276
|
+
* Get the original Response object
|
|
277
|
+
*/
|
|
278
|
+
getOriginalResponse(): Response {
|
|
279
|
+
return this.originalResponse
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
/**
|
|
283
|
+
* Convert to JSON for easy inspection
|
|
284
|
+
*/
|
|
285
|
+
toJSON(): any {
|
|
286
|
+
return {
|
|
287
|
+
status: this.status,
|
|
288
|
+
statusText: this.statusText,
|
|
289
|
+
headers: this.headers,
|
|
290
|
+
body: this.body,
|
|
291
|
+
json: this.json,
|
|
292
|
+
cookies: this.cookies,
|
|
293
|
+
redirected: this.redirected,
|
|
294
|
+
url: this.url,
|
|
295
|
+
ok: this.ok,
|
|
296
|
+
type: this.type,
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
/**
|
|
302
|
+
* Create a test response from a Response object
|
|
303
|
+
*/
|
|
304
|
+
export async function createTestResponse(response: Response): Promise<TestResponseWrapper> {
|
|
305
|
+
const body = await response.text()
|
|
306
|
+
return new TestResponseWrapper(response, body)
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
/**
|
|
310
|
+
* Create a mock response for testing
|
|
311
|
+
*/
|
|
312
|
+
export function createMockResponse(
|
|
313
|
+
status: number = 200,
|
|
314
|
+
body?: any,
|
|
315
|
+
headers?: Record<string, string>,
|
|
316
|
+
): TestResponseWrapper {
|
|
317
|
+
const responseHeaders = new Headers(headers)
|
|
318
|
+
|
|
319
|
+
if (body && typeof body === 'object' && !responseHeaders.has('content-type')) {
|
|
320
|
+
responseHeaders.set('content-type', 'application/json')
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
const response = new Response(
|
|
324
|
+
body ? (typeof body === 'string' ? body : JSON.stringify(body)) : null,
|
|
325
|
+
{
|
|
326
|
+
status,
|
|
327
|
+
headers: responseHeaders,
|
|
328
|
+
},
|
|
329
|
+
)
|
|
330
|
+
|
|
331
|
+
return new TestResponseWrapper(response, body)
|
|
332
|
+
}
|
|
@@ -0,0 +1,224 @@
|
|
|
1
|
+
import type { EnhancedRequest, MiddlewareHandler } from '../types'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Test client configuration options
|
|
5
|
+
*/
|
|
6
|
+
export interface TestClientConfig {
|
|
7
|
+
baseUrl?: string
|
|
8
|
+
defaultHeaders?: Record<string, string>
|
|
9
|
+
timeout?: number
|
|
10
|
+
followRedirects?: boolean
|
|
11
|
+
validateStatus?: (status: number) => boolean
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Test request options
|
|
16
|
+
*/
|
|
17
|
+
export interface TestRequestOptions {
|
|
18
|
+
headers?: Record<string, string>
|
|
19
|
+
query?: Record<string, string | string[]>
|
|
20
|
+
body?: any
|
|
21
|
+
files?: TestFile[]
|
|
22
|
+
cookies?: Record<string, string>
|
|
23
|
+
session?: any
|
|
24
|
+
user?: any
|
|
25
|
+
context?: Record<string, any>
|
|
26
|
+
timeout?: number
|
|
27
|
+
followRedirects?: boolean
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Test file for upload testing
|
|
32
|
+
*/
|
|
33
|
+
export interface TestFile {
|
|
34
|
+
fieldName: string
|
|
35
|
+
filename: string
|
|
36
|
+
content: string | ArrayBuffer | Uint8Array
|
|
37
|
+
mimetype?: string
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Test response interface
|
|
42
|
+
*/
|
|
43
|
+
export interface TestResponse {
|
|
44
|
+
status: number
|
|
45
|
+
statusText: string
|
|
46
|
+
headers: Record<string, string>
|
|
47
|
+
body: any
|
|
48
|
+
text: string
|
|
49
|
+
json: any
|
|
50
|
+
cookies: Record<string, string>
|
|
51
|
+
redirected: boolean
|
|
52
|
+
url: string
|
|
53
|
+
ok: boolean
|
|
54
|
+
type: string
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Middleware test context
|
|
59
|
+
*/
|
|
60
|
+
export interface MiddlewareTestContext {
|
|
61
|
+
request: EnhancedRequest
|
|
62
|
+
response?: Response
|
|
63
|
+
next: ReturnType<typeof import('bun:test').mock<() => Promise<Response | null>>>
|
|
64
|
+
error?: Error
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Authentication test user
|
|
69
|
+
*/
|
|
70
|
+
export interface TestUser {
|
|
71
|
+
id: string | number
|
|
72
|
+
email?: string
|
|
73
|
+
username?: string
|
|
74
|
+
roles?: string[]
|
|
75
|
+
permissions?: string[]
|
|
76
|
+
[key: string]: any
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* JWT test options
|
|
81
|
+
*/
|
|
82
|
+
export interface JWTTestOptions {
|
|
83
|
+
secret?: string
|
|
84
|
+
expiresIn?: string
|
|
85
|
+
algorithm?: 'HS256' | 'HS384' | 'HS512' | 'RS256'
|
|
86
|
+
payload?: Record<string, any>
|
|
87
|
+
headers?: Record<string, any>
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* Session test options
|
|
92
|
+
*/
|
|
93
|
+
export interface SessionTestOptions {
|
|
94
|
+
sessionId?: string
|
|
95
|
+
data?: Record<string, any>
|
|
96
|
+
maxAge?: number
|
|
97
|
+
secure?: boolean
|
|
98
|
+
httpOnly?: boolean
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* Model binding test options
|
|
103
|
+
*/
|
|
104
|
+
export interface ModelBindingTestOptions {
|
|
105
|
+
modelClass?: any
|
|
106
|
+
routeKey?: string
|
|
107
|
+
constraints?: Record<string, any>
|
|
108
|
+
customResolver?: (value: string) => Promise<any>
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* Performance test metrics
|
|
113
|
+
*/
|
|
114
|
+
export interface PerformanceMetrics {
|
|
115
|
+
responseTime: number
|
|
116
|
+
memoryUsage: {
|
|
117
|
+
heapUsed: number
|
|
118
|
+
heapTotal: number
|
|
119
|
+
external: number
|
|
120
|
+
rss: number
|
|
121
|
+
}
|
|
122
|
+
cpuUsage: {
|
|
123
|
+
user: number
|
|
124
|
+
system: number
|
|
125
|
+
}
|
|
126
|
+
requestsPerSecond?: number
|
|
127
|
+
concurrentRequests?: number
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* Load test configuration
|
|
132
|
+
*/
|
|
133
|
+
export interface LoadTestConfig {
|
|
134
|
+
duration: number
|
|
135
|
+
concurrency: number
|
|
136
|
+
rampUp?: number
|
|
137
|
+
rampDown?: number
|
|
138
|
+
requestsPerSecond?: number
|
|
139
|
+
maxRequests?: number
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
/**
|
|
143
|
+
* WebSocket test message
|
|
144
|
+
*/
|
|
145
|
+
export interface WSTestMessage {
|
|
146
|
+
type: 'text' | 'binary'
|
|
147
|
+
data: string | ArrayBuffer | Uint8Array
|
|
148
|
+
timestamp?: number
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
/**
|
|
152
|
+
* WebSocket test client
|
|
153
|
+
*/
|
|
154
|
+
export interface WSTestClient {
|
|
155
|
+
send: (message: string | ArrayBuffer | Uint8Array) => void
|
|
156
|
+
close: (code?: number, reason?: string) => void
|
|
157
|
+
ping: (data?: Uint8Array) => void
|
|
158
|
+
pong: (data?: Uint8Array) => void
|
|
159
|
+
messages: WSTestMessage[]
|
|
160
|
+
isConnected: boolean
|
|
161
|
+
readyState: number
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
/**
|
|
165
|
+
* Test assertion helpers
|
|
166
|
+
*/
|
|
167
|
+
export interface TestAssertions {
|
|
168
|
+
toHaveStatus: (status: number) => void
|
|
169
|
+
toHaveHeader: (name: string, value?: string) => void
|
|
170
|
+
toHaveBody: (body: any) => void
|
|
171
|
+
toHaveJson: (json: any) => void
|
|
172
|
+
toHaveCookie: (name: string, value?: string) => void
|
|
173
|
+
toBeRedirected: (url?: string) => void
|
|
174
|
+
toHaveValidationError: (field?: string, message?: string) => void
|
|
175
|
+
toBeAuthenticated: (user?: any) => void
|
|
176
|
+
toBeUnauthenticated: () => void
|
|
177
|
+
toHaveMiddleware: (middleware: string | MiddlewareHandler) => void
|
|
178
|
+
toHaveModel: (model: any) => void
|
|
179
|
+
toHaveUploadedFile: (fieldName: string, filename?: string) => void
|
|
180
|
+
toRespondWithin: (milliseconds: number) => void
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
/**
|
|
184
|
+
* Mock factory options
|
|
185
|
+
*/
|
|
186
|
+
export interface MockFactoryOptions {
|
|
187
|
+
count?: number
|
|
188
|
+
overrides?: Record<string, any>
|
|
189
|
+
relations?: Record<string, any>
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
/**
|
|
193
|
+
* Database test helpers
|
|
194
|
+
*/
|
|
195
|
+
export interface DatabaseTestHelpers {
|
|
196
|
+
seed: (data: any[]) => Promise<void>
|
|
197
|
+
truncate: (tables?: string[]) => Promise<void>
|
|
198
|
+
factory: <T>(model: string, options?: MockFactoryOptions) => T[]
|
|
199
|
+
create: <T>(model: string, data?: Partial<T>) => Promise<T>
|
|
200
|
+
transaction: <T>(callback: () => Promise<T>) => Promise<T>
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
/**
|
|
204
|
+
* Test environment configuration
|
|
205
|
+
*/
|
|
206
|
+
export interface TestEnvironment {
|
|
207
|
+
database?: DatabaseTestHelpers
|
|
208
|
+
cache?: {
|
|
209
|
+
clear: () => Promise<void>
|
|
210
|
+
get: (key: string) => Promise<any>
|
|
211
|
+
set: (key: string, value: any, ttl?: number) => Promise<void>
|
|
212
|
+
}
|
|
213
|
+
storage?: {
|
|
214
|
+
clear: () => Promise<void>
|
|
215
|
+
put: (path: string, content: string | ArrayBuffer) => Promise<void>
|
|
216
|
+
get: (path: string) => Promise<string | ArrayBuffer | null>
|
|
217
|
+
delete: (path: string) => Promise<void>
|
|
218
|
+
}
|
|
219
|
+
queue?: {
|
|
220
|
+
clear: () => Promise<void>
|
|
221
|
+
push: (job: any) => Promise<void>
|
|
222
|
+
process: () => Promise<void>
|
|
223
|
+
}
|
|
224
|
+
}
|