From 61b116d5972ceeeab1bd09ea61cc5deab59ba925 Mon Sep 17 00:00:00 2001 From: Vincent Koc Date: Thu, 18 Jun 2026 22:14:16 +0200 Subject: [PATCH] fix(e2e): validate plugin update timeout seconds --- .../plugin-update/corrupt-update-scenario.sh | 2 +- .../lib/plugin-update/unchanged-scenario.sh | 2 +- scripts/lib/openclaw-e2e-instance.sh | 13 ++++++++ test/scripts/openclaw-e2e-instance.test.ts | 32 +++++++++++++++++++ .../plugin-update-unchanged-docker.test.ts | 12 +++++++ 5 files changed, 59 insertions(+), 2 deletions(-) diff --git a/scripts/e2e/lib/plugin-update/corrupt-update-scenario.sh b/scripts/e2e/lib/plugin-update/corrupt-update-scenario.sh index 1257caaee409..1d6bb88eeed9 100644 --- a/scripts/e2e/lib/plugin-update/corrupt-update-scenario.sh +++ b/scripts/e2e/lib/plugin-update/corrupt-update-scenario.sh @@ -18,7 +18,7 @@ export OPENCLAW_NO_ONBOARD=1 export OPENCLAW_NO_PROMPT=1 baseline="${OPENCLAW_UPDATE_CORRUPT_PLUGIN_BASELINE:-openclaw@latest}" -update_timeout_seconds="${OPENCLAW_UPDATE_CORRUPT_PLUGIN_TIMEOUT_SECONDS:-900}" +update_timeout_seconds="$(openclaw_e2e_read_positive_int_env OPENCLAW_UPDATE_CORRUPT_PLUGIN_TIMEOUT_SECONDS 900)" echo "Installing baseline OpenClaw package: $baseline" if ! openclaw_e2e_maybe_timeout "${OPENCLAW_E2E_NPM_INSTALL_TIMEOUT:-600s}" npm install -g --prefix /tmp/npm-prefix --omit=optional "$baseline" >/tmp/openclaw-update-corrupt-baseline-install.log 2>&1; then openclaw_e2e_print_log /tmp/openclaw-update-corrupt-baseline-install.log >&2 diff --git a/scripts/e2e/lib/plugin-update/unchanged-scenario.sh b/scripts/e2e/lib/plugin-update/unchanged-scenario.sh index bb613011e170..4318350482b4 100644 --- a/scripts/e2e/lib/plugin-update/unchanged-scenario.sh +++ b/scripts/e2e/lib/plugin-update/unchanged-scenario.sh @@ -31,7 +31,7 @@ before_config_hash="" if [ "$OPENCLAW_PACKAGE_ACCEPTANCE_LEGACY_COMPAT" != "1" ]; then before_config_hash="$(sha256sum "$OPENCLAW_CONFIG_PATH" | awk '{print $1}')" fi -plugin_update_timeout_seconds="${OPENCLAW_PLUGIN_UPDATE_TIMEOUT_SECONDS:-180}" +plugin_update_timeout_seconds="$(openclaw_e2e_read_positive_int_env OPENCLAW_PLUGIN_UPDATE_TIMEOUT_SECONDS 180)" node "$probe" snapshot > /tmp/plugin-update-before.json diff --git a/scripts/lib/openclaw-e2e-instance.sh b/scripts/lib/openclaw-e2e-instance.sh index efa0b4e49a78..9d426265b999 100644 --- a/scripts/lib/openclaw-e2e-instance.sh +++ b/scripts/lib/openclaw-e2e-instance.sh @@ -13,6 +13,19 @@ openclaw_e2e_eval_test_state_from_b64() { fi eval "$decoded" } +openclaw_e2e_read_positive_int_env() { + local name="${1:?missing environment variable name}" + local fallback="${2:?missing fallback value}" + local value="${!name-}" + if [ -z "${!name+x}" ]; then + value="$fallback" + fi + if [[ ! "$value" =~ ^[0-9]+$ ]] || (( 10#$value < 1 )); then + echo "invalid $name: $value" >&2 + return 2 + fi + printf '%s\n' "$value" +} openclaw_e2e_resolve_entrypoint() { local entry for entry in dist/index.mjs dist/index.js; do diff --git a/test/scripts/openclaw-e2e-instance.test.ts b/test/scripts/openclaw-e2e-instance.test.ts index 0740e017c998..149f29b27992 100644 --- a/test/scripts/openclaw-e2e-instance.test.ts +++ b/test/scripts/openclaw-e2e-instance.test.ts @@ -53,6 +53,17 @@ function shellTestEnv(overrides: Record): NodeJS.Pro return env; } +function runSourcedHelper( + script: string, + overrides: Record = {}, +): ReturnType { + return spawnSync( + "bash", + ["-lc", ["set -euo pipefail", `source ${shellQuote(helperPath)}`, script].join("; ")], + { encoding: "utf8", env: shellTestEnv(overrides) }, + ); +} + function expectShellSuccess(result: ReturnType) { expect(result.status, result.stderr || result.stdout || result.error?.message).toBe(0); } @@ -149,6 +160,27 @@ describe("scripts/lib/openclaw-e2e-instance.sh", () => { expect(result.stderr).toContain("decoded to an empty script"); }); + it("reads positive integer env values without treating decimal input as durations", () => { + const fallback = runSourcedHelper( + 'printf "%s" "$(openclaw_e2e_read_positive_int_env OPENCLAW_E2E_SAMPLE_SECONDS 180)"', + ); + const leadingZero = runSourcedHelper( + 'printf "%s" "$(openclaw_e2e_read_positive_int_env OPENCLAW_E2E_SAMPLE_SECONDS 180)"', + { OPENCLAW_E2E_SAMPLE_SECONDS: "008" }, + ); + const duration = runSourcedHelper( + "openclaw_e2e_read_positive_int_env OPENCLAW_E2E_SAMPLE_SECONDS 180", + { OPENCLAW_E2E_SAMPLE_SECONDS: "30s" }, + ); + + expectShellSuccess(fallback); + expect(fallback.stdout).toBe("180"); + expectShellSuccess(leadingZero); + expect(leadingZero.stdout).toBe("008"); + expect(duration.status).toBe(2); + expect(duration.stderr).toContain("invalid OPENCLAW_E2E_SAMPLE_SECONDS: 30s"); + }); + it("requires /readyz after the gateway ready log", () => { const tempDir = fs.mkdtempSync(path.join(os.tmpdir(), "openclaw-e2e-readyz-required-")); try { diff --git a/test/scripts/plugin-update-unchanged-docker.test.ts b/test/scripts/plugin-update-unchanged-docker.test.ts index d0f7173ac39e..0a916d08fc90 100644 --- a/test/scripts/plugin-update-unchanged-docker.test.ts +++ b/test/scripts/plugin-update-unchanged-docker.test.ts @@ -78,9 +78,15 @@ describe("plugin update unchanged Docker E2E", () => { const script = readFileSync(PLUGIN_UPDATE_SCENARIO_SCRIPT, "utf8"); expect(script).toContain("OPENCLAW_PLUGIN_UPDATE_TIMEOUT_SECONDS"); + expect(script).toContain( + "openclaw_e2e_read_positive_int_env OPENCLAW_PLUGIN_UPDATE_TIMEOUT_SECONDS 180", + ); expect(script).toContain( 'openclaw_e2e_maybe_timeout "${plugin_update_timeout_seconds}s" node "$entry" plugins update', ); + expect(script).not.toContain( + 'plugin_update_timeout_seconds="${OPENCLAW_PLUGIN_UPDATE_TIMEOUT_SECONDS:-180}"', + ); expect(script).not.toMatch( /^\s*timeout "\$\{plugin_update_timeout_seconds\}s" node "\$entry"/mu, ); @@ -149,6 +155,12 @@ describe("plugin update unchanged Docker E2E", () => { const script = readFileSync(CORRUPT_UPDATE_SCENARIO_SCRIPT, "utf8"); expect(script).toContain("OPENCLAW_UPDATE_CORRUPT_PLUGIN_TIMEOUT_SECONDS"); + expect(script).toContain( + "openclaw_e2e_read_positive_int_env OPENCLAW_UPDATE_CORRUPT_PLUGIN_TIMEOUT_SECONDS 900", + ); + expect(script).not.toContain( + 'update_timeout_seconds="${OPENCLAW_UPDATE_CORRUPT_PLUGIN_TIMEOUT_SECONDS:-900}"', + ); expect( script.match(/openclaw_e2e_maybe_timeout "\$\{update_timeout_seconds\}s" \\/gu)?.length, ).toBe(2);