test: keep searxng web search contract light

Lazy-load the SearXNG web-search client from provider execution and reuse
the shared contract helper for credential and selection wiring. Keep the
shared fast-path contract focused on the single bundled manifest it checks.
This commit is contained in:
Gustavo Madeira Santana
2026-04-17 18:15:59 -04:00
parent 41ee813a45
commit c54464a887
2 changed files with 102 additions and 52 deletions

View File

@@ -1,10 +1,13 @@
import fs from "node:fs";
import path from "node:path";
import { describe, expect, it } from "vitest";
import type { OpenClawConfig } from "../../../src/config/config.js";
import { resolveManifestContractOwnerPluginId } from "../../../src/plugins/manifest-registry.js";
import { resolveBundledPluginsDir } from "../../../src/plugins/bundled-dir.js";
import {
resolveBundledExplicitRuntimeWebSearchProvidersFromPublicArtifacts,
resolveBundledExplicitWebSearchProvidersFromPublicArtifacts,
} from "../../../src/plugins/web-provider-public-artifacts.explicit.js";
import { normalizeOptionalLowercaseString } from "../../../src/shared/string-coerce.js";
type ComparableProvider = {
pluginId: string;
@@ -24,6 +27,64 @@ type ComparableProvider = {
hasResolveRuntimeMetadata: boolean;
};
type MinimalBundledPluginManifest = {
id?: unknown;
contracts?: {
webSearchProviders?: unknown;
};
};
const bundledWebSearchManifestContracts = new Map<
string,
{ pluginId: string; webSearchProviderIds: string[] } | null
>();
function readBundledWebSearchManifestContract(pluginId: string) {
if (bundledWebSearchManifestContracts.has(pluginId)) {
return bundledWebSearchManifestContracts.get(pluginId) ?? null;
}
const bundledPluginsDir = resolveBundledPluginsDir();
if (!bundledPluginsDir) {
bundledWebSearchManifestContracts.set(pluginId, null);
return null;
}
const manifestPath = path.join(bundledPluginsDir, pluginId, "openclaw.plugin.json");
const manifest = JSON.parse(
fs.readFileSync(manifestPath, "utf8"),
) as MinimalBundledPluginManifest;
const manifestPluginId = typeof manifest.id === "string" ? manifest.id : "";
const webSearchProviderIds = Array.isArray(manifest.contracts?.webSearchProviders)
? manifest.contracts.webSearchProviders.filter(
(providerId): providerId is string => typeof providerId === "string",
)
: [];
const contract = { pluginId: manifestPluginId, webSearchProviderIds };
bundledWebSearchManifestContracts.set(pluginId, contract);
return contract;
}
function resolveBundledManifestWebSearchOwnerPluginId(params: {
pluginId: string;
providerId: string;
}): string | undefined {
const normalizedProviderId = normalizeOptionalLowercaseString(params.providerId);
if (!normalizedProviderId) {
return undefined;
}
const contract = readBundledWebSearchManifestContract(params.pluginId);
if (
!contract?.webSearchProviderIds.some(
(candidate) => normalizeOptionalLowercaseString(candidate) === normalizedProviderId,
)
) {
return undefined;
}
return contract.pluginId || undefined;
}
function toComparableEntry(params: {
pluginId: string;
provider: {
@@ -87,10 +148,9 @@ export function describeBundledWebSearchFastPathContract(pluginId: string) {
expect(providers.length).toBeGreaterThan(0);
for (const provider of providers) {
expect(
resolveManifestContractOwnerPluginId({
contract: "webSearchProviders",
value: provider.id,
origin: "bundled",
resolveBundledManifestWebSearchOwnerPluginId({
pluginId,
providerId: provider.id,
}),
).toBe(pluginId);
}