diff --git a/src/cli/system-cli.test.ts b/src/cli/system-cli.test.ts index b492dd7400a6..763356504178 100644 --- a/src/cli/system-cli.test.ts +++ b/src/cli/system-cli.test.ts @@ -76,15 +76,52 @@ describe("system-cli", () => { expect(runtimeLogs).toEqual([]); expect(runtimeErrors[0]).toContain("unwakeable-session-key"); + expect(defaultRuntime.writeJson).not.toHaveBeenCalled(); + expect(defaultRuntime.exit).toHaveBeenCalledWith(1); }); it("handles invalid wake mode as runtime error", async () => { await runCli(["system", "event", "--text", "hello", "--mode", "later"]); expect(callGatewayFromCli).not.toHaveBeenCalled(); + expect(runtimeLogs).toEqual([]); expect(runtimeErrors[0]).toContain("--mode must be now or next-heartbeat"); + expect(defaultRuntime.writeJson).not.toHaveBeenCalled(); + expect(defaultRuntime.exit).toHaveBeenCalledWith(1); }); + it.each([ + { + name: "invalid wake mode", + args: ["system", "event", "--text", "hello", "--mode", "later", "--json"], + gatewayResult: undefined, + expectedError: "Error: --mode must be now or next-heartbeat", + gatewayCalls: 0, + }, + { + name: "rejected Gateway call", + args: ["system", "event", "--text", "hello", "--json"], + gatewayResult: { ok: false, reason: "unwakeable-session-key" }, + expectedError: "Error: unwakeable-session-key", + gatewayCalls: 1, + }, + ])( + "writes JSON when $name fails", + async ({ args, gatewayResult, expectedError, gatewayCalls }) => { + if (gatewayResult) { + callGatewayFromCli.mockResolvedValueOnce(gatewayResult); + } + + await runCli(args); + + expect(runtimeLogs).toEqual([JSON.stringify({ error: expectedError }, null, 2)]); + expect(runtimeErrors).toEqual([]); + expect(defaultRuntime.writeJson).toHaveBeenCalledWith({ error: expectedError }); + expect(defaultRuntime.exit).toHaveBeenCalledWith(1); + expect(callGatewayFromCli).toHaveBeenCalledTimes(gatewayCalls); + }, + ); + it("forwards --session-key on system event", async () => { await runCli([ "system", @@ -123,6 +160,24 @@ describe("system-cli", () => { expect(params).not.toHaveProperty("sessionKey"); }); + it("writes JSON when an implicit machine-output command fails", async () => { + callGatewayFromCli.mockRejectedValueOnce(new Error("Gateway unavailable")); + + await runCli(["system", "heartbeat", "last"]); + + expect(callGatewayFromCli).toHaveBeenCalledTimes(1); + const [method, gatewayOptions, params, requestOptions] = gatewayCall(); + expect(method).toBe("last-heartbeat"); + expect(typeof gatewayOptions).toBe("object"); + expect(params).toBeUndefined(); + expect(requestOptions).toEqual({ expectFinal: false }); + const expectedError = "Error: Gateway unavailable"; + expect(runtimeLogs).toEqual([JSON.stringify({ error: expectedError }, null, 2)]); + expect(runtimeErrors).toEqual([]); + expect(defaultRuntime.writeJson).toHaveBeenCalledWith({ error: expectedError }); + expect(defaultRuntime.exit).toHaveBeenCalledWith(1); + }); + it.each([ { args: ["system", "heartbeat", "last"], method: "last-heartbeat", params: undefined }, { diff --git a/src/cli/system-cli.ts b/src/cli/system-cli.ts index cfada786e343..8b54041de5c5 100644 --- a/src/cli/system-cli.ts +++ b/src/cli/system-cli.ts @@ -35,15 +35,20 @@ async function runSystemGatewayCommand( action: () => Promise, successText?: string, ): Promise { + const machineOutput = opts.json || successText === undefined; try { const result = await action(); - if (opts.json || successText === undefined) { + if (machineOutput) { defaultRuntime.writeJson(result); } else { defaultRuntime.log(successText); } } catch (err) { - defaultRuntime.error(danger(String(err))); + if (machineOutput) { + defaultRuntime.writeJson({ error: String(err) }); + } else { + defaultRuntime.error(danger(String(err))); + } defaultRuntime.exit(1); } }