diff --git a/scripts/e2e/openwebui-docker.sh b/scripts/e2e/openwebui-docker.sh index 7389d22c4ab4..bef2c22f8fb3 100755 --- a/scripts/e2e/openwebui-docker.sh +++ b/scripts/e2e/openwebui-docker.sh @@ -24,15 +24,27 @@ NET_NAME="openclaw-openwebui-e2e-$$" GW_NAME="openclaw-openwebui-gateway-$$" OW_NAME="openclaw-openwebui-$$" PROVIDER_TIMEOUT_SECONDS="${OPENCLAW_OPENWEBUI_PROVIDER_TIMEOUT_SECONDS:-900}" -PROBE_FETCH_TIMEOUT_MS="${OPENCLAW_OPENWEBUI_FETCH_TIMEOUT_MS:-$((PROVIDER_TIMEOUT_SECONDS * 1000 + 60000))}" -DOCKER_COMMAND_TIMEOUT="${OPENCLAW_OPENWEBUI_DOCKER_COMMAND_TIMEOUT:-$((PROVIDER_TIMEOUT_SECONDS + 90))s}" DOCKER_PULL_TIMEOUT="${OPENCLAW_OPENWEBUI_DOCKER_PULL_TIMEOUT:-600s}" SMOKE_MODE="${OPENWEBUI_SMOKE_MODE:-${OPENCLAW_OPENWEBUI_SMOKE_MODE:-chat}}" +validate_positive_int() { + local label="$1" + local value="$2" + if [[ ! "$value" =~ ^[0-9]+$ ]] || (( 10#$value < 1 )); then + echo "invalid $label: $value" >&2 + exit 2 + fi +} + validate_tcp_port() { local label="$1" local value="$2" - if [[ ! "$value" =~ ^[0-9]+$ ]] || [ "$value" -lt 1 ] || [ "$value" -gt 65535 ]; then + if [[ ! "$value" =~ ^[0-9]+$ ]]; then + echo "invalid $label: $value" >&2 + exit 2 + fi + local decimal_value=$((10#$value)) + if [ "$decimal_value" -lt 1 ] || [ "$decimal_value" -gt 65535 ]; then echo "invalid $label: $value" >&2 exit 2 fi @@ -40,6 +52,11 @@ validate_tcp_port() { validate_tcp_port OPENCLAW_OPENWEBUI_GATEWAY_PORT "$PORT" validate_tcp_port OPENCLAW_OPENWEBUI_PORT "$WEBUI_PORT" +validate_positive_int OPENCLAW_OPENWEBUI_PROVIDER_TIMEOUT_SECONDS "$PROVIDER_TIMEOUT_SECONDS" +PROVIDER_TIMEOUT_SECONDS_DECIMAL=$((10#$PROVIDER_TIMEOUT_SECONDS)) +PROBE_FETCH_TIMEOUT_MS="${OPENCLAW_OPENWEBUI_FETCH_TIMEOUT_MS:-$((PROVIDER_TIMEOUT_SECONDS_DECIMAL * 1000 + 60000))}" +validate_positive_int OPENCLAW_OPENWEBUI_FETCH_TIMEOUT_MS "$PROBE_FETCH_TIMEOUT_MS" +DOCKER_COMMAND_TIMEOUT="${OPENCLAW_OPENWEBUI_DOCKER_COMMAND_TIMEOUT:-$((PROVIDER_TIMEOUT_SECONDS_DECIMAL + 90))s}" case "$SMOKE_MODE" in chat | models) ;; diff --git a/test/scripts/docker-build-helper.test.ts b/test/scripts/docker-build-helper.test.ts index 5cef5a664937..5d06ff4b4b20 100644 --- a/test/scripts/docker-build-helper.test.ts +++ b/test/scripts/docker-build-helper.test.ts @@ -2688,6 +2688,12 @@ output="$(cat "$sampler_log")" it("keeps Open WebUI Docker E2E resource-guarded", () => { const runner = readFileSync(OPENWEBUI_DOCKER_E2E_PATH, "utf8"); + expect(runner).toContain( + 'validate_positive_int OPENCLAW_OPENWEBUI_PROVIDER_TIMEOUT_SECONDS "$PROVIDER_TIMEOUT_SECONDS"', + ); + expect(runner).toContain( + 'validate_positive_int OPENCLAW_OPENWEBUI_FETCH_TIMEOUT_MS "$PROBE_FETCH_TIMEOUT_MS"', + ); expect(runner).toContain('validate_tcp_port OPENCLAW_OPENWEBUI_GATEWAY_PORT "$PORT"'); expect(runner).toContain('validate_tcp_port OPENCLAW_OPENWEBUI_PORT "$WEBUI_PORT"'); expect(runner).toContain("OPENCLAW_OPENWEBUI_MAX_MEMORY_MIB"); @@ -2733,6 +2739,43 @@ output="$(cat "$sampler_log")" expect(result.stderr).not.toContain("OPENAI_API_KEY is required"); }); + it.each([ + ["provider", "OPENCLAW_OPENWEBUI_PROVIDER_TIMEOUT_SECONDS", "300s"], + ["fetch", "OPENCLAW_OPENWEBUI_FETCH_TIMEOUT_MS", "8000ms"], + ])( + "rejects invalid Open WebUI Docker %s timeouts before Docker setup", + (_label, envName, value) => { + const result = spawnSync("bash", [OPENWEBUI_DOCKER_E2E_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("OPENAI_API_KEY is required"); + }, + ); + + it("accepts decimal Open WebUI Docker numeric inputs with leading zeroes", () => { + const result = spawnSync("bash", [OPENWEBUI_DOCKER_E2E_PATH], { + encoding: "utf8", + env: { + ...process.env, + OPENCLAW_OPENWEBUI_FETCH_TIMEOUT_MS: "09000", + OPENCLAW_OPENWEBUI_GATEWAY_PORT: "018789", + OPENCLAW_OPENWEBUI_PORT: "08080", + OPENCLAW_OPENWEBUI_PROVIDER_TIMEOUT_SECONDS: "08", + }, + }); + + expect(result.status).toBe(2); + expect(result.stderr).toContain("OPENAI_API_KEY is required"); + expect(result.stderr).not.toContain("value too great for base"); + }); + it.each([ [MCP_CODE_MODE_GATEWAY_DOCKER_E2E_PATH, "OPENCLAW_MCP_CODE_MODE_GATEWAY_PORT", "1e3"], [MCP_CODE_MODE_GATEWAY_DOCKER_E2E_PATH, "OPENCLAW_MCP_CODE_MODE_MOCK_PORT", "65536"],