mirror of
https://github.com/openclaw/openclaw.git
synced 2026-08-27 04:47:03 -06:00
fix(cli): make commands, completion, and JSON output reliable (#116033)
* fix(cli): make commands, completion, and JSON output reliable * fix(cli): reconcile completion coverage with current main * test(cli): keep test routing stable across isolation lanes
This commit is contained in:
committed by
GitHub
parent
4645d4a487
commit
433bb3f954
@@ -46,6 +46,9 @@ describe("runDaemonStatus", () => {
|
||||
beforeEach(() => {
|
||||
gatherDaemonStatus.mockClear();
|
||||
printDaemonStatus.mockClear();
|
||||
defaultRuntime.error.mockClear();
|
||||
defaultRuntime.exit.mockClear();
|
||||
defaultRuntime.writeJson.mockClear();
|
||||
resetRuntimeCapture();
|
||||
});
|
||||
|
||||
@@ -79,6 +82,7 @@ describe("runDaemonStatus", () => {
|
||||
json: false,
|
||||
deep: false,
|
||||
});
|
||||
expect(defaultRuntime.exit).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it("forwards require-rpc to daemon status gathering", async () => {
|
||||
@@ -111,5 +115,81 @@ describe("runDaemonStatus", () => {
|
||||
expect(runtimeErrors[0]).toBe(
|
||||
"Gateway status failed: --require-rpc needs probing enabled. Remove --no-probe or drop --require-rpc.",
|
||||
);
|
||||
expect(defaultRuntime.exit).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it("renders disabled-probe validation failures as JSON in JSON mode", async () => {
|
||||
await expect(
|
||||
runDaemonStatus({
|
||||
rpc: {},
|
||||
probe: false,
|
||||
requireRpc: true,
|
||||
json: true,
|
||||
}),
|
||||
).rejects.toThrow("__exit__:1");
|
||||
|
||||
expect(gatherDaemonStatus).not.toHaveBeenCalled();
|
||||
expect(defaultRuntime.writeJson).toHaveBeenCalledWith({
|
||||
ok: false,
|
||||
error:
|
||||
"Gateway status failed: --require-rpc needs probing enabled. Remove --no-probe or drop --require-rpc.",
|
||||
});
|
||||
expect(defaultRuntime.error).not.toHaveBeenCalled();
|
||||
expect(defaultRuntime.exit).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it("renders service-inspection failures as JSON in JSON mode", async () => {
|
||||
gatherDaemonStatus.mockRejectedValueOnce(new Error("service manager unavailable"));
|
||||
|
||||
await expect(
|
||||
runDaemonStatus({
|
||||
rpc: {},
|
||||
probe: true,
|
||||
requireRpc: false,
|
||||
json: true,
|
||||
}),
|
||||
).rejects.toThrow("__exit__:1");
|
||||
|
||||
expect(printDaemonStatus).not.toHaveBeenCalled();
|
||||
expect(defaultRuntime.writeJson).toHaveBeenCalledWith({
|
||||
ok: false,
|
||||
error: "Gateway status failed: Error: service manager unavailable",
|
||||
});
|
||||
expect(defaultRuntime.error).not.toHaveBeenCalled();
|
||||
expect(defaultRuntime.exit).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it("exits only once after printing a failed required RPC probe", async () => {
|
||||
gatherDaemonStatus.mockResolvedValueOnce({
|
||||
service: {
|
||||
label: "LaunchAgent",
|
||||
loaded: true,
|
||||
loadedText: "loaded",
|
||||
notLoadedText: "not loaded",
|
||||
},
|
||||
rpc: {
|
||||
ok: false,
|
||||
url: "ws://127.0.0.1:18789",
|
||||
error: "gateway closed",
|
||||
},
|
||||
extraServices: [],
|
||||
});
|
||||
|
||||
await expect(
|
||||
runDaemonStatus({
|
||||
rpc: {},
|
||||
probe: true,
|
||||
requireRpc: true,
|
||||
json: true,
|
||||
}),
|
||||
).rejects.toThrow("__exit__:1");
|
||||
|
||||
expect(printDaemonStatus).toHaveBeenCalledTimes(1);
|
||||
expect(printDaemonStatus).toHaveBeenCalledWith(expect.any(Object), {
|
||||
json: true,
|
||||
deep: false,
|
||||
});
|
||||
expect(defaultRuntime.error).not.toHaveBeenCalled();
|
||||
expect(defaultRuntime.exit).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -5,29 +5,40 @@ import { gatherDaemonStatus } from "./status.gather.js";
|
||||
import { printDaemonStatus } from "./status.print.js";
|
||||
import type { DaemonStatusOptions } from "./types.js";
|
||||
|
||||
function failDaemonStatus(opts: DaemonStatusOptions, message: string): void {
|
||||
if (opts.json) {
|
||||
defaultRuntime.writeJson({ ok: false, error: message });
|
||||
} else {
|
||||
defaultRuntime.error(colorize(isRich(), theme.error, message));
|
||||
}
|
||||
defaultRuntime.exit(1);
|
||||
}
|
||||
|
||||
/** Run Gateway status diagnostics and apply --require-rpc exit behavior. */
|
||||
export async function runDaemonStatus(opts: DaemonStatusOptions) {
|
||||
if (opts.requireRpc && !opts.probe) {
|
||||
failDaemonStatus(
|
||||
opts,
|
||||
"Gateway status failed: --require-rpc needs probing enabled. Remove --no-probe or drop --require-rpc.",
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
let status: Awaited<ReturnType<typeof gatherDaemonStatus>>;
|
||||
try {
|
||||
if (opts.requireRpc && !opts.probe) {
|
||||
defaultRuntime.error(
|
||||
"Gateway status failed: --require-rpc needs probing enabled. Remove --no-probe or drop --require-rpc.",
|
||||
);
|
||||
defaultRuntime.exit(1);
|
||||
return;
|
||||
}
|
||||
const status = await gatherDaemonStatus({
|
||||
status = await gatherDaemonStatus({
|
||||
rpc: opts.rpc,
|
||||
probe: opts.probe,
|
||||
requireRpc: opts.requireRpc,
|
||||
deep: opts.deep === true,
|
||||
});
|
||||
printDaemonStatus(status, { json: opts.json, deep: opts.deep === true });
|
||||
if (opts.requireRpc && !status.rpc?.ok) {
|
||||
defaultRuntime.exit(1);
|
||||
}
|
||||
} catch (err) {
|
||||
const rich = isRich();
|
||||
defaultRuntime.error(colorize(rich, theme.error, `Gateway status failed: ${String(err)}`));
|
||||
failDaemonStatus(opts, `Gateway status failed: ${String(err)}`);
|
||||
return;
|
||||
}
|
||||
|
||||
if (opts.requireRpc && !status.rpc?.ok) {
|
||||
defaultRuntime.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user