openclaw/test/helpers/plugins/plugin-registration-contrac...

194 lines
6.4 KiB
TypeScript

import { describe, expect, it } from "vitest";
import {
imageGenerationProviderContractRegistry,
mediaUnderstandingProviderContractRegistry,
pluginRegistrationContractRegistry,
speechProviderContractRegistry,
} from "../../../src/plugins/contracts/registry.js";
import { loadPluginManifestRegistry } from "../../../src/plugins/manifest-registry.js";
type PluginRegistrationContractParams = {
pluginId: string;
providerIds?: string[];
webSearchProviderIds?: string[];
speechProviderIds?: string[];
mediaUnderstandingProviderIds?: string[];
imageGenerationProviderIds?: string[];
cliBackendIds?: string[];
toolNames?: string[];
requireSpeechVoices?: boolean;
requireDescribeImages?: boolean;
requireGenerateImage?: boolean;
manifestAuthChoice?: {
pluginId: string;
choiceId: string;
choiceLabel: string;
groupId: string;
groupLabel: string;
groupHint: string;
};
};
function findRegistration(pluginId: string) {
const entry = pluginRegistrationContractRegistry.find(
(candidate) => candidate.pluginId === pluginId,
);
if (!entry) {
throw new Error(`plugin registration contract missing for ${pluginId}`);
}
return entry;
}
function findSpeechProviderIds(pluginId: string) {
return speechProviderContractRegistry
.filter((entry) => entry.pluginId === pluginId)
.map((entry) => entry.provider.id)
.toSorted((left, right) => left.localeCompare(right));
}
function findSpeechProvider(pluginId: string) {
const entry = speechProviderContractRegistry.find((candidate) => candidate.pluginId === pluginId);
if (!entry) {
throw new Error(`speech provider contract missing for ${pluginId}`);
}
return entry.provider;
}
function findMediaUnderstandingProviderIds(pluginId: string) {
return mediaUnderstandingProviderContractRegistry
.filter((entry) => entry.pluginId === pluginId)
.map((entry) => entry.provider.id)
.toSorted((left, right) => left.localeCompare(right));
}
function findMediaUnderstandingProvider(pluginId: string) {
const entry = mediaUnderstandingProviderContractRegistry.find(
(candidate) => candidate.pluginId === pluginId,
);
if (!entry) {
throw new Error(`media-understanding provider contract missing for ${pluginId}`);
}
return entry.provider;
}
function findImageGenerationProviderIds(pluginId: string) {
return imageGenerationProviderContractRegistry
.filter((entry) => entry.pluginId === pluginId)
.map((entry) => entry.provider.id)
.toSorted((left, right) => left.localeCompare(right));
}
function findImageGenerationProvider(pluginId: string) {
const entry = imageGenerationProviderContractRegistry.find(
(candidate) => candidate.pluginId === pluginId,
);
if (!entry) {
throw new Error(`image-generation provider contract missing for ${pluginId}`);
}
return entry.provider;
}
export function describePluginRegistrationContract(params: PluginRegistrationContractParams) {
describe(`${params.pluginId} plugin registration contract`, () => {
if (params.providerIds) {
it("keeps bundled provider ownership explicit", () => {
expect(findRegistration(params.pluginId).providerIds).toEqual(params.providerIds);
});
}
if (params.webSearchProviderIds) {
it("keeps bundled web search ownership explicit", () => {
expect(findRegistration(params.pluginId).webSearchProviderIds).toEqual(
params.webSearchProviderIds,
);
});
}
if (params.speechProviderIds) {
it("keeps bundled speech ownership explicit", () => {
expect(findRegistration(params.pluginId).speechProviderIds).toEqual(
params.speechProviderIds,
);
expect(findSpeechProviderIds(params.pluginId)).toEqual(params.speechProviderIds);
});
}
if (params.mediaUnderstandingProviderIds) {
it("keeps bundled media-understanding ownership explicit", () => {
expect(findRegistration(params.pluginId).mediaUnderstandingProviderIds).toEqual(
params.mediaUnderstandingProviderIds,
);
expect(findMediaUnderstandingProviderIds(params.pluginId)).toEqual(
params.mediaUnderstandingProviderIds,
);
});
}
if (params.imageGenerationProviderIds) {
it("keeps bundled image-generation ownership explicit", () => {
expect(findRegistration(params.pluginId).imageGenerationProviderIds).toEqual(
params.imageGenerationProviderIds,
);
expect(findImageGenerationProviderIds(params.pluginId)).toEqual(
params.imageGenerationProviderIds,
);
});
}
if (params.cliBackendIds) {
it("keeps bundled CLI backend ownership explicit", () => {
expect(findRegistration(params.pluginId).cliBackendIds).toEqual(params.cliBackendIds);
});
}
if (params.toolNames) {
it("keeps bundled tool ownership explicit", () => {
expect(findRegistration(params.pluginId).toolNames).toEqual(params.toolNames);
});
}
if (params.requireSpeechVoices) {
it("keeps bundled speech voice-list support explicit", () => {
expect(findSpeechProvider(params.pluginId).listVoices).toEqual(expect.any(Function));
});
}
if (params.requireDescribeImages) {
it("keeps bundled multi-image support explicit", () => {
expect(findMediaUnderstandingProvider(params.pluginId).describeImages).toEqual(
expect.any(Function),
);
});
}
if (params.requireGenerateImage) {
it("keeps bundled image-generation support explicit", () => {
expect(findImageGenerationProvider(params.pluginId).generateImage).toEqual(
expect.any(Function),
);
});
}
const manifestAuthChoice = params.manifestAuthChoice;
if (manifestAuthChoice) {
it("keeps onboarding auth grouping explicit", () => {
const plugin = loadPluginManifestRegistry({}).plugins.find(
(entry) => entry.origin === "bundled" && entry.id === manifestAuthChoice.pluginId,
);
expect(plugin?.providerAuthChoices).toEqual(
expect.arrayContaining([
expect.objectContaining({
choiceId: manifestAuthChoice.choiceId,
choiceLabel: manifestAuthChoice.choiceLabel,
groupId: manifestAuthChoice.groupId,
groupLabel: manifestAuthChoice.groupLabel,
groupHint: manifestAuthChoice.groupHint,
}),
]),
);
});
}
});
}