@freshworks/shiftleft-tools 1.1.8
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/README.md +351 -0
- package/bin/shiftleft.js +95 -0
- package/package.json +57 -0
- package/src/commands/doctor.js +208 -0
- package/src/commands/init-postman.js +298 -0
- package/src/commands/init-rules.js +78 -0
- package/src/commands/link.js +172 -0
- package/src/commands/protect.js +61 -0
- package/src/commands/run-tests.js +182 -0
- package/src/commands/setup-pipeline.js +209 -0
- package/src/commands/update.js +203 -0
- package/src/index.js +4 -0
- package/src/utils/copy-tree.js +98 -0
- package/src/utils/gitignore.js +26 -0
- package/src/utils/logger.js +9 -0
- package/src/utils/manifest.js +145 -0
- package/src/utils/stack.js +80 -0
- package/src/utils/template.js +135 -0
- package/templates/AGENTS.md +109 -0
- package/templates/CLAUDE.md +3 -0
- package/templates/jenkins/Jenkinsfile-java.groovy +432 -0
- package/templates/jenkins/Jenkinsfile-node.groovy +450 -0
- package/templates/postman/.husky/pre-commit +19 -0
- package/templates/postman/.prettierrc.json +5 -0
- package/templates/postman/README.md.ejs +147 -0
- package/templates/postman/collections/01-core.json.ejs +91 -0
- package/templates/postman/config/local.json.ejs +12 -0
- package/templates/postman/config/staging.json.ejs +26 -0
- package/templates/postman/environments/local.postman_environment.json.ejs +31 -0
- package/templates/postman/environments/staging.postman_environment.json.ejs +31 -0
- package/templates/postman/gitignore +16 -0
- package/templates/postman/npmrc +31 -0
- package/templates/postman/package.json.ejs +66 -0
- package/templates/postman/run-all-shim.sh +16 -0
- package/templates/postman/scripts/auth/generate-jwt.sh +113 -0
- package/templates/postman/scripts/auth/get-issuer-secret.sh +140 -0
- package/templates/postman/scripts/infra/start-mocks.sh +138 -0
- package/templates/postman/scripts/infra/stop-mocks.sh +43 -0
- package/templates/postman/scripts/lib/api_coverage.py +1122 -0
- package/templates/postman/scripts/lib/cleanup-reports.sh +101 -0
- package/templates/postman/scripts/lib/cleanup-stryker.sh +44 -0
- package/templates/postman/scripts/lib/report_combined.py +527 -0
- package/templates/postman/scripts/lib/report_consolidated.py +363 -0
- package/templates/postman/scripts/lib/report_generator.py +121 -0
- package/templates/postman/scripts/lib/report_migration.py +156 -0
- package/templates/postman/scripts/lib/report_mutation.py +110 -0
- package/templates/postman/scripts/lib/report_unit.py +353 -0
- package/templates/postman/scripts/lib/report_utils.py +973 -0
- package/templates/postman/scripts/report-generators/generate-consolidated-report.sh +445 -0
- package/templates/postman/scripts/report-generators/java-api-coverage-matrix.sh +257 -0
- package/templates/postman/scripts/report-generators/mutation-report.sh +672 -0
- package/templates/postman/scripts/report-generators/node-api-coverage-matrix.sh +167 -0
- package/templates/postman/scripts/report-generators/stage-report-artifacts.sh +27 -0
- package/templates/postman/scripts/run-all.sh +452 -0
- package/templates/postman/scripts/runners/run-mutation-tests.sh +113 -0
- package/templates/postman/scripts/runners/run-tests-local.sh +936 -0
- package/templates/postman/scripts/runners/run-tests-staging.sh +741 -0
- package/templates/postman-node/README.md.ejs +26 -0
- package/templates/postman-node/collections/crud/01-bootstrap.json.ejs +34 -0
- package/templates/postman-node/config/local.json.ejs +46 -0
- package/templates/postman-node/config/staging.json.ejs +31 -0
- package/templates/postman-node/local.test.env.ejs +3 -0
- package/templates/postman-node/mocks/external.js +14 -0
- package/templates/postman-node/package.json.ejs +39 -0
- package/templates/postman-node/requirements.txt +1 -0
- package/templates/postman-node/scripts/database/cleanup-mysql.sh +12 -0
- package/templates/postman-node/scripts/database/run-migrations.js +29 -0
- package/templates/postman-node/scripts/database/start-mysql.sh +34 -0
- package/templates/postman-node/scripts/database/wait-for-mysql.sh +36 -0
- package/templates/postman-node/scripts/lib/api_coverage_node.py +1137 -0
- package/templates/postman-node/scripts/lib/fetch-jwt.sh +86 -0
- package/templates/postman-node/scripts/lib/run-newman.sh +104 -0
- package/templates/postman-node/scripts/lib/setup-database.sh +55 -0
- package/templates/postman-node/scripts/lib/start-app.sh +48 -0
- package/templates/postman-node/scripts/lib/utils.sh +114 -0
- package/templates/postman-node/scripts/report-generators/stage-report-artifacts.sh +26 -0
- package/templates/postman-node/scripts/run-all.sh +303 -0
- package/templates/postman-node/scripts/runners/run-tests.sh +123 -0
- package/templates/postman-node/scripts/setup-mocks.js.ejs +29 -0
- package/templates/postman-node/stryker.config.js.ejs +51 -0
- package/templates/rules/local-test-setup.mdc +420 -0
- package/templates/rules/testing-node.mdc +66 -0
- package/templates/rules/testing.mdc +248 -0
- package/templates/skills/_shared/postman-standards.md +380 -0
- package/templates/skills/enhance-test-pipeline/SKILL-java.md +483 -0
- package/templates/skills/enhance-test-pipeline/SKILL-node.md +431 -0
- package/templates/skills/enhance-test-pipeline/SKILL.md +9 -0
- package/templates/skills/review-test-suite/SKILL-java.md +137 -0
- package/templates/skills/review-test-suite/SKILL-node.md +78 -0
- package/templates/skills/review-test-suite/SKILL.md +9 -0
- package/templates/skills/run-test-suite/SKILL-java.md +186 -0
- package/templates/skills/run-test-suite/SKILL-node.md +191 -0
- package/templates/skills/run-test-suite/SKILL.md +9 -0
- package/templates/skills/setup-api-tests/SKILL-java.md +1094 -0
- package/templates/skills/setup-api-tests/SKILL-node.md +141 -0
- package/templates/skills/setup-api-tests/SKILL.md +9 -0
- package/templates/skills/setup-mutation-tests/SKILL-java.md +303 -0
- package/templates/skills/setup-mutation-tests/SKILL-node.md +408 -0
- package/templates/skills/setup-mutation-tests/SKILL.md +9 -0
- package/templates/skills/setup-test-pipeline/SKILL-java.md +454 -0
- package/templates/skills/setup-test-pipeline/SKILL-node.md +318 -0
- package/templates/skills/setup-test-pipeline/SKILL.md +9 -0
- package/templates/skills/write-api-tests/SKILL-java.md +115 -0
- package/templates/skills/write-api-tests/SKILL-node.md +83 -0
- package/templates/skills/write-api-tests/SKILL.md +9 -0
- package/templates/stryker.config.js +50 -0
|
@@ -0,0 +1,936 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
|
|
3
|
+
###############################################################################
|
|
4
|
+
# Local Test Runner
|
|
5
|
+
#
|
|
6
|
+
# Runs API integration tests locally with optional JaCoCo coverage.
|
|
7
|
+
#
|
|
8
|
+
# 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
|
|
18
|
+
#
|
|
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)
|
|
33
|
+
###############################################################################
|
|
34
|
+
|
|
35
|
+
set -e
|
|
36
|
+
|
|
37
|
+
#==============================================================================
|
|
38
|
+
# CONFIGURATION
|
|
39
|
+
#==============================================================================
|
|
40
|
+
|
|
41
|
+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
42
|
+
PROJECT_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)"
|
|
43
|
+
# Display name shown on the reports — the repo/service name, overridable.
|
|
44
|
+
REPORT_LOGO="${SHIFTLEFT_REPORT_LOGO:-$(basename "$PROJECT_ROOT")}"
|
|
45
|
+
POSTMAN_DIR="$PROJECT_ROOT/postman"
|
|
46
|
+
REPORT_DIR="$POSTMAN_DIR/reports"
|
|
47
|
+
TIMESTAMP=$(date +%Y%m%d-%H%M%S)
|
|
48
|
+
|
|
49
|
+
# Application settings
|
|
50
|
+
APP_PORT=8080
|
|
51
|
+
APP_PID_FILE="$SCRIPT_DIR/spring-boot-app.pid"
|
|
52
|
+
APP_LOG_FILE="$SCRIPT_DIR/spring-boot-app.log"
|
|
53
|
+
|
|
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
|
+
# Coverage settings
|
|
62
|
+
COVERAGE_DIR="$PROJECT_ROOT/coverage"
|
|
63
|
+
COVERAGE_EXEC_FILE="$COVERAGE_DIR/jacoco-api-$TIMESTAMP.exec"
|
|
64
|
+
|
|
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
|
+
#==============================================================================
|
|
80
|
+
# STATE TRACKING
|
|
81
|
+
#==============================================================================
|
|
82
|
+
|
|
83
|
+
TOTAL_PASSED=0
|
|
84
|
+
TOTAL_FAILED=0
|
|
85
|
+
declare -a RESULTS
|
|
86
|
+
declare -A COLLECTION_STATUS
|
|
87
|
+
declare -A COLLECTION_PASSED
|
|
88
|
+
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
|
+
|
|
103
|
+
#==============================================================================
|
|
104
|
+
# ARGUMENT PARSING
|
|
105
|
+
#==============================================================================
|
|
106
|
+
|
|
107
|
+
RUN_V3=false
|
|
108
|
+
RUN_V2=false
|
|
109
|
+
RUN_LEGACY=false
|
|
110
|
+
RUN_COVERAGE=false
|
|
111
|
+
MULTI_PRODUCT=false
|
|
112
|
+
SKIP_SERVICES=false
|
|
113
|
+
SHOW_HELP=false
|
|
114
|
+
RUN_ALL=true
|
|
115
|
+
|
|
116
|
+
parse_arguments() {
|
|
117
|
+
for arg in "$@"; do
|
|
118
|
+
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
|
+
;;
|
|
147
|
+
esac
|
|
148
|
+
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
|
+
}
|
|
157
|
+
|
|
158
|
+
show_help() {
|
|
159
|
+
head -32 "$0" | tail -29
|
|
160
|
+
exit 0
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
#==============================================================================
|
|
164
|
+
# UTILITY FUNCTIONS
|
|
165
|
+
#==============================================================================
|
|
166
|
+
|
|
167
|
+
# Extract value from Postman environment JSON
|
|
168
|
+
get_env_value() {
|
|
169
|
+
local env_file="$1"
|
|
170
|
+
local key="$2"
|
|
171
|
+
python3 -c "import json; d=json.load(open('$env_file')); print(next((v['value'] for v in d['values'] if v['key']=='$key'), ''))" 2>/dev/null
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
# Parse Newman output to extract assertion counts
|
|
175
|
+
parse_newman_results() {
|
|
176
|
+
local output_file="$1"
|
|
177
|
+
local assertions_line=$(grep "assertions" "$output_file" 2>/dev/null | head -1)
|
|
178
|
+
|
|
179
|
+
NEWMAN_EXECUTED=$(echo "$assertions_line" | awk -F'│' '{gsub(/[^0-9]/,"",$3); print $3}')
|
|
180
|
+
NEWMAN_FAILED=$(echo "$assertions_line" | awk -F'│' '{gsub(/[^0-9]/,"",$4); print $4}')
|
|
181
|
+
|
|
182
|
+
NEWMAN_EXECUTED=${NEWMAN_EXECUTED:-0}
|
|
183
|
+
NEWMAN_FAILED=${NEWMAN_FAILED:-0}
|
|
184
|
+
NEWMAN_PASSED=$((NEWMAN_EXECUTED > 0 ? NEWMAN_EXECUTED - NEWMAN_FAILED : 0))
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
# Print colored status
|
|
188
|
+
print_status() {
|
|
189
|
+
local status="$1"
|
|
190
|
+
local message="$2"
|
|
191
|
+
if [ "$status" = "success" ]; then
|
|
192
|
+
echo "✓ $message"
|
|
193
|
+
elif [ "$status" = "error" ]; then
|
|
194
|
+
echo "✗ $message"
|
|
195
|
+
elif [ "$status" = "warning" ]; then
|
|
196
|
+
echo "⚠ $message"
|
|
197
|
+
else
|
|
198
|
+
echo " $message"
|
|
199
|
+
fi
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
#==============================================================================
|
|
203
|
+
# JAVA SETUP
|
|
204
|
+
#==============================================================================
|
|
205
|
+
|
|
206
|
+
setup_java() {
|
|
207
|
+
echo "Detecting Java 11..."
|
|
208
|
+
|
|
209
|
+
# Try SDKMAN first
|
|
210
|
+
export SDKMAN_DIR="$HOME/.sdkman"
|
|
211
|
+
if [ -s "$SDKMAN_DIR/bin/sdkman-init.sh" ]; then
|
|
212
|
+
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
|
|
218
|
+
fi
|
|
219
|
+
|
|
220
|
+
# Try java_home (macOS)
|
|
221
|
+
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
|
|
242
|
+
fi
|
|
243
|
+
|
|
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
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
#==============================================================================
|
|
255
|
+
# SERVICE MANAGEMENT
|
|
256
|
+
#==============================================================================
|
|
257
|
+
|
|
258
|
+
# Source container runtime detection
|
|
259
|
+
source "$SCRIPT_DIR/../infra/container-runtime.sh"
|
|
260
|
+
|
|
261
|
+
start_services() {
|
|
262
|
+
if [ "$SKIP_SERVICES" = true ]; then
|
|
263
|
+
echo "Skipping service startup (--skip-services)"
|
|
264
|
+
return 0
|
|
265
|
+
fi
|
|
266
|
+
|
|
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"
|
|
273
|
+
|
|
274
|
+
# Step 2: Start WireMock
|
|
275
|
+
echo ""
|
|
276
|
+
echo "========================================="
|
|
277
|
+
echo "Step 2: Starting WireMock"
|
|
278
|
+
echo "========================================="
|
|
279
|
+
"$SCRIPT_DIR/../infra/start-mocks.sh"
|
|
280
|
+
|
|
281
|
+
# Step 3: Start Spring Boot
|
|
282
|
+
echo ""
|
|
283
|
+
echo "========================================="
|
|
284
|
+
echo "Step 3: Starting Spring Boot"
|
|
285
|
+
echo "========================================="
|
|
286
|
+
start_spring_boot
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
start_spring_boot() {
|
|
290
|
+
if curl -s "http://localhost:$APP_PORT/actuator/health" > /dev/null 2>&1; then
|
|
291
|
+
print_status "success" "Spring Boot already running on port $APP_PORT"
|
|
292
|
+
return 0
|
|
293
|
+
fi
|
|
294
|
+
|
|
295
|
+
cd "$PROJECT_ROOT"
|
|
296
|
+
echo "Starting Spring Boot with integration profile..."
|
|
297
|
+
|
|
298
|
+
local jvm_args="-Dserver.port=$APP_PORT"
|
|
299
|
+
|
|
300
|
+
if [ "$RUN_COVERAGE" = true ]; then
|
|
301
|
+
local jacoco_jar="$HOME/.m2/repository/org/jacoco/org.jacoco.agent/0.8.11/org.jacoco.agent-0.8.11-runtime.jar"
|
|
302
|
+
if [ -f "$jacoco_jar" ]; then
|
|
303
|
+
mkdir -p "$COVERAGE_DIR"
|
|
304
|
+
jvm_args="-javaagent:$jacoco_jar=destfile=$COVERAGE_EXEC_FILE,includes=com.freshworks.marketplace.installation.* $jvm_args"
|
|
305
|
+
echo "JaCoCo coverage enabled"
|
|
306
|
+
else
|
|
307
|
+
echo "Warning: JaCoCo agent not found. Run 'mvn dependency:resolve' first."
|
|
308
|
+
fi
|
|
309
|
+
fi
|
|
310
|
+
|
|
311
|
+
nohup mvn -pl installation spring-boot:run \
|
|
312
|
+
-Dspring-boot.run.profiles=integration \
|
|
313
|
+
-Dspring-boot.run.jvmArguments="$jvm_args" \
|
|
314
|
+
-Dmaven.test.skip=true -Dcheckstyle.skip=true \
|
|
315
|
+
> "$APP_LOG_FILE" 2>&1 &
|
|
316
|
+
|
|
317
|
+
local app_pid=$!
|
|
318
|
+
echo "$app_pid" > "$APP_PID_FILE"
|
|
319
|
+
|
|
320
|
+
# Wait for startup
|
|
321
|
+
local max_wait=120
|
|
322
|
+
local waited=0
|
|
323
|
+
while ! curl -s "http://localhost:$APP_PORT/actuator/health" > /dev/null 2>&1; do
|
|
324
|
+
if [ $waited -ge $max_wait ]; then
|
|
325
|
+
echo "Error: Application failed to start"
|
|
326
|
+
tail -30 "$APP_LOG_FILE"
|
|
327
|
+
exit 1
|
|
328
|
+
fi
|
|
329
|
+
sleep 2
|
|
330
|
+
waited=$((waited + 2))
|
|
331
|
+
printf "\r Waiting... %d/%ds" $waited $max_wait
|
|
332
|
+
done
|
|
333
|
+
echo ""
|
|
334
|
+
print_status "success" "Spring Boot started (PID: $app_pid)"
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
stop_services() {
|
|
338
|
+
echo ""
|
|
339
|
+
echo "========================================="
|
|
340
|
+
echo "Cleanup"
|
|
341
|
+
echo "========================================="
|
|
342
|
+
|
|
343
|
+
# Stop Spring Boot
|
|
344
|
+
if [ -f "$APP_PID_FILE" ]; then
|
|
345
|
+
local app_pid=$(cat "$APP_PID_FILE")
|
|
346
|
+
if ps -p "$app_pid" > /dev/null 2>&1; then
|
|
347
|
+
echo "Stopping Spring Boot application (PID: $app_pid)..."
|
|
348
|
+
kill "$app_pid" 2>/dev/null || true
|
|
349
|
+
sleep 2
|
|
350
|
+
fi
|
|
351
|
+
rm -f "$APP_PID_FILE"
|
|
352
|
+
fi
|
|
353
|
+
|
|
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
|
|
363
|
+
|
|
364
|
+
# Generate coverage report if enabled
|
|
365
|
+
generate_coverage_report
|
|
366
|
+
|
|
367
|
+
print_status "success" "Cleanup completed"
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
generate_coverage_report() {
|
|
371
|
+
if [ "$RUN_COVERAGE" != true ] || [ ! -f "$COVERAGE_EXEC_FILE" ]; then
|
|
372
|
+
return 0
|
|
373
|
+
fi
|
|
374
|
+
|
|
375
|
+
echo ""
|
|
376
|
+
echo "========================================="
|
|
377
|
+
echo "Generating Coverage Report"
|
|
378
|
+
echo "========================================="
|
|
379
|
+
|
|
380
|
+
local coverage_html_dir="$COVERAGE_DIR/html-report-$TIMESTAMP"
|
|
381
|
+
mkdir -p "$coverage_html_dir"
|
|
382
|
+
|
|
383
|
+
cd "$PROJECT_ROOT/installation"
|
|
384
|
+
|
|
385
|
+
echo "Generating JaCoCo report..."
|
|
386
|
+
mvn jacoco:report -q 2>/dev/null || true
|
|
387
|
+
|
|
388
|
+
if [ -d "target/site/jacoco" ]; then
|
|
389
|
+
cp -r target/site/jacoco/* "$coverage_html_dir/" 2>/dev/null || true
|
|
390
|
+
|
|
391
|
+
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"
|
|
400
|
+
else
|
|
401
|
+
echo "Warning: Coverage report generation failed"
|
|
402
|
+
fi
|
|
403
|
+
}
|
|
404
|
+
|
|
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
|
+
#==============================================================================
|
|
479
|
+
# TEST EXECUTION
|
|
480
|
+
#==============================================================================
|
|
481
|
+
|
|
482
|
+
# Run all collections in a directory
|
|
483
|
+
run_collection_dir() {
|
|
484
|
+
local dir_name="$1"
|
|
485
|
+
local collections_dir="$2"
|
|
486
|
+
local report_prefix="$3"
|
|
487
|
+
local collection_key="$4"
|
|
488
|
+
|
|
489
|
+
echo "========================================="
|
|
490
|
+
echo "Running: $dir_name"
|
|
491
|
+
echo "========================================="
|
|
492
|
+
|
|
493
|
+
local dir_passed=0
|
|
494
|
+
local dir_failed=0
|
|
495
|
+
local all_passed=true
|
|
496
|
+
local newman_tmp="/tmp/newman-output-${TIMESTAMP}-$$.txt"
|
|
497
|
+
|
|
498
|
+
# Create working environment copy
|
|
499
|
+
local working_env="/tmp/newman-env-${collection_key}-${TIMESTAMP}.json"
|
|
500
|
+
cp "$ENVIRONMENT" "$working_env"
|
|
501
|
+
|
|
502
|
+
# Run collections in order
|
|
503
|
+
for collection_file in "$collections_dir"/*.json; do
|
|
504
|
+
[ ! -f "$collection_file" ] && continue
|
|
505
|
+
|
|
506
|
+
local basename=$(basename "$collection_file" .json)
|
|
507
|
+
local html_report="$REPORT_DIR/${report_prefix}-${basename}-$TIMESTAMP.html"
|
|
508
|
+
local json_report="$REPORT_DIR/json/${report_prefix}-${basename}-$TIMESTAMP.json"
|
|
509
|
+
local env_export="/tmp/newman-env-export-${collection_key}-${basename}.json"
|
|
510
|
+
|
|
511
|
+
echo ""
|
|
512
|
+
echo " → Running: $basename"
|
|
513
|
+
echo ""
|
|
514
|
+
|
|
515
|
+
"$POSTMAN_DIR/node_modules/.bin/newman" run "$collection_file" \
|
|
516
|
+
--environment "$working_env" \
|
|
517
|
+
--reporters cli,htmlextra,json \
|
|
518
|
+
--reporter-htmlextra-export "$html_report" \
|
|
519
|
+
--reporter-json-export "$json_report" \
|
|
520
|
+
--export-environment "$env_export" \
|
|
521
|
+
--timeout-request 30000 \
|
|
522
|
+
--delay-request 100 2>&1 | tee "$newman_tmp"
|
|
523
|
+
|
|
524
|
+
parse_newman_results "$newman_tmp"
|
|
525
|
+
|
|
526
|
+
dir_passed=$((dir_passed + NEWMAN_PASSED))
|
|
527
|
+
dir_failed=$((dir_failed + NEWMAN_FAILED))
|
|
528
|
+
|
|
529
|
+
# Chain environment for next collection
|
|
530
|
+
[ -f "$env_export" ] && cp "$env_export" "$working_env"
|
|
531
|
+
|
|
532
|
+
if [ "$NEWMAN_FAILED" -gt 0 ]; then
|
|
533
|
+
all_passed=false
|
|
534
|
+
print_status "error" "$basename: FAILED ($NEWMAN_PASSED passed, $NEWMAN_FAILED failed)"
|
|
535
|
+
else
|
|
536
|
+
print_status "success" "$basename: PASSED ($NEWMAN_PASSED passed, $NEWMAN_FAILED failed)"
|
|
537
|
+
fi
|
|
538
|
+
done
|
|
539
|
+
|
|
540
|
+
# Cleanup temp files
|
|
541
|
+
rm -f "$working_env" /tmp/newman-env-export-${collection_key}-*.json "$newman_tmp"
|
|
542
|
+
|
|
543
|
+
# Update status
|
|
544
|
+
if [ "$all_passed" = true ] && [ "$dir_failed" -eq 0 ]; then
|
|
545
|
+
COLLECTION_STATUS["$collection_key"]="✓ PASSED"
|
|
546
|
+
RESULTS+=("$dir_name: ✓ PASSED ($dir_passed passed, $dir_failed failed)")
|
|
547
|
+
echo ""
|
|
548
|
+
print_status "success" "$dir_name: ALL PASSED"
|
|
549
|
+
else
|
|
550
|
+
COLLECTION_STATUS["$collection_key"]="✗ FAILED"
|
|
551
|
+
RESULTS+=("$dir_name: ✗ FAILED ($dir_passed passed, $dir_failed failed)")
|
|
552
|
+
echo ""
|
|
553
|
+
print_status "error" "$dir_name: SOME FAILED"
|
|
554
|
+
fi
|
|
555
|
+
|
|
556
|
+
COLLECTION_PASSED["$collection_key"]=$dir_passed
|
|
557
|
+
COLLECTION_FAILED["$collection_key"]=$dir_failed
|
|
558
|
+
TOTAL_PASSED=$((TOTAL_PASSED + dir_passed))
|
|
559
|
+
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
|
+
}
|
|
713
|
+
|
|
714
|
+
run_tests() {
|
|
715
|
+
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
|
|
731
|
+
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
|
|
737
|
+
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
|
|
749
|
+
fi
|
|
750
|
+
}
|
|
751
|
+
|
|
752
|
+
#==============================================================================
|
|
753
|
+
# REPORTING
|
|
754
|
+
#==============================================================================
|
|
755
|
+
|
|
756
|
+
print_summary() {
|
|
757
|
+
echo ""
|
|
758
|
+
echo "========================================="
|
|
759
|
+
if [ "$MULTI_PRODUCT" = true ]; then
|
|
760
|
+
echo "Multi-Product Test Results"
|
|
761
|
+
else
|
|
762
|
+
echo "Test Results Summary"
|
|
763
|
+
fi
|
|
764
|
+
echo "========================================="
|
|
765
|
+
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
|
|
802
|
+
|
|
803
|
+
echo ""
|
|
804
|
+
echo "Overall Total: $TOTAL_PASSED passed, $TOTAL_FAILED failed"
|
|
805
|
+
echo ""
|
|
806
|
+
echo "Reports: $REPORT_DIR/"
|
|
807
|
+
}
|
|
808
|
+
|
|
809
|
+
generate_consolidated_report() {
|
|
810
|
+
# Calculate passed/failed collections
|
|
811
|
+
local passed_collections=0
|
|
812
|
+
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
|
|
831
|
+
|
|
832
|
+
local test_result=0
|
|
833
|
+
[ $TOTAL_FAILED -gt 0 ] && test_result=1
|
|
834
|
+
|
|
835
|
+
if [ -f "$SCRIPT_DIR/../report-generators/generate-consolidated-report.sh" ]; then
|
|
836
|
+
"$SCRIPT_DIR/../report-generators/generate-consolidated-report.sh" "local" "$TIMESTAMP" "$REPORT_DIR" \
|
|
837
|
+
"$passed_collections" "$failed_collections" "$test_result" || true
|
|
838
|
+
fi
|
|
839
|
+
}
|
|
840
|
+
|
|
841
|
+
#==============================================================================
|
|
842
|
+
# MAIN
|
|
843
|
+
#==============================================================================
|
|
844
|
+
|
|
845
|
+
main() {
|
|
846
|
+
parse_arguments "$@"
|
|
847
|
+
|
|
848
|
+
[ "$SHOW_HELP" = true ] && show_help
|
|
849
|
+
|
|
850
|
+
# Display banner
|
|
851
|
+
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
|
|
857
|
+
echo "========================================="
|
|
858
|
+
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"
|
|
877
|
+
[ "$SKIP_SERVICES" = true ] && echo " ⚠ Services: Skipped (--skip-services)"
|
|
878
|
+
echo ""
|
|
879
|
+
|
|
880
|
+
# Setup
|
|
881
|
+
setup_java
|
|
882
|
+
|
|
883
|
+
echo ""
|
|
884
|
+
echo "Checking prerequisites..."
|
|
885
|
+
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; }
|
|
887
|
+
command -v node >/dev/null 2>&1 || { echo "Error: Node.js required" >&2; exit 1; }
|
|
888
|
+
check_container_runtime || exit 1
|
|
889
|
+
print_status "success" "Prerequisites OK"
|
|
890
|
+
|
|
891
|
+
# Setup cleanup trap
|
|
892
|
+
trap stop_services EXIT INT TERM
|
|
893
|
+
|
|
894
|
+
# Start services
|
|
895
|
+
start_services
|
|
896
|
+
|
|
897
|
+
# Install Newman and setup git hooks
|
|
898
|
+
echo ""
|
|
899
|
+
echo "========================================="
|
|
900
|
+
echo "Step 4: Installing Newman"
|
|
901
|
+
echo "========================================="
|
|
902
|
+
cd "$POSTMAN_DIR"
|
|
903
|
+
npm ci --ignore-scripts --silent 2>/dev/null || npm install --ignore-scripts --silent
|
|
904
|
+
print_status "success" "Newman ready"
|
|
905
|
+
|
|
906
|
+
# Setup git hooks for auto-formatting (runs once, idempotent)
|
|
907
|
+
if [ -d "$POSTMAN_DIR/.husky" ]; then
|
|
908
|
+
cd "$PROJECT_ROOT"
|
|
909
|
+
git config core.hooksPath postman/.husky/_ 2>/dev/null || true
|
|
910
|
+
fi
|
|
911
|
+
|
|
912
|
+
mkdir -p "$REPORT_DIR" "$REPORT_DIR/json"
|
|
913
|
+
|
|
914
|
+
# Run tests
|
|
915
|
+
run_tests
|
|
916
|
+
|
|
917
|
+
# Print summary
|
|
918
|
+
print_summary
|
|
919
|
+
|
|
920
|
+
# Generate consolidated report
|
|
921
|
+
generate_consolidated_report
|
|
922
|
+
|
|
923
|
+
# Exit with appropriate code
|
|
924
|
+
if [ $TOTAL_FAILED -gt 0 ]; then
|
|
925
|
+
echo ""
|
|
926
|
+
echo "❌ Some tests failed"
|
|
927
|
+
exit 1
|
|
928
|
+
else
|
|
929
|
+
echo ""
|
|
930
|
+
echo "✅ All tests passed successfully!"
|
|
931
|
+
exit 0
|
|
932
|
+
fi
|
|
933
|
+
}
|
|
934
|
+
|
|
935
|
+
# Run main
|
|
936
|
+
main "$@"
|