fix(test-runner): GSTACK_FREE_JOBS accepts digits only — parseInt truncation defeated the loud-failure contract

'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 <noreply@anthropic.com>
This commit is contained in:
Garry Tan
2026-08-29 05:58:14 +00:00
co-authored by Claude Fable 5
parent 04c96dd981
commit 507eec704d
2 changed files with 7 additions and 4 deletions
+4 -3
View File
@@ -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));
}
+3 -1
View File
@@ -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/);
}
});