From 2d39c50ee635780304395e7ffe6a2c9f20ad2b78 Mon Sep 17 00:00:00 2001 From: Peter Steinberger Date: Sat, 14 Mar 2026 00:13:58 +0000 Subject: [PATCH] refactor: share daemon lifecycle restart helpers --- src/cli/daemon-cli/lifecycle-core.ts | 40 ++++++++++----------- src/cli/daemon-cli/lifecycle.test.ts | 52 ++++++++++++++-------------- 2 files changed, 44 insertions(+), 48 deletions(-) diff --git a/src/cli/daemon-cli/lifecycle-core.ts b/src/cli/daemon-cli/lifecycle-core.ts index a1ad4073584..8def6aeefe6 100644 --- a/src/cli/daemon-cli/lifecycle-core.ts +++ b/src/cli/daemon-cli/lifecycle-core.ts @@ -339,6 +339,22 @@ export async function runServiceRestart(params: { const { stdout, emit, fail } = createActionIO({ action: "restart", json }); const warnings: string[] = []; let handledNotLoaded: NotLoadedActionResult | null = null; + const emitScheduledRestart = ( + restartStatus: ReturnType, + serviceLoaded: boolean, + ) => { + emit({ + ok: true, + result: restartStatus.daemonActionResult, + message: restartStatus.message, + service: buildDaemonServiceSnapshot(params.service, serviceLoaded), + warnings: warnings.length ? warnings : undefined, + }); + if (!json) { + defaultRuntime.log(restartStatus.message); + } + return true; + }; const loaded = await resolveServiceLoadedOrFail({ serviceNoun: params.serviceNoun, @@ -423,34 +439,14 @@ export async function runServiceRestart(params: { } let restartStatus = describeGatewayServiceRestart(params.serviceNoun, restartResult); if (restartStatus.scheduled) { - emit({ - ok: true, - result: restartStatus.daemonActionResult, - message: restartStatus.message, - service: buildDaemonServiceSnapshot(params.service, loaded), - warnings: warnings.length ? warnings : undefined, - }); - if (!json) { - defaultRuntime.log(restartStatus.message); - } - return true; + return emitScheduledRestart(restartStatus, loaded); } if (params.postRestartCheck) { const postRestartResult = await params.postRestartCheck({ json, stdout, warnings, fail }); if (postRestartResult) { restartStatus = describeGatewayServiceRestart(params.serviceNoun, postRestartResult); if (restartStatus.scheduled) { - emit({ - ok: true, - result: restartStatus.daemonActionResult, - message: restartStatus.message, - service: buildDaemonServiceSnapshot(params.service, loaded), - warnings: warnings.length ? warnings : undefined, - }); - if (!json) { - defaultRuntime.log(restartStatus.message); - } - return true; + return emitScheduledRestart(restartStatus, loaded); } } } diff --git a/src/cli/daemon-cli/lifecycle.test.ts b/src/cli/daemon-cli/lifecycle.test.ts index 7d03656f86b..f026f81399f 100644 --- a/src/cli/daemon-cli/lifecycle.test.ts +++ b/src/cli/daemon-cli/lifecycle.test.ts @@ -99,6 +99,29 @@ describe("runDaemonRestart health checks", () => { let runDaemonRestart: (opts?: { json?: boolean }) => Promise; let runDaemonStop: (opts?: { json?: boolean }) => Promise; + function mockUnmanagedRestart({ + runPostRestartCheck = false, + }: { + runPostRestartCheck?: boolean; + } = {}) { + runServiceRestart.mockImplementation( + async (params: RestartParams & { onNotLoaded?: () => Promise }) => { + await params.onNotLoaded?.(); + if (runPostRestartCheck) { + await params.postRestartCheck?.({ + json: Boolean(params.opts?.json), + stdout: process.stdout, + warnings: [], + fail: (message: string) => { + throw new Error(message); + }, + }); + } + return true; + }, + ); + } + beforeAll(async () => { ({ runDaemonRestart, runDaemonStop } = await import("./lifecycle.js")); }); @@ -234,20 +257,7 @@ describe("runDaemonRestart health checks", () => { it("signals a single unmanaged gateway process on restart", async () => { findVerifiedGatewayListenerPidsOnPortSync.mockReturnValue([4200]); - runServiceRestart.mockImplementation( - async (params: RestartParams & { onNotLoaded?: () => Promise }) => { - await params.onNotLoaded?.(); - await params.postRestartCheck?.({ - json: Boolean(params.opts?.json), - stdout: process.stdout, - warnings: [], - fail: (message: string) => { - throw new Error(message); - }, - }); - return true; - }, - ); + mockUnmanagedRestart({ runPostRestartCheck: true }); await runDaemonRestart({ json: true }); @@ -262,12 +272,7 @@ describe("runDaemonRestart health checks", () => { it("fails unmanaged restart when multiple gateway listeners are present", async () => { findVerifiedGatewayListenerPidsOnPortSync.mockReturnValue([4200, 4300]); - runServiceRestart.mockImplementation( - async (params: RestartParams & { onNotLoaded?: () => Promise }) => { - await params.onNotLoaded?.(); - return true; - }, - ); + mockUnmanagedRestart(); await expect(runDaemonRestart({ json: true })).rejects.toThrow( "multiple gateway processes are listening on port 18789", @@ -281,12 +286,7 @@ describe("runDaemonRestart health checks", () => { configSnapshot: { commands: { restart: false } }, }); isRestartEnabled.mockReturnValue(false); - runServiceRestart.mockImplementation( - async (params: RestartParams & { onNotLoaded?: () => Promise }) => { - await params.onNotLoaded?.(); - return true; - }, - ); + mockUnmanagedRestart(); await expect(runDaemonRestart({ json: true })).rejects.toThrow( "Gateway restart is disabled in the running gateway config",