fix(e2e): reject invalid bundled runtime limits

This commit is contained in:
Vincent Koc
2026-06-06 17:23:38 +02:00
parent d90a94ad16
commit 97758910fa
3 changed files with 46 additions and 58 deletions

View File

@@ -8,42 +8,33 @@ import { setTimeout as delay } from "node:timers/promises";
import { fileURLToPath } from "node:url";
const TOKEN = "bundled-plugin-runtime-smoke-token";
const OUTPUT_CAPTURE_CHARS = readPositiveInt(
process.env.OPENCLAW_BUNDLED_PLUGIN_RUNTIME_OUTPUT_CHARS,
const OUTPUT_CAPTURE_CHARS = readPositiveIntEnv(
"OPENCLAW_BUNDLED_PLUGIN_RUNTIME_OUTPUT_CHARS",
1024 * 1024,
);
const LOG_SCAN_BYTES = readPositiveInt(
process.env.OPENCLAW_BUNDLED_PLUGIN_RUNTIME_LOG_SCAN_BYTES,
const LOG_SCAN_BYTES = readPositiveIntEnv(
"OPENCLAW_BUNDLED_PLUGIN_RUNTIME_LOG_SCAN_BYTES",
256 * 1024,
);
const GATEWAY_LOG_CAPTURE_BYTES = readPositiveInt(
process.env.OPENCLAW_BUNDLED_PLUGIN_RUNTIME_GATEWAY_LOG_BYTES,
const GATEWAY_LOG_CAPTURE_BYTES = readPositiveIntEnv(
"OPENCLAW_BUNDLED_PLUGIN_RUNTIME_GATEWAY_LOG_BYTES",
16 * 1024 * 1024,
);
const WATCHDOG_MS = readPositiveInt(process.env.OPENCLAW_BUNDLED_PLUGIN_RUNTIME_WATCHDOG_MS, 1000);
const READY_TIMEOUT_MS = readPositiveInt(
process.env.OPENCLAW_BUNDLED_PLUGIN_RUNTIME_READY_MS,
900000,
);
const RPC_TIMEOUT_MS = readPositiveInt(process.env.OPENCLAW_BUNDLED_PLUGIN_RUNTIME_RPC_MS, 60000);
const RPC_READY_TIMEOUT_MS = readPositiveInt(
process.env.OPENCLAW_BUNDLED_PLUGIN_RUNTIME_RPC_READY_MS,
const WATCHDOG_MS = readPositiveIntEnv("OPENCLAW_BUNDLED_PLUGIN_RUNTIME_WATCHDOG_MS", 1000);
const READY_TIMEOUT_MS = readPositiveIntEnv("OPENCLAW_BUNDLED_PLUGIN_RUNTIME_READY_MS", 900000);
const RPC_TIMEOUT_MS = readPositiveIntEnv("OPENCLAW_BUNDLED_PLUGIN_RUNTIME_RPC_MS", 60000);
const RPC_READY_TIMEOUT_MS = readPositiveIntEnv(
"OPENCLAW_BUNDLED_PLUGIN_RUNTIME_RPC_READY_MS",
210000,
);
const COMMAND_TIMEOUT_MS = readPositiveInt(
process.env.OPENCLAW_BUNDLED_PLUGIN_RUNTIME_COMMAND_MS,
120000,
);
const HTTP_PROBE_TIMEOUT_MS = readPositiveInt(
process.env.OPENCLAW_BUNDLED_PLUGIN_RUNTIME_HTTP_MS,
5000,
);
const GATEWAY_TEARDOWN_GRACE_MS = readPositiveInt(
process.env.OPENCLAW_BUNDLED_PLUGIN_RUNTIME_TEARDOWN_GRACE_MS,
const COMMAND_TIMEOUT_MS = readPositiveIntEnv("OPENCLAW_BUNDLED_PLUGIN_RUNTIME_COMMAND_MS", 120000);
const HTTP_PROBE_TIMEOUT_MS = readPositiveIntEnv("OPENCLAW_BUNDLED_PLUGIN_RUNTIME_HTTP_MS", 5000);
const GATEWAY_TEARDOWN_GRACE_MS = readPositiveIntEnv(
"OPENCLAW_BUNDLED_PLUGIN_RUNTIME_TEARDOWN_GRACE_MS",
10000,
);
const GATEWAY_TEARDOWN_KILL_GRACE_MS = readPositiveInt(
process.env.OPENCLAW_BUNDLED_PLUGIN_RUNTIME_TEARDOWN_KILL_GRACE_MS,
const GATEWAY_TEARDOWN_KILL_GRACE_MS = readPositiveIntEnv(
"OPENCLAW_BUNDLED_PLUGIN_RUNTIME_TEARDOWN_KILL_GRACE_MS",
1000,
);
const GATEWAY_READY_LOG_NEEDLE = Buffer.from("[gateway] ready");
@@ -62,13 +53,23 @@ const activeGatewayChildren = new Set();
const parentSignalHandlers = new Map();
let parentCleanupInstalled = false;
function readPositiveInt(raw, fallback) {
function readPositiveIntEnv(name, fallback) {
return readPositiveInt(process.env[name], fallback, name);
}
function readPositiveInt(raw, fallback, name) {
const text = String(raw ?? "").trim();
if (!/^\d+$/u.test(text)) {
if (!text) {
return fallback;
}
if (!/^\d+$/u.test(text)) {
throw new Error(`invalid ${name}: ${text}`);
}
const parsed = Number(text);
return Number.isInteger(parsed) && parsed > 0 ? parsed : fallback;
if (!Number.isInteger(parsed) || parsed <= 0) {
throw new Error(`invalid ${name}: ${text}`);
}
return parsed;
}
function readJson(file) {
@@ -866,7 +867,7 @@ async function smokePlugin(pluginId, pluginDir, requiresConfig, pluginIndex, plu
const manifest = loadManifest(pluginDir, pluginRoot);
const plan = buildPluginPlan(manifest);
const port =
readPositiveInt(process.env.OPENCLAW_BUNDLED_PLUGIN_RUNTIME_PORT_BASE, 19000) + pluginIndex * 3;
readPositiveIntEnv("OPENCLAW_BUNDLED_PLUGIN_RUNTIME_PORT_BASE", 19000) + pluginIndex * 3;
const config = ensureGatewayConfig(
activateSmokePlugin(readConfig(), pluginId, plan.channels),
port,
@@ -1196,9 +1197,7 @@ async function smokeTtsGlobalDisable(pluginId, pluginDir, provider, pluginIndex,
return;
}
const port =
readPositiveInt(process.env.OPENCLAW_BUNDLED_PLUGIN_RUNTIME_PORT_BASE, 19000) +
pluginIndex * 3 +
1;
readPositiveIntEnv("OPENCLAW_BUNDLED_PLUGIN_RUNTIME_PORT_BASE", 19000) + pluginIndex * 3 + 1;
const env = createIsolatedStateEnv(`tts-disabled-${pluginId}`);
writeConfig(
ensureGatewayConfig(
@@ -1251,9 +1250,7 @@ async function smokeOpenAiTts(pluginIndex) {
return;
}
const port =
readPositiveInt(process.env.OPENCLAW_BUNDLED_PLUGIN_RUNTIME_PORT_BASE, 19000) +
pluginIndex * 3 +
2;
readPositiveIntEnv("OPENCLAW_BUNDLED_PLUGIN_RUNTIME_PORT_BASE", 19000) + pluginIndex * 3 + 2;
const env = createIsolatedStateEnv("tts-openai-live");
writeConfig(
ensureGatewayConfig(

View File

@@ -341,14 +341,11 @@ describe("bundled plugin install/uninstall probe", () => {
});
it("rejects loose runtime output limit env values instead of parsing prefixes", async () => {
const runtimeSmoke = await importRuntimeSmokeWithEnv({
OPENCLAW_BUNDLED_PLUGIN_RUNTIME_OUTPUT_CHARS: "5chars",
});
expect(runtimeSmoke.appendBoundedOutput({ text: "", truncatedChars: 0 }, "abcdef")).toEqual({
text: "abcdef",
truncatedChars: 0,
});
await expect(
importRuntimeSmokeWithEnv({
OPENCLAW_BUNDLED_PLUGIN_RUNTIME_OUTPUT_CHARS: "5chars",
}),
).rejects.toThrow("invalid OPENCLAW_BUNDLED_PLUGIN_RUNTIME_OUTPUT_CHARS: 5chars");
});
it("keeps runtime log tail reads bounded", async () => {
@@ -366,18 +363,11 @@ describe("bundled plugin install/uninstall probe", () => {
});
it("rejects loose runtime log scan byte env values instead of parsing prefixes", async () => {
const runtimeSmoke = await importRuntimeSmokeWithEnv({
OPENCLAW_BUNDLED_PLUGIN_RUNTIME_LOG_SCAN_BYTES: "64bytes",
});
const root = makePackageRoot();
const logPath = path.join(root, "gateway.log");
fs.writeFileSync(logPath, `${"old log line\n".repeat(20)}[gateway] ready\n`, "utf8");
const tail = runtimeSmoke.readFileTail(logPath);
expect(Buffer.byteLength(tail)).toBeGreaterThan(64);
expect(tail).toContain("old log line");
expect(tail).toContain("[gateway] ready");
await expect(
importRuntimeSmokeWithEnv({
OPENCLAW_BUNDLED_PLUGIN_RUNTIME_LOG_SCAN_BYTES: "64bytes",
}),
).rejects.toThrow("invalid OPENCLAW_BUNDLED_PLUGIN_RUNTIME_LOG_SCAN_BYTES: 64bytes");
});
it("remembers runtime ready logs after they fall outside the tail", async () => {

View File

@@ -2239,8 +2239,9 @@ output="$(run_logged_print_heartbeat plugins-run 08 bash -c 'printf "captured co
expect(runner).toContain('tee "$RUN_LOG"');
expect(runner).not.toContain('cat "$RUN_LOG"');
expect(probe).toContain('"openclaw.plugin.json"');
expect(runtimeSmoke).toContain("process.env.OPENCLAW_BUNDLED_PLUGIN_RUNTIME_READY_MS");
expect(runtimeSmoke).toContain("900000");
expect(runtimeSmoke).toContain(
'readPositiveIntEnv("OPENCLAW_BUNDLED_PLUGIN_RUNTIME_READY_MS", 900000)',
);
expect(sweep).toContain("read -r plugin_id plugin_dir requires_config");
expect(sweep).toContain('node "$OPENCLAW_ENTRY" plugins install "$plugin_id"');
expect(sweep).toContain('node "$OPENCLAW_ENTRY" plugins uninstall "$plugin_id" --force');