




















@@ -2,6 +2,8 @@ import { beforeEach, describe, expect, it } from "vitest";
22import type { OpenClawConfig } from "../config/config.js";
33import {
44applyExclusiveSlotSelection,
5+buildPluginDiagnosticsReport,
6+buildPluginSnapshotReport,
57enablePluginInConfig,
68refreshPluginRegistry,
79resetPluginsCliTestState,
@@ -109,6 +111,168 @@ describe("persistPluginInstall", () => {
109111expect(next).toEqual(enabledConfig);
110112});
111113114+it("falls back to runtime kind registry cleanup when metadata omits kind", async () => {
115+const { persistPluginInstall } = await import("./plugins-install-persist.js");
116+const baseConfig = {
117+plugins: {
118+entries: {
119+"legacy-memory-a": { enabled: true },
120+},
121+},
122+} as OpenClawConfig;
123+const enabledConfig = {
124+plugins: {
125+entries: {
126+"legacy-memory-a": { enabled: true },
127+"legacy-memory": { enabled: true },
128+},
129+},
130+} as OpenClawConfig;
131+enablePluginInConfig.mockReturnValue({ config: enabledConfig });
132+buildPluginSnapshotReport.mockReturnValue({
133+plugins: [{ id: "legacy-memory-a" }, { id: "legacy-memory" }],
134+diagnostics: [],
135+});
136+buildPluginDiagnosticsReport.mockReturnValue({
137+plugins: [
138+{ id: "legacy-memory-a", kind: "memory" },
139+{ id: "legacy-memory", kind: "memory" },
140+],
141+diagnostics: [],
142+});
143+applyExclusiveSlotSelection.mockImplementation(((params: {
144+config: OpenClawConfig;
145+selectedId: string;
146+selectedKind?: string;
147+registry?: { plugins: Array<{ id: string; kind?: string }> };
148+}) => {
149+expect(params.selectedId).toBe("legacy-memory");
150+expect(params.selectedKind).toBe("memory");
151+expect(params.registry?.plugins).toEqual([
152+{ id: "legacy-memory-a", kind: "memory" },
153+{ id: "legacy-memory", kind: "memory" },
154+]);
155+return {
156+config: {
157+ ...params.config,
158+plugins: {
159+ ...params.config.plugins,
160+entries: {
161+ ...params.config.plugins?.entries,
162+"legacy-memory-a": { enabled: false },
163+},
164+slots: {
165+ ...params.config.plugins?.slots,
166+memory: "legacy-memory",
167+},
168+},
169+},
170+warnings: [],
171+changed: true,
172+};
173+}) as (...args: unknown[]) => unknown);
174+175+const next = await persistPluginInstall({
176+snapshot: {
177+config: baseConfig,
178+baseHash: "config-1",
179+},
180+pluginId: "legacy-memory",
181+install: {
182+source: "path",
183+sourcePath: "/tmp/legacy-memory",
184+installPath: "/tmp/legacy-memory",
185+},
186+});
187+188+expect(buildPluginDiagnosticsReport).toHaveBeenCalledWith({
189+config: enabledConfig,
190+});
191+expect(next.plugins?.entries?.["legacy-memory-a"]?.enabled).toBe(false);
192+expect(next.plugins?.slots?.memory).toBe("legacy-memory");
193+});
194+195+it("uses runtime registry cleanup when a manifest-kind plugin has runtime-kind siblings", async () => {
196+const { persistPluginInstall } = await import("./plugins-install-persist.js");
197+const baseConfig = {
198+plugins: {
199+entries: {
200+"legacy-memory-a": { enabled: true },
201+},
202+},
203+} as OpenClawConfig;
204+const enabledConfig = {
205+plugins: {
206+entries: {
207+"legacy-memory-a": { enabled: true },
208+"memory-b": { enabled: true },
209+},
210+},
211+} as OpenClawConfig;
212+enablePluginInConfig.mockReturnValue({ config: enabledConfig });
213+buildPluginSnapshotReport.mockReturnValue({
214+plugins: [{ id: "legacy-memory-a" }, { id: "memory-b", kind: "memory" }],
215+diagnostics: [],
216+});
217+buildPluginDiagnosticsReport.mockReturnValue({
218+plugins: [
219+{ id: "legacy-memory-a", kind: "memory" },
220+{ id: "memory-b", kind: "memory" },
221+],
222+diagnostics: [],
223+});
224+applyExclusiveSlotSelection.mockImplementation(((params: {
225+config: OpenClawConfig;
226+selectedId: string;
227+selectedKind?: string;
228+registry?: { plugins: Array<{ id: string; kind?: string }> };
229+}) => {
230+expect(params.selectedId).toBe("memory-b");
231+expect(params.selectedKind).toBe("memory");
232+expect(params.registry?.plugins).toEqual([
233+{ id: "legacy-memory-a", kind: "memory" },
234+{ id: "memory-b", kind: "memory" },
235+]);
236+return {
237+config: {
238+ ...params.config,
239+plugins: {
240+ ...params.config.plugins,
241+entries: {
242+ ...params.config.plugins?.entries,
243+"legacy-memory-a": { enabled: false },
244+},
245+slots: {
246+ ...params.config.plugins?.slots,
247+memory: "memory-b",
248+},
249+},
250+},
251+warnings: [],
252+changed: true,
253+};
254+}) as (...args: unknown[]) => unknown);
255+256+const next = await persistPluginInstall({
257+snapshot: {
258+config: baseConfig,
259+baseHash: "config-1",
260+},
261+pluginId: "memory-b",
262+install: {
263+source: "path",
264+sourcePath: "/tmp/memory-b",
265+installPath: "/tmp/memory-b",
266+},
267+});
268+269+expect(buildPluginDiagnosticsReport).toHaveBeenCalledWith({
270+config: enabledConfig,
271+});
272+expect(next.plugins?.entries?.["legacy-memory-a"]?.enabled).toBe(false);
273+expect(next.plugins?.slots?.memory).toBe("memory-b");
274+});
275+112276it("can persist an install record without enabling a plugin that needs config first", async () => {
113277const { persistPluginInstall } = await import("./plugins-install-persist.js");
114278const baseConfig = {
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。