mirror of
https://github.com/openclaw/openclaw.git
synced 2026-04-17 04:01:05 +00:00
test: stabilize browser and provider ci shards
This commit is contained in:
@@ -7,6 +7,7 @@ import {
|
||||
withBundledPluginEnablementCompat,
|
||||
withBundledPluginVitestCompat,
|
||||
} from "./bundled-compat.js";
|
||||
import { resolveBundledPluginRepoEntryPath } from "./bundled-plugin-metadata.js";
|
||||
import { createCapturedPluginRegistration } from "./captured-registration.js";
|
||||
import { discoverOpenClawPlugins } from "./discovery.js";
|
||||
import type { PluginLoadOptions } from "./loader.js";
|
||||
@@ -213,6 +214,7 @@ export function loadBundledCapabilityRuntimeRegistry(params: {
|
||||
manifestRegistry.plugins.map((record) => [record.rootDir, record]),
|
||||
);
|
||||
const seenPluginIds = new Set<string>();
|
||||
const repoRoot = process.cwd();
|
||||
|
||||
for (const candidate of discovery.candidates) {
|
||||
const manifest = manifestByRoot.get(candidate.rootDir);
|
||||
@@ -229,15 +231,22 @@ export function loadBundledCapabilityRuntimeRegistry(params: {
|
||||
name: manifest.name,
|
||||
description: manifest.description,
|
||||
version: manifest.version,
|
||||
source: candidate.source,
|
||||
source:
|
||||
env?.VITEST && params.pluginSdkResolution === "dist"
|
||||
? (resolveBundledPluginRepoEntryPath({
|
||||
rootDir: repoRoot,
|
||||
pluginId: manifest.id,
|
||||
preferBuilt: true,
|
||||
}) ?? candidate.source)
|
||||
: candidate.source,
|
||||
rootDir: candidate.rootDir,
|
||||
workspaceDir: candidate.workspaceDir,
|
||||
});
|
||||
|
||||
const opened = openBoundaryFileSync({
|
||||
absolutePath: candidate.source,
|
||||
rootPath: candidate.rootDir,
|
||||
boundaryLabel: "plugin root",
|
||||
absolutePath: record.source,
|
||||
rootPath: record.source === candidate.source ? candidate.rootDir : repoRoot,
|
||||
boundaryLabel: record.source === candidate.source ? "plugin root" : "repo root",
|
||||
rejectHardlinks: false,
|
||||
skipLexicalRootCheck: true,
|
||||
});
|
||||
|
||||
@@ -5,6 +5,7 @@ import {
|
||||
clearBundledPluginMetadataCache,
|
||||
listBundledPluginMetadata,
|
||||
resolveBundledPluginGeneratedPath,
|
||||
resolveBundledPluginRepoEntryPath,
|
||||
} from "./bundled-plugin-metadata.js";
|
||||
import {
|
||||
createGeneratedPluginTempRoot,
|
||||
@@ -175,6 +176,45 @@ describe("bundled plugin metadata", () => {
|
||||
expectGeneratedPathResolution(tempRoot, path.join("plugin", "index.js"));
|
||||
});
|
||||
|
||||
it("resolves bundled repo entry paths from dist before workspace source", () => {
|
||||
const tempRoot = createGeneratedPluginTempRoot("openclaw-bundled-plugin-repo-entry-");
|
||||
const pluginRoot = path.join(tempRoot, "extensions", "alpha");
|
||||
const distPluginRoot = path.join(tempRoot, "dist", "extensions", "alpha");
|
||||
|
||||
writeJson(path.join(pluginRoot, "package.json"), {
|
||||
name: "@openclaw/alpha",
|
||||
version: "0.0.1",
|
||||
openclaw: {
|
||||
extensions: ["./index.ts"],
|
||||
},
|
||||
});
|
||||
writeJson(path.join(pluginRoot, "openclaw.plugin.json"), {
|
||||
id: "alpha",
|
||||
configSchema: { type: "object" },
|
||||
});
|
||||
fs.writeFileSync(path.join(pluginRoot, "index.ts"), "export const source = true;\n", "utf8");
|
||||
|
||||
expect(
|
||||
resolveBundledPluginRepoEntryPath({
|
||||
rootDir: tempRoot,
|
||||
pluginId: "alpha",
|
||||
preferBuilt: true,
|
||||
}),
|
||||
).toBe(path.join(pluginRoot, "index.ts"));
|
||||
|
||||
fs.mkdirSync(distPluginRoot, { recursive: true });
|
||||
fs.writeFileSync(path.join(distPluginRoot, "index.js"), "export const built = true;\n", "utf8");
|
||||
|
||||
clearBundledPluginMetadataCache();
|
||||
expect(
|
||||
resolveBundledPluginRepoEntryPath({
|
||||
rootDir: tempRoot,
|
||||
pluginId: "alpha",
|
||||
preferBuilt: true,
|
||||
}),
|
||||
).toBe(path.join(distPluginRoot, "index.js"));
|
||||
});
|
||||
|
||||
it("merges runtime channel schema metadata with manifest-owned channel config fields", () => {
|
||||
const tempRoot = createGeneratedPluginTempRoot("openclaw-bundled-plugin-channel-configs-");
|
||||
|
||||
|
||||
@@ -243,3 +243,37 @@ export function resolveBundledPluginGeneratedPath(
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
function normalizeRelativePluginEntryPath(entryPath: string): string {
|
||||
return entryPath.replace(/^\.\//u, "");
|
||||
}
|
||||
|
||||
export function resolveBundledPluginRepoEntryPath(params: {
|
||||
rootDir: string;
|
||||
pluginId: string;
|
||||
preferBuilt?: boolean;
|
||||
}): string | null {
|
||||
const metadata = findBundledPluginMetadataById(params.pluginId, { rootDir: params.rootDir });
|
||||
if (!metadata) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const entryOrder = params.preferBuilt
|
||||
? [metadata.source.built, metadata.source.source]
|
||||
: [metadata.source.source, metadata.source.built];
|
||||
const baseDirs = [
|
||||
path.resolve(params.rootDir, "dist", "extensions", metadata.dirName),
|
||||
path.resolve(params.rootDir, "extensions", metadata.dirName),
|
||||
];
|
||||
|
||||
for (const baseDir of baseDirs) {
|
||||
for (const entryPath of entryOrder) {
|
||||
const candidate = path.resolve(baseDir, normalizeRelativePluginEntryPath(entryPath));
|
||||
if (fs.existsSync(candidate)) {
|
||||
return candidate;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -15,6 +15,7 @@ import type {
|
||||
WebFetchProviderPlugin,
|
||||
WebSearchProviderPlugin,
|
||||
} from "../types.js";
|
||||
import { BUNDLED_PLUGIN_CONTRACT_SNAPSHOTS } from "./inventory/bundled-capability-metadata.js";
|
||||
import {
|
||||
loadVitestImageGenerationProviderContractRegistry,
|
||||
loadVitestMediaUnderstandingProviderContractRegistry,
|
||||
@@ -91,6 +92,21 @@ function uniqueStrings(values: readonly string[]): string[] {
|
||||
}
|
||||
|
||||
function resolveBundledManifestContracts(): PluginRegistrationContractEntry[] {
|
||||
if (process.env.VITEST) {
|
||||
return BUNDLED_PLUGIN_CONTRACT_SNAPSHOTS.map((entry) => ({
|
||||
pluginId: entry.pluginId,
|
||||
providerIds: [...entry.providerIds],
|
||||
speechProviderIds: [...entry.speechProviderIds],
|
||||
realtimeTranscriptionProviderIds: [...entry.realtimeTranscriptionProviderIds],
|
||||
realtimeVoiceProviderIds: [...entry.realtimeVoiceProviderIds],
|
||||
mediaUnderstandingProviderIds: [...entry.mediaUnderstandingProviderIds],
|
||||
imageGenerationProviderIds: [...entry.imageGenerationProviderIds],
|
||||
videoGenerationProviderIds: [...entry.videoGenerationProviderIds],
|
||||
webFetchProviderIds: [...entry.webFetchProviderIds],
|
||||
webSearchProviderIds: [...entry.webSearchProviderIds],
|
||||
toolNames: [...entry.toolNames],
|
||||
}));
|
||||
}
|
||||
return loadPluginManifestRegistry({})
|
||||
.plugins.filter(
|
||||
(plugin) =>
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
import { createJiti } from "jiti";
|
||||
import { loadBundledCapabilityRuntimeRegistry } from "../bundled-capability-runtime.js";
|
||||
import { resolveManifestContractPluginIds } from "../manifest-registry.js";
|
||||
import { resolveBundledPluginRepoEntryPath } from "../bundled-plugin-metadata.js";
|
||||
import { createCapturedPluginRegistration } from "../captured-registration.js";
|
||||
import type { OpenClawPluginDefinition } from "../types.js";
|
||||
import type {
|
||||
ImageGenerationProviderPlugin,
|
||||
MediaUnderstandingProviderPlugin,
|
||||
@@ -9,6 +12,7 @@ import type {
|
||||
SpeechProviderPlugin,
|
||||
VideoGenerationProviderPlugin,
|
||||
} from "../types.js";
|
||||
import { BUNDLED_PLUGIN_CONTRACT_SNAPSHOTS } from "./inventory/bundled-capability-metadata.js";
|
||||
|
||||
export type SpeechProviderContractEntry = {
|
||||
pluginId: string;
|
||||
@@ -54,6 +58,67 @@ type ManifestContractKey =
|
||||
| "videoGenerationProviders"
|
||||
| "musicGenerationProviders";
|
||||
|
||||
const VITEST_CONTRACT_PLUGIN_IDS = {
|
||||
imageGenerationProviders: BUNDLED_PLUGIN_CONTRACT_SNAPSHOTS.filter(
|
||||
(entry) => entry.imageGenerationProviderIds.length > 0,
|
||||
).map((entry) => entry.pluginId),
|
||||
speechProviders: BUNDLED_PLUGIN_CONTRACT_SNAPSHOTS.filter(
|
||||
(entry) => entry.speechProviderIds.length > 0,
|
||||
).map((entry) => entry.pluginId),
|
||||
mediaUnderstandingProviders: BUNDLED_PLUGIN_CONTRACT_SNAPSHOTS.filter(
|
||||
(entry) => entry.mediaUnderstandingProviderIds.length > 0,
|
||||
).map((entry) => entry.pluginId),
|
||||
realtimeVoiceProviders: BUNDLED_PLUGIN_CONTRACT_SNAPSHOTS.filter(
|
||||
(entry) => entry.realtimeVoiceProviderIds.length > 0,
|
||||
).map((entry) => entry.pluginId),
|
||||
realtimeTranscriptionProviders: BUNDLED_PLUGIN_CONTRACT_SNAPSHOTS.filter(
|
||||
(entry) => entry.realtimeTranscriptionProviderIds.length > 0,
|
||||
).map((entry) => entry.pluginId),
|
||||
videoGenerationProviders: BUNDLED_PLUGIN_CONTRACT_SNAPSHOTS.filter(
|
||||
(entry) => entry.videoGenerationProviderIds.length > 0,
|
||||
).map((entry) => entry.pluginId),
|
||||
} satisfies Record<ManifestContractKey, string[]>;
|
||||
|
||||
function loadVitestVideoGenerationFallbackEntries(
|
||||
pluginIds: readonly string[],
|
||||
): VideoGenerationProviderContractEntry[] {
|
||||
const jiti = createJiti(import.meta.url, {
|
||||
interopDefault: true,
|
||||
moduleCache: false,
|
||||
fsCache: false,
|
||||
});
|
||||
const repoRoot = process.cwd();
|
||||
return pluginIds.flatMap((pluginId) => {
|
||||
const modulePath = resolveBundledPluginRepoEntryPath({
|
||||
rootDir: repoRoot,
|
||||
pluginId,
|
||||
preferBuilt: true,
|
||||
});
|
||||
if (!modulePath) {
|
||||
return [];
|
||||
}
|
||||
try {
|
||||
const mod = jiti(modulePath) as
|
||||
| OpenClawPluginDefinition
|
||||
| { default?: OpenClawPluginDefinition };
|
||||
const plugin =
|
||||
(mod as { default?: OpenClawPluginDefinition }).default ??
|
||||
(mod as OpenClawPluginDefinition);
|
||||
if (typeof plugin?.register !== "function") {
|
||||
return [];
|
||||
}
|
||||
const captured = createCapturedPluginRegistration();
|
||||
void plugin.register(captured.api);
|
||||
return captured.videoGenerationProviders.map((provider) => ({
|
||||
pluginId,
|
||||
provider,
|
||||
}));
|
||||
} catch {
|
||||
return [];
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function loadVitestCapabilityContractEntries<T>(params: {
|
||||
contract: ManifestContractKey;
|
||||
pickEntries: (registry: ReturnType<typeof loadBundledCapabilityRuntimeRegistry>) => Array<{
|
||||
@@ -61,19 +126,30 @@ function loadVitestCapabilityContractEntries<T>(params: {
|
||||
provider: T;
|
||||
}>;
|
||||
}): Array<{ pluginId: string; provider: T }> {
|
||||
const pluginIds = resolveManifestContractPluginIds({
|
||||
contract: params.contract,
|
||||
origin: "bundled",
|
||||
});
|
||||
const pluginIds = VITEST_CONTRACT_PLUGIN_IDS[params.contract];
|
||||
if (pluginIds.length === 0) {
|
||||
return [];
|
||||
}
|
||||
return params.pickEntries(
|
||||
const bulkEntries = params.pickEntries(
|
||||
loadBundledCapabilityRuntimeRegistry({
|
||||
pluginIds,
|
||||
pluginSdkResolution: "dist",
|
||||
}),
|
||||
);
|
||||
const coveredPluginIds = new Set(bulkEntries.map((entry) => entry.pluginId));
|
||||
if (coveredPluginIds.size === pluginIds.length) {
|
||||
return bulkEntries;
|
||||
}
|
||||
return pluginIds.flatMap((pluginId) =>
|
||||
params
|
||||
.pickEntries(
|
||||
loadBundledCapabilityRuntimeRegistry({
|
||||
pluginIds: [pluginId],
|
||||
pluginSdkResolution: "dist",
|
||||
}),
|
||||
)
|
||||
.filter((entry) => entry.pluginId === pluginId),
|
||||
);
|
||||
}
|
||||
|
||||
export function loadVitestSpeechProviderContractRegistry(): SpeechProviderContractEntry[] {
|
||||
@@ -132,7 +208,7 @@ export function loadVitestImageGenerationProviderContractRegistry(): ImageGenera
|
||||
}
|
||||
|
||||
export function loadVitestVideoGenerationProviderContractRegistry(): VideoGenerationProviderContractEntry[] {
|
||||
return loadVitestCapabilityContractEntries({
|
||||
const entries = loadVitestCapabilityContractEntries({
|
||||
contract: "videoGenerationProviders",
|
||||
pickEntries: (registry) =>
|
||||
registry.videoGenerationProviders.map((entry) => ({
|
||||
@@ -140,6 +216,14 @@ export function loadVitestVideoGenerationProviderContractRegistry(): VideoGenera
|
||||
provider: entry.provider,
|
||||
})),
|
||||
});
|
||||
const coveredPluginIds = new Set(entries.map((entry) => entry.pluginId));
|
||||
const missingPluginIds = VITEST_CONTRACT_PLUGIN_IDS.videoGenerationProviders.filter(
|
||||
(pluginId) => !coveredPluginIds.has(pluginId),
|
||||
);
|
||||
if (missingPluginIds.length === 0) {
|
||||
return entries;
|
||||
}
|
||||
return [...entries, ...loadVitestVideoGenerationFallbackEntries(missingPluginIds)];
|
||||
}
|
||||
|
||||
export function loadVitestMusicGenerationProviderContractRegistry(): MusicGenerationProviderContractEntry[] {
|
||||
|
||||
Reference in New Issue
Block a user