mirror of https://github.com/openclaw/openclaw.git
WhatsApp: stabilize inbound monitor and setup tests (#50007)
This commit is contained in:
parent
91d37ccfc3
commit
859889aae9
|
|
@ -151,6 +151,7 @@ Docs: https://docs.openclaw.ai
|
|||
- xAI/web search: add missing Grok credential metadata so the bundled provider registration type-checks again. (#49472) thanks @scoootscooob.
|
||||
- Signal/runtime API: re-export `SignalAccountConfig` so Signal account resolution type-checks again. (#49470) Thanks @scoootscooob.
|
||||
- Google Chat/runtime API: thin the private runtime barrel onto the curated public SDK surface while keeping public Google Chat exports intact. (#49504) Thanks @scoootscooob.
|
||||
- WhatsApp: stabilize inbound monitor and setup tests (#50007) Thanks @joshavant.
|
||||
|
||||
### Breaking
|
||||
|
||||
|
|
|
|||
|
|
@ -41,7 +41,25 @@ vi.mock("openclaw/plugin-sdk/config-runtime", async (importOriginal) => {
|
|||
};
|
||||
});
|
||||
|
||||
vi.mock("openclaw/plugin-sdk/conversation-runtime", () => ({
|
||||
readChannelAllowFromStore: (...args: unknown[]) => readAllowFromStoreMock(...args),
|
||||
upsertChannelPairingRequest: (...args: unknown[]) => upsertPairingRequestMock(...args),
|
||||
}));
|
||||
vi.mock("openclaw/plugin-sdk/conversation-runtime", async (importOriginal) => {
|
||||
const actual = await importOriginal<typeof import("openclaw/plugin-sdk/conversation-runtime")>();
|
||||
return {
|
||||
...actual,
|
||||
upsertChannelPairingRequest: (...args: unknown[]) => upsertPairingRequestMock(...args),
|
||||
};
|
||||
});
|
||||
|
||||
vi.mock("openclaw/plugin-sdk/security-runtime", async (importOriginal) => {
|
||||
const actual = await importOriginal<typeof import("openclaw/plugin-sdk/security-runtime")>();
|
||||
return {
|
||||
...actual,
|
||||
readStoreAllowFromForDmPolicy: async (
|
||||
params: Parameters<typeof actual.readStoreAllowFromForDmPolicy>[0],
|
||||
) =>
|
||||
await actual.readStoreAllowFromForDmPolicy({
|
||||
...params,
|
||||
readStore: async (provider, accountId) =>
|
||||
(await readAllowFromStoreMock(provider, accountId)) as string[],
|
||||
}),
|
||||
};
|
||||
});
|
||||
|
|
|
|||
|
|
@ -38,6 +38,19 @@ async function openInboxMonitor(onMessage = vi.fn()) {
|
|||
return { onMessage, listener, sock: getSock() };
|
||||
}
|
||||
|
||||
async function settleInboundWork() {
|
||||
await new Promise((resolve) => setTimeout(resolve, 25));
|
||||
}
|
||||
|
||||
async function waitForMessageCalls(onMessage: ReturnType<typeof vi.fn>, count: number) {
|
||||
await vi.waitFor(
|
||||
() => {
|
||||
expect(onMessage).toHaveBeenCalledTimes(count);
|
||||
},
|
||||
{ timeout: 2_000, interval: 5 },
|
||||
);
|
||||
}
|
||||
|
||||
async function expectOutboundDmSkipsPairing(params: {
|
||||
selfChatMode: boolean;
|
||||
messageId: string;
|
||||
|
|
@ -77,7 +90,7 @@ async function expectOutboundDmSkipsPairing(params: {
|
|||
},
|
||||
],
|
||||
});
|
||||
await new Promise((resolve) => setImmediate(resolve));
|
||||
await settleInboundWork();
|
||||
|
||||
expect(onMessage).not.toHaveBeenCalled();
|
||||
expect(upsertPairingRequestMock).not.toHaveBeenCalled();
|
||||
|
|
@ -111,7 +124,7 @@ describe("web monitor inbox", () => {
|
|||
};
|
||||
|
||||
sock.ev.emit("messages.upsert", upsert);
|
||||
await new Promise((resolve) => setImmediate(resolve));
|
||||
await waitForMessageCalls(onMessage, 1);
|
||||
|
||||
// Should call onMessage for authorized senders
|
||||
expect(onMessage).toHaveBeenCalledWith(
|
||||
|
|
@ -145,7 +158,7 @@ describe("web monitor inbox", () => {
|
|||
};
|
||||
|
||||
sock.ev.emit("messages.upsert", upsert);
|
||||
await new Promise((resolve) => setImmediate(resolve));
|
||||
await waitForMessageCalls(onMessage, 1);
|
||||
|
||||
// Should allow self-messages even if not in allowFrom
|
||||
expect(onMessage).toHaveBeenCalledWith(
|
||||
|
|
@ -181,7 +194,12 @@ describe("web monitor inbox", () => {
|
|||
};
|
||||
|
||||
sock.ev.emit("messages.upsert", upsertBlocked);
|
||||
await new Promise((resolve) => setImmediate(resolve));
|
||||
await vi.waitFor(
|
||||
() => {
|
||||
expect(sock.sendMessage).toHaveBeenCalledTimes(1);
|
||||
},
|
||||
{ timeout: 2_000, interval: 5 },
|
||||
);
|
||||
expect(onMessage).not.toHaveBeenCalled();
|
||||
expectPairingPromptSent(sock, "999@s.whatsapp.net", "+999");
|
||||
|
||||
|
|
@ -201,7 +219,7 @@ describe("web monitor inbox", () => {
|
|||
};
|
||||
|
||||
sock.ev.emit("messages.upsert", upsertBlockedAgain);
|
||||
await new Promise((resolve) => setImmediate(resolve));
|
||||
await settleInboundWork();
|
||||
expect(onMessage).not.toHaveBeenCalled();
|
||||
expect(sock.sendMessage).toHaveBeenCalledTimes(1);
|
||||
|
||||
|
|
@ -222,7 +240,7 @@ describe("web monitor inbox", () => {
|
|||
};
|
||||
|
||||
sock.ev.emit("messages.upsert", upsertSelf);
|
||||
await new Promise((resolve) => setImmediate(resolve));
|
||||
await waitForMessageCalls(onMessage, 1);
|
||||
|
||||
expect(onMessage).toHaveBeenCalledTimes(1);
|
||||
expect(onMessage).toHaveBeenCalledWith(
|
||||
|
|
@ -273,17 +291,19 @@ describe("web monitor inbox", () => {
|
|||
};
|
||||
|
||||
sock.ev.emit("messages.upsert", upsert);
|
||||
await new Promise((resolve) => setImmediate(resolve));
|
||||
|
||||
// Verify it WAS marked as read
|
||||
expect(sock.readMessages).toHaveBeenCalledWith([
|
||||
{
|
||||
remoteJid: "999@s.whatsapp.net",
|
||||
id: "history1",
|
||||
participant: undefined,
|
||||
fromMe: false,
|
||||
await vi.waitFor(
|
||||
() => {
|
||||
expect(sock.readMessages).toHaveBeenCalledWith([
|
||||
{
|
||||
remoteJid: "999@s.whatsapp.net",
|
||||
id: "history1",
|
||||
participant: undefined,
|
||||
fromMe: false,
|
||||
},
|
||||
]);
|
||||
},
|
||||
]);
|
||||
{ timeout: 2_000, interval: 5 },
|
||||
);
|
||||
|
||||
// Verify it WAS NOT passed to onMessage
|
||||
expect(onMessage).not.toHaveBeenCalled();
|
||||
|
|
|
|||
|
|
@ -12,8 +12,17 @@ describe("append upsert handling (#20952)", () => {
|
|||
installWebMonitorInboxUnitTestHooks();
|
||||
type InboxOnMessage = NonNullable<Parameters<typeof monitorWebInbox>[0]["onMessage"]>;
|
||||
|
||||
async function tick() {
|
||||
await new Promise((resolve) => setImmediate(resolve));
|
||||
async function settleInboundWork() {
|
||||
await new Promise((resolve) => setTimeout(resolve, 25));
|
||||
}
|
||||
|
||||
async function waitForMessageCalls(onMessage: ReturnType<typeof vi.fn>, count: number) {
|
||||
await vi.waitFor(
|
||||
() => {
|
||||
expect(onMessage).toHaveBeenCalledTimes(count);
|
||||
},
|
||||
{ timeout: 2_000, interval: 5 },
|
||||
);
|
||||
}
|
||||
|
||||
async function startInboxMonitor(onMessage: InboxOnMessage) {
|
||||
|
|
@ -43,7 +52,7 @@ describe("append upsert handling (#20952)", () => {
|
|||
},
|
||||
],
|
||||
});
|
||||
await tick();
|
||||
await waitForMessageCalls(onMessage, 1);
|
||||
|
||||
expect(onMessage).toHaveBeenCalledTimes(1);
|
||||
|
||||
|
|
@ -67,7 +76,7 @@ describe("append upsert handling (#20952)", () => {
|
|||
},
|
||||
],
|
||||
});
|
||||
await tick();
|
||||
await settleInboundWork();
|
||||
|
||||
expect(onMessage).not.toHaveBeenCalled();
|
||||
|
||||
|
|
@ -90,7 +99,7 @@ describe("append upsert handling (#20952)", () => {
|
|||
},
|
||||
],
|
||||
});
|
||||
await tick();
|
||||
await settleInboundWork();
|
||||
|
||||
expect(onMessage).not.toHaveBeenCalled();
|
||||
|
||||
|
|
@ -116,7 +125,7 @@ describe("append upsert handling (#20952)", () => {
|
|||
},
|
||||
],
|
||||
});
|
||||
await tick();
|
||||
await waitForMessageCalls(onMessage, 1);
|
||||
|
||||
expect(onMessage).toHaveBeenCalledTimes(1);
|
||||
|
||||
|
|
@ -140,7 +149,7 @@ describe("append upsert handling (#20952)", () => {
|
|||
},
|
||||
],
|
||||
});
|
||||
await tick();
|
||||
await waitForMessageCalls(onMessage, 1);
|
||||
|
||||
expect(onMessage).toHaveBeenCalledTimes(1);
|
||||
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@ const TIMESTAMP_OFF_MESSAGES_CFG = {
|
|||
} as const;
|
||||
|
||||
async function flushInboundQueue() {
|
||||
await new Promise((resolve) => setImmediate(resolve));
|
||||
await new Promise((resolve) => setTimeout(resolve, 25));
|
||||
}
|
||||
|
||||
const createNotifyUpsert = (message: Record<string, unknown>) => ({
|
||||
|
|
|
|||
|
|
@ -31,7 +31,7 @@ describe("web monitor inbox", () => {
|
|||
const listener = await openMonitor(onMessage);
|
||||
const sock = getSock();
|
||||
sock.ev.emit("messages.upsert", upsert);
|
||||
await new Promise((resolve) => setImmediate(resolve));
|
||||
await new Promise((resolve) => setTimeout(resolve, 25));
|
||||
return { onMessage, listener, sock };
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -14,8 +14,13 @@ describe("web monitor inbox", () => {
|
|||
installWebMonitorInboxUnitTestHooks();
|
||||
type InboxOnMessage = NonNullable<Parameters<typeof monitorWebInbox>[0]["onMessage"]>;
|
||||
|
||||
async function tick() {
|
||||
await new Promise((resolve) => setImmediate(resolve));
|
||||
async function waitForMessageCalls(onMessage: ReturnType<typeof vi.fn>, count: number) {
|
||||
await vi.waitFor(
|
||||
() => {
|
||||
expect(onMessage).toHaveBeenCalledTimes(count);
|
||||
},
|
||||
{ timeout: 2_000, interval: 5 },
|
||||
);
|
||||
}
|
||||
|
||||
async function startInboxMonitor(onMessage: InboxOnMessage) {
|
||||
|
|
@ -82,7 +87,7 @@ describe("web monitor inbox", () => {
|
|||
};
|
||||
|
||||
sock.ev.emit("messages.upsert", upsert);
|
||||
await tick();
|
||||
await waitForMessageCalls(onMessage, 1);
|
||||
|
||||
expect(onMessage).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
|
|
@ -115,7 +120,7 @@ describe("web monitor inbox", () => {
|
|||
});
|
||||
|
||||
sock.ev.emit("messages.upsert", upsert);
|
||||
await tick();
|
||||
await waitForMessageCalls(onMessage, 1);
|
||||
|
||||
expect(onMessage).toHaveBeenCalledWith(
|
||||
expect.objectContaining({ body: "ping", from: "+999", to: "+123" }),
|
||||
|
|
@ -153,7 +158,7 @@ describe("web monitor inbox", () => {
|
|||
|
||||
sock.ev.emit("messages.upsert", upsert);
|
||||
sock.ev.emit("messages.upsert", upsert);
|
||||
await tick();
|
||||
await waitForMessageCalls(onMessage, 1);
|
||||
|
||||
expect(onMessage).toHaveBeenCalledTimes(1);
|
||||
|
||||
|
|
@ -177,7 +182,7 @@ describe("web monitor inbox", () => {
|
|||
});
|
||||
|
||||
sock.ev.emit("messages.upsert", upsert);
|
||||
await tick();
|
||||
await waitForMessageCalls(onMessage, 1);
|
||||
|
||||
expect(getPNForLID).toHaveBeenCalledWith("999@lid");
|
||||
expect(onMessage).toHaveBeenCalledWith(
|
||||
|
|
@ -207,7 +212,7 @@ describe("web monitor inbox", () => {
|
|||
});
|
||||
|
||||
sock.ev.emit("messages.upsert", upsert);
|
||||
await tick();
|
||||
await waitForMessageCalls(onMessage, 1);
|
||||
|
||||
expect(onMessage).toHaveBeenCalledWith(
|
||||
expect.objectContaining({ body: "ping", from: "+1555", to: "+123" }),
|
||||
|
|
@ -234,7 +239,7 @@ describe("web monitor inbox", () => {
|
|||
});
|
||||
|
||||
sock.ev.emit("messages.upsert", upsert);
|
||||
await tick();
|
||||
await waitForMessageCalls(onMessage, 1);
|
||||
|
||||
expect(getPNForLID).toHaveBeenCalledWith("444@lid");
|
||||
expect(onMessage).toHaveBeenCalledWith(
|
||||
|
|
@ -277,7 +282,7 @@ describe("web monitor inbox", () => {
|
|||
};
|
||||
|
||||
sock.ev.emit("messages.upsert", upsert);
|
||||
await tick();
|
||||
await waitForMessageCalls(onMessage, 2);
|
||||
|
||||
expect(onMessage).toHaveBeenCalledTimes(2);
|
||||
|
||||
|
|
|
|||
|
|
@ -70,15 +70,6 @@ function createMockSock(): MockSock {
|
|||
};
|
||||
}
|
||||
|
||||
function getPairingStoreMocks() {
|
||||
const readChannelAllowFromStore = (...args: unknown[]) => readAllowFromStoreMock(...args);
|
||||
const upsertChannelPairingRequest = (...args: unknown[]) => upsertPairingRequestMock(...args);
|
||||
return {
|
||||
readChannelAllowFromStore,
|
||||
upsertChannelPairingRequest,
|
||||
};
|
||||
}
|
||||
|
||||
const sock: MockSock = createMockSock();
|
||||
|
||||
vi.mock("openclaw/plugin-sdk/media-runtime", async (importOriginal) => {
|
||||
|
|
@ -102,7 +93,28 @@ vi.mock("openclaw/plugin-sdk/config-runtime", async (importOriginal) => {
|
|||
};
|
||||
});
|
||||
|
||||
vi.mock("openclaw/plugin-sdk/conversation-runtime", () => getPairingStoreMocks());
|
||||
vi.mock("openclaw/plugin-sdk/conversation-runtime", async (importOriginal) => {
|
||||
const actual = await importOriginal<typeof import("openclaw/plugin-sdk/conversation-runtime")>();
|
||||
return {
|
||||
...actual,
|
||||
upsertChannelPairingRequest: (...args: unknown[]) => upsertPairingRequestMock(...args),
|
||||
};
|
||||
});
|
||||
|
||||
vi.mock("openclaw/plugin-sdk/security-runtime", async (importOriginal) => {
|
||||
const actual = await importOriginal<typeof import("openclaw/plugin-sdk/security-runtime")>();
|
||||
return {
|
||||
...actual,
|
||||
readStoreAllowFromForDmPolicy: async (
|
||||
params: Parameters<typeof actual.readStoreAllowFromForDmPolicy>[0],
|
||||
) =>
|
||||
await actual.readStoreAllowFromForDmPolicy({
|
||||
...params,
|
||||
readStore: async (provider, accountId) =>
|
||||
(await readAllowFromStoreMock(provider, accountId)) as string[],
|
||||
}),
|
||||
};
|
||||
});
|
||||
|
||||
vi.mock("./session.js", () => ({
|
||||
createWaSocket: vi.fn().mockResolvedValue(sock),
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@ const resolveWhatsAppAuthDirMock = vi.hoisted(() =>
|
|||
})),
|
||||
);
|
||||
|
||||
vi.mock("../../../src/channel-web.js", () => ({
|
||||
vi.mock("./login.js", () => ({
|
||||
loginWeb: loginWebMock,
|
||||
}));
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue