352 lines
9.7 KiB
Bash
Executable File
352 lines
9.7 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
# Windows lifecycle tests for the brainstorm server.
|
|
#
|
|
# Verifies that the brainstorm server survives the 60-second lifecycle
|
|
# check on Windows, where OWNER_PID monitoring is disabled because the
|
|
# MSYS2 PID namespace is invisible to Node.js.
|
|
#
|
|
# Requirements:
|
|
# - Node.js in PATH
|
|
# - Run from the repository root, or set SUPERPOWERS_ROOT
|
|
# - On Windows: Git Bash (OSTYPE=msys*)
|
|
#
|
|
# Usage:
|
|
# bash tests/brainstorm-server/windows-lifecycle.test.sh
|
|
set -uo pipefail
|
|
|
|
# ========== Configuration ==========
|
|
|
|
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
|
REPO_ROOT="${SUPERPOWERS_ROOT:-$(cd "$SCRIPT_DIR/../.." && pwd)}"
|
|
START_SCRIPT="$REPO_ROOT/skills/brainstorming/scripts/start-server.sh"
|
|
STOP_SCRIPT="$REPO_ROOT/skills/brainstorming/scripts/stop-server.sh"
|
|
SERVER_JS="$REPO_ROOT/skills/brainstorming/scripts/server.js"
|
|
|
|
TEST_DIR="${TMPDIR:-/tmp}/brainstorm-win-test-$$"
|
|
|
|
passed=0
|
|
failed=0
|
|
skipped=0
|
|
|
|
# ========== Helpers ==========
|
|
|
|
cleanup() {
|
|
# Kill any server processes we started
|
|
for pidvar in SERVER_PID CONTROL_PID STOP_TEST_PID; do
|
|
pid="${!pidvar:-}"
|
|
if [[ -n "$pid" ]]; then
|
|
kill "$pid" 2>/dev/null || true
|
|
wait "$pid" 2>/dev/null || true
|
|
fi
|
|
done
|
|
if [[ -n "${TEST_DIR:-}" && -d "$TEST_DIR" ]]; then
|
|
rm -rf "$TEST_DIR"
|
|
fi
|
|
}
|
|
trap cleanup EXIT
|
|
|
|
pass() {
|
|
echo " PASS: $1"
|
|
passed=$((passed + 1))
|
|
}
|
|
|
|
fail() {
|
|
echo " FAIL: $1"
|
|
echo " $2"
|
|
failed=$((failed + 1))
|
|
}
|
|
|
|
skip() {
|
|
echo " SKIP: $1 ($2)"
|
|
skipped=$((skipped + 1))
|
|
}
|
|
|
|
wait_for_server_info() {
|
|
local dir="$1"
|
|
for _ in $(seq 1 50); do
|
|
if [[ -f "$dir/.server-info" ]]; then
|
|
return 0
|
|
fi
|
|
sleep 0.1
|
|
done
|
|
return 1
|
|
}
|
|
|
|
get_port_from_info() {
|
|
# Read the port from .server-info. Use grep/sed instead of Node.js
|
|
# to avoid MSYS2-to-Windows path translation issues.
|
|
grep -o '"port":[0-9]*' "$1/.server-info" | head -1 | sed 's/"port"://'
|
|
}
|
|
|
|
http_check() {
|
|
local port="$1"
|
|
node -e "
|
|
const http = require('http');
|
|
http.get('http://localhost:$port/', (res) => {
|
|
process.exit(res.statusCode === 200 ? 0 : 1);
|
|
}).on('error', () => process.exit(1));
|
|
" 2>/dev/null
|
|
}
|
|
|
|
# ========== Platform Detection ==========
|
|
|
|
echo ""
|
|
echo "=== Brainstorm Server Windows Lifecycle Tests ==="
|
|
echo "Platform: ${OSTYPE:-unknown}"
|
|
echo "MSYSTEM: ${MSYSTEM:-unset}"
|
|
echo "Node: $(node --version 2>/dev/null || echo 'not found')"
|
|
echo ""
|
|
|
|
is_windows="false"
|
|
case "${OSTYPE:-}" in
|
|
msys*|cygwin*|mingw*) is_windows="true" ;;
|
|
esac
|
|
if [[ -n "${MSYSTEM:-}" ]]; then
|
|
is_windows="true"
|
|
fi
|
|
|
|
if [[ "$is_windows" != "true" ]]; then
|
|
echo "NOTE: Not running on Windows/MSYS2 (OSTYPE=${OSTYPE:-unset})."
|
|
echo "Windows-specific tests will be skipped. Tests 4-6 still run."
|
|
echo ""
|
|
fi
|
|
|
|
mkdir -p "$TEST_DIR"
|
|
|
|
SERVER_PID=""
|
|
CONTROL_PID=""
|
|
STOP_TEST_PID=""
|
|
|
|
# ========== Test 1: OWNER_PID is empty on Windows ==========
|
|
|
|
echo "--- Owner PID Resolution ---"
|
|
|
|
if [[ "$is_windows" == "true" ]]; then
|
|
# Replicate the PID resolution logic from start-server.sh lines 104-112
|
|
TEST_OWNER_PID="$(ps -o ppid= -p "$PPID" 2>/dev/null | tr -d ' ' || true)"
|
|
if [[ -z "$TEST_OWNER_PID" || "$TEST_OWNER_PID" == "1" ]]; then
|
|
TEST_OWNER_PID="$PPID"
|
|
fi
|
|
# The fix: clear on Windows
|
|
case "${OSTYPE:-}" in
|
|
msys*|cygwin*|mingw*) TEST_OWNER_PID="" ;;
|
|
esac
|
|
|
|
if [[ -z "$TEST_OWNER_PID" ]]; then
|
|
pass "OWNER_PID is empty on Windows after fix"
|
|
else
|
|
fail "OWNER_PID is empty on Windows after fix" \
|
|
"Expected empty, got '$TEST_OWNER_PID'"
|
|
fi
|
|
else
|
|
skip "OWNER_PID is empty on Windows" "not on Windows"
|
|
fi
|
|
|
|
# ========== Test 2: start-server.sh passes empty BRAINSTORM_OWNER_PID ==========
|
|
|
|
if [[ "$is_windows" == "true" ]]; then
|
|
# Use a fake 'node' that captures the env var and exits
|
|
FAKE_NODE_DIR="$TEST_DIR/fake-bin"
|
|
mkdir -p "$FAKE_NODE_DIR"
|
|
cat > "$FAKE_NODE_DIR/node" <<'FAKENODE'
|
|
#!/usr/bin/env bash
|
|
echo "CAPTURED_OWNER_PID=${BRAINSTORM_OWNER_PID:-__UNSET__}"
|
|
exit 0
|
|
FAKENODE
|
|
chmod +x "$FAKE_NODE_DIR/node"
|
|
|
|
captured=$(PATH="$FAKE_NODE_DIR:$PATH" bash "$START_SCRIPT" --project-dir "$TEST_DIR/session" --foreground 2>/dev/null || true)
|
|
owner_pid_value=$(echo "$captured" | grep "CAPTURED_OWNER_PID=" | head -1 | sed 's/CAPTURED_OWNER_PID=//')
|
|
|
|
if [[ "$owner_pid_value" == "" || "$owner_pid_value" == "__UNSET__" ]]; then
|
|
pass "start-server.sh passes empty BRAINSTORM_OWNER_PID on Windows"
|
|
else
|
|
fail "start-server.sh passes empty BRAINSTORM_OWNER_PID on Windows" \
|
|
"Expected empty or unset, got '$owner_pid_value'"
|
|
fi
|
|
|
|
rm -rf "$FAKE_NODE_DIR" "$TEST_DIR/session"
|
|
else
|
|
skip "start-server.sh passes empty BRAINSTORM_OWNER_PID" "not on Windows"
|
|
fi
|
|
|
|
# ========== Test 3: Auto-foreground detection on Windows ==========
|
|
|
|
echo ""
|
|
echo "--- Foreground Mode Detection ---"
|
|
|
|
if [[ "$is_windows" == "true" ]]; then
|
|
FAKE_NODE_DIR="$TEST_DIR/fake-bin"
|
|
mkdir -p "$FAKE_NODE_DIR"
|
|
cat > "$FAKE_NODE_DIR/node" <<'FAKENODE'
|
|
#!/usr/bin/env bash
|
|
echo "FOREGROUND_MODE=true"
|
|
exit 0
|
|
FAKENODE
|
|
chmod +x "$FAKE_NODE_DIR/node"
|
|
|
|
# Run WITHOUT --foreground flag — Windows should auto-detect
|
|
captured=$(PATH="$FAKE_NODE_DIR:$PATH" bash "$START_SCRIPT" --project-dir "$TEST_DIR/session2" 2>/dev/null || true)
|
|
|
|
if echo "$captured" | grep -q "FOREGROUND_MODE=true"; then
|
|
pass "Windows auto-detects foreground mode"
|
|
else
|
|
fail "Windows auto-detects foreground mode" \
|
|
"Expected foreground code path, output: $captured"
|
|
fi
|
|
|
|
rm -rf "$FAKE_NODE_DIR" "$TEST_DIR/session2"
|
|
else
|
|
skip "Windows auto-detects foreground mode" "not on Windows"
|
|
fi
|
|
|
|
# ========== Test 4: Server survives past 60-second lifecycle check ==========
|
|
|
|
echo ""
|
|
echo "--- Server Survival (lifecycle check) ---"
|
|
|
|
mkdir -p "$TEST_DIR/survival"
|
|
|
|
echo " Starting server (will wait ~75s to verify survival past lifecycle check)..."
|
|
|
|
BRAINSTORM_DIR="$TEST_DIR/survival" \
|
|
BRAINSTORM_HOST="127.0.0.1" \
|
|
BRAINSTORM_URL_HOST="localhost" \
|
|
BRAINSTORM_OWNER_PID="" \
|
|
BRAINSTORM_PORT=$((49152 + RANDOM % 16383)) \
|
|
node "$SERVER_JS" > "$TEST_DIR/survival/.server.log" 2>&1 &
|
|
SERVER_PID=$!
|
|
|
|
if ! wait_for_server_info "$TEST_DIR/survival"; then
|
|
fail "Server starts successfully" "Server did not write .server-info within 5 seconds"
|
|
kill "$SERVER_PID" 2>/dev/null || true
|
|
SERVER_PID=""
|
|
else
|
|
pass "Server starts successfully with empty OWNER_PID"
|
|
|
|
SERVER_PORT=$(get_port_from_info "$TEST_DIR/survival")
|
|
|
|
sleep 75
|
|
|
|
if kill -0 "$SERVER_PID" 2>/dev/null; then
|
|
pass "Server is still alive after 75 seconds"
|
|
else
|
|
fail "Server is still alive after 75 seconds" \
|
|
"Server died. Log tail: $(tail -5 "$TEST_DIR/survival/.server.log" 2>/dev/null)"
|
|
fi
|
|
|
|
if http_check "$SERVER_PORT"; then
|
|
pass "Server responds to HTTP after lifecycle check window"
|
|
else
|
|
fail "Server responds to HTTP after lifecycle check window" \
|
|
"HTTP request to port $SERVER_PORT failed"
|
|
fi
|
|
|
|
if grep -q "owner process exited" "$TEST_DIR/survival/.server.log" 2>/dev/null; then
|
|
fail "No 'owner process exited' in logs" \
|
|
"Found spurious owner-exit shutdown in log"
|
|
else
|
|
pass "No 'owner process exited' in logs"
|
|
fi
|
|
|
|
kill "$SERVER_PID" 2>/dev/null || true
|
|
wait "$SERVER_PID" 2>/dev/null || true
|
|
SERVER_PID=""
|
|
fi
|
|
|
|
# ========== Test 5: Bad OWNER_PID causes shutdown (control) ==========
|
|
|
|
echo ""
|
|
echo "--- Control: Bad OWNER_PID causes shutdown ---"
|
|
|
|
mkdir -p "$TEST_DIR/control"
|
|
|
|
# Find a PID that does not exist
|
|
BAD_PID=99999
|
|
while kill -0 "$BAD_PID" 2>/dev/null; do
|
|
BAD_PID=$((BAD_PID + 1))
|
|
done
|
|
|
|
BRAINSTORM_DIR="$TEST_DIR/control" \
|
|
BRAINSTORM_HOST="127.0.0.1" \
|
|
BRAINSTORM_URL_HOST="localhost" \
|
|
BRAINSTORM_OWNER_PID="$BAD_PID" \
|
|
BRAINSTORM_PORT=$((49152 + RANDOM % 16383)) \
|
|
node "$SERVER_JS" > "$TEST_DIR/control/.server.log" 2>&1 &
|
|
CONTROL_PID=$!
|
|
|
|
if ! wait_for_server_info "$TEST_DIR/control"; then
|
|
fail "Control server starts" "Server did not write .server-info within 5 seconds"
|
|
kill "$CONTROL_PID" 2>/dev/null || true
|
|
CONTROL_PID=""
|
|
else
|
|
pass "Control server starts with bad OWNER_PID=$BAD_PID"
|
|
|
|
echo " Waiting ~75s for lifecycle check to kill server..."
|
|
sleep 75
|
|
|
|
if kill -0 "$CONTROL_PID" 2>/dev/null; then
|
|
fail "Control server self-terminates with bad OWNER_PID" \
|
|
"Server is still alive (expected it to die)"
|
|
kill "$CONTROL_PID" 2>/dev/null || true
|
|
else
|
|
pass "Control server self-terminates with bad OWNER_PID"
|
|
fi
|
|
|
|
if grep -q "owner process exited" "$TEST_DIR/control/.server.log" 2>/dev/null; then
|
|
pass "Control server logs 'owner process exited'"
|
|
else
|
|
fail "Control server logs 'owner process exited'" \
|
|
"Log tail: $(tail -5 "$TEST_DIR/control/.server.log" 2>/dev/null)"
|
|
fi
|
|
fi
|
|
|
|
wait "$CONTROL_PID" 2>/dev/null || true
|
|
CONTROL_PID=""
|
|
|
|
# ========== Test 6: stop-server.sh cleanly stops the server ==========
|
|
|
|
echo ""
|
|
echo "--- Clean Shutdown ---"
|
|
|
|
mkdir -p "$TEST_DIR/stop-test"
|
|
|
|
BRAINSTORM_DIR="$TEST_DIR/stop-test" \
|
|
BRAINSTORM_HOST="127.0.0.1" \
|
|
BRAINSTORM_URL_HOST="localhost" \
|
|
BRAINSTORM_OWNER_PID="" \
|
|
BRAINSTORM_PORT=$((49152 + RANDOM % 16383)) \
|
|
node "$SERVER_JS" > "$TEST_DIR/stop-test/.server.log" 2>&1 &
|
|
STOP_TEST_PID=$!
|
|
echo "$STOP_TEST_PID" > "$TEST_DIR/stop-test/.server.pid"
|
|
|
|
if ! wait_for_server_info "$TEST_DIR/stop-test"; then
|
|
fail "Stop-test server starts" "Server did not start"
|
|
kill "$STOP_TEST_PID" 2>/dev/null || true
|
|
STOP_TEST_PID=""
|
|
else
|
|
bash "$STOP_SCRIPT" "$TEST_DIR/stop-test" >/dev/null 2>&1 || true
|
|
sleep 1
|
|
|
|
if ! kill -0 "$STOP_TEST_PID" 2>/dev/null; then
|
|
pass "stop-server.sh cleanly stops the server"
|
|
else
|
|
fail "stop-server.sh cleanly stops the server" \
|
|
"Server PID $STOP_TEST_PID is still alive after stop"
|
|
kill "$STOP_TEST_PID" 2>/dev/null || true
|
|
fi
|
|
fi
|
|
|
|
wait "$STOP_TEST_PID" 2>/dev/null || true
|
|
STOP_TEST_PID=""
|
|
|
|
# ========== Summary ==========
|
|
|
|
echo ""
|
|
echo "=== Results: $passed passed, $failed failed, $skipped skipped ==="
|
|
|
|
if [[ $failed -gt 0 ]]; then
|
|
exit 1
|
|
fi
|
|
exit 0
|