

























@@ -5,8 +5,13 @@ import { afterEach, describe, expect, it, vi } from "vitest";
55import type { ConfigWriteNotification } from "../config/config.js";
66import type { OpenClawConfig } from "../config/types.openclaw.js";
77import { consumeGatewaySigusr1RestartIntent } from "../infra/restart.js";
8+import {
9+pinActivePluginChannelRegistry,
10+releasePinnedPluginChannelRegistry,
11+} from "../plugins/runtime.js";
812import { createEmptyRuntimeWebToolsMetadata } from "../secrets/runtime-fast-path.js";
913import { activateSecretsRuntimeSnapshot, clearSecretsRuntimeSnapshot } from "../secrets/runtime.js";
14+import { createChannelTestPluginBase, createTestRegistry } from "../test-utils/channel-plugins.js";
1015import { diffConfigPaths } from "./config-diff.js";
1116import {
1217buildGatewayReloadPlan,
@@ -141,7 +146,13 @@ vi.mock("../agents/agent-bundle-mcp-tools.js", () => ({
141146disposeAllSessionMcpRuntimes: hoisted.disposeAllSessionMcpRuntimes,
142147}));
143148144-function createReloadHandlersForTest(logReload = { info: vi.fn(), warn: vi.fn() }) {
149+function createReloadHandlersForTest(
150+logReload = { info: vi.fn(), warn: vi.fn() },
151+channels?: {
152+start: (channel: ChannelKind) => Promise<void>;
153+stop: (channel: ChannelKind) => Promise<void>;
154+},
155+) {
145156const cron = { start: vi.fn(async () => {}), stop: vi.fn() };
146157const heartbeatRunner = {
147158stop: vi.fn(),
@@ -158,8 +169,8 @@ function createReloadHandlersForTest(logReload = { info: vi.fn(), warn: vi.fn()
158169channelHealthMonitor: null,
159170}),
160171setState: vi.fn(),
161-startChannel: vi.fn(async () => {}),
162-stopChannel: vi.fn(async () => {}),
172+startChannel: channels?.start ?? vi.fn(async () => {}),
173+stopChannel: channels?.stop ?? vi.fn(async () => {}),
163174stopPostReadySidecars: vi.fn(),
164175reloadPlugins: vi.fn(
165176async (): Promise<GatewayPluginReloadResult> => ({
@@ -889,6 +900,42 @@ describe("gateway channel hot reload handlers", () => {
889900}
890901}
891902903+it("restarts WhatsApp when the planner receives a selfChatMode change", async () => {
904+const whatsappPlugin = {
905+ ...createChannelTestPluginBase({ id: "whatsapp" }),
906+reload: {
907+configPrefixes: ["web", "channels.whatsapp.accounts", "channels.whatsapp.selfChatMode"],
908+noopPrefixes: ["channels.whatsapp"],
909+},
910+};
911+const registry = createTestRegistry([
912+{ pluginId: "whatsapp", plugin: whatsappPlugin, source: "test" },
913+]);
914+const events: string[] = [];
915+const channels = {
916+stop: vi.fn(async (channel: ChannelKind) => {
917+events.push(`stop:${channel}`);
918+}),
919+start: vi.fn(async (channel: ChannelKind) => {
920+events.push(`start:${channel}`);
921+}),
922+};
923+924+pinActivePluginChannelRegistry(registry);
925+try {
926+const plan = buildGatewayReloadPlan(["channels.whatsapp.selfChatMode"]);
927+const { applyHotReload } = createReloadHandlersForTest(undefined, channels);
928+929+expect(plan.restartGateway).toBe(false);
930+expect(plan.restartChannels).toEqual(new Set(["whatsapp"]));
931+await withChannelReloadsEnabled(() => applyHotReload(plan, {}));
932+933+expect(events).toEqual(["stop:whatsapp", "start:whatsapp"]);
934+} finally {
935+releasePinnedPluginChannelRegistry(registry);
936+}
937+});
938+892939it("continues restarting later channels after a hot-reload stop failure", async () => {
893940const events: string[] = [];
894941const setState = vi.fn();
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。