Files
openclaw/src/cli/plugins-location-bridges.test.ts
2026-05-02 13:30:07 -07:00

187 lines
6.1 KiB
TypeScript

import { beforeEach, describe, expect, it, vi } from "vitest";
import type { InstalledPluginIndex } from "../plugins/installed-plugin-index.js";
import type { InstalledPluginStartupInfo } from "../plugins/installed-plugin-index.js";
import type { PluginManifestRegistry } from "../plugins/manifest-registry.js";
const readPersistedInstalledPluginIndexMock = vi.fn();
const loadPluginManifestRegistryForInstalledIndexMock = vi.fn();
const startupInfo: InstalledPluginStartupInfo = {
sidecar: false,
memory: false,
deferConfiguredChannelFullLoadUntilAfterListen: false,
agentHarnesses: [],
};
vi.mock("../plugins/installed-plugin-index-store.js", () => ({
readPersistedInstalledPluginIndex: (...args: unknown[]) =>
readPersistedInstalledPluginIndexMock(...args),
}));
vi.mock("../plugins/manifest-registry-installed.js", () => ({
loadPluginManifestRegistryForInstalledIndex: (...args: unknown[]) =>
loadPluginManifestRegistryForInstalledIndexMock(...args),
}));
const { listPersistedBundledPluginLocationBridges } = await import("./plugins-location-bridges.js");
function makeIndex(record: InstalledPluginIndex["plugins"][number]): InstalledPluginIndex {
return {
version: 1,
hostContractVersion: "2026.5.2",
compatRegistryVersion: "test",
migrationVersion: 1,
policyHash: "test",
generatedAtMs: 1,
refreshReason: "manual",
installRecords: {},
plugins: [record],
diagnostics: [],
};
}
function makeRegistry(pluginId: string): PluginManifestRegistry {
return {
plugins: [
{
id: pluginId,
name: pluginId,
rootDir: `/app/dist/extensions/${pluginId}`,
source: `/app/dist/extensions/${pluginId}/index.js`,
origin: "bundled",
channels: [pluginId],
providers: [],
cliBackends: [],
syntheticAuthRefs: [],
nonSecretAuthMarkers: [],
skills: [],
settingsFiles: [],
hooks: [],
configContracts: [],
activation: {},
startup: {},
packageInstall: {
clawhubSpec: `clawhub:@openclaw/${pluginId}`,
npmSpec: `@openclaw/${pluginId}`,
defaultChoice: "clawhub",
},
},
],
diagnostics: [],
} as unknown as PluginManifestRegistry;
}
describe("listPersistedBundledPluginLocationBridges", () => {
beforeEach(() => {
readPersistedInstalledPluginIndexMock.mockReset();
loadPluginManifestRegistryForInstalledIndexMock.mockReset();
});
it("keeps persisted bundled relocations npm-first for launch", async () => {
readPersistedInstalledPluginIndexMock.mockResolvedValue(
makeIndex({
pluginId: "diagnostics-otel",
manifestPath: "/app/dist/extensions/diagnostics-otel/openclaw.plugin.json",
manifestHash: "hash",
source: "/app/dist/extensions/diagnostics-otel/index.js",
rootDir: "/app/dist/extensions/diagnostics-otel",
origin: "bundled",
enabled: true,
startup: startupInfo,
compat: [],
packageInstall: {
defaultChoice: "clawhub",
clawhub: {
spec: "clawhub:@openclaw/diagnostics-otel",
packageName: "@openclaw/diagnostics-otel",
exactVersion: false,
},
npm: {
spec: "@openclaw/diagnostics-otel",
packageName: "@openclaw/diagnostics-otel",
selectorKind: "none",
exactVersion: false,
pinState: "floating-without-integrity",
},
warnings: [],
},
}),
);
loadPluginManifestRegistryForInstalledIndexMock.mockReturnValue(
makeRegistry("diagnostics-otel"),
);
await expect(listPersistedBundledPluginLocationBridges({})).resolves.toEqual([
{
bundledPluginId: "diagnostics-otel",
pluginId: "diagnostics-otel",
preferredSource: "npm",
npmSpec: "@openclaw/diagnostics-otel",
clawhubSpec: "clawhub:@openclaw/diagnostics-otel",
channelIds: ["diagnostics-otel"],
},
]);
});
it("uses official external catalog metadata when the persisted bundled row lacks npm metadata", async () => {
readPersistedInstalledPluginIndexMock.mockResolvedValue(
makeIndex({
pluginId: "diagnostics-otel",
manifestPath: "/app/dist/extensions/diagnostics-otel/openclaw.plugin.json",
manifestHash: "hash",
source: "/app/dist/extensions/diagnostics-otel/index.js",
rootDir: "/app/dist/extensions/diagnostics-otel",
origin: "bundled",
enabled: true,
startup: startupInfo,
compat: [],
packageInstall: {
defaultChoice: "clawhub",
clawhub: {
spec: "clawhub:@openclaw/diagnostics-otel",
packageName: "@openclaw/diagnostics-otel",
exactVersion: false,
},
warnings: [],
},
}),
);
loadPluginManifestRegistryForInstalledIndexMock.mockReturnValue(
makeRegistry("diagnostics-otel"),
);
await expect(listPersistedBundledPluginLocationBridges({})).resolves.toEqual([
{
bundledPluginId: "diagnostics-otel",
pluginId: "diagnostics-otel",
preferredSource: "npm",
npmSpec: "@openclaw/diagnostics-otel",
clawhubSpec: "clawhub:@openclaw/diagnostics-otel",
channelIds: ["diagnostics-otel"],
},
]);
});
it("does not create a relocation bridge without persisted or official install metadata", async () => {
readPersistedInstalledPluginIndexMock.mockResolvedValue(
makeIndex({
pluginId: "local-only",
manifestPath: "/app/dist/extensions/local-only/openclaw.plugin.json",
manifestHash: "hash",
source: "/app/dist/extensions/local-only/index.js",
rootDir: "/app/dist/extensions/local-only",
origin: "bundled",
enabled: true,
startup: startupInfo,
compat: [],
packageInstall: {
warnings: [],
},
}),
);
loadPluginManifestRegistryForInstalledIndexMock.mockReturnValue(makeRegistry("local-only"));
await expect(listPersistedBundledPluginLocationBridges({})).resolves.toEqual([]);
});
});