diff --git a/src/plugins/providers.test.ts b/src/plugins/providers.test.ts index be42f8dc5ac..31d5d4c32cc 100644 --- a/src/plugins/providers.test.ts +++ b/src/plugins/providers.test.ts @@ -2,6 +2,7 @@ import { beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; import type { OpenClawConfig } from "../config/config.js"; import type { PluginAutoEnableResult } from "../config/plugin-auto-enable.js"; import type { PluginManifestRecord } from "./manifest-registry.js"; +import type { PluginRegistrySnapshot } from "./plugin-registry.js"; import { createEmptyPluginRegistry } from "./registry-empty.js"; import type { ProviderPlugin } from "./types.js"; @@ -125,6 +126,90 @@ function setOwningProviderManifestPluginsWithWorkspace() { ]); } +function createProviderRegistrySnapshotFixture(): PluginRegistrySnapshot { + const manifestRegistry = loadPluginManifestRegistryMock(); + const plugins = manifestRegistry.plugins.map((plugin) => ({ + pluginId: plugin.id, + manifestPath: plugin.manifestPath, + manifestHash: `test-${plugin.id}`, + source: plugin.source, + rootDir: plugin.rootDir, + origin: plugin.origin, + enabled: plugin.enabledByDefault !== false, + ...(plugin.enabledByDefault === true ? { enabledByDefault: true } : {}), + syntheticAuthRefs: plugin.syntheticAuthRefs, + startup: { + sidecar: false, + memory: false, + deferConfiguredChannelFullLoadUntilAfterListen: false, + agentHarnesses: [], + }, + compat: [], + })); + + return { + version: 1, + hostContractVersion: "test", + compatRegistryVersion: "test", + migrationVersion: 1, + policyHash: "test", + generatedAtMs: 0, + installRecords: {}, + plugins, + diagnostics: [], + }; +} + +function normalizeProviderForFixture(value: string): string { + return value.trim().toLowerCase(); +} + +function sortUniqueFixtureValues(values: Iterable): string[] { + return [...new Set(values)].toSorted((left, right) => left.localeCompare(right)); +} + +function listManifestContributionIdsForFixture( + plugin: PluginManifestRecord, + contribution: string, +): readonly string[] { + switch (contribution) { + case "providers": + return plugin.providers; + case "cliBackends": + return plugin.cliBackends; + default: + return []; + } +} + +function resolvePluginContributionOwnersFixture(params: { + contribution: string; + matches: string | ((contributionId: string) => boolean); +}): readonly string[] { + const matcher = + typeof params.matches === "string" + ? (contributionId: string) => contributionId === params.matches + : params.matches; + return sortUniqueFixtureValues( + loadPluginManifestRegistryMock().plugins.flatMap((plugin) => + listManifestContributionIdsForFixture(plugin, params.contribution).some(matcher) + ? [plugin.id] + : [], + ), + ); +} + +function resolveProviderOwnersFixture(params: { providerId: string }): readonly string[] { + const providerId = normalizeProviderForFixture(params.providerId); + if (!providerId) { + return []; + } + return resolvePluginContributionOwnersFixture({ + contribution: "providers", + matches: (contributionId) => normalizeProviderForFixture(contributionId) === providerId, + }); +} + function getLastRuntimeRegistryCall(): Record { const call = resolveRuntimePluginRegistryMock.mock.calls.at(-1)?.[0]; expect(call).toBeDefined(); @@ -290,6 +375,16 @@ describe("resolvePluginProviders", () => { loadPluginManifestRegistry: (...args: Parameters) => loadPluginManifestRegistryMock(...args), })); + vi.doMock("./plugin-registry.js", async () => { + const actual = + await vi.importActual("./plugin-registry.js"); + return { + ...actual, + loadPluginRegistrySnapshot: () => createProviderRegistrySnapshotFixture(), + resolvePluginContributionOwners: resolvePluginContributionOwnersFixture, + resolveProviderOwners: resolveProviderOwnersFixture, + }; + }); vi.doMock("./installed-plugin-index-store.js", async (importOriginal) => { const actual = await importOriginal(); return {