@@ -5,7 +5,8 @@ import { request as httpRequest } from "node:http";
55import { tmpdir } from "node:os" ;
66import { join } from "node:path" ;
77import { saveCodexAccountCredential } from "../src/codex/account-store" ;
8- import { getTrackedCodexWebSocketCountForAccount } from "../src/codex/websocket-registry" ;
8+ import { clearCodexWebSocketRegistry , getTrackedCodexWebSocketCountForAccount } from "../src/codex/websocket-registry" ;
9+ import { INTERNAL_DEADLINE_MS , SERVER_BUDGET_MS } from "./helpers/test-budget" ;
910import { clearAccountNeedsReauth , clearAccountQuota , getAccountQuota , isAccountNeedsReauth , markAccountNeedsReauth , updateAccountQuota } from "../src/codex/auth-api" ;
1011import {
1112 CODEX_THREAD_AFFINITY_IDLE_TTL_MS ,
@@ -162,6 +163,12 @@ async function startPoolRetryHarness(
162163 clearAccountQuota ( ) ;
163164 clearAccountNeedsReauth ( "pool-a" ) ;
164165 clearAccountNeedsReauth ( "pool-b" ) ;
166+ // The registry is process-global and survives a harness teardown. WS-REBIND-01
167+ // asserts exact per-account socket counts, so a socket leaked by any earlier test
168+ // in this file shifts its snapshots and fails it in milliseconds — which reads as
169+ // a flake next to the timeouts, but is ordinary shared state. Reset it with the
170+ // rest rather than leaving one of six kinds of state uncleaned.
171+ clearCodexWebSocketRegistry ( ) ;
165172
166173 const dispatches : string [ ] = [ ] ;
167174 const upstream = Bun . serve ( {
@@ -1288,7 +1295,7 @@ describe("server local API auth", () => {
12881295 if ( globalThis . fetch === matrixFetch ) globalThis . fetch = originalGlobalFetch ;
12891296 await upstream . stop ( true ) ;
12901297 }
1291- } , { timeout : 30_000 } ) ;
1298+ } , { timeout : SERVER_BUDGET_MS } ) ;
12921299
12931300 test ( "internal web-search and vision never forward a non-ChatGPT bearer as Direct sidecar auth" , async ( ) => {
12941301 if ( existsSync ( TEST_DIR ) ) rmSync ( TEST_DIR , { recursive : true } ) ;
@@ -1984,15 +1991,18 @@ describe("server local API auth", () => {
19841991 `${ exact } suffix` ,
19851992 exact . replace ( "ChatGPT account." , "ChatGPT account" ) ,
19861993 ] ;
1987- for ( const detail of cases ) {
1988- const body = JSON . stringify ( { detail } ) ;
1989- const harness = await startPoolRetryHarness ( ( ) => rejectionResponse ( body ) ) ;
1990- try {
1991- await expectOriginal400 ( await harness . request ( ) , body ) ;
1992- expect ( harness . dispatches ) . toEqual ( [ "acct-pool-a" ] ) ;
1993- } finally {
1994- await stopPoolRetryHarness ( harness ) ;
1994+ const negativeBodies = cases . map ( detail => JSON . stringify ( { detail } ) ) ;
1995+ let nextNegative = 0 ;
1996+ const negative = await startPoolRetryHarness ( ( ) => rejectionResponse ( negativeBodies [ nextNegative ++ ] ! ) ) ;
1997+ try {
1998+ for ( const body of negativeBodies ) {
1999+ const priorDispatches = negative . dispatches . length ;
2000+ await expectOriginal400 ( await negative . request ( ) , body ) ;
2001+ expect ( negative . dispatches . slice ( priorDispatches ) ) . toEqual ( [ "acct-pool-a" ] ) ;
19952002 }
2003+ expect ( nextNegative ) . toBe ( negativeBodies . length ) ;
2004+ } finally {
2005+ await stopPoolRetryHarness ( negative ) ;
19962006 }
19972007 const positive = await startPoolRetryHarness ( accountId => accountId === "acct-pool-a"
19982008 ? rejectionResponse ( JSON . stringify ( { detail : ` THE '${ POOL_RETRY_MODEL } ' MODEL IS NOT SUPPORTED\nWHEN USING CODEX WITH A CHATGPT ACCOUNT. ` } ) )
@@ -2006,14 +2016,23 @@ describe("server local API auth", () => {
20062016 } , 12_000 ) ;
20072017
20082018 test ( "valid JSON wrong top-level shape never authorizes a pool retry" , async ( ) => {
2009- for ( const body of [ '"string"' , "42" , "true" , "null" , '["detail"]' ] ) {
2010- const harness = await startPoolRetryHarness ( ( ) => rejectionResponse ( body ) ) ;
2011- try {
2019+ // One harness, five bodies — same reason as the sibling above. Each
2020+ // startPoolRetryHarness() wipes and recreates TEST_DIR, binds a server, and
2021+ // redirects global fetch; five of those did not fit Bun's 5s default on a
2022+ // Windows runner, and the request still in flight when the budget expired
2023+ // raced the next test through that same global fetch.
2024+ const bodies = [ '"string"' , "42" , "true" , "null" , '["detail"]' ] ;
2025+ let nextBody = 0 ;
2026+ const harness = await startPoolRetryHarness ( ( ) => rejectionResponse ( bodies [ nextBody ++ ] ! ) ) ;
2027+ try {
2028+ for ( const body of bodies ) {
2029+ const priorDispatches = harness . dispatches . length ;
20122030 await expectOriginal400 ( await harness . request ( ) , body ) ;
2013- expect ( harness . dispatches ) . toEqual ( [ "acct-pool-a" ] ) ;
2014- } finally {
2015- await stopPoolRetryHarness ( harness ) ;
2031+ expect ( harness . dispatches . slice ( priorDispatches ) ) . toEqual ( [ "acct-pool-a" ] ) ;
20162032 }
2033+ expect ( nextBody ) . toBe ( bodies . length ) ;
2034+ } finally {
2035+ await stopPoolRetryHarness ( harness ) ;
20172036 }
20182037 } ) ;
20192038
@@ -2085,7 +2104,13 @@ describe("server local API auth", () => {
20852104 if ( String ( event . data ) . includes ( "response.completed" ) ) resolve ( ) ;
20862105 } ) ;
20872106 ws . addEventListener ( "error" , ( ) => reject ( new Error ( "websocket retry failed" ) ) , { once : true } ) ;
2088- setTimeout ( ( ) => reject ( new Error ( "websocket retry timed out" ) ) , 1_000 ) ;
2107+ // This 1s was the real cause of WS-REBIND-01 failing at 748ms on windows-latest:
2108+ // an internal deadline, not Bun's test budget, which is why it died far too fast
2109+ // to look like a timeout. The test stops and restarts a real server, opens a real
2110+ // WebSocket, and waits for a two-hop retry across accounts — a second of that on a
2111+ // contended runner is optimistic. The assertions below are unchanged; only the
2112+ // room to reach them grew.
2113+ setTimeout ( ( ) => reject ( new Error ( "websocket retry timed out" ) ) , INTERNAL_DEADLINE_MS ) ;
20892114 } ) ;
20902115 expect ( harness . dispatches ) . toEqual ( [ "acct-pool-a" , "acct-pool-b" ] ) ;
20912116 expect ( registrySnapshots ) . toEqual ( [ [ 1 , 0 ] , [ 0 , 1 ] ] ) ;
@@ -2095,7 +2120,7 @@ describe("server local API auth", () => {
20952120 ws . close ( ) ;
20962121 await stopPoolRetryHarness ( harness ) ;
20972122 }
2098- } ) ;
2123+ } , { timeout : 30_000 } ) ;
20992124
21002125 test ( "passthrough connect failure records selected pool account health" , async ( ) => {
21012126 if ( existsSync ( TEST_DIR ) ) rmSync ( TEST_DIR , { recursive : true } ) ;
0 commit comments