import { describe, expect, it } from "vitest"; import { normalizeRegisteredProvider } from "./provider-validation.js"; import type { PluginDiagnostic, ProviderPlugin } from "./types.js"; function collectDiagnostics() { const diagnostics: PluginDiagnostic[] = []; return { diagnostics, pushDiagnostic: (diag: PluginDiagnostic) => { diagnostics.push(diag); }, }; } function makeProvider(overrides: Partial): ProviderPlugin { return { id: "demo", label: "Demo", auth: [], ...overrides, }; } describe("normalizeRegisteredProvider", () => { it("drops invalid and duplicate auth methods, and clears bad wizard method bindings", () => { const { diagnostics, pushDiagnostic } = collectDiagnostics(); const provider = normalizeRegisteredProvider({ pluginId: "demo-plugin", source: "/tmp/demo/index.ts", provider: makeProvider({ id: " demo ", label: " Demo Provider ", aliases: [" alias-one ", "alias-one", ""], envVars: [" DEMO_API_KEY ", "DEMO_API_KEY"], auth: [ { id: " primary ", label: " Primary ", kind: "custom", run: async () => ({ profiles: [] }), }, { id: "primary", label: "Duplicate", kind: "custom", run: async () => ({ profiles: [] }), }, { id: " ", label: "Missing", kind: "custom", run: async () => ({ profiles: [] }) }, ], wizard: { setup: { choiceId: " demo-choice ", methodId: " missing ", }, modelPicker: { label: " Demo models ", methodId: " missing ", }, }, }), pushDiagnostic, }); expect(provider).toMatchObject({ id: "demo", label: "Demo Provider", aliases: ["alias-one"], envVars: ["DEMO_API_KEY"], auth: [{ id: "primary", label: "Primary" }], wizard: { setup: { choiceId: "demo-choice", }, modelPicker: { label: "Demo models", }, }, }); expect(diagnostics.map((diag) => ({ level: diag.level, message: diag.message }))).toEqual([ { 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', }, ]); }); it("drops wizard metadata when a provider has no auth methods", () => { const { diagnostics, pushDiagnostic } = collectDiagnostics(); const provider = normalizeRegisteredProvider({ pluginId: "demo-plugin", source: "/tmp/demo/index.ts", provider: makeProvider({ wizard: { setup: { choiceId: "demo", }, modelPicker: { label: "Demo", }, }, }), pushDiagnostic, }); expect(provider?.wizard).toBeUndefined(); expect(diagnostics.map((diag) => diag.message)).toEqual([ 'provider "demo" setup metadata ignored because it has no auth methods', 'provider "demo" model-picker metadata ignored because it has no auth methods', ]); }); it("prefers catalog when a provider registers both catalog and discovery", () => { const { diagnostics, pushDiagnostic } = collectDiagnostics(); const provider = normalizeRegisteredProvider({ pluginId: "demo-plugin", source: "/tmp/demo/index.ts", provider: makeProvider({ catalog: { run: async () => null, }, discovery: { run: async () => ({ provider: { baseUrl: "http://127.0.0.1:8000/v1", models: [], }, }), }, }), pushDiagnostic, }); expect(provider?.catalog).toBeDefined(); expect(provider?.discovery).toBeUndefined(); expect(diagnostics.map((diag) => diag.message)).toEqual([ 'provider "demo" registered both catalog and discovery; using catalog', ]); }); });