Files
openclaw/src/agents/tools/web-tool-runtime-context.test.ts
Peter Steinberger 0177a4b6c9 fix(gateway): speed up secrets startup
Summary:
- Split the lightweight secrets runtime state and auth-store cache from the full secrets runtime.
- Use the startup fast path whenever gateway startup has no SecretRef values, while preserving cleanup and refresh semantics.
- Add regression coverage for startup-only empty auth-store snapshots and update affected gateway/tool tests.

Verification:
- pnpm test src/secrets/runtime.fast-path.test.ts src/secrets/runtime-state.test.ts src/gateway/server-startup-config.secrets.test.ts src/gateway/server-import-boundary.test.ts src/gateway/server-aux-handlers.test.ts src/gateway/server-methods/config.shared-auth.test.ts src/agents/tools/web-tools.enabled-defaults.test.ts src/agents/tools/web-tool-runtime-context.test.ts -- --reporter=verbose
- pnpm build
- pnpm format:check -- src/agents/tools/web-tools.enabled-defaults.test.ts src/secrets/runtime-command-secrets.ts src/secrets/runtime-fast-path.ts src/secrets/runtime.fast-path.test.ts src/agents/auth-profiles/store.ts src/agents/auth-profiles/store-cache.ts src/secrets/runtime-state.ts src/secrets/runtime-state.test.ts src/gateway/server-startup-config.ts
- codex-review --mode branch
- isolated gateway token-auth smoke: openclaw gateway run + openclaw gateway health returned ok: true
- GitHub CI on PR #83031 green; newer Real behavior proof run passed on current SHA f27ed3f7ce.

Co-authored-by: samzong <samzong.lu@gmail.com>
2026-05-17 10:55:41 +01:00

164 lines
5.9 KiB
TypeScript

import { beforeEach, describe, expect, it, vi } from "vitest";
import {
resolveWebFetchToolRuntimeContext,
resolveWebSearchToolRuntimeContext,
} from "./web-tool-runtime-context.js";
const mocks = vi.hoisted(() => ({
getActiveRuntimeWebToolsMetadata: vi.fn(),
getActiveSecretsRuntimeSnapshot: vi.fn(),
resolveManifestContractOwnerPluginId: vi.fn(),
}));
vi.mock("../../plugins/plugin-registry.js", () => ({
resolveManifestContractOwnerPluginId: mocks.resolveManifestContractOwnerPluginId,
}));
vi.mock("../../secrets/runtime-web-tools-state.js", () => ({
getActiveRuntimeWebToolsMetadata: mocks.getActiveRuntimeWebToolsMetadata,
}));
vi.mock("../../secrets/runtime-state.js", () => ({
getActiveSecretsRuntimeSnapshot: mocks.getActiveSecretsRuntimeSnapshot,
}));
function latestOwnerLookupParams(): Record<string, unknown> {
const params = mocks.resolveManifestContractOwnerPluginId.mock.calls.at(-1)?.[0];
if (!params || typeof params !== "object") {
throw new Error("expected owner lookup params");
}
return params as Record<string, unknown>;
}
describe("web tool runtime context", () => {
beforeEach(() => {
mocks.getActiveRuntimeWebToolsMetadata.mockReset();
mocks.getActiveRuntimeWebToolsMetadata.mockReturnValue(null);
mocks.getActiveSecretsRuntimeSnapshot.mockReset();
mocks.getActiveSecretsRuntimeSnapshot.mockReturnValue(null);
mocks.resolveManifestContractOwnerPluginId.mockReset();
mocks.resolveManifestContractOwnerPluginId.mockReturnValue(undefined);
});
it("late-binds search config and metadata from active runtime before captured options", async () => {
const runtimeConfig = {
tools: { web: { search: { provider: "perplexity" } } },
};
mocks.getActiveSecretsRuntimeSnapshot.mockReturnValue({ config: runtimeConfig });
mocks.getActiveRuntimeWebToolsMetadata.mockReturnValue({
search: {
providerConfigured: "perplexity",
providerSource: "configured",
selectedProvider: "perplexity",
selectedProviderKeySource: "config",
diagnostics: [],
},
fetch: {
providerSource: "none",
diagnostics: [],
},
diagnostics: [],
});
const resolved = resolveWebSearchToolRuntimeContext({
config: { tools: { web: { search: { provider: "brave" } } } },
lateBindRuntimeConfig: true,
runtimeWebSearch: {
providerConfigured: "brave",
providerSource: "configured",
selectedProvider: "brave",
selectedProviderKeySource: "config",
diagnostics: [],
},
});
expect(resolved.config).toBe(runtimeConfig);
expect(resolved.runtimeWebSearch?.selectedProvider).toBe("perplexity");
const ownerLookup = latestOwnerLookupParams();
expect(ownerLookup.contract).toBe("webSearchProviders");
expect(ownerLookup.value).toBe("perplexity");
expect(ownerLookup).not.toHaveProperty("origin");
expect(ownerLookup.config).toBe(runtimeConfig);
});
it("falls back to captured search config and runtime metadata when active globals are missing", async () => {
const capturedConfig = {
tools: { web: { search: { provider: "brave" } } },
};
const resolved = resolveWebSearchToolRuntimeContext({
config: capturedConfig,
lateBindRuntimeConfig: true,
runtimeWebSearch: {
providerConfigured: "brave",
providerSource: "configured",
selectedProvider: "brave",
selectedProviderKeySource: "config",
diagnostics: [],
},
});
expect(resolved.config).toBe(capturedConfig);
expect(resolved.runtimeWebSearch?.selectedProvider).toBe("brave");
const ownerLookup = latestOwnerLookupParams();
expect(ownerLookup.contract).toBe("webSearchProviders");
expect(ownerLookup.value).toBe("brave");
expect(ownerLookup).not.toHaveProperty("origin");
expect(ownerLookup.config).toBe(capturedConfig);
});
it("uses configured provider ids when runtime metadata is absent", () => {
resolveWebSearchToolRuntimeContext({
config: { tools: { web: { search: { provider: "Brave" } } } },
});
const ownerLookup = latestOwnerLookupParams();
expect(ownerLookup.contract).toBe("webSearchProviders");
expect(ownerLookup.value).toBe("brave");
expect(ownerLookup).not.toHaveProperty("origin");
expect(ownerLookup.config).toEqual({
tools: { web: { search: { provider: "Brave" } } },
});
});
it("treats resolved global provider owners as explicit selections", async () => {
mocks.resolveManifestContractOwnerPluginId.mockReturnValue("brave");
const { resolveWebSearchToolRuntimeContext } = await import("./web-tool-runtime-context.js");
const resolved = resolveWebSearchToolRuntimeContext({
config: { tools: { web: { search: { provider: "brave" } } } },
});
expect(resolved.preferRuntimeProviders).toBe(false);
expect(mocks.resolveManifestContractOwnerPluginId.mock.calls.at(-1)?.[0]).not.toHaveProperty(
"origin",
);
});
it("keeps runtime providers disabled for bundled fetch owners", async () => {
mocks.resolveManifestContractOwnerPluginId.mockReturnValue("firecrawl");
const resolved = resolveWebFetchToolRuntimeContext({
config: { tools: { web: { fetch: { provider: "firecrawl" } } } },
});
expect(resolved.preferRuntimeProviders).toBe(false);
const ownerLookup = latestOwnerLookupParams();
expect(ownerLookup.contract).toBe("webFetchProviders");
expect(ownerLookup.value).toBe("firecrawl");
expect(ownerLookup.origin).toBe("bundled");
expect(ownerLookup.config).toEqual({
tools: { web: { fetch: { provider: "firecrawl" } } },
});
});
it("keeps runtime provider discovery enabled when no provider is selected", () => {
const resolved = resolveWebFetchToolRuntimeContext({
config: {},
});
expect(resolved.preferRuntimeProviders).toBe(true);
expect(mocks.resolveManifestContractOwnerPluginId).not.toHaveBeenCalled();
});
});