mirror of
https://github.com/openclaw/openclaw.git
synced 2026-04-30 20:50:23 +00:00
Tests: centralize contract coverage follow-ups (#48751)
* Plugins: harden global contract coverage * Channels: tighten global contract coverage * Channels: centralize inbound contract coverage * Channels: move inbound contract helpers into core * Tests: rename local inbound context checks * Tests: stabilize contract runner profile * Tests: split scoped contract lanes * Channels: move inbound dispatch testkit into contracts * Plugins: share provider contract registry helpers * Plugins: reuse provider contract registry helpers
This commit is contained in:
@@ -10,8 +10,9 @@ import {
|
||||
setupAuthTestEnv,
|
||||
} from "../../commands/test-wizard-helpers.js";
|
||||
import { createCapturedPluginRegistration } from "../../test-utils/plugin-registration.js";
|
||||
import { buildProviderPluginMethodChoice } from "../provider-wizard.js";
|
||||
import type { OpenClawPluginApi, ProviderPlugin } from "../types.js";
|
||||
import { providerContractRegistry } from "./registry.js";
|
||||
import { requireProviderContractProvider, uniqueProviderContractProviders } from "./registry.js";
|
||||
|
||||
type ResolvePluginProviders =
|
||||
typeof import("../../commands/auth-choice.apply.plugin-provider.runtime.js").resolvePluginProviders;
|
||||
@@ -101,11 +102,7 @@ describe("provider auth-choice contract", () => {
|
||||
|
||||
beforeEach(() => {
|
||||
resolvePreferredProviderPluginProvidersMock.mockReset();
|
||||
resolvePreferredProviderPluginProvidersMock.mockReturnValue([
|
||||
...new Map(
|
||||
providerContractRegistry.map((entry) => [entry.provider.id, entry.provider]),
|
||||
).values(),
|
||||
]);
|
||||
resolvePreferredProviderPluginProvidersMock.mockReturnValue(uniqueProviderContractProviders);
|
||||
});
|
||||
|
||||
afterEach(async () => {
|
||||
@@ -121,21 +118,34 @@ describe("provider auth-choice contract", () => {
|
||||
activeStateDir = null;
|
||||
});
|
||||
|
||||
it("maps plugin-backed auth choices through the shared preferred-provider resolver", async () => {
|
||||
const scenarios = [
|
||||
{ authChoice: "github-copilot" as const, expectedProvider: "github-copilot" },
|
||||
{ authChoice: "qwen-portal" as const, expectedProvider: "qwen-portal" },
|
||||
{ authChoice: "minimax-global-oauth" as const, expectedProvider: "minimax-portal" },
|
||||
{ authChoice: "modelstudio-api-key" as const, expectedProvider: "modelstudio" },
|
||||
{ authChoice: "ollama" as const, expectedProvider: "ollama" },
|
||||
{ authChoice: "unknown", expectedProvider: undefined },
|
||||
] as const;
|
||||
it("maps provider-plugin choices through the shared preferred-provider fallback resolver", async () => {
|
||||
const pluginFallbackScenarios = [
|
||||
"github-copilot",
|
||||
"qwen-portal",
|
||||
"minimax-portal",
|
||||
"modelstudio",
|
||||
"ollama",
|
||||
].map((providerId) => {
|
||||
const provider = requireProviderContractProvider(providerId);
|
||||
return {
|
||||
authChoice: buildProviderPluginMethodChoice(provider.id, provider.auth[0]?.id ?? "default"),
|
||||
expectedProvider: provider.id,
|
||||
};
|
||||
});
|
||||
|
||||
for (const scenario of scenarios) {
|
||||
for (const scenario of pluginFallbackScenarios) {
|
||||
resolvePreferredProviderPluginProvidersMock.mockClear();
|
||||
await expect(
|
||||
resolvePreferredProviderForAuthChoice({ choice: scenario.authChoice }),
|
||||
resolvePreferredProviderForAuthChoice({ choice: scenario.authChoice as AuthChoice }),
|
||||
).resolves.toBe(scenario.expectedProvider);
|
||||
expect(resolvePreferredProviderPluginProvidersMock).toHaveBeenCalled();
|
||||
}
|
||||
|
||||
resolvePreferredProviderPluginProvidersMock.mockClear();
|
||||
await expect(
|
||||
resolvePreferredProviderForAuthChoice({ choice: "unknown" as AuthChoice }),
|
||||
).resolves.toBe(undefined);
|
||||
expect(resolvePreferredProviderPluginProvidersMock).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("applies qwen portal auth choices through the shared plugin-provider path", async () => {
|
||||
|
||||
@@ -1,13 +1,9 @@
|
||||
import { beforeEach, describe, expect, it, vi } from "vitest";
|
||||
import { providerContractRegistry } from "./registry.js";
|
||||
|
||||
function uniqueProviders() {
|
||||
return [
|
||||
...new Map(
|
||||
providerContractRegistry.map((entry) => [entry.provider.id, entry.provider]),
|
||||
).values(),
|
||||
];
|
||||
}
|
||||
import {
|
||||
providerContractPluginIds,
|
||||
resolveProviderContractProvidersForPluginIds,
|
||||
uniqueProviderContractProviders,
|
||||
} from "./registry.js";
|
||||
|
||||
const resolvePluginProvidersMock = vi.fn();
|
||||
const resolveOwningPluginIdsForProviderMock = vi.fn();
|
||||
@@ -30,12 +26,10 @@ const {
|
||||
|
||||
describe("provider catalog contract", () => {
|
||||
beforeEach(() => {
|
||||
const providers = uniqueProviders();
|
||||
const providerIds = [...new Set(providerContractRegistry.map((entry) => entry.pluginId))];
|
||||
resetProviderRuntimeHookCacheForTest();
|
||||
|
||||
resolveOwningPluginIdsForProviderMock.mockReset();
|
||||
resolveOwningPluginIdsForProviderMock.mockReturnValue(providerIds);
|
||||
resolveOwningPluginIdsForProviderMock.mockReturnValue(providerContractPluginIds);
|
||||
|
||||
resolveNonBundledProviderPluginIdsMock.mockReset();
|
||||
resolveNonBundledProviderPluginIdsMock.mockReturnValue([]);
|
||||
@@ -44,12 +38,9 @@ describe("provider catalog contract", () => {
|
||||
resolvePluginProvidersMock.mockImplementation((params?: { onlyPluginIds?: string[] }) => {
|
||||
const onlyPluginIds = params?.onlyPluginIds;
|
||||
if (!onlyPluginIds || onlyPluginIds.length === 0) {
|
||||
return providers;
|
||||
return uniqueProviderContractProviders;
|
||||
}
|
||||
const allowed = new Set(onlyPluginIds);
|
||||
return providerContractRegistry
|
||||
.filter((entry) => allowed.has(entry.pluginId))
|
||||
.map((entry) => entry.provider);
|
||||
return resolveProviderContractProvidersForPluginIds(onlyPluginIds);
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -2,7 +2,10 @@ import { beforeEach, describe, expect, it, vi } from "vitest";
|
||||
import { withBundledPluginAllowlistCompat } from "../bundled-compat.js";
|
||||
import { __testing as providerTesting } from "../providers.js";
|
||||
import { resolvePluginWebSearchProviders } from "../web-search-providers.js";
|
||||
import { providerContractRegistry, webSearchProviderContractRegistry } from "./registry.js";
|
||||
import {
|
||||
providerContractPluginIds,
|
||||
webSearchProviderContractRegistry,
|
||||
} from "./registry.js";
|
||||
|
||||
function uniqueSortedPluginIds(values: string[]) {
|
||||
return [...new Set(values)].toSorted((left, right) => left.localeCompare(right));
|
||||
@@ -19,7 +22,7 @@ describe("plugin loader contract", () => {
|
||||
|
||||
it("keeps bundled provider compatibility wired to the provider registry", () => {
|
||||
const providerPluginIds = uniqueSortedPluginIds(
|
||||
providerContractRegistry.map((entry) => normalizeProviderContractPluginId(entry.pluginId)),
|
||||
providerContractPluginIds.map(normalizeProviderContractPluginId),
|
||||
);
|
||||
const compatPluginIds = providerTesting.resolveBundledProviderCompatPluginIds({
|
||||
config: {
|
||||
@@ -46,7 +49,7 @@ describe("plugin loader contract", () => {
|
||||
|
||||
it("keeps vitest bundled provider enablement wired to the provider registry", () => {
|
||||
const providerPluginIds = uniqueSortedPluginIds(
|
||||
providerContractRegistry.map((entry) => normalizeProviderContractPluginId(entry.pluginId)),
|
||||
providerContractPluginIds.map(normalizeProviderContractPluginId),
|
||||
);
|
||||
const compatConfig = providerTesting.withBundledProviderVitestCompat({
|
||||
config: undefined,
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { loadPluginManifestRegistry } from "../manifest-registry.js";
|
||||
import { resolvePluginWebSearchProviders } from "../web-search-providers.js";
|
||||
import {
|
||||
mediaUnderstandingProviderContractRegistry,
|
||||
pluginRegistrationContractRegistry,
|
||||
providerContractPluginIds,
|
||||
providerContractRegistry,
|
||||
speechProviderContractRegistry,
|
||||
webSearchProviderContractRegistry,
|
||||
@@ -84,6 +87,27 @@ describe("plugin contract registry", () => {
|
||||
expect(ids).toEqual([...new Set(ids)]);
|
||||
});
|
||||
|
||||
it("covers every bundled provider plugin discovered from manifests", () => {
|
||||
const bundledProviderPluginIds = loadPluginManifestRegistry({})
|
||||
.plugins.filter((plugin) => plugin.origin === "bundled" && plugin.providers.length > 0)
|
||||
.map((plugin) => plugin.id)
|
||||
.toSorted((left, right) => left.localeCompare(right));
|
||||
|
||||
expect(providerContractPluginIds).toEqual(bundledProviderPluginIds);
|
||||
});
|
||||
|
||||
it("covers every bundled web search plugin from the shared resolver", () => {
|
||||
const bundledWebSearchPluginIds = resolvePluginWebSearchProviders({})
|
||||
.map((provider) => provider.pluginId)
|
||||
.toSorted((left, right) => left.localeCompare(right));
|
||||
|
||||
expect(
|
||||
[...new Set(webSearchProviderContractRegistry.map((entry) => entry.pluginId))].toSorted(
|
||||
(left, right) => left.localeCompare(right),
|
||||
),
|
||||
).toEqual(bundledWebSearchPluginIds);
|
||||
});
|
||||
|
||||
it("keeps multi-provider plugin ownership explicit", () => {
|
||||
expect(findProviderIdsForPlugin("google")).toEqual(["google", "google-gemini-cli"]);
|
||||
expect(findProviderIdsForPlugin("minimax")).toEqual(["minimax", "minimax-portal"]);
|
||||
@@ -146,6 +170,23 @@ describe("plugin contract registry", () => {
|
||||
});
|
||||
});
|
||||
|
||||
it("tracks every provider, speech, media, or web search plugin in the registration registry", () => {
|
||||
const expectedPluginIds = [
|
||||
...new Set([
|
||||
...providerContractRegistry.map((entry) => entry.pluginId),
|
||||
...speechProviderContractRegistry.map((entry) => entry.pluginId),
|
||||
...mediaUnderstandingProviderContractRegistry.map((entry) => entry.pluginId),
|
||||
...webSearchProviderContractRegistry.map((entry) => entry.pluginId),
|
||||
]),
|
||||
].toSorted((left, right) => left.localeCompare(right));
|
||||
|
||||
expect(
|
||||
pluginRegistrationContractRegistry
|
||||
.map((entry) => entry.pluginId)
|
||||
.toSorted((left, right) => left.localeCompare(right)),
|
||||
).toEqual(expectedPluginIds);
|
||||
});
|
||||
|
||||
it("keeps bundled speech voice-list support explicit", () => {
|
||||
expect(findSpeechProviderForPlugin("openai").listVoices).toEqual(expect.any(Function));
|
||||
expect(findSpeechProviderForPlugin("elevenlabs").listVoices).toEqual(expect.any(Function));
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import amazonBedrockPlugin from "../../../extensions/amazon-bedrock/index.js";
|
||||
import anthropicPlugin from "../../../extensions/anthropic/index.js";
|
||||
import bravePlugin from "../../../extensions/brave/index.js";
|
||||
import byteplusPlugin from "../../../extensions/byteplus/index.js";
|
||||
@@ -72,6 +73,7 @@ type PluginRegistrationContractEntry = {
|
||||
};
|
||||
|
||||
const bundledProviderPlugins: RegistrablePlugin[] = [
|
||||
amazonBedrockPlugin,
|
||||
anthropicPlugin,
|
||||
byteplusPlugin,
|
||||
cloudflareAiGatewayPlugin,
|
||||
@@ -150,6 +152,35 @@ export const providerContractRegistry: ProviderContractEntry[] = buildCapability
|
||||
select: (captured) => captured.providers,
|
||||
});
|
||||
|
||||
export const uniqueProviderContractProviders: ProviderPlugin[] = [
|
||||
...new Map(providerContractRegistry.map((entry) => [entry.provider.id, entry.provider])).values(),
|
||||
];
|
||||
|
||||
export const providerContractPluginIds = [
|
||||
...new Set(providerContractRegistry.map((entry) => entry.pluginId)),
|
||||
].toSorted((left, right) => left.localeCompare(right));
|
||||
|
||||
export function requireProviderContractProvider(providerId: string): ProviderPlugin {
|
||||
const provider = uniqueProviderContractProviders.find((entry) => entry.id === providerId);
|
||||
if (!provider) {
|
||||
throw new Error(`provider contract entry missing for ${providerId}`);
|
||||
}
|
||||
return provider;
|
||||
}
|
||||
|
||||
export function resolveProviderContractProvidersForPluginIds(
|
||||
pluginIds: readonly string[],
|
||||
): ProviderPlugin[] {
|
||||
const allowed = new Set(pluginIds);
|
||||
return [
|
||||
...new Map(
|
||||
providerContractRegistry
|
||||
.filter((entry) => allowed.has(entry.pluginId))
|
||||
.map((entry) => [entry.provider.id, entry.provider]),
|
||||
).values(),
|
||||
];
|
||||
}
|
||||
|
||||
export const webSearchProviderContractRegistry: WebSearchProviderContractEntry[] =
|
||||
bundledWebSearchPlugins.flatMap((plugin) => {
|
||||
const captured = captureRegistrations(plugin);
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { describe, expect, it, vi } from "vitest";
|
||||
import { createProviderUsageFetch, makeResponse } from "../../test-utils/provider-usage-fetch.js";
|
||||
import type { ProviderRuntimeModel } from "../types.js";
|
||||
import { requireProviderContractProvider } from "./registry.js";
|
||||
|
||||
const getOAuthApiKeyMock = vi.hoisted(() => vi.fn());
|
||||
const refreshQwenPortalCredentialsMock = vi.hoisted(() => vi.fn());
|
||||
@@ -17,16 +18,6 @@ vi.mock("../../providers/qwen-portal-oauth.js", () => ({
|
||||
refreshQwenPortalCredentials: refreshQwenPortalCredentialsMock,
|
||||
}));
|
||||
|
||||
const { providerContractRegistry } = await import("./registry.js");
|
||||
|
||||
function requireProvider(providerId: string) {
|
||||
const entry = providerContractRegistry.find((candidate) => candidate.provider.id === providerId);
|
||||
if (!entry) {
|
||||
throw new Error(`provider contract entry missing for ${providerId}`);
|
||||
}
|
||||
return entry.provider;
|
||||
}
|
||||
|
||||
function createModel(overrides: Partial<ProviderRuntimeModel> & Pick<ProviderRuntimeModel, "id">) {
|
||||
return {
|
||||
id: overrides.id,
|
||||
@@ -45,7 +36,7 @@ function createModel(overrides: Partial<ProviderRuntimeModel> & Pick<ProviderRun
|
||||
describe("provider runtime contract", () => {
|
||||
describe("anthropic", () => {
|
||||
it("owns anthropic 4.6 forward-compat resolution", () => {
|
||||
const provider = requireProvider("anthropic");
|
||||
const provider = requireProviderContractProvider("anthropic");
|
||||
const model = provider.resolveDynamicModel?.({
|
||||
provider: "anthropic",
|
||||
modelId: "claude-sonnet-4.6-20260219",
|
||||
@@ -71,7 +62,7 @@ describe("provider runtime contract", () => {
|
||||
});
|
||||
|
||||
it("owns usage auth resolution", async () => {
|
||||
const provider = requireProvider("anthropic");
|
||||
const provider = requireProviderContractProvider("anthropic");
|
||||
await expect(
|
||||
provider.resolveUsageAuth?.({
|
||||
config: {} as never,
|
||||
@@ -88,7 +79,7 @@ describe("provider runtime contract", () => {
|
||||
});
|
||||
|
||||
it("owns auth doctor hint generation", () => {
|
||||
const provider = requireProvider("anthropic");
|
||||
const provider = requireProviderContractProvider("anthropic");
|
||||
const hint = provider.buildAuthDoctorHint?.({
|
||||
provider: "anthropic",
|
||||
profileId: "anthropic:default",
|
||||
@@ -121,7 +112,7 @@ describe("provider runtime contract", () => {
|
||||
});
|
||||
|
||||
it("owns usage snapshot fetching", async () => {
|
||||
const provider = requireProvider("anthropic");
|
||||
const provider = requireProviderContractProvider("anthropic");
|
||||
const mockFetch = createProviderUsageFetch(async (url) => {
|
||||
if (url.includes("api.anthropic.com/api/oauth/usage")) {
|
||||
return makeResponse(200, {
|
||||
@@ -154,7 +145,7 @@ describe("provider runtime contract", () => {
|
||||
|
||||
describe("github-copilot", () => {
|
||||
it("owns Copilot-specific forward-compat fallbacks", () => {
|
||||
const provider = requireProvider("github-copilot");
|
||||
const provider = requireProviderContractProvider("github-copilot");
|
||||
const model = provider.resolveDynamicModel?.({
|
||||
provider: "github-copilot",
|
||||
modelId: "gpt-5.3-codex",
|
||||
@@ -181,7 +172,7 @@ describe("provider runtime contract", () => {
|
||||
|
||||
describe("google", () => {
|
||||
it("owns google direct gemini 3.1 forward-compat resolution", () => {
|
||||
const provider = requireProvider("google");
|
||||
const provider = requireProviderContractProvider("google");
|
||||
const model = provider.resolveDynamicModel?.({
|
||||
provider: "google",
|
||||
modelId: "gemini-3.1-pro-preview",
|
||||
@@ -213,7 +204,7 @@ describe("provider runtime contract", () => {
|
||||
|
||||
describe("google-gemini-cli", () => {
|
||||
it("owns gemini cli 3.1 forward-compat resolution", () => {
|
||||
const provider = requireProvider("google-gemini-cli");
|
||||
const provider = requireProviderContractProvider("google-gemini-cli");
|
||||
const model = provider.resolveDynamicModel?.({
|
||||
provider: "google-gemini-cli",
|
||||
modelId: "gemini-3.1-pro-preview",
|
||||
@@ -241,7 +232,7 @@ describe("provider runtime contract", () => {
|
||||
});
|
||||
|
||||
it("owns usage-token parsing", async () => {
|
||||
const provider = requireProvider("google-gemini-cli");
|
||||
const provider = requireProviderContractProvider("google-gemini-cli");
|
||||
await expect(
|
||||
provider.resolveUsageAuth?.({
|
||||
config: {} as never,
|
||||
@@ -260,7 +251,7 @@ describe("provider runtime contract", () => {
|
||||
});
|
||||
|
||||
it("owns OAuth auth-profile formatting", () => {
|
||||
const provider = requireProvider("google-gemini-cli");
|
||||
const provider = requireProviderContractProvider("google-gemini-cli");
|
||||
|
||||
expect(
|
||||
provider.formatApiKey?.({
|
||||
@@ -275,7 +266,7 @@ describe("provider runtime contract", () => {
|
||||
});
|
||||
|
||||
it("owns usage snapshot fetching", async () => {
|
||||
const provider = requireProvider("google-gemini-cli");
|
||||
const provider = requireProviderContractProvider("google-gemini-cli");
|
||||
const mockFetch = createProviderUsageFetch(async (url) => {
|
||||
if (url.includes("cloudcode-pa.googleapis.com/v1internal:retrieveUserQuota")) {
|
||||
return makeResponse(200, {
|
||||
@@ -309,7 +300,7 @@ describe("provider runtime contract", () => {
|
||||
|
||||
describe("openai", () => {
|
||||
it("owns openai gpt-5.4 forward-compat resolution", () => {
|
||||
const provider = requireProvider("openai");
|
||||
const provider = requireProviderContractProvider("openai");
|
||||
const model = provider.resolveDynamicModel?.({
|
||||
provider: "openai",
|
||||
modelId: "gpt-5.4-pro",
|
||||
@@ -337,7 +328,7 @@ describe("provider runtime contract", () => {
|
||||
});
|
||||
|
||||
it("owns direct openai transport normalization", () => {
|
||||
const provider = requireProvider("openai");
|
||||
const provider = requireProviderContractProvider("openai");
|
||||
expect(
|
||||
provider.normalizeResolvedModel?.({
|
||||
provider: "openai",
|
||||
@@ -360,7 +351,7 @@ describe("provider runtime contract", () => {
|
||||
|
||||
describe("openai-codex", () => {
|
||||
it("owns refresh fallback for accountId extraction failures", async () => {
|
||||
const provider = requireProvider("openai-codex");
|
||||
const provider = requireProviderContractProvider("openai-codex");
|
||||
const credential = {
|
||||
type: "oauth" as const,
|
||||
provider: "openai-codex",
|
||||
@@ -376,7 +367,7 @@ describe("provider runtime contract", () => {
|
||||
});
|
||||
|
||||
it("owns forward-compat codex models", () => {
|
||||
const provider = requireProvider("openai-codex");
|
||||
const provider = requireProviderContractProvider("openai-codex");
|
||||
const model = provider.resolveDynamicModel?.({
|
||||
provider: "openai-codex",
|
||||
modelId: "gpt-5.4",
|
||||
@@ -403,7 +394,7 @@ describe("provider runtime contract", () => {
|
||||
});
|
||||
|
||||
it("owns codex transport defaults", () => {
|
||||
const provider = requireProvider("openai-codex");
|
||||
const provider = requireProviderContractProvider("openai-codex");
|
||||
expect(
|
||||
provider.prepareExtraParams?.({
|
||||
provider: "openai-codex",
|
||||
@@ -417,7 +408,7 @@ describe("provider runtime contract", () => {
|
||||
});
|
||||
|
||||
it("owns usage snapshot fetching", async () => {
|
||||
const provider = requireProvider("openai-codex");
|
||||
const provider = requireProviderContractProvider("openai-codex");
|
||||
const mockFetch = createProviderUsageFetch(async (url) => {
|
||||
if (url.includes("chatgpt.com/backend-api/wham/usage")) {
|
||||
return makeResponse(200, {
|
||||
@@ -455,7 +446,7 @@ describe("provider runtime contract", () => {
|
||||
|
||||
describe("qwen-portal", () => {
|
||||
it("owns OAuth refresh", async () => {
|
||||
const provider = requireProvider("qwen-portal");
|
||||
const provider = requireProviderContractProvider("qwen-portal");
|
||||
const credential = {
|
||||
type: "oauth" as const,
|
||||
provider: "qwen-portal",
|
||||
@@ -478,7 +469,7 @@ describe("provider runtime contract", () => {
|
||||
|
||||
describe("zai", () => {
|
||||
it("owns glm-5 forward-compat resolution", () => {
|
||||
const provider = requireProvider("zai");
|
||||
const provider = requireProviderContractProvider("zai");
|
||||
const model = provider.resolveDynamicModel?.({
|
||||
provider: "zai",
|
||||
modelId: "glm-5",
|
||||
@@ -507,7 +498,7 @@ describe("provider runtime contract", () => {
|
||||
});
|
||||
|
||||
it("owns usage auth resolution", async () => {
|
||||
const provider = requireProvider("zai");
|
||||
const provider = requireProviderContractProvider("zai");
|
||||
await expect(
|
||||
provider.resolveUsageAuth?.({
|
||||
config: {} as never,
|
||||
@@ -524,7 +515,7 @@ describe("provider runtime contract", () => {
|
||||
});
|
||||
|
||||
it("owns usage snapshot fetching", async () => {
|
||||
const provider = requireProvider("zai");
|
||||
const provider = requireProviderContractProvider("zai");
|
||||
const mockFetch = createProviderUsageFetch(async (url) => {
|
||||
if (url.includes("api.z.ai/api/monitor/usage/quota/limit")) {
|
||||
return makeResponse(200, {
|
||||
|
||||
@@ -1,14 +1,6 @@
|
||||
import { beforeEach, describe, expect, it, vi } from "vitest";
|
||||
import type { ProviderPlugin } from "../types.js";
|
||||
import { providerContractRegistry } from "./registry.js";
|
||||
|
||||
function uniqueProviders(): ProviderPlugin[] {
|
||||
return [
|
||||
...new Map(
|
||||
providerContractRegistry.map((entry) => [entry.provider.id, entry.provider]),
|
||||
).values(),
|
||||
];
|
||||
}
|
||||
import { providerContractPluginIds, uniqueProviderContractProviders } from "./registry.js";
|
||||
|
||||
const resolvePluginProvidersMock = vi.fn();
|
||||
|
||||
@@ -81,18 +73,16 @@ function resolveExpectedModelPickerValues(providers: ProviderPlugin[]) {
|
||||
|
||||
describe("provider wizard contract", () => {
|
||||
beforeEach(() => {
|
||||
const providers = uniqueProviders();
|
||||
resolvePluginProvidersMock.mockReset();
|
||||
resolvePluginProvidersMock.mockReturnValue(providers);
|
||||
resolvePluginProvidersMock.mockReturnValue(uniqueProviderContractProviders);
|
||||
});
|
||||
|
||||
it("exposes every registered provider setup choice through the shared wizard layer", () => {
|
||||
const providers = uniqueProviders();
|
||||
const options = resolveProviderWizardOptions({
|
||||
config: {
|
||||
plugins: {
|
||||
enabled: true,
|
||||
allow: [...new Set(providerContractRegistry.map((entry) => entry.pluginId))],
|
||||
allow: providerContractPluginIds,
|
||||
slots: {
|
||||
memory: "none",
|
||||
},
|
||||
@@ -103,18 +93,16 @@ describe("provider wizard contract", () => {
|
||||
|
||||
expect(
|
||||
options.map((option) => option.value).toSorted((left, right) => left.localeCompare(right)),
|
||||
).toEqual(resolveExpectedWizardChoiceValues(providers));
|
||||
).toEqual(resolveExpectedWizardChoiceValues(uniqueProviderContractProviders));
|
||||
expect(options.map((option) => option.value)).toEqual([
|
||||
...new Set(options.map((option) => option.value)),
|
||||
]);
|
||||
});
|
||||
|
||||
it("round-trips every shared wizard choice back to its provider and auth method", () => {
|
||||
const providers = uniqueProviders();
|
||||
|
||||
for (const option of resolveProviderWizardOptions({ config: {}, env: process.env })) {
|
||||
const resolved = resolveProviderPluginChoice({
|
||||
providers,
|
||||
providers: uniqueProviderContractProviders,
|
||||
choice: option.value,
|
||||
});
|
||||
expect(resolved).not.toBeNull();
|
||||
@@ -124,15 +112,14 @@ describe("provider wizard contract", () => {
|
||||
});
|
||||
|
||||
it("exposes every registered model-picker entry through the shared wizard layer", () => {
|
||||
const providers = uniqueProviders();
|
||||
const entries = resolveProviderModelPickerEntries({ config: {}, env: process.env });
|
||||
|
||||
expect(
|
||||
entries.map((entry) => entry.value).toSorted((left, right) => left.localeCompare(right)),
|
||||
).toEqual(resolveExpectedModelPickerValues(providers));
|
||||
).toEqual(resolveExpectedModelPickerValues(uniqueProviderContractProviders));
|
||||
for (const entry of entries) {
|
||||
const resolved = resolveProviderPluginChoice({
|
||||
providers,
|
||||
providers: uniqueProviderContractProviders,
|
||||
choice: entry.value,
|
||||
});
|
||||
expect(resolved).not.toBeNull();
|
||||
|
||||
Reference in New Issue
Block a user