diff --git a/scripts/e2e/lib/plugins/assertions.mjs b/scripts/e2e/lib/plugins/assertions.mjs index 58285112aa9..05df10551c7 100644 --- a/scripts/e2e/lib/plugins/assertions.mjs +++ b/scripts/e2e/lib/plugins/assertions.mjs @@ -1,23 +1,24 @@ import fs from "node:fs"; import os from "node:os"; import path from "node:path"; +import { readPositiveIntEnv } from "../env-limits.mjs"; const command = process.argv[2]; const scratchRoot = process.env.OPENCLAW_PLUGINS_TMP_DIR || os.tmpdir(); -const CLAWHUB_PREFLIGHT_TIMEOUT_MS = readPositiveInt( - process.env.OPENCLAW_PLUGINS_E2E_CLAWHUB_PREFLIGHT_TIMEOUT_MS, - 30_000, -); -const CLAWHUB_PREFLIGHT_BODY_MAX_BYTES = readPositiveInt( - process.env.OPENCLAW_PLUGINS_E2E_CLAWHUB_PREFLIGHT_BODY_MAX_BYTES, - 1024 * 1024, -); const readJson = (file) => JSON.parse(fs.readFileSync(file, "utf8")); const scratchFile = (name) => path.join(scratchRoot, name); -function readPositiveInt(raw, fallback) { - const parsed = Number.parseInt(String(raw || ""), 10); - return Number.isInteger(parsed) && parsed > 0 ? parsed : fallback; +function readClawHubPreflightLimits() { + return { + bodyMaxBytes: readPositiveIntEnv( + "OPENCLAW_PLUGINS_E2E_CLAWHUB_PREFLIGHT_BODY_MAX_BYTES", + 1024 * 1024, + ), + timeoutMs: readPositiveIntEnv( + "OPENCLAW_PLUGINS_E2E_CLAWHUB_PREFLIGHT_TIMEOUT_MS", + 30_000, + ), + }; } function createTimeoutError(label, timeoutMs) { @@ -826,6 +827,7 @@ async function assertClawHubPreflight() { throw new Error(`expected clawhub: spec, got ${spec}`); } + const limits = readClawHubPreflightLimits(); const packageName = parseClawHubPackageName(spec); const baseUrl = ( process.env.OPENCLAW_CLAWHUB_URL || @@ -840,7 +842,7 @@ async function assertClawHubPreflight() { const preflightUrl = `${baseUrl}/api/v1/packages/${encodeURIComponent(packageName)}`; const response = await withTimeout( `ClawHub package preflight for ${packageName}`, - CLAWHUB_PREFLIGHT_TIMEOUT_MS, + limits.timeoutMs, (signal) => fetch(preflightUrl, { headers: token ? { Authorization: `Bearer ${token}` } : undefined, @@ -850,12 +852,12 @@ async function assertClawHubPreflight() { if (!response.ok) { const body = await withTimeout( `ClawHub package preflight response for ${packageName}`, - CLAWHUB_PREFLIGHT_TIMEOUT_MS, + limits.timeoutMs, () => readBoundedResponseText( response, `ClawHub package preflight response for ${packageName}`, - CLAWHUB_PREFLIGHT_BODY_MAX_BYTES, + limits.bodyMaxBytes, ), ); throw new Error( @@ -864,17 +866,17 @@ async function assertClawHubPreflight() { } const rawDetail = await withTimeout( `ClawHub package preflight response for ${packageName}`, - CLAWHUB_PREFLIGHT_TIMEOUT_MS, + limits.timeoutMs, () => readBoundedResponseText( response, `ClawHub package preflight response for ${packageName}`, - CLAWHUB_PREFLIGHT_BODY_MAX_BYTES, + limits.bodyMaxBytes, ), ); const detail = await withTimeout( `ClawHub package preflight JSON for ${packageName}`, - CLAWHUB_PREFLIGHT_TIMEOUT_MS, + limits.timeoutMs, () => JSON.parse(rawDetail), ); const family = detail.package?.family; diff --git a/test/scripts/plugins-assertions.test.ts b/test/scripts/plugins-assertions.test.ts index 1452753a347..b3506b65546 100644 --- a/test/scripts/plugins-assertions.test.ts +++ b/test/scripts/plugins-assertions.test.ts @@ -48,6 +48,34 @@ function runAssertionAsync(args: string[], env: NodeJS.ProcessEnv) { } describe("plugins Docker assertions", () => { + it("rejects loose ClawHub preflight limits instead of parsing prefixes", () => { + const timeoutResult = spawnSync(process.execPath, [ASSERTIONS_SCRIPT, "clawhub-preflight"], { + encoding: "utf8", + env: { + ...process.env, + CLAWHUB_PLUGIN_SPEC: "clawhub:@openclaw/kitchen-sink", + OPENCLAW_PLUGINS_E2E_CLAWHUB_PREFLIGHT_TIMEOUT_MS: "1e3", + }, + }); + expect(timeoutResult.status).not.toBe(0); + expect(timeoutResult.stderr).toContain( + "invalid OPENCLAW_PLUGINS_E2E_CLAWHUB_PREFLIGHT_TIMEOUT_MS: 1e3", + ); + + const bodyLimitResult = spawnSync(process.execPath, [ASSERTIONS_SCRIPT, "clawhub-preflight"], { + encoding: "utf8", + env: { + ...process.env, + CLAWHUB_PLUGIN_SPEC: "clawhub:@openclaw/kitchen-sink", + OPENCLAW_PLUGINS_E2E_CLAWHUB_PREFLIGHT_BODY_MAX_BYTES: "1000bytes", + }, + }); + expect(bodyLimitResult.status).not.toBe(0); + expect(bodyLimitResult.stderr).toContain( + "invalid OPENCLAW_PLUGINS_E2E_CLAWHUB_PREFLIGHT_BODY_MAX_BYTES: 1000bytes", + ); + }); + it("keeps sweep artifact paths aligned with the assertion scratch root", () => { const scripts = [ "scripts/e2e/lib/plugins/sweep.sh", @@ -92,6 +120,7 @@ describe("plugins Docker assertions", () => { env: { ...process.env, HOME: home, + OPENCLAW_PLUGINS_E2E_CLAWHUB_PREFLIGHT_TIMEOUT_MS: "1e3", OPENCLAW_PLUGINS_TMP_DIR: scratchRoot, }, });