mirror of https://github.com/openclaw/openclaw.git
185 lines
5.4 KiB
TypeScript
185 lines
5.4 KiB
TypeScript
import { describe, expect, it, vi } from "vitest";
|
|
import { createWizardPrompter } from "../../test/helpers/wizard-prompter.js";
|
|
import { createNonExitingRuntime } from "../runtime.js";
|
|
import { runSearchSetupFlow } from "./search-setup.js";
|
|
|
|
const mockGrokProvider = vi.hoisted(() => ({
|
|
id: "grok",
|
|
pluginId: "xai",
|
|
label: "Grok",
|
|
hint: "Search with xAI",
|
|
docsUrl: "https://docs.openclaw.ai/tools/web",
|
|
requiresCredential: true,
|
|
credentialLabel: "xAI API key",
|
|
placeholder: "xai-...",
|
|
signupUrl: "https://x.ai/api",
|
|
envVars: ["XAI_API_KEY"],
|
|
onboardingScopes: ["text-inference"],
|
|
credentialPath: "plugins.entries.xai.config.webSearch.apiKey",
|
|
getCredentialValue: (search?: Record<string, unknown>) => search?.apiKey,
|
|
setCredentialValue: (searchConfigTarget: Record<string, unknown>, value: unknown) => {
|
|
searchConfigTarget.apiKey = value;
|
|
},
|
|
getConfiguredCredentialValue: (config?: Record<string, unknown>) =>
|
|
(
|
|
config?.plugins as
|
|
| {
|
|
entries?: Record<
|
|
string,
|
|
{
|
|
config?: {
|
|
webSearch?: { apiKey?: unknown };
|
|
};
|
|
}
|
|
>;
|
|
}
|
|
| undefined
|
|
)?.entries?.xai?.config?.webSearch?.apiKey,
|
|
setConfiguredCredentialValue: (configTarget: Record<string, unknown>, value: unknown) => {
|
|
const plugins = (configTarget.plugins ??= {}) as Record<string, unknown>;
|
|
const entries = (plugins.entries ??= {}) as Record<string, unknown>;
|
|
const xaiEntry = (entries.xai ??= {}) as Record<string, unknown>;
|
|
const xaiConfig = (xaiEntry.config ??= {}) as Record<string, unknown>;
|
|
const webSearch = (xaiConfig.webSearch ??= {}) as Record<string, unknown>;
|
|
webSearch.apiKey = value;
|
|
},
|
|
runSetup: async ({
|
|
config,
|
|
prompter,
|
|
}: {
|
|
config: Record<string, unknown>;
|
|
prompter: { select: (params: Record<string, unknown>) => Promise<string> };
|
|
}) => {
|
|
const enableXSearch = await prompter.select({
|
|
message: "Enable x_search",
|
|
options: [
|
|
{ value: "yes", label: "Yes" },
|
|
{ value: "no", label: "No" },
|
|
],
|
|
});
|
|
if (enableXSearch !== "yes") {
|
|
return config;
|
|
}
|
|
const model = await prompter.select({
|
|
message: "Grok model",
|
|
options: [{ value: "grok-4-1-fast", label: "grok-4-1-fast" }],
|
|
});
|
|
const pluginEntries = (config.plugins as { entries?: Record<string, unknown> } | undefined)
|
|
?.entries;
|
|
const existingXaiEntry = pluginEntries?.xai as Record<string, unknown> | undefined;
|
|
const existingXaiConfig = (
|
|
pluginEntries?.xai as { config?: Record<string, unknown> } | undefined
|
|
)?.config;
|
|
return {
|
|
...config,
|
|
plugins: {
|
|
...(config.plugins as Record<string, unknown> | undefined),
|
|
entries: {
|
|
...pluginEntries,
|
|
xai: {
|
|
...existingXaiEntry,
|
|
config: {
|
|
...existingXaiConfig,
|
|
xSearch: {
|
|
enabled: true,
|
|
model,
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
};
|
|
},
|
|
}));
|
|
|
|
vi.mock("../plugins/bundled-web-search.js", () => ({
|
|
listBundledWebSearchProviders: () => [mockGrokProvider],
|
|
resolveBundledWebSearchPluginId: (providerId: string | undefined) =>
|
|
providerId === "grok" ? "xai" : undefined,
|
|
}));
|
|
|
|
vi.mock("../plugins/web-search-providers.runtime.js", () => ({
|
|
resolvePluginWebSearchProviders: () => [mockGrokProvider],
|
|
}));
|
|
|
|
describe("runSearchSetupFlow", () => {
|
|
it("runs provider-owned setup after selecting Grok web search", async () => {
|
|
const select = vi
|
|
.fn()
|
|
.mockResolvedValueOnce("grok")
|
|
.mockResolvedValueOnce("yes")
|
|
.mockResolvedValueOnce("grok-4-1-fast");
|
|
const text = vi.fn().mockResolvedValue("xai-test-key");
|
|
const prompter = createWizardPrompter({
|
|
select: select as never,
|
|
text: text as never,
|
|
});
|
|
|
|
const next = await runSearchSetupFlow(
|
|
{ plugins: { allow: ["xai"] } },
|
|
createNonExitingRuntime(),
|
|
prompter,
|
|
);
|
|
|
|
expect(next.plugins?.entries?.xai?.config?.webSearch).toMatchObject({
|
|
apiKey: "xai-test-key",
|
|
});
|
|
expect(next.tools?.web?.search).toMatchObject({
|
|
provider: "grok",
|
|
enabled: true,
|
|
});
|
|
expect(next.plugins?.entries?.xai?.config?.xSearch).toMatchObject({
|
|
enabled: true,
|
|
model: "grok-4-1-fast",
|
|
});
|
|
});
|
|
|
|
it("preserves disabled web_search state while still allowing provider-owned x_search setup", async () => {
|
|
const select = vi
|
|
.fn()
|
|
.mockResolvedValueOnce("grok")
|
|
.mockResolvedValueOnce("yes")
|
|
.mockResolvedValueOnce("grok-4-1-fast");
|
|
const prompter = createWizardPrompter({
|
|
select: select as never,
|
|
});
|
|
|
|
const next = await runSearchSetupFlow(
|
|
{
|
|
plugins: {
|
|
allow: ["xai"],
|
|
entries: {
|
|
xai: {
|
|
enabled: true,
|
|
config: {
|
|
webSearch: {
|
|
apiKey: "xai-test-key",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
tools: {
|
|
web: {
|
|
search: {
|
|
provider: "grok",
|
|
enabled: false,
|
|
},
|
|
},
|
|
},
|
|
},
|
|
createNonExitingRuntime(),
|
|
prompter,
|
|
);
|
|
|
|
expect(next.tools?.web?.search).toMatchObject({
|
|
provider: "grok",
|
|
enabled: false,
|
|
});
|
|
expect(next.plugins?.entries?.xai?.config?.xSearch).toMatchObject({
|
|
enabled: true,
|
|
model: "grok-4-1-fast",
|
|
});
|
|
});
|
|
});
|