Files
openclaw/src/plugins/current-plugin-metadata-snapshot.test.ts
2026-05-02 04:10:49 +01:00

170 lines
6.1 KiB
TypeScript

import fs from "node:fs";
import os from "node:os";
import path from "node:path";
import { describe, expect, it } from "vitest";
import {
clearCurrentPluginMetadataSnapshot,
getCurrentPluginMetadataSnapshot,
setCurrentPluginMetadataSnapshot,
} from "./current-plugin-metadata-snapshot.js";
import { resolveInstalledPluginIndexPolicyHash } from "./installed-plugin-index-policy.js";
import { writePersistedInstalledPluginIndexSync } from "./installed-plugin-index-store.js";
import type { PluginMetadataSnapshot } from "./plugin-metadata-snapshot.js";
function createSnapshot(
params: {
config?: Parameters<typeof resolveInstalledPluginIndexPolicyHash>[0];
workspaceDir?: string;
} = {},
): PluginMetadataSnapshot {
return {
policyHash: resolveInstalledPluginIndexPolicyHash(params.config),
...(params.workspaceDir ? { workspaceDir: params.workspaceDir } : {}),
index: {
version: 1,
hostContractVersion: "test",
compatRegistryVersion: "test",
migrationVersion: 1,
policyHash: resolveInstalledPluginIndexPolicyHash(params.config),
generatedAtMs: 1,
installRecords: {},
plugins: [],
diagnostics: [],
},
registryDiagnostics: [],
manifestRegistry: { plugins: [], diagnostics: [] },
plugins: [],
diagnostics: [],
byPluginId: new Map(),
normalizePluginId: (pluginId) => pluginId,
owners: {
channels: new Map(),
channelConfigs: new Map(),
providers: new Map(),
modelCatalogProviders: new Map(),
cliBackends: new Map(),
setupProviders: new Map(),
commandAliases: new Map(),
contracts: new Map(),
},
metrics: {
registrySnapshotMs: 0,
manifestRegistryMs: 0,
ownerMapsMs: 0,
totalMs: 0,
indexPluginCount: 0,
manifestPluginCount: 0,
},
};
}
describe("current plugin metadata snapshot", () => {
it("returns the current snapshot only for matching config policy and workspace", () => {
const config = { plugins: { allow: ["demo"] } };
const snapshot = createSnapshot({ config, workspaceDir: "/workspace/a" });
setCurrentPluginMetadataSnapshot(snapshot, { config });
expect(getCurrentPluginMetadataSnapshot({ config, workspaceDir: "/workspace/a" })).toBe(
snapshot,
);
expect(getCurrentPluginMetadataSnapshot({ config })).toBeUndefined();
expect(
getCurrentPluginMetadataSnapshot({
config: { plugins: { allow: ["other"] } },
workspaceDir: "/workspace/a",
}),
).toBeUndefined();
expect(
getCurrentPluginMetadataSnapshot({ config, workspaceDir: "/workspace/b" }),
).toBeUndefined();
});
it("rejects a workspace-scoped snapshot when the caller does not provide workspace scope", () => {
const config = { plugins: { allow: ["demo"] } };
const snapshot = createSnapshot({ config, workspaceDir: "/workspace/a" });
setCurrentPluginMetadataSnapshot(snapshot, { config });
expect(getCurrentPluginMetadataSnapshot({ config })).toBeUndefined();
});
it("rejects a current snapshot when plugin load paths change", () => {
const config = { plugins: { load: { paths: ["/plugins/one"] } } };
const snapshot = createSnapshot({ config });
setCurrentPluginMetadataSnapshot(snapshot, { config });
expect(getCurrentPluginMetadataSnapshot({ config })).toBe(snapshot);
expect(
getCurrentPluginMetadataSnapshot({
config: { plugins: { load: { paths: ["/plugins/two"] } } },
}),
).toBeUndefined();
});
it("rejects a current snapshot when env-resolved plugin load paths change", () => {
const config = { plugins: { load: { paths: ["~/plugins"] } } };
const snapshot = createSnapshot({ config });
const snapshotEnv = {
HOME: "/home/snapshot",
OPENCLAW_HOME: undefined,
} as NodeJS.ProcessEnv;
const requestedEnv = {
HOME: "/home/requested",
OPENCLAW_HOME: undefined,
} as NodeJS.ProcessEnv;
setCurrentPluginMetadataSnapshot(snapshot, { config, env: snapshotEnv });
expect(getCurrentPluginMetadataSnapshot({ config, env: snapshotEnv })).toBe(snapshot);
expect(getCurrentPluginMetadataSnapshot({ config, env: requestedEnv })).toBeUndefined();
});
it("rejects a current snapshot when env-resolved plugin roots change", () => {
const config = {};
const snapshot = createSnapshot({ config });
const snapshotEnv = {
HOME: "/home/snapshot",
OPENCLAW_HOME: undefined,
} as NodeJS.ProcessEnv;
const requestedEnv = {
HOME: "/home/requested",
OPENCLAW_HOME: undefined,
} as NodeJS.ProcessEnv;
setCurrentPluginMetadataSnapshot(snapshot, { config, env: snapshotEnv });
expect(getCurrentPluginMetadataSnapshot({ config, env: snapshotEnv })).toBe(snapshot);
expect(getCurrentPluginMetadataSnapshot({ config, env: requestedEnv })).toBeUndefined();
});
it("keeps source-policy compatibility when storing an auto-enabled runtime config", () => {
const sourceConfig = { channels: { telegram: { botToken: "token" } } };
const autoEnabledConfig = {
...sourceConfig,
plugins: { allow: ["telegram"] },
};
const snapshot = createSnapshot({ config: sourceConfig });
setCurrentPluginMetadataSnapshot(snapshot, { config: autoEnabledConfig });
expect(getCurrentPluginMetadataSnapshot({ config: sourceConfig })).toBe(snapshot);
expect(getCurrentPluginMetadataSnapshot({ config: autoEnabledConfig })).toBeUndefined();
});
it("clears the current snapshot", () => {
setCurrentPluginMetadataSnapshot(createSnapshot());
clearCurrentPluginMetadataSnapshot();
expect(getCurrentPluginMetadataSnapshot()).toBeUndefined();
});
it("clears the current snapshot when the persisted installed index changes", () => {
const tempDir = fs.mkdtempSync(path.join(os.tmpdir(), "openclaw-plugin-metadata-"));
try {
setCurrentPluginMetadataSnapshot(createSnapshot());
writePersistedInstalledPluginIndexSync(createSnapshot().index, { stateDir: tempDir });
expect(getCurrentPluginMetadataSnapshot()).toBeUndefined();
} finally {
fs.rmSync(tempDir, { recursive: true, force: true });
}
});
});