Skip to content

Commit d1577a2

Browse files
committed
fix(perf): reject invalid startup bench counts
1 parent d2319d7 commit d1577a2

3 files changed

Lines changed: 40 additions & 25 deletions

File tree

CHANGELOG.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ Docs: https://docs.openclaw.ai
5555
- Gateway/performance: cache plugin metadata fingerprints and stable plugin index fingerprints, borrow read-only session metadata safely, keep the active session working store hot, keep status on a bounded fast path, and preserve model auth profile suffixes. (#86439)
5656
- Package/install/release: align npm package exclusions and inventory, omit unpacked test helpers, skip Homebrew until macOS packages need it, cap tsdown heap in containers, bound install/release smoke waits, and harden post-publish verification.
5757
- Codex/Auth: bound ChatGPT OAuth token exchange and refresh requests, and honor cancellation across Codex and Anthropic OAuth login flows.
58-
- QA/E2E/CI: bound Telegram, kitchen-sink, Open WebUI, ClawHub, MCP, Discord, realtime, labeler, and GitHub API waits; fail empty explicit test, live-media, gateway CPU, plugin gauntlet, and beta-smoke runs instead of false-greening.
58+
- QA/E2E/CI: bound Telegram, kitchen-sink, Open WebUI, ClawHub, MCP, Discord, realtime, labeler, and GitHub API waits; fail empty explicit test, live-media, gateway CPU, startup benchmark, plugin gauntlet, and beta-smoke runs instead of false-greening.
5959
- Agents/Codex: keep spawned agent bootstrap files rooted in the agent workspace while running task commands, transcripts, and compaction from the requested cwd. (#87218) Thanks @mbelinky.
6060

6161
## 2026.5.26

scripts/bench-cli-startup.ts

Lines changed: 17 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import { mkdirSync, mkdtempSync, rmSync, writeFileSync } from "node:fs";
33
import os from "node:os";
44
import path from "node:path";
55
import { pathToFileURL } from "node:url";
6+
import { parseStrictIntegerOption } from "./lib/dev-tooling-safety.ts";
67

78
type CommandCase = {
89
id: string;
@@ -377,7 +378,11 @@ function parseFlagValue(flag: string): string | undefined {
377378
if (idx === -1) {
378379
return undefined;
379380
}
380-
return process.argv[idx + 1];
381+
const value = process.argv[idx + 1];
382+
if (!value || value.startsWith("--")) {
383+
throw new Error(`${flag} requires a value`);
384+
}
385+
return value;
381386
}
382387

383388
function hasFlag(flag: string): boolean {
@@ -394,26 +399,12 @@ function parseRepeatableFlag(flag: string): string[] {
394399
return values;
395400
}
396401

397-
function parsePositiveInt(raw: string | undefined, fallback: number): number {
398-
if (!raw) {
399-
return fallback;
400-
}
401-
const parsed = Number.parseInt(raw, 10);
402-
if (!Number.isFinite(parsed) || parsed < 1) {
403-
return fallback;
404-
}
405-
return parsed;
402+
function parsePositiveInt(raw: string | undefined, fallback: number, label = "value"): number {
403+
return parseStrictIntegerOption({ fallback, label, min: 1, raw });
406404
}
407405

408-
function parseNonNegativeInt(raw: string | undefined, fallback: number): number {
409-
if (!raw) {
410-
return fallback;
411-
}
412-
const parsed = Number.parseInt(raw, 10);
413-
if (!Number.isFinite(parsed) || parsed < 0) {
414-
return fallback;
415-
}
416-
return parsed;
406+
function parseNonNegativeInt(raw: string | undefined, fallback: number, label = "value"): number {
407+
return parseStrictIntegerOption({ fallback, label, min: 0, raw });
417408
}
418409

419410
function parsePresets(raw: string | undefined): string[] {
@@ -826,9 +817,13 @@ function parseOptions(): CliOptions {
826817
cases,
827818
entryPrimary: parseFlagValue("--entry-primary") ?? parseFlagValue("--entry") ?? DEFAULT_ENTRY,
828819
entrySecondary: parseFlagValue("--entry-secondary"),
829-
runs: parsePositiveInt(parseFlagValue("--runs"), DEFAULT_RUNS),
830-
warmup: parseNonNegativeInt(parseFlagValue("--warmup"), DEFAULT_WARMUP),
831-
timeoutMs: parsePositiveInt(parseFlagValue("--timeout-ms"), DEFAULT_TIMEOUT_MS),
820+
runs: parsePositiveInt(parseFlagValue("--runs"), DEFAULT_RUNS, "--runs"),
821+
warmup: parseNonNegativeInt(parseFlagValue("--warmup"), DEFAULT_WARMUP, "--warmup"),
822+
timeoutMs: parsePositiveInt(
823+
parseFlagValue("--timeout-ms"),
824+
DEFAULT_TIMEOUT_MS,
825+
"--timeout-ms",
826+
),
832827
json: hasFlag("--json"),
833828
output: parseFlagValue("--output"),
834829
cpuProfDir: parseFlagValue("--cpu-prof-dir"),

test/scripts/bench-cli-startup.test.ts

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -65,9 +65,29 @@ describe("bench-cli-startup", () => {
6565
]);
6666
});
6767

68-
it("does not accept zero measured runs", () => {
69-
expect(testing.parsePositiveInt("0", 5)).toBe(5);
68+
it("rejects invalid measured run counts", () => {
69+
expect(() => testing.parsePositiveInt("0", 5, "--runs")).toThrow(
70+
"--runs must be an integer >= 1",
71+
);
72+
expect(() => testing.parsePositiveInt("2abc", 5, "--runs")).toThrow(
73+
"--runs must be an integer >= 1",
74+
);
75+
expect(() => testing.parsePositiveInt("1.5", 5, "--runs")).toThrow(
76+
"--runs must be an integer >= 1",
77+
);
78+
expect(() => testing.parsePositiveInt("1e3", 5, "--runs")).toThrow(
79+
"--runs must be an integer >= 1",
80+
);
81+
expect(() => testing.parsePositiveInt("0x10", 5, "--runs")).toThrow(
82+
"--runs must be an integer >= 1",
83+
);
7084
expect(testing.parsePositiveInt("1", 5)).toBe(1);
7185
expect(testing.parseNonNegativeInt("0", 1)).toBe(0);
86+
expect(() => testing.parseNonNegativeInt("-1", 1, "--warmup")).toThrow(
87+
"--warmup must be an integer >= 0",
88+
);
89+
expect(() => testing.parseNonNegativeInt("0b10", 1, "--warmup")).toThrow(
90+
"--warmup must be an integer >= 0",
91+
);
7292
});
7393
});

0 commit comments

Comments
 (0)