Skip to content

fix(integration-tests): honor stdinDoesNotEnd option#2966

Open
chinesepowered wants to merge 1 commit intoQwenLM:mainfrom
chinesepowered:fix/stdin-does-not-end
Open

fix(integration-tests): honor stdinDoesNotEnd option#2966
chinesepowered wants to merge 1 commit intoQwenLM:mainfrom
chinesepowered:fix/stdin-does-not-end

Conversation

@chinesepowered
Copy link
Copy Markdown
Contributor

Fix stdinDoesNotEnd test option being completely non-functional.

TLDR

integration-tests/test-helper.ts had a conditional child.stdin.end() scoped to object-type promptOrOptions, followed immediately by an unconditional child.stdin.end(). The unconditional call always ran, so { stdinDoesNotEnd: true } had no effect — any integration test that tried to keep stdin open for streaming input had it closed anyway. This PR restructures the check so stdin stays open exactly when stdinDoesNotEnd: true was explicitly requested.

Screenshots / Video Demo

N/A — test-infrastructure fix, no user-visible UI.

Dive Deeper

TestRig.run() accepts promptOrOptions: string | { prompt?; stdin?; stdinDoesNotEnd? }. The broken code was:

// Before
if (
  typeof promptOrOptions === 'object' &&
  !promptOrOptions.stdinDoesNotEnd
) {
  child.stdin!.end();   // conditional — honored the flag
}
child.stdin!.end();     // unconditional — defeated the flag

Simply deleting the unconditional line would have broken the string-prompt path — when the caller passes rig.run('say hi') the typeof 'object' guard is false, the conditional skips, and the stray unconditional call was actually the catch-all that closed stdin for string callers. Removing it without restructuring the guard would cause every string-prompt test to hang waiting for EOF.

The correct fix inverts the logic: default to closing stdin, and only keep it open when stdinDoesNotEnd: true is explicitly passed on an options object.

// After
const keepStdinOpen =
  typeof promptOrOptions === 'object' &&
  promptOrOptions !== null &&
  promptOrOptions.stdinDoesNotEnd === true;
if (!keepStdinOpen) {
  child.stdin!.end();
}

Coverage of all call patterns:

Call keepStdinOpen stdin.end() called?
run('say hi') (string) false
run({ prompt: 'hi' }) false
run({ stdinDoesNotEnd: true }) true ❌ (as intended)
run({ stdinDoesNotEnd: false }) false
run(null) (null is typeof 'object') false

The explicit !== null guard covers the JavaScript quirk that typeof null === 'object' — otherwise the first clause would pass and we'd dereference .stdinDoesNotEnd on null.

Modified file:

  • integration-tests/test-helper.ts — restructured stdin-close logic

Reviewer Test Plan

  1. Existing string-prompt tests (the most common case) should still close stdin normally — no regressions
  2. Existing object-prompt tests without stdinDoesNotEnd should still close stdin normally
  3. Any test passing stdinDoesNotEnd: true should now actually keep stdin open — this path was previously dead code
  4. grep -n "stdin!.end" integration-tests/test-helper.ts — should show a single call inside the if (!keepStdinOpen) block

Testing Matrix

macOS Windows Linux
npm run ? pass ?
npx ? ? ?
Docker ? ? ?
Podman ? - -
Seatbelt ? - -

The stdinDoesNotEnd option was completely broken. The original code had
a conditional stdin.end() scoped to object-type promptOrOptions, followed
by an unconditional stdin.end() that always ran — so stdinDoesNotEnd: true
had no effect.

Restructure as an explicit keepStdinOpen check: close stdin unless the
caller passed an options object with stdinDoesNotEnd: true. The string-
prompt call path still closes stdin, and null is guarded (typeof null
=== 'object' in JS).
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.

1 participant