1+ const EcommerceClient = require ( './EcommerceClient' ) ;
2+
3+ // Utility to add random delay
4+ const randomDelay = ( min , max ) => {
5+ const delay = Math . floor ( Math . random ( ) * ( max - min + 1 ) + min ) ;
6+ return new Promise ( resolve => setTimeout ( resolve , delay ) ) ;
7+ } ;
8+
9+ // Simulate a single user's behavior
10+ class VirtualUser {
11+ constructor ( userId , baseURL ) {
12+ this . userId = userId ;
13+ this . client = new EcommerceClient ( {
14+ baseURL,
15+ headers : {
16+ 'User-Agent' : `Virtual-User-${ userId } ` ,
17+ 'X-Test-User-Id' : userId . toString ( )
18+ }
19+ } ) ;
20+ }
21+
22+ async randomAction ( ) {
23+ // List of possible actions with their relative weights
24+ const actions = [
25+ { weight : 40 , action : ( ) => this . client . getInventory ( ) } , // Most common
26+ { weight : 30 , action : ( ) => this . client . getSales ( ) } , // Very common
27+ { weight : 15 , action : ( ) => this . client . getFulfillmentStatus ( 1 ) } , // Less common
28+ { weight : 10 , action : ( ) => this . client . getAllUsers ( ) } , // Occasional
29+ { weight : 5 , action : ( ) => this . createRandomFulfillment ( ) } // Rare
30+ ] ;
31+
32+ // Choose random action based on weights
33+ const totalWeight = actions . reduce ( ( sum , action ) => sum + action . weight , 0 ) ;
34+ let random = Math . random ( ) * totalWeight ;
35+
36+ for ( const { weight, action } of actions ) {
37+ if ( random < weight ) return action ( ) ;
38+ random -= weight ;
39+ }
40+ }
41+
42+ async createRandomFulfillment ( ) {
43+ const orderId = `ORD-${ Date . now ( ) } -${ this . userId } ` ;
44+ const warehouseId = Math . floor ( Math . random ( ) * 3 ) + 1 ;
45+ const items = [
46+ {
47+ product_id : Math . floor ( Math . random ( ) * 5 ) + 1 ,
48+ quantity : Math . floor ( Math . random ( ) * 3 ) + 1
49+ }
50+ ] ;
51+
52+ return this . client . createFulfillment ( orderId , warehouseId , items ) ;
53+ }
54+
55+ async simulateUserSession ( duration = 60000 ) {
56+ const startTime = Date . now ( ) ;
57+ const results = [ ] ;
58+
59+ while ( Date . now ( ) - startTime < duration ) {
60+ try {
61+ const startActionTime = Date . now ( ) ;
62+ const result = await this . randomAction ( ) ;
63+ const endActionTime = Date . now ( ) ;
64+
65+ results . push ( {
66+ userId : this . userId ,
67+ success : true ,
68+ responseTime : endActionTime - startActionTime
69+ } ) ;
70+
71+ // Random delay between actions (0.5 to 3 seconds)
72+ await randomDelay ( 500 , 3000 ) ;
73+ } catch ( error ) {
74+ results . push ( {
75+ userId : this . userId ,
76+ success : false ,
77+ error : error . message
78+ } ) ;
79+ }
80+ }
81+
82+ return results ;
83+ }
84+ }
85+
86+ // Load test orchestrator
87+ class LoadTest {
88+ constructor ( config = { } ) {
89+ this . baseURL = config . baseURL || 'https://your-api-endpoint.amazonaws.com' ;
90+ this . numUsers = config . numUsers || 10 ;
91+ this . testDuration = config . testDuration || 60000 ; // 1 minute default
92+ this . results = [ ] ;
93+ }
94+
95+ async run ( ) {
96+ console . log ( `Starting load test with ${ this . numUsers } users for ${ this . testDuration / 1000 } seconds` ) ;
97+ const startTime = Date . now ( ) ;
98+
99+ // Create and start all virtual users
100+ const users = Array . from ( { length : this . numUsers } ,
101+ ( _ , i ) => new VirtualUser ( i + 1 , this . baseURL )
102+ ) ;
103+
104+ const userSessions = users . map ( user =>
105+ user . simulateUserSession ( this . testDuration )
106+ ) ;
107+
108+ // Wait for all user sessions to complete
109+ const allResults = await Promise . all ( userSessions ) ;
110+ this . results = allResults . flat ( ) ;
111+
112+ // Calculate statistics
113+ this . printStatistics ( ) ;
114+ }
115+
116+ printStatistics ( ) {
117+ const totalRequests = this . results . length ;
118+ const successfulRequests = this . results . filter ( r => r . success ) . length ;
119+ const failedRequests = totalRequests - successfulRequests ;
120+
121+ const responseTimes = this . results
122+ . filter ( r => r . success )
123+ . map ( r => r . responseTime ) ;
124+
125+ const avgResponseTime = responseTimes . reduce ( ( a , b ) => a + b , 0 ) / responseTimes . length ;
126+
127+ console . log ( '\nLoad Test Results:' ) ;
128+ console . log ( '==================' ) ;
129+ console . log ( `Total Requests: ${ totalRequests } ` ) ;
130+ console . log ( `Successful Requests: ${ successfulRequests } ` ) ;
131+ console . log ( `Failed Requests: ${ failedRequests } ` ) ;
132+ console . log ( `Success Rate: ${ ( ( successfulRequests / totalRequests ) * 100 ) . toFixed ( 2 ) } %` ) ;
133+ console . log ( `Average Response Time: ${ avgResponseTime . toFixed ( 2 ) } ms` ) ;
134+
135+ // Print error distribution if any failures
136+ if ( failedRequests > 0 ) {
137+ const errorCounts = this . results
138+ . filter ( r => ! r . success )
139+ . reduce ( ( acc , curr ) => {
140+ acc [ curr . error ] = ( acc [ curr . error ] || 0 ) + 1 ;
141+ return acc ;
142+ } , { } ) ;
143+
144+ console . log ( '\nError Distribution:' ) ;
145+ Object . entries ( errorCounts ) . forEach ( ( [ error , count ] ) => {
146+ console . log ( `${ error } : ${ count } occurrences` ) ;
147+ } ) ;
148+ }
149+ }
150+ }
151+
152+ // Example usage
153+ const runLoadTest = async ( ) => {
154+ const loadTest = new LoadTest ( {
155+ baseURL : 'https://q25v3hkkfk.execute-api.us-east-1.amazonaws.com' ,
156+ numUsers : 50 , // 50 concurrent users
157+ testDuration : 300000 // 5 minutes
158+ } ) ;
159+
160+ await loadTest . run ( ) ;
161+ } ;
162+
163+ // Run the test
164+ runLoadTest ( ) . catch ( console . error ) ;
0 commit comments