openclaw/src/auto-reply/reply/commands-plugins.install.te...

202 lines
6.4 KiB
TypeScript

import fs from "node:fs/promises";
import path from "node:path";
import { afterEach, describe, expect, it, vi } from "vitest";
import { withTempHome } from "../../config/home-env.test-harness.js";
import { handleCommands } from "./commands-core.js";
import { createCommandWorkspaceHarness } from "./commands-filesystem.test-support.js";
import { buildCommandTestParams } from "./commands.test-harness.js";
const installPluginFromPathMock = vi.fn();
const installPluginFromClawHubMock = vi.fn();
const persistPluginInstallMock = vi.fn();
vi.mock("../../plugins/install.js", async () => {
const actual = await vi.importActual<typeof import("../../plugins/install.js")>(
"../../plugins/install.js",
);
return {
...actual,
installPluginFromPath: installPluginFromPathMock,
};
});
vi.mock("../../plugins/clawhub.js", async () => {
const actual = await vi.importActual<typeof import("../../plugins/clawhub.js")>(
"../../plugins/clawhub.js",
);
return {
...actual,
installPluginFromClawHub: installPluginFromClawHubMock,
};
});
vi.mock("../../cli/plugins-install-persist.js", () => ({
persistPluginInstall: persistPluginInstallMock,
}));
const workspaceHarness = createCommandWorkspaceHarness("openclaw-command-plugins-install-");
describe("handleCommands /plugins install", () => {
afterEach(async () => {
installPluginFromPathMock.mockReset();
installPluginFromClawHubMock.mockReset();
persistPluginInstallMock.mockReset();
await workspaceHarness.cleanupWorkspaces();
});
it("installs a plugin from a local path", async () => {
installPluginFromPathMock.mockResolvedValue({
ok: true,
pluginId: "path-install-plugin",
targetDir: "/tmp/path-install-plugin",
version: "0.0.1",
extensions: ["index.js"],
});
persistPluginInstallMock.mockResolvedValue({});
await withTempHome("openclaw-command-plugins-home-", async () => {
const workspaceDir = await workspaceHarness.createWorkspace();
const pluginDir = path.join(workspaceDir, "fixtures", "path-install-plugin");
await fs.mkdir(pluginDir, { recursive: true });
const params = buildCommandTestParams(
`/plugins install ${pluginDir}`,
{
commands: {
text: true,
plugins: true,
},
},
undefined,
{ workspaceDir },
);
params.command.senderIsOwner = true;
const result = await handleCommands(params);
expect(result.reply?.text).toContain('Installed plugin "path-install-plugin"');
expect(installPluginFromPathMock).toHaveBeenCalledWith(
expect.objectContaining({
path: pluginDir,
}),
);
expect(persistPluginInstallMock).toHaveBeenCalledWith(
expect.objectContaining({
pluginId: "path-install-plugin",
install: expect.objectContaining({
source: "path",
sourcePath: pluginDir,
installPath: "/tmp/path-install-plugin",
version: "0.0.1",
}),
}),
);
});
});
it("installs from an explicit clawhub: spec", async () => {
installPluginFromClawHubMock.mockResolvedValue({
ok: true,
pluginId: "clawhub-demo",
targetDir: "/tmp/clawhub-demo",
version: "1.2.3",
extensions: ["index.js"],
packageName: "@openclaw/clawhub-demo",
clawhub: {
source: "clawhub",
clawhubUrl: "https://clawhub.ai",
clawhubPackage: "@openclaw/clawhub-demo",
clawhubFamily: "code-plugin",
clawhubChannel: "official",
version: "1.2.3",
integrity: "sha512-demo",
resolvedAt: "2026-03-22T12:00:00.000Z",
},
});
persistPluginInstallMock.mockResolvedValue({});
await withTempHome("openclaw-command-plugins-home-", async () => {
const workspaceDir = await workspaceHarness.createWorkspace();
const params = buildCommandTestParams(
"/plugins install clawhub:@openclaw/clawhub-demo@1.2.3",
{
commands: {
text: true,
plugins: true,
},
},
undefined,
{ workspaceDir },
);
params.command.senderIsOwner = true;
const result = await handleCommands(params);
expect(result.reply?.text).toContain('Installed plugin "clawhub-demo"');
expect(installPluginFromClawHubMock).toHaveBeenCalledWith(
expect.objectContaining({
spec: "clawhub:@openclaw/clawhub-demo@1.2.3",
}),
);
expect(persistPluginInstallMock).toHaveBeenCalledWith(
expect.objectContaining({
pluginId: "clawhub-demo",
install: expect.objectContaining({
source: "clawhub",
spec: "clawhub:@openclaw/clawhub-demo@1.2.3",
installPath: "/tmp/clawhub-demo",
version: "1.2.3",
integrity: "sha512-demo",
clawhubPackage: "@openclaw/clawhub-demo",
clawhubChannel: "official",
}),
}),
);
});
});
it("treats /plugin add as an install alias", async () => {
installPluginFromClawHubMock.mockResolvedValue({
ok: true,
pluginId: "alias-demo",
targetDir: "/tmp/alias-demo",
version: "1.0.0",
extensions: ["index.js"],
packageName: "@openclaw/alias-demo",
clawhub: {
source: "clawhub",
clawhubUrl: "https://clawhub.ai",
clawhubPackage: "@openclaw/alias-demo",
clawhubFamily: "code-plugin",
clawhubChannel: "official",
version: "1.0.0",
integrity: "sha512-alias",
resolvedAt: "2026-03-23T12:00:00.000Z",
},
});
persistPluginInstallMock.mockResolvedValue({});
await withTempHome("openclaw-command-plugins-home-", async () => {
const workspaceDir = await workspaceHarness.createWorkspace();
const params = buildCommandTestParams(
"/plugin add clawhub:@openclaw/alias-demo@1.0.0",
{
commands: {
text: true,
plugins: true,
},
},
undefined,
{ workspaceDir },
);
params.command.senderIsOwner = true;
const result = await handleCommands(params);
expect(result.reply?.text).toContain('Installed plugin "alias-demo"');
expect(installPluginFromClawHubMock).toHaveBeenCalledWith(
expect.objectContaining({
spec: "clawhub:@openclaw/alias-demo@1.0.0",
}),
);
});
});
});