import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; import type { AuthProfileStore } from "../agents/auth-profiles.js"; import type { OpenClawConfig } from "../config/config.js"; import { createEmptyPluginRegistry } from "../plugins/registry.js"; import { setActivePluginRegistry } from "../plugins/runtime.js"; import type { PluginWebSearchProviderEntry } from "../plugins/types.js"; type WebProviderUnderTest = "brave" | "gemini" | "grok" | "kimi" | "perplexity" | "firecrawl"; const { resolvePluginWebSearchProvidersMock } = vi.hoisted(() => ({ resolvePluginWebSearchProvidersMock: vi.fn(() => buildTestWebSearchProviders()), })); vi.mock("../plugins/web-search-providers.runtime.js", () => ({ resolvePluginWebSearchProviders: resolvePluginWebSearchProvidersMock, })); const OPENAI_ENV_KEY_REF = { source: "env", provider: "default", id: "OPENAI_API_KEY" } as const; function asConfig(value: unknown): OpenClawConfig { return value as OpenClawConfig; } function createTestProvider(params: { id: WebProviderUnderTest; pluginId: string; order: number; }): PluginWebSearchProviderEntry { const credentialPath = `plugins.entries.${params.pluginId}.config.webSearch.apiKey`; const readSearchConfigKey = (searchConfig?: Record): unknown => { const providerConfig = searchConfig?.[params.id] && typeof searchConfig[params.id] === "object" ? (searchConfig[params.id] as { apiKey?: unknown }) : undefined; return providerConfig?.apiKey ?? searchConfig?.apiKey; }; return { pluginId: params.pluginId, id: params.id, label: params.id, hint: `${params.id} test provider`, envVars: [`${params.id.toUpperCase()}_API_KEY`], placeholder: `${params.id}-...`, signupUrl: `https://example.com/${params.id}`, autoDetectOrder: params.order, credentialPath, inactiveSecretPaths: [credentialPath], getCredentialValue: readSearchConfigKey, setCredentialValue: (searchConfigTarget, value) => { const providerConfig = params.id === "brave" || params.id === "firecrawl" ? searchConfigTarget : ((searchConfigTarget[params.id] ??= {}) as { apiKey?: unknown }); providerConfig.apiKey = value; }, getConfiguredCredentialValue: (config) => (config?.plugins?.entries?.[params.pluginId]?.config as { webSearch?: { apiKey?: unknown } }) ?.webSearch?.apiKey, setConfiguredCredentialValue: (configTarget, value) => { const plugins = (configTarget.plugins ??= {}) as { entries?: Record }; const entries = (plugins.entries ??= {}); const entry = (entries[params.pluginId] ??= {}) as { config?: Record }; const config = (entry.config ??= {}); const webSearch = (config.webSearch ??= {}) as { apiKey?: unknown }; webSearch.apiKey = value; }, resolveRuntimeMetadata: params.id === "perplexity" ? () => ({ perplexityTransport: "search_api" as const, }) : undefined, createTool: () => null, }; } function buildTestWebSearchProviders(): PluginWebSearchProviderEntry[] { return [ createTestProvider({ id: "brave", pluginId: "brave", order: 10 }), createTestProvider({ id: "gemini", pluginId: "google", order: 20 }), createTestProvider({ id: "grok", pluginId: "xai", order: 30 }), createTestProvider({ id: "kimi", pluginId: "moonshot", order: 40 }), createTestProvider({ id: "perplexity", pluginId: "perplexity", order: 50 }), createTestProvider({ id: "firecrawl", pluginId: "firecrawl", order: 60 }), ]; } function loadAuthStoreWithProfiles(profiles: AuthProfileStore["profiles"]): AuthProfileStore { return { version: 1, profiles, }; } let clearConfigCache: typeof import("../config/config.js").clearConfigCache; let clearRuntimeConfigSnapshot: typeof import("../config/config.js").clearRuntimeConfigSnapshot; let clearSecretsRuntimeSnapshot: typeof import("./runtime.js").clearSecretsRuntimeSnapshot; let prepareSecretsRuntimeSnapshot: typeof import("./runtime.js").prepareSecretsRuntimeSnapshot; describe("secrets runtime snapshot core auth stores", () => { beforeAll(async () => { ({ clearConfigCache, clearRuntimeConfigSnapshot } = await import("../config/config.js")); ({ clearSecretsRuntimeSnapshot, prepareSecretsRuntimeSnapshot } = await import("./runtime.js")); }); beforeEach(() => { resolvePluginWebSearchProvidersMock.mockReset(); resolvePluginWebSearchProvidersMock.mockReturnValue(buildTestWebSearchProviders()); }); afterEach(() => { setActivePluginRegistry(createEmptyPluginRegistry()); clearSecretsRuntimeSnapshot(); clearRuntimeConfigSnapshot(); clearConfigCache(); }); it("resolves auth profile SecretRefs from env and inline placeholders", async () => { const snapshot = await prepareSecretsRuntimeSnapshot({ config: asConfig({}), env: { OPENAI_API_KEY: "sk-env-openai", GITHUB_TOKEN: "ghp-env-token", }, agentDirs: ["/tmp/openclaw-agent-main"], loadablePluginOrigins: new Map(), loadAuthStore: () => loadAuthStoreWithProfiles({ "openai:default": { type: "api_key", provider: "openai", key: "old-openai", keyRef: OPENAI_ENV_KEY_REF, }, "github-copilot:default": { type: "token", provider: "github-copilot", token: "old-gh", tokenRef: { source: "env", provider: "default", id: "GITHUB_TOKEN" }, }, }), }); expect(snapshot.warnings.map((warning) => warning.path)).toEqual( expect.arrayContaining([ "/tmp/openclaw-agent-main.auth-profiles.openai:default.key", "/tmp/openclaw-agent-main.auth-profiles.github-copilot:default.token", ]), ); expect(snapshot.authStores[0]?.store.profiles["openai:default"]).toMatchObject({ type: "api_key", key: "sk-env-openai", }); expect(snapshot.authStores[0]?.store.profiles["github-copilot:default"]).toMatchObject({ type: "token", token: "ghp-env-token", }); }); });