From f6cd49a630386696e5de4f6db307109a4c91b0be Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Pierzcha=C5=82a?= Date: Tue, 29 Sep 2026 15:37:59 +0200 Subject: [PATCH 1/2] test(daemon-client): a restart probe that fails outright near the RPC deadline reports the daemon unavailable --- .../__tests__/daemon-client-transport.test.ts | 57 +++++++++++++++++++ 1 file changed, 57 insertions(+) diff --git a/src/daemon-client/__tests__/daemon-client-transport.test.ts b/src/daemon-client/__tests__/daemon-client-transport.test.ts index 117ef21681..0a375ea360 100644 --- a/src/daemon-client/__tests__/daemon-client-transport.test.ts +++ b/src/daemon-client/__tests__/daemon-client-transport.test.ts @@ -256,6 +256,63 @@ test('a restart health probe cut short by the RPC deadline reports the deadline } }); +type RestartProbeFailure = 'http-503' | 'closed-connection' | 'refused-connection'; + +async function assertRestartProbeFailureReportsUnavailable( + t: Parameters[0], + failure: RestartProbeFailure, +) { + if (await skipWhenLoopbackUnavailable(t)) return; + let healthProbes = 0; + let rpcCount = 0; + const server = http.createServer((req, res) => { + if (req.url === '/health') { + healthProbes += 1; + if (failure === 'http-503') { + res.statusCode = 503; + res.end(); + } else { + req.socket.destroy(); + } + return; + } + rpcCount += 1; + res.statusCode = 409; + res.setHeader(DAEMON_HTTP_INSTANCE_MISMATCH_HEADER, 'true'); + res.setHeader('connection', 'close'); + res.end(); + if (failure === 'refused-connection') server.close(); + }); + try { + const port = await listenOnLoopback(server); + await assert.rejects(sendWithStaleInstance(port, 150), (error: unknown) => { + assert.ok(error instanceof AppError); + assert.equal(error.message, 'Remote daemon is unavailable'); + assert.equal(error.details?.daemonBaseUrl, `http://127.0.0.1:${port}`); + assert.notEqual(error.details?.reason, 'daemon_transport_timeout'); + return true; + }); + assert.equal(rpcCount, 1); + if (failure !== 'refused-connection') assert.equal(healthProbes, 1); + } finally { + await closeLoopbackServer(server); + } +} + +// Catches a mutation that marks every unreachable restart probe timedOut: a probe that fails +// outright inside the capped budget would then surface as an RPC timeout. +test('a restart health probe answering 503 near the RPC deadline reports the daemon unavailable', async (t) => { + await assertRestartProbeFailureReportsUnavailable(t, 'http-503'); +}); + +test('a restart health probe on a closed connection near the RPC deadline reports the daemon unavailable', async (t) => { + await assertRestartProbeFailureReportsUnavailable(t, 'closed-connection'); +}); + +test('a restart health probe refused near the RPC deadline reports the daemon unavailable', async (t) => { + await assertRestartProbeFailureReportsUnavailable(t, 'refused-connection'); +}); + test('proxy forwards cached upstream identity and rejects a restarted upstream before dispatch', async (t) => { if (await skipWhenLoopbackUnavailable(t)) return; let upstreamInstance = 'upstream-one'; From 00833fba65c21ad01000766eea1e555dd56180c5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Pierzcha=C5=82a?= Date: Tue, 29 Sep 2026 15:42:02 +0200 Subject: [PATCH 2/2] test(daemon-client): document how the refused-probe case proves the probe attempt --- src/daemon-client/__tests__/daemon-client-transport.test.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/daemon-client/__tests__/daemon-client-transport.test.ts b/src/daemon-client/__tests__/daemon-client-transport.test.ts index 0a375ea360..f5e85cd068 100644 --- a/src/daemon-client/__tests__/daemon-client-transport.test.ts +++ b/src/daemon-client/__tests__/daemon-client-transport.test.ts @@ -293,6 +293,8 @@ async function assertRestartProbeFailureReportsUnavailable( return true; }); assert.equal(rpcCount, 1); + // A closed listener cannot count the probe; its attempt is proven by elimination: a skipped + // probe would exhaust the budget and report daemon_transport_timeout, which the error check rejects. if (failure !== 'refused-connection') assert.equal(healthProbes, 1); } finally { await closeLoopbackServer(server);