diff --git a/extensions/whatsapp/src/creds-persistence.test.ts b/extensions/whatsapp/src/creds-persistence.test.ts new file mode 100644 index 00000000000..6db058e2456 --- /dev/null +++ b/extensions/whatsapp/src/creds-persistence.test.ts @@ -0,0 +1,25 @@ +import { MAX_TIMER_TIMEOUT_MS } from "openclaw/plugin-sdk/number-runtime"; +import { afterEach, describe, expect, it, vi } from "vitest"; +import { enqueueCredsSave, waitForCredsSaveQueueWithTimeout } from "./creds-persistence.js"; + +describe("creds-persistence", () => { + afterEach(() => { + vi.useRealTimers(); + vi.restoreAllMocks(); + }); + + it("caps oversized credential flush timeouts before scheduling", async () => { + vi.useFakeTimers(); + const timeoutSpy = vi.spyOn(globalThis, "setTimeout"); + const authDir = "oversized-timeout"; + enqueueCredsSave( + authDir, + () => undefined, + () => undefined, + ); + + await waitForCredsSaveQueueWithTimeout(authDir, Number.MAX_SAFE_INTEGER); + + expect(timeoutSpy).toHaveBeenCalledWith(expect.any(Function), MAX_TIMER_TIMEOUT_MS); + }); +}); diff --git a/extensions/whatsapp/src/creds-persistence.ts b/extensions/whatsapp/src/creds-persistence.ts index 204af357677..9ad82529022 100644 --- a/extensions/whatsapp/src/creds-persistence.ts +++ b/extensions/whatsapp/src/creds-persistence.ts @@ -1,3 +1,4 @@ +import { resolveTimerTimeoutMs } from "openclaw/plugin-sdk/number-runtime"; import { replaceFileAtomic } from "openclaw/plugin-sdk/security-runtime"; import { assertWebCredsPathRegularFileOrMissing, resolveWebCredsPath } from "./creds-files.js"; @@ -71,11 +72,12 @@ export async function waitForCredsSaveQueueWithTimeout( authDir: string, timeoutMs = CREDS_SAVE_FLUSH_TIMEOUT_MS, ): Promise { + const boundedTimeoutMs = resolveTimerTimeoutMs(timeoutMs, CREDS_SAVE_FLUSH_TIMEOUT_MS, 0); let flushTimeout: ReturnType | undefined; return await Promise.race([ waitForCredsSaveQueue(authDir).then(() => "drained" as const), new Promise((resolve) => { - flushTimeout = setTimeout(() => resolve("timed_out"), timeoutMs); + flushTimeout = setTimeout(() => resolve("timed_out"), boundedTimeoutMs); }), ]).finally(() => { if (flushTimeout) {