diff --git a/scripts/docker/install-sh-e2e/run.sh b/scripts/docker/install-sh-e2e/run.sh index f8a7b72bcfe8..434361d45abb 100755 --- a/scripts/docker/install-sh-e2e/run.sh +++ b/scripts/docker/install-sh-e2e/run.sh @@ -15,6 +15,38 @@ fi # shellcheck source=../install-sh-common/version-parse.sh source "$VERIFY_HELPER_PATH" +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" "$((10#$value))" +} + +read_boolean_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 + case "$value" in + 0 | 1) + printf "%s\n" "$value" + ;; + *) + echo "invalid $name: $value" >&2 + return 2 + ;; + esac +} + INSTALL_URL="${OPENCLAW_INSTALL_URL:-https://openclaw.bot/install.sh}" MODELS_MODE="${OPENCLAW_E2E_MODELS:-both}" # both|openai|anthropic INSTALL_TAG="${OPENCLAW_INSTALL_TAG:-latest}" @@ -23,11 +55,11 @@ SKIP_PREVIOUS="${OPENCLAW_INSTALL_E2E_SKIP_PREVIOUS:-0}" OPENAI_API_KEY="${OPENAI_API_KEY:-}" ANTHROPIC_API_KEY="${ANTHROPIC_API_KEY:-}" ANTHROPIC_API_TOKEN="${ANTHROPIC_API_TOKEN:-}" -AGENT_TURN_TIMEOUT_SECONDS="${OPENCLAW_INSTALL_E2E_AGENT_TURN_TIMEOUT_SECONDS:-300}" -AGENT_TURNS_PARALLEL="${OPENCLAW_INSTALL_E2E_AGENT_TURNS_PARALLEL:-1}" -AGENT_TOOL_SMOKE="${OPENCLAW_INSTALL_E2E_AGENT_TOOL_SMOKE:-1}" +AGENT_TURN_TIMEOUT_SECONDS="$(read_positive_int_env OPENCLAW_INSTALL_E2E_AGENT_TURN_TIMEOUT_SECONDS 300)" +AGENT_TURNS_PARALLEL="$(read_boolean_env OPENCLAW_INSTALL_E2E_AGENT_TURNS_PARALLEL 1)" +AGENT_TOOL_SMOKE="$(read_boolean_env OPENCLAW_INSTALL_E2E_AGENT_TOOL_SMOKE 1)" OPENAI_AGENT_MODEL="${OPENCLAW_INSTALL_E2E_OPENAI_MODEL:-openai/gpt-5.5}" -OPENAI_PROVIDER_TIMEOUT_SECONDS="${OPENCLAW_INSTALL_E2E_OPENAI_PROVIDER_TIMEOUT_SECONDS:-${AGENT_TURN_TIMEOUT_SECONDS}}" +OPENAI_PROVIDER_TIMEOUT_SECONDS="$(read_positive_int_env OPENCLAW_INSTALL_E2E_OPENAI_PROVIDER_TIMEOUT_SECONDS "$AGENT_TURN_TIMEOUT_SECONDS")" time_phase() { local name="$1" diff --git a/test/scripts/test-install-sh-docker.test.ts b/test/scripts/test-install-sh-docker.test.ts index 4a6265434b2c..ca79aaad0f2c 100644 --- a/test/scripts/test-install-sh-docker.test.ts +++ b/test/scripts/test-install-sh-docker.test.ts @@ -5,6 +5,7 @@ import { runInNewContext } from "node:vm"; import { describe, expect, it } from "vitest"; const SCRIPT_PATH = "scripts/test-install-sh-docker.sh"; +const INSTALL_E2E_RUNNER_PATH = "scripts/docker/install-sh-e2e/run.sh"; const DOCKER_SETUP_PATH = "scripts/docker/setup.sh"; const PODMAN_SETUP_PATH = "scripts/podman/setup.sh"; const PODMAN_RUN_PATH = "scripts/run-openclaw-podman.sh"; @@ -468,6 +469,46 @@ describe("test-install-sh-docker", () => { }); }); +describe("install-sh E2E runner", () => { + it("validates agent timing and toggle knobs before running provider setup", () => { + const script = readFileSync(INSTALL_E2E_RUNNER_PATH, "utf8"); + + expect(script).toContain( + 'AGENT_TURN_TIMEOUT_SECONDS="$(read_positive_int_env OPENCLAW_INSTALL_E2E_AGENT_TURN_TIMEOUT_SECONDS 300)"', + ); + expect(script).toContain( + 'AGENT_TURNS_PARALLEL="$(read_boolean_env OPENCLAW_INSTALL_E2E_AGENT_TURNS_PARALLEL 1)"', + ); + expect(script).toContain( + 'AGENT_TOOL_SMOKE="$(read_boolean_env OPENCLAW_INSTALL_E2E_AGENT_TOOL_SMOKE 1)"', + ); + expect(script).toContain( + 'OPENAI_PROVIDER_TIMEOUT_SECONDS="$(read_positive_int_env OPENCLAW_INSTALL_E2E_OPENAI_PROVIDER_TIMEOUT_SECONDS "$AGENT_TURN_TIMEOUT_SECONDS")"', + ); + expect(script).toContain('timeout --kill-after=15s "${AGENT_TURN_TIMEOUT_SECONDS}s"'); + expect(script).toContain('\\"timeoutSeconds\\":${OPENAI_PROVIDER_TIMEOUT_SECONDS}'); + }); + + it.each([ + ["turn timeout", "OPENCLAW_INSTALL_E2E_AGENT_TURN_TIMEOUT_SECONDS", "300s"], + ["provider timeout", "OPENCLAW_INSTALL_E2E_OPENAI_PROVIDER_TIMEOUT_SECONDS", "1e3"], + ["parallel toggle", "OPENCLAW_INSTALL_E2E_AGENT_TURNS_PARALLEL", "2"], + ["tool smoke toggle", "OPENCLAW_INSTALL_E2E_AGENT_TOOL_SMOKE", "false"], + ])("rejects invalid install E2E %s before credential preflight", (_label, envName, value) => { + const result = spawnSync("bash", [INSTALL_E2E_RUNNER_PATH], { + encoding: "utf8", + env: { + ...process.env, + [envName]: value, + }, + }); + + expect(result.status).toBe(2); + expect(result.stderr).toContain(`invalid ${envName}: ${value}`); + expect(result.stderr).not.toContain("OPENCLAW_E2E_MODELS=both requires"); + }); +}); + describe("install-sh smoke runner", () => { it("wraps long npm/update operations with heartbeat and install-size audits", () => { const script = readFileSync(SMOKE_RUNNER_PATH, "utf8");