From 938a78f9bf467d1bfdec9e0c14f86b380b7ca9f5 Mon Sep 17 00:00:00 2001 From: Peter Steinberger Date: Mon, 20 Apr 2026 16:19:58 +0100 Subject: [PATCH] test: share whatsapp monitor fixtures --- .../auto-reply/monitor/ack-reaction.test.ts | 81 +++++------ .../monitor/group-activation.test.ts | 126 +++++++++--------- 2 files changed, 97 insertions(+), 110 deletions(-) diff --git a/extensions/whatsapp/src/auto-reply/monitor/ack-reaction.test.ts b/extensions/whatsapp/src/auto-reply/monitor/ack-reaction.test.ts index 1cdf9aee6b9..3520142c720 100644 --- a/extensions/whatsapp/src/auto-reply/monitor/ack-reaction.test.ts +++ b/extensions/whatsapp/src/auto-reply/monitor/ack-reaction.test.ts @@ -47,6 +47,36 @@ function createConfig( } as OpenClawConfig; } +type AckReactionParams = Parameters[0]; + +const runAckReaction = (overrides: Partial = {}) => + maybeSendAckReaction({ + cfg: createConfig("ack"), + msg: createMessage(), + agentId: "agent", + sessionKey: "whatsapp:default:15551234567", + conversationId: "15551234567", + verbose: false, + accountId: "default", + info: vi.fn(), + warn: vi.fn(), + ...overrides, + }); + +const expectAckReactionSent = (accountId: string) => { + expect(hoisted.sendReactionWhatsApp).toHaveBeenCalledWith( + "15551234567@s.whatsapp.net", + "msg-1", + "👀", + { + verbose: false, + fromMe: false, + participant: undefined, + accountId, + }, + ); +}; + describe("maybeSendAckReaction", () => { beforeEach(() => { vi.clearAllMocks(); @@ -55,50 +85,24 @@ describe("maybeSendAckReaction", () => { it.each(["ack", "minimal", "extensive"] as const)( "sends ack reactions when reactionLevel is %s", async (reactionLevel) => { - await maybeSendAckReaction({ + await runAckReaction({ cfg: createConfig(reactionLevel), - msg: createMessage(), - agentId: "agent", - sessionKey: "whatsapp:default:15551234567", - conversationId: "15551234567", - verbose: false, - accountId: "default", - info: vi.fn(), - warn: vi.fn(), }); - expect(hoisted.sendReactionWhatsApp).toHaveBeenCalledWith( - "15551234567@s.whatsapp.net", - "msg-1", - "👀", - { - verbose: false, - fromMe: false, - participant: undefined, - accountId: "default", - }, - ); + expectAckReactionSent("default"); }, ); it("suppresses ack reactions when reactionLevel is off", async () => { - await maybeSendAckReaction({ + await runAckReaction({ cfg: createConfig("off"), - msg: createMessage(), - agentId: "agent", - sessionKey: "whatsapp:default:15551234567", - conversationId: "15551234567", - verbose: false, - accountId: "default", - info: vi.fn(), - warn: vi.fn(), }); expect(hoisted.sendReactionWhatsApp).not.toHaveBeenCalled(); }); it("uses the active account reactionLevel override for ack gating", async () => { - await maybeSendAckReaction({ + await runAckReaction({ cfg: createConfig("off", { accounts: { work: { @@ -109,25 +113,10 @@ describe("maybeSendAckReaction", () => { msg: createMessage({ accountId: "work", }), - agentId: "agent", sessionKey: "whatsapp:work:15551234567", - conversationId: "15551234567", - verbose: false, accountId: "work", - info: vi.fn(), - warn: vi.fn(), }); - expect(hoisted.sendReactionWhatsApp).toHaveBeenCalledWith( - "15551234567@s.whatsapp.net", - "msg-1", - "👀", - { - verbose: false, - fromMe: false, - participant: undefined, - accountId: "work", - }, - ); + expectAckReactionSent("work"); }); }); diff --git a/extensions/whatsapp/src/auto-reply/monitor/group-activation.test.ts b/extensions/whatsapp/src/auto-reply/monitor/group-activation.test.ts index 008b8849946..5e0f424a5af 100644 --- a/extensions/whatsapp/src/auto-reply/monitor/group-activation.test.ts +++ b/extensions/whatsapp/src/auto-reply/monitor/group-activation.test.ts @@ -3,6 +3,54 @@ import { makeSessionStore } from "../../auto-reply.test-harness.js"; import { loadSessionStore } from "../config.runtime.js"; import { resolveGroupActivationFor } from "./group-activation.js"; +const GROUP_CONVERSATION_ID = "123@g.us"; +const LEGACY_GROUP_SESSION_KEY = "agent:main:whatsapp:group:123@g.us"; +const WORK_GROUP_SESSION_KEY = "agent:main:whatsapp:group:123@g.us:thread:whatsapp-account-work"; + +type SessionStoreEntry = { + groupActivation?: unknown; + sessionId?: unknown; + updatedAt?: unknown; +}; + +const resolveWorkGroupActivation = (storePath: string) => + resolveGroupActivationFor({ + cfg: { + channels: { + whatsapp: { + accounts: { + work: {}, + }, + }, + }, + session: { store: storePath }, + } as never, + accountId: "work", + agentId: "main", + sessionKey: WORK_GROUP_SESSION_KEY, + conversationId: GROUP_CONVERSATION_ID, + }); + +const expectWorkGroupActivationEntry = async ( + storePath: string, + assertEntry?: (entry: SessionStoreEntry | undefined) => void, +) => { + await vi.waitFor(() => { + const scopedEntry = loadSessionStore(storePath, { skipCache: true })[WORK_GROUP_SESSION_KEY]; + expect(scopedEntry?.groupActivation).toBe("always"); + assertEntry?.(scopedEntry); + }); +}; + +const expectResolvedWorkGroupActivation = async ( + storePath: string, + assertEntry?: (entry: SessionStoreEntry | undefined) => void, +) => { + const activation = await resolveWorkGroupActivation(storePath); + expect(activation).toBe("always"); + await expectWorkGroupActivationEntry(storePath, assertEntry); +}; + describe("resolveGroupActivationFor", () => { const cleanups: Array<() => Promise> = []; @@ -13,10 +61,8 @@ describe("resolveGroupActivationFor", () => { }); it("reads legacy named-account group activation and backfills the scoped key", async () => { - const sessionKey = "agent:main:whatsapp:group:123@g.us:thread:whatsapp-account-work"; - const legacySessionKey = "agent:main:whatsapp:group:123@g.us"; const { storePath, cleanup } = await makeSessionStore({ - [legacySessionKey]: { + [LEGACY_GROUP_SESSION_KEY]: { groupActivation: "always", sessionId: "legacy-session", updatedAt: 123, @@ -24,75 +70,31 @@ describe("resolveGroupActivationFor", () => { }); cleanups.push(cleanup); - const activation = await resolveGroupActivationFor({ - cfg: { - channels: { - whatsapp: { - accounts: { - work: {}, - }, - }, - }, - session: { store: storePath }, - } as never, - accountId: "work", - agentId: "main", - sessionKey, - conversationId: "123@g.us", - }); - - expect(activation).toBe("always"); - await vi.waitFor(() => { - const scopedEntry = loadSessionStore(storePath, { skipCache: true })[sessionKey]; - expect(scopedEntry?.groupActivation).toBe("always"); + await expectResolvedWorkGroupActivation(storePath, (scopedEntry) => { expect(scopedEntry?.sessionId).toBeUndefined(); expect(scopedEntry?.updatedAt).toBeUndefined(); }); }); it("preserves legacy group activation when the scoped entry already exists without activation", async () => { - const sessionKey = "agent:main:whatsapp:group:123@g.us:thread:whatsapp-account-work"; - const legacySessionKey = "agent:main:whatsapp:group:123@g.us"; const { storePath, cleanup } = await makeSessionStore({ - [legacySessionKey]: { + [LEGACY_GROUP_SESSION_KEY]: { groupActivation: "always", }, - [sessionKey]: { + [WORK_GROUP_SESSION_KEY]: { sessionId: "scoped-session", }, }); cleanups.push(cleanup); - const activation = await resolveGroupActivationFor({ - cfg: { - channels: { - whatsapp: { - accounts: { - work: {}, - }, - }, - }, - session: { store: storePath }, - } as never, - accountId: "work", - agentId: "main", - sessionKey, - conversationId: "123@g.us", - }); - - expect(activation).toBe("always"); - await vi.waitFor(() => { - const scopedEntry = loadSessionStore(storePath, { skipCache: true })[sessionKey]; - expect(scopedEntry?.groupActivation).toBe("always"); + await expectResolvedWorkGroupActivation(storePath, (scopedEntry) => { expect(scopedEntry?.sessionId).toBe("scoped-session"); }); }); it("does not wake the default account from an activation-only legacy group entry in multi-account setups", async () => { - const defaultSessionKey = "agent:main:whatsapp:group:123@g.us"; - const workSessionKey = "agent:main:whatsapp:group:123@g.us:thread:whatsapp-account-work"; const { storePath, cleanup } = await makeSessionStore({ - [defaultSessionKey]: { + [LEGACY_GROUP_SESSION_KEY]: { groupActivation: "always", }, }); @@ -118,8 +120,8 @@ describe("resolveGroupActivationFor", () => { cfg, accountId: "work", agentId: "main", - sessionKey: workSessionKey, - conversationId: "123@g.us", + sessionKey: WORK_GROUP_SESSION_KEY, + conversationId: GROUP_CONVERSATION_ID, }); expect(workActivation).toBe("always"); @@ -128,21 +130,17 @@ describe("resolveGroupActivationFor", () => { cfg, accountId: "default", agentId: "main", - sessionKey: defaultSessionKey, - conversationId: "123@g.us", + sessionKey: LEGACY_GROUP_SESSION_KEY, + conversationId: GROUP_CONVERSATION_ID, }); expect(defaultActivation).toBe("mention"); - await vi.waitFor(() => { - const scopedEntry = loadSessionStore(storePath, { skipCache: true })[workSessionKey]; - expect(scopedEntry?.groupActivation).toBe("always"); - }); + await expectWorkGroupActivationEntry(storePath); }); it("does not treat mixed-case default account keys as named accounts", async () => { - const defaultSessionKey = "agent:main:whatsapp:group:123@g.us"; const { storePath, cleanup } = await makeSessionStore({ - [defaultSessionKey]: { + [LEGACY_GROUP_SESSION_KEY]: { groupActivation: "always", }, }); @@ -166,8 +164,8 @@ describe("resolveGroupActivationFor", () => { } as never, accountId: "default", agentId: "main", - sessionKey: defaultSessionKey, - conversationId: "123@g.us", + sessionKey: LEGACY_GROUP_SESSION_KEY, + conversationId: GROUP_CONVERSATION_ID, }); expect(activation).toBe("always");