From d38fb7456a2a28f25a083a57a326a7e5cee40730 Mon Sep 17 00:00:00 2001 From: Vincent Koc Date: Tue, 23 Jun 2026 14:46:43 +0200 Subject: [PATCH] fix(qa): reject duplicate otel smoke options --- .../qa-lab/runtime/qa-otel-smoke-runtime.ts | 29 +++++++++++++++---- .../qa-lab/runtime/qa-otel-smoke.e2e.test.ts | 16 ++++++++++ 2 files changed, 40 insertions(+), 5 deletions(-) diff --git a/test/e2e/qa-lab/runtime/qa-otel-smoke-runtime.ts b/test/e2e/qa-lab/runtime/qa-otel-smoke-runtime.ts index de8019fd3811..2bb22663f437 100644 --- a/test/e2e/qa-lab/runtime/qa-otel-smoke-runtime.ts +++ b/test/e2e/qa-lab/runtime/qa-otel-smoke-runtime.ts @@ -238,6 +238,13 @@ function parseArgs(argv: string[]): CliOptions { scenarioId: DEFAULT_SCENARIO_ID, help: false, }; + const seen = new Set(); + const recordOnce = (flag: string) => { + if (seen.has(flag)) { + throw new Error(`${flag} was provided more than once`); + } + seen.add(flag); + }; for (let index = 0; index < args.length; index += 1) { const arg = args[index]; @@ -254,15 +261,19 @@ function parseArgs(argv: string[]): CliOptions { return value; }; if (arg === "--output-dir") { - options.outputDir = readValue(); + const value = readValue(); + recordOnce(arg); + options.outputDir = value; } else if (arg === "--collector") { const value = readValue(); + recordOnce(arg); if (value !== "local" && value !== "docker") { throw new Error(`--collector must be local or docker, got ${JSON.stringify(value)}`); } options.collectorMode = value; } else if (arg === "--logs-exporter") { const value = readValue(); + recordOnce(arg); if (value !== "otlp" && value !== "stdout" && value !== "both") { throw new Error( `--logs-exporter must be otlp, stdout, or both, got ${JSON.stringify(value)}`, @@ -270,14 +281,22 @@ function parseArgs(argv: string[]): CliOptions { } options.logsExporter = value; } else if (arg === "--provider-mode") { - options.providerMode = readValue(); + const value = readValue(); + recordOnce(arg); + options.providerMode = value; } else if (arg === "--scenario") { - options.scenarioId = readValue(); + const value = readValue(); + recordOnce(arg); + options.scenarioId = value; scenarioExplicit = true; } else if (arg === "--model") { - options.primaryModel = readValue(); + const value = readValue(); + recordOnce(arg); + options.primaryModel = value; } else if (arg === "--alt-model") { - options.alternateModel = readValue(); + const value = readValue(); + recordOnce(arg); + options.alternateModel = value; } else { throw new Error(`unknown argument: ${arg}`); } diff --git a/test/e2e/qa-lab/runtime/qa-otel-smoke.e2e.test.ts b/test/e2e/qa-lab/runtime/qa-otel-smoke.e2e.test.ts index 35f3af49f233..dbb767d1dc31 100644 --- a/test/e2e/qa-lab/runtime/qa-otel-smoke.e2e.test.ts +++ b/test/e2e/qa-lab/runtime/qa-otel-smoke.e2e.test.ts @@ -148,6 +148,22 @@ describe("qa-otel-smoke receiver bounds", () => { expect(() => testing.parseArgs(args)).toThrow(`${flag} requires a value`); }); + it("rejects duplicate OTEL smoke CLI options", () => { + const duplicateCases = [ + ["--collector", ["--collector", "local", "--collector", "docker"]], + ["--logs-exporter", ["--logs-exporter", "otlp", "--logs-exporter", "stdout"]], + ["--output-dir", ["--output-dir", ".artifacts/one", "--output-dir", ".artifacts/two"]], + ["--provider-mode", ["--provider-mode", "mock-openai", "--provider-mode", "live-frontier"]], + ["--scenario", ["--scenario", "custom-one", "--scenario", "custom-two"]], + ["--model", ["--model", "openai/gpt-5.5", "--model", "openai/gpt-5.4"]], + ["--alt-model", ["--alt-model", "openai/gpt-5.5", "--alt-model", "openai/gpt-5.4"]], + ] satisfies Array<[string, string[]]>; + + for (const [flag, args] of duplicateCases) { + expect(() => testing.parseArgs(args), flag).toThrow(`${flag} was provided more than once`); + } + }); + it("selects the matching scenario for the requested log exporter", () => { expect(testing.parseArgs(["--logs-exporter", "otlp"]).scenarioId).toBe("otel-trace-smoke"); expect(testing.parseArgs(["--logs-exporter", "stdout"]).scenarioId).toBe(