Skip to content

Commit f2cb59f

Browse files
os-zhuangclaude
andauthored
test(spec): let the sdui collision harness OWN the port it calls busy (#10456)
`BUSY_HELD=no` / `BUSY_PORT=5180` / `PICKED_WITH_BUSY=5181` reddened three unrelated PRs in one afternoon. The picker was right every time; the harness could not guarantee its own precondition. The occupier took its port from `sdui_pick_free_port` and bound it afterwards. A reservation is not a bind, so the port stays takeable in that gap — and `port_held`, spawned milliseconds later, binds and closes that very port. Measured over 80 trials on an idle container: the probe won the bind 39 times, and once the occupier's bind landed inside the probe's hold window and the occupier exited. No external holder of 5180 is required, which is why none was ever identified. Both occupiers (case 2 and case 7) now bind `:0` and report back the port the kernel gave them, so the port is held before it is named. Second, independent half: sourcing `gen-sdui-manifest.sh` (`set -euo pipefail` at its top) turned errexit back ON in the harness, overriding its own `set -uo pipefail`. The failing cleanup `kill` of a dead occupier then aborted the harness mid-measurement, `execFileSync` threw in the `describe` body, and vitest reported `0 test` against a bare "Command failed". `set +e` after the source and a final `exit 0` make a lost precondition fail as an assertion, with the whole `seen` map printed beside it. `expect(seen.BUSY_HELD).toBe('yes')` is untouched, both probes still address 127.0.0.1, and case 7 still covers a holder foreign to the registry. A third sequential pick (RPORT) replaces BUSY_PORT in the "distinct picks in one run" set, which would otherwise compare an ephemeral port against the 5180 band and pass for free. Claude-Session: https://claude.ai/code/session_01DdCnBGcHeufjrq7drTD3wt Co-authored-by: Claude <noreply@anthropic.com>
1 parent f094214 commit f2cb59f

1 file changed

Lines changed: 116 additions & 21 deletions

File tree

‎packages/spec/scripts/gen-sdui-manifest-collision.test.ts‎

Lines changed: 116 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,50 @@
6969
// BUSY_HELD is asserted before PICKED_WITH_BUSY is believed: a precondition
7070
// that can evaporate silently is a test that accuses the wrong function.
7171
//
72+
// ## The precondition needed an OWNED port, not a picked one — measured
73+
//
74+
// That paragraph was still only half of it, and the other half reddened three
75+
// unrelated PRs in one afternoon, byte-identical every time:
76+
//
77+
// BUSY_HELD=no
78+
// BUSY_PORT=5180
79+
// PICKED_WITH_BUSY=5181
80+
//
81+
// `PICKED_WITH_BUSY=5181` is the picker answering CORRECTLY — asked to avoid
82+
// 5180 it returned 5181. `BUSY_HELD=no` alone is the failure, and the thief was
83+
// this file. The occupier used to bind the port `sdui_pick_free_port` had just
84+
// handed it, and a RESERVATION IS NOT A BIND: the port stays takeable in the gap
85+
// between the pick returning and the occupier binding. `port_held` below binds
86+
// and closes that exact port, and it is spawned within milliseconds of the
87+
// occupier. Measured, 80 trials on an idle container:
88+
//
89+
// probe won the bind 39/80 # the two genuinely race
90+
// occupier died 1/80 # its bind landed inside the probe's hold
91+
//
92+
// Nothing outside this file has to hold 5180 for that to fire, which is why no
93+
// one could name the process that did. So the occupier binds `:0` and reports
94+
// back the port the kernel gave it: it is already holding that port when it
95+
// names it, which is the property BUSY_HELD asserts, and no gap is left.
96+
//
97+
// ## Why `set +e` follows the `source`, and why the harness ends `exit 0`
98+
//
99+
// `scripts/gen-sdui-manifest.sh` is `set -euo pipefail` at its top, so SOURCING
100+
// it turns errexit back ON here, overriding the `set -uo pipefail` written one
101+
// line earlier. Measured rather than read: `case "$-" in *e*)` after the source
102+
// reports `e` set. That is what truncated the three captures above. When the
103+
// occupier had already exited, the cleanup `kill` returned 1, errexit aborted
104+
// the harness on that line, `execFileSync` threw in the `describe` body, and
105+
// vitest reported `0 test` and a bare `Error: Command failed: bash
106+
// /tmp/sdui-collision-*/harness.sh` — every assertion that would have named the
107+
// problem pre-empted, the diagnosis surviving only because stdout rides along
108+
// on the serialized error. Reproduced here: forcing the occupier to lose its
109+
// bind stopped the harness dead after `PICKED_WITH_BUSY`, exit 1, exactly the
110+
// captured shape. So errexit is turned off again AFTER the source, and the
111+
// harness exits 0 unconditionally: its exit status is not a measurement. Every
112+
// measurement is a printed KEY=VALUE line and the assertions below grade those,
113+
// so a precondition that fails now fails AS AN ASSERTION, printing the whole
114+
// `seen` map with it.
115+
//
72116
// No vite and no console build: the contract under test is one the shell script
73117
// owns, and the ports are picked at run time by the script's own helper so this
74118
// test cannot collide with a concurrent agent — which would be a poor look here.
@@ -103,24 +147,31 @@ const HTTP_STUB = [
103147
'const s = http.createServer((_q, r) => { r.writeHead(200); r.end("SERVER"); });',
104148
// Losing the bind must EXIT, not throw: an unhandled 'error' event kills the
105149
// harness with a stack trace where a vacuity guard would have named the
106-
// problem. See RAW_LISTENER below for the same reasoning.
150+
// problem. See OWNED_LISTENER below for the same reasoning.
107151
's.once("error", () => process.exit(1));',
108152
's.listen(Number(process.env.SDUI_TEST_PORT), "127.0.0.1");',
109153
].join('');
110154

111155
/**
112-
* A bare TCP listener on $SDUI_TEST_PORT that reports a lost bind by exiting.
156+
* A bare TCP listener that binds an EPHEMERAL port and prints the one it got.
157+
*
158+
* Bind first, name second. Asking the picker for a port and binding it
159+
* afterwards leaves a window in which anything at all — including `port_held`
160+
* below, measured — can take it, and losing there evaporates the precondition
161+
* the BUSY/STEAL assertions rest on. Port 0 closes the window by construction:
162+
* the number is written from inside the `listening` callback, so by the time
163+
* the harness can read it the socket is already held.
113164
*
114-
* The unguarded form of this line is what made the merge-queue failure this
115-
* file now pins so hard to read: when a concurrent scanner took the port
116-
* first, node threw `Unhandled 'error' event ... EADDRINUSE 127.0.0.1:5180`,
117-
* the harness died mid-measurement, and the report was a stack trace instead
118-
* of "the port this test meant to occupy was never occupied".
165+
* The error guard stays. It is near-unreachable on `:0`, but the unguarded form
166+
* of this line is what made the merge-queue failure this file pins so hard to
167+
* read: node threw `Unhandled 'error' event ... EADDRINUSE 127.0.0.1:5180`, the
168+
* harness died mid-measurement, and the report was a stack trace instead of
169+
* "the port this test meant to occupy was never occupied".
119170
*/
120-
const RAW_LISTENER = [
171+
const OWNED_LISTENER = [
121172
'const s = require("node:net").createServer();',
122173
's.once("error", () => process.exit(1));',
123-
's.listen(Number(process.env.SDUI_TEST_PORT), "127.0.0.1");',
174+
's.listen(0, "127.0.0.1", () => console.log(s.address().port));',
124175
].join('');
125176

126177
function runHarness(): Record<string, string> {
@@ -137,6 +188,11 @@ function runHarness(): Record<string, string> {
137188
// Sourcing runs no generation — the script returns right after defining
138189
// its helpers, so this exercises the REAL functions.
139190
`source ${JSON.stringify(SCRIPT)}`,
191+
// …but the file just sourced opens with `set -euo pipefail`, so the source
192+
// turns errexit back ON here and silently overrides the line above. That
193+
// is what turned a failed precondition into a harness crash and a `0 test`
194+
// report; see the header. Off again, after the source, where it sticks.
195+
'set +e',
140196
`DIR=${JSON.stringify(dir)}`,
141197
'NODE_BIN="$(command -v node)"',
142198
// "is $1 held right now?" — the same probe shape the script uses, so a
@@ -150,22 +206,42 @@ function runHarness(): Record<string, string> {
150206
'printf "DEV_ARGV=%s\\n" "${ARGV[*]}"',
151207
'',
152208
'# ── 2. the free-port search skips a port that is taken right now ────',
153-
'BUSY="$(sdui_pick_free_port 5180)"',
154-
'export SDUI_TEST_PORT="$BUSY"',
155-
`"$NODE_BIN" -e ${JSON.stringify(RAW_LISTENER)} &`,
209+
// The busy port is the one the occupier BOUND, not one the picker handed
210+
// it: a reservation is not a bind, and the gap between the two is what
211+
// `port_held` walked through. See the header for the 80-trial count.
212+
'BUSY_FILE="$DIR/busy.port"',
213+
`"$NODE_BIN" -e ${JSON.stringify(OWNED_LISTENER)} > "$BUSY_FILE" &`,
156214
'BUSY_PID=$!',
157215
// disown: otherwise bash prints its own "Killed" job notice when this is
158216
// reaped below, which reads like a test failure in the vitest output.
159217
'disown "$BUSY_PID" 2>/dev/null || true',
160-
'for _ in $(seq 1 40); do [ "$(port_held "$BUSY")" = yes ] && break; sleep 0.25; done',
218+
// The port appears only once the socket is bound, so waiting for the
219+
// number IS waiting for the hold — there is no second thing to wait for.
220+
'BUSY=""',
221+
'for _ in $(seq 1 40); do BUSY="$(tr -d "[:space:]" < "$BUSY_FILE" 2>/dev/null || true)"; [ -n "$BUSY" ] && break; sleep 0.25; done',
161222
// Vacuity guard, and the one this file was missing when it ejected a PR:
162223
// if the occupier never took the port, the pick below returns it and the
163-
// green/red says nothing about the picker.
224+
// green/red says nothing about the picker. Nothing can lose the port to a
225+
// racer any more; the guard stays because it is what would make the next
226+
// way of losing it legible instead of an accusation of the picker.
164227
'printf "BUSY_HELD=%s\\n" "$(port_held "$BUSY")"',
165228
'printf "BUSY_PORT=%s\\n" "$BUSY"',
229+
// An ephemeral base does not risk scanning off the end of the port space:
230+
// the first candidate is the port we hold, the second is free, and the
231+
// scan returns there rather than walking its 200-port span upward.
166232
'printf "PICKED_WITH_BUSY=%s\\n" "$(sdui_pick_free_port "$BUSY")"',
167233
'kill -KILL "$BUSY_PID" 2>/dev/null',
168234
'',
235+
'# ── 2b. a third sequential pick from the shared base ────────────────',
236+
// Case 2 used to contribute one of these as a by-product, back when its
237+
// occupier took its port from the picker. It owns an ephemeral port now,
238+
// so the third pick is taken explicitly: without it the "each pick within
239+
// one run its own port" assertion compares two picked ports against one
240+
// ephemeral one, and those differ whatever the registry does — a guard
241+
// that would go green for a reason unrelated to what it guards.
242+
'RPORT="$(sdui_pick_free_port 5180)"',
243+
'printf "RPORT=%s\\n" "$RPORT"',
244+
'',
169245
'# ── 3. a NEIGHBOUR answering on our port is refused, not accepted ───',
170246
'NPORT="$(sdui_pick_free_port 5180)"',
171247
'printf "NPORT=%s\\n" "$NPORT"',
@@ -232,15 +308,21 @@ function runHarness(): Record<string, string> {
232308
// the registry. Against everything else the probe is still the answer,
233309
// and losing there must not leak the claim — a hoarded claim would cost
234310
// a port on every future run in this container.
235-
'SPORT="$(sdui_pick_free_port 5180)"',
236-
'export SDUI_TEST_PORT="$SPORT"',
237-
`"$NODE_BIN" -e ${JSON.stringify(RAW_LISTENER)} &`,
311+
// Bound, then named — the same precondition case 2 needs, and the same
312+
// way of guaranteeing it. STEAL_HELD is not a weaker assertion than
313+
// BUSY_HELD and had no business resting on a weaker mechanism.
314+
'STEAL_FILE="$DIR/steal.port"',
315+
`"$NODE_BIN" -e ${JSON.stringify(OWNED_LISTENER)} > "$STEAL_FILE" &`,
238316
'STEAL_PID=$!',
239317
'disown "$STEAL_PID" 2>/dev/null || true',
240-
'for _ in $(seq 1 40); do [ "$(port_held "$SPORT")" = yes ] && break; sleep 0.25; done',
318+
'SPORT=""',
319+
'for _ in $(seq 1 40); do SPORT="$(tr -d "[:space:]" < "$STEAL_FILE" 2>/dev/null || true)"; [ -n "$SPORT" ] && break; sleep 0.25; done',
241320
'printf "STEAL_HELD=%s\\n" "$(port_held "$SPORT")"',
242-
// A registry of its own, so the listener above is genuinely foreign to
243-
// it — the shared registry already knows this port is ours.
321+
// A registry of its own, so the claim files the two lines below read are
322+
// written by this leg and nothing else. The holder is foreign to that
323+
// registry either way, and more plainly than before: a port bound
324+
// directly from the ephemeral range was never claimed in any registry,
325+
// which is exactly the non-participant this case exists to cover.
244326
'RESV="$DIR/resv"',
245327
'STEAL_PICK="$( (export SDUI_PORT_RESERVATION_DIR="$RESV"; sdui_pick_free_port "$SPORT") )"',
246328
'printf "STEAL_PORT=%s\\n" "$SPORT"',
@@ -253,6 +335,15 @@ function runHarness(): Record<string, string> {
253335
// half that can only be true when the registry is real.
254336
'printf "STEAL_CLAIM_ON_PICK=%s\\n" "$([ -e "$RESV/$STEAL_PICK" ] && echo yes || echo no)"',
255337
'kill -KILL "$STEAL_PID" 2>/dev/null',
338+
'',
339+
// Grading belongs to the assertions, not to whatever the last cleanup
340+
// returned. Reaping an occupier that already exited fails, and under the
341+
// errexit this file used to inherit that failure ended the harness where
342+
// it stood; `execFileSync` then threw in the `describe` body and vitest
343+
// reported `0 test` against a bare "Command failed". Exit 0 and let the
344+
// KEY=VALUE lines above be the evidence — a red then names its assertion
345+
// and prints the whole `seen` map beside it.
346+
'exit 0',
256347
].join('\n'),
257348
{ mode: 0o755 },
258349
);
@@ -311,7 +402,11 @@ describe.skipIf(!RUNNABLE)('gen-sdui-manifest.sh concurrent-run contract', () =>
311402
});
312403

313404
it('gives each pick within one run its own port', () => {
314-
const picked = [seen.BUSY_PORT, seen.NPORT, seen.OPORT];
405+
// Three ports the PICKER handed out, from one base, in one run. BUSY_PORT
406+
// is deliberately not among them any more: the occupier binds an ephemeral
407+
// port of its own, and an ephemeral port differs from the 5180 band however
408+
// the registry behaves, so counting it here would be free.
409+
const picked = [seen.NPORT, seen.OPORT, seen.RPORT];
315410
expect(picked.every((p) => /^\d+$/.test(p ?? '')), picked.join(',')).toBe(true);
316411
expect(new Set(picked).size, picked.join(',')).toBe(3);
317412
});

0 commit comments

Comments
 (0)