Files
openclaw/src/plugins/contracts/registry.contract.test.ts
2026-05-11 07:00:58 +01:00

223 lines
8.5 KiB
TypeScript

import { describe, expect, it } from "vitest";
import { uniqueSortedStrings } from "../../plugin-sdk/test-helpers/string-utils.js";
import { loadPluginManifestRegistry, type PluginManifestRecord } from "../manifest-registry.js";
import { resolveManifestContractPluginIds } from "../plugin-registry.js";
import { BUNDLED_PLUGIN_CONTRACT_SNAPSHOTS } from "./inventory/bundled-capability-metadata.js";
import {
pluginRegistrationContractRegistry,
providerContractLoadError,
providerContractPluginIds,
} from "./registry.js";
describe("plugin contract registry", () => {
function expectUniqueIds(ids: readonly string[]) {
expect(ids).toEqual([...new Set(ids)]);
}
function expectRegistryPluginIds(params: {
actualPluginIds: readonly string[];
predicate: (plugin: PluginManifestRecord) => boolean;
}) {
expect(uniqueSortedStrings(params.actualPluginIds)).toEqual(
resolveBundledManifestPluginIds(params.predicate),
);
}
function resolveBundledManifestPluginIds(predicate: (plugin: PluginManifestRecord) => boolean) {
const snapshotPluginIds = new Set(
BUNDLED_PLUGIN_CONTRACT_SNAPSHOTS.map((entry) => entry.pluginId),
);
return loadPluginManifestRegistry({})
.plugins.filter((plugin) => snapshotPluginIds.has(plugin.id) && predicate(plugin))
.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 migration provider ids",
ids: () => pluginRegistrationContractRegistry.flatMap((entry) => entry.migrationProviderIds),
},
{
name: "does not duplicate bundled media provider ids",
ids: () =>
pluginRegistrationContractRegistry.flatMap((entry) => entry.mediaUnderstandingProviderIds),
},
{
name: "does not duplicate bundled realtime transcription provider ids",
ids: () =>
pluginRegistrationContractRegistry.flatMap(
(entry) => entry.realtimeTranscriptionProviderIds,
),
},
{
name: "does not duplicate bundled realtime voice provider ids",
ids: () =>
pluginRegistrationContractRegistry.flatMap((entry) => entry.realtimeVoiceProviderIds),
},
{
name: "does not duplicate bundled image-generation provider ids",
ids: () =>
pluginRegistrationContractRegistry.flatMap((entry) => entry.imageGenerationProviderIds),
},
] as const)("$name", ({ ids }) => {
expectUniqueIds(ids());
});
it("does not duplicate bundled speech provider ids", () => {
expectUniqueIds(pluginRegistrationContractRegistry.flatMap((entry) => entry.speechProviderIds));
});
it("covers every bundled provider plugin discovered from manifests", () => {
expectRegistryPluginIds({
actualPluginIds: providerContractPluginIds,
predicate: (plugin) => plugin.origin === "bundled" && plugin.providers.length > 0,
});
});
it("keeps video-only provider auth choices out of text onboarding", () => {
const registry = loadPluginManifestRegistry({});
for (const pluginId of ["alibaba", "runway"]) {
const plugin = registry.plugins.find(
(entry) => entry.origin === "bundled" && entry.id === pluginId,
);
expect(plugin?.providerAuthChoices).toEqual([
{
provider: pluginId,
method: "api-key",
choiceId: pluginId === "alibaba" ? "alibaba-model-studio-api-key" : "runway-api-key",
choiceLabel: pluginId === "alibaba" ? "Alibaba Model Studio API key" : "Runway API key",
groupId: pluginId,
groupLabel: pluginId === "alibaba" ? "Alibaba Model Studio" : "Runway",
groupHint: pluginId === "alibaba" ? "DashScope / Model Studio API key" : "API key",
onboardingScopes: ["image-generation"],
optionKey: pluginId === "alibaba" ? "alibabaModelStudioApiKey" : "runwayApiKey",
cliFlag: pluginId === "alibaba" ? "--alibaba-model-studio-api-key" : "--runway-api-key",
cliOption:
pluginId === "alibaba"
? "--alibaba-model-studio-api-key <key>"
: "--runway-api-key <key>",
cliDescription:
pluginId === "alibaba" ? "Alibaba Model Studio API key" : "Runway API key",
},
]);
}
});
it("exposes the GitHub Copilot non-interactive onboarding token flag from manifest metadata", () => {
const registry = loadPluginManifestRegistry({});
const plugin = registry.plugins.find(
(entry) => entry.origin === "bundled" && entry.id === "github-copilot",
);
expect(plugin?.providerAuthChoices).toEqual([
{
provider: "github-copilot",
method: "device",
choiceId: "github-copilot",
choiceLabel: "GitHub Copilot",
choiceHint: "Device login with your GitHub account",
groupId: "copilot",
groupLabel: "Copilot",
groupHint: "GitHub + local proxy",
optionKey: "githubCopilotToken",
cliFlag: "--github-copilot-token",
cliOption: "--github-copilot-token <token>",
cliDescription: "GitHub Copilot OAuth token",
},
]);
});
it("covers every bundled speech plugin discovered from manifests", () => {
expectRegistryPluginIds({
actualPluginIds: pluginRegistrationContractRegistry
.filter((entry) => entry.speechProviderIds.length > 0)
.map((entry) => entry.pluginId),
predicate: (plugin) =>
plugin.origin === "bundled" && (plugin.contracts?.speechProviders?.length ?? 0) > 0,
});
});
it("covers every bundled realtime voice plugin discovered from manifests", () => {
expectRegistryPluginIds({
actualPluginIds: pluginRegistrationContractRegistry
.filter((entry) => entry.realtimeVoiceProviderIds.length > 0)
.map((entry) => entry.pluginId),
predicate: (plugin) =>
plugin.origin === "bundled" && (plugin.contracts?.realtimeVoiceProviders?.length ?? 0) > 0,
});
});
it("covers every bundled realtime transcription plugin discovered from manifests", () => {
expectRegistryPluginIds({
actualPluginIds: pluginRegistrationContractRegistry
.filter((entry) => entry.realtimeTranscriptionProviderIds.length > 0)
.map((entry) => entry.pluginId),
predicate: (plugin) =>
plugin.origin === "bundled" &&
(plugin.contracts?.realtimeTranscriptionProviders?.length ?? 0) > 0,
});
});
it("covers every bundled web fetch plugin from the shared resolver", () => {
const bundledWebFetchPluginIds = resolveManifestContractPluginIds({
contract: "webFetchProviders",
origin: "bundled",
});
expect(
uniqueSortedStrings(
pluginRegistrationContractRegistry
.filter((entry) => entry.webFetchProviderIds.length > 0)
.map((entry) => entry.pluginId),
),
).toEqual(bundledWebFetchPluginIds);
});
it("covers every bundled web search plugin from the shared resolver", () => {
const snapshotPluginIds = new Set(
BUNDLED_PLUGIN_CONTRACT_SNAPSHOTS.map((entry) => entry.pluginId),
);
const bundledWebSearchPluginIds = resolveManifestContractPluginIds({
contract: "webSearchProviders",
origin: "bundled",
}).filter((pluginId) => snapshotPluginIds.has(pluginId));
expect(
uniqueSortedStrings(
pluginRegistrationContractRegistry
.filter((entry) => entry.webSearchProviderIds.length > 0)
.map((entry) => entry.pluginId),
),
).toEqual(bundledWebSearchPluginIds);
});
it("covers every bundled migration provider plugin discovered from manifests", () => {
expectRegistryPluginIds({
actualPluginIds: pluginRegistrationContractRegistry
.filter((entry) => entry.migrationProviderIds.length > 0)
.map((entry) => entry.pluginId),
predicate: (plugin) =>
plugin.origin === "bundled" && (plugin.contracts?.migrationProviders?.length ?? 0) > 0,
});
});
});