@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.
Files changed (106) hide show
  1. package/README.md +351 -0
  2. package/bin/shiftleft.js +95 -0
  3. package/package.json +57 -0
  4. package/src/commands/doctor.js +208 -0
  5. package/src/commands/init-postman.js +298 -0
  6. package/src/commands/init-rules.js +78 -0
  7. package/src/commands/link.js +172 -0
  8. package/src/commands/protect.js +61 -0
  9. package/src/commands/run-tests.js +182 -0
  10. package/src/commands/setup-pipeline.js +209 -0
  11. package/src/commands/update.js +203 -0
  12. package/src/index.js +4 -0
  13. package/src/utils/copy-tree.js +98 -0
  14. package/src/utils/gitignore.js +26 -0
  15. package/src/utils/logger.js +9 -0
  16. package/src/utils/manifest.js +145 -0
  17. package/src/utils/stack.js +80 -0
  18. package/src/utils/template.js +135 -0
  19. package/templates/AGENTS.md +109 -0
  20. package/templates/CLAUDE.md +3 -0
  21. package/templates/jenkins/Jenkinsfile-java.groovy +432 -0
  22. package/templates/jenkins/Jenkinsfile-node.groovy +450 -0
  23. package/templates/postman/.husky/pre-commit +19 -0
  24. package/templates/postman/.prettierrc.json +5 -0
  25. package/templates/postman/README.md.ejs +147 -0
  26. package/templates/postman/collections/01-core.json.ejs +91 -0
  27. package/templates/postman/config/local.json.ejs +12 -0
  28. package/templates/postman/config/staging.json.ejs +26 -0
  29. package/templates/postman/environments/local.postman_environment.json.ejs +31 -0
  30. package/templates/postman/environments/staging.postman_environment.json.ejs +31 -0
  31. package/templates/postman/gitignore +16 -0
  32. package/templates/postman/npmrc +31 -0
  33. package/templates/postman/package.json.ejs +66 -0
  34. package/templates/postman/run-all-shim.sh +16 -0
  35. package/templates/postman/scripts/auth/generate-jwt.sh +113 -0
  36. package/templates/postman/scripts/auth/get-issuer-secret.sh +140 -0
  37. package/templates/postman/scripts/infra/start-mocks.sh +138 -0
  38. package/templates/postman/scripts/infra/stop-mocks.sh +43 -0
  39. package/templates/postman/scripts/lib/api_coverage.py +1122 -0
  40. package/templates/postman/scripts/lib/cleanup-reports.sh +101 -0
  41. package/templates/postman/scripts/lib/cleanup-stryker.sh +44 -0
  42. package/templates/postman/scripts/lib/report_combined.py +527 -0
  43. package/templates/postman/scripts/lib/report_consolidated.py +363 -0
  44. package/templates/postman/scripts/lib/report_generator.py +121 -0
  45. package/templates/postman/scripts/lib/report_migration.py +156 -0
  46. package/templates/postman/scripts/lib/report_mutation.py +110 -0
  47. package/templates/postman/scripts/lib/report_unit.py +353 -0
  48. package/templates/postman/scripts/lib/report_utils.py +973 -0
  49. package/templates/postman/scripts/report-generators/generate-consolidated-report.sh +445 -0
  50. package/templates/postman/scripts/report-generators/java-api-coverage-matrix.sh +257 -0
  51. package/templates/postman/scripts/report-generators/mutation-report.sh +672 -0
  52. package/templates/postman/scripts/report-generators/node-api-coverage-matrix.sh +167 -0
  53. package/templates/postman/scripts/report-generators/stage-report-artifacts.sh +27 -0
  54. package/templates/postman/scripts/run-all.sh +452 -0
  55. package/templates/postman/scripts/runners/run-mutation-tests.sh +113 -0
  56. package/templates/postman/scripts/runners/run-tests-local.sh +936 -0
  57. package/templates/postman/scripts/runners/run-tests-staging.sh +741 -0
  58. package/templates/postman-node/README.md.ejs +26 -0
  59. package/templates/postman-node/collections/crud/01-bootstrap.json.ejs +34 -0
  60. package/templates/postman-node/config/local.json.ejs +46 -0
  61. package/templates/postman-node/config/staging.json.ejs +31 -0
  62. package/templates/postman-node/local.test.env.ejs +3 -0
  63. package/templates/postman-node/mocks/external.js +14 -0
  64. package/templates/postman-node/package.json.ejs +39 -0
  65. package/templates/postman-node/requirements.txt +1 -0
  66. package/templates/postman-node/scripts/database/cleanup-mysql.sh +12 -0
  67. package/templates/postman-node/scripts/database/run-migrations.js +29 -0
  68. package/templates/postman-node/scripts/database/start-mysql.sh +34 -0
  69. package/templates/postman-node/scripts/database/wait-for-mysql.sh +36 -0
  70. package/templates/postman-node/scripts/lib/api_coverage_node.py +1137 -0
  71. package/templates/postman-node/scripts/lib/fetch-jwt.sh +86 -0
  72. package/templates/postman-node/scripts/lib/run-newman.sh +104 -0
  73. package/templates/postman-node/scripts/lib/setup-database.sh +55 -0
  74. package/templates/postman-node/scripts/lib/start-app.sh +48 -0
  75. package/templates/postman-node/scripts/lib/utils.sh +114 -0
  76. package/templates/postman-node/scripts/report-generators/stage-report-artifacts.sh +26 -0
  77. package/templates/postman-node/scripts/run-all.sh +303 -0
  78. package/templates/postman-node/scripts/runners/run-tests.sh +123 -0
  79. package/templates/postman-node/scripts/setup-mocks.js.ejs +29 -0
  80. package/templates/postman-node/stryker.config.js.ejs +51 -0
  81. package/templates/rules/local-test-setup.mdc +420 -0
  82. package/templates/rules/testing-node.mdc +66 -0
  83. package/templates/rules/testing.mdc +248 -0
  84. package/templates/skills/_shared/postman-standards.md +380 -0
  85. package/templates/skills/enhance-test-pipeline/SKILL-java.md +483 -0
  86. package/templates/skills/enhance-test-pipeline/SKILL-node.md +431 -0
  87. package/templates/skills/enhance-test-pipeline/SKILL.md +9 -0
  88. package/templates/skills/review-test-suite/SKILL-java.md +137 -0
  89. package/templates/skills/review-test-suite/SKILL-node.md +78 -0
  90. package/templates/skills/review-test-suite/SKILL.md +9 -0
  91. package/templates/skills/run-test-suite/SKILL-java.md +186 -0
  92. package/templates/skills/run-test-suite/SKILL-node.md +191 -0
  93. package/templates/skills/run-test-suite/SKILL.md +9 -0
  94. package/templates/skills/setup-api-tests/SKILL-java.md +1094 -0
  95. package/templates/skills/setup-api-tests/SKILL-node.md +141 -0
  96. package/templates/skills/setup-api-tests/SKILL.md +9 -0
  97. package/templates/skills/setup-mutation-tests/SKILL-java.md +303 -0
  98. package/templates/skills/setup-mutation-tests/SKILL-node.md +408 -0
  99. package/templates/skills/setup-mutation-tests/SKILL.md +9 -0
  100. package/templates/skills/setup-test-pipeline/SKILL-java.md +454 -0
  101. package/templates/skills/setup-test-pipeline/SKILL-node.md +318 -0
  102. package/templates/skills/setup-test-pipeline/SKILL.md +9 -0
  103. package/templates/skills/write-api-tests/SKILL-java.md +115 -0
  104. package/templates/skills/write-api-tests/SKILL-node.md +83 -0
  105. package/templates/skills/write-api-tests/SKILL.md +9 -0
  106. package/templates/stryker.config.js +50 -0
@@ -0,0 +1,167 @@
1
+ #!/bin/bash
2
+
3
+ ###############################################################################
4
+ # API Coverage Matrix Generator - Node.js Services (thin wrapper)
5
+ #
6
+ # Usage:
7
+ # ./node-api-coverage-matrix.sh [ROUTES_DIR] [POSTMAN_DIR]
8
+ ###############################################################################
9
+
10
+ set -e
11
+
12
+ if [ -t 1 ]; then
13
+ RED='\033[0;31m'
14
+ GREEN='\033[0;32m'
15
+ YELLOW='\033[1;33m'
16
+ BLUE='\033[0;34m'
17
+ NC='\033[0m'
18
+ else
19
+ RED='' GREEN='' YELLOW='' BLUE='' NC=''
20
+ fi
21
+
22
+ log_info() { echo -e "${BLUE}[INFO]${NC} $1"; }
23
+ log_success() { echo -e "${GREEN}[SUCCESS]${NC} $1"; }
24
+ log_warn() { echo -e "${YELLOW}[WARN]${NC} $1"; }
25
+ log_error() { echo -e "${RED}[ERROR]${NC} $1" >&2; }
26
+
27
+ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
28
+ PROJECT_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)"
29
+ REPO_ROOT="$PROJECT_ROOT"
30
+
31
+ source "$SCRIPT_DIR/../lib/cleanup-reports.sh"
32
+
33
+ check_python() {
34
+ if ! command -v python3 &> /dev/null; then
35
+ log_error "Python 3 is required but not installed."
36
+ exit 2
37
+ fi
38
+ }
39
+
40
+ find_routes_dir() {
41
+ local base_dir="$1"
42
+ local patterns=("src/controllers" "src/routes" "routes" "lib/routes" "app/routes" "api/routes" "server/routes" "controllers")
43
+ for pattern in "${patterns[@]}"; do
44
+ if [ -d "$base_dir/$pattern" ]; then
45
+ echo "$base_dir/$pattern"
46
+ return 0
47
+ fi
48
+ done
49
+ echo ""
50
+ }
51
+
52
+ find_postman_dir() {
53
+ local base_dir="$1"
54
+ if [ -d "$base_dir/postman" ]; then
55
+ echo "$base_dir/postman"
56
+ return 0
57
+ fi
58
+ echo ""
59
+ }
60
+
61
+ validate_inputs() {
62
+ local routes_dir="$1"
63
+ local postman_dir="$2"
64
+ if [ -z "$routes_dir" ] || [ ! -d "$routes_dir" ]; then
65
+ log_error "Routes directory not found: $routes_dir"
66
+ exit 1
67
+ fi
68
+ JS_COUNT=$(find "$routes_dir" \( -name "*.js" -o -name "*.ts" \) 2>/dev/null | wc -l | tr -d ' ')
69
+ if [ "$JS_COUNT" -eq 0 ]; then
70
+ log_error "No JavaScript/TypeScript files found in: $routes_dir"
71
+ exit 3
72
+ fi
73
+ log_info "Found $JS_COUNT route files"
74
+ if [ -z "$postman_dir" ] || [ ! -d "$postman_dir" ]; then
75
+ log_error "Postman directory not found: $postman_dir"
76
+ exit 1
77
+ fi
78
+ }
79
+
80
+ echo "========================================"
81
+ echo "Node.js API Coverage Matrix Generator"
82
+ echo "========================================"
83
+ echo ""
84
+
85
+ check_python
86
+
87
+ if [ -n "${1:-}" ]; then
88
+ if [ -d "$1" ]; then
89
+ ROUTES_DIR="$(cd "$1" && pwd)"
90
+ elif [ -d "$REPO_ROOT/$1" ]; then
91
+ ROUTES_DIR="$(cd "$REPO_ROOT/$1" && pwd)"
92
+ else
93
+ ROUTES_DIR=$(find_routes_dir "$REPO_ROOT")
94
+ log_warn "Routes path '$1' not found; using: ${ROUTES_DIR:-<none>}"
95
+ fi
96
+ else
97
+ ROUTES_DIR=$(find_routes_dir "$REPO_ROOT")
98
+ fi
99
+
100
+ if [ -n "${2:-}" ]; then
101
+ if [ -d "$2" ]; then
102
+ POSTMAN_DIR="$(cd "$2" && pwd)"
103
+ elif [ -d "$REPO_ROOT/$2" ]; then
104
+ POSTMAN_DIR="$(cd "$REPO_ROOT/$2" && pwd)"
105
+ else
106
+ POSTMAN_DIR=$(find_postman_dir "$REPO_ROOT")
107
+ log_warn "Postman path '$2' not found; using: ${POSTMAN_DIR:-<none>}"
108
+ fi
109
+ else
110
+ POSTMAN_DIR=$(find_postman_dir "$REPO_ROOT")
111
+ fi
112
+
113
+ echo "Configuration:"
114
+ echo " ROUTES_DIR: ${ROUTES_DIR:-<not found>}"
115
+ echo " POSTMAN_DIR: ${POSTMAN_DIR:-<not found>}"
116
+ echo ""
117
+
118
+ validate_inputs "$ROUTES_DIR" "$POSTMAN_DIR"
119
+
120
+ REPORTS_DIR="$POSTMAN_DIR/reports"
121
+ mkdir -p "$REPORTS_DIR"
122
+ cleanup_reports_by_pattern "$REPORTS_DIR" "api-coverage-matrix-*.html" 1
123
+ cleanup_reports_by_pattern "$REPORTS_DIR" "api-coverage-matrix-*.json" 1
124
+
125
+ TIMESTAMP=$(date +%Y%m%d-%H%M%S)
126
+ HTML_OUTPUT_FILE="$REPORTS_DIR/api-coverage-matrix-$TIMESTAMP.html"
127
+
128
+ log_info "Generating coverage matrix..."
129
+ cd "$SCRIPT_DIR/lib"
130
+ if [ -f api_coverage_node.py ]; then
131
+ COVERAGE_SCRIPT=api_coverage_node.py
132
+ else
133
+ log_error "api_coverage_node.py not found in lib/ — re-run shiftleft init-postman --stack node"
134
+ exit 1
135
+ fi
136
+ python3 "$COVERAGE_SCRIPT" \
137
+ --routes-dir "$ROUTES_DIR" \
138
+ --postman-dir "$POSTMAN_DIR" \
139
+ --html-output "$HTML_OUTPUT_FILE"
140
+ PYTHON_EXIT=$?
141
+
142
+ if [ $PYTHON_EXIT -ne 0 ]; then
143
+ log_error "Matrix generation failed (exit code: $PYTHON_EXIT)"
144
+ exit $PYTHON_EXIT
145
+ fi
146
+
147
+ LATEST_LINK="$REPORTS_DIR/api-coverage-matrix-latest.html"
148
+ cp -f "$HTML_OUTPUT_FILE" "$LATEST_LINK"
149
+ log_info "Latest report copy: $LATEST_LINK"
150
+
151
+ CANONICAL_REPORTS_DIR="$REPO_ROOT/postman/reports"
152
+ mkdir -p "$CANONICAL_REPORTS_DIR"
153
+ REAL_REPORTS="$(cd "$REPORTS_DIR" && pwd)"
154
+ REAL_CANON="$(cd "$CANONICAL_REPORTS_DIR" && pwd)"
155
+ if [ "$REAL_REPORTS" != "$REAL_CANON" ]; then
156
+ cp -f "$HTML_OUTPUT_FILE" "$CANONICAL_REPORTS_DIR/"
157
+ cp -f "$LATEST_LINK" "$CANONICAL_REPORTS_DIR/api-coverage-matrix-latest.html"
158
+ JSON_SIDE="${HTML_OUTPUT_FILE%.html}.json"
159
+ [ -f "$JSON_SIDE" ] && cp -f "$JSON_SIDE" "$CANONICAL_REPORTS_DIR/"
160
+ log_info "Mirrored reports to $CANONICAL_REPORTS_DIR for Jenkins publishHTML."
161
+ fi
162
+
163
+ echo ""
164
+ echo "========================================"
165
+ log_success "Matrix Generation Complete"
166
+ echo "HTML: $HTML_OUTPUT_FILE"
167
+ echo "========================================"
@@ -0,0 +1,27 @@
1
+ #!/bin/bash
2
+ #
3
+ # Copy JaCoCo and PIT HTML reports into postman/reports/artifacts/
4
+ # so the combined quality report can link to them with relative URLs (Jenkins-safe).
5
+ #
6
+ # Usage: stage-report-artifacts.sh <project_root> <reports_dir>
7
+
8
+ set -euo pipefail
9
+
10
+ PROJECT_ROOT="${1:?project root required}"
11
+ REPORTS_DIR="${2:?reports dir required}"
12
+ ARTIFACTS_DIR="$REPORTS_DIR/artifacts"
13
+
14
+ rm -rf "$ARTIFACTS_DIR"
15
+ mkdir -p "$ARTIFACTS_DIR/jacoco" "$ARTIFACTS_DIR/pit"
16
+
17
+ JACOCO_SRC="$PROJECT_ROOT/target/site/jacoco"
18
+ if [ -d "$JACOCO_SRC" ] && [ -f "$JACOCO_SRC/index.html" ]; then
19
+ cp -R "$JACOCO_SRC/." "$ARTIFACTS_DIR/jacoco/"
20
+ echo "Staged JaCoCo report -> artifacts/jacoco/"
21
+ fi
22
+
23
+ PIT_DIR="$PROJECT_ROOT/target/pit-reports"
24
+ if [ -f "$PIT_DIR/index.html" ]; then
25
+ cp -R "$PIT_DIR/." "$ARTIFACTS_DIR/pit/"
26
+ echo "Staged PIT report -> artifacts/pit/"
27
+ fi
@@ -0,0 +1,452 @@
1
+ #!/bin/bash
2
+ #
3
+ # Unified Test & Quality Report Runner
4
+ # =====================================
5
+ # Single script that runs everything and produces one combined report:
6
+ # 1. Unit Tests + JaCoCo Coverage
7
+ # 2. PIT Mutation Testing
8
+ # 3. Postman API Integration Tests (local or staging)
9
+ # 4. API Coverage Matrix
10
+ # 5. Combined Quality Report (all in one)
11
+ #
12
+ # Usage:
13
+ # ./run-all.sh [OPTIONS]
14
+ #
15
+ # Options:
16
+ # --env {local|staging} Target environment for Postman tests (default: local)
17
+ # --skip-unit Skip unit tests (use existing results)
18
+ # --skip-mutation Skip mutation tests (use existing results)
19
+ # --skip-postman Skip Postman API tests (use existing results)
20
+ # --skip-coverage Skip API coverage matrix (use existing results)
21
+ # --skip-report Skip quality report generation
22
+ # --no-delay Skip delays between phases
23
+ # -h, --help Show this help message
24
+ #
25
+ # Examples:
26
+ # ./run-all.sh # Run everything (local tests)
27
+ # ./run-all.sh --env staging # Run everything against staging
28
+ # ./run-all.sh --skip-mutation # Skip slow mutation tests
29
+ # ./run-all.sh --env staging --skip-unit # Staging tests only, no unit tests
30
+ #
31
+
32
+ set -euo pipefail
33
+
34
+ # Colors
35
+ GREEN='\033[0;32m'
36
+ RED='\033[0;31m'
37
+ YELLOW='\033[1;33m'
38
+ BLUE='\033[0;34m'
39
+ CYAN='\033[0;36m'
40
+ BOLD='\033[1m'
41
+ NC='\033[0m'
42
+
43
+ # Configuration
44
+ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
45
+ PROJECT_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)"
46
+ # Display name shown on the reports — the repo/service name, overridable.
47
+ REPORT_LOGO="${SHIFTLEFT_REPORT_LOGO:-$(basename "$PROJECT_ROOT")}"
48
+ export REPORT_LOGO
49
+ POSTMAN_DIR="$(dirname "$SCRIPT_DIR")"
50
+ REPORTS_DIR="$POSTMAN_DIR/reports"
51
+ APP_PORT="${APP_PORT:-9090}"
52
+ TIMESTAMP=$(date +"%Y%m%d-%H%M%S")
53
+
54
+ # Environment selection (local or staging)
55
+ POSTMAN_ENV="${POSTMAN_ENV:-local}"
56
+
57
+ # Flags
58
+ SKIP_UNIT=false
59
+ SKIP_MUTATION=false
60
+ SKIP_POSTMAN=false
61
+ SKIP_COVERAGE=false
62
+ SKIP_REPORT=false
63
+ SKIP_DELAY=false
64
+
65
+ # Track results
66
+ UNIT_OK=false
67
+ MUTATION_OK=false
68
+ POSTMAN_OK=false
69
+ COVERAGE_OK=false
70
+ POSTMAN_RESULT=0
71
+
72
+ # Parse arguments
73
+ while [[ $# -gt 0 ]]; do
74
+ case $1 in
75
+ -h|--help) head -31 "$0" | tail -29; exit 0 ;;
76
+ --env) POSTMAN_ENV="$2"; shift 2 ;;
77
+ --skip-unit) SKIP_UNIT=true; shift ;;
78
+ --skip-mutation) SKIP_MUTATION=true; shift ;;
79
+ --skip-postman) SKIP_POSTMAN=true; shift ;;
80
+ --skip-coverage) SKIP_COVERAGE=true; shift ;;
81
+ --skip-report) SKIP_REPORT=true; shift ;;
82
+ --no-delay) SKIP_DELAY=true; shift ;;
83
+ *) echo -e "${RED}Unknown option: $1${NC}"; exit 1 ;;
84
+ esac
85
+ done
86
+
87
+ # Validate environment
88
+ if [[ ! "$POSTMAN_ENV" =~ ^(local|staging)$ ]]; then
89
+ echo -e "${RED}✗ Error: Invalid environment '$POSTMAN_ENV'${NC}"
90
+ echo -e "${YELLOW}Supported environments: local, staging${NC}"
91
+ exit 1
92
+ fi
93
+
94
+ # Cleanup function
95
+ cleanup() {
96
+ # Stop Spring Boot if running
97
+ if [ -f "$SCRIPT_DIR/spring-boot-app.pid" ]; then
98
+ APP_PID=$(cat "$SCRIPT_DIR/spring-boot-app.pid" 2>/dev/null || echo "")
99
+ if [ -n "$APP_PID" ] && ps -p "$APP_PID" > /dev/null 2>&1; then
100
+ echo -e "${YELLOW}Stopping Spring Boot application...${NC}"
101
+ kill "$APP_PID" 2>/dev/null || true
102
+ sleep 2
103
+ if ps -p "$APP_PID" > /dev/null 2>&1; then
104
+ kill -9 "$APP_PID" 2>/dev/null || true
105
+ fi
106
+ fi
107
+ rm -f "$SCRIPT_DIR/spring-boot-app.pid"
108
+ fi
109
+ # Stop WireMock only if local Postman tests were actually run (mocks were started)
110
+ if [ "$SKIP_POSTMAN" = false ] && [ "$POSTMAN_ENV" = "local" ]; then
111
+ "$SCRIPT_DIR/infra/stop-mocks.sh" 2>/dev/null || true
112
+ fi
113
+ }
114
+
115
+ trap cleanup EXIT
116
+
117
+ mkdir -p "$REPORTS_DIR"
118
+
119
+ # Clean up old reports — keep only the latest 3 of each type
120
+ echo -e "${YELLOW}Cleaning up old reports...${NC}"
121
+ _keep_latest() {
122
+ local pattern="$1" keep="${2:-3}"
123
+ # shellcheck disable=SC2012
124
+ ls -t $pattern 2>/dev/null | tail -n +$((keep + 1)) | xargs rm -f 2>/dev/null || true
125
+ }
126
+ _keep_latest "$REPORTS_DIR/quality-report-*.html" 3
127
+ if [ "$SKIP_POSTMAN" = false ]; then
128
+ # Remove only THIS environment's stale per-collection Newman HTML reports
129
+ # (they are about to be regenerated). Scoping the delete to the current env
130
+ # leaves the other env's reports — and the "View Details" links of its kept
131
+ # consolidated report — intact.
132
+ find "$REPORTS_DIR" -maxdepth 1 -name "test-${POSTMAN_ENV}-*.html" \
133
+ -delete 2>/dev/null || true
134
+ fi
135
+ if [ "$SKIP_POSTMAN" = false ]; then
136
+ _keep_latest "$REPORTS_DIR/consolidated-*.html" 3
137
+ _keep_latest "$REPORTS_DIR/consolidated-*.json" 3
138
+ rm -rf "$REPORTS_DIR"/json 2>/dev/null || true
139
+ fi
140
+ if [ "$SKIP_COVERAGE" = false ]; then
141
+ _keep_latest "$REPORTS_DIR/api-coverage-matrix-*.html" 3
142
+ _keep_latest "$REPORTS_DIR/api-coverage-matrix-*.json" 3
143
+ fi
144
+ echo -e "${GREEN}✓ Old reports cleaned${NC}"
145
+ echo ""
146
+
147
+ # Print header
148
+ [ -t 1 ] && clear || true
149
+ echo -e "${BOLD}${BLUE}╔════════════════════════════════════════════════════════════╗${NC}"
150
+ echo -e "${BOLD}${BLUE}║ UNIFIED TEST & QUALITY REPORT RUNNER ║${NC}"
151
+ echo -e "${BOLD}${BLUE}║ (One Command, One Report!) ║${NC}"
152
+ echo -e "${BOLD}${BLUE}╚════════════════════════════════════════════════════════════╝${NC}"
153
+ echo ""
154
+ echo -e "${CYAN}This will run:${NC}"
155
+ [ "$SKIP_UNIT" = false ] && echo -e " ${BOLD}1.${NC} Unit Tests + JaCoCo Coverage" || echo -e " ${BOLD}1.${NC} Unit Tests + JaCoCo Coverage ${YELLOW}(skipped)${NC}"
156
+ [ "$SKIP_MUTATION" = false ] && echo -e " ${BOLD}2.${NC} PIT Mutation Testing" || echo -e " ${BOLD}2.${NC} PIT Mutation Testing ${YELLOW}(skipped)${NC}"
157
+ [ "$SKIP_POSTMAN" = false ] && echo -e " ${BOLD}3.${NC} Postman API Integration Tests" || echo -e " ${BOLD}3.${NC} Postman API Integration Tests ${YELLOW}(skipped)${NC}"
158
+ [ "$SKIP_COVERAGE" = false ] && echo -e " ${BOLD}4.${NC} API Coverage Matrix" || echo -e " ${BOLD}4.${NC} API Coverage Matrix ${YELLOW}(skipped)${NC}"
159
+ echo -e " ${BOLD}5.${NC} Combined Quality Report"
160
+ echo ""
161
+ if [ "$SKIP_DELAY" = "false" ]; then
162
+ echo -e "${YELLOW}Starting in 2 seconds... (Ctrl+C to cancel)${NC}"
163
+ sleep 2
164
+ fi
165
+
166
+ # ============================================================================
167
+ # PHASE 1: Unit Tests + JaCoCo Coverage
168
+ # ============================================================================
169
+ echo ""
170
+ echo -e "${CYAN}╔════════════════════════════════════════════════════════════╗${NC}"
171
+ echo -e "${CYAN}║ Phase 1: Unit Tests + JaCoCo Coverage ║${NC}"
172
+ echo -e "${CYAN}╚════════════════════════════════════════════════════════════╝${NC}"
173
+ echo ""
174
+
175
+ SUREFIRE_DIR="$PROJECT_ROOT/target/surefire-reports"
176
+ JACOCO_XML="$PROJECT_ROOT/target/site/jacoco/jacoco.xml"
177
+ UNIT_JACOCO_XML="$PROJECT_ROOT/target/site/jacoco/jacoco-unit.xml"
178
+
179
+ if [ "$SKIP_UNIT" = false ]; then
180
+ echo -e "${YELLOW}Running unit tests with coverage...${NC}"
181
+ cd "$PROJECT_ROOT"
182
+ if mvn clean test jacoco:report -q; then
183
+ echo -e "${GREEN}✓ Unit tests passed${NC}"
184
+ UNIT_OK=true
185
+ else
186
+ echo -e "${RED}✗ Unit tests had failures${NC}"
187
+ UNIT_OK=true # Still continue - report will show failures
188
+ fi
189
+ # Preserve unit test JaCoCo before Postman phase overwrites it
190
+ if [ -f "$JACOCO_XML" ]; then
191
+ cp "$JACOCO_XML" "$UNIT_JACOCO_XML"
192
+ echo -e "${GREEN}✓ Unit test coverage preserved${NC}"
193
+ fi
194
+ else
195
+ echo -e "${YELLOW}Skipping unit tests (using existing results)${NC}"
196
+ if [ -d "$SUREFIRE_DIR" ]; then
197
+ UNIT_OK=true
198
+ echo -e "${GREEN}✓ Found existing Surefire reports${NC}"
199
+ else
200
+ echo -e "${RED}✗ No existing Surefire reports found${NC}"
201
+ fi
202
+ # Check for preserved unit JaCoCo
203
+ if [ -f "$UNIT_JACOCO_XML" ]; then
204
+ echo -e "${GREEN}✓ Found preserved unit test coverage${NC}"
205
+ elif [ -f "$JACOCO_XML" ]; then
206
+ cp "$JACOCO_XML" "$UNIT_JACOCO_XML"
207
+ echo -e "${GREEN}✓ Current JaCoCo report preserved${NC}"
208
+ fi
209
+ fi
210
+
211
+ # ============================================================================
212
+ # PHASE 2: PIT Mutation Testing
213
+ # ============================================================================
214
+ echo ""
215
+ echo -e "${CYAN}╔════════════════════════════════════════════════════════════╗${NC}"
216
+ echo -e "${CYAN}║ Phase 2: PIT Mutation Testing ║${NC}"
217
+ echo -e "${CYAN}╚════════════════════════════════════════════════════════════╝${NC}"
218
+ echo ""
219
+
220
+ MUTATIONS_XML="$PROJECT_ROOT/target/pit-reports/mutations.xml"
221
+
222
+ if [ "$SKIP_MUTATION" = false ]; then
223
+ echo -e "${YELLOW}Running mutation tests (this may take 2-3 minutes)...${NC}"
224
+ cd "$PROJECT_ROOT"
225
+ # PIT is profile-gated (-Pmutation-tests) per the setup-mutation-tests skill, so the
226
+ # profile's pinned pitest-maven version + pitest-junit5-plugin dependency are used.
227
+ # Without it, Maven resolves PIT's latest with no JUnit 5 plugin -> "could not run any tests".
228
+ # test-compile ensures test classes exist when unit tests were skipped/reused.
229
+ if mvn test-compile org.pitest:pitest-maven:mutationCoverage -Pmutation-tests -q 2>&1; then
230
+ echo -e "${GREEN}✓ Mutation tests completed${NC}"
231
+ MUTATION_OK=true
232
+ else
233
+ echo -e "${RED}✗ Mutation tests failed${NC}"
234
+ fi
235
+ else
236
+ echo -e "${YELLOW}Skipping mutation tests (using existing results)${NC}"
237
+ if [ -f "$MUTATIONS_XML" ]; then
238
+ MUTATION_OK=true
239
+ echo -e "${GREEN}✓ Found existing mutation data${NC}"
240
+ else
241
+ echo -e "${RED}✗ No existing mutation data found${NC}"
242
+ fi
243
+ fi
244
+
245
+ # ============================================================================
246
+ # PHASE 3: Postman API Integration Tests
247
+ # ============================================================================
248
+ echo ""
249
+ echo -e "${CYAN}╔════════════════════════════════════════════════════════════╗${NC}"
250
+ echo -e "${CYAN}║ Phase 3: Postman API Integration Tests ($(echo "$POSTMAN_ENV" | tr '[:lower:]' '[:upper:]')) ║${NC}"
251
+ echo -e "${CYAN}╚════════════════════════════════════════════════════════════╝${NC}"
252
+ echo ""
253
+
254
+ POSTMAN_PASSED=0
255
+ POSTMAN_FAILED=0
256
+ POSTMAN_JSON_DIR="$REPORTS_DIR/json"
257
+
258
+ if [ "$SKIP_POSTMAN" = false ]; then
259
+ # Clear old JSON reports so totals only reflect this run
260
+ rm -rf "$POSTMAN_JSON_DIR"
261
+
262
+ # Delegate to appropriate test runner (local or staging)
263
+ set +e
264
+ if [ "$POSTMAN_ENV" = "staging" ]; then
265
+ "$SCRIPT_DIR/runners/run-tests-staging.sh"
266
+ else
267
+ "$SCRIPT_DIR/runners/run-tests-local.sh"
268
+ fi
269
+ POSTMAN_RESULT=$?
270
+ set -e
271
+
272
+ # Derive collection pass/fail counts from the consolidated JSON
273
+ # (individual JSON reports are cleaned up by run-*sh scripts after consolidation)
274
+ CONSOLIDATED_JSON=$(ls -t "$REPORTS_DIR"/consolidated-*.json 2>/dev/null | head -1)
275
+ if [ -n "$CONSOLIDATED_JSON" ] && [ -f "$CONSOLIDATED_JSON" ]; then
276
+ POSTMAN_PASSED=$(jq '.summary.collections.passed // 0' "$CONSOLIDATED_JSON" 2>/dev/null || echo "0")
277
+ POSTMAN_FAILED=$(jq '.summary.collections.failed // 0' "$CONSOLIDATED_JSON" 2>/dev/null || echo "0")
278
+ POSTMAN_OK=true
279
+ fi
280
+ else
281
+ echo -e "${YELLOW}Skipping Postman tests (using existing results)${NC}"
282
+ if ls "$POSTMAN_JSON_DIR"/*.json 1>/dev/null 2>&1; then
283
+ POSTMAN_OK=true
284
+ echo -e "${GREEN}✓ Found existing Postman results${NC}"
285
+ else
286
+ echo -e "${RED}✗ No existing Postman results found${NC}"
287
+ fi
288
+ fi
289
+
290
+ # ============================================================================
291
+ # PHASE 4: API Coverage Matrix
292
+ # ============================================================================
293
+ echo ""
294
+ echo -e "${CYAN}╔════════════════════════════════════════════════════════════╗${NC}"
295
+ echo -e "${CYAN}║ Phase 4: API Coverage Matrix ║${NC}"
296
+ echo -e "${CYAN}╚════════════════════════════════════════════════════════════╝${NC}"
297
+ echo ""
298
+
299
+ API_COVERAGE_JSON=""
300
+
301
+ if [ "$SKIP_COVERAGE" = false ]; then
302
+ echo -e "${YELLOW}Generating API coverage matrix...${NC}"
303
+ if "$SCRIPT_DIR/report-generators/java-api-coverage-matrix.sh" 2>&1; then
304
+ COVERAGE_OK=true
305
+ echo -e "${GREEN}✓ API coverage matrix generated${NC}"
306
+ else
307
+ echo -e "${RED}✗ API coverage matrix failed${NC}"
308
+ fi
309
+ else
310
+ echo -e "${YELLOW}Skipping API coverage matrix (using existing results)${NC}"
311
+ if ls "$REPORTS_DIR"/api-coverage-matrix-*.json 1>/dev/null 2>&1; then
312
+ COVERAGE_OK=true
313
+ echo -e "${GREEN}✓ Found existing API coverage data${NC}"
314
+ fi
315
+ fi
316
+
317
+ # Load API coverage JSON if available
318
+ COVERAGE_JSON_FILE=$(ls -t "$REPORTS_DIR"/api-coverage-matrix-*.json 2>/dev/null | head -1 || true)
319
+ if [ -n "$COVERAGE_JSON_FILE" ] && [ -f "$COVERAGE_JSON_FILE" ]; then
320
+ API_COVERAGE_JSON=$(cat "$COVERAGE_JSON_FILE")
321
+ fi
322
+
323
+ # ============================================================================
324
+ # PHASE 5: Generate Combined Quality Report
325
+ # ============================================================================
326
+ echo ""
327
+ echo -e "${CYAN}╔════════════════════════════════════════════════════════════╗${NC}"
328
+ echo -e "${CYAN}║ Phase 5: Generate Combined Quality Report ║${NC}"
329
+ echo -e "${CYAN}╚════════════════════════════════════════════════════════════╝${NC}"
330
+ echo ""
331
+
332
+ if [ "$SKIP_REPORT" = false ]; then
333
+ # Calculate Postman stats from the consolidated JSON
334
+ # (individual JSON reports are cleaned up by run-tests-local.sh after consolidation)
335
+ POSTMAN_TOTAL_ASSERTIONS=0
336
+ POSTMAN_PASSED_ASSERTIONS=0
337
+ POSTMAN_FAILED_ASSERTIONS=0
338
+ POSTMAN_TOTAL_REQUESTS=0
339
+ POSTMAN_DURATION="0"
340
+
341
+ CONSOLIDATED_JSON="${CONSOLIDATED_JSON:-$(ls -t "$REPORTS_DIR"/consolidated-*.json 2>/dev/null | head -1)}"
342
+ if [ -n "$CONSOLIDATED_JSON" ] && [ -f "$CONSOLIDATED_JSON" ]; then
343
+ POSTMAN_TOTAL_ASSERTIONS=$(jq '.summary.assertions.total // 0' "$CONSOLIDATED_JSON" 2>/dev/null || echo "0")
344
+ POSTMAN_FAILED_ASSERTIONS=$(jq '.summary.assertions.failed // 0' "$CONSOLIDATED_JSON" 2>/dev/null || echo "0")
345
+ POSTMAN_TOTAL_REQUESTS=$(jq '.summary.requests.total // 0' "$CONSOLIDATED_JSON" 2>/dev/null || echo "0")
346
+ POSTMAN_TOTAL_DURATION=$(jq '.summary.duration_ms // 0' "$CONSOLIDATED_JSON" 2>/dev/null || echo "0")
347
+ POSTMAN_PASSED_ASSERTIONS=$((POSTMAN_TOTAL_ASSERTIONS - POSTMAN_FAILED_ASSERTIONS))
348
+ POSTMAN_DURATION=$(echo "scale=2; $POSTMAN_TOTAL_DURATION / 1000" | bc 2>/dev/null || echo "0")
349
+ fi
350
+
351
+ COMBINED_OUTPUT="$REPORTS_DIR/quality-report-$TIMESTAMP.html"
352
+
353
+ echo -e "${YELLOW}Staging report artifacts for Jenkins-safe links...${NC}"
354
+ "$SCRIPT_DIR/report-generators/stage-report-artifacts.sh" "$PROJECT_ROOT" "$REPORTS_DIR"
355
+
356
+ echo -e "${YELLOW}Generating combined report...${NC}"
357
+
358
+ # Build report arguments as an array to handle spaces properly
359
+ REPORT_ARGS=(combined "$COMBINED_OUTPUT" --logo "$REPORT_LOGO" --env "$POSTMAN_ENV" --timestamp "$TIMESTAMP")
360
+
361
+ # Unit test data (use preserved unit test JaCoCo, not the postman one)
362
+ [ -d "$SUREFIRE_DIR" ] && REPORT_ARGS+=(--surefire-dir "$SUREFIRE_DIR")
363
+ if [ -f "$UNIT_JACOCO_XML" ]; then
364
+ REPORT_ARGS+=(--jacoco-xml "$UNIT_JACOCO_XML")
365
+ elif [ -f "$JACOCO_XML" ]; then
366
+ REPORT_ARGS+=(--jacoco-xml "$JACOCO_XML")
367
+ fi
368
+ [ -f "$MUTATIONS_XML" ] && REPORT_ARGS+=(--mutations-xml "$MUTATIONS_XML")
369
+
370
+ # Postman data
371
+ REPORT_ARGS+=(--postman-assertions-total "$POSTMAN_TOTAL_ASSERTIONS")
372
+ REPORT_ARGS+=(--postman-assertions-passed "$POSTMAN_PASSED_ASSERTIONS")
373
+ REPORT_ARGS+=(--postman-assertions-failed "$POSTMAN_FAILED_ASSERTIONS")
374
+ REPORT_ARGS+=(--postman-requests "$POSTMAN_TOTAL_REQUESTS")
375
+ REPORT_ARGS+=(--postman-duration "$POSTMAN_DURATION")
376
+ REPORT_ARGS+=(--postman-collections-passed "$POSTMAN_PASSED")
377
+ REPORT_ARGS+=(--postman-collections-failed "$POSTMAN_FAILED")
378
+
379
+ # API coverage data
380
+ TEMP_COVERAGE=""
381
+ if [ -n "$API_COVERAGE_JSON" ]; then
382
+ TEMP_COVERAGE="/tmp/api-coverage-data-$$.json"
383
+ echo "$API_COVERAGE_JSON" > "$TEMP_COVERAGE"
384
+ REPORT_ARGS+=(--api-coverage-file "$TEMP_COVERAGE")
385
+ fi
386
+
387
+ # API coverage HTML (for linking)
388
+ API_COVERAGE_HTML=$(ls -t "$REPORTS_DIR"/api-coverage-matrix-*.html 2>/dev/null | head -1 || true)
389
+ if [ -n "$API_COVERAGE_HTML" ]; then
390
+ REPORT_ARGS+=(--api-coverage-html "$API_COVERAGE_HTML")
391
+ fi
392
+
393
+ # Newman JSON results directory (for per-collection details)
394
+ if [ -d "$POSTMAN_JSON_DIR" ]; then
395
+ REPORT_ARGS+=(--postman-json-dir "$POSTMAN_JSON_DIR")
396
+ fi
397
+
398
+ # Consolidated Postman HTML report (for linking).
399
+ # Always (re)generate it for THIS run's timestamp so the combined report never
400
+ # links to a stale consolidated report whose per-collection detail files have
401
+ # already been cleaned up. Only fall back to the newest existing one if this
402
+ # run produced no consolidated JSON to render.
403
+ POSTMAN_CONSOLIDATED_HTML="$REPORTS_DIR/consolidated-${POSTMAN_ENV}-${TIMESTAMP}.html"
404
+ CONSOLIDATED_JSON_THIS_RUN="$REPORTS_DIR/consolidated-${POSTMAN_ENV}-${TIMESTAMP}.json"
405
+ if [ ! -f "$POSTMAN_CONSOLIDATED_HTML" ] && [ -f "$CONSOLIDATED_JSON_THIS_RUN" ]; then
406
+ echo -e "${YELLOW}Generating consolidated HTML report...${NC}"
407
+ "$SCRIPT_DIR/report-generators/generate-consolidated-report.sh" "$POSTMAN_ENV" "$TIMESTAMP" "$REPORTS_DIR" "$POSTMAN_PASSED" "$POSTMAN_FAILED" 0 >/dev/null 2>&1 || true
408
+ fi
409
+ if [ ! -f "$POSTMAN_CONSOLIDATED_HTML" ]; then
410
+ POSTMAN_CONSOLIDATED_HTML=$(ls -t "$REPORTS_DIR"/consolidated-*.html 2>/dev/null | head -1 || true)
411
+ fi
412
+ if [ -n "$POSTMAN_CONSOLIDATED_HTML" ] && [ -f "$POSTMAN_CONSOLIDATED_HTML" ]; then
413
+ REPORT_ARGS+=(--postman-consolidated-html "$POSTMAN_CONSOLIDATED_HTML")
414
+ fi
415
+
416
+ python3 "$SCRIPT_DIR/lib/report_generator.py" "${REPORT_ARGS[@]}"
417
+
418
+ # Cleanup temp file
419
+ [ -n "$TEMP_COVERAGE" ] && rm -f "$TEMP_COVERAGE"
420
+
421
+ if [ -f "$COMBINED_OUTPUT" ]; then
422
+ echo -e "${GREEN}✓ Combined report generated${NC}"
423
+
424
+ echo ""
425
+ echo -e "${CYAN}═══════════════════════════════════════════════════════════════${NC}"
426
+ echo -e "${BOLD} FINAL SUMMARY${NC}"
427
+ echo -e "${CYAN}═══════════════════════════════════════════════════════════════${NC}"
428
+ echo ""
429
+ echo -e " ${BLUE}Environment:${NC} ${POSTMAN_ENV^^}"
430
+ echo ""
431
+ [ "$UNIT_OK" = true ] && echo -e " ${GREEN}✓${NC} Unit Tests" || echo -e " ${RED}✗${NC} Unit Tests"
432
+ [ "$MUTATION_OK" = true ] && echo -e " ${GREEN}✓${NC} Mutation Testing" || echo -e " ${RED}✗${NC} Mutation Testing"
433
+ [ "$POSTMAN_OK" = true ] && echo -e " ${GREEN}✓${NC} Postman API Tests (${POSTMAN_PASSED} passed, ${POSTMAN_FAILED} failed)" || echo -e " ${RED}✗${NC} Postman API Tests"
434
+ [ "$COVERAGE_OK" = true ] && echo -e " ${GREEN}✓${NC} API Coverage Matrix" || echo -e " ${RED}✗${NC} API Coverage Matrix"
435
+ echo ""
436
+ echo -e " ${GREEN}Report:${NC} $COMBINED_OUTPUT"
437
+ echo ""
438
+ echo -e "${CYAN}═══════════════════════════════════════════════════════════════${NC}"
439
+
440
+ # Open report on macOS
441
+ if [[ "$OSTYPE" == "darwin"* ]]; then
442
+ open "$COMBINED_OUTPUT" 2>/dev/null || true
443
+ fi
444
+ else
445
+ echo -e "${RED}✗ Failed to generate combined report${NC}"
446
+ exit 1
447
+ fi
448
+ else
449
+ echo -e "${YELLOW}Skipping quality report generation (--skip-report flag set)${NC}"
450
+ fi
451
+
452
+ exit $POSTMAN_RESULT