stop-server.test.sh 3.1 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394
  1. #!/usr/bin/env bash
  2. # Tests for stop-server.sh PID-ownership safety.
  3. #
  4. # A stale server.pid (e.g. after a reboot, when the kernel has recycled the PID)
  5. # can point at an unrelated, live process. stop-server.sh must verify the PID is
  6. # actually our brainstorm server before signalling it.
  7. set -u
  8. SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
  9. STOP="$SCRIPT_DIR/../../skills/brainstorming/scripts/stop-server.sh"
  10. SERVER="$SCRIPT_DIR/../../skills/brainstorming/scripts/server.cjs"
  11. PASS=0; FAIL=0
  12. ok() { echo " PASS: $1"; PASS=$((PASS + 1)); }
  13. bad() { echo " FAIL: $1"; echo " $2"; FAIL=$((FAIL + 1)); }
  14. reap_job() { wait "$1" 2>/dev/null || true; }
  15. # --- Test 1: an unrelated, reused PID must NOT be killed ---
  16. SESS="$(mktemp -d)"; mkdir -p "$SESS/state"
  17. sleep 600 &
  18. UNRELATED=$!
  19. disown "$UNRELATED" 2>/dev/null || true
  20. echo "$UNRELATED" > "$SESS/state/server.pid"
  21. OUT="$("$STOP" "$SESS")"
  22. if kill -0 "$UNRELATED" 2>/dev/null; then
  23. case "$OUT" in
  24. *stale_pid*) ok "unrelated reused PID is left alone (stale_pid)" ;;
  25. *) bad "unrelated PID survived but status was not stale_pid" "$OUT" ;;
  26. esac
  27. else
  28. bad "unrelated reused PID was KILLED" "$OUT"
  29. fi
  30. kill -9 "$UNRELATED" 2>/dev/null
  31. reap_job "$UNRELATED"
  32. rm -rf "$SESS"
  33. # --- Test 2: a real brainstorm server IS stopped ---
  34. SESS="$(mktemp -d)"; mkdir -p "$SESS/content" "$SESS/state"
  35. BRAINSTORM_DIR="$SESS" BRAINSTORM_PORT=3399 node "$SERVER" > /dev/null 2>&1 &
  36. SRV=$!
  37. disown "$SRV" 2>/dev/null || true
  38. for _ in $(seq 1 40); do kill -0 "$SRV" 2>/dev/null && break; sleep 0.1; done
  39. sleep 0.4
  40. echo "$SRV" > "$SESS/state/server.pid"
  41. OUT="$("$STOP" "$SESS")"
  42. sleep 0.3
  43. if kill -0 "$SRV" 2>/dev/null; then
  44. bad "real brainstorm server still running after stop" "$OUT"
  45. kill -9 "$SRV" 2>/dev/null
  46. reap_job "$SRV"
  47. else
  48. reap_job "$SRV"
  49. case "$OUT" in
  50. *stopped*) ok "real brainstorm server is stopped" ;;
  51. *) bad "server stopped but status was not 'stopped'" "$OUT" ;;
  52. esac
  53. fi
  54. rm -rf "$SESS"
  55. # --- Test 3: no pid file ---
  56. SESS="$(mktemp -d)"; mkdir -p "$SESS/state"
  57. OUT="$("$STOP" "$SESS")"
  58. case "$OUT" in
  59. *not_running*) ok "missing pid file reports not_running" ;;
  60. *) bad "missing pid file: unexpected status" "$OUT" ;;
  61. esac
  62. rm -rf "$SESS"
  63. # --- Test 4: a `node server.cjs` impostor NOT listening on our port is spared ---
  64. if command -v lsof > /dev/null 2>&1; then
  65. SESS="$(mktemp -d)"; mkdir -p "$SESS/state"
  66. echo '{"type":"server-started","port":3499}' > "$SESS/state/server-info" # nothing listens on 3499
  67. ( exec -a "node server.cjs" sleep 600 ) &
  68. IMPOSTOR=$!
  69. disown "$IMPOSTOR" 2>/dev/null || true
  70. echo "$IMPOSTOR" > "$SESS/state/server.pid"
  71. OUT="$("$STOP" "$SESS")"
  72. if kill -0 "$IMPOSTOR" 2>/dev/null; then
  73. case "$OUT" in
  74. *stale_pid*) ok "a node server.cjs not listening on our port is left alone" ;;
  75. *) bad "impostor survived but status was not stale_pid" "$OUT" ;;
  76. esac
  77. else
  78. bad "killed a node server.cjs that was NOT on our recorded port" "$OUT"
  79. fi
  80. kill -9 "$IMPOSTOR" 2>/dev/null
  81. reap_job "$IMPOSTOR"
  82. rm -rf "$SESS"
  83. else
  84. echo " SKIP: lsof unavailable — port cross-check test"
  85. fi
  86. echo "--- Results: $PASS passed, $FAIL failed ---"
  87. [ "$FAIL" -eq 0 ] || exit 1