mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-06 16:50:43 +00:00
test: cover staged bundled facade deps
This commit is contained in:
@@ -342,6 +342,9 @@ Facade-loaded bundled plugin public surfaces (`api.ts`, `runtime-api.ts`,
|
||||
`index.ts`, `setup-entry.ts`, and similar public entry files) prefer the
|
||||
active runtime config snapshot when OpenClaw is already running. If no runtime
|
||||
snapshot exists yet, they fall back to the resolved config file on disk.
|
||||
Packaged bundled plugin facades should be loaded through the OpenClaw SDK
|
||||
facade loaders; direct imports from `dist/extensions/...` bypass staged runtime
|
||||
dependency mirrors that packaged installs use for plugin-owned dependencies.
|
||||
|
||||
Provider plugins can expose a narrow plugin-local contract barrel when a
|
||||
helper is intentionally provider-specific and does not belong in a generic SDK
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import fs from "node:fs";
|
||||
import path from "node:path";
|
||||
import { fileURLToPath, pathToFileURL } from "node:url";
|
||||
import { afterEach, describe, expect, it, vi } from "vitest";
|
||||
import {
|
||||
clearBundledRuntimeDependencyNodePaths,
|
||||
@@ -8,6 +9,7 @@ import {
|
||||
import { shouldExpectNativeJitiForJavaScriptTestRuntime } from "../test-utils/jiti-runtime.js";
|
||||
import {
|
||||
listImportedBundledPluginFacadeIds,
|
||||
loadBundledPluginPublicSurfaceModule,
|
||||
loadBundledPluginPublicSurfaceModuleSync,
|
||||
resetFacadeLoaderStateForTest,
|
||||
setFacadeLoaderJitiFactoryForTest,
|
||||
@@ -24,6 +26,7 @@ const originalBundledPluginsDir = process.env.OPENCLAW_BUNDLED_PLUGINS_DIR;
|
||||
const originalDisableBundledPlugins = process.env.OPENCLAW_DISABLE_BUNDLED_PLUGINS;
|
||||
const originalPluginStageDir = process.env.OPENCLAW_PLUGIN_STAGE_DIR;
|
||||
const FACADE_LOADER_GLOBAL = "__openclawTestLoadBundledPluginPublicSurfaceModuleSync";
|
||||
const STAGED_RUNTIME_DEP_NAME = "openclaw-facade-loader-runtime-dep";
|
||||
type FacadeLoaderJitiFactory = NonNullable<Parameters<typeof setFacadeLoaderJitiFactoryForTest>[0]>;
|
||||
|
||||
function forceNodeRuntimeVersionsForTest(): () => void {
|
||||
@@ -82,74 +85,84 @@ function createCircularPluginDir(prefix: string): string {
|
||||
return rootDir;
|
||||
}
|
||||
|
||||
function createPackagedBundledPluginDirWithStagedRuntimeDep(prefix: string): {
|
||||
function writeJsonFile(filePath: string, value: unknown): void {
|
||||
fs.mkdirSync(path.dirname(filePath), { recursive: true });
|
||||
fs.writeFileSync(filePath, `${JSON.stringify(value, null, 2)}\n`, "utf8");
|
||||
}
|
||||
|
||||
function createPackagedBundledPluginDirWithStagedRuntimeDep(params: {
|
||||
marker: string;
|
||||
prefix: string;
|
||||
}): {
|
||||
bundledPluginsDir: string;
|
||||
env: NodeJS.ProcessEnv;
|
||||
installRoot: string;
|
||||
modulePath: string;
|
||||
packageRoot: string;
|
||||
pluginRoot: string;
|
||||
stageRoot: string;
|
||||
} {
|
||||
const packageRoot = createTempDirSync(prefix);
|
||||
const packageRoot = createTempDirSync(params.prefix);
|
||||
const pluginRoot = path.join(packageRoot, "dist", "extensions", "demo");
|
||||
const stageRoot = path.join(packageRoot, "stage");
|
||||
const env = {
|
||||
...process.env,
|
||||
OPENCLAW_BUNDLED_PLUGINS_DIR: path.join(packageRoot, "dist", "extensions"),
|
||||
OPENCLAW_PLUGIN_STAGE_DIR: stageRoot,
|
||||
};
|
||||
fs.mkdirSync(pluginRoot, { recursive: true });
|
||||
|
||||
writeJsonFile(path.join(packageRoot, "package.json"), {
|
||||
name: "openclaw",
|
||||
version: "0.0.0",
|
||||
type: "module",
|
||||
});
|
||||
writeJsonFile(path.join(pluginRoot, "package.json"), {
|
||||
name: "@openclaw/plugin-demo",
|
||||
version: "0.0.0",
|
||||
type: "module",
|
||||
dependencies: {
|
||||
[STAGED_RUNTIME_DEP_NAME]: "1.0.0",
|
||||
},
|
||||
});
|
||||
const modulePath = path.join(pluginRoot, "api.js");
|
||||
fs.writeFileSync(
|
||||
path.join(packageRoot, "package.json"),
|
||||
JSON.stringify({ name: "openclaw", version: "0.0.0", type: "module" }, null, 2),
|
||||
"utf8",
|
||||
);
|
||||
fs.writeFileSync(
|
||||
path.join(pluginRoot, "package.json"),
|
||||
JSON.stringify(
|
||||
{
|
||||
name: "@openclaw/plugin-demo",
|
||||
version: "0.0.0",
|
||||
type: "module",
|
||||
dependencies: {
|
||||
"facade-runtime-dep": "1.0.0",
|
||||
},
|
||||
},
|
||||
null,
|
||||
2,
|
||||
),
|
||||
"utf8",
|
||||
);
|
||||
fs.writeFileSync(
|
||||
path.join(pluginRoot, "api.js"),
|
||||
modulePath,
|
||||
[
|
||||
'import { marker as depMarker } from "facade-runtime-dep";',
|
||||
`import { marker as depMarker } from ${JSON.stringify(STAGED_RUNTIME_DEP_NAME)};`,
|
||||
"export const marker = `facade:${depMarker}`;",
|
||||
"export const moduleUrl = import.meta.url;",
|
||||
"",
|
||||
].join("\n"),
|
||||
"utf8",
|
||||
);
|
||||
|
||||
const installRoot = resolveBundledRuntimeDependencyInstallRoot(pluginRoot, {
|
||||
env: {
|
||||
...process.env,
|
||||
OPENCLAW_PLUGIN_STAGE_DIR: stageRoot,
|
||||
},
|
||||
env,
|
||||
});
|
||||
const depRoot = path.join(installRoot, "node_modules", STAGED_RUNTIME_DEP_NAME);
|
||||
writeJsonFile(path.join(depRoot, "package.json"), {
|
||||
name: STAGED_RUNTIME_DEP_NAME,
|
||||
version: "1.0.0",
|
||||
type: "module",
|
||||
exports: "./index.js",
|
||||
});
|
||||
const depRoot = path.join(installRoot, "node_modules", "facade-runtime-dep");
|
||||
fs.mkdirSync(depRoot, { recursive: true });
|
||||
fs.writeFileSync(
|
||||
path.join(depRoot, "package.json"),
|
||||
JSON.stringify(
|
||||
{ name: "facade-runtime-dep", version: "1.0.0", type: "module", exports: "./index.js" },
|
||||
null,
|
||||
2,
|
||||
),
|
||||
path.join(depRoot, "index.js"),
|
||||
`export const marker = ${JSON.stringify(params.marker)};\n`,
|
||||
"utf8",
|
||||
);
|
||||
fs.writeFileSync(path.join(depRoot, "index.js"), 'export const marker = "staged";\n', "utf8");
|
||||
|
||||
return {
|
||||
bundledPluginsDir: path.join(packageRoot, "dist", "extensions"),
|
||||
env,
|
||||
installRoot,
|
||||
modulePath,
|
||||
packageRoot,
|
||||
pluginRoot,
|
||||
stageRoot,
|
||||
};
|
||||
}
|
||||
|
||||
afterEach(() => {
|
||||
vi.restoreAllMocks();
|
||||
resetFacadeLoaderStateForTest();
|
||||
@@ -278,20 +291,52 @@ describe("plugin-sdk facade loader", () => {
|
||||
}
|
||||
});
|
||||
|
||||
it("loads built bundled public surfaces through staged runtime deps", () => {
|
||||
const fixture = createPackagedBundledPluginDirWithStagedRuntimeDep(
|
||||
"openclaw-facade-loader-runtime-deps-",
|
||||
);
|
||||
it("loads built bundled sync public surfaces through staged runtime deps", async () => {
|
||||
const fixture = createPackagedBundledPluginDirWithStagedRuntimeDep({
|
||||
marker: "staged",
|
||||
prefix: "openclaw-facade-loader-runtime-deps-",
|
||||
});
|
||||
process.env.OPENCLAW_BUNDLED_PLUGINS_DIR = fixture.bundledPluginsDir;
|
||||
process.env.OPENCLAW_PLUGIN_STAGE_DIR = fixture.stageRoot;
|
||||
|
||||
const loaded = loadBundledPluginPublicSurfaceModuleSync<{ marker: string }>({
|
||||
await expect(import(pathToFileURL(fixture.modulePath).href)).rejects.toMatchObject({
|
||||
code: "ERR_MODULE_NOT_FOUND",
|
||||
});
|
||||
|
||||
const loaded = loadBundledPluginPublicSurfaceModuleSync<{
|
||||
marker: string;
|
||||
moduleUrl: string;
|
||||
}>({
|
||||
dirName: "demo",
|
||||
artifactBasename: "api.js",
|
||||
});
|
||||
|
||||
expect(loaded.marker).toBe("facade:staged");
|
||||
expect(fs.existsSync(path.join(fixture.pluginRoot, "node_modules"))).toBe(false);
|
||||
expect(fs.realpathSync(fileURLToPath(loaded.moduleUrl))).toBe(
|
||||
fs.realpathSync(path.join(fixture.installRoot, "dist", "extensions", "demo", "api.js")),
|
||||
);
|
||||
});
|
||||
|
||||
it("loads built bundled async public surfaces through staged runtime deps", async () => {
|
||||
const fixture = createPackagedBundledPluginDirWithStagedRuntimeDep({
|
||||
marker: "async-staged",
|
||||
prefix: "openclaw-facade-loader-built-async-",
|
||||
});
|
||||
|
||||
const loaded = await loadBundledPluginPublicSurfaceModule<{
|
||||
marker: string;
|
||||
moduleUrl: string;
|
||||
}>({
|
||||
dirName: "demo",
|
||||
artifactBasename: "api.js",
|
||||
env: fixture.env,
|
||||
});
|
||||
|
||||
expect(loaded.marker).toBe("facade:async-staged");
|
||||
expect(fs.realpathSync(fileURLToPath(loaded.moduleUrl))).toBe(
|
||||
fs.realpathSync(path.join(fixture.installRoot, "dist", "extensions", "demo", "api.js")),
|
||||
);
|
||||
});
|
||||
|
||||
it("breaks circular facade re-entry during module evaluation", () => {
|
||||
|
||||
Reference in New Issue
Block a user