mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-18 17:04:46 +00:00
255 lines
8.1 KiB
TypeScript
255 lines
8.1 KiB
TypeScript
import { beforeAll, beforeEach, describe, expect, it, vi } from "vitest";
|
|
import type { ChannelPluginCatalogEntry } from "../channels/plugins/catalog.js";
|
|
import type { ChannelPlugin } from "../channels/plugins/types.plugin.js";
|
|
import { resetPluginRuntimeStateForTest, setActivePluginRegistry } from "../plugins/runtime.js";
|
|
import { createTestRegistry } from "../test-utils/channel-plugins.js";
|
|
import {
|
|
ensureChannelSetupPluginInstalled,
|
|
loadChannelSetupPluginRegistrySnapshotForChannel,
|
|
} from "./channel-setup/plugin-install.js";
|
|
import { configMocks } from "./channels.mock-harness.js";
|
|
import {
|
|
createExternalChatCatalogEntry,
|
|
createExternalChatDeletePlugin,
|
|
} from "./channels.plugin-install.test-helpers.js";
|
|
import { baseConfigSnapshot, createTestRuntime } from "./test-runtime-config-helpers.js";
|
|
|
|
let channelsRemoveCommand: typeof import("./channels.js").channelsRemoveCommand;
|
|
|
|
const catalogMocks = vi.hoisted(() => ({
|
|
listChannelPluginCatalogEntries: vi.fn((): ChannelPluginCatalogEntry[] => []),
|
|
}));
|
|
|
|
const registryRefreshMocks = vi.hoisted(() => ({
|
|
refreshPluginRegistryAfterConfigMutation: vi.fn(async () => undefined),
|
|
}));
|
|
|
|
const gatewayMocks = vi.hoisted(() => ({
|
|
callGateway: vi.fn(async () => ({ stopped: true })),
|
|
}));
|
|
|
|
vi.mock("../channels/plugins/catalog.js", async () => {
|
|
const actual = await vi.importActual<typeof import("../channels/plugins/catalog.js")>(
|
|
"../channels/plugins/catalog.js",
|
|
);
|
|
return {
|
|
...actual,
|
|
listChannelPluginCatalogEntries: catalogMocks.listChannelPluginCatalogEntries,
|
|
};
|
|
});
|
|
|
|
vi.mock("../channels/plugins/bundled.js", async () => {
|
|
const actual = await vi.importActual<typeof import("../channels/plugins/bundled.js")>(
|
|
"../channels/plugins/bundled.js",
|
|
);
|
|
return {
|
|
...actual,
|
|
getBundledChannelPlugin: vi.fn(() => undefined),
|
|
};
|
|
});
|
|
|
|
vi.mock("./channel-setup/plugin-install.js", async () => {
|
|
const actual = await vi.importActual<typeof import("./channel-setup/plugin-install.js")>(
|
|
"./channel-setup/plugin-install.js",
|
|
);
|
|
const { createMockChannelSetupPluginInstallModule } =
|
|
await import("./channels.plugin-install.test-helpers.js");
|
|
return createMockChannelSetupPluginInstallModule(actual);
|
|
});
|
|
|
|
vi.mock("../cli/plugins-registry-refresh.js", () => registryRefreshMocks);
|
|
|
|
vi.mock("../gateway/call.js", () => ({
|
|
callGateway: gatewayMocks.callGateway,
|
|
}));
|
|
|
|
const runtime = createTestRuntime();
|
|
|
|
describe("channelsRemoveCommand", () => {
|
|
beforeAll(async () => {
|
|
({ channelsRemoveCommand } = await import("./channels.js"));
|
|
});
|
|
|
|
beforeEach(() => {
|
|
resetPluginRuntimeStateForTest();
|
|
configMocks.readConfigFileSnapshot.mockClear();
|
|
configMocks.writeConfigFile.mockClear();
|
|
configMocks.replaceConfigFile
|
|
.mockReset()
|
|
.mockImplementation(async (params: { nextConfig: unknown }) => {
|
|
await configMocks.writeConfigFile(params.nextConfig);
|
|
});
|
|
runtime.log.mockClear();
|
|
runtime.error.mockClear();
|
|
runtime.exit.mockClear();
|
|
catalogMocks.listChannelPluginCatalogEntries.mockClear();
|
|
catalogMocks.listChannelPluginCatalogEntries.mockReturnValue([]);
|
|
vi.mocked(ensureChannelSetupPluginInstalled).mockClear();
|
|
vi.mocked(ensureChannelSetupPluginInstalled).mockImplementation(async ({ cfg }) => ({
|
|
cfg,
|
|
installed: true,
|
|
status: "installed",
|
|
}));
|
|
vi.mocked(loadChannelSetupPluginRegistrySnapshotForChannel).mockClear();
|
|
vi.mocked(loadChannelSetupPluginRegistrySnapshotForChannel).mockReturnValue(
|
|
createTestRegistry(),
|
|
);
|
|
registryRefreshMocks.refreshPluginRegistryAfterConfigMutation.mockClear();
|
|
gatewayMocks.callGateway.mockClear();
|
|
gatewayMocks.callGateway.mockResolvedValue({ stopped: true });
|
|
setActivePluginRegistry(createTestRegistry());
|
|
});
|
|
|
|
it("asks users to add an external channel plugin before removing its account", async () => {
|
|
configMocks.readConfigFileSnapshot.mockResolvedValue({
|
|
...baseConfigSnapshot,
|
|
config: {
|
|
channels: {
|
|
"external-chat": {
|
|
enabled: true,
|
|
token: "token-1",
|
|
},
|
|
},
|
|
},
|
|
});
|
|
const catalogEntry: ChannelPluginCatalogEntry = createExternalChatCatalogEntry();
|
|
catalogMocks.listChannelPluginCatalogEntries.mockReturnValue([catalogEntry]);
|
|
|
|
await channelsRemoveCommand(
|
|
{
|
|
channel: "external-chat",
|
|
account: "default",
|
|
delete: true,
|
|
},
|
|
runtime,
|
|
{ hasFlags: true },
|
|
);
|
|
|
|
expect(ensureChannelSetupPluginInstalled).not.toHaveBeenCalled();
|
|
expect(loadChannelSetupPluginRegistrySnapshotForChannel).toHaveBeenCalledTimes(1);
|
|
expect(configMocks.writeConfigFile).not.toHaveBeenCalled();
|
|
expect(runtime.error).toHaveBeenCalledWith(
|
|
expect.stringContaining('Channel plugin "external-chat" is not installed. Run '),
|
|
);
|
|
expect(runtime.error).toHaveBeenCalledWith(
|
|
expect.stringContaining("channels add --channel external-chat"),
|
|
);
|
|
expect(runtime.exit).toHaveBeenCalledWith(1);
|
|
});
|
|
|
|
it("removes an external channel account when its plugin is already installed", async () => {
|
|
configMocks.readConfigFileSnapshot.mockResolvedValue({
|
|
...baseConfigSnapshot,
|
|
config: {
|
|
channels: {
|
|
"external-chat": {
|
|
enabled: true,
|
|
token: "token-1",
|
|
},
|
|
},
|
|
},
|
|
});
|
|
const catalogEntry: ChannelPluginCatalogEntry = createExternalChatCatalogEntry();
|
|
catalogMocks.listChannelPluginCatalogEntries.mockReturnValue([catalogEntry]);
|
|
const scopedPlugin = createExternalChatDeletePlugin();
|
|
vi.mocked(loadChannelSetupPluginRegistrySnapshotForChannel).mockReturnValue(
|
|
createTestRegistry([
|
|
{
|
|
pluginId: "@vendor/external-chat-plugin",
|
|
plugin: scopedPlugin,
|
|
source: "test",
|
|
},
|
|
]),
|
|
);
|
|
|
|
await channelsRemoveCommand(
|
|
{
|
|
channel: "external-chat",
|
|
account: "default",
|
|
delete: true,
|
|
},
|
|
runtime,
|
|
{ hasFlags: true },
|
|
);
|
|
|
|
expect(ensureChannelSetupPluginInstalled).not.toHaveBeenCalled();
|
|
expect(registryRefreshMocks.refreshPluginRegistryAfterConfigMutation).not.toHaveBeenCalled();
|
|
expect(configMocks.writeConfigFile).toHaveBeenCalledWith(
|
|
expect.not.objectContaining({
|
|
channels: expect.objectContaining({
|
|
"external-chat": expect.anything(),
|
|
}),
|
|
}),
|
|
);
|
|
expect(runtime.error).not.toHaveBeenCalled();
|
|
expect(runtime.exit).not.toHaveBeenCalled();
|
|
});
|
|
|
|
it("stops an active gateway channel runtime before deleting a runtime-backed account", async () => {
|
|
configMocks.readConfigFileSnapshot.mockResolvedValue({
|
|
...baseConfigSnapshot,
|
|
config: {
|
|
channels: {
|
|
"external-chat": {
|
|
enabled: true,
|
|
token: "token-1",
|
|
},
|
|
},
|
|
},
|
|
});
|
|
const catalogEntry: ChannelPluginCatalogEntry = createExternalChatCatalogEntry();
|
|
catalogMocks.listChannelPluginCatalogEntries.mockReturnValue([catalogEntry]);
|
|
const scopedPlugin = {
|
|
...createExternalChatDeletePlugin(),
|
|
gateway: {
|
|
startAccount: vi.fn(),
|
|
},
|
|
} as ChannelPlugin;
|
|
vi.mocked(loadChannelSetupPluginRegistrySnapshotForChannel).mockReturnValue(
|
|
createTestRegistry([
|
|
{
|
|
pluginId: "@vendor/external-chat-plugin",
|
|
plugin: scopedPlugin,
|
|
source: "test",
|
|
},
|
|
]),
|
|
);
|
|
|
|
await channelsRemoveCommand(
|
|
{
|
|
channel: "external-chat",
|
|
account: "default",
|
|
delete: true,
|
|
},
|
|
runtime,
|
|
{ hasFlags: true },
|
|
);
|
|
|
|
expect(gatewayMocks.callGateway).toHaveBeenCalledWith({
|
|
config: {
|
|
channels: {
|
|
"external-chat": {
|
|
enabled: true,
|
|
token: "token-1",
|
|
},
|
|
},
|
|
},
|
|
method: "channels.stop",
|
|
params: {
|
|
channel: "external-chat",
|
|
accountId: "default",
|
|
},
|
|
mode: "backend",
|
|
clientName: "gateway-client",
|
|
deviceIdentity: null,
|
|
});
|
|
expect(configMocks.writeConfigFile).toHaveBeenCalledWith(
|
|
expect.not.objectContaining({
|
|
channels: expect.objectContaining({
|
|
"external-chat": expect.anything(),
|
|
}),
|
|
}),
|
|
);
|
|
});
|
|
});
|