




















@@ -49,6 +49,7 @@ const probeGateway = vi.fn<
4949configSnapshot: unknown;
5050}>
5151>();
52+const callGatewayCli = vi.fn();
5253const isRestartEnabled = vi.fn<(config?: { commands?: unknown }) => boolean>(() => true);
5354const loadConfig = vi.hoisted(() => vi.fn(() => ({})));
5455const recoverInstalledLaunchAgent = vi.hoisted(() => vi.fn());
@@ -77,6 +78,10 @@ vi.mock("../../gateway/probe.js", () => ({
7778}) => probeGateway(opts),
7879}));
798081+vi.mock("../../gateway/call.js", () => ({
82+callGatewayCli: (opts: unknown) => callGatewayCli(opts),
83+}));
84+8085vi.mock("../../config/commands.js", () => ({
8186isRestartEnabled: (config?: { commands?: unknown }) => isRestartEnabled(config),
8287}));
@@ -113,7 +118,11 @@ vi.mock("./lifecycle-core.js", () => ({
113118114119describe("runDaemonRestart health checks", () => {
115120let runDaemonStart: (opts?: { json?: boolean }) => Promise<void>;
116-let runDaemonRestart: (opts?: { json?: boolean }) => Promise<boolean>;
121+let runDaemonRestart: (opts?: {
122+json?: boolean;
123+safe?: boolean;
124+force?: boolean;
125+}) => Promise<boolean>;
117126let runDaemonStop: (opts?: { json?: boolean }) => Promise<void>;
118127let envSnapshot: ReturnType<typeof captureEnv>;
119128@@ -162,6 +171,7 @@ describe("runDaemonRestart health checks", () => {
162171signalVerifiedGatewayPidSync.mockReset();
163172formatGatewayPidList.mockReset();
164173probeGateway.mockReset();
174+callGatewayCli.mockReset();
165175isRestartEnabled.mockReset();
166176loadConfig.mockReset();
167177recoverInstalledLaunchAgent.mockReset();
@@ -204,6 +214,31 @@ describe("runDaemonRestart health checks", () => {
204214ok: true,
205215configSnapshot: { commands: { restart: true } },
206216});
217+callGatewayCli.mockResolvedValue({
218+ok: true,
219+status: "deferred",
220+preflight: {
221+safe: false,
222+counts: {
223+queueSize: 1,
224+pendingReplies: 0,
225+embeddedRuns: 0,
226+activeTasks: 0,
227+totalActive: 1,
228+},
229+blockers: [{ kind: "queue", count: 1, message: "1 queued or active operation(s)" }],
230+summary: "restart deferred: 1 queued or active operation(s)",
231+},
232+restart: {
233+ok: true,
234+pid: 123,
235+signal: "SIGUSR1",
236+delayMs: 0,
237+mode: "emit",
238+coalesced: false,
239+cooldownMsApplied: 0,
240+},
241+});
207242isRestartEnabled.mockReturnValue(true);
208243signalVerifiedGatewayPidSync.mockImplementation(() => {});
209244formatGatewayPidList.mockImplementation((pids) => pids.join(", "));
@@ -230,6 +265,24 @@ describe("runDaemonRestart health checks", () => {
230265expect(recoverInstalledLaunchAgent).toHaveBeenCalledWith({ result: "started" });
231266});
232267268+it("requests a safe gateway restart over RPC without touching the service manager", async () => {
269+await runDaemonRestart({ json: true, safe: true });
270+271+expect(callGatewayCli).toHaveBeenCalledWith({
272+method: "gateway.restart.request",
273+params: { reason: "gateway.restart.safe" },
274+timeoutMs: 10_000,
275+});
276+expect(runServiceRestart).not.toHaveBeenCalled();
277+});
278+279+it("keeps force restart on the existing non-safe path", async () => {
280+await runDaemonRestart({ json: true, force: true });
281+282+expect(callGatewayCli).not.toHaveBeenCalled();
283+expect(runServiceRestart).toHaveBeenCalled();
284+});
285+233286it("repairs stale loaded service definitions from gateway start", async () => {
234287repairLoadedGatewayServiceForStart.mockResolvedValue({
235288result: "started",
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。