@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.
- package/package.json +1 -1
- package/templates/postman/scripts/infra/container-runtime.sh +30 -0
- package/templates/postman/scripts/infra/setup-localstack.sh +37 -0
- package/templates/postman/scripts/infra/start-mocks.sh +80 -0
- package/templates/postman/scripts/infra/stop-localstack.sh +17 -0
- package/templates/postman/scripts/lib/api_coverage.py +1 -1
- package/templates/postman/scripts/lib/report_consolidated.py +2 -2
- package/templates/postman/scripts/report-generators/mutation-report.sh +6 -1
- package/templates/postman/scripts/run-all.sh +13 -0
- package/templates/postman/scripts/runners/run-tests-local.sh +143 -570
- package/templates/postman/scripts/runners/run-tests-staging.sh +124 -577
- package/templates/postman-node/scripts/lib/api_coverage_node.py +1 -1
- package/templates/skills/run-test-suite/SKILL-java.md +7 -6
- package/templates/skills/setup-api-tests/SKILL-java.md +70 -26
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@freshworks/shiftleft-tools",
|
|
3
|
-
"version": "1.1.
|
|
3
|
+
"version": "1.1.17",
|
|
4
4
|
"description": "CLI for managing Cursor rules/skills and Postman test infrastructure across Java Spring Boot and Node.js/Express projects",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "src/index.js",
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# Detect container runtime: prefer docker, fall back to podman.
|
|
3
|
+
# Sourced by runners — sets CONTAINER_CMD and CONTAINER_COMPOSE_CMD. Must not exit parent.
|
|
4
|
+
[ -n "${__CONTAINER_RUNTIME_LOADED:-}" ] && return 0
|
|
5
|
+
__CONTAINER_RUNTIME_LOADED=1
|
|
6
|
+
|
|
7
|
+
CONTAINER_CMD=""
|
|
8
|
+
CONTAINER_COMPOSE_CMD=""
|
|
9
|
+
|
|
10
|
+
if command -v docker >/dev/null 2>&1 && docker info >/dev/null 2>&1; then
|
|
11
|
+
CONTAINER_CMD="docker"
|
|
12
|
+
elif command -v podman >/dev/null 2>&1 && podman info >/dev/null 2>&1; then
|
|
13
|
+
CONTAINER_CMD="podman"
|
|
14
|
+
fi
|
|
15
|
+
|
|
16
|
+
case "$CONTAINER_CMD" in
|
|
17
|
+
docker)
|
|
18
|
+
if docker compose version >/dev/null 2>&1; then CONTAINER_COMPOSE_CMD="docker compose"
|
|
19
|
+
elif command -v docker-compose >/dev/null 2>&1; then CONTAINER_COMPOSE_CMD="docker-compose"; fi ;;
|
|
20
|
+
podman)
|
|
21
|
+
if podman compose version >/dev/null 2>&1; then CONTAINER_COMPOSE_CMD="podman compose"
|
|
22
|
+
elif command -v podman-compose >/dev/null 2>&1; then CONTAINER_COMPOSE_CMD="podman-compose"; fi ;;
|
|
23
|
+
esac
|
|
24
|
+
|
|
25
|
+
if [ -z "$CONTAINER_CMD" ]; then
|
|
26
|
+
echo "Warning: No container runtime (docker/podman) available; LocalStack steps will be skipped."
|
|
27
|
+
else
|
|
28
|
+
echo "Container runtime: $CONTAINER_CMD (compose: ${CONTAINER_COMPOSE_CMD:-none})"
|
|
29
|
+
fi
|
|
30
|
+
export CONTAINER_CMD CONTAINER_COMPOSE_CMD
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# Start LocalStack (S3) for integration tests.
|
|
3
|
+
# Container runtime auto-detected (docker preferred, podman fallback) via container-runtime.sh.
|
|
4
|
+
|
|
5
|
+
set -euo pipefail
|
|
6
|
+
|
|
7
|
+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
8
|
+
PROJECT_ROOT="$(cd "$SCRIPT_DIR/../../.." && pwd)"
|
|
9
|
+
COMPOSE_FILE="${LOCALSTACK_COMPOSE_FILE:-$PROJECT_ROOT/docker-compose-localstack.yml}"
|
|
10
|
+
CONTAINER_NAME="${LOCALSTACK_CONTAINER_NAME:-$(basename "$PROJECT_ROOT")-localstack}"
|
|
11
|
+
|
|
12
|
+
source "$SCRIPT_DIR/container-runtime.sh"
|
|
13
|
+
|
|
14
|
+
if [ -z "$CONTAINER_CMD" ] || [ -z "$CONTAINER_COMPOSE_CMD" ]; then
|
|
15
|
+
echo "Warning: No container runtime (docker/podman) available — skipping LocalStack (S3 tests may fail)"
|
|
16
|
+
exit 0
|
|
17
|
+
fi
|
|
18
|
+
|
|
19
|
+
if $CONTAINER_CMD ps --format '{{.Names}}' | grep -q "^${CONTAINER_NAME}$"; then
|
|
20
|
+
echo "LocalStack already running"
|
|
21
|
+
exit 0
|
|
22
|
+
fi
|
|
23
|
+
|
|
24
|
+
echo "Starting LocalStack..."
|
|
25
|
+
$CONTAINER_COMPOSE_CMD -f "$COMPOSE_FILE" up -d
|
|
26
|
+
|
|
27
|
+
echo "Waiting for LocalStack S3..."
|
|
28
|
+
for _ in $(seq 1 30); do
|
|
29
|
+
if curl -sf http://localhost:4566/_localstack/health >/dev/null 2>&1; then
|
|
30
|
+
echo "LocalStack is ready"
|
|
31
|
+
exit 0
|
|
32
|
+
fi
|
|
33
|
+
sleep 2
|
|
34
|
+
done
|
|
35
|
+
|
|
36
|
+
echo "LocalStack failed to start within timeout" >&2
|
|
37
|
+
exit 1
|
|
@@ -24,6 +24,15 @@ WIREMOCK_DIR="$SCRIPT_DIR/wiremock"
|
|
|
24
24
|
MAPPINGS_DIR="$WIREMOCK_DIR/mappings"
|
|
25
25
|
FILES_DIR="$WIREMOCK_DIR/__files"
|
|
26
26
|
|
|
27
|
+
# Idempotent: if WireMock is already up and serving mappings, do nothing.
|
|
28
|
+
# Lets the orchestrator (run-all.sh) and the runner both call this safely —
|
|
29
|
+
# whichever runs first starts it, the second call is a no-op.
|
|
30
|
+
if curl -s "http://localhost:$WIREMOCK_PORT/__admin/mappings" 2>/dev/null \
|
|
31
|
+
| grep -q '"id"'; then
|
|
32
|
+
echo -e "${GREEN}✓ WireMock already running on port $WIREMOCK_PORT — skipping start${NC}"
|
|
33
|
+
exit 0
|
|
34
|
+
fi
|
|
35
|
+
|
|
27
36
|
# Create directories
|
|
28
37
|
mkdir -p "$MAPPINGS_DIR"
|
|
29
38
|
mkdir -p "$FILES_DIR"
|
|
@@ -81,6 +90,62 @@ cat > "$MAPPINGS_DIR/crypto-decrypt.json" << 'EOF'
|
|
|
81
90
|
}
|
|
82
91
|
EOF
|
|
83
92
|
|
|
93
|
+
# Create Global Search Platform mock
|
|
94
|
+
# App calls POST ${search.api.host}/<version>/<account.name>/<account.id>/query via the
|
|
95
|
+
# GlobalSearchPlatformClient Feign client. Point search.api.host at this WireMock (e.g.
|
|
96
|
+
# search.api.host=http://localhost:9999 in the app's integration config) so this mapping
|
|
97
|
+
# serves it. Returns internal listing ids; the service hydrates them from the DB.
|
|
98
|
+
cat > "$MAPPINGS_DIR/global-search-query.json" << 'EOF'
|
|
99
|
+
{
|
|
100
|
+
"request": {
|
|
101
|
+
"method": "POST",
|
|
102
|
+
"urlPathPattern": "/.*/query"
|
|
103
|
+
},
|
|
104
|
+
"response": {
|
|
105
|
+
"status": 200,
|
|
106
|
+
"headers": {
|
|
107
|
+
"Content-Type": "application/json"
|
|
108
|
+
},
|
|
109
|
+
"jsonBody": {
|
|
110
|
+
"total": 3,
|
|
111
|
+
"results": [
|
|
112
|
+
{ "id": 1 },
|
|
113
|
+
{ "id": 2 },
|
|
114
|
+
{ "id": 4 }
|
|
115
|
+
]
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
EOF
|
|
120
|
+
|
|
121
|
+
# Create Freddy AI Platform mocks
|
|
122
|
+
# SuggestionService -> FreddyAIPlatformClient POSTs to
|
|
123
|
+
# ${client.freddy.ai.platform.host}/v1/ai-service/neodeveloperplatform/azure/<op>.
|
|
124
|
+
# Point client.freddy.ai.platform.host at this WireMock to serve suggestion tests.
|
|
125
|
+
# check_profanity content MUST be "false" (Boolean.parseBoolean) so descriptions pass.
|
|
126
|
+
freddy_base="/v1/ai-service/neodeveloperplatform/azure"
|
|
127
|
+
make_freddy_mock() {
|
|
128
|
+
local op="$1" content="$2"
|
|
129
|
+
cat > "$MAPPINGS_DIR/freddy-${op}.json" << EOF
|
|
130
|
+
{
|
|
131
|
+
"request": { "method": "POST", "urlPathPattern": "${freddy_base}/${op}" },
|
|
132
|
+
"response": {
|
|
133
|
+
"status": 200,
|
|
134
|
+
"headers": { "Content-Type": "application/json" },
|
|
135
|
+
"jsonBody": {
|
|
136
|
+
"choices": [ { "index": 0, "message": { "role": "assistant", "content": "${content}" } } ],
|
|
137
|
+
"usage": { "completion_tokens": 1, "prompt_tokens": 1, "total_tokens": 2 }
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
EOF
|
|
142
|
+
}
|
|
143
|
+
make_freddy_mock "check_profanity" "false"
|
|
144
|
+
make_freddy_mock "suggest_app_name" "Postman Suggested App"
|
|
145
|
+
make_freddy_mock "suggest_app_categories" "Category Suggestion"
|
|
146
|
+
make_freddy_mock "enrich_app_description" "Enriched description from WireMock."
|
|
147
|
+
make_freddy_mock "summarise_app_description" "Summary from WireMock."
|
|
148
|
+
|
|
84
149
|
echo -e "${GREEN}✓ Mock mappings created${NC}"
|
|
85
150
|
echo ""
|
|
86
151
|
|
|
@@ -121,6 +186,19 @@ if ! curl -s "http://localhost:$WIREMOCK_PORT/__admin" > /dev/null 2>&1; then
|
|
|
121
186
|
exit 1
|
|
122
187
|
fi
|
|
123
188
|
|
|
189
|
+
# Verify the mappings were actually loaded (server up != mocks served).
|
|
190
|
+
# Count the mappings we wrote to MAPPINGS_DIR and compare against what WireMock loaded.
|
|
191
|
+
expected_mappings=$(ls -1 "$MAPPINGS_DIR"/*.json 2>/dev/null | wc -l | tr -d ' ')
|
|
192
|
+
loaded_mappings=$(curl -s "http://localhost:$WIREMOCK_PORT/__admin/mappings" \
|
|
193
|
+
| grep -o '"id"' | wc -l | tr -d ' ')
|
|
194
|
+
if [ "${loaded_mappings:-0}" -lt "${expected_mappings:-1}" ]; then
|
|
195
|
+
echo -e "${RED}✗ WireMock loaded $loaded_mappings/$expected_mappings mappings — mocks not fully served${NC}"
|
|
196
|
+
echo -e "${YELLOW}Check log file: $WIREMOCK_DIR/wiremock.log${NC}"
|
|
197
|
+
cat "$WIREMOCK_DIR/wiremock.log"
|
|
198
|
+
exit 1
|
|
199
|
+
fi
|
|
200
|
+
echo -e "${GREEN}✓ WireMock loaded $loaded_mappings mapping(s)${NC}"
|
|
201
|
+
|
|
124
202
|
echo ""
|
|
125
203
|
echo -e "${BLUE}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
|
|
126
204
|
echo -e "${GREEN}✓ Mock services ready!${NC}"
|
|
@@ -130,6 +208,8 @@ echo -e "${GREEN}WireMock Admin UI:${NC} http://localhost:$WIREMOCK_PORT/__admin
|
|
|
130
208
|
echo -e "${GREEN}Mock Endpoints:${NC}"
|
|
131
209
|
echo -e " - GET http://localhost:$WIREMOCK_PORT/api/v2/oauth/apps/*"
|
|
132
210
|
echo -e " - POST http://localhost:$WIREMOCK_PORT/api/crypto/decrypt"
|
|
211
|
+
echo -e " - POST http://localhost:$WIREMOCK_PORT/<version>/<account>/<id>/query (global search)"
|
|
212
|
+
echo -e " - POST http://localhost:$WIREMOCK_PORT/v1/ai-service/neodeveloperplatform/azure/* (freddy ai)"
|
|
133
213
|
echo ""
|
|
134
214
|
echo -e "${YELLOW}To stop WireMock:${NC} ./stop-mocks.sh"
|
|
135
215
|
echo ""
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# Stop LocalStack.
|
|
3
|
+
# Container runtime auto-detected (docker preferred, podman fallback) via container-runtime.sh.
|
|
4
|
+
|
|
5
|
+
set -euo pipefail
|
|
6
|
+
|
|
7
|
+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
8
|
+
PROJECT_ROOT="$(cd "$SCRIPT_DIR/../../.." && pwd)"
|
|
9
|
+
COMPOSE_FILE="${LOCALSTACK_COMPOSE_FILE:-$PROJECT_ROOT/docker-compose-localstack.yml}"
|
|
10
|
+
CONTAINER_NAME="${LOCALSTACK_CONTAINER_NAME:-$(basename "$PROJECT_ROOT")-localstack}"
|
|
11
|
+
|
|
12
|
+
source "$SCRIPT_DIR/container-runtime.sh"
|
|
13
|
+
|
|
14
|
+
if [ -n "$CONTAINER_CMD" ] && $CONTAINER_CMD ps --format '{{.Names}}' | grep -q "^${CONTAINER_NAME}$"; then
|
|
15
|
+
echo "Stopping LocalStack..."
|
|
16
|
+
$CONTAINER_COMPOSE_CMD -f "$COMPOSE_FILE" down >/dev/null 2>&1 || true
|
|
17
|
+
fi
|
|
@@ -337,7 +337,7 @@ def extract_tests_from_item(item, collection_name, depth=0):
|
|
|
337
337
|
|
|
338
338
|
# Try format with Reason and Env (flexible spacing/separators)
|
|
339
339
|
skip_match = re.search(
|
|
340
|
-
r"\[SKIP\].*?Reason:\s*([^|,;]+)(?:[|,;]\s*Env:\s*(\
|
|
340
|
+
r"\[SKIP\].*?Reason:\s*([^|,;]+)(?:[|,;]\s*Env:\s*(\w+))?",
|
|
341
341
|
prereq_text, re.IGNORECASE
|
|
342
342
|
)
|
|
343
343
|
if skip_match:
|
|
@@ -9,9 +9,9 @@ from html import escape as esc
|
|
|
9
9
|
from report_utils import get_html_template
|
|
10
10
|
|
|
11
11
|
|
|
12
|
-
_HTML_EXTRA_HASH_NAV_MARKER = '
|
|
12
|
+
_HTML_EXTRA_HASH_NAV_MARKER = 'shiftleft:htmlextra-hash-nav'
|
|
13
13
|
_HTML_EXTRA_HASH_NAV_SCRIPT = '''<script>
|
|
14
|
-
/*
|
|
14
|
+
/* shiftleft:htmlextra-hash-nav */
|
|
15
15
|
(function() {
|
|
16
16
|
function showFailedTab() {
|
|
17
17
|
if (window.location.hash !== '#pills-failed' || !window.jQuery) {
|
|
@@ -126,11 +126,16 @@ python3 "$SCRIPT_DIR/../lib/report_generator.py" mutation "$MUTATIONS_XML" "$HTM
|
|
|
126
126
|
: << 'SKIP_INLINE_PYTHON'
|
|
127
127
|
# Python script to parse mutations.xml and generate report
|
|
128
128
|
python3 << 'PYTHON_SCRIPT' - "$MUTATIONS_XML" "$HTML_OUTPUT"
|
|
129
|
+
import os
|
|
129
130
|
import sys
|
|
130
131
|
import xml.etree.ElementTree as ET
|
|
131
132
|
from collections import defaultdict
|
|
132
133
|
from datetime import datetime
|
|
133
134
|
|
|
135
|
+
# Optional package prefix to strip from displayed names (set by the runner /
|
|
136
|
+
# /setup-api-tests). Empty = show fully-qualified package names.
|
|
137
|
+
PACKAGE_PREFIX = os.environ.get('MUTATION_PACKAGE_PREFIX', '')
|
|
138
|
+
|
|
134
139
|
mutations_xml = sys.argv[1]
|
|
135
140
|
html_output = sys.argv[2]
|
|
136
141
|
|
|
@@ -537,7 +542,7 @@ html += ''' </tbody>
|
|
|
537
542
|
for package, stats in package_sorted:
|
|
538
543
|
rate = (stats['killed'] / stats['total'] * 100) if stats['total'] > 0 else 0
|
|
539
544
|
status_class = 'success' if rate >= 80 else 'good' if rate >= 70 else 'warning' if rate >= 50 else 'error'
|
|
540
|
-
short_pkg = package.replace(
|
|
545
|
+
short_pkg = package.replace(PACKAGE_PREFIX, '') if PACKAGE_PREFIX else package
|
|
541
546
|
html += f''' <tr>
|
|
542
547
|
<td class="left">{short_pkg}</td>
|
|
543
548
|
<td><strong>{rate:.1f}%</strong></td>
|
|
@@ -259,6 +259,19 @@ if [ "$SKIP_POSTMAN" = false ]; then
|
|
|
259
259
|
# Clear old JSON reports so totals only reflect this run
|
|
260
260
|
rm -rf "$POSTMAN_JSON_DIR"
|
|
261
261
|
|
|
262
|
+
# Start mocked externals centrally (local only) BEFORE delegating to the
|
|
263
|
+
# runner — search + suggestion tests need WireMock up. Done here rather than
|
|
264
|
+
# in the runner because runners/run-tests-local.sh can be 'protected' in
|
|
265
|
+
# .shiftleft.json (repo-owned, not refreshed from the package), so it may
|
|
266
|
+
# not start mocks. start-mocks.sh is idempotent; cleanup() stops it on exit.
|
|
267
|
+
if [ "$POSTMAN_ENV" = "local" ]; then
|
|
268
|
+
echo -e "${YELLOW}Starting WireMock (mocked externals)...${NC}"
|
|
269
|
+
if ! "$SCRIPT_DIR/infra/start-mocks.sh"; then
|
|
270
|
+
echo -e "${RED}✗ WireMock failed to start — required for local API tests${NC}"
|
|
271
|
+
exit 1
|
|
272
|
+
fi
|
|
273
|
+
fi
|
|
274
|
+
|
|
262
275
|
# Delegate to appropriate test runner (local or staging)
|
|
263
276
|
set +e
|
|
264
277
|
if [ "$POSTMAN_ENV" = "staging" ]; then
|