openclaw/src/plugins/config-state.test.ts

257 lines
7.2 KiB
TypeScript

import { describe, expect, it } from "vitest";
import {
normalizePluginsConfig,
resolveEffectiveEnableState,
resolveEnableState,
} from "./config-state.js";
function normalizeVoiceCallEntry(entry: Record<string, unknown>) {
return normalizePluginsConfig({
entries: {
"voice-call": entry,
},
}).entries["voice-call"];
}
function expectResolvedEnableState(
params: Parameters<typeof resolveEnableState>,
expected: ReturnType<typeof resolveEnableState>,
) {
expect(resolveEnableState(...params)).toEqual(expected);
}
function expectMemoryPluginState(
config: Parameters<typeof normalizePluginsConfig>[0],
expected: ReturnType<typeof resolveEnableState>,
) {
expect(resolveEnableState("memory-core", "bundled", normalizePluginsConfig(config))).toEqual(
expected,
);
}
describe("normalizePluginsConfig", () => {
it.each([
[{}, "memory-core"],
[{ slots: { memory: "custom-memory" } }, "custom-memory"],
[{ slots: { memory: "none" } }, null],
[{ slots: { memory: "None" } }, null],
[{ slots: { memory: " custom-memory " } }, "custom-memory"],
[{ slots: { memory: "" } }, "memory-core"],
[{ slots: { memory: " " } }, "memory-core"],
] as const)("normalizes memory slot for %o", (config, expected) => {
expect(normalizePluginsConfig(config).slots.memory).toBe(expected);
});
it.each([
{
name: "normalizes plugin hook policy flags",
entry: {
hooks: {
allowPromptInjection: false,
},
},
expectedHooks: {
allowPromptInjection: false,
},
},
{
name: "drops invalid plugin hook policy values",
entry: {
hooks: {
allowPromptInjection: "nope",
} as unknown as { allowPromptInjection: boolean },
},
expectedHooks: undefined,
},
] as const)("$name", ({ entry, expectedHooks }) => {
expect(normalizeVoiceCallEntry(entry)?.hooks).toEqual(expectedHooks);
});
it.each([
{
name: "normalizes plugin subagent override policy settings",
subagent: {
allowModelOverride: true,
allowedModels: [" anthropic/claude-sonnet-4-6 ", "", "openai/gpt-5.4"],
},
expected: {
allowModelOverride: true,
hasAllowedModelsConfig: true,
allowedModels: ["anthropic/claude-sonnet-4-6", "openai/gpt-5.4"],
},
},
{
name: "preserves explicit subagent allowlist intent even when all entries are invalid",
subagent: {
allowModelOverride: true,
allowedModels: [42, null, "anthropic"],
} as unknown as { allowModelOverride: boolean; allowedModels: string[] },
expected: {
allowModelOverride: true,
hasAllowedModelsConfig: true,
allowedModels: ["anthropic"],
},
},
{
name: "keeps explicit invalid subagent allowlist config visible to callers",
subagent: {
allowModelOverride: "nope",
allowedModels: [42, null],
} as unknown as { allowModelOverride: boolean; allowedModels: string[] },
expected: {
hasAllowedModelsConfig: true,
},
},
] as const)("$name", ({ subagent, expected }) => {
expect(normalizeVoiceCallEntry({ subagent })?.subagent).toEqual(expected);
});
it("normalizes legacy plugin ids to their merged bundled plugin id", () => {
const result = normalizePluginsConfig({
allow: ["openai-codex", "google-gemini-cli", "minimax-portal-auth"],
deny: ["openai-codex", "google-gemini-cli", "minimax-portal-auth"],
entries: {
"openai-codex": {
enabled: true,
},
"google-gemini-cli": {
enabled: true,
},
"minimax-portal-auth": {
enabled: false,
},
},
});
expect(result.allow).toEqual(["openai", "google", "minimax"]);
expect(result.deny).toEqual(["openai", "google", "minimax"]);
expect(result.entries.openai?.enabled).toBe(true);
expect(result.entries.google?.enabled).toBe(true);
expect(result.entries.minimax?.enabled).toBe(false);
});
});
describe("resolveEffectiveEnableState", () => {
function resolveBundledTelegramState(config: Parameters<typeof normalizePluginsConfig>[0]) {
const normalized = normalizePluginsConfig(config);
return resolveEffectiveEnableState({
id: "telegram",
origin: "bundled",
config: normalized,
rootConfig: {
channels: {
telegram: {
enabled: true,
},
},
},
});
}
it.each([
[{ enabled: true }, { enabled: true }],
[
{
enabled: true,
entries: {
telegram: {
enabled: false,
},
},
},
{ enabled: false, reason: "disabled in config" },
],
] as const)("resolves bundled telegram state for %o", (config, expected) => {
expect(resolveBundledTelegramState(config)).toEqual(expected);
});
});
describe("resolveEnableState", () => {
it.each([
[
"openai",
"bundled",
normalizePluginsConfig({}),
undefined,
{ enabled: false, reason: "bundled (disabled by default)" },
],
["openai", "bundled", normalizePluginsConfig({}), true, { enabled: true }],
["google", "bundled", normalizePluginsConfig({}), true, { enabled: true }],
["profile-aware", "bundled", normalizePluginsConfig({}), true, { enabled: true }],
] as const)(
"resolves %s enable state for origin=%s manifestEnabledByDefault=%s",
(id, origin, config, manifestEnabledByDefault, expected) => {
expectResolvedEnableState([id, origin, config, manifestEnabledByDefault], expected);
},
);
it("keeps the selected memory slot plugin enabled even when omitted from plugins.allow", () => {
expectMemoryPluginState(
{
allow: ["telegram"],
slots: { memory: "memory-core" },
},
{ enabled: true },
);
});
it("keeps explicit disable authoritative for the selected memory slot plugin", () => {
expectMemoryPluginState(
{
allow: ["telegram"],
slots: { memory: "memory-core" },
entries: {
"memory-core": {
enabled: false,
},
},
},
{ enabled: false, reason: "disabled in config" },
);
});
it.each([
[
normalizePluginsConfig({}),
{
enabled: false,
reason: "workspace plugin (disabled by default)",
},
],
[
normalizePluginsConfig({
allow: ["workspace-helper"],
}),
{ enabled: true },
],
[
normalizePluginsConfig({
entries: {
"workspace-helper": {
enabled: true,
},
},
}),
{ enabled: true },
],
] as const)("resolves workspace-helper enable state for %o", (config, expected) => {
expect(resolveEnableState("workspace-helper", "workspace", config)).toEqual(expected);
});
it("does not let the default memory slot auto-enable an untrusted workspace plugin", () => {
expectResolvedEnableState(
[
"memory-core",
"workspace",
normalizePluginsConfig({
slots: { memory: "memory-core" },
}),
],
{
enabled: false,
reason: "workspace plugin (disabled by default)",
},
);
});
});