Files
openclaw/src/cli/plugin-registry.test.ts

177 lines
5.2 KiB
TypeScript

import { beforeEach, describe, expect, it, vi } from "vitest";
const mocks = vi.hoisted(() => ({
applyPluginAutoEnable: vi.fn(),
resolveAgentWorkspaceDir: vi.fn(() => "/tmp/workspace"),
resolveDefaultAgentId: vi.fn(() => "main"),
loadConfig: vi.fn(),
loadOpenClawPlugins: vi.fn(),
loadPluginManifestRegistry: vi.fn(),
getActivePluginRegistry: vi.fn(),
}));
vi.mock("../agents/agent-scope.js", () => ({
resolveAgentWorkspaceDir: mocks.resolveAgentWorkspaceDir,
resolveDefaultAgentId: mocks.resolveDefaultAgentId,
}));
vi.mock("../config/config.js", () => ({
loadConfig: mocks.loadConfig,
}));
vi.mock("../config/plugin-auto-enable.js", () => ({
applyPluginAutoEnable: mocks.applyPluginAutoEnable,
}));
vi.mock("../plugins/loader.js", () => ({
loadOpenClawPlugins: mocks.loadOpenClawPlugins,
}));
vi.mock("../plugins/manifest-registry.js", () => ({
loadPluginManifestRegistry: mocks.loadPluginManifestRegistry,
}));
vi.mock("../plugins/runtime.js", () => ({
getActivePluginRegistry: mocks.getActivePluginRegistry,
}));
describe("ensurePluginRegistryLoaded", () => {
beforeEach(() => {
vi.resetModules();
vi.clearAllMocks();
mocks.getActivePluginRegistry.mockReturnValue({
plugins: [],
channels: [],
tools: [],
});
});
it("uses the auto-enabled config snapshot for configured channel scope", async () => {
const baseConfig = {
channels: {
"demo-chat": {
botToken: "demo-bot-token",
appToken: "demo-app-token",
},
},
};
const autoEnabledConfig = {
...baseConfig,
plugins: {
entries: {
"demo-chat": {
enabled: true,
},
},
},
};
mocks.loadConfig.mockReturnValue(baseConfig);
mocks.applyPluginAutoEnable.mockReturnValue({ config: autoEnabledConfig, changes: [] });
mocks.loadPluginManifestRegistry.mockReturnValue({
plugins: [{ id: "demo-chat", channels: ["demo-chat"] }],
diagnostics: [],
});
const { ensurePluginRegistryLoaded } = await import("./plugin-registry.js");
ensurePluginRegistryLoaded({ scope: "configured-channels" });
expect(mocks.applyPluginAutoEnable).toHaveBeenCalledWith({
config: baseConfig,
env: process.env,
});
expect(mocks.resolveDefaultAgentId).toHaveBeenCalledWith(autoEnabledConfig);
expect(mocks.resolveAgentWorkspaceDir).toHaveBeenCalledWith(autoEnabledConfig, "main");
expect(mocks.loadPluginManifestRegistry).toHaveBeenCalledWith(
expect.objectContaining({
config: autoEnabledConfig,
workspaceDir: "/tmp/workspace",
}),
);
expect(mocks.loadOpenClawPlugins).toHaveBeenCalledWith(
expect.objectContaining({
config: autoEnabledConfig,
onlyPluginIds: ["demo-chat"],
throwOnLoadError: true,
workspaceDir: "/tmp/workspace",
}),
);
});
it("reloads when escalating from configured-channels to channels", async () => {
const config = {
plugins: { enabled: true },
channels: { "demo-channel-a": { enabled: false } },
};
mocks.loadConfig.mockReturnValue(config);
mocks.applyPluginAutoEnable.mockReturnValue({ config, changes: [] });
mocks.loadPluginManifestRegistry.mockReturnValue({
plugins: [
{ id: "demo-channel-a", channels: ["demo-channel-a"] },
{ id: "demo-channel-b", channels: ["demo-channel-b"] },
{ id: "demo-provider", channels: [] },
],
diagnostics: [],
});
mocks.getActivePluginRegistry
.mockReturnValueOnce({
plugins: [],
channels: [],
tools: [],
})
.mockReturnValue({
plugins: [{ id: "demo-channel-a" }],
channels: [{ plugin: { id: "demo-channel-a" } }],
tools: [],
});
const { ensurePluginRegistryLoaded } = await import("./plugin-registry.js");
ensurePluginRegistryLoaded({ scope: "configured-channels" });
ensurePluginRegistryLoaded({ scope: "channels" });
expect(mocks.loadOpenClawPlugins).toHaveBeenCalledTimes(2);
expect(mocks.loadOpenClawPlugins).toHaveBeenNthCalledWith(
1,
expect.objectContaining({ onlyPluginIds: [], throwOnLoadError: true }),
);
expect(mocks.loadOpenClawPlugins).toHaveBeenNthCalledWith(
2,
expect.objectContaining({
onlyPluginIds: ["demo-channel-a", "demo-channel-b"],
throwOnLoadError: true,
}),
);
});
it("does not treat a pre-seeded partial registry as all scope", async () => {
const config = {
plugins: { enabled: true },
channels: { "demo-channel-a": { enabled: true } },
};
mocks.loadConfig.mockReturnValue(config);
mocks.applyPluginAutoEnable.mockReturnValue({ config, changes: [] });
mocks.getActivePluginRegistry.mockReturnValue({
plugins: [],
channels: [{ plugin: { id: "demo-channel-a" } }],
tools: [],
});
const { ensurePluginRegistryLoaded } = await import("./plugin-registry.js");
ensurePluginRegistryLoaded({ scope: "all" });
expect(mocks.loadOpenClawPlugins).toHaveBeenCalledTimes(1);
expect(mocks.loadOpenClawPlugins).toHaveBeenCalledWith(
expect.objectContaining({
config,
throwOnLoadError: true,
workspaceDir: "/tmp/workspace",
}),
);
});
});