
















@@ -4,6 +4,8 @@ import type { GatewayPluginReloadResult } from "./server-reload-handlers.js";
44import { createGatewayReloadHandlers } from "./server-reload-handlers.js";
5566const hoisted = vi.hoisted(() => ({
7+startGmailWatcherWithLogs: vi.fn(async () => {}),
8+stopGmailWatcher: vi.fn(async () => {}),
79activeTaskCount: { value: 0 },
810activeTaskBlockers: [] as Array<{
911taskId: string;
@@ -15,6 +17,14 @@ const hoisted = vi.hoisted(() => ({
1517}>,
1618}));
171920+vi.mock("../hooks/gmail-watcher.js", () => ({
21+stopGmailWatcher: hoisted.stopGmailWatcher,
22+}));
23+24+vi.mock("../hooks/gmail-watcher-lifecycle.js", () => ({
25+startGmailWatcherWithLogs: hoisted.startGmailWatcherWithLogs,
26+}));
27+1828vi.mock("../tasks/task-registry.maintenance.js", async () => {
1929const actual = await vi.importActual<typeof import("../tasks/task-registry.maintenance.js")>(
2030"../tasks/task-registry.maintenance.js",
@@ -65,6 +75,7 @@ function createReloadHandlersForTest(logReload = { info: vi.fn(), warn: vi.fn()
6575setState: vi.fn(),
6676startChannel: vi.fn(async () => {}),
6777stopChannel: vi.fn(async () => {}),
78+stopPostReadySidecars: vi.fn(),
6879reloadPlugins: vi.fn(
6980async (): Promise<GatewayPluginReloadResult> => ({
7081restartChannels: new Set(),
@@ -80,6 +91,8 @@ function createReloadHandlersForTest(logReload = { info: vi.fn(), warn: vi.fn()
8091}
81928293afterEach(() => {
94+hoisted.startGmailWatcherWithLogs.mockClear();
95+hoisted.stopGmailWatcher.mockClear();
8396hoisted.activeTaskCount.value = 0;
8497hoisted.activeTaskBlockers.length = 0;
8598});
@@ -158,6 +171,69 @@ describe("gateway restart deferral preflight", () => {
158171});
159172});
160173174+describe("gateway Gmail hot reload handlers", () => {
175+it("stops queued post-ready sidecars before restarting Gmail watcher", async () => {
176+const stopPostReadySidecars = vi.fn();
177+const { applyHotReload } = createGatewayReloadHandlers({
178+deps: {} as never,
179+broadcast: vi.fn(),
180+getState: () => ({
181+hooksConfig: {} as never,
182+hookClientIpConfig: {} as never,
183+heartbeatRunner: { stop: vi.fn(), updateConfig: vi.fn() } as never,
184+cronState: {
185+cron: { start: vi.fn(async () => {}), stop: vi.fn() },
186+storePath: "/tmp/cron.json",
187+cronEnabled: false,
188+} as never,
189+channelHealthMonitor: null,
190+}),
191+setState: vi.fn(),
192+startChannel: vi.fn(async () => {}),
193+stopChannel: vi.fn(async () => {}),
194+ stopPostReadySidecars,
195+reloadPlugins: vi.fn(
196+async (): Promise<GatewayPluginReloadResult> => ({
197+restartChannels: new Set(),
198+activeChannels: new Set(),
199+}),
200+),
201+logHooks: { info: vi.fn(), warn: vi.fn(), error: vi.fn() },
202+logChannels: { info: vi.fn(), error: vi.fn() },
203+logCron: { error: vi.fn() },
204+logReload: { info: vi.fn(), warn: vi.fn() },
205+createHealthMonitor: () => null,
206+});
207+const nextConfig = {
208+hooks: { enabled: true, gmail: { account: "next@example.com" } },
209+} as never;
210+211+await applyHotReload(
212+{
213+changedPaths: ["hooks.gmail.account"],
214+restartGateway: false,
215+restartReasons: [],
216+hotReasons: ["hooks.gmail.account"],
217+reloadHooks: false,
218+restartGmailWatcher: true,
219+restartCron: false,
220+restartHeartbeat: false,
221+restartHealthMonitor: false,
222+reloadPlugins: false,
223+restartChannels: new Set(),
224+disposeMcpRuntimes: false,
225+noopPaths: [],
226+},
227+nextConfig,
228+);
229+230+expect(stopPostReadySidecars).toHaveBeenCalledBefore(hoisted.stopGmailWatcher);
231+expect(hoisted.startGmailWatcherWithLogs).toHaveBeenCalledWith(
232+expect.objectContaining({ cfg: nextConfig }),
233+);
234+});
235+});
236+161237describe("gateway plugin hot reload handlers", () => {
162238it("stops removed channel plugins from broad activation before swapping plugin runtime", async () => {
163239const previousSkipChannels = process.env.OPENCLAW_SKIP_CHANNELS;
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。