mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-06 05:20:43 +00:00
222 lines
8.3 KiB
TypeScript
222 lines
8.3 KiB
TypeScript
import fs from "node:fs";
|
|
import os from "node:os";
|
|
import path from "node:path";
|
|
import { importFreshModule } from "openclaw/plugin-sdk/test-fixtures";
|
|
import { afterEach, describe, expect, it, vi } from "vitest";
|
|
|
|
const tempDirs: string[] = [];
|
|
const originalBundledPluginsDir = process.env.OPENCLAW_BUNDLED_PLUGINS_DIR;
|
|
|
|
function makeBundledRoot(prefix: string): { root: string; pluginsDir: string } {
|
|
const root = fs.mkdtempSync(path.join(os.tmpdir(), prefix));
|
|
tempDirs.push(root);
|
|
const pluginsDir = path.join(root, "dist", "extensions");
|
|
fs.mkdirSync(pluginsDir, { recursive: true });
|
|
return { root, pluginsDir };
|
|
}
|
|
|
|
function resolveMockRootSuffix(params: {
|
|
activeRoot: string | undefined;
|
|
rootAPluginsDir: string;
|
|
rootBPluginsDir: string;
|
|
}): "A" | "B" | "unknown" {
|
|
if (params.activeRoot === params.rootAPluginsDir) {
|
|
return "A";
|
|
}
|
|
if (params.activeRoot === params.rootBPluginsDir) {
|
|
return "B";
|
|
}
|
|
return "unknown";
|
|
}
|
|
|
|
afterEach(() => {
|
|
for (const dir of tempDirs.splice(0)) {
|
|
fs.rmSync(dir, { recursive: true, force: true });
|
|
}
|
|
if (originalBundledPluginsDir === undefined) {
|
|
delete process.env.OPENCLAW_BUNDLED_PLUGINS_DIR;
|
|
} else {
|
|
process.env.OPENCLAW_BUNDLED_PLUGINS_DIR = originalBundledPluginsDir;
|
|
}
|
|
vi.resetModules();
|
|
vi.doUnmock("../../plugins/channel-catalog-registry.js");
|
|
vi.doUnmock("./bundled.js");
|
|
vi.doUnmock("./bundled-ids.js");
|
|
});
|
|
|
|
describe("bundled root-aware caches", () => {
|
|
it("partitions bundled channel ids by active bundled root without re-importing", async () => {
|
|
const rootA = makeBundledRoot("openclaw-bundled-ids-a-");
|
|
const rootB = makeBundledRoot("openclaw-bundled-ids-b-");
|
|
|
|
vi.doMock("../../plugins/channel-catalog-registry.js", () => ({
|
|
listChannelCatalogEntries: (params?: { env?: NodeJS.ProcessEnv }) => {
|
|
const activeRoot = params?.env?.OPENCLAW_BUNDLED_PLUGINS_DIR;
|
|
if (activeRoot === rootA.pluginsDir) {
|
|
return [{ pluginId: "alpha" }];
|
|
}
|
|
if (activeRoot === rootB.pluginsDir) {
|
|
return [{ pluginId: "beta" }];
|
|
}
|
|
return [];
|
|
},
|
|
}));
|
|
|
|
const bundledIds = await importFreshModule<typeof import("./bundled-ids.js")>(
|
|
import.meta.url,
|
|
"./bundled-ids.js?scope=root-aware-id-cache",
|
|
);
|
|
|
|
process.env.OPENCLAW_BUNDLED_PLUGINS_DIR = rootA.pluginsDir;
|
|
expect(bundledIds.listBundledChannelPluginIds()).toEqual(["alpha"]);
|
|
|
|
process.env.OPENCLAW_BUNDLED_PLUGINS_DIR = rootB.pluginsDir;
|
|
expect(bundledIds.listBundledChannelPluginIds()).toEqual(["beta"]);
|
|
});
|
|
|
|
it("partitions bootstrap plugin caches by active bundled root without re-importing", async () => {
|
|
const rootA = makeBundledRoot("openclaw-bootstrap-a-");
|
|
const rootB = makeBundledRoot("openclaw-bootstrap-b-");
|
|
|
|
vi.doMock("./bundled-ids.js", () => ({
|
|
listBundledChannelPluginIdsForRoot: (cacheKey: string) => {
|
|
if (cacheKey === rootA.pluginsDir) {
|
|
return ["alpha"];
|
|
}
|
|
if (cacheKey === rootB.pluginsDir) {
|
|
return ["beta"];
|
|
}
|
|
return [];
|
|
},
|
|
}));
|
|
|
|
vi.doMock("./bundled.js", () => ({
|
|
getBundledChannelPlugin: (id: string) => ({
|
|
id,
|
|
meta: { id, label: `runtime-${id}` },
|
|
capabilities: {},
|
|
config: {},
|
|
}),
|
|
getBundledChannelSetupPlugin: (id: string) => {
|
|
const suffix = resolveMockRootSuffix({
|
|
activeRoot: process.env.OPENCLAW_BUNDLED_PLUGINS_DIR,
|
|
rootAPluginsDir: rootA.pluginsDir,
|
|
rootBPluginsDir: rootB.pluginsDir,
|
|
});
|
|
return {
|
|
id,
|
|
meta: { id, label: `setup-${suffix}` },
|
|
capabilities: {},
|
|
config: {},
|
|
};
|
|
},
|
|
getBundledChannelSecrets: (id: string) => ({
|
|
secretTargetRegistryEntries: [{ id: `runtime-${id}`, targetType: "channel" }],
|
|
}),
|
|
getBundledChannelSetupSecrets: (id: string) => {
|
|
const suffix = resolveMockRootSuffix({
|
|
activeRoot: process.env.OPENCLAW_BUNDLED_PLUGINS_DIR,
|
|
rootAPluginsDir: rootA.pluginsDir,
|
|
rootBPluginsDir: rootB.pluginsDir,
|
|
});
|
|
return {
|
|
secretTargetRegistryEntries: [{ id: `setup-${id}-${suffix}`, targetType: "channel" }],
|
|
};
|
|
},
|
|
}));
|
|
|
|
const bootstrapRegistry = await importFreshModule<typeof import("./bootstrap-registry.js")>(
|
|
import.meta.url,
|
|
"./bootstrap-registry.js?scope=root-aware-bootstrap-cache",
|
|
);
|
|
|
|
process.env.OPENCLAW_BUNDLED_PLUGINS_DIR = rootA.pluginsDir;
|
|
expect(bootstrapRegistry.listBootstrapChannelPluginIds()).toEqual(["alpha"]);
|
|
expect(bootstrapRegistry.getBootstrapChannelPlugin("alpha")?.meta.label).toBe("setup-A");
|
|
expect(
|
|
bootstrapRegistry.getBootstrapChannelSecrets("alpha")?.secretTargetRegistryEntries?.[0]?.id,
|
|
).toBe("setup-alpha-A");
|
|
|
|
process.env.OPENCLAW_BUNDLED_PLUGINS_DIR = rootB.pluginsDir;
|
|
expect(bootstrapRegistry.listBootstrapChannelPluginIds()).toEqual(["beta"]);
|
|
expect(bootstrapRegistry.getBootstrapChannelPlugin("beta")?.meta.label).toBe("setup-B");
|
|
expect(
|
|
bootstrapRegistry.getBootstrapChannelSecrets("beta")?.secretTargetRegistryEntries?.[0]?.id,
|
|
).toBe("setup-beta-B");
|
|
});
|
|
|
|
it("marks bundled plugin ids missing when bootstrap plugin loading throws", async () => {
|
|
const root = makeBundledRoot("openclaw-bootstrap-plugin-throw-");
|
|
|
|
vi.doMock("./bundled-ids.js", () => ({
|
|
listBundledChannelPluginIdsForRoot: (cacheKey: string) =>
|
|
cacheKey === root.pluginsDir ? ["alpha"] : [],
|
|
}));
|
|
|
|
const getBundledChannelPluginMock = vi.fn(() => {
|
|
throw new Error("Cannot find module 'nostr-tools'");
|
|
});
|
|
const getBundledChannelSecretsMock = vi.fn(() => {
|
|
throw new Error("secrets should not load after plugin is marked missing");
|
|
});
|
|
|
|
vi.doMock("./bundled.js", () => ({
|
|
getBundledChannelPlugin: getBundledChannelPluginMock,
|
|
getBundledChannelSetupPlugin: vi.fn(() => undefined),
|
|
getBundledChannelSecrets: getBundledChannelSecretsMock,
|
|
getBundledChannelSetupSecrets: vi.fn(() => undefined),
|
|
}));
|
|
|
|
const bootstrapRegistry = await importFreshModule<typeof import("./bootstrap-registry.js")>(
|
|
import.meta.url,
|
|
"./bootstrap-registry.js?scope=bootstrap-plugin-load-guard",
|
|
);
|
|
|
|
process.env.OPENCLAW_BUNDLED_PLUGINS_DIR = root.pluginsDir;
|
|
expect(bootstrapRegistry.listBootstrapChannelPluginIds()).toEqual(["alpha"]);
|
|
expect(bootstrapRegistry.getBootstrapChannelPlugin("alpha")).toBeUndefined();
|
|
expect(bootstrapRegistry.getBootstrapChannelPlugin("alpha")).toBeUndefined();
|
|
expect(bootstrapRegistry.getBootstrapChannelSecrets("alpha")).toBeUndefined();
|
|
expect(getBundledChannelPluginMock).toHaveBeenCalledTimes(1);
|
|
expect(getBundledChannelSecretsMock).not.toHaveBeenCalled();
|
|
});
|
|
|
|
it("marks bundled plugin ids missing when bootstrap secrets loading throws", async () => {
|
|
const root = makeBundledRoot("openclaw-bootstrap-secrets-throw-");
|
|
|
|
vi.doMock("./bundled-ids.js", () => ({
|
|
listBundledChannelPluginIdsForRoot: (cacheKey: string) =>
|
|
cacheKey === root.pluginsDir ? ["alpha"] : [],
|
|
}));
|
|
|
|
const getBundledChannelSecretsMock = vi.fn(() => {
|
|
throw new Error("Cannot find module '@larksuiteoapi/node-sdk'");
|
|
});
|
|
const getBundledChannelPluginMock = vi.fn(() => ({
|
|
id: "alpha",
|
|
meta: { id: "alpha", label: "Alpha" },
|
|
capabilities: {},
|
|
config: {},
|
|
}));
|
|
|
|
vi.doMock("./bundled.js", () => ({
|
|
getBundledChannelPlugin: getBundledChannelPluginMock,
|
|
getBundledChannelSetupPlugin: vi.fn(() => undefined),
|
|
getBundledChannelSecrets: getBundledChannelSecretsMock,
|
|
getBundledChannelSetupSecrets: vi.fn(() => undefined),
|
|
}));
|
|
|
|
const bootstrapRegistry = await importFreshModule<typeof import("./bootstrap-registry.js")>(
|
|
import.meta.url,
|
|
"./bootstrap-registry.js?scope=bootstrap-secrets-load-guard",
|
|
);
|
|
|
|
process.env.OPENCLAW_BUNDLED_PLUGINS_DIR = root.pluginsDir;
|
|
expect(bootstrapRegistry.getBootstrapChannelSecrets("alpha")).toBeUndefined();
|
|
expect(bootstrapRegistry.getBootstrapChannelSecrets("alpha")).toBeUndefined();
|
|
expect(bootstrapRegistry.getBootstrapChannelPlugin("alpha")).toBeUndefined();
|
|
expect(getBundledChannelSecretsMock).toHaveBeenCalledTimes(1);
|
|
expect(getBundledChannelPluginMock).not.toHaveBeenCalled();
|
|
});
|
|
});
|