@@ -256,6 +256,65 @@ test('a restart health probe cut short by the RPC deadline reports the deadline
256256 }
257257} ) ;
258258
259+ type RestartProbeFailure = 'http-503' | 'closed-connection' | 'refused-connection' ;
260+
261+ async function assertRestartProbeFailureReportsUnavailable (
262+ t : Parameters < typeof skipWhenLoopbackUnavailable > [ 0 ] ,
263+ failure : RestartProbeFailure ,
264+ ) {
265+ if ( await skipWhenLoopbackUnavailable ( t ) ) return ;
266+ let healthProbes = 0 ;
267+ let rpcCount = 0 ;
268+ const server = http . createServer ( ( req , res ) => {
269+ if ( req . url === '/health' ) {
270+ healthProbes += 1 ;
271+ if ( failure === 'http-503' ) {
272+ res . statusCode = 503 ;
273+ res . end ( ) ;
274+ } else {
275+ req . socket . destroy ( ) ;
276+ }
277+ return ;
278+ }
279+ rpcCount += 1 ;
280+ res . statusCode = 409 ;
281+ res . setHeader ( DAEMON_HTTP_INSTANCE_MISMATCH_HEADER , 'true' ) ;
282+ res . setHeader ( 'connection' , 'close' ) ;
283+ res . end ( ) ;
284+ if ( failure === 'refused-connection' ) server . close ( ) ;
285+ } ) ;
286+ try {
287+ const port = await listenOnLoopback ( server ) ;
288+ await assert . rejects ( sendWithStaleInstance ( port , 150 ) , ( error : unknown ) => {
289+ assert . ok ( error instanceof AppError ) ;
290+ assert . equal ( error . message , 'Remote daemon is unavailable' ) ;
291+ assert . equal ( error . details ?. daemonBaseUrl , `http://127.0.0.1:${ port } ` ) ;
292+ assert . notEqual ( error . details ?. reason , 'daemon_transport_timeout' ) ;
293+ return true ;
294+ } ) ;
295+ assert . equal ( rpcCount , 1 ) ;
296+ // A closed listener cannot count the probe; its attempt is proven by elimination: a skipped
297+ // probe would exhaust the budget and report daemon_transport_timeout, which the error check rejects.
298+ if ( failure !== 'refused-connection' ) assert . equal ( healthProbes , 1 ) ;
299+ } finally {
300+ await closeLoopbackServer ( server ) ;
301+ }
302+ }
303+
304+ // Catches a mutation that marks every unreachable restart probe timedOut: a probe that fails
305+ // outright inside the capped budget would then surface as an RPC timeout.
306+ test ( 'a restart health probe answering 503 near the RPC deadline reports the daemon unavailable' , async ( t ) => {
307+ await assertRestartProbeFailureReportsUnavailable ( t , 'http-503' ) ;
308+ } ) ;
309+
310+ test ( 'a restart health probe on a closed connection near the RPC deadline reports the daemon unavailable' , async ( t ) => {
311+ await assertRestartProbeFailureReportsUnavailable ( t , 'closed-connection' ) ;
312+ } ) ;
313+
314+ test ( 'a restart health probe refused near the RPC deadline reports the daemon unavailable' , async ( t ) => {
315+ await assertRestartProbeFailureReportsUnavailable ( t , 'refused-connection' ) ;
316+ } ) ;
317+
259318test ( 'proxy forwards cached upstream identity and rejects a restarted upstream before dispatch' , async ( t ) => {
260319 if ( await skipWhenLoopbackUnavailable ( t ) ) return ;
261320 let upstreamInstance = 'upstream-one' ;
0 commit comments