Skip to content

Commit bcefa27

Browse files
committed
test(cli): make the #4873 exit-code pin deterministic — literal duration, no wall clock (#6266)
The case read the duration off a live `createTimer()`: const durationMs = timer.elapsed() + 531; expect(durationMs & 0xff).toBe(19); which holds only while `elapsed()` returns exactly 0 — and an `await emitJson(...)` sits between the two statements. One millisecond turns 531 into 532 and `& 0xff` from 19 into 20, the exact number CI reported. The clock supplied a failure mode and no coverage: neither the type rejection nor Node's 8-bit truncation is a function of how long anything took. The duration is now the literal 531. The guard keeps bearing load — both `@ts-expect-error` directives are untouched, and the runtime half still asserts that the duration reaches the exit-code slot verbatim; the truncation line is joined by an explicit statement of why 19 is a defect (it is not one of the two codes `CliExitCode` defines). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017uFVNMmTxLpmfQYiuKM1Yx
1 parent eb7613c commit bcefa27

1 file changed

Lines changed: 22 additions & 5 deletions

File tree

packages/cli/src/utils/format.exit-code.test.ts

Lines changed: 22 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@
2828
*/
2929

3030
import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest';
31-
import { emitJson, emitText, createTimer } from './format.js';
31+
import { emitJson, emitText } from './format.js';
3232

3333
/** Whatever the runner was holding before this file ran — restored after each case. */
3434
const OUTER_EXIT_CODE = process.exitCode;
@@ -96,10 +96,20 @@ describe('emitJson / emitText — process.exitCode (#4873)', () => {
9696
* suppressed, the exact call `recorded-by.ts` used to make still reproduces
9797
* the defect verbatim, so this test states what the type is preventing
9898
* rather than merely asserting that it prevents something.
99+
*
100+
* The duration below is a LITERAL, and deliberately so (#6266). It used to
101+
* be read off a live clock — `createTimer()` then `timer.elapsed() + 531` —
102+
* which made the truncation assertion hold only while `elapsed()` returned
103+
* exactly `0`: one millisecond ticking between those two statements (there
104+
* is an `await emitJson(...)` in between) turned 531 into 532, `& 0xff` from
105+
* 19 into 20, and the case red on a machine that was merely busy. Nothing
106+
* this case pins is a function of how long anything took — neither the type
107+
* rejection nor Node's truncation of `process.exitCode` — so the clock
108+
* supplied a failure mode and no coverage whatsoever. A duration that never
109+
* varies still is one.
99110
*/
100111
it('a duration can no longer reach the exit-code slot (#4873)', async () => {
101-
const timer = createTimer();
102-
const durationMs = timer.elapsed() + 531; // a plausible `os migrate` run
112+
const durationMs = 531; // a plausible `os migrate` run, in milliseconds
103113

104114
// @ts-expect-error — a `number` is not a `CliExitCode`. This is exactly the
105115
// call `migrate/recorded-by.ts` and `migrate/resume.ts` used to make.
@@ -109,9 +119,16 @@ describe('emitJson / emitText — process.exitCode (#4873)', () => {
109119
// @ts-expect-error — same rejection at the call site, which is where it bit.
110120
await emitJson({ pending: 0, applied: false }, durationMs);
111121

112-
// And this is why the reported codes looked random rather than wrong: Node
113-
// truncates the exit status to 8 bits, so 531 leaves the process as 19.
122+
// The defect itself, reproduced: the duration lands in the exit-code slot
123+
// verbatim, on a run whose JSON was correct and whose stderr was empty.
114124
expect(process.exitCode).toBe(durationMs);
125+
126+
// And this is why the reported codes looked random rather than wrong: Node
127+
// truncates the exit status to 8 bits, so 531 leaves the process as 19 —
128+
// which is not one of the two codes this CLI defines, so every scripted
129+
// caller read a successful run as a failure it could not name.
115130
expect(durationMs & 0xff).toBe(19);
131+
const definedExitCodes = [0, 1]; // the whole of `CliExitCode`
132+
expect(definedExitCodes).not.toContain(durationMs & 0xff);
116133
});
117134
});

0 commit comments

Comments
 (0)