Files
openclaw/src/plugins/runtime/runtime-web-channel-plugin.test.ts
T
Marcus Castro dd2083c7ec fix(whastapp): bound connection startup waits (#90486)
* fix: add timeout to waitForWaConnection to prevent indefinite hangs

If Baileys fails to emit a 'connection.update' event with either 'open'
or 'close' status (e.g. due to network issues or internal errors), the
waitForWaConnection promise hangs forever, blocking the entire monitor
loop.

Add a configurable timeout (default 60s) that rejects the promise and
cleans up the event listener if no connection state is received in time.
The timeout is backward-compatible as an optional parameter with a
sensible default.

* test: add coverage for waitForWaConnection timeout path

- Test that promise rejects with descriptive error after timeout
- Test that event listener is cleaned up after timeout
- Test that timer is cleared when connection opens before timeout

* fix: default timeoutMs to 0 to preserve QR login behavior

The 60s default broke the QR login flow in login-qr.ts, which calls
waitForWaConnection without a timeout and expects to wait up to 3 minutes
while the user scans. Change the default to 0 (wait forever, matching
original behavior) and pass the 60s timeout explicitly at the monitor
callsite where it's actually needed.

* fix: bound whatsapp connection startup waits

* fix: align web channel wait contract

* fix: retry whatsapp setup timeouts

* fix: satisfy whatsapp status lint

* fix: preserve whatsapp wait compatibility

---------

Co-authored-by: MMMMSSSS8899 <praelovk@gmail.com>
2026-06-04 21:45:43 -03:00

85 lines
3.2 KiB
TypeScript

// Runtime web-channel plugin tests cover web channel plugin activation and runtime behavior.
import { afterEach, describe, expect, it, vi } from "vitest";
afterEach(() => {
vi.doUnmock("./runtime-plugin-boundary.js");
vi.resetModules();
});
describe("runtime web channel plugin", () => {
it("resolves the default auth dir through the light runtime on each call", async () => {
let authDir = "/tmp/openclaw-default-auth";
const resolveDefaultWebAuthDir = vi.fn(() => authDir);
vi.doMock("./runtime-plugin-boundary.js", () => ({
loadPluginBoundaryModule: () => ({ resolveDefaultWebAuthDir }),
resolvePluginRuntimeModulePath: () => "/tmp/light-runtime-api.js",
resolvePluginRuntimeRecordByEntryBaseNames: () => ({
origin: "bundled",
source: "test",
}),
}));
const { resolveWebChannelAuthDir } = await import("./runtime-web-channel-plugin.js");
expect(resolveWebChannelAuthDir()).toBe("/tmp/openclaw-default-auth");
authDir = "/tmp/openclaw-profile-auth";
expect(resolveWebChannelAuthDir()).toBe("/tmp/openclaw-profile-auth");
expect(resolveDefaultWebAuthDir).toHaveBeenCalledTimes(2);
});
it("falls back to the older WhatsApp light runtime auth dir export", async () => {
vi.doMock("./runtime-plugin-boundary.js", () => ({
loadPluginBoundaryModule: () => ({ WA_WEB_AUTH_DIR: "/tmp/openclaw-legacy-auth" }),
resolvePluginRuntimeModulePath: () => "/tmp/light-runtime-api.js",
resolvePluginRuntimeRecordByEntryBaseNames: () => ({
origin: "external",
source: "test",
}),
}));
const { resolveWebChannelAuthDir } = await import("./runtime-web-channel-plugin.js");
expect(resolveWebChannelAuthDir()).toBe("/tmp/openclaw-legacy-auth");
});
it("rejects non-string legacy auth dir exports", async () => {
vi.doMock("./runtime-plugin-boundary.js", () => ({
loadPluginBoundaryModule: () => ({
WA_WEB_AUTH_DIR: Object("/tmp/openclaw-string-object-auth"),
}),
resolvePluginRuntimeModulePath: () => "/tmp/light-runtime-api.js",
resolvePluginRuntimeRecordByEntryBaseNames: () => ({
origin: "external",
source: "test",
}),
}));
const { resolveWebChannelAuthDir } = await import("./runtime-web-channel-plugin.js");
expect(() => resolveWebChannelAuthDir()).toThrow(
"web channel plugin runtime is missing export 'resolveDefaultWebAuthDir'",
);
});
it("forwards the explicit connection wait policy to the heavy runtime", async () => {
const waitForWaConnection = vi.fn().mockResolvedValue(undefined);
const sock = { id: "socket" };
vi.doMock("./runtime-plugin-boundary.js", () => ({
loadPluginBoundaryModule: () => ({ waitForWaConnection }),
resolvePluginRuntimeModulePath: () => "/tmp/runtime-api.js",
resolvePluginRuntimeRecordByEntryBaseNames: () => ({
origin: "bundled",
source: "test",
}),
}));
const { waitForWebChannelConnection } = await import("./runtime-web-channel-plugin.js");
await waitForWebChannelConnection(sock, { timeoutMs: 12_345 });
expect(waitForWaConnection).toHaveBeenCalledWith(sock, { timeoutMs: 12_345 });
});
});