From 507eec704ddae55200de5c7adb989b1b0591b573 Mon Sep 17 00:00:00 2001 From: Garry Tan Date: Sat, 29 Aug 2026 05:58:14 +0000 Subject: [PATCH] =?UTF-8?q?fix(test-runner):=20GSTACK=5FFREE=5FJOBS=20acce?= =?UTF-8?q?pts=20digits=20only=20=E2=80=94=20parseInt=20truncation=20defea?= =?UTF-8?q?ted=20the=20loud-failure=20contract?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit '2abc' silently became 2 and '3.7' became 3 despite the error text claiming a positive-integer requirement. Strict /^\d+$/ pre-check; both shapes pinned. Co-Authored-By: Claude Fable 5 --- scripts/test-free-shards.ts | 7 ++++--- test/test-free-shards-sandbox-knobs.test.ts | 4 +++- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/scripts/test-free-shards.ts b/scripts/test-free-shards.ts index 11187876c..1762bbee3 100755 --- a/scripts/test-free-shards.ts +++ b/scripts/test-free-shards.ts @@ -349,11 +349,12 @@ export const RESERVED_CPUS = 2; export function fullSuiteJobs(): number { const raw = process.env.GSTACK_FREE_JOBS; if (raw !== undefined && raw !== '') { - const value = Number.parseInt(raw, 10); - if (!Number.isInteger(value) || value <= 0) { + // Strict digits-only: parseInt would silently truncate "2abc" -> 2 and + // "3.7" -> 3, defeating the loud-failure contract the error text claims. + if (!/^\d+$/.test(raw.trim()) || Number.parseInt(raw, 10) <= 0) { throw new Error(`GSTACK_FREE_JOBS must be a positive integer, got: ${raw}`); } - return value; + return Number.parseInt(raw, 10); } return Math.max(1, Math.min(MAX_FULL_SUITE_JOBS, os.cpus().length - RESERVED_CPUS)); } diff --git a/test/test-free-shards-sandbox-knobs.test.ts b/test/test-free-shards-sandbox-knobs.test.ts index 1de5b81b7..92279d27a 100644 --- a/test/test-free-shards-sandbox-knobs.test.ts +++ b/test/test-free-shards-sandbox-knobs.test.ts @@ -55,7 +55,9 @@ describe('test-free-shards: fullSuiteJobs (GSTACK_FREE_JOBS override)', () => { }); test('zero, negative, and non-numeric values throw loudly instead of silently defaulting', () => { - for (const bad of ['0', '-2', 'abc', 'NaN']) { + // '2abc' and '3.7' pin the strict digits-only check: parseInt would + // silently truncate them to 2 and 3, defeating the loud-failure contract. + for (const bad of ['0', '-2', 'abc', 'NaN', '2abc', '3.7']) { expect(() => withJobsEnv(bad, fullSuiteJobs)).toThrow(/positive integer/); } });