Files
openclaw/src/plugins/loader.runtime-registry.test.ts
2026-04-04 01:07:28 +09:00

190 lines
5.8 KiB
TypeScript

import { afterEach, describe, expect, it } from "vitest";
import { __testing, clearPluginLoaderCache, resolveRuntimePluginRegistry } from "./loader.js";
import { resetPluginLoaderTestStateForTest } from "./loader.test-fixtures.js";
import {
getMemoryEmbeddingProvider,
registerMemoryEmbeddingProvider,
} from "./memory-embedding-providers.js";
import {
buildMemoryPromptSection,
getMemoryRuntime,
registerMemoryFlushPlanResolver,
registerMemoryPromptSection,
registerMemoryRuntime,
resolveMemoryFlushPlan,
} from "./memory-state.js";
import { createEmptyPluginRegistry } from "./registry.js";
import { setActivePluginRegistry } from "./runtime.js";
afterEach(() => {
resetPluginLoaderTestStateForTest();
});
describe("getCompatibleActivePluginRegistry", () => {
it("reuses the active registry only when the load context cache key matches", () => {
const registry = createEmptyPluginRegistry();
const loadOptions = {
config: {
plugins: {
allow: ["demo"],
load: { paths: ["/tmp/demo.js"] },
},
},
workspaceDir: "/tmp/workspace-a",
runtimeOptions: {
allowGatewaySubagentBinding: true,
},
};
const { cacheKey } = __testing.resolvePluginLoadCacheContext(loadOptions);
setActivePluginRegistry(registry, cacheKey);
expect(__testing.getCompatibleActivePluginRegistry(loadOptions)).toBe(registry);
expect(
__testing.getCompatibleActivePluginRegistry({
...loadOptions,
workspaceDir: "/tmp/workspace-b",
}),
).toBeUndefined();
expect(
__testing.getCompatibleActivePluginRegistry({
...loadOptions,
onlyPluginIds: ["demo"],
}),
).toBeUndefined();
expect(
__testing.getCompatibleActivePluginRegistry({
...loadOptions,
runtimeOptions: undefined,
}),
).toBeUndefined();
});
it("does not embed activation secrets in the loader cache key", () => {
const { cacheKey } = __testing.resolvePluginLoadCacheContext({
config: {
plugins: {
allow: ["telegram"],
},
},
activationSourceConfig: {
plugins: {
allow: ["telegram"],
},
channels: {
telegram: {
enabled: true,
botToken: "secret-token",
},
},
},
autoEnabledReasons: {
telegram: ["telegram configured"],
},
});
expect(cacheKey).not.toContain("secret-token");
expect(cacheKey).not.toContain("botToken");
expect(cacheKey).not.toContain("telegram configured");
});
it("falls back to the current active runtime when no compatibility-shaping inputs are supplied", () => {
const registry = createEmptyPluginRegistry();
setActivePluginRegistry(registry, "startup-registry");
expect(__testing.getCompatibleActivePluginRegistry()).toBe(registry);
});
it("does not reuse the active registry when core gateway method names differ", () => {
const registry = createEmptyPluginRegistry();
const loadOptions = {
config: {
plugins: {
allow: ["demo"],
load: { paths: ["/tmp/demo.js"] },
},
},
workspaceDir: "/tmp/workspace-a",
coreGatewayHandlers: {
"sessions.get": () => undefined,
},
};
const { cacheKey } = __testing.resolvePluginLoadCacheContext(loadOptions);
setActivePluginRegistry(registry, cacheKey);
expect(__testing.getCompatibleActivePluginRegistry(loadOptions)).toBe(registry);
expect(
__testing.getCompatibleActivePluginRegistry({
...loadOptions,
coreGatewayHandlers: {
"sessions.get": () => undefined,
"sessions.list": () => undefined,
},
}),
).toBeUndefined();
});
});
describe("resolveRuntimePluginRegistry", () => {
it("reuses the compatible active registry before attempting a fresh load", () => {
const registry = createEmptyPluginRegistry();
const loadOptions = {
config: {
plugins: {
allow: ["demo"],
},
},
workspaceDir: "/tmp/workspace-a",
};
const { cacheKey } = __testing.resolvePluginLoadCacheContext(loadOptions);
setActivePluginRegistry(registry, cacheKey);
expect(resolveRuntimePluginRegistry(loadOptions)).toBe(registry);
});
it("falls back to the current active runtime when no explicit load context is provided", () => {
const registry = createEmptyPluginRegistry();
setActivePluginRegistry(registry, "startup-registry");
expect(resolveRuntimePluginRegistry()).toBe(registry);
});
});
describe("clearPluginLoaderCache", () => {
it("resets registered memory plugin registries", () => {
registerMemoryEmbeddingProvider({
id: "stale",
create: async () => ({ provider: null }),
});
registerMemoryPromptSection(() => ["stale memory section"]);
registerMemoryFlushPlanResolver(() => ({
softThresholdTokens: 1,
forceFlushTranscriptBytes: 2,
reserveTokensFloor: 3,
prompt: "stale",
systemPrompt: "stale",
relativePath: "memory/stale.md",
}));
registerMemoryRuntime({
async getMemorySearchManager() {
return { manager: null };
},
resolveMemoryBackendConfig() {
return { backend: "builtin" as const };
},
});
expect(buildMemoryPromptSection({ availableTools: new Set() })).toEqual([
"stale memory section",
]);
expect(resolveMemoryFlushPlan({})?.relativePath).toBe("memory/stale.md");
expect(getMemoryRuntime()).toBeDefined();
expect(getMemoryEmbeddingProvider("stale")).toBeDefined();
clearPluginLoaderCache();
expect(buildMemoryPromptSection({ availableTools: new Set() })).toEqual([]);
expect(resolveMemoryFlushPlan({})).toBeNull();
expect(getMemoryRuntime()).toBeUndefined();
expect(getMemoryEmbeddingProvider("stale")).toBeUndefined();
});
});