


















@@ -28,6 +28,38 @@ const mocks = vi.hoisted(() => ({
2828let ensurePluginRegistryLoaded: typeof import("./runtime-registry-loader.js").ensurePluginRegistryLoaded;
2929let resetPluginRegistryLoadedForTests: typeof import("./runtime-registry-loader.js").__testing.resetPluginRegistryLoadedForTests;
303031+function requireRecord(value: unknown, label: string): Record<string, unknown> {
32+expect(value, label).toBeTypeOf("object");
33+expect(value, label).not.toBeNull();
34+return value as Record<string, unknown>;
35+}
36+37+function loadOptions(index = 0) {
38+return requireRecord(mocks.loadOpenClawPlugins.mock.calls[index]?.[0], `load options ${index}`);
39+}
40+41+function configuredChannelOptions(index = 0) {
42+return requireRecord(
43+mocks.resolveConfiguredChannelPluginIds.mock.calls[index]?.[0],
44+`configured channel options ${index}`,
45+);
46+}
47+48+function scopedChannelOptions(index = 0) {
49+return requireRecord(
50+mocks.resolveDiscoverableScopedChannelPluginIds.mock.calls[index]?.[0],
51+`scoped channel options ${index}`,
52+);
53+}
54+55+function pluginsConfig(config: Record<string, unknown>) {
56+return requireRecord(config.plugins, "plugins config");
57+}
58+59+function pluginEntries(config: Record<string, unknown>) {
60+return requireRecord(pluginsConfig(config).entries, "plugin entries");
61+}
62+3163vi.mock("../loader.js", () => ({
3264loadOpenClawPlugins: (...args: Parameters<typeof mocks.loadOpenClawPlugins>) =>
3365mocks.loadOpenClawPlugins(...args),
@@ -142,46 +174,37 @@ describe("ensurePluginRegistryLoaded", () => {
142174activationSourceConfig: { plugins: { allow: ["demo-channel"] } } as never,
143175});
144176145-expect(mocks.resolveConfiguredChannelPluginIds).toHaveBeenCalledWith(
146-expect.objectContaining({
147-config: resolvedConfig,
148-activationSourceConfig: { plugins: { allow: ["demo-channel"] } },
149- env,
150-workspaceDir: "/resolved-workspace",
151-}),
152-);
177+const channelOptions = configuredChannelOptions();
178+expect(channelOptions.config).toEqual(resolvedConfig);
179+expect(channelOptions.activationSourceConfig).toEqual({ plugins: { allow: ["demo-channel"] } });
180+expect(channelOptions.env).toBe(env);
181+expect(channelOptions.workspaceDir).toBe("/resolved-workspace");
153182expect(mocks.applyPluginAutoEnable).toHaveBeenCalledWith({
154183config: rawConfig,
155184 env,
156185});
157-expect(mocks.loadOpenClawPlugins).toHaveBeenCalledWith(
158-expect.objectContaining({
159-config: expect.objectContaining({
160- ...resolvedConfig,
161-plugins: expect.objectContaining({
162-entries: expect.objectContaining({
163-demo: { enabled: true },
164-"demo-channel": { enabled: true },
165-}),
166-allow: ["demo-channel"],
167-}),
168-}),
169-activationSourceConfig: {
170-plugins: {
171-allow: ["demo-channel"],
172-entries: {
173-"demo-channel": { enabled: true },
174-},
175-},
176-},
177-autoEnabledReasons: {
178-demo: ["demo configured"],
186+const load = loadOptions();
187+const loadConfig = requireRecord(load.config, "load config");
188+expect(loadConfig.channels).toEqual(rawConfig.channels);
189+expect(pluginEntries(loadConfig)).toEqual({
190+demo: { enabled: true },
191+"demo-channel": { enabled: true },
192+});
193+expect(pluginsConfig(loadConfig).allow).toEqual(["demo-channel"]);
194+expect(load.activationSourceConfig).toEqual({
195+plugins: {
196+allow: ["demo-channel"],
197+entries: {
198+"demo-channel": { enabled: true },
179199},
180-workspaceDir: "/resolved-workspace",
181-onlyPluginIds: ["demo-channel"],
182-throwOnLoadError: true,
183-}),
184-);
200+},
201+});
202+expect(load.autoEnabledReasons).toEqual({
203+demo: ["demo configured"],
204+});
205+expect(load.workspaceDir).toBe("/resolved-workspace");
206+expect(load.onlyPluginIds).toEqual(["demo-channel"]);
207+expect(load.throwOnLoadError).toBe(true);
185208});
186209187210it("temporarily activates configured-channel owners before loading them", () => {
@@ -194,27 +217,14 @@ describe("ensurePluginRegistryLoaded", () => {
194217config: rawConfig as never,
195218});
196219197-expect(mocks.loadOpenClawPlugins).toHaveBeenCalledWith(
198-expect.objectContaining({
199-config: expect.objectContaining({
200-plugins: expect.objectContaining({
201-entries: expect.objectContaining({
202-"activation-only-channel": { enabled: true },
203-}),
204-allow: ["activation-only-channel"],
205-}),
206-}),
207-activationSourceConfig: expect.objectContaining({
208-plugins: expect.objectContaining({
209-entries: expect.objectContaining({
210-"activation-only-channel": { enabled: true },
211-}),
212-allow: ["activation-only-channel"],
213-}),
214-}),
215-onlyPluginIds: ["activation-only-channel"],
216-}),
217-);
220+const load = loadOptions();
221+const loadConfig = requireRecord(load.config, "load config");
222+expect(pluginEntries(loadConfig)["activation-only-channel"]).toEqual({ enabled: true });
223+expect(pluginsConfig(loadConfig).allow).toEqual(["activation-only-channel"]);
224+const activation = requireRecord(load.activationSourceConfig, "activation config");
225+expect(pluginEntries(activation)["activation-only-channel"]).toEqual({ enabled: true });
226+expect(pluginsConfig(activation).allow).toEqual(["activation-only-channel"]);
227+expect(load.onlyPluginIds).toEqual(["activation-only-channel"]);
218228});
219229220230it("does not cache scoped loads by explicit plugin ids", () => {
@@ -230,14 +240,8 @@ describe("ensurePluginRegistryLoaded", () => {
230240});
231241232242expect(mocks.loadOpenClawPlugins).toHaveBeenCalledTimes(2);
233-expect(mocks.loadOpenClawPlugins).toHaveBeenNthCalledWith(
234-1,
235-expect.objectContaining({ onlyPluginIds: ["demo-a"] }),
236-);
237-expect(mocks.loadOpenClawPlugins).toHaveBeenNthCalledWith(
238-2,
239-expect.objectContaining({ onlyPluginIds: ["demo-b"] }),
240-);
243+expect(loadOptions(0).onlyPluginIds).toEqual(["demo-a"]);
244+expect(loadOptions(1).onlyPluginIds).toEqual(["demo-b"]);
241245});
242246243247it("maps explicit channel scopes to owner plugin ids before loading", () => {
@@ -250,42 +254,21 @@ describe("ensurePluginRegistryLoaded", () => {
250254onlyChannelIds: ["external-chat"],
251255});
252256253-expect(mocks.resolveDiscoverableScopedChannelPluginIds).toHaveBeenCalledWith(
254-expect.objectContaining({
255-config: expect.objectContaining({
256- ...rawConfig,
257-plugins: expect.objectContaining({
258-entries: expect.objectContaining({
259-demo: { enabled: true },
260-}),
261-}),
262-}),
263-activationSourceConfig: rawConfig,
264-channelIds: ["external-chat"],
265-workspaceDir: "/resolved-workspace",
266-}),
267-);
268-expect(mocks.loadOpenClawPlugins).toHaveBeenCalledWith(
269-expect.objectContaining({
270-config: expect.objectContaining({
271-plugins: expect.objectContaining({
272-allow: ["external-chat-plugin"],
273-entries: expect.objectContaining({
274-"external-chat-plugin": { enabled: true },
275-}),
276-}),
277-}),
278-activationSourceConfig: expect.objectContaining({
279-plugins: expect.objectContaining({
280-allow: ["external-chat-plugin"],
281-entries: expect.objectContaining({
282-"external-chat-plugin": { enabled: true },
283-}),
284-}),
285-}),
286-onlyPluginIds: ["external-chat-plugin"],
287-}),
288-);
257+const channelOptions = scopedChannelOptions();
258+const channelConfig = requireRecord(channelOptions.config, "scoped channel config");
259+expect(channelConfig.channels).toEqual(rawConfig.channels);
260+expect(pluginEntries(channelConfig).demo).toEqual({ enabled: true });
261+expect(channelOptions.activationSourceConfig).toBe(rawConfig);
262+expect(channelOptions.channelIds).toEqual(["external-chat"]);
263+expect(channelOptions.workspaceDir).toBe("/resolved-workspace");
264+const load = loadOptions();
265+const loadConfig = requireRecord(load.config, "load config");
266+expect(pluginsConfig(loadConfig).allow).toEqual(["external-chat-plugin"]);
267+expect(pluginEntries(loadConfig)["external-chat-plugin"]).toEqual({ enabled: true });
268+const activation = requireRecord(load.activationSourceConfig, "activation config");
269+expect(pluginsConfig(activation).allow).toEqual(["external-chat-plugin"]);
270+expect(pluginEntries(activation)["external-chat-plugin"]).toEqual({ enabled: true });
271+expect(load.onlyPluginIds).toEqual(["external-chat-plugin"]);
289272});
290273291274it("forwards explicit empty scopes without widening to channel resolution", () => {
@@ -297,11 +280,7 @@ describe("ensurePluginRegistryLoaded", () => {
297280298281expect(mocks.resolveConfiguredChannelPluginIds).not.toHaveBeenCalled();
299282expect(mocks.resolveChannelPluginIds).not.toHaveBeenCalled();
300-expect(mocks.loadOpenClawPlugins).toHaveBeenCalledWith(
301-expect.objectContaining({
302-onlyPluginIds: [],
303-}),
304-);
283+expect(loadOptions().onlyPluginIds).toEqual([]);
305284});
306285307286it("preserves empty configured-channel scopes when no owners are activatable", () => {
@@ -312,11 +291,7 @@ describe("ensurePluginRegistryLoaded", () => {
312291config: { channels: { demo: { enabled: true } } } as never,
313292});
314293315-expect(mocks.loadOpenClawPlugins).toHaveBeenCalledWith(
316-expect.objectContaining({
317-onlyPluginIds: [],
318-}),
319-);
294+expect(loadOptions().onlyPluginIds).toEqual([]);
320295});
321296322297it("does not forward empty channel scopes for broad channel loads", () => {
@@ -327,14 +302,7 @@ describe("ensurePluginRegistryLoaded", () => {
327302config: {} as never,
328303});
329304330-expect(mocks.loadOpenClawPlugins).toHaveBeenCalledWith(
331-expect.not.objectContaining({
332-onlyPluginIds: [],
333-}),
334-);
335-expect(
336-(mocks.loadOpenClawPlugins.mock.calls[0]?.[0] as { onlyPluginIds?: string[] }).onlyPluginIds,
337-).toBeUndefined();
305+expect(loadOptions().onlyPluginIds).toBeUndefined();
338306});
339307340308it("derives all-scope runtime loads from effective plugin ids", () => {
@@ -353,21 +321,13 @@ describe("ensurePluginRegistryLoaded", () => {
353321 env,
354322workspaceDir: "/resolved-workspace",
355323});
356-expect(mocks.loadOpenClawPlugins).toHaveBeenCalledWith(
357-expect.objectContaining({
358-config: expect.objectContaining({
359- ...config,
360-plugins: expect.objectContaining({
361-entries: expect.objectContaining({
362-demo: { enabled: true },
363-}),
364-}),
365-}),
366-onlyPluginIds: ["demo-effective", "demo-hook"],
367-throwOnLoadError: true,
368-workspaceDir: "/resolved-workspace",
369-}),
370-);
324+const load = loadOptions();
325+const loadConfig = requireRecord(load.config, "load config");
326+expect(loadConfig.channels).toEqual(config.channels);
327+expect(pluginEntries(loadConfig).demo).toEqual({ enabled: true });
328+expect(load.onlyPluginIds).toEqual(["demo-effective", "demo-hook"]);
329+expect(load.throwOnLoadError).toBe(true);
330+expect(load.workspaceDir).toBe("/resolved-workspace");
371331});
372332373333it("preserves empty all-scope loads instead of widening to all discovered plugins", () => {
@@ -378,11 +338,7 @@ describe("ensurePluginRegistryLoaded", () => {
378338config: { plugins: { enabled: true } } as never,
379339});
380340381-expect(mocks.loadOpenClawPlugins).toHaveBeenCalledWith(
382-expect.objectContaining({
383-onlyPluginIds: [],
384-}),
385-);
341+expect(loadOptions().onlyPluginIds).toEqual([]);
386342});
387343388344it("reuses a compatible active registry instead of forcing a broad reload", () => {
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。