mirror of
https://github.com/openclaw/openclaw.git
synced 2026-04-04 22:01:15 +00:00
305 lines
9.8 KiB
TypeScript
305 lines
9.8 KiB
TypeScript
import fs from "node:fs";
|
|
import path from "node:path";
|
|
import { describe, expect, it } from "vitest";
|
|
import {
|
|
clearBundledPluginMetadataCache,
|
|
listBundledPluginMetadata,
|
|
resolveBundledPluginGeneratedPath,
|
|
} from "./bundled-plugin-metadata.js";
|
|
import {
|
|
createGeneratedPluginTempRoot,
|
|
installGeneratedPluginTempRootCleanup,
|
|
pluginTestRepoRoot as repoRoot,
|
|
writeJson,
|
|
} from "./generated-plugin-test-helpers.js";
|
|
|
|
const BUNDLED_PLUGIN_METADATA_TEST_TIMEOUT_MS = 300_000;
|
|
|
|
installGeneratedPluginTempRootCleanup();
|
|
|
|
function expectTestOnlyArtifactsExcluded(artifacts: readonly string[]) {
|
|
artifacts.forEach((artifact) => {
|
|
expect(artifact).not.toMatch(/^test-/);
|
|
expect(artifact).not.toContain(".test-");
|
|
expect(artifact).not.toMatch(/\.test\.js$/);
|
|
});
|
|
}
|
|
|
|
function expectGeneratedPathResolution(tempRoot: string, expectedRelativePath: string) {
|
|
expect(
|
|
resolveBundledPluginGeneratedPath(tempRoot, {
|
|
source: "plugin/index.ts",
|
|
built: "plugin/index.js",
|
|
}),
|
|
).toBe(path.join(tempRoot, expectedRelativePath));
|
|
}
|
|
|
|
function expectArtifactPresence(
|
|
artifacts: readonly string[] | undefined,
|
|
params: { contains?: readonly string[]; excludes?: readonly string[] },
|
|
) {
|
|
if (params.contains) {
|
|
for (const artifact of params.contains) {
|
|
expect(artifacts).toContain(artifact);
|
|
}
|
|
}
|
|
if (params.excludes) {
|
|
for (const artifact of params.excludes) {
|
|
expect(artifacts).not.toContain(artifact);
|
|
}
|
|
}
|
|
}
|
|
|
|
describe("bundled plugin metadata", () => {
|
|
it(
|
|
"matches the runtime metadata snapshot",
|
|
{ timeout: BUNDLED_PLUGIN_METADATA_TEST_TIMEOUT_MS },
|
|
() => {
|
|
expect(listBundledPluginMetadata({ rootDir: repoRoot })).toEqual(listBundledPluginMetadata());
|
|
},
|
|
);
|
|
|
|
it("captures setup-entry metadata for bundled channel plugins", () => {
|
|
const discord = listBundledPluginMetadata().find((entry) => entry.dirName === "discord");
|
|
expect(discord?.source).toEqual({ source: "./index.ts", built: "index.js" });
|
|
expect(discord?.setupSource).toEqual({ source: "./setup-entry.ts", built: "setup-entry.js" });
|
|
expectArtifactPresence(discord?.publicSurfaceArtifacts, {
|
|
contains: ["api.js", "runtime-api.js", "session-key-api.js"],
|
|
excludes: ["test-api.js"],
|
|
});
|
|
expectArtifactPresence(discord?.runtimeSidecarArtifacts, {
|
|
contains: ["runtime-api.js"],
|
|
});
|
|
expect(discord?.manifest.id).toBe("discord");
|
|
expect(discord?.manifest.channelConfigs?.discord).toEqual(
|
|
expect.objectContaining({
|
|
schema: expect.objectContaining({ type: "object" }),
|
|
}),
|
|
);
|
|
});
|
|
|
|
it("loads tlon channel config metadata from the lightweight schema surface", () => {
|
|
const tlon = listBundledPluginMetadata().find((entry) => entry.dirName === "tlon");
|
|
expect(tlon?.manifest.channelConfigs?.tlon).toEqual(
|
|
expect.objectContaining({
|
|
schema: expect.objectContaining({ type: "object" }),
|
|
}),
|
|
);
|
|
});
|
|
|
|
it("excludes test-only public surface artifacts", () => {
|
|
listBundledPluginMetadata().forEach((entry) =>
|
|
expectTestOnlyArtifactsExcluded(entry.publicSurfaceArtifacts ?? []),
|
|
);
|
|
});
|
|
|
|
it("prefers built generated paths when present and falls back to source paths", () => {
|
|
const tempRoot = createGeneratedPluginTempRoot("openclaw-bundled-plugin-metadata-");
|
|
|
|
fs.mkdirSync(path.join(tempRoot, "plugin"), { recursive: true });
|
|
fs.writeFileSync(path.join(tempRoot, "plugin", "index.ts"), "export {};\n", "utf8");
|
|
expectGeneratedPathResolution(tempRoot, path.join("plugin", "index.ts"));
|
|
|
|
fs.writeFileSync(path.join(tempRoot, "plugin", "index.js"), "export {};\n", "utf8");
|
|
expectGeneratedPathResolution(tempRoot, path.join("plugin", "index.js"));
|
|
});
|
|
|
|
it("merges runtime channel schema metadata with manifest-owned channel config fields", () => {
|
|
const tempRoot = createGeneratedPluginTempRoot("openclaw-bundled-plugin-channel-configs-");
|
|
|
|
writeJson(path.join(tempRoot, "extensions", "alpha", "package.json"), {
|
|
name: "@openclaw/alpha",
|
|
version: "0.0.1",
|
|
openclaw: {
|
|
extensions: ["./index.ts"],
|
|
channel: {
|
|
id: "alpha",
|
|
label: "Alpha Root Label",
|
|
blurb: "Alpha Root Description",
|
|
preferOver: ["alpha-legacy"],
|
|
},
|
|
},
|
|
});
|
|
writeJson(path.join(tempRoot, "extensions", "alpha", "openclaw.plugin.json"), {
|
|
id: "alpha",
|
|
channels: ["alpha"],
|
|
configSchema: { type: "object" },
|
|
channelConfigs: {
|
|
alpha: {
|
|
schema: { type: "object", properties: { stale: { type: "boolean" } } },
|
|
label: "Manifest Label",
|
|
uiHints: {
|
|
"channels.alpha.explicitOnly": {
|
|
help: "manifest hint",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
});
|
|
fs.writeFileSync(
|
|
path.join(tempRoot, "extensions", "alpha", "index.ts"),
|
|
"export {};\n",
|
|
"utf8",
|
|
);
|
|
fs.mkdirSync(path.join(tempRoot, "extensions", "alpha", "src"), { recursive: true });
|
|
fs.writeFileSync(
|
|
path.join(tempRoot, "extensions", "alpha", "src", "config-schema.js"),
|
|
[
|
|
"export const AlphaChannelConfigSchema = {",
|
|
" schema: {",
|
|
" type: 'object',",
|
|
" properties: { generated: { type: 'string' } },",
|
|
" },",
|
|
" uiHints: {",
|
|
" 'channels.alpha.generatedOnly': { help: 'generated hint' },",
|
|
" },",
|
|
"};",
|
|
"",
|
|
].join("\n"),
|
|
"utf8",
|
|
);
|
|
|
|
clearBundledPluginMetadataCache();
|
|
const entries = listBundledPluginMetadata({ rootDir: tempRoot });
|
|
const channelConfigs = entries[0]?.manifest.channelConfigs as
|
|
| Record<string, unknown>
|
|
| undefined;
|
|
expect(channelConfigs?.alpha).toEqual({
|
|
schema: {
|
|
type: "object",
|
|
properties: {
|
|
generated: { type: "string" },
|
|
},
|
|
},
|
|
label: "Manifest Label",
|
|
description: "Alpha Root Description",
|
|
preferOver: ["alpha-legacy"],
|
|
uiHints: {
|
|
"channels.alpha.generatedOnly": { help: "generated hint" },
|
|
"channels.alpha.explicitOnly": { help: "manifest hint" },
|
|
},
|
|
});
|
|
});
|
|
|
|
it("captures top-level public surface artifacts without duplicating the primary entrypoints", () => {
|
|
const tempRoot = createGeneratedPluginTempRoot("openclaw-bundled-plugin-public-artifacts-");
|
|
|
|
writeJson(path.join(tempRoot, "extensions", "alpha", "package.json"), {
|
|
name: "@openclaw/alpha",
|
|
version: "0.0.1",
|
|
openclaw: {
|
|
extensions: ["./index.ts"],
|
|
setupEntry: "./setup-entry.ts",
|
|
},
|
|
});
|
|
writeJson(path.join(tempRoot, "extensions", "alpha", "openclaw.plugin.json"), {
|
|
id: "alpha",
|
|
configSchema: { type: "object" },
|
|
});
|
|
fs.writeFileSync(
|
|
path.join(tempRoot, "extensions", "alpha", "index.ts"),
|
|
"export {};\n",
|
|
"utf8",
|
|
);
|
|
fs.writeFileSync(
|
|
path.join(tempRoot, "extensions", "alpha", "setup-entry.ts"),
|
|
"export {};\n",
|
|
"utf8",
|
|
);
|
|
fs.writeFileSync(path.join(tempRoot, "extensions", "alpha", "api.ts"), "export {};\n", "utf8");
|
|
fs.writeFileSync(
|
|
path.join(tempRoot, "extensions", "alpha", "runtime-api.ts"),
|
|
"export {};\n",
|
|
"utf8",
|
|
);
|
|
|
|
clearBundledPluginMetadataCache();
|
|
const entries = listBundledPluginMetadata({ rootDir: tempRoot });
|
|
const firstEntry = entries[0] as
|
|
| {
|
|
publicSurfaceArtifacts?: string[];
|
|
runtimeSidecarArtifacts?: string[];
|
|
}
|
|
| undefined;
|
|
expect(firstEntry?.publicSurfaceArtifacts).toEqual(["api.js", "runtime-api.js"]);
|
|
expect(firstEntry?.runtimeSidecarArtifacts).toEqual(["runtime-api.js"]);
|
|
});
|
|
|
|
it("loads channel config metadata from built public surfaces in dist-only roots", () => {
|
|
const tempRoot = createGeneratedPluginTempRoot("openclaw-bundled-plugin-dist-config-");
|
|
const distRoot = path.join(tempRoot, "dist");
|
|
|
|
writeJson(path.join(distRoot, "extensions", "alpha", "package.json"), {
|
|
name: "@openclaw/alpha",
|
|
version: "0.0.1",
|
|
openclaw: {
|
|
extensions: ["./index.ts"],
|
|
channel: {
|
|
id: "alpha",
|
|
label: "Alpha Root Label",
|
|
blurb: "Alpha Root Description",
|
|
},
|
|
},
|
|
});
|
|
writeJson(path.join(distRoot, "extensions", "alpha", "openclaw.plugin.json"), {
|
|
id: "alpha",
|
|
configSchema: {
|
|
type: "object",
|
|
properties: {},
|
|
},
|
|
channels: ["alpha"],
|
|
channelConfigs: {
|
|
alpha: {
|
|
schema: { type: "object", properties: { stale: { type: "boolean" } } },
|
|
uiHints: {
|
|
"channels.alpha.explicitOnly": {
|
|
help: "manifest hint",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
});
|
|
fs.writeFileSync(
|
|
path.join(distRoot, "extensions", "alpha", "index.js"),
|
|
"export {};\n",
|
|
"utf8",
|
|
);
|
|
fs.writeFileSync(
|
|
path.join(distRoot, "extensions", "alpha", "channel-config-api.js"),
|
|
[
|
|
"export const AlphaChannelConfigSchema = {",
|
|
" schema: {",
|
|
" type: 'object',",
|
|
" properties: { built: { type: 'string' } },",
|
|
" },",
|
|
" uiHints: {",
|
|
" 'channels.alpha.generatedOnly': { help: 'built hint' },",
|
|
" },",
|
|
"};",
|
|
"",
|
|
].join("\n"),
|
|
"utf8",
|
|
);
|
|
|
|
clearBundledPluginMetadataCache();
|
|
const entries = listBundledPluginMetadata({ rootDir: distRoot });
|
|
const channelConfigs = entries[0]?.manifest.channelConfigs as
|
|
| Record<string, unknown>
|
|
| undefined;
|
|
expect(channelConfigs?.alpha).toEqual({
|
|
schema: {
|
|
type: "object",
|
|
properties: {
|
|
built: { type: "string" },
|
|
},
|
|
},
|
|
label: "Alpha Root Label",
|
|
description: "Alpha Root Description",
|
|
uiHints: {
|
|
"channels.alpha.generatedOnly": { help: "built hint" },
|
|
"channels.alpha.explicitOnly": { help: "manifest hint" },
|
|
},
|
|
});
|
|
});
|
|
});
|