@freshworks/shiftleft-tools 1.1.15 → 1.1.17

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.
@@ -1,35 +1,21 @@
1
1
  #!/usr/bin/env bash
2
2
 
3
3
  ###############################################################################
4
- # Local Test Runner
4
+ # Local Test Runner (generic)
5
5
  #
6
- # Runs API integration tests locally with optional JaCoCo coverage.
6
+ # Runs Postman/Newman API integration tests against a locally-started Spring
7
+ # Boot app, with optional JaCoCo coverage. LocalStack + WireMock are started if
8
+ # the repo provides them (auto-skipped otherwise).
7
9
  #
8
10
  # Usage:
9
- # ./run-tests-local.sh # Run all tests (V3 + V2 + Legacy)
10
- # ./run-tests-local.sh --v3 # Run only V3 tests (CRUD + Files)
11
- # ./run-tests-local.sh --v2 # Run only V2 tests (OAuth, Transfer)
12
- # ./run-tests-local.sh --legacy # Run only Legacy tests
13
- # ./run-tests-local.sh --all # Run all test suites
14
- # ./run-tests-local.sh --coverage # Run tests with JaCoCo coverage
15
- # ./run-tests-local.sh --multi-product # Run V3 tests across all products
16
- # ./run-tests-local.sh --skip-services # Skip starting LocalStack/WireMock/Spring
17
- # ./run-tests-local.sh --help # Show this help
11
+ # ./run-tests-local.sh # Run all collections
12
+ # ./run-tests-local.sh --coverage # Run with JaCoCo coverage
13
+ # ./run-tests-local.sh --skip-services # Skip starting LocalStack/WireMock/Spring
14
+ # ./run-tests-local.sh --help # Show this help
18
15
  #
19
- # Multi-Product Testing:
20
- # Tests V3 APIs (CRUD + Files) across Freshdesk, Freshservice, and Freshworks CRM
21
- # Requires product-specific environment files in postman/environments/:
22
- # - local.postman_environment.json (Freshdesk)
23
- # - local-freshservice.postman_environment.json
24
- # - local-freshworks-crm.postman_environment.json
25
- #
26
- # Examples:
27
- # ./run-tests-local.sh # Quick: run all API tests
28
- # ./run-tests-local.sh --coverage # Full: all tests + coverage
29
- # ./run-tests-local.sh --v3 # Dev: just test V3 changes
30
- # ./run-tests-local.sh --multi-product # Test all products locally
31
- # ./run-tests-local.sh --all --multi-product # All suites, all products
32
- # ./run-tests-local.sh --skip-services # Tests only (services running)
16
+ # This runner ships generic. The /setup-api-tests skill fills the repo-specific
17
+ # blocks below (marked `repo-specific`) and then runs `shiftleft protect` so
18
+ # `shiftleft test` staging never overwrites your customizations.
33
19
  ###############################################################################
34
20
 
35
21
  set -e
@@ -46,36 +32,32 @@ POSTMAN_DIR="$PROJECT_ROOT/postman"
46
32
  REPORT_DIR="$POSTMAN_DIR/reports"
47
33
  TIMESTAMP=$(date +%Y%m%d-%H%M%S)
48
34
 
49
- # Application settings
50
- APP_PORT=8080
35
+ # >>> repo-specific (set by /setup-api-tests) <<<
36
+ # Spring Boot app under test.
37
+ APP_PORT="${APP_PORT:-8080}"
38
+ CONTEXT_PATH="${CONTEXT_PATH:-}"
39
+ SPRING_PROFILE="${SPRING_PROFILE:-postman}"
40
+ # Maven module holding the app, used as `mvn -pl <MAVEN_MODULE>`.
41
+ # Leave empty for a single-module project.
42
+ MAVEN_MODULE="${MAVEN_MODULE:-}"
43
+ # Java version to select via SDKMAN/java_home (empty = use current java).
44
+ JAVA_VERSION="${JAVA_VERSION:-}"
45
+ # JaCoCo include pattern for coverage (your service's base package).
46
+ COVERAGE_PACKAGE="${COVERAGE_PACKAGE:-com.freshworks.*}"
47
+ # Directory the coverage report is generated from (module dir or project root).
48
+ COVERAGE_MODULE_DIR="${COVERAGE_MODULE_DIR:-$PROJECT_ROOT}"
49
+ # <<< repo-specific >>>
50
+
51
+ COLLECTIONS_DIR="$POSTMAN_DIR/collections"
52
+ ENVIRONMENT="$POSTMAN_DIR/environments/local.postman_environment.json"
53
+
51
54
  APP_PID_FILE="$SCRIPT_DIR/spring-boot-app.pid"
52
55
  APP_LOG_FILE="$SCRIPT_DIR/spring-boot-app.log"
53
56
 
54
- # Collection directories
55
- CRUD_COLLECTIONS_DIR="$POSTMAN_DIR/collections/crud"
56
- FILES_COLLECTIONS_DIR="$POSTMAN_DIR/collections/files"
57
- V2_COLLECTIONS_DIR="$POSTMAN_DIR/collections/v2"
58
- LEGACY_COLLECTIONS_DIR="$POSTMAN_DIR/collections/legacy"
59
- ENVIRONMENT="$POSTMAN_DIR/environments/local.postman_environment.json"
60
-
61
57
  # Coverage settings
62
58
  COVERAGE_DIR="$PROJECT_ROOT/coverage"
63
59
  COVERAGE_EXEC_FILE="$COVERAGE_DIR/jacoco-api-$TIMESTAMP.exec"
64
60
 
65
- # Multi-product configuration
66
- PRODUCTS="freshdesk freshservice freshworks-crm"
67
- declare -A PRODUCT_CONFIG=(
68
- ["freshdesk_id"]="1"
69
- ["freshdesk_env"]="local.postman_environment.json"
70
- ["freshdesk_display"]="Freshdesk"
71
- ["freshservice_id"]="3"
72
- ["freshservice_env"]="local-freshservice.postman_environment.json"
73
- ["freshservice_display"]="Freshservice"
74
- ["freshworks-crm_id"]="39"
75
- ["freshworks-crm_env"]="local-freshworks-crm.postman_environment.json"
76
- ["freshworks-crm_display"]="Freshworks CRM"
77
- )
78
-
79
61
  #==============================================================================
80
62
  # STATE TRACKING
81
63
  #==============================================================================
@@ -86,77 +68,27 @@ declare -a RESULTS
86
68
  declare -A COLLECTION_STATUS
87
69
  declare -A COLLECTION_PASSED
88
70
  declare -A COLLECTION_FAILED
89
- declare -A PRODUCT_STATUS
90
-
91
- # Initialize collection status
92
- for key in V3_CRUD V3_FILES V2 LEGACY; do
93
- COLLECTION_STATUS["$key"]="Not Run"
94
- COLLECTION_PASSED["$key"]=0
95
- COLLECTION_FAILED["$key"]=0
96
- done
97
-
98
- # Initialize product status
99
- for product in $PRODUCTS; do
100
- PRODUCT_STATUS["$product"]="Not Run"
101
- done
102
71
 
103
72
  #==============================================================================
104
73
  # ARGUMENT PARSING
105
74
  #==============================================================================
106
75
 
107
- RUN_V3=false
108
- RUN_V2=false
109
- RUN_LEGACY=false
110
76
  RUN_COVERAGE=false
111
- MULTI_PRODUCT=false
112
77
  SKIP_SERVICES=false
113
78
  SHOW_HELP=false
114
- RUN_ALL=true
115
79
 
116
80
  parse_arguments() {
117
81
  for arg in "$@"; do
118
82
  case $arg in
119
- --v3)
120
- RUN_V3=true
121
- RUN_ALL=false
122
- ;;
123
- --v2)
124
- RUN_V2=true
125
- RUN_ALL=false
126
- ;;
127
- --legacy)
128
- RUN_LEGACY=true
129
- RUN_ALL=false
130
- ;;
131
- --all)
132
- RUN_ALL=true
133
- ;;
134
- --coverage)
135
- RUN_COVERAGE=true
136
- ;;
137
- --multi-product)
138
- MULTI_PRODUCT=true
139
- RUN_V3=true
140
- ;;
141
- --skip-services)
142
- SKIP_SERVICES=true
143
- ;;
144
- --help|-h)
145
- SHOW_HELP=true
146
- ;;
83
+ --coverage) RUN_COVERAGE=true ;;
84
+ --skip-services) SKIP_SERVICES=true ;;
85
+ --help|-h) SHOW_HELP=true ;;
147
86
  esac
148
87
  done
149
-
150
- # If running all, enable all test suites
151
- if [ "$RUN_ALL" = true ]; then
152
- RUN_V3=true
153
- RUN_V2=true
154
- RUN_LEGACY=true
155
- fi
156
88
  }
157
89
 
158
90
  show_help() {
159
- head -32 "$0" | tail -29
91
+ head -19 "$0" | tail -16
160
92
  exit 0
161
93
  }
162
94
 
@@ -175,10 +107,10 @@ get_env_value() {
175
107
  parse_newman_results() {
176
108
  local output_file="$1"
177
109
  local assertions_line=$(grep "assertions" "$output_file" 2>/dev/null | head -1)
178
-
110
+
179
111
  NEWMAN_EXECUTED=$(echo "$assertions_line" | awk -F'│' '{gsub(/[^0-9]/,"",$3); print $3}')
180
112
  NEWMAN_FAILED=$(echo "$assertions_line" | awk -F'│' '{gsub(/[^0-9]/,"",$4); print $4}')
181
-
113
+
182
114
  NEWMAN_EXECUTED=${NEWMAN_EXECUTED:-0}
183
115
  NEWMAN_FAILED=${NEWMAN_FAILED:-0}
184
116
  NEWMAN_PASSED=$((NEWMAN_EXECUTED > 0 ? NEWMAN_EXECUTED - NEWMAN_FAILED : 0))
@@ -204,81 +136,56 @@ print_status() {
204
136
  #==============================================================================
205
137
 
206
138
  setup_java() {
207
- echo "Detecting Java 11..."
139
+ [ -z "$JAVA_VERSION" ] && return 0
140
+ echo "Selecting Java $JAVA_VERSION..."
208
141
 
209
- # Try SDKMAN first
142
+ # Try SDKMAN
210
143
  export SDKMAN_DIR="$HOME/.sdkman"
211
144
  if [ -s "$SDKMAN_DIR/bin/sdkman-init.sh" ]; then
212
145
  source "$SDKMAN_DIR/bin/sdkman-init.sh"
213
- # Try to use any Java 11 version available
214
- local java11=$(sdk list java 2>/dev/null | grep -o '11\.[0-9.]*-[a-z]*' | head -1)
215
- if [ -n "$java11" ]; then
216
- sdk use java "$java11" 2>/dev/null || true
217
- fi
146
+ local sdk_java=$(sdk list java 2>/dev/null | grep -o "${JAVA_VERSION}\.[0-9.]*-[a-z]*" | head -1)
147
+ [ -n "$sdk_java" ] && sdk use java "$sdk_java" 2>/dev/null || true
218
148
  fi
219
149
 
220
150
  # Try java_home (macOS)
221
151
  if command -v /usr/libexec/java_home >/dev/null 2>&1; then
222
- local java_11_home=$(/usr/libexec/java_home -v 11 2>/dev/null)
223
- if [ -n "$java_11_home" ]; then
224
- export JAVA_HOME="$java_11_home"
225
- export PATH="$JAVA_HOME/bin:$PATH"
226
- fi
227
- fi
228
-
229
- # Fallback to common installation paths
230
- if [ -z "$JAVA_HOME" ] || [ ! -d "$JAVA_HOME" ]; then
231
- for java_path in \
232
- "/Library/Java/JavaVirtualMachines/amazon-corretto-11.jdk/Contents/Home" \
233
- "$HOME/.sdkman/candidates/java/current" \
234
- "/usr/lib/jvm/java-11-openjdk" \
235
- "/usr/lib/jvm/java-11"; do
236
- if [ -d "$java_path" ]; then
237
- export JAVA_HOME="$java_path"
238
- export PATH="$JAVA_HOME/bin:$PATH"
239
- break
240
- fi
241
- done
152
+ local jh=$(/usr/libexec/java_home -v "$JAVA_VERSION" 2>/dev/null || true)
153
+ [ -n "$jh" ] && export JAVA_HOME="$jh"
242
154
  fi
243
155
 
244
- # Verify Java version
245
- local java_version=$(java -version 2>&1 | head -1 | grep -oE '[0-9]+' | head -1)
246
- if [ "$java_version" != "11" ]; then
247
- echo "Warning: Java 11 not detected. Current version: $(java -version 2>&1 | head -1)"
248
- echo "Please set JAVA_HOME to Java 11 or run: export JAVA_HOME=\$(/usr/libexec/java_home -v 11)"
249
- else
250
- print_status "success" "Java 11 detected: $(java -version 2>&1 | head -1)"
251
- fi
156
+ local detected=$(java -version 2>&1 | head -1)
157
+ echo " Java: $detected"
252
158
  }
253
159
 
254
160
  #==============================================================================
255
161
  # SERVICE MANAGEMENT
256
162
  #==============================================================================
257
163
 
258
- # Source container runtime detection
259
- source "$SCRIPT_DIR/../infra/container-runtime.sh"
260
-
261
164
  start_services() {
262
165
  if [ "$SKIP_SERVICES" = true ]; then
263
166
  echo "Skipping service startup (--skip-services)"
264
167
  return 0
265
168
  fi
266
169
 
267
- # Step 1: Start LocalStack
268
- echo ""
269
- echo "========================================="
270
- echo "Step 1: Starting LocalStack"
271
- echo "========================================="
272
- "$SCRIPT_DIR/../infra/setup-localstack.sh" || echo "Warning: LocalStack setup issues"
170
+ # Step 1: LocalStack (optional — self-skips if no compose file / runtime)
171
+ if [ -x "$SCRIPT_DIR/../infra/setup-localstack.sh" ]; then
172
+ echo ""
173
+ echo "========================================="
174
+ echo "Step 1: Starting LocalStack"
175
+ echo "========================================="
176
+ "$SCRIPT_DIR/../infra/setup-localstack.sh" || echo "Warning: LocalStack setup issues"
177
+ fi
273
178
 
274
- # Step 2: Start WireMock
275
- echo ""
276
- echo "========================================="
277
- echo "Step 2: Starting WireMock"
278
- echo "========================================="
279
- "$SCRIPT_DIR/../infra/start-mocks.sh"
179
+ # Step 2: WireMock (optional)
180
+ if [ -x "$SCRIPT_DIR/../infra/start-mocks.sh" ]; then
181
+ echo ""
182
+ echo "========================================="
183
+ echo "Step 2: Starting WireMock"
184
+ echo "========================================="
185
+ "$SCRIPT_DIR/../infra/start-mocks.sh"
186
+ fi
280
187
 
281
- # Step 3: Start Spring Boot
188
+ # Step 3: Spring Boot
282
189
  echo ""
283
190
  echo "========================================="
284
191
  echo "Step 3: Starting Spring Boot"
@@ -293,26 +200,33 @@ start_spring_boot() {
293
200
  fi
294
201
 
295
202
  cd "$PROJECT_ROOT"
296
- echo "Starting Spring Boot with integration profile..."
297
-
203
+ echo "Starting Spring Boot (profile: $SPRING_PROFILE)..."
204
+
298
205
  local jvm_args="-Dserver.port=$APP_PORT"
299
-
206
+
300
207
  if [ "$RUN_COVERAGE" = true ]; then
301
208
  local jacoco_jar="$HOME/.m2/repository/org/jacoco/org.jacoco.agent/0.8.11/org.jacoco.agent-0.8.11-runtime.jar"
302
209
  if [ -f "$jacoco_jar" ]; then
303
210
  mkdir -p "$COVERAGE_DIR"
304
- jvm_args="-javaagent:$jacoco_jar=destfile=$COVERAGE_EXEC_FILE,includes=com.freshworks.marketplace.installation.* $jvm_args"
211
+ jvm_args="-javaagent:$jacoco_jar=destfile=$COVERAGE_EXEC_FILE,includes=$COVERAGE_PACKAGE $jvm_args"
305
212
  echo "JaCoCo coverage enabled"
306
213
  else
307
214
  echo "Warning: JaCoCo agent not found. Run 'mvn dependency:resolve' first."
308
215
  fi
309
216
  fi
310
217
 
311
- nohup mvn -pl installation spring-boot:run \
312
- -Dspring-boot.run.profiles=integration \
218
+ # >>> repo-specific (set by /setup-api-tests): app start command <<<
219
+ # Default: `mvn spring-boot:run`. For a submodule set MAVEN_MODULE.
220
+ # If H2_SCOPE=test prevents spring-boot:run, replace this block with a
221
+ # `java -cp <test-classpath>` launch (see /setup-api-tests SKILL 2.3).
222
+ local module_args=""
223
+ [ -n "$MAVEN_MODULE" ] && module_args="-pl $MAVEN_MODULE"
224
+ nohup mvn $module_args spring-boot:run \
225
+ -Dspring-boot.run.profiles="$SPRING_PROFILE" \
313
226
  -Dspring-boot.run.jvmArguments="$jvm_args" \
314
227
  -Dmaven.test.skip=true -Dcheckstyle.skip=true \
315
228
  > "$APP_LOG_FILE" 2>&1 &
229
+ # <<< repo-specific >>>
316
230
 
317
231
  local app_pid=$!
318
232
  echo "$app_pid" > "$APP_PID_FILE"
@@ -320,7 +234,8 @@ start_spring_boot() {
320
234
  # Wait for startup
321
235
  local max_wait=120
322
236
  local waited=0
323
- while ! curl -s "http://localhost:$APP_PORT/actuator/health" > /dev/null 2>&1; do
237
+ local health_url="http://localhost:$APP_PORT${CONTEXT_PATH}/actuator/health"
238
+ while ! curl -s "$health_url" > /dev/null 2>&1; do
324
239
  if [ $waited -ge $max_wait ]; then
325
240
  echo "Error: Application failed to start"
326
241
  tail -30 "$APP_LOG_FILE"
@@ -351,15 +266,9 @@ stop_services() {
351
266
  rm -f "$APP_PID_FILE"
352
267
  fi
353
268
 
354
- # Stop WireMock
355
- "$SCRIPT_DIR/../infra/stop-mocks.sh" 2>/dev/null || true
356
-
357
- # Stop LocalStack
358
- if $CONTAINER_CMD ps 2>/dev/null | grep -q mp-installation-localstack; then
359
- echo "Stopping LocalStack..."
360
- $CONTAINER_COMPOSE_CMD -f "$PROJECT_ROOT/docker-compose-localstack.yml" down > /dev/null 2>&1 || true
361
- print_status "success" "LocalStack stopped"
362
- fi
269
+ # Stop WireMock + LocalStack (runtime auto-detected inside the scripts)
270
+ [ -x "$SCRIPT_DIR/../infra/stop-mocks.sh" ] && "$SCRIPT_DIR/../infra/stop-mocks.sh" 2>/dev/null || true
271
+ [ -x "$SCRIPT_DIR/../infra/stop-localstack.sh" ] && "$SCRIPT_DIR/../infra/stop-localstack.sh" 2>/dev/null || true
363
272
 
364
273
  # Generate coverage report if enabled
365
274
  generate_coverage_report
@@ -376,142 +285,60 @@ generate_coverage_report() {
376
285
  echo "========================================="
377
286
  echo "Generating Coverage Report"
378
287
  echo "========================================="
379
-
288
+
380
289
  local coverage_html_dir="$COVERAGE_DIR/html-report-$TIMESTAMP"
381
290
  mkdir -p "$coverage_html_dir"
382
-
383
- cd "$PROJECT_ROOT/installation"
384
-
291
+
292
+ cd "$COVERAGE_MODULE_DIR"
293
+
385
294
  echo "Generating JaCoCo report..."
386
295
  mvn jacoco:report -q 2>/dev/null || true
387
-
296
+
388
297
  if [ -d "target/site/jacoco" ]; then
389
298
  cp -r target/site/jacoco/* "$coverage_html_dir/" 2>/dev/null || true
390
-
391
299
  echo ""
392
- echo "========================================="
393
- echo "COVERAGE REPORT GENERATED"
394
- echo "========================================="
395
- echo "Timestamped Report: $coverage_html_dir/index.html"
396
- echo "Latest Report: installation/target/site/jacoco/index.html"
397
- echo "Raw Data: $COVERAGE_EXEC_FILE"
398
- echo ""
399
- echo "To view: open $coverage_html_dir/index.html"
300
+ echo "Coverage report: $coverage_html_dir/index.html"
400
301
  else
401
302
  echo "Warning: Coverage report generation failed"
402
303
  fi
403
304
  }
404
305
 
405
- #==============================================================================
406
- # CLEANUP FUNCTIONS
407
- #==============================================================================
408
-
409
- # Cleanup all installations across ALL products before running tests
410
- cleanup_all_installations() {
411
- echo ""
412
- echo " → Cleaning up existing installations..."
413
-
414
- local base_url="http://localhost:8080/api/v3"
415
- local total_count=0
416
- local product
417
-
418
- for product in $PRODUCTS; do
419
- local env_key="${product}_env"
420
- local env_file="$POSTMAN_DIR/environments/${PRODUCT_CONFIG[$env_key]}"
421
-
422
- if [ ! -f "$env_file" ]; then
423
- continue
424
- fi
425
-
426
- local auth_token=$(get_env_value "$env_file" "auth_token")
427
- local product_id=$(get_env_value "$env_file" "product_id")
428
- local account_id=$(get_env_value "$env_file" "account_id")
429
-
430
- if [ -z "$auth_token" ]; then
431
- continue
432
- fi
433
-
434
- # List all installations (types: 1=extension, 4=bundle, 6=custom)
435
- local list_response=$(curl -s -X GET "${base_url}/installations?type=1,4,6" \
436
- -H "Authorization: Bearer ${auth_token}" \
437
- -H "x-mp-product-id: ${product_id}" \
438
- -H "x-mp-account-id: ${account_id}" \
439
- -H "X-MP-USER-ROLE: admin" \
440
- -H "Content-Type: application/json" 2>/dev/null)
441
-
442
- # Extract and delete installation IDs
443
- local installation_ids=$(echo "$list_response" | python3 -c "
444
- import json, sys
445
- try:
446
- data = json.load(sys.stdin)
447
- if isinstance(data, list):
448
- ids = [str(item.get('installed_extension_id', '')) for item in data if item.get('installed_extension_id')]
449
- else:
450
- ids = []
451
- print(' '.join(ids))
452
- except:
453
- print('')
454
- " 2>/dev/null)
455
-
456
- if [ -n "$installation_ids" ] && [ "$installation_ids" != "" ]; then
457
- for inst_id in $installation_ids; do
458
- curl -s -X DELETE "${base_url}/installations/${inst_id}" \
459
- -H "Authorization: Bearer ${auth_token}" \
460
- -H "x-mp-product-id: ${product_id}" \
461
- -H "x-mp-account-id: ${account_id}" \
462
- -H "X-MP-USER-ROLE: admin" \
463
- -H "Content-Type: application/json" >/dev/null 2>&1
464
- total_count=$((total_count + 1))
465
- done
466
- fi
467
- done
468
-
469
- if [ $total_count -gt 0 ]; then
470
- print_status "success" "Cleaned up $total_count installation(s)"
471
- else
472
- print_status "success" "No existing installations to clean up"
473
- fi
474
-
475
- sleep 1
476
- }
477
-
478
306
  #==============================================================================
479
307
  # TEST EXECUTION
480
308
  #==============================================================================
481
309
 
482
- # Run all collections in a directory
310
+ # Run all collections in a directory, in filename order, chaining the env.
483
311
  run_collection_dir() {
484
312
  local dir_name="$1"
485
313
  local collections_dir="$2"
486
314
  local report_prefix="$3"
487
315
  local collection_key="$4"
488
-
316
+
489
317
  echo "========================================="
490
318
  echo "Running: $dir_name"
491
319
  echo "========================================="
492
-
320
+
493
321
  local dir_passed=0
494
322
  local dir_failed=0
495
323
  local all_passed=true
496
324
  local newman_tmp="/tmp/newman-output-${TIMESTAMP}-$$.txt"
497
-
498
- # Create working environment copy
325
+
499
326
  local working_env="/tmp/newman-env-${collection_key}-${TIMESTAMP}.json"
500
327
  cp "$ENVIRONMENT" "$working_env"
501
-
502
- # Run collections in order
328
+
329
+ shopt -s nullglob
503
330
  for collection_file in "$collections_dir"/*.json; do
504
331
  [ ! -f "$collection_file" ] && continue
505
-
332
+
506
333
  local basename=$(basename "$collection_file" .json)
507
334
  local html_report="$REPORT_DIR/${report_prefix}-${basename}-$TIMESTAMP.html"
508
335
  local json_report="$REPORT_DIR/json/${report_prefix}-${basename}-$TIMESTAMP.json"
509
336
  local env_export="/tmp/newman-env-export-${collection_key}-${basename}.json"
510
-
337
+
511
338
  echo ""
512
339
  echo " → Running: $basename"
513
340
  echo ""
514
-
341
+
515
342
  "$POSTMAN_DIR/node_modules/.bin/newman" run "$collection_file" \
516
343
  --environment "$working_env" \
517
344
  --reporters cli,htmlextra,json \
@@ -520,15 +347,15 @@ run_collection_dir() {
520
347
  --export-environment "$env_export" \
521
348
  --timeout-request 30000 \
522
349
  --delay-request 100 2>&1 | tee "$newman_tmp"
523
-
350
+
524
351
  parse_newman_results "$newman_tmp"
525
-
352
+
526
353
  dir_passed=$((dir_passed + NEWMAN_PASSED))
527
354
  dir_failed=$((dir_failed + NEWMAN_FAILED))
528
-
355
+
529
356
  # Chain environment for next collection
530
357
  [ -f "$env_export" ] && cp "$env_export" "$working_env"
531
-
358
+
532
359
  if [ "$NEWMAN_FAILED" -gt 0 ]; then
533
360
  all_passed=false
534
361
  print_status "error" "$basename: FAILED ($NEWMAN_PASSED passed, $NEWMAN_FAILED failed)"
@@ -536,11 +363,10 @@ run_collection_dir() {
536
363
  print_status "success" "$basename: PASSED ($NEWMAN_PASSED passed, $NEWMAN_FAILED failed)"
537
364
  fi
538
365
  done
539
-
540
- # Cleanup temp files
366
+ shopt -u nullglob
367
+
541
368
  rm -f "$working_env" /tmp/newman-env-export-${collection_key}-*.json "$newman_tmp"
542
-
543
- # Update status
369
+
544
370
  if [ "$all_passed" = true ] && [ "$dir_failed" -eq 0 ]; then
545
371
  COLLECTION_STATUS["$collection_key"]="✓ PASSED"
546
372
  RESULTS+=("$dir_name: ✓ PASSED ($dir_passed passed, $dir_failed failed)")
@@ -552,200 +378,28 @@ run_collection_dir() {
552
378
  echo ""
553
379
  print_status "error" "$dir_name: SOME FAILED"
554
380
  fi
555
-
381
+
556
382
  COLLECTION_PASSED["$collection_key"]=$dir_passed
557
383
  COLLECTION_FAILED["$collection_key"]=$dir_failed
558
384
  TOTAL_PASSED=$((TOTAL_PASSED + dir_passed))
559
385
  TOTAL_FAILED=$((TOTAL_FAILED + dir_failed))
560
-
561
- [ "$all_passed" = true ] && [ "$dir_failed" -eq 0 ]
562
- }
563
-
564
- # Run tests for a single product (multi-product mode)
565
- run_product_tests() {
566
- local product="$1"
567
- local display_name="${PRODUCT_CONFIG[${product}_display]}"
568
- local env_file="$POSTMAN_DIR/environments/${PRODUCT_CONFIG[${product}_env]}"
569
-
570
- echo ""
571
- echo "========================================="
572
- echo "Testing Product: $display_name"
573
- echo "========================================="
574
-
575
- if [ ! -f "$env_file" ]; then
576
- echo "Warning: Environment file not found: $env_file"
577
- echo "Skipping $product"
578
- PRODUCT_STATUS["$product"]="⚠ SKIPPED"
579
- return 1
580
- fi
581
-
582
- # Clean up installations before testing this product
583
- cleanup_all_installations
584
-
585
- local product_passed=true
586
- local newman_tmp="/tmp/newman-output-${product}-${TIMESTAMP}-$$.txt"
587
-
588
- # Each product gets its own report subdirectory to avoid cross-product overwrites
589
- local product_report_dir="$REPORT_DIR/${product}"
590
- local product_json_dir="$REPORT_DIR/json/${product}"
591
- mkdir -p "$product_report_dir" "$product_json_dir"
592
-
593
- # Create working environment copy
594
- local working_env="/tmp/newman-env-${product}-${TIMESTAMP}.json"
595
- cp "$env_file" "$working_env"
596
-
597
- # Run CRUD tests
598
- local crud_key="${product}_CRUD"
599
- COLLECTION_STATUS["$crud_key"]="Not Run"
600
- COLLECTION_PASSED["$crud_key"]=0
601
- COLLECTION_FAILED["$crud_key"]=0
602
-
603
- echo ""
604
- echo "Running CRUD API tests for $display_name..."
605
-
606
- local crud_passed=0 crud_failed=0 crud_all_passed=true
607
-
608
- for collection_file in "$CRUD_COLLECTIONS_DIR"/*.json; do
609
- [ ! -f "$collection_file" ] && continue
610
-
611
- local basename=$(basename "$collection_file" .json)
612
- local html_report="$product_report_dir/crud-${basename}-$TIMESTAMP.html"
613
- local json_report="$product_json_dir/crud-${basename}-$TIMESTAMP.json"
614
- local env_export="/tmp/newman-env-export-${product}-crud-${basename}.json"
615
-
616
- "$POSTMAN_DIR/node_modules/.bin/newman" run "$collection_file" \
617
- --environment "$working_env" \
618
- --reporters cli,htmlextra,json \
619
- --reporter-htmlextra-export "$html_report" \
620
- --reporter-htmlextra-title "${REPORT_LOGO} - $display_name - CRUD - $basename (Local)" \
621
- --reporter-htmlextra-darkTheme \
622
- --reporter-json-export "$json_report" \
623
- --export-environment "$env_export" \
624
- --timeout-request 30000 \
625
- --delay-request 100 2>&1 | tee "$newman_tmp"
626
-
627
- parse_newman_results "$newman_tmp"
628
-
629
- crud_passed=$((crud_passed + NEWMAN_PASSED))
630
- crud_failed=$((crud_failed + NEWMAN_FAILED))
631
-
632
- [ -f "$env_export" ] && cp "$env_export" "$working_env"
633
- [ "$NEWMAN_FAILED" -gt 0 ] && crud_all_passed=false
634
- done
635
-
636
- if [ "$crud_all_passed" = true ] && [ "$crud_failed" -eq 0 ]; then
637
- print_status "success" "CRUD tests passed for $display_name"
638
- COLLECTION_STATUS["$crud_key"]="✓ PASSED"
639
- else
640
- print_status "error" "CRUD tests failed for $display_name"
641
- COLLECTION_STATUS["$crud_key"]="✗ FAILED"
642
- product_passed=false
643
- fi
644
-
645
- COLLECTION_PASSED["$crud_key"]=$crud_passed
646
- COLLECTION_FAILED["$crud_key"]=$crud_failed
647
- TOTAL_PASSED=$((TOTAL_PASSED + crud_passed))
648
- TOTAL_FAILED=$((TOTAL_FAILED + crud_failed))
649
-
650
- # Run Files tests (using environment from CRUD tests)
651
- local files_key="${product}_FILES"
652
- COLLECTION_STATUS["$files_key"]="Not Run"
653
- COLLECTION_PASSED["$files_key"]=0
654
- COLLECTION_FAILED["$files_key"]=0
655
-
656
- echo ""
657
- echo "Running Files API tests for $display_name..."
658
-
659
- local files_passed=0 files_failed=0 files_all_passed=true
660
-
661
- for collection_file in "$FILES_COLLECTIONS_DIR"/*.json; do
662
- [ ! -f "$collection_file" ] && continue
663
-
664
- local basename=$(basename "$collection_file" .json)
665
- local html_report="$product_report_dir/files-${basename}-$TIMESTAMP.html"
666
- local json_report="$product_json_dir/files-${basename}-$TIMESTAMP.json"
667
- local env_export="/tmp/newman-env-export-${product}-files-${basename}.json"
668
-
669
- "$POSTMAN_DIR/node_modules/.bin/newman" run "$collection_file" \
670
- --environment "$working_env" \
671
- --reporters cli,htmlextra,json \
672
- --reporter-htmlextra-export "$html_report" \
673
- --reporter-htmlextra-title "${REPORT_LOGO} - $display_name - Files - $basename (Local)" \
674
- --reporter-htmlextra-darkTheme \
675
- --reporter-json-export "$json_report" \
676
- --export-environment "$env_export" \
677
- --timeout-request 30000 \
678
- --delay-request 100 2>&1 | tee "$newman_tmp"
679
-
680
- parse_newman_results "$newman_tmp"
681
-
682
- files_passed=$((files_passed + NEWMAN_PASSED))
683
- files_failed=$((files_failed + NEWMAN_FAILED))
684
-
685
- [ -f "$env_export" ] && cp "$env_export" "$working_env"
686
- [ "$NEWMAN_FAILED" -gt 0 ] && files_all_passed=false
687
- done
688
-
689
- # Cleanup temp files for this product
690
- rm -f "$working_env" /tmp/newman-env-export-${product}-*.json "$newman_tmp"
691
-
692
- if [ "$files_all_passed" = true ] && [ "$files_failed" -eq 0 ]; then
693
- print_status "success" "Files tests passed for $display_name"
694
- COLLECTION_STATUS["$files_key"]="✓ PASSED"
695
- else
696
- print_status "error" "Files tests failed for $display_name"
697
- COLLECTION_STATUS["$files_key"]="✗ FAILED"
698
- product_passed=false
699
- fi
700
-
701
- COLLECTION_PASSED["$files_key"]=$files_passed
702
- COLLECTION_FAILED["$files_key"]=$files_failed
703
- TOTAL_PASSED=$((TOTAL_PASSED + files_passed))
704
- TOTAL_FAILED=$((TOTAL_FAILED + files_failed))
705
-
706
- # Update product status
707
- if [ "$product_passed" = true ]; then
708
- PRODUCT_STATUS["$product"]="✓ PASSED"
709
- else
710
- PRODUCT_STATUS["$product"]="✗ FAILED"
711
- fi
712
386
  }
713
387
 
714
388
  run_tests() {
715
389
  echo ""
716
-
717
- if [ "$MULTI_PRODUCT" = true ]; then
718
- # Multi-product mode: run V3 (CRUD + Files) across all products
719
- for product in $PRODUCTS; do
720
- run_product_tests "$product"
721
- done
722
- # Also run V2 and Legacy once (single-product) if --all was specified
723
- if [ "$RUN_V2" = true ]; then
724
- run_collection_dir "V2 API" "$V2_COLLECTIONS_DIR" "v2" "V2" || true
725
- echo ""
726
- fi
727
- if [ "$RUN_LEGACY" = true ]; then
728
- run_collection_dir "Legacy API" "$LEGACY_COLLECTIONS_DIR" "legacy" "LEGACY" || true
729
- echo ""
730
- fi
390
+ # Generic single-suite run. Repos with versioned suites or multi-product
391
+ # testing extend this via /setup-api-tests (see the optional add-on section).
392
+ if compgen -G "$COLLECTIONS_DIR/*.json" >/dev/null 2>&1; then
393
+ run_collection_dir "API" "$COLLECTIONS_DIR" "api" "API" || true
731
394
  else
732
- # Single product mode
733
- if [ "$RUN_V3" = true ]; then
734
- run_collection_dir "V3 CRUD API" "$CRUD_COLLECTIONS_DIR" "crud" "V3_CRUD" || true
735
- echo ""
736
- run_collection_dir "V3 Files API" "$FILES_COLLECTIONS_DIR" "files" "V3_FILES" || true
395
+ # Each immediate subdirectory of collections/ is a suite.
396
+ shopt -s nullglob
397
+ for suite_dir in "$COLLECTIONS_DIR"/*/; do
398
+ local suite=$(basename "$suite_dir")
399
+ run_collection_dir "$suite" "${suite_dir%/}" "$suite" "$suite" || true
737
400
  echo ""
738
- fi
739
-
740
- if [ "$RUN_V2" = true ]; then
741
- run_collection_dir "V2 API" "$V2_COLLECTIONS_DIR" "v2" "V2" || true
742
- echo ""
743
- fi
744
-
745
- if [ "$RUN_LEGACY" = true ]; then
746
- run_collection_dir "Legacy API" "$LEGACY_COLLECTIONS_DIR" "legacy" "LEGACY" || true
747
- echo ""
748
- fi
401
+ done
402
+ shopt -u nullglob
749
403
  fi
750
404
  }
751
405
 
@@ -756,49 +410,14 @@ run_tests() {
756
410
  print_summary() {
757
411
  echo ""
758
412
  echo "========================================="
759
- if [ "$MULTI_PRODUCT" = true ]; then
760
- echo "Multi-Product Test Results"
761
- else
762
- echo "Test Results Summary"
763
- fi
413
+ echo "Test Results Summary"
764
414
  echo "========================================="
765
415
  echo ""
766
-
767
- if [ "$MULTI_PRODUCT" = true ]; then
768
- echo "Product Results:"
769
- for product in $PRODUCTS; do
770
- local status="${PRODUCT_STATUS[$product]}"
771
- local display_name="${PRODUCT_CONFIG[${product}_display]}"
772
- local crud_key="${product}_CRUD"
773
- local files_key="${product}_FILES"
774
-
775
- if [ "$status" != "Not Run" ]; then
776
- echo " $display_name: $status"
777
- if [ "${COLLECTION_STATUS[$crud_key]}" != "Not Run" ]; then
778
- echo " - CRUD API: ${COLLECTION_STATUS[$crud_key]} (${COLLECTION_PASSED[$crud_key]} passed, ${COLLECTION_FAILED[$crud_key]} failed)"
779
- fi
780
- if [ "${COLLECTION_STATUS[$files_key]}" != "Not Run" ]; then
781
- echo " - Files API: ${COLLECTION_STATUS[$files_key]} (${COLLECTION_PASSED[$files_key]} passed, ${COLLECTION_FAILED[$files_key]} failed)"
782
- fi
783
- fi
784
- done
785
- else
786
- echo "Collection Results:"
787
- for collection_key in V3_CRUD V3_FILES V2 LEGACY; do
788
- local status="${COLLECTION_STATUS[$collection_key]}"
789
- local passed="${COLLECTION_PASSED[$collection_key]}"
790
- local failed="${COLLECTION_FAILED[$collection_key]}"
791
-
792
- if [ "$status" != "Not Run" ]; then
793
- case $collection_key in
794
- V3_CRUD) echo " V3 CRUD API: $status ($passed passed, $failed failed)" ;;
795
- V3_FILES) echo " V3 Files API: $status ($passed passed, $failed failed)" ;;
796
- V2) echo " V2 API: $status ($passed passed, $failed failed)" ;;
797
- LEGACY) echo " Legacy API: $status ($passed passed, $failed failed)" ;;
798
- esac
799
- fi
800
- done
801
- fi
416
+ echo "Collection Results:"
417
+ local key
418
+ for key in "${!COLLECTION_STATUS[@]}"; do
419
+ echo " $key: ${COLLECTION_STATUS[$key]} (${COLLECTION_PASSED[$key]} passed, ${COLLECTION_FAILED[$key]} failed)"
420
+ done
802
421
 
803
422
  echo ""
804
423
  echo "Overall Total: $TOTAL_PASSED passed, $TOTAL_FAILED failed"
@@ -807,27 +426,16 @@ print_summary() {
807
426
  }
808
427
 
809
428
  generate_consolidated_report() {
810
- # Calculate passed/failed collections
811
429
  local passed_collections=0
812
430
  local failed_collections=0
813
-
814
- if [ "$MULTI_PRODUCT" = true ]; then
815
- for product in $PRODUCTS; do
816
- if [ "${PRODUCT_STATUS[$product]}" = "✓ PASSED" ]; then
817
- passed_collections=$((passed_collections + 1))
818
- elif [ "${PRODUCT_STATUS[$product]}" != "Not Run" ]; then
819
- failed_collections=$((failed_collections + 1))
820
- fi
821
- done
822
- else
823
- for collection_key in V3_CRUD V3_FILES V2 LEGACY; do
824
- if [ "${COLLECTION_STATUS[$collection_key]}" = "✓ PASSED" ]; then
825
- passed_collections=$((passed_collections + 1))
826
- elif [ "${COLLECTION_STATUS[$collection_key]}" != "Not Run" ]; then
827
- failed_collections=$((failed_collections + 1))
828
- fi
829
- done
830
- fi
431
+ local key
432
+ for key in "${!COLLECTION_STATUS[@]}"; do
433
+ if [ "${COLLECTION_STATUS[$key]}" = "✓ PASSED" ]; then
434
+ passed_collections=$((passed_collections + 1))
435
+ else
436
+ failed_collections=$((failed_collections + 1))
437
+ fi
438
+ done
831
439
 
832
440
  local test_result=0
833
441
  [ $TOTAL_FAILED -gt 0 ] && test_result=1
@@ -844,83 +452,49 @@ generate_consolidated_report() {
844
452
 
845
453
  main() {
846
454
  parse_arguments "$@"
847
-
455
+
848
456
  [ "$SHOW_HELP" = true ] && show_help
849
-
850
- # Display banner
457
+
851
458
  echo "========================================="
852
- if [ "$MULTI_PRODUCT" = true ]; then
853
- echo "${REPORT_LOGO} API Integration Tests (Multi-Product)"
854
- else
855
- echo "${REPORT_LOGO} API Integration Tests"
856
- fi
459
+ echo "${REPORT_LOGO} API Integration Tests (Local)"
857
460
  echo "========================================="
858
461
  echo ""
859
-
860
- if [ "$MULTI_PRODUCT" = true ]; then
861
- echo "Mode: Multi-Product Testing (Local)"
862
- echo "Products:"
863
- for product in $PRODUCTS; do
864
- local display="${PRODUCT_CONFIG[${product}_display]}"
865
- local id="${PRODUCT_CONFIG[${product}_id]}"
866
- echo " - $display (product_id: $id)"
867
- done
868
- echo ""
869
- echo "Test Suites: V3 API (CRUD + Files) for each product"
870
- else
871
- echo "Test Suites:"
872
- [ "$RUN_V3" = true ] && echo " ✓ V3 API (CRUD + Files)"
873
- [ "$RUN_V2" = true ] && echo " ✓ V2 API (OAuth, Transfer)"
874
- [ "$RUN_LEGACY" = true ] && echo " ✓ Legacy API"
875
- fi
876
- [ "$RUN_COVERAGE" = true ] && echo " ✓ Coverage: Enabled"
462
+ [ "$RUN_COVERAGE" = true ] && echo " ✓ Coverage: Enabled"
877
463
  [ "$SKIP_SERVICES" = true ] && echo " ⚠ Services: Skipped (--skip-services)"
878
464
  echo ""
879
-
880
- # Setup
465
+
881
466
  setup_java
882
-
467
+
883
468
  echo ""
884
469
  echo "Checking prerequisites..."
885
470
  command -v java >/dev/null 2>&1 || { echo "Error: Java required" >&2; exit 1; }
886
- command -v mvn >/dev/null 2>&1 || { echo "Error: Maven required" >&2; exit 1; }
471
+ command -v mvn >/dev/null 2>&1 || { echo "Error: Maven required" >&2; exit 1; }
887
472
  command -v node >/dev/null 2>&1 || { echo "Error: Node.js required" >&2; exit 1; }
888
- check_container_runtime || exit 1
889
473
  print_status "success" "Prerequisites OK"
890
-
891
- # Setup cleanup trap
474
+
892
475
  trap stop_services EXIT INT TERM
893
-
894
- # Start services
476
+
895
477
  start_services
896
-
897
- # Install Newman and setup git hooks
478
+
898
479
  echo ""
899
480
  echo "========================================="
900
- echo "Step 4: Installing Newman"
481
+ echo "Installing Newman"
901
482
  echo "========================================="
902
483
  cd "$POSTMAN_DIR"
903
484
  npm ci --ignore-scripts --silent 2>/dev/null || npm install --ignore-scripts --silent
904
485
  print_status "success" "Newman ready"
905
-
906
- # Setup git hooks for auto-formatting (runs once, idempotent)
486
+
907
487
  if [ -d "$POSTMAN_DIR/.husky" ]; then
908
488
  cd "$PROJECT_ROOT"
909
489
  git config core.hooksPath postman/.husky/_ 2>/dev/null || true
910
490
  fi
911
-
491
+
912
492
  mkdir -p "$REPORT_DIR" "$REPORT_DIR/json"
913
-
914
- # Run tests
493
+
915
494
  run_tests
916
-
917
- # Print summary
918
495
  print_summary
919
-
920
- # Generate consolidated report
921
496
  generate_consolidated_report
922
-
923
- # Exit with appropriate code
497
+
924
498
  if [ $TOTAL_FAILED -gt 0 ]; then
925
499
  echo ""
926
500
  echo "❌ Some tests failed"
@@ -932,5 +506,4 @@ main() {
932
506
  fi
933
507
  }
934
508
 
935
- # Run main
936
509
  main "$@"