mirror of
https://github.com/openclaw/openclaw.git
synced 2026-03-27 09:52:25 +00:00
test(cli): split plugin cli test coverage by surface
This commit is contained in:
288
src/cli/plugins-cli.update.test.ts
Normal file
288
src/cli/plugins-cli.update.test.ts
Normal file
@@ -0,0 +1,288 @@
|
||||
import { beforeEach, describe, expect, it } from "vitest";
|
||||
import type { OpenClawConfig } from "../config/config.js";
|
||||
import {
|
||||
loadConfig,
|
||||
resetPluginsCliTestState,
|
||||
runPluginsCommand,
|
||||
runtimeErrors,
|
||||
runtimeLogs,
|
||||
updateNpmInstalledHookPacks,
|
||||
updateNpmInstalledPlugins,
|
||||
writeConfigFile,
|
||||
} from "./plugins-cli-test-helpers.js";
|
||||
|
||||
describe("plugins cli update", () => {
|
||||
beforeEach(() => {
|
||||
resetPluginsCliTestState();
|
||||
});
|
||||
|
||||
it("updates tracked hook packs through plugins update", async () => {
|
||||
const cfg = {
|
||||
hooks: {
|
||||
internal: {
|
||||
installs: {
|
||||
"demo-hooks": {
|
||||
source: "npm",
|
||||
spec: "@acme/demo-hooks@1.0.0",
|
||||
installPath: "/tmp/hooks/demo-hooks",
|
||||
resolvedName: "@acme/demo-hooks",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
} as OpenClawConfig;
|
||||
const nextConfig = {
|
||||
hooks: {
|
||||
internal: {
|
||||
installs: {
|
||||
"demo-hooks": {
|
||||
source: "npm",
|
||||
spec: "@acme/demo-hooks@1.1.0",
|
||||
installPath: "/tmp/hooks/demo-hooks",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
} as OpenClawConfig;
|
||||
|
||||
loadConfig.mockReturnValue(cfg);
|
||||
updateNpmInstalledPlugins.mockResolvedValue({
|
||||
config: cfg,
|
||||
changed: false,
|
||||
outcomes: [],
|
||||
});
|
||||
updateNpmInstalledHookPacks.mockResolvedValue({
|
||||
config: nextConfig,
|
||||
changed: true,
|
||||
outcomes: [
|
||||
{
|
||||
hookId: "demo-hooks",
|
||||
status: "updated",
|
||||
message: 'Updated hook pack "demo-hooks": 1.0.0 -> 1.1.0.',
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
await runPluginsCommand(["plugins", "update", "demo-hooks"]);
|
||||
|
||||
expect(updateNpmInstalledHookPacks).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
config: cfg,
|
||||
hookIds: ["demo-hooks"],
|
||||
}),
|
||||
);
|
||||
expect(writeConfigFile).toHaveBeenCalledWith(nextConfig);
|
||||
expect(
|
||||
runtimeLogs.some((line) => line.includes("Restart the gateway to load plugins and hooks.")),
|
||||
).toBe(true);
|
||||
});
|
||||
|
||||
it("exits when update is called without id and without --all", async () => {
|
||||
loadConfig.mockReturnValue({
|
||||
plugins: {
|
||||
installs: {},
|
||||
},
|
||||
} as OpenClawConfig);
|
||||
|
||||
await expect(runPluginsCommand(["plugins", "update"])).rejects.toThrow("__exit__:1");
|
||||
|
||||
expect(runtimeErrors.at(-1)).toContain("Provide a plugin or hook-pack id, or use --all.");
|
||||
expect(updateNpmInstalledPlugins).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("reports no tracked plugins or hook packs when update --all has empty install records", async () => {
|
||||
loadConfig.mockReturnValue({
|
||||
plugins: {
|
||||
installs: {},
|
||||
},
|
||||
} as OpenClawConfig);
|
||||
|
||||
await runPluginsCommand(["plugins", "update", "--all"]);
|
||||
|
||||
expect(updateNpmInstalledPlugins).not.toHaveBeenCalled();
|
||||
expect(updateNpmInstalledHookPacks).not.toHaveBeenCalled();
|
||||
expect(runtimeLogs.at(-1)).toBe("No tracked plugins or hook packs to update.");
|
||||
});
|
||||
|
||||
it("maps an explicit unscoped npm dist-tag update to the tracked plugin id", async () => {
|
||||
const config = {
|
||||
plugins: {
|
||||
installs: {
|
||||
"openclaw-codex-app-server": {
|
||||
source: "npm",
|
||||
spec: "openclaw-codex-app-server",
|
||||
installPath: "/tmp/openclaw-codex-app-server",
|
||||
resolvedName: "openclaw-codex-app-server",
|
||||
},
|
||||
},
|
||||
},
|
||||
} as OpenClawConfig;
|
||||
loadConfig.mockReturnValue(config);
|
||||
updateNpmInstalledPlugins.mockResolvedValue({
|
||||
config,
|
||||
changed: false,
|
||||
outcomes: [],
|
||||
});
|
||||
|
||||
await runPluginsCommand(["plugins", "update", "openclaw-codex-app-server@beta"]);
|
||||
|
||||
expect(updateNpmInstalledPlugins).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
config,
|
||||
pluginIds: ["openclaw-codex-app-server"],
|
||||
specOverrides: {
|
||||
"openclaw-codex-app-server": "openclaw-codex-app-server@beta",
|
||||
},
|
||||
}),
|
||||
);
|
||||
});
|
||||
|
||||
it("maps an explicit scoped npm dist-tag update to the tracked plugin id", async () => {
|
||||
const config = {
|
||||
plugins: {
|
||||
installs: {
|
||||
"voice-call": {
|
||||
source: "npm",
|
||||
spec: "@openclaw/voice-call",
|
||||
installPath: "/tmp/voice-call",
|
||||
resolvedName: "@openclaw/voice-call",
|
||||
},
|
||||
},
|
||||
},
|
||||
} as OpenClawConfig;
|
||||
loadConfig.mockReturnValue(config);
|
||||
updateNpmInstalledPlugins.mockResolvedValue({
|
||||
config,
|
||||
changed: false,
|
||||
outcomes: [],
|
||||
});
|
||||
|
||||
await runPluginsCommand(["plugins", "update", "@openclaw/voice-call@beta"]);
|
||||
|
||||
expect(updateNpmInstalledPlugins).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
config,
|
||||
pluginIds: ["voice-call"],
|
||||
specOverrides: {
|
||||
"voice-call": "@openclaw/voice-call@beta",
|
||||
},
|
||||
}),
|
||||
);
|
||||
});
|
||||
|
||||
it("maps an explicit npm version update to the tracked plugin id", async () => {
|
||||
const config = {
|
||||
plugins: {
|
||||
installs: {
|
||||
"openclaw-codex-app-server": {
|
||||
source: "npm",
|
||||
spec: "openclaw-codex-app-server",
|
||||
installPath: "/tmp/openclaw-codex-app-server",
|
||||
resolvedName: "openclaw-codex-app-server",
|
||||
},
|
||||
},
|
||||
},
|
||||
} as OpenClawConfig;
|
||||
loadConfig.mockReturnValue(config);
|
||||
updateNpmInstalledPlugins.mockResolvedValue({
|
||||
config,
|
||||
changed: false,
|
||||
outcomes: [],
|
||||
});
|
||||
|
||||
await runPluginsCommand(["plugins", "update", "openclaw-codex-app-server@0.2.0-beta.4"]);
|
||||
|
||||
expect(updateNpmInstalledPlugins).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
config,
|
||||
pluginIds: ["openclaw-codex-app-server"],
|
||||
specOverrides: {
|
||||
"openclaw-codex-app-server": "openclaw-codex-app-server@0.2.0-beta.4",
|
||||
},
|
||||
}),
|
||||
);
|
||||
});
|
||||
|
||||
it("keeps using the recorded npm tag when update is invoked by plugin id", async () => {
|
||||
const config = {
|
||||
plugins: {
|
||||
installs: {
|
||||
"openclaw-codex-app-server": {
|
||||
source: "npm",
|
||||
spec: "openclaw-codex-app-server@beta",
|
||||
installPath: "/tmp/openclaw-codex-app-server",
|
||||
resolvedName: "openclaw-codex-app-server",
|
||||
},
|
||||
},
|
||||
},
|
||||
} as OpenClawConfig;
|
||||
loadConfig.mockReturnValue(config);
|
||||
updateNpmInstalledPlugins.mockResolvedValue({
|
||||
config,
|
||||
changed: false,
|
||||
outcomes: [],
|
||||
});
|
||||
|
||||
await runPluginsCommand(["plugins", "update", "openclaw-codex-app-server"]);
|
||||
|
||||
expect(updateNpmInstalledPlugins).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
config,
|
||||
pluginIds: ["openclaw-codex-app-server"],
|
||||
}),
|
||||
);
|
||||
expect(updateNpmInstalledPlugins).not.toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
specOverrides: expect.anything(),
|
||||
}),
|
||||
);
|
||||
});
|
||||
|
||||
it("writes updated config when updater reports changes", async () => {
|
||||
const cfg = {
|
||||
plugins: {
|
||||
installs: {
|
||||
alpha: {
|
||||
source: "npm",
|
||||
spec: "@openclaw/alpha@1.0.0",
|
||||
},
|
||||
},
|
||||
},
|
||||
} as OpenClawConfig;
|
||||
const nextConfig = {
|
||||
plugins: {
|
||||
installs: {
|
||||
alpha: {
|
||||
source: "npm",
|
||||
spec: "@openclaw/alpha@1.1.0",
|
||||
},
|
||||
},
|
||||
},
|
||||
} as OpenClawConfig;
|
||||
loadConfig.mockReturnValue(cfg);
|
||||
updateNpmInstalledPlugins.mockResolvedValue({
|
||||
outcomes: [{ status: "ok", message: "Updated alpha -> 1.1.0" }],
|
||||
changed: true,
|
||||
config: nextConfig,
|
||||
});
|
||||
updateNpmInstalledHookPacks.mockResolvedValue({
|
||||
outcomes: [],
|
||||
changed: false,
|
||||
config: nextConfig,
|
||||
});
|
||||
|
||||
await runPluginsCommand(["plugins", "update", "alpha"]);
|
||||
|
||||
expect(updateNpmInstalledPlugins).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
config: cfg,
|
||||
pluginIds: ["alpha"],
|
||||
dryRun: false,
|
||||
}),
|
||||
);
|
||||
expect(writeConfigFile).toHaveBeenCalledWith(nextConfig);
|
||||
expect(
|
||||
runtimeLogs.some((line) => line.includes("Restart the gateway to load plugins and hooks.")),
|
||||
).toBe(true);
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user