Files
openclaw/src/plugins/contracts/registry.contract.test.ts
Vincent Koc 38d2faee20 !feat(plugins): add web fetch provider boundary (#59465)
* feat(plugins): add web fetch provider boundary

* feat(plugins): add web fetch provider modules

* refactor(web-fetch): remove remaining core firecrawl fetch config

* fix(web-fetch): address review follow-ups

* fix(web-fetch): harden provider runtime boundaries

* fix(web-fetch): restore firecrawl compare helper

* fix(web-fetch): restore env-based provider autodetect

* fix(web-fetch): tighten provider hardening

* fix(web-fetch): restore fetch autodetect and compat args

* chore(changelog): note firecrawl fetch config break
2026-04-02 20:25:19 +09:00

153 lines
5.1 KiB
TypeScript

import { describe, expect, it } from "vitest";
import { resolveBundledWebFetchPluginIds } from "../bundled-web-fetch.js";
import { resolveBundledWebSearchPluginIds } from "../bundled-web-search.js";
import { loadPluginManifestRegistry } from "../manifest-registry.js";
import {
imageGenerationProviderContractRegistry,
mediaUnderstandingProviderContractRegistry,
pluginRegistrationContractRegistry,
providerContractLoadError,
providerContractPluginIds,
resolveWebFetchProviderContractEntriesForPluginId,
resolveWebSearchProviderContractEntriesForPluginId,
speechProviderContractRegistry,
webFetchProviderContractRegistry,
} from "./registry.js";
import { uniqueSortedStrings } from "./testkit.js";
const REGISTRY_CONTRACT_TIMEOUT_MS = 300_000;
describe("plugin contract registry", () => {
function expectUniqueIds(ids: readonly string[]) {
expect(ids).toEqual([...new Set(ids)]);
}
function expectRegistryPluginIds(params: {
actualPluginIds: readonly string[];
predicate: (plugin: {
origin: string;
providers: unknown[];
contracts?: { speechProviders?: unknown[] };
}) => boolean;
}) {
expect(uniqueSortedStrings(params.actualPluginIds)).toEqual(
resolveBundledManifestPluginIds(params.predicate),
);
}
function resolveBundledManifestPluginIds(
predicate: (plugin: {
origin: string;
providers: unknown[];
contracts?: { speechProviders?: unknown[] };
}) => boolean,
) {
return loadPluginManifestRegistry({})
.plugins.filter(predicate)
.map((plugin) => plugin.id)
.toSorted((left, right) => left.localeCompare(right));
}
it("loads bundled non-provider capability registries without import-time failure", () => {
expect(providerContractLoadError).toBeUndefined();
expect(pluginRegistrationContractRegistry.length).toBeGreaterThan(0);
});
it.each([
{
name: "does not duplicate bundled provider ids",
ids: () => pluginRegistrationContractRegistry.flatMap((entry) => entry.providerIds),
},
{
name: "does not duplicate bundled web fetch provider ids",
ids: () => pluginRegistrationContractRegistry.flatMap((entry) => entry.webFetchProviderIds),
},
{
name: "does not duplicate bundled web search provider ids",
ids: () => pluginRegistrationContractRegistry.flatMap((entry) => entry.webSearchProviderIds),
},
{
name: "does not duplicate bundled media provider ids",
ids: () => mediaUnderstandingProviderContractRegistry.map((entry) => entry.provider.id),
},
{
name: "does not duplicate bundled image-generation provider ids",
ids: () => imageGenerationProviderContractRegistry.map((entry) => entry.provider.id),
},
] as const)("$name", ({ ids }) => {
expectUniqueIds(ids());
});
it(
"does not duplicate bundled speech provider ids",
{ timeout: REGISTRY_CONTRACT_TIMEOUT_MS },
() => {
expectUniqueIds(speechProviderContractRegistry.map((entry) => entry.provider.id));
},
);
it("covers every bundled provider plugin discovered from manifests", () => {
expectRegistryPluginIds({
actualPluginIds: providerContractPluginIds,
predicate: (plugin) => plugin.origin === "bundled" && plugin.providers.length > 0,
});
});
it("covers every bundled speech plugin discovered from manifests", () => {
expectRegistryPluginIds({
actualPluginIds: speechProviderContractRegistry.map((entry) => entry.pluginId),
predicate: (plugin) =>
plugin.origin === "bundled" && (plugin.contracts?.speechProviders?.length ?? 0) > 0,
});
});
it("covers every bundled web fetch plugin from the shared resolver", () => {
const bundledWebFetchPluginIds = resolveBundledWebFetchPluginIds({});
expect(
uniqueSortedStrings(
pluginRegistrationContractRegistry
.filter((entry) => entry.webFetchProviderIds.length > 0)
.map((entry) => entry.pluginId),
),
).toEqual(bundledWebFetchPluginIds);
});
it(
"loads bundled web fetch providers for each shared-resolver plugin",
{ timeout: REGISTRY_CONTRACT_TIMEOUT_MS },
() => {
for (const pluginId of resolveBundledWebFetchPluginIds({})) {
expect(resolveWebFetchProviderContractEntriesForPluginId(pluginId).length).toBeGreaterThan(
0,
);
}
expect(webFetchProviderContractRegistry.length).toBeGreaterThan(0);
},
);
it("covers every bundled web search plugin from the shared resolver", () => {
const bundledWebSearchPluginIds = resolveBundledWebSearchPluginIds({});
expect(
uniqueSortedStrings(
pluginRegistrationContractRegistry
.filter((entry) => entry.webSearchProviderIds.length > 0)
.map((entry) => entry.pluginId),
),
).toEqual(bundledWebSearchPluginIds);
});
it(
"loads bundled web search providers for each shared-resolver plugin",
{ timeout: REGISTRY_CONTRACT_TIMEOUT_MS },
() => {
for (const pluginId of resolveBundledWebSearchPluginIds({})) {
expect(resolveWebSearchProviderContractEntriesForPluginId(pluginId).length).toBeGreaterThan(
0,
);
}
},
);
});