Files
openclaw/src/plugins/provider-validation.test.ts
Peter Steinberger c7e7ac2728 refactor: remove expired plugin compatibility surfaces (#111451)
* docs(secrets): remove retired web credential paths

* refactor(web): remove retired provider compatibility paths

* refactor(providers): delete retired compatibility routes

* refactor(secrets): remove retired credential aliases

* refactor(plugin-sdk): delete retired compatibility surfaces

* docs(plugin-sdk): remove retired migration guidance

* chore(plugin-sdk): refresh rebased surface budgets

* chore(plugin-sdk): refresh API removal baseline

* refactor(compat): migrate retired internal callers

* chore(plugin-sdk): refresh current-main baselines

* test(config): migrate plugin-owned secret assertions

* test(gateway): narrow plugin secret refs

* fix(plugin-sdk): preserve private boundary type identity

* chore(compat): remove stale sweep references

* chore(lint): lower max-lines budget

* refactor(secrets): remove unused web helper

* build(plugin-sdk): drop removed compat entries

* chore(plugin-sdk): refresh rebased API baseline

* chore(plugin-sdk): use Linux API baseline hash

* fix(plugin-sdk): preserve private bundled build entries

* fix(plugin-sdk): package private runtime facades

* fix(plugins): preserve external credential contracts
2026-07-19 11:04:48 -07:00

222 lines
6.6 KiB
TypeScript

/** Covers provider registration validation for ids, duplicates, and required hooks. */
import { describe, expect, it } from "vitest";
import type { PluginDiagnostic } from "./manifest-types.js";
import { normalizeRegisteredProvider } from "./provider-validation.js";
import type { ProviderPlugin } from "./types.js";
function collectDiagnostics() {
const diagnostics: PluginDiagnostic[] = [];
return {
diagnostics,
pushDiagnostic: (diag: PluginDiagnostic) => {
diagnostics.push(diag);
},
};
}
function makeProvider(overrides: Partial<ProviderPlugin>): ProviderPlugin {
return {
id: "demo",
label: "Demo",
auth: [],
...overrides,
};
}
function expectDiagnosticMessages(
diagnostics: PluginDiagnostic[],
expectedDiagnostics: ReadonlyArray<{ level: PluginDiagnostic["level"]; message: string }>,
) {
expect(diagnostics.map((diag) => ({ level: diag.level, message: diag.message }))).toEqual(
expectedDiagnostics,
);
}
function expectDiagnosticText(diagnostics: PluginDiagnostic[], messages: readonly string[]) {
expect(diagnostics.map((diag) => diag.message)).toEqual([...messages]);
}
function normalizeProviderFixture(provider: ProviderPlugin) {
const { diagnostics, pushDiagnostic } = collectDiagnostics();
const normalizedProvider = normalizeRegisteredProvider({
pluginId: "demo-plugin",
source: "/tmp/demo/index.ts",
provider,
pushDiagnostic,
});
return {
diagnostics,
provider: normalizedProvider,
};
}
function expectNormalizedProviderFixture(params: {
provider: ProviderPlugin;
expectedProvider?: unknown;
expectedDiagnostics?: ReadonlyArray<{ level: PluginDiagnostic["level"]; message: string }>;
expectedDiagnosticText?: readonly string[];
}) {
const result = normalizeProviderFixture(params.provider);
if (params.expectedProvider) {
expect(result.provider).toEqual(params.expectedProvider);
}
if (params.expectedDiagnostics) {
expectDiagnosticMessages(result.diagnostics, params.expectedDiagnostics);
}
if (params.expectedDiagnosticText) {
expectDiagnosticText(result.diagnostics, params.expectedDiagnosticText);
}
return result;
}
function expectProviderNormalizationResult(params: {
provider: ProviderPlugin;
expectedProvider?: unknown;
expectedDiagnostics?: ReadonlyArray<{ level: PluginDiagnostic["level"]; message: string }>;
expectedDiagnosticText?: readonly string[];
assert?: (
provider: ReturnType<typeof normalizeRegisteredProvider>,
diagnostics: PluginDiagnostic[],
inputProvider: ProviderPlugin,
) => void;
}) {
const { diagnostics, provider } = expectNormalizedProviderFixture(params);
params.assert?.(provider, diagnostics, params.provider);
}
describe("normalizeRegisteredProvider", () => {
const primaryAuthRun = async () => ({ profiles: [] });
const cases = [
{
name: "drops invalid and duplicate auth methods, and clears bad wizard method bindings",
provider: makeProvider({
id: " demo ",
label: " Demo Provider ",
aliases: [" alias-one ", "alias-one", ""],
deprecatedProfileIds: [" demo:legacy ", "demo:legacy", ""],
envVars: [" DEMO_API_KEY ", "DEMO_API_KEY"],
auth: [
{
id: " primary ",
label: " Primary ",
kind: "custom",
wizard: {
choiceId: " demo-primary ",
onboardingFeatured: true,
modelAllowlist: {
allowedKeys: [" demo/model ", "demo/model"],
initialSelections: [" demo/model "],
loadCatalog: true,
message: " Demo models ",
},
},
run: primaryAuthRun,
},
{
id: "primary",
label: "Duplicate",
kind: "custom",
run: async () => ({ profiles: [] }),
},
{ id: " ", label: "Missing", kind: "custom", run: async () => ({ profiles: [] }) },
],
wizard: {
setup: {
choiceId: " demo-choice ",
onboardingFeatured: true,
methodId: " missing ",
},
modelPicker: {
label: " Demo models ",
methodId: " missing ",
},
},
}),
expectedProvider: makeProvider({
id: "demo",
label: "Demo Provider",
aliases: ["alias-one"],
deprecatedProfileIds: ["demo:legacy"],
envVars: ["DEMO_API_KEY"],
auth: [
{
id: "primary",
label: "Primary",
kind: "custom",
wizard: {
choiceId: "demo-primary",
onboardingFeatured: true,
modelAllowlist: {
allowedKeys: ["demo/model"],
initialSelections: ["demo/model"],
loadCatalog: true,
message: "Demo models",
},
},
run: primaryAuthRun,
},
],
wizard: {
setup: {
choiceId: "demo-choice",
onboardingFeatured: true,
},
modelPicker: {
label: "Demo models",
},
},
}),
expectedDiagnostics: [
{
level: "error",
message: 'provider "demo" auth method duplicated id "primary"',
},
{
level: "error",
message: 'provider "demo" auth method missing id',
},
{
level: "warn",
message:
'provider "demo" setup method "missing" not found; falling back to available methods',
},
{
level: "warn",
message:
'provider "demo" model-picker method "missing" not found; falling back to available methods',
},
],
},
{
name: "drops wizard metadata when a provider has no auth methods",
provider: makeProvider({
wizard: {
setup: {
choiceId: "demo",
},
modelPicker: {
label: "Demo",
},
},
}),
assert: (
provider: ReturnType<typeof normalizeRegisteredProvider>,
diagnostics: PluginDiagnostic[],
) => {
expect(provider?.wizard).toBeUndefined();
expectDiagnosticText(diagnostics, [
'provider "demo" setup metadata ignored because it has no auth methods',
'provider "demo" model-picker metadata ignored because it has no auth methods',
]);
},
},
] satisfies readonly (Parameters<typeof expectProviderNormalizationResult>[0] & {
name: string;
})[];
it.each(cases)("$name", (testCase) => {
expectProviderNormalizationResult(testCase);
});
});