#!/bin/sh set -eu SCRIPT_DIR=$(CDPATH= cd -- "$(dirname "$0")" && pwd) REPO_ROOT=$(CDPATH= cd -- "$SCRIPT_DIR/.." && pwd) ENV_SCRIPT="$REPO_ROOT/root/usr/bin/openclaw-env" . "$REPO_ROOT/root/usr/libexec/openclaw-node.sh" fail() { echo "FAIL: $1" >&2 exit 1 } # 1. 基础版本归一化与版本比较契约 [ "$(oc_normalize_node_version v22.23.2)" = "22.23.2" ] || fail "normalize v" oc_node_version_ge 22.22.3 22.22.3 || fail "exact version" oc_node_version_ge 22.22.4 22.22.3 || fail "patch version" oc_node_version_ge 22.23.2 22.22.3 || fail "supported LTS version" if oc_node_version_ge 22.22.2 22.22.3; then fail "older patch accepted" fi if oc_node_version_ge 22.19.0 22.22.3; then fail "older minor accepted" fi # 2. 隔离 Mock 环境: 验证全部候选镜像源失败时的失败闭合行为 TMP_DIR=$(mktemp -d 2>/dev/null || mktemp -d -t 'oc-node-test-XXXXXX' 2>/dev/null || echo "/tmp/oc-node-test-$$") mkdir -p "$TMP_DIR" cleanup() { rm -rf "$TMP_DIR" 2>/dev/null || true } trap cleanup EXIT INT TERM MOCK_BIN="$TMP_DIR/bin" mkdir -p "$MOCK_BIN" CURL_LOG="$TMP_DIR/curl.log" cat > "$MOCK_BIN/curl" <> "$CURL_LOG" exit 22 EOF chmod +x "$MOCK_BIN/curl" WGET_LOG="$TMP_DIR/wget.log" cat > "$MOCK_BIN/wget" <> "$WGET_LOG" exit 1 EOF chmod +x "$MOCK_BIN/wget" cat > "$MOCK_BIN/id" < "$MOCK_BIN/npm" <> "$NPM_LOG" exit 0 EOF chmod +x "$MOCK_BIN/npm" MOCK_ROOT="$TMP_DIR/opt" MOCK_OC="$MOCK_ROOT/openclaw" MOCK_GLOBAL="$MOCK_OC/global" MOCK_NODE="$MOCK_OC/node" mkdir -p "$MOCK_GLOBAL/bin" "$MOCK_GLOBAL/lib/node_modules/openclaw" "$MOCK_NODE/bin" echo "EXISTING_MARKER_GLOBAL" > "$MOCK_GLOBAL/marker.txt" echo "EXISTING_WRAPPER" > "$MOCK_GLOBAL/bin/openclaw" chmod +x "$MOCK_GLOBAL/bin/openclaw" echo "EXISTING_ENTRY" > "$MOCK_GLOBAL/lib/node_modules/openclaw/openclaw.mjs" echo '{"name":"openclaw","version":"2026.9.1"}' > "$MOCK_GLOBAL/lib/node_modules/openclaw/package.json" # (2.1) 提取 download_node 逻辑并在隔离环境直接调用: 候选镜像全部失败必须退出非零 HARNESS_DOWNLOADER="$TMP_DIR/test-download.sh" cat > "$HARNESS_DOWNLOADER" <> "$HARNESS_DOWNLOADER" awk '/^download_node\(\)/,/^install_pnpm\(\)/' "$ENV_SCRIPT" | sed '$d' >> "$HARNESS_DOWNLOADER" cat >> "$HARNESS_DOWNLOADER" </dev/null 2>&1 RC_EXHAUSTED=$? set -e [ "$RC_EXHAUSTED" -ne 0 ] || fail "downloader must fail when all candidate mirrors fail" # (2.2) 验证仅有 wget 可用且全部候选失败时同样必须退出非零 MOCK_BIN_WGET="$TMP_DIR/bin-wget" mkdir -p "$MOCK_BIN_WGET" cp "$MOCK_BIN/wget" "$MOCK_BIN_WGET/wget" HARNESS_WGET="$TMP_DIR/test-download-wget.sh" cat > "$HARNESS_WGET" <> "$HARNESS_WGET" awk '/^download_node\(\)/,/^install_pnpm\(\)/' "$ENV_SCRIPT" | sed '$d' >> "$HARNESS_WGET" cat >> "$HARNESS_WGET" </dev/null 2>&1 RC_WGET_EXHAUSTED=$? set -e [ "$RC_WGET_EXHAUSTED" -ne 0 ] || fail "downloader must fail when all candidate mirrors fail (wget only)" # (2.3) 验证 openclaw-env setup 调用链:Node 下载失败立即退出,不产生伪成功,且 OC_GLOBAL 零副作用 rm -f "$MOCK_NODE/bin/node" 2>/dev/null || true set +e OUT_SETUP=$( PATH="$MOCK_BIN:$PATH" \ OC_INSTALL_PATH="$MOCK_ROOT" \ NODE_VERSION="22.23.2" \ sh "$ENV_SCRIPT" setup 2>&1 ) RC_SETUP=$? set -e if [ "$RC_SETUP" -eq 0 ]; then echo "UNEXPECTED SUCCESS of openclaw-env setup (RC=0)! Output was:" >&2 echo "$OUT_SETUP" >&2 fail "openclaw-env setup must fail non-zero when candidate mirrors fail" fi # 检查后续安装未发生且原有安装保持完整 [ ! -f "$NPM_LOG" ] || fail "npm install/pack was called after node download failure" [ -f "$MOCK_GLOBAL/marker.txt" ] || fail "existing OC_GLOBAL marker was deleted on download failure" [ "$(cat "$MOCK_GLOBAL/marker.txt")" = "EXISTING_MARKER_GLOBAL" ] || fail "existing OC_GLOBAL marker corrupted" [ -f "$MOCK_GLOBAL/bin/openclaw" ] || fail "existing OC_GLOBAL wrapper was lost" [ "$(cat "$MOCK_GLOBAL/bin/openclaw")" = "EXISTING_WRAPPER" ] || fail "existing OC_GLOBAL wrapper corrupted" [ -f "$MOCK_GLOBAL/lib/node_modules/openclaw/openclaw.mjs" ] || fail "existing OC_GLOBAL entry was lost" # (2.4) 验证 openclaw-env node 调用链:旧 Node 版本需要升级但镜像全挂时必须退出非零且保留旧安装 cat > "$MOCK_NODE/bin/node" <<'EOF' #!/bin/sh case "$1" in --version|-v) echo "v22.15.1" ;; *) exit 0 ;; esac EOF chmod +x "$MOCK_NODE/bin/node" echo "EXISTING_NODE_MARKER" > "$MOCK_NODE/marker.txt" set +e PATH="$MOCK_BIN:$PATH" \ OC_INSTALL_PATH="$MOCK_ROOT" \ NODE_VERSION="22.23.2" \ sh "$ENV_SCRIPT" node >/dev/null 2>&1 RC_NODE=$? set -e [ "$RC_NODE" -ne 0 ] || fail "openclaw-env node must fail non-zero when candidate mirrors fail" # 检查旧 node 标记及旧 OC_GLOBAL 均未被破坏 [ -f "$MOCK_NODE/marker.txt" ] || fail "existing NODE_BASE marker was lost on node download failure" [ -f "$MOCK_GLOBAL/marker.txt" ] || fail "existing OC_GLOBAL marker was deleted on node download failure" [ "$(cat "$MOCK_GLOBAL/marker.txt")" = "EXISTING_MARKER_GLOBAL" ] || fail "existing OC_GLOBAL was modified on node download failure" echo "ok"