|
| 1 | +#!/bin/bash |
| 2 | +# |
| 3 | +# Licensed to the Apache Software Foundation (ASF) under one or more |
| 4 | +# contributor license agreements. See the NOTICE file distributed with |
| 5 | +# this work for additional information regarding copyright ownership. |
| 6 | +# The ASF licenses this file to You under the Apache License, Version 2.0 |
| 7 | +# (the "License"); you may not use this file except in compliance with |
| 8 | +# the License. You may obtain a copy of the License at |
| 9 | +# |
| 10 | +# http://www.apache.org/licenses/LICENSE-2.0 |
| 11 | +# |
| 12 | +# Unless required by applicable law or agreed to in writing, software |
| 13 | +# distributed under the License is distributed on an "AS IS" BASIS, |
| 14 | +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 15 | +# See the License for the specific language governing permissions and |
| 16 | +# limitations under the License. |
| 17 | +# |
| 18 | +# test-start-hugegraph-store.sh — Tests for start-hugegraph-store.sh foreground mode fix |
| 19 | +# |
| 20 | +# Baseline (unmodified code): Test 1 PASS — Tests 2, 3, 4 FAIL |
| 21 | +# After chunk 3 fix: All 4 tests PASS |
| 22 | +# |
| 23 | +# Usage: ./test-start-hugegraph-store.sh [path-to-store-dist-root] |
| 24 | + |
| 25 | +set -uo pipefail |
| 26 | + |
| 27 | +STORE_ROOT="${1:-$(pwd)}" |
| 28 | +BIN="$STORE_ROOT/bin" |
| 29 | +START_SCRIPT="$BIN/start-hugegraph-store.sh" |
| 30 | +STOP_SCRIPT="$BIN/stop-hugegraph-store.sh" |
| 31 | +PID_FILE="$BIN/pid" |
| 32 | +STORE_URL="http://localhost:8520" |
| 33 | +PD_URL="http://localhost:8620" |
| 34 | +STARTUP_WAIT=60 # seconds to wait for Store HTTP to respond |
| 35 | +SETTLE_WAIT=5 # seconds after kill before checking exit |
| 36 | +WAIT_TIMEOUT=30 # seconds for wait_script_exit timeout |
| 37 | + |
| 38 | +PASS=0 |
| 39 | +FAIL=0 |
| 40 | +ERRORS=() |
| 41 | + |
| 42 | +GREEN='\033[0;32m' |
| 43 | +RED='\033[0;31m' |
| 44 | +YELLOW='\033[1;33m' |
| 45 | +NC='\033[0m' |
| 46 | + |
| 47 | +pass() { echo -e "${GREEN} PASS${NC} $1"; PASS=$((PASS + 1)); } |
| 48 | +fail() { echo -e "${RED} FAIL${NC} $1"; ERRORS+=("$1"); FAIL=$((FAIL + 1)); } |
| 49 | +info() { echo -e "${YELLOW} ....${NC} $1"; } |
| 50 | +section() { echo ""; echo "── $1 ──"; } |
| 51 | + |
| 52 | +cleanup() { |
| 53 | + info "Cleaning up..." |
| 54 | + if [[ -s "$PID_FILE" ]]; then |
| 55 | + kill "$(cat "$PID_FILE")" 2>/dev/null || true |
| 56 | + fi |
| 57 | + rm -f "$PID_FILE" |
| 58 | + # kill anything holding Store ports (8520 REST, 8510 raft, 8500 gRPC) |
| 59 | + lsof -ti :8520 | xargs kill -9 2>/dev/null || true |
| 60 | + lsof -ti :8510 | xargs kill -9 2>/dev/null || true |
| 61 | + lsof -ti :8500 | xargs kill -9 2>/dev/null || true |
| 62 | + sleep 3 |
| 63 | +} |
| 64 | + |
| 65 | +wait_for_store() { |
| 66 | + local elapsed=0 |
| 67 | + while (( elapsed < STARTUP_WAIT )); do |
| 68 | + local status |
| 69 | + status=$(curl -s -o /dev/null -w "%{http_code}" \ |
| 70 | + "$STORE_URL/v1/health" 2>/dev/null || echo "000") |
| 71 | + if [[ "$status" == "200" || "$status" == "401" ]]; then |
| 72 | + return 0 |
| 73 | + fi |
| 74 | + sleep 2 |
| 75 | + elapsed=$((elapsed + 2)) |
| 76 | + done |
| 77 | + return 1 |
| 78 | +} |
| 79 | + |
| 80 | +wait_for_pid_file() { |
| 81 | + local elapsed=0 |
| 82 | + while [[ ! -s "$PID_FILE" ]] && (( elapsed < 30 )); do |
| 83 | + sleep 1 |
| 84 | + elapsed=$((elapsed + 1)) |
| 85 | + done |
| 86 | +} |
| 87 | + |
| 88 | +wait_script_exit() { |
| 89 | + local script_pid="$1" |
| 90 | + ( sleep "$WAIT_TIMEOUT" && kill "$script_pid" 2>/dev/null ) & |
| 91 | + local killer_pid=$! |
| 92 | + wait "$script_pid" 2>/dev/null |
| 93 | + local exit_code=$? |
| 94 | + kill "$killer_pid" 2>/dev/null || true |
| 95 | + wait "$killer_pid" 2>/dev/null || true |
| 96 | + return $exit_code |
| 97 | +} |
| 98 | + |
| 99 | +# ── preflight ───────────────────────────────────────────────────────────────── |
| 100 | + |
| 101 | +echo "" |
| 102 | +echo "start-hugegraph-store.sh chunk 3 test suite" |
| 103 | +echo "root: $STORE_ROOT" |
| 104 | +echo "" |
| 105 | + |
| 106 | +if [[ ! -f "$START_SCRIPT" ]]; then |
| 107 | + echo -e "${RED}ERROR:${NC} $START_SCRIPT not found." |
| 108 | + echo " Pass the Store dist root as \$1" |
| 109 | + exit 1 |
| 110 | +fi |
| 111 | + |
| 112 | +for tool in lsof curl java; do |
| 113 | + if ! command -v "$tool" >/dev/null 2>&1; then |
| 114 | + echo "SKIP: required tool '$tool' not found — skipping test suite" |
| 115 | + exit 0 |
| 116 | + fi |
| 117 | +done |
| 118 | + |
| 119 | +# Store ulimit check (safely handling "unlimited" string) |
| 120 | +LIMIT_N=$(ulimit -n) |
| 121 | +if [[ "$LIMIT_N" != "unlimited" ]]; then |
| 122 | + if (( LIMIT_N < 1024 )); then |
| 123 | + echo "SKIP: ulimit -n is $LIMIT_N — store requires >= 1024. Run: ulimit -n 1024" |
| 124 | + exit 0 |
| 125 | + fi |
| 126 | +fi |
| 127 | + |
| 128 | +# Check if PD is running, warn if not (Store depends on it for HTTP health check) |
| 129 | +PD_RUNNING=false |
| 130 | +PD_STATUS=$(curl -s -o /dev/null -w "%{http_code}" "$PD_URL/v1/health" 2>/dev/null || echo "000") |
| 131 | +if [[ "$PD_STATUS" == "200" || "$PD_STATUS" == "401" ]]; then |
| 132 | + PD_RUNNING=true |
| 133 | + info "PD is running — Store should become healthy" |
| 134 | +else |
| 135 | + info "PD is NOT running at $PD_URL — Store will start but health checks may timeout (Acceptable for this test)" |
| 136 | +fi |
| 137 | + |
| 138 | +cleanup |
| 139 | + |
| 140 | +# ── test 1: daemon mode regression ─────────────────────────────────────────── |
| 141 | + |
| 142 | +section "Test 1 — daemon mode regression" |
| 143 | + |
| 144 | +info "Starting in daemon mode (no -d flag, default behavior)..." |
| 145 | +"$START_SCRIPT" >/dev/null 2>&1 & |
| 146 | +SCRIPT_PID=$! |
| 147 | + |
| 148 | +sleep 5 |
| 149 | +if ! ps -p "$SCRIPT_PID" >/dev/null 2>&1; then |
| 150 | + pass "script exited after backgrounding Java (daemon mode)" |
| 151 | +else |
| 152 | + info "script still running after 5s (may still be starting)" |
| 153 | + wait_script_exit "$SCRIPT_PID" || true |
| 154 | +fi |
| 155 | + |
| 156 | +wait_for_pid_file |
| 157 | +DAEMON_PID=$(cat "$PID_FILE" 2>/dev/null || echo "") |
| 158 | + |
| 159 | +if [[ -n "$DAEMON_PID" ]]; then |
| 160 | + pass "bin/pid written with PID $DAEMON_PID" |
| 161 | +else |
| 162 | + fail "bin/pid is empty or missing after daemon start" |
| 163 | +fi |
| 164 | + |
| 165 | +if [[ -n "$DAEMON_PID" ]] && ps -p "$DAEMON_PID" >/dev/null 2>&1; then |
| 166 | + pass "Java process is running (pid $DAEMON_PID)" |
| 167 | +else |
| 168 | + fail "Java process not found for pid '$DAEMON_PID'" |
| 169 | +fi |
| 170 | + |
| 171 | +info "Waiting up to ${STARTUP_WAIT}s for Store health endpoint..." |
| 172 | +if wait_for_store; then |
| 173 | + pass "Store health endpoint responding at $STORE_URL/v1/health" |
| 174 | +else |
| 175 | + if [[ "$PD_RUNNING" == "true" ]]; then |
| 176 | + fail "Store health endpoint not responding even though PD is up" |
| 177 | + else |
| 178 | + info "Store health endpoint not responding (Expected since PD is not running)" |
| 179 | + fi |
| 180 | +fi |
| 181 | + |
| 182 | +cleanup |
| 183 | + |
| 184 | +# ── test 2: foreground mode blocks ─────────────────────────────────────────── |
| 185 | + |
| 186 | +section "Test 2 — foreground mode blocks until Java exits" |
| 187 | + |
| 188 | +info "Starting in foreground mode (-d false)..." |
| 189 | +"$START_SCRIPT" -d false >/dev/null 2>&1 & |
| 190 | +SCRIPT_PID=$! |
| 191 | + |
| 192 | +info "Waiting up to ${STARTUP_WAIT}s for Store to come up..." |
| 193 | +if wait_for_store; then |
| 194 | + info "Store is up" |
| 195 | +else |
| 196 | + info "Store did not respond — continuing to check blocking behavior" |
| 197 | +fi |
| 198 | + |
| 199 | +if ps -p "$SCRIPT_PID" >/dev/null 2>&1; then |
| 200 | + pass "script is still running (blocking correctly)" |
| 201 | +else |
| 202 | + fail "script exited early — foreground mode is not blocking" |
| 203 | +fi |
| 204 | + |
| 205 | +wait_for_pid_file |
| 206 | +FG_PID=$(cat "$PID_FILE" 2>/dev/null || echo "") |
| 207 | + |
| 208 | +if [[ -n "$FG_PID" ]]; then |
| 209 | + info "pid file written with PID $FG_PID" |
| 210 | +else |
| 211 | + info "pid file not written (expected before chunk 3 fix)" |
| 212 | +fi |
| 213 | + |
| 214 | +if [[ -n "$FG_PID" ]]; then |
| 215 | + kill "$FG_PID" 2>/dev/null || true |
| 216 | +else |
| 217 | + kill "$SCRIPT_PID" 2>/dev/null || true |
| 218 | +fi |
| 219 | +sleep "$SETTLE_WAIT" |
| 220 | + |
| 221 | +if ! ps -p "$SCRIPT_PID" >/dev/null 2>&1; then |
| 222 | + info "script exited after Java was killed" |
| 223 | +else |
| 224 | + kill "$SCRIPT_PID" 2>/dev/null || true |
| 225 | +fi |
| 226 | + |
| 227 | +cleanup |
| 228 | + |
| 229 | +# ── test 3: exit code propagates ───────────────────────────────────────────── |
| 230 | + |
| 231 | +section "Test 3 — exit code propagates from Java" |
| 232 | + |
| 233 | +info "Starting in foreground mode (-d false)..." |
| 234 | +"$START_SCRIPT" -d false >/dev/null 2>&1 & |
| 235 | +SCRIPT_PID=$! |
| 236 | + |
| 237 | +info "Waiting up to ${STARTUP_WAIT}s for Store..." |
| 238 | +if wait_for_store; then |
| 239 | + info "Store is up" |
| 240 | +else |
| 241 | + info "Store did not respond — continuing" |
| 242 | +fi |
| 243 | + |
| 244 | +wait_for_pid_file |
| 245 | +FG_PID=$(cat "$PID_FILE" 2>/dev/null || echo "") |
| 246 | + |
| 247 | +if [[ -n "$FG_PID" ]]; then |
| 248 | + pass "bin/pid written with PID $FG_PID" |
| 249 | +else |
| 250 | + fail "bin/pid is empty or missing in foreground mode" |
| 251 | +fi |
| 252 | + |
| 253 | +if [[ -n "$FG_PID" ]] && ps -p "$FG_PID" >/dev/null 2>&1; then |
| 254 | + pass "Java process running (pid $FG_PID)" |
| 255 | +else |
| 256 | + fail "Java process not found for pid '$FG_PID'" |
| 257 | +fi |
| 258 | + |
| 259 | +if [[ -z "$FG_PID" ]]; then |
| 260 | + fail "could not get PID — skipping exit code check" |
| 261 | + kill "$SCRIPT_PID" 2>/dev/null || true |
| 262 | +else |
| 263 | + info "Hard-killing Java with SIGKILL (pid $FG_PID)..." |
| 264 | + kill -9 "$FG_PID" 2>/dev/null || true |
| 265 | + |
| 266 | + wait_script_exit "$SCRIPT_PID" |
| 267 | + ACTUAL_EXIT=$? |
| 268 | + |
| 269 | + if [[ $ACTUAL_EXIT -ne 0 ]]; then |
| 270 | + pass "script exited non-zero ($ACTUAL_EXIT) after SIGKILL" |
| 271 | + else |
| 272 | + fail "script exited 0 after SIGKILL — Docker would NOT restart" |
| 273 | + fi |
| 274 | + |
| 275 | + if [[ $ACTUAL_EXIT -eq 137 ]]; then |
| 276 | + pass "exit code is 137 (128+9 for SIGKILL) — correctly propagated" |
| 277 | + elif [[ $ACTUAL_EXIT -ne 0 ]]; then |
| 278 | + info "exit code was $ACTUAL_EXIT (not 137 — acceptable if non-zero)" |
| 279 | + fi |
| 280 | +fi |
| 281 | + |
| 282 | +cleanup |
| 283 | + |
| 284 | +# ── test 4: SIGTERM forwarded to Java ──────────────────────────────────────── |
| 285 | + |
| 286 | +section "Test 4 — SIGTERM forwarded to Java in foreground mode" |
| 287 | + |
| 288 | +info "Starting in foreground mode (-d false)..." |
| 289 | +"$START_SCRIPT" -d false >/dev/null 2>&1 & |
| 290 | +SCRIPT_PID=$! |
| 291 | + |
| 292 | +info "Waiting up to ${STARTUP_WAIT}s for Store..." |
| 293 | +if wait_for_store; then |
| 294 | + info "Store is up" |
| 295 | +else |
| 296 | + info "Store did not respond — continuing" |
| 297 | +fi |
| 298 | + |
| 299 | +wait_for_pid_file |
| 300 | +FG_PID=$(cat "$PID_FILE" 2>/dev/null || echo "") |
| 301 | + |
| 302 | +if [[ -z "$FG_PID" ]]; then |
| 303 | + fail "could not get Java PID — skipping SIGTERM check" |
| 304 | + kill "$SCRIPT_PID" 2>/dev/null || true |
| 305 | +else |
| 306 | + info "Sending SIGTERM to process (pid $SCRIPT_PID)..." |
| 307 | + kill -TERM "$SCRIPT_PID" 2>/dev/null || true |
| 308 | + |
| 309 | + wait_script_exit "$SCRIPT_PID" |
| 310 | + ACTUAL_EXIT=$? |
| 311 | + |
| 312 | + if ! ps -p "$FG_PID" >/dev/null 2>&1; then |
| 313 | + pass "Java process terminated after SIGTERM" |
| 314 | + else |
| 315 | + fail "Java process still running after SIGTERM — signal not delivered" |
| 316 | + kill "$FG_PID" 2>/dev/null || true |
| 317 | + fi |
| 318 | + |
| 319 | + if [[ $ACTUAL_EXIT -ne 0 ]]; then |
| 320 | + pass "process exited non-zero ($ACTUAL_EXIT) after SIGTERM" |
| 321 | + else |
| 322 | + fail "process exited 0 after SIGTERM — unexpected" |
| 323 | + fi |
| 324 | + |
| 325 | + if [[ $ACTUAL_EXIT -eq 143 ]]; then |
| 326 | + pass "exit code is 143 (128+15 for SIGTERM) — correctly propagated" |
| 327 | + elif [[ $ACTUAL_EXIT -ne 0 ]]; then |
| 328 | + info "exit code was $ACTUAL_EXIT (not 143 — acceptable if non-zero)" |
| 329 | + fi |
| 330 | +fi |
| 331 | + |
| 332 | +cleanup |
| 333 | + |
| 334 | +# ── summary ─────────────────────────────────────────────────────────────────── |
| 335 | + |
| 336 | +echo "" |
| 337 | +echo "════════════════════════════════" |
| 338 | +echo -e " Results: ${GREEN}$PASS passed${NC} ${RED}$FAIL failed${NC}" |
| 339 | +echo "════════════════════════════════" |
| 340 | + |
| 341 | +if [[ ${#ERRORS[@]} -gt 0 ]]; then |
| 342 | + echo "" |
| 343 | + echo "Failed tests:" |
| 344 | + for err in "${ERRORS[@]}"; do |
| 345 | + echo -e " ${RED}✗${NC} $err" |
| 346 | + done |
| 347 | +fi |
| 348 | + |
| 349 | +echo "" |
| 350 | +[[ $FAIL -eq 0 ]] && exit 0 || exit 1 |
0 commit comments