Skip to content

fix(cli): validate --request-timeout flag and de-duplicate its parser#17

Open
Davidson3556 wants to merge 1 commit into
TestSprite:mainfrom
Davidson3556:fix/validate-request-timeout-flag
Open

fix(cli): validate --request-timeout flag and de-duplicate its parser#17
Davidson3556 wants to merge 1 commit into
TestSprite:mainfrom
Davidson3556:fix/validate-request-timeout-flag

Conversation

@Davidson3556

@Davidson3556 Davidson3556 commented Jun 24, 2026

Copy link
Copy Markdown

Fixes #16

Describe the changes you have made in this PR -

Two related problems with the global --request-timeout <seconds> flag:

  1. Invalid values were silently dropped. parseRequestTimeoutFlag returned undefined for any non-numeric / zero / negative input, and undefined is indistinguishable from "flag omitted" — so the command fell back to TESTSPRITE_REQUEST_TIMEOUT_MS or the 120s default. A natural typo like --request-timeout 30s (meaning "30 seconds") ran with the default 120s deadline while the operator believed they had set 30s, with no error. Every other validated flag (--page-size, --output, --type) exits 5 on bad input; this one was the exception.

  2. The parser was duplicated five times. The identical parseRequestTimeoutFlag body lived in auth.ts, project.ts, usage.ts, init.ts, and test.ts. Drift between copies would silently change timeout behaviour depending on the command.

Changes:

  • Hoist one parseRequestTimeoutFlag into src/lib/client-factory.ts, co-located with resolveRequestTimeoutMs and the REQUEST_TIMEOUT_* constants; the five command files import it.
  • Make the flag strict: a non-numeric / zero / negative value throws a typed VALIDATION_ERROR (exit 5).
  • Preserve intentional leniency where it exists: positive out-of-range values still flow through to resolveRequestTimeoutMs, which already clamps to [1s, 600s]; the TESTSPRITE_REQUEST_TIMEOUT_MS env-var path stays lenient (its existing tests assert non-numeric/zero/negative env vars fall back to the default) — a stray global env var should not hard-fail every command.
  • Add unit coverage for parseRequestTimeoutFlag and a subprocess regression that --request-timeout 30s exits 5 instead of silently using 120s.

Demo/Screenshot for feature changes and bug fixes -

Before (main) — the invalid flag is ignored and execution proceeds past validation (here reaching the auth step), so it would have silently run with the default 120s:

image

After (this branch) — the flag is validated up front; valid values still proceed:

image

Tests:

image

Code Understanding and AI Usage

Did you use AI assistance (ChatGPT, Claude, Copilot, etc.) to write any part of this code?

  • No, I wrote all the code myself
  • Yes, I used AI assistance (continue below)

If you used AI assistance:

  • I have reviewed every single line of the AI-generated code
  • I can explain the purpose and logic of each function/component I added
  • I have tested edge cases and understand how the code handles them
  • I have modified the AI output to follow this project's coding standards and conventions

Explain your implementation approach:

Problem: an explicit --request-timeout value that didn't parse was silently discarded, so the operator's chosen deadline was ignored without any signal — and the parser was copy-pasted into five files.

Alternatives considered:

  1. Keep the lenient behaviour and just de-duplicate. Rejected: that leaves the real footgun (a typo silently runs at 120s) in place.
  2. Make both the flag and the env var strict. Rejected: the env-var leniency is intentional and pinned by resolveRequestTimeoutMs tests — a globally-set TESTSPRITE_REQUEST_TIMEOUT_MS should not hard-fail every single command. Only the explicit per-invocation flag should fail loudly.
  3. (chosen) One shared parser, strict on the flag, lenient on the env var, with out-of-range positives still clamped downstream.

Key function: parseRequestTimeoutFlag(raw)undefinedundefined (flag omitted, factory falls back to env/default); a positive number → seconds×1000 ms (including out-of-range values, which resolveRequestTimeoutMs clamps to [1s, 600s]); non-numeric / zero / negative → throws the typed VALIDATION_ERROR (exit 5). Placed in client-factory.ts next to the related timeout logic so the flag, env, and default resolution all live together.

Edge cases handled/tested: omitted flag; positive seconds (incl. fractional like 2.5); positive out-of-range (700 parses to 700000, clamped later, not rejected); abc, 30s, 0, -5, NaN, Infinity, and empty string each throw exit 5; subprocess test confirms the CLI exit code is 5, not a silent fall-back.


Checklist before requesting a review

  • I have added proper PR title and linked to the issue
  • I have performed a self-review of my code
  • I can explain the purpose of every function, class, and logic block I added
  • I understand why my changes work and have tested them thoroughly
  • I have considered potential edge cases and how my code handles them
  • If it is a core feature, I have added thorough tests
  • My code follows the project's style guidelines and conventions

Note: Please check Allow edits from maintainers if you would like us to assist in the PR.

`parseRequestTimeoutFlag` was copy-pasted byte-for-byte into five command
files (auth, project, usage, init, test). Every copy silently returned
`undefined` for an invalid value, so an explicit `--request-timeout 30s`
(a natural "30 seconds" typo) resolved to undefined and the command ran
with the default 120s deadline — the operator believed they had set a
timeout but had not, with no signal.

Hoist a single definition into client-factory.ts (next to
resolveRequestTimeoutMs and the REQUEST_TIMEOUT_* constants) and make the
flag strict: a non-numeric, zero, or negative value now throws a typed
VALIDATION_ERROR (exit 5), consistent with every other validated flag
(--page-size, --output, --type). Positive out-of-range values are still
accepted and clamped by resolveRequestTimeoutMs, and the
TESTSPRITE_REQUEST_TIMEOUT_MS env-var path stays lenient by design (a
stray global env var should not hard-fail every command).

Adds unit coverage for parseRequestTimeoutFlag and a subprocess
regression that `--request-timeout 30s` exits 5 instead of falling back
to 120s.
@Davidson3556

Copy link
Copy Markdown
Author

@zeshi-du please kindly review.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Invalid --request-timeout value is silently ignored (falls back to 120s); parser duplicated 5×

1 participant