Files
openclaw-zero-token/cli/run-main.exit.test.ts
sjhu 571e14a236 feat: upgrade to upstream v2026.3.28
Major upgrade from e26988a38 to upstream v2026.3.28 (f9b107928).
Key changes:
- Upstream src/, ui/, extensions/ (89 bundled extensions)
- Zero-token web providers preserved in src/zero-token/
- AskOnce plugin restored and registered as CLI command
- Added missing packages: @anthropic-ai/vertex-sdk, @modelcontextprotocol/sdk
- Fixed tsconfig rootDir, skipLibCheck for plugin-sdk DTS build
- Added askonce to bundled plugin metadata and package.json exports
- Fixed AskOnce CLI command registration (missing commands metadata)
- Restored AskOnce adapter imports (correct 5-level relative paths)
- Removed stale migration artifacts from root directory
2026-03-30 17:58:12 +08:00

135 lines
4.5 KiB
TypeScript

import process from "node:process";
import { beforeEach, describe, expect, it, vi } from "vitest";
const tryRouteCliMock = vi.hoisted(() => vi.fn());
const loadDotEnvMock = vi.hoisted(() => vi.fn());
const normalizeEnvMock = vi.hoisted(() => vi.fn());
const ensurePathMock = vi.hoisted(() => vi.fn());
const assertRuntimeMock = vi.hoisted(() => vi.fn());
const closeActiveMemorySearchManagersMock = vi.hoisted(() => vi.fn(async () => {}));
const hasMemoryRuntimeMock = vi.hoisted(() => vi.fn(() => false));
const outputRootHelpMock = vi.hoisted(() => vi.fn());
const buildProgramMock = vi.hoisted(() => vi.fn());
const maybeRunCliInContainerMock = vi.hoisted(() =>
vi.fn<
(argv: string[]) => { handled: true; exitCode: number } | { handled: false; argv: string[] }
>((argv: string[]) => ({ handled: false, argv })),
);
vi.mock("./route.js", () => ({
tryRouteCli: tryRouteCliMock,
}));
vi.mock("./container-target.js", () => ({
maybeRunCliInContainer: maybeRunCliInContainerMock,
parseCliContainerArgs: (argv: string[]) => ({ ok: true, container: null, argv }),
}));
vi.mock("./dotenv.js", () => ({
loadCliDotEnv: loadDotEnvMock,
}));
vi.mock("../infra/env.js", () => ({
normalizeEnv: normalizeEnvMock,
}));
vi.mock("../infra/path-env.js", () => ({
ensureOpenClawCliOnPath: ensurePathMock,
}));
vi.mock("../infra/runtime-guard.js", () => ({
assertSupportedRuntime: assertRuntimeMock,
}));
vi.mock("../plugins/memory-runtime.js", () => ({
closeActiveMemorySearchManagers: closeActiveMemorySearchManagersMock,
}));
vi.mock("../plugins/memory-state.js", () => ({
hasMemoryRuntime: hasMemoryRuntimeMock,
}));
vi.mock("./program/root-help.js", () => ({
outputRootHelp: outputRootHelpMock,
}));
vi.mock("./program.js", () => ({
buildProgram: buildProgramMock,
}));
const { runCli } = await import("./run-main.js");
describe("runCli exit behavior", () => {
beforeEach(() => {
vi.clearAllMocks();
hasMemoryRuntimeMock.mockReturnValue(false);
});
it("does not force process.exit after successful routed command", async () => {
tryRouteCliMock.mockResolvedValueOnce(true);
const exitSpy = vi.spyOn(process, "exit").mockImplementation(((code?: number) => {
throw new Error(`unexpected process.exit(${String(code)})`);
}) as typeof process.exit);
await runCli(["node", "openclaw", "status"]);
expect(maybeRunCliInContainerMock).toHaveBeenCalledWith(["node", "openclaw", "status"]);
expect(tryRouteCliMock).toHaveBeenCalledWith(["node", "openclaw", "status"]);
expect(closeActiveMemorySearchManagersMock).not.toHaveBeenCalled();
expect(exitSpy).not.toHaveBeenCalled();
exitSpy.mockRestore();
});
it("renders root help without building the full program", async () => {
const exitSpy = vi.spyOn(process, "exit").mockImplementation(((code?: number) => {
throw new Error(`unexpected process.exit(${String(code)})`);
}) as typeof process.exit);
await runCli(["node", "openclaw", "--help"]);
expect(maybeRunCliInContainerMock).toHaveBeenCalledWith(["node", "openclaw", "--help"]);
expect(tryRouteCliMock).not.toHaveBeenCalled();
expect(outputRootHelpMock).toHaveBeenCalledTimes(1);
expect(buildProgramMock).not.toHaveBeenCalled();
expect(closeActiveMemorySearchManagersMock).not.toHaveBeenCalled();
expect(exitSpy).not.toHaveBeenCalled();
exitSpy.mockRestore();
});
it("closes memory managers when a runtime was registered", async () => {
tryRouteCliMock.mockResolvedValueOnce(true);
hasMemoryRuntimeMock.mockReturnValue(true);
await runCli(["node", "openclaw", "status"]);
expect(closeActiveMemorySearchManagersMock).toHaveBeenCalledTimes(1);
});
it("returns after a handled container-target invocation", async () => {
maybeRunCliInContainerMock.mockReturnValueOnce({ handled: true, exitCode: 0 });
await runCli(["node", "openclaw", "--container", "demo", "status"]);
expect(maybeRunCliInContainerMock).toHaveBeenCalledWith([
"node",
"openclaw",
"--container",
"demo",
"status",
]);
expect(loadDotEnvMock).not.toHaveBeenCalled();
expect(tryRouteCliMock).not.toHaveBeenCalled();
expect(closeActiveMemorySearchManagersMock).not.toHaveBeenCalled();
});
it("propagates a handled container-target exit code", async () => {
const exitCode = process.exitCode;
maybeRunCliInContainerMock.mockReturnValueOnce({ handled: true, exitCode: 7 });
await runCli(["node", "openclaw", "--container", "demo", "status"]);
expect(process.exitCode).toBe(7);
process.exitCode = exitCode;
});
});