From fcb956a0a2982da1e14295e701986cd909b49b77 Mon Sep 17 00:00:00 2001 From: Peter Steinberger Date: Mon, 2 Mar 2026 09:45:47 +0000 Subject: [PATCH] test(cli): reduce update/program suite overhead --- src/cli/program.smoke.test.ts | 9 +-- src/cli/program/preaction.test.ts | 35 +++------ src/cli/update-cli.test.ts | 117 ++++++++++-------------------- 3 files changed, 48 insertions(+), 113 deletions(-) diff --git a/src/cli/program.smoke.test.ts b/src/cli/program.smoke.test.ts index 0c3bd0720..c86a2651a 100644 --- a/src/cli/program.smoke.test.ts +++ b/src/cli/program.smoke.test.ts @@ -4,7 +4,6 @@ import { ensureConfigReady, installBaseProgramMocks, installSmokeProgramMocks, - messageCommand, onboardCommand, runTui, runtime, @@ -42,16 +41,10 @@ describe("cli program (smoke)", () => { ensureConfigReady.mockResolvedValue(undefined); }); - it("runs message command with required options", async () => { - await expect( - runProgram(["message", "send", "--target", "+1", "--message", "hi"]), - ).rejects.toThrow("exit"); - expect(messageCommand).toHaveBeenCalled(); - }); - it("registers memory + status commands", () => { const program = createProgram(); const names = program.commands.map((command) => command.name()); + expect(names).toContain("message"); expect(names).toContain("memory"); expect(names).toContain("status"); }); diff --git a/src/cli/program/preaction.test.ts b/src/cli/program/preaction.test.ts index c40cc5951..d85d48d9b 100644 --- a/src/cli/program/preaction.test.ts +++ b/src/cli/program/preaction.test.ts @@ -141,31 +141,16 @@ describe("registerPreActionHooks", () => { expect(ensurePluginRegistryLoadedMock).toHaveBeenCalledTimes(1); }); - it("loads plugin registry for configure command", async () => { - await runCommand({ - parseArgv: ["configure"], - processArgv: ["node", "openclaw", "configure"], - }); - - expect(ensurePluginRegistryLoadedMock).toHaveBeenCalledTimes(1); - }); - - it("loads plugin registry for onboard command", async () => { - await runCommand({ - parseArgv: ["onboard"], - processArgv: ["node", "openclaw", "onboard"], - }); - - expect(ensurePluginRegistryLoadedMock).toHaveBeenCalledTimes(1); - }); - - it("loads plugin registry for agents command", async () => { - await runCommand({ - parseArgv: ["agents"], - processArgv: ["node", "openclaw", "agents"], - }); - - expect(ensurePluginRegistryLoadedMock).toHaveBeenCalledTimes(1); + it("loads plugin registry for configure/onboard/agents commands", async () => { + const commands = ["configure", "onboard", "agents"] as const; + for (const command of commands) { + vi.clearAllMocks(); + await runCommand({ + parseArgv: [command], + processArgv: ["node", "openclaw", command], + }); + expect(ensurePluginRegistryLoadedMock, command).toHaveBeenCalledTimes(1); + } }); it("skips config guard for doctor, completion, and secrets commands", async () => { diff --git a/src/cli/update-cli.test.ts b/src/cli/update-cli.test.ts index 7edff76fe..2fe5e8f9b 100644 --- a/src/cli/update-cli.test.ts +++ b/src/cli/update-cli.test.ts @@ -1,4 +1,3 @@ -import fs from "node:fs/promises"; import path from "node:path"; import { beforeEach, describe, expect, it, vi } from "vitest"; import type { OpenClawConfig, ConfigFileSnapshot } from "../config/types.openclaw.js"; @@ -21,6 +20,9 @@ const serviceReadRuntime = vi.fn(); const inspectPortUsage = vi.fn(); const classifyPortListener = vi.fn(); const formatPortDiagnostics = vi.fn(); +const pathExists = vi.fn(); +const syncPluginsForUpdateChannel = vi.fn(); +const updateNpmInstalledPlugins = vi.fn(); vi.mock("@clack/prompts", () => ({ confirm, @@ -73,6 +75,19 @@ vi.mock("../process/exec.js", () => ({ runCommandWithTimeout: vi.fn(), })); +vi.mock("../utils.js", async (importOriginal) => { + const actual = await importOriginal(); + return { + ...actual, + pathExists: (...args: unknown[]) => pathExists(...args), + }; +}); + +vi.mock("../plugins/update.js", () => ({ + syncPluginsForUpdateChannel: (...args: unknown[]) => syncPluginsForUpdateChannel(...args), + updateNpmInstalledPlugins: (...args: unknown[]) => updateNpmInstalledPlugins(...args), +})); + vi.mock("./update-cli/shared.js", async (importOriginal) => { const actual = await importOriginal(); return { @@ -129,8 +144,7 @@ const { runCommandWithTimeout } = await import("../process/exec.js"); const { runDaemonRestart, runDaemonInstall } = await import("./daemon-cli.js"); const { doctorCommand } = await import("../commands/doctor.js"); const { defaultRuntime } = await import("../runtime.js"); -const { updateCommand, registerUpdateCli, updateStatusCommand, updateWizardCommand } = - await import("./update-cli.js"); +const { updateCommand, updateStatusCommand, updateWizardCommand } = await import("./update-cli.js"); describe("update-cli", () => { const fixtureRoot = "/tmp/openclaw-update-tests"; @@ -243,32 +257,7 @@ describe("update-cli", () => { }; beforeEach(() => { - confirm.mockClear(); - select.mockClear(); - vi.mocked(runGatewayUpdate).mockClear(); - vi.mocked(resolveOpenClawPackageRoot).mockClear(); - vi.mocked(readConfigFileSnapshot).mockClear(); - vi.mocked(writeConfigFile).mockClear(); - vi.mocked(checkUpdateStatus).mockClear(); - vi.mocked(fetchNpmTagVersion).mockClear(); - vi.mocked(resolveNpmChannelTag).mockClear(); - vi.mocked(runCommandWithTimeout).mockClear(); - vi.mocked(runDaemonRestart).mockClear(); - vi.mocked(mockedRunDaemonInstall).mockClear(); - vi.mocked(doctorCommand).mockClear(); - vi.mocked(defaultRuntime.log).mockClear(); - vi.mocked(defaultRuntime.error).mockClear(); - vi.mocked(defaultRuntime.exit).mockClear(); - readPackageName.mockClear(); - readPackageVersion.mockClear(); - resolveGlobalManager.mockClear(); - serviceLoaded.mockClear(); - serviceReadRuntime.mockClear(); - prepareRestartScript.mockClear(); - runRestartScript.mockClear(); - inspectPortUsage.mockClear(); - classifyPortListener.mockClear(); - formatPortDiagnostics.mockClear(); + vi.clearAllMocks(); vi.mocked(resolveOpenClawPackageRoot).mockResolvedValue(process.cwd()); vi.mocked(readConfigFileSnapshot).mockResolvedValue(baseSnapshot); vi.mocked(fetchNpmTagVersion).mockResolvedValue({ @@ -331,6 +320,22 @@ describe("update-cli", () => { }); classifyPortListener.mockReturnValue("gateway"); formatPortDiagnostics.mockReturnValue(["Port 18789 is already in use."]); + pathExists.mockResolvedValue(false); + syncPluginsForUpdateChannel.mockResolvedValue({ + changed: false, + config: baseConfig, + summary: { + switchedToBundled: [], + switchedToNpm: [], + warnings: [], + errors: [], + }, + }); + updateNpmInstalledPlugins.mockResolvedValue({ + changed: false, + config: baseConfig, + outcomes: [], + }); vi.mocked(runDaemonInstall).mockResolvedValue(undefined); vi.mocked(runDaemonRestart).mockResolvedValue(true); vi.mocked(doctorCommand).mockResolvedValue(undefined); @@ -341,39 +346,6 @@ describe("update-cli", () => { setStdoutTty(false); }); - it("exports updateCommand and registerUpdateCli", async () => { - expect(typeof updateCommand).toBe("function"); - expect(typeof registerUpdateCli).toBe("function"); - expect(typeof updateWizardCommand).toBe("function"); - }, 20_000); - - it("updateCommand runs update and outputs result", async () => { - const mockResult: UpdateRunResult = { - status: "ok", - mode: "git", - root: "/test/path", - before: { sha: "abc123", version: "1.0.0" }, - after: { sha: "def456", version: "1.0.1" }, - steps: [ - { - name: "git fetch", - command: "git fetch", - cwd: "/test/path", - durationMs: 100, - exitCode: 0, - }, - ], - durationMs: 500, - }; - - vi.mocked(runGatewayUpdate).mockResolvedValue(mockResult); - - await updateCommand({ json: false }); - - expect(runGatewayUpdate).toHaveBeenCalled(); - expect(defaultRuntime.log).toHaveBeenCalled(); - }); - it("updateCommand --dry-run previews without mutating", async () => { vi.mocked(defaultRuntime.log).mockClear(); serviceLoaded.mockResolvedValue(true); @@ -527,15 +499,6 @@ describe("update-cli", () => { expect(defaultRuntime.exit).toHaveBeenCalledWith(1); }); - it("updateCommand restarts daemon by default", async () => { - vi.mocked(runGatewayUpdate).mockResolvedValue(makeOkUpdateResult()); - vi.mocked(runDaemonRestart).mockResolvedValue(true); - - await updateCommand({}); - - expect(runDaemonRestart).toHaveBeenCalled(); - }); - it("updateCommand refreshes gateway service env when service is already installed", async () => { const mockResult: UpdateRunResult = { status: "ok", @@ -560,8 +523,8 @@ describe("update-cli", () => { it("updateCommand refreshes service env from updated install root when available", async () => { const root = createCaseDir("openclaw-updated-root"); - await fs.mkdir(path.join(root, "dist"), { recursive: true }); - await fs.writeFile(path.join(root, "dist", "entry.js"), "console.log('ok');\n", "utf8"); + const entryPath = path.join(root, "dist", "entry.js"); + pathExists.mockImplementation(async (candidate: string) => candidate === entryPath); vi.mocked(runGatewayUpdate).mockResolvedValue({ status: "ok", @@ -575,13 +538,7 @@ describe("update-cli", () => { await updateCommand({}); expect(runCommandWithTimeout).toHaveBeenCalledWith( - [ - expect.stringMatching(/node/), - path.join(root, "dist", "entry.js"), - "gateway", - "install", - "--force", - ], + [expect.stringMatching(/node/), entryPath, "gateway", "install", "--force"], expect.objectContaining({ timeoutMs: 60_000 }), ); expect(runDaemonInstall).not.toHaveBeenCalled();