




















@@ -0,0 +1,110 @@
1+import { describe, expect, it } from "vitest";
2+import type { InstalledPluginIndex } from "./installed-plugin-index.js";
3+import {
4+resolvePluginControlPlaneContext,
5+resolvePluginControlPlaneFingerprint,
6+resolvePluginDiscoveryContext,
7+resolvePluginDiscoveryFingerprint,
8+} from "./plugin-control-plane-context.js";
9+10+function createIndex(pluginId: string): InstalledPluginIndex {
11+return {
12+version: 1,
13+hostContractVersion: "test",
14+compatRegistryVersion: "test",
15+migrationVersion: 1,
16+policyHash: "policy",
17+generatedAtMs: 1,
18+installRecords: {},
19+diagnostics: [],
20+plugins: [
21+{
22+ pluginId,
23+manifestPath: `/plugins/${pluginId}/openclaw.plugin.json`,
24+manifestHash: `${pluginId}-manifest-hash`,
25+rootDir: `/plugins/${pluginId}`,
26+origin: "global",
27+enabled: true,
28+startup: {
29+sidecar: false,
30+memory: false,
31+deferConfiguredChannelFullLoadUntilAfterListen: false,
32+agentHarnesses: [],
33+},
34+compat: [],
35+},
36+],
37+};
38+}
39+40+describe("plugin control-plane context", () => {
41+it("resolves env-sensitive discovery roots and load paths before fingerprinting", () => {
42+const config = { plugins: { load: { paths: ["~/plugins", "/opt/shared"] } } };
43+const envA = { HOME: "/home/a", OPENCLAW_HOME: "/openclaw/a" } as NodeJS.ProcessEnv;
44+const envB = { HOME: "/home/b", OPENCLAW_HOME: "/openclaw/b" } as NodeJS.ProcessEnv;
45+46+const contextA = resolvePluginDiscoveryContext({ config, env: envA });
47+const contextB = resolvePluginDiscoveryContext({ config, env: envB });
48+49+expect(contextA.loadPaths).toEqual(["/openclaw/a/plugins", "/opt/shared"]);
50+expect(contextB.loadPaths).toEqual(["/openclaw/b/plugins", "/opt/shared"]);
51+expect(resolvePluginDiscoveryFingerprint({ config, env: envA })).not.toBe(
52+resolvePluginDiscoveryFingerprint({ config, env: envB }),
53+);
54+});
55+56+it("includes policy, inventory, and activation in one control-plane fingerprint", () => {
57+const config = { plugins: { allow: ["demo"] } };
58+const base = resolvePluginControlPlaneFingerprint({
59+ config,
60+env: { HOME: "/home/a", OPENCLAW_HOME: "/openclaw/a" } as NodeJS.ProcessEnv,
61+index: createIndex("demo"),
62+activationFingerprint: "activation-a",
63+});
64+65+expect(
66+resolvePluginControlPlaneFingerprint({
67+ config,
68+env: { HOME: "/home/a", OPENCLAW_HOME: "/openclaw/a" } as NodeJS.ProcessEnv,
69+index: createIndex("other"),
70+activationFingerprint: "activation-a",
71+}),
72+).not.toBe(base);
73+expect(
74+resolvePluginControlPlaneFingerprint({
75+ config,
76+env: { HOME: "/home/a", OPENCLAW_HOME: "/openclaw/a" } as NodeJS.ProcessEnv,
77+index: createIndex("demo"),
78+activationFingerprint: "activation-b",
79+}),
80+).not.toBe(base);
81+expect(
82+resolvePluginControlPlaneFingerprint({
83+config: { plugins: { deny: ["demo"] } },
84+env: { HOME: "/home/a", OPENCLAW_HOME: "/openclaw/a" } as NodeJS.ProcessEnv,
85+index: createIndex("demo"),
86+activationFingerprint: "activation-a",
87+}),
88+).not.toBe(base);
89+});
90+91+it("keeps the canonical context inspectable for cache diagnostics", () => {
92+const context = resolvePluginControlPlaneContext({
93+config: { plugins: { load: { paths: ["/opt/plugins"] } } },
94+env: { HOME: "/home/a", OPENCLAW_HOME: "/openclaw/a" } as NodeJS.ProcessEnv,
95+inventoryFingerprint: "inventory",
96+policyHash: "policy",
97+});
98+99+expect(context).toMatchObject({
100+discovery: {
101+loadPaths: ["/opt/plugins"],
102+roots: {
103+global: "/openclaw/a/.openclaw/extensions",
104+},
105+},
106+inventoryFingerprint: "inventory",
107+policyFingerprint: "policy",
108+});
109+});
110+});
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。