@freshworks/shiftleft-tools 1.1.15 → 1.1.16

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@freshworks/shiftleft-tools",
3
- "version": "1.1.15",
3
+ "version": "1.1.16",
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
@@ -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 = 'mp-installation:htmlextra-hash-nav'
12
+ _HTML_EXTRA_HASH_NAV_MARKER = 'shiftleft:htmlextra-hash-nav'
13
13
  _HTML_EXTRA_HASH_NAV_SCRIPT = '''<script>
14
- /* mp-installation:htmlextra-hash-nav */
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('com.freshworks.marketplace.installation.', '')
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