1+ #! /bin/bash
2+
3+ # Show usage information
4+ show_help () {
5+ echo " Dragon Resort API Load Test Script"
6+ echo
7+ echo " Usage: $0 [OPTIONS] [api-endpoint]"
8+ echo
9+ echo " Options:"
10+ echo " [--help] Shows this help message"
11+ echo
12+ echo " Arguments:"
13+ echo " [api-endpoint] Optional. If not provided, will attempt to discover endpoint using AWS CLI"
14+ echo
15+ echo " Environment Variables:"
16+ echo " [AWS_REGION] Required if no api-endpoint provided. AWS region for API discovery"
17+ echo
18+ echo " Examples:"
19+ echo " $0 # Uses AWS CLI for endpoint discovery"
20+ echo " $0 https://api-id.execute-api.region.amazonaws.com/prod # Uses provided endpoint"
21+ echo " AWS_REGION=eu-central-1 $0 # Explicitly sets region for discovery"
22+ }
23+
24+ # Show help if requested
25+ if [ " $1 " = " --help" ]; then
26+ show_help
27+ exit 0
28+ fi
29+
30+ # Function to get API endpoint from AWS CLI
31+ get_aws_endpoint () {
32+ # Check if AWS_REGION is set
33+ if [ -z " $AWS_REGION " ]; then
34+ echo " Error: AWS_REGION environment variable is not set"
35+ echo " Please set AWS_REGION or provide an API endpoint directly"
36+ echo
37+ show_help
38+ exit 1
39+ fi
40+
41+ local api_name=" Dragon Resort API"
42+
43+ # Get API ID from name (REST API)
44+ local api_id=$( aws apigateway get-rest-apis --region $AWS_REGION | \
45+ jq -r --arg NAME " $api_name " ' .items[] | select(.name==$NAME) | .id' )
46+
47+ if [ -z " $api_id " ]; then
48+ echo " Failed to find REST API with name: $api_name "
49+ exit 1
50+ fi
51+
52+ # Construct API endpoint for REST API
53+ echo " https://${api_id} .execute-api.${AWS_REGION} .amazonaws.com/prod"
54+ }
55+
56+
57+ # Check if API endpoint is provided as argument
58+ if [ $# -eq 1 ]; then
59+ API_ENDPOINT=$1
60+ echo " Using provided API endpoint: $API_ENDPOINT "
61+ else
62+ # Get API endpoint from AWS CLI
63+ output=$( get_aws_endpoint 2>&1 )
64+ exit_code=$?
65+ if [ $exit_code -ne 0 ]; then
66+ echo " $output " # Print the error message
67+ exit $exit_code
68+ fi
69+ API_ENDPOINT=$output
70+ echo " Using AWS CLI discovered endpoint: $API_ENDPOINT "
71+ fi
72+
73+
74+
75+ # Rest of the configuration
76+ NUM_REQUESTS=100
77+ CONTENT_TYPE=" Content-Type: application/json"
78+
79+ # Dragon characteristics arrays
80+ FIRST_NAMES=(" Ancient" " Mighty" " Wise" " Fierce" " Noble" " Shadow" " Storm" " Crystal" " Ember" " Frost" )
81+ SECOND_NAMES=(" Wing" " Claw" " Fang" " Scale" " Heart" " Soul" " Spirit" " Flame" " Thunder" " Ice" )
82+ TYPES=(" Celestial Dragon" " Fire Dragon" " Ice Dragon" " Storm Dragon" " Earth Dragon" )
83+ COLORS=(" bronze" " golden" " silver" " crimson" " azure" " emerald" " obsidian" )
84+
85+ # Function to get random element from an array
86+ get_random_element () {
87+ local array=(" $@ " )
88+ local index=$(( RANDOM % ${# array[@]} ))
89+ echo " ${array[$index]} "
90+ }
91+
92+ # Function to generate random number in range
93+ random_number () {
94+ local min=$1
95+ local max=$2
96+ echo $(( RANDOM % (max - min + 1 ) + min))
97+ }
98+
99+ # Function to create dragon payload with random attributes and occasional invalid JSON
100+ create_dragon_payload () {
101+ local id=$1
102+ local first_name=$( get_random_element " ${FIRST_NAMES[@]} " )
103+ local second_name=$( get_random_element " ${SECOND_NAMES[@]} " )
104+ local dragon_type=$( get_random_element " ${TYPES[@]} " )
105+ local color=$( get_random_element " ${COLORS[@]} " )
106+ local age=$( random_number 100 5000)
107+
108+ # Randomly decide to create an invalid payload (20% chance)
109+ if [ $(( RANDOM % 5 )) -eq 0 ]; then
110+ # Array of different ways to make invalid JSON
111+ local invalid_types=(
112+ " missing_comma"
113+ " missing_quote"
114+ " missing_field"
115+ " incomplete_json"
116+ )
117+
118+ case $( get_random_element " ${invalid_types[@]} " ) in
119+ " missing_comma" )
120+ # Missing comma between fields
121+ echo " {
122+ \" id\" : \" $id \"
123+ \" name\" : \" $first_name$second_name \"
124+ \" type\" : \" $dragon_type \"
125+ \" age\" : \" $age \"
126+ \" color\" : \" $color \"
127+ }"
128+ ;;
129+ " missing_quote" )
130+ # Missing closing quote
131+ echo " {
132+ \" id\" : \" $id \" ,
133+ \" name\" : \" $first_name$second_name ,
134+ \" type\" : \" $dragon_type \" ,
135+ \" age\" : \" $age \" ,
136+ \" color\" : \" $color \"
137+ }"
138+ ;;
139+ " missing_field" )
140+ # Randomly omit one field
141+ local fields=(" id" " name" " type" " age" " color" )
142+ local omit_field=$( get_random_element " ${fields[@]} " )
143+ local json=" {"
144+
145+ [ " $omit_field " != " id" ] && json=" $json \" id\" : \" $id \" ,"
146+ [ " $omit_field " != " name" ] && json=" $json \" name\" : \" $first_name$second_name \" ,"
147+ [ " $omit_field " != " type" ] && json=" $json \" type\" : \" $dragon_type \" ,"
148+ [ " $omit_field " != " age" ] && json=" $json \" age\" : \" $age \" ,"
149+ [ " $omit_field " != " color" ] && json=" $json \" color\" : \" $color \" "
150+
151+ echo " $json }"
152+ ;;
153+ " incomplete_json" )
154+ # Incomplete JSON structure
155+ echo " {
156+ \" id\" : \" $id \" ,
157+ \" name\" : \" $first_name$second_name \" ,
158+ \" type\" : \" $dragon_type \" ,"
159+ ;;
160+ esac
161+
162+ echo " Generated invalid payload for testing error handling" >&2
163+ else
164+ # Generate valid payload
165+ echo " {
166+ \" id\" : \" $id \" ,
167+ \" name\" : \" $first_name$second_name \" ,
168+ \" type\" : \" $dragon_type \" ,
169+ \" age\" : \" $age \" ,
170+ \" color\" : \" $color \"
171+ }"
172+ fi
173+ }
174+
175+ # Function to make API calls with error handling and response logging
176+ make_request () {
177+ local method=$1
178+ local endpoint=$2
179+ local data=$3
180+
181+ response=$( curl -s -w " \n%{http_code}" -X " $method " \
182+ " $API_ENDPOINT$endpoint " \
183+ -H " $CONTENT_TYPE " \
184+ ${data: +-d " $data " } )
185+
186+ status_code=$( echo " $response " | tail -n1)
187+ response_body=$( echo " $response " | sed ' $d' )
188+
189+ if [ " $status_code " -lt 200 ] || [ " $status_code " -gt 299 ]; then
190+ echo " Error: $method request to $endpoint failed with status $status_code "
191+ echo " Response: $response_body "
192+ # Check if response contains invalid JSON error messages
193+ if echo " $response_body " | grep -q " Unexpected end-of-input\|Illegal unquoted character\|Unexpected character" ; then
194+ return 2 # Invalid JSON payload
195+ fi
196+ return 1 # Regular error
197+ fi
198+ return 0 # Success
199+ }
200+
201+ # Function to run batch of requests with progress indicator and invalid payload handling
202+ run_batch () {
203+ local operation=$1
204+ local total=$2
205+ local successful=0
206+ local failed=0
207+ local invalid_payloads=0
208+
209+ echo " Starting $operation test..."
210+ for (( i= 1 ; i<= $total ; i++ )) ; do
211+ echo -ne " Progress: $i /$total (Success: $successful , Failed: $failed , Invalid: $invalid_payloads )\r"
212+
213+ case $operation in
214+ " CREATE" )
215+ payload=$( create_dragon_payload $i )
216+ make_request " POST" " /dragons" " $payload "
217+ result=$?
218+
219+ case $result in
220+ 0) (( successful++ )) ;;
221+ 2) (( invalid_payloads++ )) ;;
222+ * ) (( failed++ )) ;;
223+ esac
224+ ;;
225+ " LIST" )
226+ if make_request " GET" " /dragons" ; then
227+ (( successful++ ))
228+ else
229+ (( failed++ ))
230+ fi
231+ ;;
232+ " GET" )
233+ if make_request " GET" " /dragons/$i " ; then
234+ (( successful++ ))
235+ else
236+ (( failed++ ))
237+ fi
238+ ;;
239+ " DELETE" )
240+ if make_request " DELETE" " /dragons/$i " ; then
241+ (( successful++ ))
242+ else
243+ (( failed++ ))
244+ fi
245+ ;;
246+ esac
247+
248+ # Small delay to prevent overwhelming the API
249+ sleep 0.1
250+ done
251+ echo -e " \nCompleted $operation test (Success: $successful , Failed: $failed , Invalid: $invalid_payloads )"
252+ }
253+
254+
255+ # Main execution
256+ echo " Starting load test at $( date) "
257+ run_batch " CREATE" $NUM_REQUESTS
258+ run_batch " LIST" $NUM_REQUESTS
259+ run_batch " GET" $NUM_REQUESTS
260+ run_batch " DELETE" $NUM_REQUESTS
261+ echo " Load test completed at $( date) "
0 commit comments