@stacksjs/bun-router 0.0.17 → 0.0.18
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/cli.js +1 -1
- package/dist/container/service-provider.d.ts +3 -3
- package/dist/development/performance-profiler.d.ts +3 -3
- package/dist/development/route-debugger.d.ts +1 -1
- package/dist/development/route-inspector.d.ts +2 -2
- package/dist/observability/integration.d.ts +1 -1
- package/dist/response/macros.d.ts +1 -1
- package/dist/router/index.d.ts +7 -7
- package/dist/router/router.d.ts +0 -32
- package/dist/testing/performance-testing.d.ts +2 -2
- package/dist/testing/test-client.d.ts +1 -1
- package/dist/testing/websocket-testing.d.ts +1 -1
- package/package.json +2 -3
- package/src/auth.ts +0 -489
- package/src/cache/lru-cache.ts +0 -457
- package/src/cache/middleware-memoization.ts +0 -531
- package/src/cache/route-cache-warmer.ts +0 -486
- package/src/cache/sqlite-cache.ts +0 -783
- package/src/cache/streaming-cache.ts +0 -572
- package/src/cli/colors.ts +0 -29
- package/src/cli/index.ts +0 -287
- package/src/cli/middleware.ts +0 -291
- package/src/cli/openapi.ts +0 -407
- package/src/cli/router.ts +0 -188
- package/src/cli/routes.ts +0 -265
- package/src/cli/utils.ts +0 -531
- package/src/cli.ts +0 -5
- package/src/config.ts +0 -322
- package/src/container/container.ts +0 -786
- package/src/container/contextual-binding.ts +0 -606
- package/src/container/decorators.ts +0 -368
- package/src/container/index.ts +0 -49
- package/src/container/service-provider.ts +0 -596
- package/src/development/hot-reload.ts +0 -673
- package/src/development/index.ts +0 -499
- package/src/development/performance-profiler.ts +0 -717
- package/src/development/route-debugger.ts +0 -527
- package/src/development/route-inspector.ts +0 -749
- package/src/development/typescript-utilities.ts +0 -682
- package/src/docs.ts +0 -397
- package/src/errors/circuit-breaker.ts +0 -733
- package/src/errors/error-handler.ts +0 -569
- package/src/errors/error-reporting.ts +0 -672
- package/src/errors/exceptions.ts +0 -536
- package/src/errors/graceful-degradation.ts +0 -623
- package/src/errors/index.ts +0 -21
- package/src/errors/router-errors.ts +0 -632
- package/src/file-serving/static-files.ts +0 -718
- package/src/index.ts +0 -66
- package/src/middleware/auth.ts +0 -220
- package/src/middleware/content_security_policy.ts +0 -215
- package/src/middleware/cors.ts +0 -142
- package/src/middleware/csrf.ts +0 -166
- package/src/middleware/ddos_protection.ts +0 -279
- package/src/middleware/file_security.ts +0 -191
- package/src/middleware/file_upload.ts +0 -275
- package/src/middleware/helmet.ts +0 -268
- package/src/middleware/index.ts +0 -117
- package/src/middleware/input_validation.ts +0 -453
- package/src/middleware/json_body.ts +0 -37
- package/src/middleware/performance_alerting.ts +0 -540
- package/src/middleware/performance_dashboard.ts +0 -661
- package/src/middleware/performance_monitor.ts +0 -945
- package/src/middleware/pipeline.ts +0 -489
- package/src/middleware/rate_limit.ts +0 -251
- package/src/middleware/request_id.ts +0 -36
- package/src/middleware/request_signing.ts +0 -638
- package/src/middleware/request_tracer.ts +0 -653
- package/src/middleware/response_cache.ts +0 -778
- package/src/middleware/security.ts +0 -482
- package/src/middleware/security_suite.ts +0 -257
- package/src/middleware/session.ts +0 -91
- package/src/model-binding/index.ts +0 -18
- package/src/model-binding/model-middleware.ts +0 -425
- package/src/model-binding/model-registry.ts +0 -550
- package/src/model-binding.ts +0 -370
- package/src/model-resolver-factory.ts +0 -106
- package/src/observability/correlation.ts +0 -691
- package/src/observability/health-checks.ts +0 -729
- package/src/observability/index.ts +0 -184
- package/src/observability/integration.ts +0 -548
- package/src/observability/metrics.ts +0 -753
- package/src/observability/tracing.ts +0 -638
- package/src/optimization/bun-utilities.ts +0 -778
- package/src/query-builder-integration.ts +0 -137
- package/src/request/context.ts +0 -109
- package/src/request/enhanced-request.ts +0 -876
- package/src/request/macros.ts +0 -822
- package/src/response/macros.ts +0 -665
- package/src/response/response-factory.ts +0 -596
- package/src/router/api-routes.ts +0 -243
- package/src/router/file-based-routing.ts +0 -697
- package/src/router/file-streaming.ts +0 -383
- package/src/router/fluent-routing.ts +0 -866
- package/src/router/group-organization.ts +0 -229
- package/src/router/handler-resolver.ts +0 -327
- package/src/router/http-methods.ts +0 -407
- package/src/router/index.ts +0 -193
- package/src/router/middleware-groups.ts +0 -222
- package/src/router/middleware-integration.ts +0 -399
- package/src/router/middleware.ts +0 -234
- package/src/router/model-binding.ts +0 -215
- package/src/router/optimized-route-matching.ts +0 -302
- package/src/router/route-building.ts +0 -221
- package/src/router/route-compiler.ts +0 -691
- package/src/router/route-matching.ts +0 -368
- package/src/router/route-trie.ts +0 -450
- package/src/router/router.ts +0 -1616
- package/src/router/server.ts +0 -684
- package/src/router/validation-integration.ts +0 -449
- package/src/router/view-rendering.ts +0 -233
- package/src/router/websocket.ts +0 -100
- package/src/routing/route-caching.ts +0 -402
- package/src/routing/route-throttling.ts +0 -469
- package/src/routing/subdomain-routing.ts +0 -492
- package/src/session/database-store.ts +0 -109
- package/src/session/file-store.ts +0 -148
- package/src/session/index.ts +0 -244
- package/src/session/memory-store.ts +0 -88
- package/src/session/redis-store.ts +0 -93
- package/src/streaming/index.ts +0 -17
- package/src/streaming/sse-handler.ts +0 -482
- package/src/streaming/stream-handler.ts +0 -552
- package/src/testing/auth-testing.ts +0 -448
- package/src/testing/file-upload-testing.ts +0 -543
- package/src/testing/index.ts +0 -10
- package/src/testing/middleware-testing.ts +0 -322
- package/src/testing/model-binding-testing.ts +0 -645
- package/src/testing/performance-testing.ts +0 -738
- package/src/testing/test-client.ts +0 -310
- package/src/testing/test-request.ts +0 -315
- package/src/testing/test-response.ts +0 -332
- package/src/testing/types.ts +0 -224
- package/src/testing/websocket-testing.ts +0 -590
- package/src/types/controller-types.ts +0 -385
- package/src/types/core.ts +0 -683
- package/src/types/middleware-types.ts +0 -422
- package/src/types/request-response-augmentation.ts +0 -489
- package/src/types/route-inference.ts +0 -372
- package/src/types.ts +0 -2123
- package/src/url.ts +0 -131
- package/src/utils/index.ts +0 -1
- package/src/utils/query-preservation.ts +0 -201
- package/src/utils.ts +0 -395
- package/src/validation/validator.ts +0 -688
- package/src/websocket/clustering.ts +0 -769
|
@@ -1,738 +0,0 @@
|
|
|
1
|
-
import type { EnhancedRequest } from '../types'
|
|
2
|
-
import type { TestClient } from './test-client'
|
|
3
|
-
import type { LoadTestConfig, PerformanceMetrics } from './types'
|
|
4
|
-
import { mock } from 'bun:test'
|
|
5
|
-
import process from 'node:process'
|
|
6
|
-
import { createMockRequest } from './test-request'
|
|
7
|
-
|
|
8
|
-
/** Route handler benchmark result */
|
|
9
|
-
export interface RouteHandlerBenchmarkResult {
|
|
10
|
-
averageTime: number
|
|
11
|
-
minTime: number
|
|
12
|
-
maxTime: number
|
|
13
|
-
totalTime: number
|
|
14
|
-
throughput: number
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
/** Middleware benchmark result */
|
|
18
|
-
export interface MiddlewareBenchmarkResult {
|
|
19
|
-
averageTime: number
|
|
20
|
-
minTime: number
|
|
21
|
-
maxTime: number
|
|
22
|
-
totalTime: number
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
/** JSON serialization benchmark result */
|
|
26
|
-
export interface JsonBenchmarkResult {
|
|
27
|
-
averageTime: number
|
|
28
|
-
minTime: number
|
|
29
|
-
maxTime: number
|
|
30
|
-
totalTime: number
|
|
31
|
-
dataSize: number
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
/** URL parsing benchmark result */
|
|
35
|
-
export interface UrlParsingBenchmarkResult {
|
|
36
|
-
averageTime: number
|
|
37
|
-
minTime: number
|
|
38
|
-
maxTime: number
|
|
39
|
-
totalTime: number
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
/** Memory test result */
|
|
43
|
-
export interface MemoryTestResult {
|
|
44
|
-
heapUsedBefore: number
|
|
45
|
-
heapUsedAfter: number
|
|
46
|
-
heapUsedDelta: number
|
|
47
|
-
rssBefore: number
|
|
48
|
-
rssAfter: number
|
|
49
|
-
rssDelta: number
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
/** Memory usage result */
|
|
53
|
-
export interface MemoryUsageResult<T> {
|
|
54
|
-
averageHeapUsed: number
|
|
55
|
-
maxHeapUsed: number
|
|
56
|
-
memoryLeakDetected: boolean
|
|
57
|
-
results: T[]
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
/** Route handler memory leak result */
|
|
61
|
-
export interface RouteHandlerMemoryLeakResult {
|
|
62
|
-
memoryLeakDetected: boolean
|
|
63
|
-
initialMemory: number
|
|
64
|
-
finalMemory: number
|
|
65
|
-
memoryGrowth: number
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
/** Ramp up test options */
|
|
69
|
-
export interface RampUpTestOptions {
|
|
70
|
-
startConcurrency: number
|
|
71
|
-
endConcurrency: number
|
|
72
|
-
rampUpTime: number
|
|
73
|
-
testDuration: number
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
/** Ramp up test result */
|
|
77
|
-
export interface RampUpTestResult {
|
|
78
|
-
concurrency: number
|
|
79
|
-
requestsPerSecond: number
|
|
80
|
-
averageResponseTime: number
|
|
81
|
-
errorRate: number
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
/** Spike test options */
|
|
85
|
-
export interface SpikeTestOptions {
|
|
86
|
-
normalLoad: number
|
|
87
|
-
spikeLoad: number
|
|
88
|
-
spikeDuration: number
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
/** Spike test result */
|
|
92
|
-
export interface SpikeTestResult {
|
|
93
|
-
normalPhase: any
|
|
94
|
-
spikePhase: any
|
|
95
|
-
recoveryPhase: any
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
/**
|
|
99
|
-
* Performance testing utilities
|
|
100
|
-
*/
|
|
101
|
-
export class PerformanceTester {
|
|
102
|
-
private metrics: PerformanceMetrics[] = []
|
|
103
|
-
private startTime: number = 0
|
|
104
|
-
private endTime: number = 0
|
|
105
|
-
|
|
106
|
-
/**
|
|
107
|
-
* Start performance measurement
|
|
108
|
-
*/
|
|
109
|
-
start(): PerformanceTester {
|
|
110
|
-
this.startTime = performance.now()
|
|
111
|
-
return this
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
/**
|
|
115
|
-
* End performance measurement
|
|
116
|
-
*/
|
|
117
|
-
end(): PerformanceTester {
|
|
118
|
-
this.endTime = performance.now()
|
|
119
|
-
return this
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
/**
|
|
123
|
-
* Record a performance metric
|
|
124
|
-
*/
|
|
125
|
-
recordMetric(metric: PerformanceMetrics): PerformanceTester {
|
|
126
|
-
this.metrics.push(metric)
|
|
127
|
-
return this
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
/**
|
|
131
|
-
* Get elapsed time
|
|
132
|
-
*/
|
|
133
|
-
getElapsedTime(): number {
|
|
134
|
-
return this.endTime - this.startTime
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
/**
|
|
138
|
-
* Get all recorded metrics
|
|
139
|
-
*/
|
|
140
|
-
getMetrics(): PerformanceMetrics[] {
|
|
141
|
-
return [...this.metrics]
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
/**
|
|
145
|
-
* Get average response time
|
|
146
|
-
*/
|
|
147
|
-
getAverageResponseTime(): number {
|
|
148
|
-
if (this.metrics.length === 0)
|
|
149
|
-
return 0
|
|
150
|
-
const total = this.metrics.reduce((sum, metric) => sum + metric.responseTime, 0)
|
|
151
|
-
return total / this.metrics.length
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
/**
|
|
155
|
-
* Get memory usage statistics
|
|
156
|
-
*/
|
|
157
|
-
getMemoryStats(): {
|
|
158
|
-
averageHeapUsed: number
|
|
159
|
-
maxHeapUsed: number
|
|
160
|
-
averageRSS: number
|
|
161
|
-
maxRSS: number
|
|
162
|
-
} {
|
|
163
|
-
if (this.metrics.length === 0) {
|
|
164
|
-
return { averageHeapUsed: 0, maxHeapUsed: 0, averageRSS: 0, maxRSS: 0 }
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
const heapUsed = this.metrics.map(m => m.memoryUsage.heapUsed)
|
|
168
|
-
const rss = this.metrics.map(m => m.memoryUsage.rss)
|
|
169
|
-
|
|
170
|
-
return {
|
|
171
|
-
averageHeapUsed: heapUsed.reduce((sum, val) => sum + val, 0) / heapUsed.length,
|
|
172
|
-
maxHeapUsed: Math.max(...heapUsed),
|
|
173
|
-
averageRSS: rss.reduce((sum, val) => sum + val, 0) / rss.length,
|
|
174
|
-
maxRSS: Math.max(...rss),
|
|
175
|
-
}
|
|
176
|
-
}
|
|
177
|
-
|
|
178
|
-
/**
|
|
179
|
-
* Clear all metrics
|
|
180
|
-
*/
|
|
181
|
-
clear(): PerformanceTester {
|
|
182
|
-
this.metrics = []
|
|
183
|
-
this.startTime = 0
|
|
184
|
-
this.endTime = 0
|
|
185
|
-
return this
|
|
186
|
-
}
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
/**
|
|
190
|
-
* Load testing utilities
|
|
191
|
-
*/
|
|
192
|
-
export class LoadTester {
|
|
193
|
-
private client: TestClient
|
|
194
|
-
private config: LoadTestConfig
|
|
195
|
-
|
|
196
|
-
constructor(client: TestClient, config: LoadTestConfig) {
|
|
197
|
-
this.client = client
|
|
198
|
-
this.config = config
|
|
199
|
-
}
|
|
200
|
-
|
|
201
|
-
/**
|
|
202
|
-
* Run load test
|
|
203
|
-
*/
|
|
204
|
-
async run(
|
|
205
|
-
path: string,
|
|
206
|
-
method: 'GET' | 'POST' | 'PUT' | 'DELETE' = 'GET',
|
|
207
|
-
requestOptions: any = {},
|
|
208
|
-
): Promise<{
|
|
209
|
-
totalRequests: number
|
|
210
|
-
successfulRequests: number
|
|
211
|
-
failedRequests: number
|
|
212
|
-
averageResponseTime: number
|
|
213
|
-
requestsPerSecond: number
|
|
214
|
-
errors: Array<{ error: string, count: number }>
|
|
215
|
-
}> {
|
|
216
|
-
const results = {
|
|
217
|
-
totalRequests: 0,
|
|
218
|
-
successfulRequests: 0,
|
|
219
|
-
failedRequests: 0,
|
|
220
|
-
averageResponseTime: 0,
|
|
221
|
-
requestsPerSecond: 0,
|
|
222
|
-
errors: [] as Array<{ error: string, count: number }>,
|
|
223
|
-
}
|
|
224
|
-
|
|
225
|
-
const responseTimes: number[] = []
|
|
226
|
-
const errorCounts = new Map<string, number>()
|
|
227
|
-
const startTime = Date.now()
|
|
228
|
-
|
|
229
|
-
// Calculate total requests based on duration and rate
|
|
230
|
-
const totalRequests = this.config.maxRequests
|
|
231
|
-
|| Math.floor((this.config.duration / 1000) * (this.config.requestsPerSecond || this.config.concurrency))
|
|
232
|
-
|
|
233
|
-
const requestPromises: Promise<void>[] = []
|
|
234
|
-
|
|
235
|
-
for (let i = 0; i < totalRequests; i++) {
|
|
236
|
-
const requestPromise = this.makeRequest(path, method, requestOptions)
|
|
237
|
-
.then((responseTime) => {
|
|
238
|
-
responseTimes.push(responseTime)
|
|
239
|
-
results.successfulRequests++
|
|
240
|
-
})
|
|
241
|
-
.catch((error) => {
|
|
242
|
-
const errorMessage = error instanceof Error ? error.message : 'Unknown error'
|
|
243
|
-
errorCounts.set(errorMessage, (errorCounts.get(errorMessage) || 0) + 1)
|
|
244
|
-
results.failedRequests++
|
|
245
|
-
})
|
|
246
|
-
|
|
247
|
-
requestPromises.push(requestPromise)
|
|
248
|
-
|
|
249
|
-
// Apply rate limiting if specified
|
|
250
|
-
if (this.config.requestsPerSecond) {
|
|
251
|
-
const delay = 1000 / this.config.requestsPerSecond
|
|
252
|
-
await new Promise(resolve => setTimeout(resolve, delay))
|
|
253
|
-
}
|
|
254
|
-
|
|
255
|
-
// Apply concurrency limiting
|
|
256
|
-
if (requestPromises.length >= this.config.concurrency) {
|
|
257
|
-
await Promise.race(requestPromises)
|
|
258
|
-
// Remove completed promises
|
|
259
|
-
const completedPromises = requestPromises.filter(p =>
|
|
260
|
-
p.then(() => true).catch(() => true),
|
|
261
|
-
)
|
|
262
|
-
completedPromises.forEach((p) => {
|
|
263
|
-
const index = requestPromises.indexOf(p)
|
|
264
|
-
if (index > -1)
|
|
265
|
-
requestPromises.splice(index, 1)
|
|
266
|
-
})
|
|
267
|
-
}
|
|
268
|
-
}
|
|
269
|
-
|
|
270
|
-
// Wait for all remaining requests to complete
|
|
271
|
-
await Promise.allSettled(requestPromises)
|
|
272
|
-
|
|
273
|
-
const endTime = Date.now()
|
|
274
|
-
const totalTime = (endTime - startTime) / 1000
|
|
275
|
-
|
|
276
|
-
results.totalRequests = totalRequests
|
|
277
|
-
results.averageResponseTime = responseTimes.length > 0
|
|
278
|
-
? responseTimes.reduce((sum, time) => sum + time, 0) / responseTimes.length
|
|
279
|
-
: 0
|
|
280
|
-
results.requestsPerSecond = totalRequests / totalTime
|
|
281
|
-
results.errors = Array.from(errorCounts.entries()).map(([error, count]) => ({ error, count }))
|
|
282
|
-
|
|
283
|
-
return results
|
|
284
|
-
}
|
|
285
|
-
|
|
286
|
-
private async makeRequest(
|
|
287
|
-
path: string,
|
|
288
|
-
method: 'GET' | 'POST' | 'PUT' | 'DELETE',
|
|
289
|
-
options: any,
|
|
290
|
-
): Promise<number> {
|
|
291
|
-
const startTime = performance.now()
|
|
292
|
-
|
|
293
|
-
try {
|
|
294
|
-
await this.client.request(method, path, options)
|
|
295
|
-
const endTime = performance.now()
|
|
296
|
-
return endTime - startTime
|
|
297
|
-
}
|
|
298
|
-
catch (error) {
|
|
299
|
-
const _endTime = performance.now()
|
|
300
|
-
throw error
|
|
301
|
-
}
|
|
302
|
-
}
|
|
303
|
-
}
|
|
304
|
-
|
|
305
|
-
/** Route matching benchmark result */
|
|
306
|
-
export interface RouteMatchingBenchmarkResult {
|
|
307
|
-
averageTime: number
|
|
308
|
-
minTime: number
|
|
309
|
-
maxTime: number
|
|
310
|
-
totalTime: number
|
|
311
|
-
matchRate: number
|
|
312
|
-
}
|
|
313
|
-
|
|
314
|
-
/**
|
|
315
|
-
* Benchmark testing utilities
|
|
316
|
-
*/
|
|
317
|
-
export const benchmarkTests: {
|
|
318
|
-
routeHandler: (handler: (req: EnhancedRequest) => Promise<Response>, iterations?: number) => Promise<RouteHandlerBenchmarkResult>
|
|
319
|
-
middleware: (middleware: (req: EnhancedRequest, next: () => Promise<Response | null>) => Promise<Response | null>, iterations?: number) => Promise<MiddlewareBenchmarkResult>
|
|
320
|
-
jsonSerialization: (data: any, iterations?: number) => Promise<JsonBenchmarkResult>
|
|
321
|
-
routeMatching: (routes: Array<{ path: string, handler: any }>, testPaths: string[], iterations?: number) => Promise<RouteMatchingBenchmarkResult>
|
|
322
|
-
} = {
|
|
323
|
-
/**
|
|
324
|
-
* Benchmark route handler performance
|
|
325
|
-
*/
|
|
326
|
-
routeHandler: async (
|
|
327
|
-
handler: (req: EnhancedRequest) => Promise<Response>,
|
|
328
|
-
iterations: number = 1000,
|
|
329
|
-
): Promise<RouteHandlerBenchmarkResult> => {
|
|
330
|
-
const times: number[] = []
|
|
331
|
-
const request = createMockRequest()
|
|
332
|
-
|
|
333
|
-
for (let i = 0; i < iterations; i++) {
|
|
334
|
-
const start = performance.now()
|
|
335
|
-
await handler(request)
|
|
336
|
-
const end = performance.now()
|
|
337
|
-
times.push(end - start)
|
|
338
|
-
}
|
|
339
|
-
|
|
340
|
-
const totalTime = times.reduce((sum, time) => sum + time, 0)
|
|
341
|
-
const averageTime = totalTime / iterations
|
|
342
|
-
const minTime = Math.min(...times)
|
|
343
|
-
const maxTime = Math.max(...times)
|
|
344
|
-
const throughput = (iterations / totalTime) * 1000 // requests per second
|
|
345
|
-
|
|
346
|
-
return {
|
|
347
|
-
averageTime,
|
|
348
|
-
minTime,
|
|
349
|
-
maxTime,
|
|
350
|
-
totalTime,
|
|
351
|
-
throughput,
|
|
352
|
-
}
|
|
353
|
-
},
|
|
354
|
-
|
|
355
|
-
/**
|
|
356
|
-
* Benchmark middleware performance
|
|
357
|
-
*/
|
|
358
|
-
middleware: async (
|
|
359
|
-
middleware: (req: EnhancedRequest, next: () => Promise<Response | null>) => Promise<Response | null>,
|
|
360
|
-
iterations: number = 1000,
|
|
361
|
-
): Promise<MiddlewareBenchmarkResult> => {
|
|
362
|
-
const times: number[] = []
|
|
363
|
-
const request = createMockRequest()
|
|
364
|
-
const next = mock(async () => new Response('OK'))
|
|
365
|
-
|
|
366
|
-
for (let i = 0; i < iterations; i++) {
|
|
367
|
-
const start = performance.now()
|
|
368
|
-
await middleware(request, next)
|
|
369
|
-
const end = performance.now()
|
|
370
|
-
times.push(end - start)
|
|
371
|
-
}
|
|
372
|
-
|
|
373
|
-
const totalTime = times.reduce((sum, time) => sum + time, 0)
|
|
374
|
-
const averageTime = totalTime / iterations
|
|
375
|
-
const minTime = Math.min(...times)
|
|
376
|
-
const maxTime = Math.max(...times)
|
|
377
|
-
|
|
378
|
-
return {
|
|
379
|
-
averageTime,
|
|
380
|
-
minTime,
|
|
381
|
-
maxTime,
|
|
382
|
-
totalTime,
|
|
383
|
-
}
|
|
384
|
-
},
|
|
385
|
-
|
|
386
|
-
/**
|
|
387
|
-
* Benchmark JSON serialization
|
|
388
|
-
*/
|
|
389
|
-
jsonSerialization: async (
|
|
390
|
-
data: any,
|
|
391
|
-
iterations: number = 10000,
|
|
392
|
-
): Promise<JsonBenchmarkResult> => {
|
|
393
|
-
const times: number[] = []
|
|
394
|
-
|
|
395
|
-
for (let i = 0; i < iterations; i++) {
|
|
396
|
-
const start = performance.now()
|
|
397
|
-
const _serialized = JSON.stringify(data)
|
|
398
|
-
const end = performance.now()
|
|
399
|
-
times.push(end - start)
|
|
400
|
-
}
|
|
401
|
-
|
|
402
|
-
const totalTime = times.reduce((sum, time) => sum + time, 0)
|
|
403
|
-
const averageTime = totalTime / iterations
|
|
404
|
-
const minTime = Math.min(...times)
|
|
405
|
-
const maxTime = Math.max(...times)
|
|
406
|
-
const dataSize = JSON.stringify(data).length
|
|
407
|
-
|
|
408
|
-
return {
|
|
409
|
-
averageTime,
|
|
410
|
-
minTime,
|
|
411
|
-
maxTime,
|
|
412
|
-
totalTime,
|
|
413
|
-
dataSize,
|
|
414
|
-
}
|
|
415
|
-
},
|
|
416
|
-
|
|
417
|
-
/**
|
|
418
|
-
* Benchmark route matching
|
|
419
|
-
*/
|
|
420
|
-
routeMatching: async (
|
|
421
|
-
routes: Array<{ path: string, handler: any }>,
|
|
422
|
-
testPaths: string[],
|
|
423
|
-
iterations: number = 1000,
|
|
424
|
-
): Promise<RouteMatchingBenchmarkResult> => {
|
|
425
|
-
const times: number[] = []
|
|
426
|
-
let matches = 0
|
|
427
|
-
|
|
428
|
-
for (let i = 0; i < iterations; i++) {
|
|
429
|
-
const testPath = testPaths[i % testPaths.length]
|
|
430
|
-
const start = performance.now()
|
|
431
|
-
|
|
432
|
-
// Simple route matching simulation
|
|
433
|
-
const match = routes.find((route) => {
|
|
434
|
-
const pattern = route.path.replace(/:\w+/g, '[^/]+')
|
|
435
|
-
const regex = new RegExp(`^${pattern}$`)
|
|
436
|
-
return regex.test(testPath)
|
|
437
|
-
})
|
|
438
|
-
|
|
439
|
-
if (match)
|
|
440
|
-
matches++
|
|
441
|
-
|
|
442
|
-
const end = performance.now()
|
|
443
|
-
times.push(end - start)
|
|
444
|
-
}
|
|
445
|
-
|
|
446
|
-
const totalTime = times.reduce((sum, time) => sum + time, 0)
|
|
447
|
-
const averageTime = totalTime / iterations
|
|
448
|
-
const minTime = Math.min(...times)
|
|
449
|
-
const maxTime = Math.max(...times)
|
|
450
|
-
const matchRate = matches / iterations
|
|
451
|
-
|
|
452
|
-
return {
|
|
453
|
-
averageTime,
|
|
454
|
-
minTime,
|
|
455
|
-
maxTime,
|
|
456
|
-
totalTime,
|
|
457
|
-
matchRate,
|
|
458
|
-
}
|
|
459
|
-
},
|
|
460
|
-
}
|
|
461
|
-
|
|
462
|
-
/**
|
|
463
|
-
* Memory testing utilities
|
|
464
|
-
*/
|
|
465
|
-
export const memoryTests: {
|
|
466
|
-
measureMemoryUsage: <T>(fn: () => Promise<T> | T, iterations?: number) => Promise<MemoryUsageResult<T>>
|
|
467
|
-
testRouteHandlerMemoryLeaks: (handler: (req: EnhancedRequest) => Promise<Response>, iterations?: number) => Promise<RouteHandlerMemoryLeakResult>
|
|
468
|
-
} = {
|
|
469
|
-
/**
|
|
470
|
-
* Test memory usage of a function
|
|
471
|
-
*/
|
|
472
|
-
measureMemoryUsage: async <T>(
|
|
473
|
-
fn: () => Promise<T> | T,
|
|
474
|
-
iterations: number = 100,
|
|
475
|
-
): Promise<MemoryUsageResult<T>> => {
|
|
476
|
-
const results: T[] = []
|
|
477
|
-
const heapUsages: number[] = []
|
|
478
|
-
|
|
479
|
-
// Force garbage collection if available
|
|
480
|
-
if (globalThis.gc) {
|
|
481
|
-
globalThis.gc()
|
|
482
|
-
}
|
|
483
|
-
|
|
484
|
-
const initialHeap = process.memoryUsage().heapUsed
|
|
485
|
-
|
|
486
|
-
for (let i = 0; i < iterations; i++) {
|
|
487
|
-
const result = await fn()
|
|
488
|
-
results.push(result)
|
|
489
|
-
|
|
490
|
-
const memUsage = process.memoryUsage()
|
|
491
|
-
heapUsages.push(memUsage.heapUsed)
|
|
492
|
-
|
|
493
|
-
// Periodic garbage collection
|
|
494
|
-
if (i % 10 === 0 && globalThis.gc) {
|
|
495
|
-
globalThis.gc()
|
|
496
|
-
}
|
|
497
|
-
}
|
|
498
|
-
|
|
499
|
-
// Final garbage collection
|
|
500
|
-
if (globalThis.gc) {
|
|
501
|
-
globalThis.gc()
|
|
502
|
-
}
|
|
503
|
-
|
|
504
|
-
const finalHeap = process.memoryUsage().heapUsed
|
|
505
|
-
const averageHeapUsed = heapUsages.reduce((sum, usage) => sum + usage, 0) / heapUsages.length
|
|
506
|
-
const maxHeapUsed = Math.max(...heapUsages)
|
|
507
|
-
|
|
508
|
-
// Simple memory leak detection
|
|
509
|
-
const memoryLeakDetected = (finalHeap - initialHeap) > (1024 * 1024) // 1MB threshold
|
|
510
|
-
|
|
511
|
-
return {
|
|
512
|
-
averageHeapUsed,
|
|
513
|
-
maxHeapUsed,
|
|
514
|
-
memoryLeakDetected,
|
|
515
|
-
results,
|
|
516
|
-
}
|
|
517
|
-
},
|
|
518
|
-
|
|
519
|
-
/**
|
|
520
|
-
* Test for memory leaks in route handlers
|
|
521
|
-
*/
|
|
522
|
-
testRouteHandlerMemoryLeaks: async (
|
|
523
|
-
handler: (req: EnhancedRequest) => Promise<Response>,
|
|
524
|
-
iterations: number = 1000,
|
|
525
|
-
): Promise<RouteHandlerMemoryLeakResult> => {
|
|
526
|
-
if (globalThis.gc)
|
|
527
|
-
globalThis.gc()
|
|
528
|
-
const initialMemory = process.memoryUsage().heapUsed
|
|
529
|
-
|
|
530
|
-
for (let i = 0; i < iterations; i++) {
|
|
531
|
-
const request = createMockRequest()
|
|
532
|
-
await handler(request)
|
|
533
|
-
|
|
534
|
-
if (i % 100 === 0 && globalThis.gc) {
|
|
535
|
-
globalThis.gc()
|
|
536
|
-
}
|
|
537
|
-
}
|
|
538
|
-
|
|
539
|
-
if (globalThis.gc)
|
|
540
|
-
globalThis.gc()
|
|
541
|
-
const finalMemory = process.memoryUsage().heapUsed
|
|
542
|
-
const memoryGrowth = finalMemory - initialMemory
|
|
543
|
-
const memoryLeakDetected = memoryGrowth > (5 * 1024 * 1024) // 5MB threshold
|
|
544
|
-
|
|
545
|
-
return {
|
|
546
|
-
memoryLeakDetected,
|
|
547
|
-
initialMemory,
|
|
548
|
-
finalMemory,
|
|
549
|
-
memoryGrowth,
|
|
550
|
-
}
|
|
551
|
-
},
|
|
552
|
-
}
|
|
553
|
-
|
|
554
|
-
/**
|
|
555
|
-
* Stress testing utilities
|
|
556
|
-
*/
|
|
557
|
-
export const stressTests: {
|
|
558
|
-
rampUpTest: (client: TestClient, path: string, options: RampUpTestOptions) => Promise<RampUpTestResult[]>
|
|
559
|
-
spikeTest: (client: TestClient, path: string, options: SpikeTestOptions) => Promise<SpikeTestResult>
|
|
560
|
-
} = {
|
|
561
|
-
/**
|
|
562
|
-
* Stress test with increasing load
|
|
563
|
-
*/
|
|
564
|
-
rampUpTest: async (
|
|
565
|
-
client: TestClient,
|
|
566
|
-
path: string,
|
|
567
|
-
options: RampUpTestOptions,
|
|
568
|
-
): Promise<RampUpTestResult[]> => {
|
|
569
|
-
const results: RampUpTestResult[] = []
|
|
570
|
-
|
|
571
|
-
const steps = 10
|
|
572
|
-
const stepDuration = options.testDuration / steps
|
|
573
|
-
const concurrencyStep = (options.endConcurrency - options.startConcurrency) / steps
|
|
574
|
-
|
|
575
|
-
for (let step = 0; step < steps; step++) {
|
|
576
|
-
const concurrency = Math.round(options.startConcurrency + (concurrencyStep * step))
|
|
577
|
-
|
|
578
|
-
const loadTester = new LoadTester(client, {
|
|
579
|
-
duration: stepDuration,
|
|
580
|
-
concurrency,
|
|
581
|
-
requestsPerSecond: concurrency * 2, // Allow higher RPS than concurrency
|
|
582
|
-
})
|
|
583
|
-
|
|
584
|
-
const result = await loadTester.run(path)
|
|
585
|
-
|
|
586
|
-
results.push({
|
|
587
|
-
concurrency,
|
|
588
|
-
requestsPerSecond: result.requestsPerSecond,
|
|
589
|
-
averageResponseTime: result.averageResponseTime,
|
|
590
|
-
errorRate: result.failedRequests / result.totalRequests,
|
|
591
|
-
})
|
|
592
|
-
}
|
|
593
|
-
|
|
594
|
-
return results
|
|
595
|
-
},
|
|
596
|
-
|
|
597
|
-
/**
|
|
598
|
-
* Spike test - sudden load increase
|
|
599
|
-
*/
|
|
600
|
-
spikeTest: async (
|
|
601
|
-
client: TestClient,
|
|
602
|
-
path: string,
|
|
603
|
-
options: SpikeTestOptions,
|
|
604
|
-
): Promise<SpikeTestResult> => {
|
|
605
|
-
// Normal load phase
|
|
606
|
-
const normalTester = new LoadTester(client, {
|
|
607
|
-
duration: 30000, // 30 seconds
|
|
608
|
-
concurrency: options.normalLoad,
|
|
609
|
-
})
|
|
610
|
-
const normalPhase = await normalTester.run(path)
|
|
611
|
-
|
|
612
|
-
// Spike phase
|
|
613
|
-
const spikeTester = new LoadTester(client, {
|
|
614
|
-
duration: options.spikeDuration,
|
|
615
|
-
concurrency: options.spikeLoad,
|
|
616
|
-
})
|
|
617
|
-
const spikePhase = await spikeTester.run(path)
|
|
618
|
-
|
|
619
|
-
// Recovery phase
|
|
620
|
-
const recoveryTester = new LoadTester(client, {
|
|
621
|
-
duration: 30000, // 30 seconds
|
|
622
|
-
concurrency: options.normalLoad,
|
|
623
|
-
})
|
|
624
|
-
const recoveryPhase = await recoveryTester.run(path)
|
|
625
|
-
|
|
626
|
-
return {
|
|
627
|
-
normalPhase,
|
|
628
|
-
spikePhase,
|
|
629
|
-
recoveryPhase,
|
|
630
|
-
}
|
|
631
|
-
},
|
|
632
|
-
}
|
|
633
|
-
|
|
634
|
-
/**
|
|
635
|
-
* Performance assertion helpers
|
|
636
|
-
*/
|
|
637
|
-
export const performanceAssertions = {
|
|
638
|
-
/**
|
|
639
|
-
* Assert response time is within acceptable range
|
|
640
|
-
*/
|
|
641
|
-
responseTimeWithin: (actualTime: number, maxTime: number): void => {
|
|
642
|
-
if (actualTime > maxTime) {
|
|
643
|
-
throw new Error(`Response time ${actualTime}ms exceeds maximum ${maxTime}ms`)
|
|
644
|
-
}
|
|
645
|
-
},
|
|
646
|
-
|
|
647
|
-
/**
|
|
648
|
-
* Assert throughput meets minimum requirements
|
|
649
|
-
*/
|
|
650
|
-
throughputAtLeast: (actualThroughput: number, minThroughput: number): void => {
|
|
651
|
-
if (actualThroughput < minThroughput) {
|
|
652
|
-
throw new Error(`Throughput ${actualThroughput} RPS is below minimum ${minThroughput} RPS`)
|
|
653
|
-
}
|
|
654
|
-
},
|
|
655
|
-
|
|
656
|
-
/**
|
|
657
|
-
* Assert error rate is acceptable
|
|
658
|
-
*/
|
|
659
|
-
errorRateBelow: (errorRate: number, maxErrorRate: number): void => {
|
|
660
|
-
if (errorRate > maxErrorRate) {
|
|
661
|
-
throw new Error(`Error rate ${(errorRate * 100).toFixed(2)}% exceeds maximum ${(maxErrorRate * 100).toFixed(2)}%`)
|
|
662
|
-
}
|
|
663
|
-
},
|
|
664
|
-
|
|
665
|
-
/**
|
|
666
|
-
* Assert memory usage is within limits
|
|
667
|
-
*/
|
|
668
|
-
memoryUsageWithin: (actualUsage: number, maxUsage: number): void => {
|
|
669
|
-
if (actualUsage > maxUsage) {
|
|
670
|
-
throw new Error(`Memory usage ${(actualUsage / 1024 / 1024).toFixed(2)}MB exceeds maximum ${(maxUsage / 1024 / 1024).toFixed(2)}MB`)
|
|
671
|
-
}
|
|
672
|
-
},
|
|
673
|
-
|
|
674
|
-
/**
|
|
675
|
-
* Assert no memory leaks detected
|
|
676
|
-
*/
|
|
677
|
-
noMemoryLeaks: (memoryGrowth: number, threshold: number = 5 * 1024 * 1024): void => {
|
|
678
|
-
if (memoryGrowth > threshold) {
|
|
679
|
-
throw new Error(`Potential memory leak detected: ${(memoryGrowth / 1024 / 1024).toFixed(2)}MB growth`)
|
|
680
|
-
}
|
|
681
|
-
},
|
|
682
|
-
}
|
|
683
|
-
|
|
684
|
-
/**
|
|
685
|
-
* Factory functions
|
|
686
|
-
*/
|
|
687
|
-
export function createPerformanceTester(): PerformanceTester {
|
|
688
|
-
return new PerformanceTester()
|
|
689
|
-
}
|
|
690
|
-
|
|
691
|
-
export function createLoadTester(client: TestClient, config: LoadTestConfig): LoadTester {
|
|
692
|
-
return new LoadTester(client, config)
|
|
693
|
-
}
|
|
694
|
-
|
|
695
|
-
/**
|
|
696
|
-
* Performance test utilities
|
|
697
|
-
*/
|
|
698
|
-
export const performanceUtils = {
|
|
699
|
-
/**
|
|
700
|
-
* Get current memory usage
|
|
701
|
-
*/
|
|
702
|
-
getCurrentMemoryUsage: (): PerformanceMetrics['memoryUsage'] => {
|
|
703
|
-
const usage = process.memoryUsage()
|
|
704
|
-
return {
|
|
705
|
-
heapUsed: usage.heapUsed,
|
|
706
|
-
heapTotal: usage.heapTotal,
|
|
707
|
-
external: usage.external,
|
|
708
|
-
rss: usage.rss,
|
|
709
|
-
}
|
|
710
|
-
},
|
|
711
|
-
|
|
712
|
-
/**
|
|
713
|
-
* Get CPU usage
|
|
714
|
-
*/
|
|
715
|
-
getCPUUsage: (): PerformanceMetrics['cpuUsage'] => {
|
|
716
|
-
const usage = process.cpuUsage()
|
|
717
|
-
return {
|
|
718
|
-
user: usage.user,
|
|
719
|
-
system: usage.system,
|
|
720
|
-
}
|
|
721
|
-
},
|
|
722
|
-
|
|
723
|
-
/**
|
|
724
|
-
* Create performance metric snapshot
|
|
725
|
-
*/
|
|
726
|
-
createMetricSnapshot: (responseTime: number): PerformanceMetrics => ({
|
|
727
|
-
responseTime,
|
|
728
|
-
memoryUsage: performanceUtils.getCurrentMemoryUsage(),
|
|
729
|
-
cpuUsage: performanceUtils.getCPUUsage(),
|
|
730
|
-
}),
|
|
731
|
-
|
|
732
|
-
/**
|
|
733
|
-
* Format performance results for display
|
|
734
|
-
*/
|
|
735
|
-
formatResults: (results: any): string => {
|
|
736
|
-
return JSON.stringify(results, null, 2)
|
|
737
|
-
},
|
|
738
|
-
}
|