mirror of
https://github.com/openclaw/openclaw.git
synced 2026-04-17 04:01:05 +00:00
214 lines
6.7 KiB
TypeScript
214 lines
6.7 KiB
TypeScript
import fs from "node:fs";
|
|
import os from "node:os";
|
|
import path from "node:path";
|
|
import { describe, expect, it } from "vitest";
|
|
import {
|
|
getChannelPluginCatalogEntry,
|
|
listChannelPluginCatalogEntries,
|
|
} from "../../../src/channels/plugins/catalog.js";
|
|
|
|
type CatalogEntryMeta = {
|
|
id: string;
|
|
label: string;
|
|
selectionLabel: string;
|
|
docsPath: string;
|
|
blurb: string;
|
|
detailLabel?: string;
|
|
aliases?: string[];
|
|
};
|
|
|
|
export function describeChannelCatalogEntryContract(params: {
|
|
channelId: string;
|
|
npmSpec: string;
|
|
alias?: string;
|
|
}) {
|
|
describe(`${params.channelId} channel catalog contract`, () => {
|
|
it("keeps the shipped catalog entry aligned", () => {
|
|
const entry = getChannelPluginCatalogEntry(params.channelId);
|
|
expect(entry?.install.npmSpec).toBe(params.npmSpec);
|
|
if (params.alias) {
|
|
expect(entry?.meta.aliases).toContain(params.alias);
|
|
}
|
|
});
|
|
|
|
it("appears in the channel catalog listing", () => {
|
|
const ids = listChannelPluginCatalogEntries().map((entry) => entry.id);
|
|
expect(ids).toContain(params.channelId);
|
|
});
|
|
});
|
|
}
|
|
|
|
export function describeBundledMetadataOnlyChannelCatalogContract(params: {
|
|
pluginId: string;
|
|
packageName: string;
|
|
npmSpec: string;
|
|
meta: CatalogEntryMeta;
|
|
defaultChoice?: string;
|
|
}) {
|
|
describe(`${params.pluginId} bundled metadata-only channel catalog contract`, () => {
|
|
it("includes the bundled metadata-only channel entry when the runtime entrypoint is omitted", () => {
|
|
const packageRoot = fs.mkdtempSync(path.join(os.tmpdir(), "openclaw-bundled-catalog-"));
|
|
const bundledDir = path.join(packageRoot, "dist", "extensions", params.pluginId);
|
|
fs.mkdirSync(bundledDir, { recursive: true });
|
|
fs.writeFileSync(
|
|
path.join(packageRoot, "package.json"),
|
|
JSON.stringify({ name: "openclaw" }),
|
|
"utf8",
|
|
);
|
|
fs.writeFileSync(
|
|
path.join(bundledDir, "package.json"),
|
|
JSON.stringify({
|
|
name: params.packageName,
|
|
openclaw: {
|
|
extensions: ["./index.js"],
|
|
channel: params.meta,
|
|
install: {
|
|
npmSpec: params.npmSpec,
|
|
defaultChoice: params.defaultChoice,
|
|
},
|
|
},
|
|
}),
|
|
"utf8",
|
|
);
|
|
fs.writeFileSync(path.join(bundledDir, "index.js"), "export default {};\n", "utf8");
|
|
fs.writeFileSync(
|
|
path.join(bundledDir, "openclaw.plugin.json"),
|
|
JSON.stringify({ id: params.pluginId, channels: [params.meta.id], configSchema: {} }),
|
|
"utf8",
|
|
);
|
|
|
|
const entry = listChannelPluginCatalogEntries({
|
|
env: {
|
|
...process.env,
|
|
OPENCLAW_BUNDLED_PLUGINS_DIR: path.join(packageRoot, "dist", "extensions"),
|
|
},
|
|
}).find((item) => item.id === params.meta.id);
|
|
|
|
expect(entry?.install.npmSpec).toBe(params.npmSpec);
|
|
expect(entry?.pluginId).toBe(params.pluginId);
|
|
});
|
|
});
|
|
}
|
|
|
|
export function describeOfficialFallbackChannelCatalogContract(params: {
|
|
channelId: string;
|
|
npmSpec: string;
|
|
meta: CatalogEntryMeta;
|
|
packageName: string;
|
|
pluginId: string;
|
|
externalNpmSpec: string;
|
|
externalLabel: string;
|
|
}) {
|
|
describe(`${params.channelId} official fallback channel catalog contract`, () => {
|
|
it("includes shipped official channel catalog entries when bundled metadata is omitted", () => {
|
|
const dir = fs.mkdtempSync(path.join(os.tmpdir(), "openclaw-official-catalog-"));
|
|
const catalogPath = path.join(dir, "channel-catalog.json");
|
|
fs.writeFileSync(
|
|
catalogPath,
|
|
JSON.stringify({
|
|
entries: [
|
|
{
|
|
name: params.packageName,
|
|
openclaw: {
|
|
channel: params.meta,
|
|
install: {
|
|
npmSpec: params.npmSpec,
|
|
defaultChoice: "npm",
|
|
},
|
|
},
|
|
},
|
|
],
|
|
}),
|
|
);
|
|
|
|
const entry = listChannelPluginCatalogEntries({
|
|
env: {
|
|
...process.env,
|
|
OPENCLAW_BUNDLED_PLUGINS_DIR: "/nonexistent/bundled/plugins",
|
|
},
|
|
officialCatalogPaths: [catalogPath],
|
|
}).find((item) => item.id === params.channelId);
|
|
|
|
expect(entry?.install.npmSpec).toBe(params.npmSpec);
|
|
expect(entry?.pluginId).toBeUndefined();
|
|
});
|
|
|
|
it("lets external catalogs override shipped fallback channel metadata", () => {
|
|
const dir = fs.mkdtempSync(path.join(os.tmpdir(), "openclaw-fallback-catalog-"));
|
|
const bundledDir = path.join(dir, "dist", "extensions", params.pluginId);
|
|
const officialCatalogPath = path.join(dir, "channel-catalog.json");
|
|
const externalCatalogPath = path.join(dir, "catalog.json");
|
|
fs.mkdirSync(bundledDir, { recursive: true });
|
|
fs.writeFileSync(
|
|
path.join(bundledDir, "package.json"),
|
|
JSON.stringify({
|
|
name: params.packageName,
|
|
openclaw: {
|
|
channel: {
|
|
...params.meta,
|
|
label: `${params.meta.label} Bundled`,
|
|
selectionLabel: `${params.meta.label} Bundled`,
|
|
blurb: "bundled fallback",
|
|
},
|
|
install: { npmSpec: params.npmSpec },
|
|
},
|
|
}),
|
|
"utf8",
|
|
);
|
|
fs.writeFileSync(
|
|
officialCatalogPath,
|
|
JSON.stringify({
|
|
entries: [
|
|
{
|
|
name: params.packageName,
|
|
openclaw: {
|
|
channel: {
|
|
...params.meta,
|
|
label: `${params.meta.label} Official`,
|
|
selectionLabel: `${params.meta.label} Official`,
|
|
blurb: "official fallback",
|
|
},
|
|
install: { npmSpec: params.npmSpec },
|
|
},
|
|
},
|
|
],
|
|
}),
|
|
"utf8",
|
|
);
|
|
fs.writeFileSync(
|
|
externalCatalogPath,
|
|
JSON.stringify({
|
|
entries: [
|
|
{
|
|
name: params.externalNpmSpec,
|
|
openclaw: {
|
|
channel: {
|
|
...params.meta,
|
|
label: params.externalLabel,
|
|
selectionLabel: params.externalLabel,
|
|
blurb: "external override",
|
|
},
|
|
install: { npmSpec: params.externalNpmSpec },
|
|
},
|
|
},
|
|
],
|
|
}),
|
|
"utf8",
|
|
);
|
|
|
|
const entry = listChannelPluginCatalogEntries({
|
|
catalogPaths: [externalCatalogPath],
|
|
officialCatalogPaths: [officialCatalogPath],
|
|
env: {
|
|
...process.env,
|
|
OPENCLAW_BUNDLED_PLUGINS_DIR: path.join(dir, "dist", "extensions"),
|
|
},
|
|
}).find((item) => item.id === params.channelId);
|
|
|
|
expect(entry?.install.npmSpec).toBe(params.externalNpmSpec);
|
|
expect(entry?.meta.label).toBe(params.externalLabel);
|
|
expect(entry?.pluginId).toBeUndefined();
|
|
});
|
|
});
|
|
}
|