Skip to content

Commit 4515a11

Browse files
committed
fix(ci): use Java WireMock instead of Docker for macOS runners
- Replace Colima/Docker with standalone WireMock JAR - Add run_integration_tests_ci.sh for CI-specific execution - Use sudo for binding to privileged HTTPS port 443
1 parent 40d3998 commit 4515a11

File tree

2 files changed

+294
-37
lines changed

2 files changed

+294
-37
lines changed

.github/workflows/integration-tests.yml

Lines changed: 28 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ permissions:
1919
env:
2020
XCODE_VERSION: "16.4"
2121
WORKING_DIRECTORY: IntegrationTests
22+
WIREMOCK_VERSION: "3.9.1"
2223

2324
jobs:
2425
integration-tests:
@@ -33,25 +34,20 @@ jobs:
3334
- name: Select Xcode
3435
run: sudo xcode-select -s /Applications/Xcode_${{ env.XCODE_VERSION }}.app
3536

36-
- name: Install Homebrew dependencies
37-
run: |
38-
brew install tuist colima docker
39-
40-
- name: Start Colima (Docker runtime for macOS)
41-
run: |
42-
colima start --cpu 2 --memory 4 --disk 10
43-
# Wait for Docker to be ready
44-
timeout 60 bash -c 'until docker info &>/dev/null; do sleep 2; done'
45-
echo "✅ Docker is ready"
37+
- name: Install Tuist
38+
run: brew install tuist
4639

47-
- name: Verify Docker is working
48-
run: |
49-
docker --version
50-
docker info
40+
- name: Setup Java
41+
uses: actions/setup-java@v4
42+
with:
43+
distribution: 'temurin'
44+
java-version: '17'
5145

52-
- name: Pull WireMock image
46+
- name: Download WireMock standalone
5347
run: |
54-
docker pull wiremock/wiremock:3.9.1
48+
curl -L -o wiremock.jar \
49+
"https://repo1.maven.org/maven2/org/wiremock/wiremock-standalone/${{ env.WIREMOCK_VERSION }}/wiremock-standalone-${{ env.WIREMOCK_VERSION }}.jar"
50+
echo "✅ WireMock downloaded"
5551
5652
- name: List available simulators
5753
run: |
@@ -62,12 +58,13 @@ jobs:
6258
working-directory: ${{ env.WORKING_DIRECTORY }}
6359
env:
6460
CI: "true"
61+
WIREMOCK_JAR: ${{ github.workspace }}/wiremock.jar
6562
run: |
6663
# Make scripts executable
67-
chmod +x run_clean_integration_tests.sh common.sh
64+
chmod +x run_clean_integration_tests.sh common.sh run_integration_tests_ci.sh
6865
69-
# Run the verification script
70-
./run_clean_integration_tests.sh
66+
# Run the CI-specific verification script
67+
./run_integration_tests_ci.sh
7168
7269
- name: Collect WireMock logs on failure
7370
if: failure()
@@ -76,20 +73,17 @@ jobs:
7673
echo "📋 Collecting WireMock logs..."
7774
mkdir -p artifacts
7875
79-
# Get container name from the script
80-
CONTAINER_NAME="wiremock-verify"
81-
82-
# Collect logs if container exists
83-
if docker ps -a --format '{{.Names}}' | grep -q "$CONTAINER_NAME"; then
84-
docker logs "$CONTAINER_NAME" > artifacts/wiremock-logs.txt 2>&1 || true
85-
86-
# Get unmatched requests
87-
curl -s http://localhost:8080/__admin/requests/unmatched > artifacts/unmatched-requests.json 2>/dev/null || true
88-
89-
# Get all requests
90-
curl -s http://localhost:8080/__admin/requests > artifacts/all-requests.json 2>/dev/null || true
76+
# Collect WireMock log file if exists
77+
if [ -f "wiremock.log" ]; then
78+
cp wiremock.log artifacts/wiremock-logs.txt || true
9179
fi
9280
81+
# Get unmatched requests
82+
curl -s http://localhost:8080/__admin/requests/unmatched > artifacts/unmatched-requests.json 2>/dev/null || true
83+
84+
# Get all requests
85+
curl -s http://localhost:8080/__admin/requests > artifacts/all-requests.json 2>/dev/null || true
86+
9387
echo "✅ Logs collected"
9488
9589
- name: Collect test artifacts on failure
@@ -123,13 +117,10 @@ jobs:
123117

124118
- name: Cleanup
125119
if: always()
120+
working-directory: ${{ env.WORKING_DIRECTORY }}
126121
run: |
127-
# Stop any running containers
128-
docker stop wiremock-verify 2>/dev/null || true
129-
docker rm wiremock-verify 2>/dev/null || true
130-
131-
# Stop Colima
132-
colima stop || true
122+
# Stop WireMock if running
123+
pkill -f wiremock || true
133124
134125
# Shutdown simulators
135126
xcrun simctl shutdown all || true
Lines changed: 266 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,266 @@
1+
#!/bin/bash
2+
# CI-specific integration test script that runs WireMock as a Java process
3+
# instead of Docker (for GitHub Actions macOS runners)
4+
set -e
5+
6+
# === Parse script-specific arguments ===
7+
HTTP_PORT=${1:-8080}
8+
# Use port 443 for HTTPS since the SDK connects to this port by default
9+
# Note: This requires elevated privileges on macOS
10+
HTTPS_PORT=${2:-443}
11+
MAPPINGS_DIR=${3:-"./wiremock-recordings"}
12+
13+
# Source common functions
14+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
15+
source "${SCRIPT_DIR}/common.sh"
16+
17+
# === CI-specific configuration ===
18+
WIREMOCK_JAR="${WIREMOCK_JAR:-../wiremock.jar}"
19+
WIREMOCK_PID_FILE="${SCRIPT_DIR}/wiremock.pid"
20+
WIREMOCK_LOG_FILE="${SCRIPT_DIR}/wiremock.log"
21+
22+
# === Build framework and generate project ===
23+
build_framework
24+
25+
echo "🔄 Generating project with Tuist..."
26+
tuist generate --no-open
27+
28+
escape_mapping_bodies() {
29+
echo "🔄 Converting mapping bodies to escaped format (WireMock-compatible)..."
30+
local MAPPINGS_FILES="${MAPPINGS_DIR}/mappings"
31+
32+
if [ -d "$MAPPINGS_FILES" ] && [ "$(ls -A $MAPPINGS_FILES/*.json 2>/dev/null)" ]; then
33+
for mapping_file in "$MAPPINGS_FILES"/*.json; do
34+
if [ -f "$mapping_file" ]; then
35+
python3 transform_mapping_body.py "$mapping_file" escape > /dev/null 2>&1 || {
36+
echo "⚠️ Failed to escape $(basename $mapping_file)"
37+
}
38+
fi
39+
done
40+
fi
41+
}
42+
43+
unescape_mapping_bodies() {
44+
echo "🔄 Converting mapping bodies back to unescaped format (readable)..."
45+
local MAPPINGS_FILES="${MAPPINGS_DIR}/mappings"
46+
47+
if [ -d "$MAPPINGS_FILES" ] && [ "$(ls -A $MAPPINGS_FILES/*.json 2>/dev/null)" ]; then
48+
for mapping_file in "$MAPPINGS_FILES"/*.json; do
49+
if [ -f "$mapping_file" ]; then
50+
python3 transform_mapping_body.py "$mapping_file" unescape > /dev/null 2>&1 || {
51+
echo "⚠️ Failed to unescape $(basename $mapping_file)"
52+
}
53+
fi
54+
done
55+
fi
56+
}
57+
58+
start_wiremock_java() {
59+
echo "🚀 Starting WireMock as Java process..."
60+
61+
# Stop any existing WireMock
62+
stop_wiremock_java
63+
64+
# Check if JAR exists
65+
if [ ! -f "$WIREMOCK_JAR" ]; then
66+
echo "❌ WireMock JAR not found at: $WIREMOCK_JAR"
67+
exit 1
68+
fi
69+
70+
# Convert MAPPINGS_DIR to absolute path
71+
local ABS_MAPPINGS_DIR="$(cd "${MAPPINGS_DIR}" && pwd)"
72+
73+
# Start WireMock in background
74+
# Use sudo if HTTPS_PORT is privileged (< 1024)
75+
if [ "$HTTPS_PORT" -lt 1024 ]; then
76+
echo "ℹ️ Using sudo to bind to privileged port ${HTTPS_PORT}"
77+
sudo java -jar "$WIREMOCK_JAR" \
78+
--port ${HTTP_PORT} \
79+
--https-port ${HTTPS_PORT} \
80+
--root-dir "${ABS_MAPPINGS_DIR}" \
81+
--verbose \
82+
> "$WIREMOCK_LOG_FILE" 2>&1 &
83+
else
84+
java -jar "$WIREMOCK_JAR" \
85+
--port ${HTTP_PORT} \
86+
--https-port ${HTTPS_PORT} \
87+
--root-dir "${ABS_MAPPINGS_DIR}" \
88+
--verbose \
89+
> "$WIREMOCK_LOG_FILE" 2>&1 &
90+
fi
91+
92+
echo $! > "$WIREMOCK_PID_FILE"
93+
echo "✅ WireMock started with PID: $(cat $WIREMOCK_PID_FILE)"
94+
}
95+
96+
stop_wiremock_java() {
97+
if [ -f "$WIREMOCK_PID_FILE" ]; then
98+
local pid=$(cat "$WIREMOCK_PID_FILE")
99+
if kill -0 "$pid" 2>/dev/null; then
100+
echo "🛑 Stopping WireMock (PID: $pid)..."
101+
kill "$pid" 2>/dev/null || sudo kill "$pid" 2>/dev/null || true
102+
sleep 2
103+
kill -9 "$pid" 2>/dev/null || sudo kill -9 "$pid" 2>/dev/null || true
104+
fi
105+
rm -f "$WIREMOCK_PID_FILE"
106+
fi
107+
# Also try to kill any remaining WireMock processes (may need sudo if started with sudo)
108+
pkill -f "wiremock" 2>/dev/null || true
109+
sudo pkill -f "wiremock" 2>/dev/null || true
110+
}
111+
112+
wait_for_wiremock_java() {
113+
echo "⏳ Waiting for WireMock to start..."
114+
local MAX_RETRIES=30
115+
local RETRY_COUNT=0
116+
117+
while [ $RETRY_COUNT -lt $MAX_RETRIES ]; do
118+
# Check HTTP admin endpoint
119+
if curl -s -o /dev/null -w "%{http_code}" http://localhost:${HTTP_PORT}/__admin/mappings 2>/dev/null | grep -q "200"; then
120+
# Also verify HTTPS is working (skip certificate validation with -k)
121+
if curl -k -s -o /dev/null -w "%{http_code}" https://localhost:${HTTPS_PORT}/__admin/mappings 2>/dev/null | grep -q "200"; then
122+
echo "✅ WireMock is ready! (HTTP: ${HTTP_PORT}, HTTPS: ${HTTPS_PORT})"
123+
return 0
124+
fi
125+
fi
126+
RETRY_COUNT=$((RETRY_COUNT + 1))
127+
echo "Waiting... ($RETRY_COUNT/$MAX_RETRIES)"
128+
sleep 1
129+
done
130+
131+
echo "❌ WireMock failed to start within ${MAX_RETRIES} seconds"
132+
echo "📋 WireMock logs:"
133+
cat "$WIREMOCK_LOG_FILE" || true
134+
exit 1
135+
}
136+
137+
show_wiremock_logs_java() {
138+
echo ""
139+
echo "📋 WireMock logs:"
140+
echo "════════════════════════════════════════════════════════════════"
141+
cat "$WIREMOCK_LOG_FILE" 2>/dev/null || echo "❌ Could not retrieve logs"
142+
echo "════════════════════════════════════════════════════════════════"
143+
echo ""
144+
}
145+
146+
verify_wiremock_results() {
147+
echo ""
148+
echo "🔍 Verifying WireMock results..."
149+
echo ""
150+
151+
local WIREMOCK_PORT=${HTTP_PORT}
152+
153+
# Count all requests
154+
local TOTAL=$(curl -s http://localhost:${WIREMOCK_PORT}/__admin/requests | jq '.requests | length')
155+
local UNMATCHED=$(curl -s http://localhost:${WIREMOCK_PORT}/__admin/requests/unmatched | jq '.requests | length')
156+
local MATCHED=$((TOTAL - UNMATCHED))
157+
158+
echo "📊 WireMock summary:"
159+
echo "──────────────────────────────"
160+
echo " Total requests: $TOTAL"
161+
echo " Matched requests: $MATCHED"
162+
echo " Unmatched requests: $UNMATCHED"
163+
echo "──────────────────────────────"
164+
echo ""
165+
166+
# Check for unmatched requests
167+
if [ "$UNMATCHED" -gt 0 ]; then
168+
echo "❌ Found requests that did not match any mappings:"
169+
curl -s http://localhost:${WIREMOCK_PORT}/__admin/requests/unmatched | \
170+
jq -r '.requests[] | " [\(.method)] \(.url)"'
171+
echo ""
172+
show_wiremock_logs_java
173+
stop_wiremock_java
174+
exit 1
175+
else
176+
echo "✅ All incoming requests matched their mappings."
177+
fi
178+
179+
# Check for unused mappings
180+
echo ""
181+
echo "🧩 Checking: were all mappings invoked..."
182+
183+
local EXPECTED_MAPPINGS=$(jq -r 'select(.response.proxyBaseUrl == null) | "\(.request.method // "ANY") \(.request.url // .request.urlPattern // .request.urlPath // .request.urlPathPattern)"' ${MAPPINGS_DIR}/mappings/*.json 2>/dev/null | sort)
184+
185+
local ACTUAL_REQUESTS=$(curl -s http://localhost:${WIREMOCK_PORT}/__admin/requests | \
186+
jq -r '.requests[] | "\(.request.method) \(.request.url)"' | sort | uniq)
187+
188+
local UNUSED_FOUND=false
189+
while IFS= read -r mapping; do
190+
if [ -n "$mapping" ]; then
191+
local method=$(echo "$mapping" | awk '{print $1}')
192+
local url=$(echo "$mapping" | awk '{$1=""; print $0}' | sed 's/^ //')
193+
194+
local matched=false
195+
196+
if echo "$url" | grep -q '\[' || echo "$url" | grep -q '\\'; then
197+
local url_start=$(echo "$url" | cut -d'[' -f1 | cut -d'\' -f1)
198+
if echo "$ACTUAL_REQUESTS" | grep -Fq "$method $url_start"; then
199+
matched=true
200+
fi
201+
else
202+
if echo "$ACTUAL_REQUESTS" | grep -Fq "$mapping"; then
203+
matched=true
204+
fi
205+
fi
206+
207+
if [ "$matched" = false ]; then
208+
if [ "$UNUSED_FOUND" = false ]; then
209+
echo "⚠️ Some mappings were not invoked by the application:"
210+
UNUSED_FOUND=true
211+
fi
212+
echo " $mapping"
213+
fi
214+
fi
215+
done <<< "$EXPECTED_MAPPINGS"
216+
217+
if [ "$UNUSED_FOUND" = false ]; then
218+
echo "✅ All recorded mappings were invoked by the application."
219+
fi
220+
221+
echo ""
222+
echo "🎉 Verification completed successfully!"
223+
}
224+
225+
# Cleanup function
226+
cleanup() {
227+
unescape_mapping_bodies
228+
stop_wiremock_java
229+
}
230+
231+
# Error handler
232+
error_handler() {
233+
local exit_code=$?
234+
echo ""
235+
echo "❌ Script failed with exit code: $exit_code"
236+
show_wiremock_logs_java
237+
unescape_mapping_bodies
238+
stop_wiremock_java
239+
exit $exit_code
240+
}
241+
242+
# Trap to ensure cleanup on exit or error
243+
trap cleanup EXIT INT TERM
244+
trap error_handler ERR
245+
246+
# === Main execution flow ===
247+
build_application
248+
find_app_path
249+
reset_simulators
250+
find_available_device
251+
find_device
252+
escape_mapping_bodies
253+
start_wiremock_java
254+
wait_for_wiremock_java
255+
echo "📝 WireMock is running in verification mode"
256+
echo "🔗 Admin UI: http://localhost:${HTTP_PORT}/__admin"
257+
echo "🔗 HTTPS Endpoint: https://localhost:${HTTPS_PORT}"
258+
echo ""
259+
start_simulator
260+
install_application
261+
launch_application
262+
wait_for_app_completion
263+
verify_wiremock_results
264+
unescape_mapping_bodies
265+
stop_wiremock_java
266+

0 commit comments

Comments
 (0)