#!/bin/bash set -uo pipefail PROJECT_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd -P)" SCRIPT="$PROJECT_ROOT/run.command" PASS=0 FAIL=0 run_test() { local name="$1" shift if ("$@"); then printf 'PASS %s\n' "$name" PASS=$((PASS + 1)) else printf 'FAIL %s\n' "$name" FAIL=$((FAIL + 1)) fi } test_resolves_root_when_sourced_elsewhere() ( [ -r "$SCRIPT" ] || return 1 cd /tmp || return 1 source "$SCRIPT" [ "$ROOT_DIR" = "$PROJECT_ROOT" ] [ -z "$BACKEND_PID" ] [ -z "$NGROK_PID" ] ) test_reuses_healthy_backend() ( source "$SCRIPT" declare -F ensure_backend >/dev/null || return 1 backend_is_healthy() { return 0; } port_listener_pid() { printf '4242\n'; } start_backend() { return 77; } ensure_backend [ -z "$BACKEND_PID" ] ) test_rejects_unhealthy_port_without_killing_owner() ( source "$SCRIPT" declare -F ensure_backend >/dev/null || return 1 sleep 30 & local external_pid=$! trap "kill '$external_pid' 2>/dev/null || true" EXIT backend_is_healthy() { return 1; } port_listener_pid() { printf '%s\n' "$external_pid"; } if ensure_backend; then return 1; fi kill -0 "$external_pid" 2>/dev/null || return 1 kill "$external_pid" 2>/dev/null || true wait "$external_pid" 2>/dev/null || true trap - EXIT ) test_cleanup_stops_owned_pid_only() ( source "$SCRIPT" declare -F cleanup >/dev/null || return 1 local ready="${TMPDIR:-/tmp}/run-command-owned-ready-$$" rm -f "$ready" python3 -c 'import signal,sys,time; signal.signal(signal.SIGTERM, lambda *_: sys.exit(0)); open(sys.argv[1], "w").close(); time.sleep(30)' "$ready" & BACKEND_PID=$! local attempt=0 while [ ! -e "$ready" ] && [ "$attempt" -lt 50 ]; do sleep 0.02 attempt=$((attempt + 1)) done [ -e "$ready" ] || return 1 sleep 30 & local external_pid=$! cleanup if kill -0 "$BACKEND_PID" 2>/dev/null; then return 1; fi kill -0 "$external_pid" 2>/dev/null || return 1 kill "$external_pid" 2>/dev/null || true wait "$external_pid" 2>/dev/null || true rm -f "$ready" ) test_reuses_healthy_ngrok() ( source "$SCRIPT" declare -F ensure_ngrok >/dev/null || return 1 ngrok_is_healthy() { return 0; } port_listener_pid() { printf '5252\n'; } start_ngrok() { return 78; } ensure_ngrok [ -z "$NGROK_PID" ] ) test_no_open_mode_skips_browser() ( source "$SCRIPT" declare -F maybe_open_browser >/dev/null || return 1 local marker="${TMPDIR:-/tmp}/run-command-open-$$" rm -f "$marker" open() { : > "$marker"; } ERP_RUN_NO_OPEN=1 maybe_open_browser [ ! -e "$marker" ] ) test_main_runs_steps_in_order() ( source "$SCRIPT" local events="${TMPDIR:-/tmp}/run-command-events-$$" : > "$events" preflight() { printf 'preflight\n' >> "$events"; } ensure_backend() { printf 'backend\n' >> "$events"; } ensure_ngrok() { printf 'ngrok\n' >> "$events"; } wait_for_public() { printf 'public\n' >> "$events"; } maybe_open_browser() { printf 'open\n' >> "$events"; } monitor_services() { printf 'monitor\n' >> "$events"; } main [ "$(tr '\n' ' ' < "$events")" = 'preflight backend ngrok public open monitor ' ] ) test_postgres_profile_is_forwarded_to_backend() ( local tmp="${TMPDIR:-/tmp}/run-command-postgres-$$" mkdir -p "$tmp/backend" "$tmp/log" trap 'rm -rf "$tmp"' EXIT : > "$tmp/backend/app.jar" printf '%s\n' \ '#!/bin/bash' \ 'if [ "${1:-}" = "-version" ]; then printf '\''openjdk version "17.0.12"\n'\'' >&2; exit 0; fi' \ 'printf '\''%s\n'\'' "$@" > "$MOCK_JAVA_ARGS"' > "$tmp/java" chmod +x "$tmp/java" export MOCK_JAVA_ARGS="$tmp/args" ERP_RUN_BACKEND_DIR="$tmp/backend" ERP_RUN_JAVA_BIN="$tmp/java" \ ERP_RUN_JAR_PATH="$tmp/backend/app.jar" ERP_RUN_LOG_DIR="$tmp/log" \ ERP_RUN_PROFILE=postgres source "$SCRIPT" start_backend wait "$BACKEND_PID" grep -Fqx -- '--spring.profiles.active=postgres' "$tmp/args" grep -Fqx -- '--server.port=8091' "$tmp/args" ) test_ngrok_uses_configured_endpoint_and_api_port() ( source "$SCRIPT" local tmp="${TMPDIR:-/tmp}/run-command-ngrok-config-$$" mkdir -p "$tmp" trap 'rm -rf "$tmp"' EXIT printf '%s\n' \ '#!/bin/bash' \ 'printf '\''%s\n'\'' "$@" > "$MOCK_NGROK_ARGS"' > "$tmp/ngrok" chmod +x "$tmp/ngrok" printf 'version: "3"\n' > "$tmp/base.yml" export MOCK_NGROK_ARGS="$tmp/args" NGROK_BIN="$tmp/ngrok" NGROK_BASE_CONFIG="$tmp/base.yml" NGROK_RUNTIME_CONFIG="$tmp/runtime.yml" NGROK_LOG="$tmp/ngrok.log" NGROK_API_PORT=4141 BACKEND_PORT=8191 PUBLIC_URL=https://example.ngrok.app start_ngrok wait "$NGROK_PID" grep -Fqx -- '--url=https://example.ngrok.app' "$tmp/args" || return 1 grep -Fqx -- '8191' "$tmp/args" || return 1 grep -Fq -- ' web_addr: "127.0.0.1:4141"' "$tmp/runtime.yml" ) run_test 'resolves project root when sourced elsewhere' test_resolves_root_when_sourced_elsewhere run_test 'reuses a healthy backend' test_reuses_healthy_backend run_test 'rejects a foreign 8091 listener without killing it' test_rejects_unhealthy_port_without_killing_owner run_test 'cleanup stops owned PID only' test_cleanup_stops_owned_pid_only run_test 'reuses a healthy ngrok tunnel' test_reuses_healthy_ngrok run_test 'no-open mode skips browser launch' test_no_open_mode_skips_browser run_test 'main orchestrates steps in order' test_main_runs_steps_in_order run_test 'PostgreSQL profile is forwarded to the backend' test_postgres_profile_is_forwarded_to_backend run_test 'ngrok uses the configured endpoint and API port' test_ngrok_uses_configured_endpoint_and_api_port printf 'RESULT pass=%s fail=%s\n' "$PASS" "$FAIL" [ "$FAIL" -eq 0 ]