diff --git a/src/plugins/contracts/runtime.contract.test.ts b/src/plugins/contracts/runtime.contract.test.ts index f241c23d64f..1e614150cb3 100644 --- a/src/plugins/contracts/runtime.contract.test.ts +++ b/src/plugins/contracts/runtime.contract.test.ts @@ -2,10 +2,13 @@ import fs from "node:fs/promises"; import os from "node:os"; import path from "node:path"; import { beforeEach, describe, expect, it, vi } from "vitest"; +import openAIPlugin from "../../../extensions/openai/index.js"; +import qwenPortalPlugin from "../../../extensions/qwen-portal-auth/index.js"; +import { createCapturedPluginRegistration } from "../../test-utils/plugin-registration.js"; import { createProviderUsageFetch, makeResponse } from "../../test-utils/provider-usage-fetch.js"; +import type { OpenClawPluginApi, ProviderPlugin } from "../types.js"; import type { ProviderRuntimeModel } from "../types.js"; -import { requireProviderContractProvider } from "./registry.js"; -import { registerProviders, requireProvider } from "./testkit.js"; +import { requireProviderContractProvider as requireBundledProviderContractProvider } from "./registry.js"; const CONTRACT_SETUP_TIMEOUT_MS = 300_000; @@ -43,11 +46,38 @@ function createModel(overrides: Partial & Pick) { + const captured = createCapturedPluginRegistration(); + for (const plugin of plugins) { + plugin.register(captured.api); + } + return captured.providers; +} + +function requireProvider(providers: ProviderPlugin[], providerId: string) { + const provider = providers.find((entry) => entry.id === providerId); + if (!provider) { + throw new Error(`provider ${providerId} missing`); + } + return provider; +} + +function requireProviderContractProvider(providerId: string): ProviderPlugin { + if (providerId === "openai-codex") { + return requireProvider(registerProviders(openAIPlugin), providerId); + } + if (providerId === "qwen-portal") { + return requireProvider(registerProviders(qwenPortalPlugin), providerId); + } + return requireBundledProviderContractProvider(providerId); +} + describe("provider runtime contract", () => { beforeEach(() => { getOAuthApiKeyMock.mockReset(); refreshQwenPortalCredentialsMock.mockReset(); }, CONTRACT_SETUP_TIMEOUT_MS); + describe("anthropic", () => { it("owns anthropic 4.6 forward-compat resolution", () => { const provider = requireProviderContractProvider("anthropic"); @@ -511,9 +541,7 @@ describe("provider runtime contract", () => { describe("openai-codex", () => { it("owns refresh fallback for accountId extraction failures", async () => { - vi.resetModules(); - const openAIPlugin = (await import("../../../extensions/openai/index.js")).default; - const provider = requireProvider(registerProviders(openAIPlugin), "openai-codex"); + const provider = requireProviderContractProvider("openai-codex"); const credential = { type: "oauth" as const, provider: "openai-codex", @@ -608,9 +636,7 @@ describe("provider runtime contract", () => { describe("qwen-portal", () => { it("owns OAuth refresh", async () => { - const qwenPortalPlugin = (await import("../../../extensions/qwen-portal-auth/index.js")) - .default; - const provider = requireProvider(registerProviders(qwenPortalPlugin), "qwen-portal"); + const provider = requireProviderContractProvider("qwen-portal"); const credential = { type: "oauth" as const, provider: "qwen-portal", diff --git a/src/plugins/contracts/web-search-provider.contract.test.ts b/src/plugins/contracts/web-search-provider.contract.test.ts index c07eebaf6b5..ca51d97862e 100644 --- a/src/plugins/contracts/web-search-provider.contract.test.ts +++ b/src/plugins/contracts/web-search-provider.contract.test.ts @@ -1,7 +1,13 @@ -import { describe } from "vitest"; +import { describe, expect, it } from "vitest"; import { webSearchProviderContractRegistry } from "./registry.js"; import { installWebSearchProviderContractSuite } from "./suites.js"; +describe("web search provider contract registry load", () => { + it("loads bundled web search providers", () => { + expect(webSearchProviderContractRegistry.length).toBeGreaterThan(0); + }); +}); + for (const entry of webSearchProviderContractRegistry) { describe(`${entry.pluginId}:${entry.provider.id} web search contract`, () => { installWebSearchProviderContractSuite({ diff --git a/src/plugins/contracts/wizard.contract.test.ts b/src/plugins/contracts/wizard.contract.test.ts index 245fc46435a..59a9ab2bbc4 100644 --- a/src/plugins/contracts/wizard.contract.test.ts +++ b/src/plugins/contracts/wizard.contract.test.ts @@ -1,23 +1,19 @@ -import { beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; +import { beforeEach, describe, expect, it, vi } from "vitest"; +import { + buildProviderPluginMethodChoice, + resolveProviderModelPickerEntries, + resolveProviderPluginChoice, + resolveProviderWizardOptions, +} from "../provider-wizard.js"; import type { ProviderPlugin } from "../types.js"; +import { providerContractPluginIds, uniqueProviderContractProviders } from "./registry.js"; -const CONTRACT_SETUP_TIMEOUT_MS = 300_000; -type ResolvePluginProviders = typeof import("../providers.js").resolvePluginProviders; - -const resolvePluginProvidersMock = vi.hoisted(() => vi.fn(() => [])); +const resolvePluginProvidersMock = vi.fn(); vi.mock("../providers.js", () => ({ - resolvePluginProviders: (params?: { onlyPluginIds?: string[] }) => - resolvePluginProvidersMock(params as never), + resolvePluginProviders: (...args: unknown[]) => resolvePluginProvidersMock(...args), })); -let buildProviderPluginMethodChoice: typeof import("../provider-wizard.js").buildProviderPluginMethodChoice; -let resolveProviderModelPickerEntries: typeof import("../provider-wizard.js").resolveProviderModelPickerEntries; -let resolveProviderPluginChoice: typeof import("../provider-wizard.js").resolveProviderPluginChoice; -let resolveProviderWizardOptions: typeof import("../provider-wizard.js").resolveProviderWizardOptions; -let providerContractPluginIds: typeof import("./registry.js").providerContractPluginIds; -let uniqueProviderContractProviders: typeof import("./registry.js").uniqueProviderContractProviders; - function resolveExpectedWizardChoiceValues(providers: ProviderPlugin[]) { const values: string[] = []; @@ -75,25 +71,8 @@ function resolveExpectedModelPickerValues(providers: ProviderPlugin[]) { } describe("provider wizard contract", () => { - beforeAll(async () => { - const actualProviders = - await vi.importActual("../providers.js"); - resolvePluginProvidersMock.mockImplementation((params?: { onlyPluginIds?: string[] }) => - actualProviders.resolvePluginProviders(params as never), - ); - ({ providerContractPluginIds, uniqueProviderContractProviders } = - await import("./registry.js")); - resolvePluginProvidersMock.mockReturnValue(uniqueProviderContractProviders); - ({ - buildProviderPluginMethodChoice, - resolveProviderModelPickerEntries, - resolveProviderPluginChoice, - resolveProviderWizardOptions, - } = await import("../provider-wizard.js")); - }, CONTRACT_SETUP_TIMEOUT_MS); - beforeEach(() => { - resolvePluginProvidersMock.mockClear(); + resolvePluginProvidersMock.mockReset(); resolvePluginProvidersMock.mockReturnValue(uniqueProviderContractProviders); });