test(cli): reduce update/program suite overhead
This commit is contained in:
@@ -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");
|
||||
});
|
||||
|
||||
@@ -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 () => {
|
||||
|
||||
@@ -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<typeof import("../utils.js")>();
|
||||
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<typeof import("./update-cli/shared.js")>();
|
||||
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();
|
||||
|
||||
Reference in New Issue
Block a user