|
5 | 5 |
|
6 | 6 | import { test } from "node:test"; |
7 | 7 | import assert from "node:assert/strict"; |
| 8 | +import { spawnSync } from "node:child_process"; |
8 | 9 | import { |
| 10 | + GH_MAX_BUFFER, |
9 | 11 | SWEEP_LABELS, |
10 | 12 | cell, |
11 | 13 | currentMilestone, |
@@ -176,6 +178,34 @@ test("gh throws on a spawn error, and passes stdin only when given", () => { |
176 | 178 | assert.equal(calls[1].input, "body"); |
177 | 179 | }); |
178 | 180 |
|
| 181 | +// The real spawnSync, with "gh" swapped for a node that writes `bytes` of |
| 182 | +// stdout, so the options gh() passes are the ones that decide the outcome. |
| 183 | +const bigReply = (bytes) => (_cmd, _args, opts) => |
| 184 | + spawnSync( |
| 185 | + process.execPath, |
| 186 | + ["-e", `process.stdout.write("x".repeat(${bytes}))`], |
| 187 | + opts, |
| 188 | + ); |
| 189 | + |
| 190 | +test("gh reads a reply over Node's 1 MiB default without ENOBUFS (#5339)", () => { |
| 191 | + const bytes = 2 * 1024 * 1024; |
| 192 | + assert.ok(GH_MAX_BUFFER > bytes); |
| 193 | + const result = gh(bigReply(bytes), ["api", "x"]); |
| 194 | + assert.equal(result.status, 0); |
| 195 | + assert.equal(result.stdout.length, bytes); |
| 196 | +}); |
| 197 | + |
| 198 | +test("gh still rethrows a genuine spawn error from the real spawnSync", () => { |
| 199 | + assert.throws( |
| 200 | + () => |
| 201 | + gh( |
| 202 | + (_cmd, args, opts) => spawnSync("/nonexistent/gh-5339", args, opts), |
| 203 | + ["api", "x"], |
| 204 | + ), |
| 205 | + (err) => err.code === "ENOENT", |
| 206 | + ); |
| 207 | +}); |
| 208 | + |
179 | 209 | test("currentMilestone reads open milestones and fails loudly", () => { |
180 | 210 | const { spawn, calls } = fakeSpawn(() => ({ |
181 | 211 | status: 0, |
|
0 commit comments