diff --git a/scripts/bench-cli-startup.ts b/scripts/bench-cli-startup.ts index 1d5b8adb345..d28d06a103d 100644 --- a/scripts/bench-cli-startup.ts +++ b/scripts/bench-cli-startup.ts @@ -533,7 +533,12 @@ function parsePresets(raw: string | undefined): string[] { function resolveCases(options: { presets: string[]; caseIds: string[] }): CommandCase[] { const byId = new Map(COMMAND_CASES.map((commandCase) => [commandCase.id, commandCase])); if (options.caseIds.length > 0) { + const seenIds = new Set(); return options.caseIds.map((id) => { + if (seenIds.has(id)) { + throw new Error(`Duplicate --case "${id}"`); + } + seenIds.add(id); const commandCase = byId.get(id); if (!commandCase) { throw new Error(`Unknown --case "${id}"`); diff --git a/scripts/bench-gateway-restart.ts b/scripts/bench-gateway-restart.ts index 8e59757bfce..54adeed4313 100644 --- a/scripts/bench-gateway-restart.ts +++ b/scripts/bench-gateway-restart.ts @@ -337,8 +337,13 @@ function resolveCases(caseIds: string[]): GatewayBenchCase[] { if (caseIds.length === 0) { return [GATEWAY_CASES[0]]; } + const seenIds = new Set(); const byId = new Map(GATEWAY_CASES.map((benchCase) => [benchCase.id, benchCase])); return caseIds.map((id) => { + if (seenIds.has(id)) { + throw new CliArgumentError(`Duplicate --case "${id}"`); + } + seenIds.add(id); const benchCase = byId.get(id); if (!benchCase) { throw new Error(`Unknown --case "${id}"`); diff --git a/test/scripts/bench-cli-startup.test.ts b/test/scripts/bench-cli-startup.test.ts index 10a8cc24ae4..97be9c335c8 100644 --- a/test/scripts/bench-cli-startup.test.ts +++ b/test/scripts/bench-cli-startup.test.ts @@ -57,6 +57,31 @@ describe("bench-cli-startup", () => { expect(result.stderr).not.toContain("\n at "); }); + it("rejects duplicate benchmark cases before running benchmarks", () => { + const result = spawnSync( + process.execPath, + [ + "--import", + "tsx", + "scripts/bench-cli-startup.ts", + "--case", + "version", + "--case", + "version", + ], + { + cwd: join(__dirname, "../.."), + encoding: "utf8", + }, + ); + + expect(result.status).toBe(1); + expect(result.stdout).toBe(""); + expect(result.stderr.trim()).toBe('Duplicate --case "version"'); + expect(result.stderr).not.toContain("Node.js"); + expect(result.stderr).not.toContain("\n at "); + }); + it.runIf(process.platform !== "win32")( "cleans timed-out benchmark process groups when the leader exits first", () => { diff --git a/test/scripts/bench-gateway-restart.test.ts b/test/scripts/bench-gateway-restart.test.ts index 35a1b421cda..47ead435237 100644 --- a/test/scripts/bench-gateway-restart.test.ts +++ b/test/scripts/bench-gateway-restart.test.ts @@ -91,6 +91,9 @@ describe("gateway restart benchmark script", () => { "--output requires a value", ); expect(() => testing.parseOptions(["--case"])).toThrow("--case requires a value"); + expect(() => + testing.parseOptions(["--case", "skipChannels", "--case", "skipChannels"]), + ).toThrow('Duplicate --case "skipChannels"'); expect(() => testing.parseOptions(["--restarts", "--runs", "1"])).toThrow( "--restarts requires a value", ); @@ -117,6 +120,34 @@ describe("gateway restart benchmark script", () => { expect(result.stderr).not.toContain("\n at "); }); + it("reports duplicate benchmark cases without a stack trace", () => { + const result = spawnSync( + process.execPath, + [ + "--import", + "tsx", + "scripts/bench-gateway-restart.ts", + "--case", + "skipChannels", + "--case", + "skipChannels", + ], + { + cwd: process.cwd(), + encoding: "utf8", + env: { + ...process.env, + NODE_NO_WARNINGS: "1", + }, + }, + ); + + expect(result.status).toBe(1); + expect(result.stdout).toBe(""); + expect(result.stderr.trim()).toBe('Duplicate --case "skipChannels"'); + expect(result.stderr).not.toContain("\n at "); + }); + it("guards the SIGUSR1 restart benchmark on Windows", () => { expect(() => testing.ensureSupportedRestartPlatform("linux")).not.toThrow(); expect(() => testing.ensureSupportedRestartPlatform("darwin")).not.toThrow();