Files
openclaw/src/agents/openclaw-tools.browser-plugin.integration.test.ts
2026-05-03 19:33:00 -07:00

305 lines
8.6 KiB
TypeScript

import { afterEach, describe, expect, it, vi } from "vitest";
import type { OpenClawConfig } from "../config/config.js";
import { resetConfigRuntimeState, setRuntimeConfigSnapshot } from "../config/config.js";
import { activateSecretsRuntimeSnapshot, clearSecretsRuntimeSnapshot } from "../secrets/runtime.js";
import { resolveOpenClawPluginToolsForOptions } from "./openclaw-plugin-tools.js";
const hoisted = vi.hoisted(() => ({
resolvePluginTools: vi.fn(),
}));
vi.mock("../plugins/tools.js", () => ({
resolvePluginTools: (...args: unknown[]) => hoisted.resolvePluginTools(...args),
}));
describe("createOpenClawTools browser plugin integration", () => {
afterEach(() => {
hoisted.resolvePluginTools.mockReset();
clearSecretsRuntimeSnapshot();
resetConfigRuntimeState();
});
it("keeps the browser tool returned by plugin resolution", () => {
hoisted.resolvePluginTools.mockReturnValue([
{
name: "browser",
description: "browser fixture tool",
parameters: {
type: "object",
properties: {},
},
async execute() {
return {
content: [{ type: "text", text: "ok" }],
};
},
},
]);
const config = {
plugins: {
allow: ["browser"],
},
} as OpenClawConfig;
const tools = resolveOpenClawPluginToolsForOptions({
options: { config },
resolvedConfig: config,
});
expect(tools.map((tool) => tool.name)).toContain("browser");
});
it("omits the browser tool when plugin resolution returns no browser tool", () => {
hoisted.resolvePluginTools.mockReturnValue([]);
const config = {
plugins: {
allow: ["browser"],
entries: {
browser: {
enabled: false,
},
},
},
} as OpenClawConfig;
const tools = resolveOpenClawPluginToolsForOptions({
options: { config },
resolvedConfig: config,
});
expect(tools.map((tool) => tool.name)).not.toContain("browser");
});
it("forwards fsPolicy into plugin tool context", async () => {
let capturedContext: { fsPolicy?: { workspaceOnly: boolean } } | undefined;
hoisted.resolvePluginTools.mockImplementation((params: unknown) => {
const resolvedParams = params as { context?: { fsPolicy?: { workspaceOnly: boolean } } };
capturedContext = resolvedParams.context;
return [
{
name: "browser",
description: "browser fixture tool",
parameters: {
type: "object",
properties: {},
},
async execute() {
return {
content: [{ type: "text", text: "ok" }],
details: { workspaceOnly: capturedContext?.fsPolicy?.workspaceOnly ?? null },
};
},
},
];
});
const tools = resolveOpenClawPluginToolsForOptions({
options: {
config: {
plugins: {
allow: ["browser"],
},
} as OpenClawConfig,
fsPolicy: { workspaceOnly: true },
},
resolvedConfig: {
plugins: {
allow: ["browser"],
},
} as OpenClawConfig,
});
const browserTool = tools.find((tool) => tool.name === "browser");
expect(browserTool).toBeDefined();
if (!browserTool) {
throw new Error("expected browser tool");
}
const result = await browserTool.execute("tool-call", {});
const details = (result.details ?? {}) as { workspaceOnly?: boolean | null };
expect(details.workspaceOnly).toBe(true);
});
it("forwards gateway subagent binding to plugin resolution", () => {
hoisted.resolvePluginTools.mockReturnValue([]);
const config = {
plugins: {
allow: ["browser"],
},
} as OpenClawConfig;
resolveOpenClawPluginToolsForOptions({
options: { config, allowGatewaySubagentBinding: true },
resolvedConfig: config,
});
expect(hoisted.resolvePluginTools).toHaveBeenCalledWith(
expect.objectContaining({
allowGatewaySubagentBinding: true,
}),
);
});
it("forwards plugin tool deny policy to plugin resolution", () => {
hoisted.resolvePluginTools.mockReturnValue([]);
const config = {
plugins: {
allow: ["browser"],
},
} as OpenClawConfig;
resolveOpenClawPluginToolsForOptions({
options: {
config,
pluginToolAllowlist: ["*"],
pluginToolDenylist: ["browser"],
},
resolvedConfig: config,
});
expect(hoisted.resolvePluginTools).toHaveBeenCalledWith(
expect.objectContaining({
toolAllowlist: ["*"],
toolDenylist: ["browser"],
}),
);
});
it("does not pass a stale active snapshot as plugin runtime config for a resolved run config", () => {
const staleSourceConfig = {
plugins: {
allow: ["old-plugin"],
},
} as OpenClawConfig;
const staleRuntimeConfig = {
plugins: {
allow: ["old-plugin"],
},
} as OpenClawConfig;
const resolvedRunConfig = {
plugins: {
allow: ["browser"],
},
tools: {
experimental: {
planTool: true,
},
},
} as OpenClawConfig;
let capturedRuntimeConfig: OpenClawConfig | undefined;
hoisted.resolvePluginTools.mockImplementation((params: unknown) => {
capturedRuntimeConfig = (params as { context?: { runtimeConfig?: OpenClawConfig } }).context
?.runtimeConfig;
return [];
});
activateSecretsRuntimeSnapshot({
sourceConfig: staleSourceConfig,
config: staleRuntimeConfig,
authStores: [],
warnings: [],
webTools: {
search: {
providerSource: "none",
diagnostics: [],
},
fetch: {
providerSource: "none",
diagnostics: [],
},
diagnostics: [],
},
});
resolveOpenClawPluginToolsForOptions({
options: { config: resolvedRunConfig },
resolvedConfig: resolvedRunConfig,
});
expect(capturedRuntimeConfig).toBe(resolvedRunConfig);
});
it("does not let a source-less pinned config snapshot override explicit plugin tool config", () => {
const pinnedRuntimeConfig = {
plugins: {
allow: ["old-plugin"],
},
} as OpenClawConfig;
const explicitConfig = {
plugins: {
allow: ["browser"],
},
tools: {
experimental: {
planTool: true,
},
},
} as OpenClawConfig;
let capturedRuntimeConfig: OpenClawConfig | undefined;
let getRuntimeConfig: (() => OpenClawConfig | undefined) | undefined;
hoisted.resolvePluginTools.mockImplementation((params: unknown) => {
const context = (
params as {
context?: {
runtimeConfig?: OpenClawConfig;
getRuntimeConfig?: () => OpenClawConfig | undefined;
};
}
).context;
capturedRuntimeConfig = context?.runtimeConfig;
getRuntimeConfig = context?.getRuntimeConfig;
return [];
});
setRuntimeConfigSnapshot(pinnedRuntimeConfig);
resolveOpenClawPluginToolsForOptions({
options: { config: explicitConfig },
resolvedConfig: explicitConfig,
});
expect(capturedRuntimeConfig).toBe(explicitConfig);
expect(getRuntimeConfig?.()).toBe(explicitConfig);
});
it("exposes a live runtime config getter to plugin tool factories", () => {
const sourceConfig = {
plugins: {
allow: ["memory-core"],
},
} as OpenClawConfig;
const firstRuntimeConfig = {
plugins: {
allow: ["memory-core"],
entries: { "memory-core": { enabled: true } },
},
} as OpenClawConfig;
const nextRuntimeConfig = {
plugins: {
allow: ["memory-core"],
entries: { "memory-core": { enabled: false } },
},
} as OpenClawConfig;
let getRuntimeConfig: (() => OpenClawConfig | undefined) | undefined;
hoisted.resolvePluginTools.mockImplementation((params: unknown) => {
getRuntimeConfig = (
params as { context?: { getRuntimeConfig?: () => OpenClawConfig | undefined } }
).context?.getRuntimeConfig;
return [];
});
setRuntimeConfigSnapshot(firstRuntimeConfig, sourceConfig);
resolveOpenClawPluginToolsForOptions({
options: { config: sourceConfig },
resolvedConfig: sourceConfig,
});
expect(getRuntimeConfig?.()).toStrictEqual(firstRuntimeConfig);
setRuntimeConfigSnapshot(nextRuntimeConfig, sourceConfig);
expect(getRuntimeConfig?.()).toStrictEqual(nextRuntimeConfig);
expect(getRuntimeConfig?.()?.plugins?.entries?.["memory-core"]?.enabled).toBe(false);
});
});