






















@@ -198,6 +198,57 @@ function getCapturedReplyOptions() {
198198)?.replyOptions;
199199}
200200201+function requireRecord(value: unknown, label: string): Record<string, unknown> {
202+expect(typeof value).toBe("object");
203+expect(value).not.toBeNull();
204+if (typeof value !== "object" || value === null) {
205+throw new Error(`${label} was not an object`);
206+}
207+return value as Record<string, unknown>;
208+}
209+210+function expectRecordFields(record: Record<string, unknown>, fields: Record<string, unknown>) {
211+for (const [key, value] of Object.entries(fields)) {
212+expect(record[key]).toEqual(value);
213+}
214+}
215+216+function requireMockArg(
217+mock: { mock: { calls: unknown[][] } },
218+callIndex: number,
219+argIndex: number,
220+label: string,
221+) {
222+return requireRecord(mock.mock.calls[callIndex]?.[argIndex], label);
223+}
224+225+function requireLastMockArg(
226+mock: { mock: { calls: unknown[][] } },
227+argIndex: number,
228+label: string,
229+) {
230+const callIndex = mock.mock.calls.length - 1;
231+return requireMockArg(mock, callIndex, argIndex, label);
232+}
233+234+function expectReplyResultFields(
235+deliverReply: { mock: { calls: unknown[][] } },
236+fields: Record<string, unknown>,
237+) {
238+const params = requireLastMockArg(deliverReply, 0, "deliver reply params");
239+expectRecordFields(requireRecord(params.replyResult, "reply result"), fields);
240+}
241+242+function expectRememberSentContextFields(
243+rememberSentText: { mock: { calls: unknown[][] } },
244+text: unknown,
245+fields: Record<string, unknown>,
246+) {
247+const call = rememberSentText.mock.calls.at(-1);
248+expect(call?.[0]).toBe(text);
249+expectRecordFields(requireRecord(call?.[1], "remember sent context"), fields);
250+}
251+201252type BufferedReplyParams = Parameters<typeof dispatchWhatsAppBufferedReply>[0];
202253203254function makeReplyLogger(): BufferedReplyParams["replyLogger"] {
@@ -288,7 +339,7 @@ describe("whatsapp inbound dispatch", () => {
288339},
289340});
290341291-expect(ctx).toMatchObject({
342+expectRecordFields(requireRecord(ctx, "inbound context"), {
292343Body: "Alice: hi",
293344BodyForAgent: "hi",
294345BodyForCommands: "hi",
@@ -321,7 +372,7 @@ describe("whatsapp inbound dispatch", () => {
321372transcript: "spoken transcript",
322373});
323374324-expect(ctx).toMatchObject({
375+expectRecordFields(requireRecord(ctx, "voice inbound context"), {
325376Body: "spoken transcript",
326377BodyForAgent: "spoken transcript",
327378BodyForCommands: "<media:audio>",
@@ -520,14 +571,10 @@ describe("whatsapp inbound dispatch", () => {
520571);
521572expect(deliverReply).toHaveBeenCalledTimes(1);
522573expect(rememberSentText).toHaveBeenCalledTimes(1);
523-expect(deliverReply).toHaveBeenLastCalledWith(
524-expect.objectContaining({
525-replyResult: expect.objectContaining({
526-mediaUrls: ["/tmp/generated.jpg"],
527-text: "generated image",
528-}),
529-}),
530-);
574+expectReplyResultFields(deliverReply, {
575+mediaUrls: ["/tmp/generated.jpg"],
576+text: "generated image",
577+});
531578532579await deliver?.({ text: "block payload" }, { kind: "block" });
533580await deliver?.({ text: "final payload" }, { kind: "final" });
@@ -560,27 +607,30 @@ describe("whatsapp inbound dispatch", () => {
560607const deliver = getCapturedDeliver();
561608await deliver?.({ text: "final payload" }, { kind: "final" });
562609563-expect(deliverInboundReplyWithMessageSendContextMock).toHaveBeenCalledWith(
564-expect.objectContaining({
565-channel: "whatsapp",
566-accountId: "default",
567-agentId: "main",
568-to: "+1000",
569-payload: expect.objectContaining({ text: "final payload" }),
570-info: { kind: "final" },
571-ctxPayload: expect.objectContaining({
572-SessionKey: "agent:main:whatsapp:+15551234567",
573-}),
574-}),
610+const durableParams = requireMockArg(
611+deliverInboundReplyWithMessageSendContextMock,
612+0,
613+0,
614+"durable delivery params",
575615);
616+expectRecordFields(durableParams, {
617+channel: "whatsapp",
618+accountId: "default",
619+agentId: "main",
620+to: "+1000",
621+info: { kind: "final" },
622+});
623+expectRecordFields(requireRecord(durableParams.payload, "durable payload"), {
624+text: "final payload",
625+});
626+expectRecordFields(requireRecord(durableParams.ctxPayload, "durable context"), {
627+SessionKey: "agent:main:whatsapp:+15551234567",
628+});
576629expect(deliverReply).not.toHaveBeenCalled();
577-expect(rememberSentText).toHaveBeenCalledWith(
578-"final payload",
579-expect.objectContaining({
580-combinedBody: "incoming",
581-combinedBodySessionKey: "agent:main:whatsapp:+15551234567",
582-}),
583-);
630+expectRememberSentContextFields(rememberSentText, "final payload", {
631+combinedBody: "incoming",
632+combinedBodySessionKey: "agent:main:whatsapp:+15551234567",
633+});
584634});
585635586636it("does not fall back when durable WhatsApp delivery suppresses a send", async () => {
@@ -603,13 +653,19 @@ describe("whatsapp inbound dispatch", () => {
603653const deliver = getCapturedDeliver();
604654await deliver?.({ text: "cancelled by hook" }, { kind: "final" });
605655606-expect(deliverInboundReplyWithMessageSendContextMock).toHaveBeenCalledWith(
607-expect.objectContaining({
608-channel: "whatsapp",
609-payload: expect.objectContaining({ text: "cancelled by hook" }),
610-info: { kind: "final" },
611-}),
656+const durableParams = requireMockArg(
657+deliverInboundReplyWithMessageSendContextMock,
658+0,
659+0,
660+"suppressed durable delivery params",
612661);
662+expectRecordFields(durableParams, {
663+channel: "whatsapp",
664+info: { kind: "final" },
665+});
666+expectRecordFields(requireRecord(durableParams.payload, "suppressed payload"), {
667+text: "cancelled by hook",
668+});
613669expect(deliverReply).not.toHaveBeenCalled();
614670expect(rememberSentText).not.toHaveBeenCalled();
615671});
@@ -637,21 +693,14 @@ describe("whatsapp inbound dispatch", () => {
637693);
638694639695expect(deliverInboundReplyWithMessageSendContextMock).not.toHaveBeenCalled();
640-expect(deliverReply).toHaveBeenCalledWith(
641-expect.objectContaining({
642-replyResult: expect.objectContaining({
643-mediaUrls: ["/tmp/generated.jpg"],
644-text: "generated image",
645-}),
646-}),
647-);
648-expect(rememberSentText).toHaveBeenCalledWith(
649-"generated image",
650-expect.objectContaining({
651-combinedBody: "hi",
652-combinedBodySessionKey: "agent:main:whatsapp:direct:+1000",
653-}),
654-);
696+expectReplyResultFields(deliverReply, {
697+mediaUrls: ["/tmp/generated.jpg"],
698+text: "generated image",
699+});
700+expectRememberSentContextFields(rememberSentText, "generated image", {
701+combinedBody: "hi",
702+combinedBodySessionKey: "agent:main:whatsapp:direct:+1000",
703+});
655704});
656705657706it("normalizes WhatsApp payload text before delivery and echo bookkeeping", async () => {
@@ -673,18 +722,11 @@ describe("whatsapp inbound dispatch", () => {
673722{ kind: "final" },
674723);
675724676-expect(deliverReply).toHaveBeenCalledWith(
677-expect.objectContaining({
678-replyResult: expect.objectContaining({ text: "Before\n\nAfter" }),
679-}),
680-);
681-expect(rememberSentText).toHaveBeenCalledWith(
682-"Before\n\nAfter",
683-expect.objectContaining({
684-combinedBody: "hi",
685-combinedBodySessionKey: "agent:main:whatsapp:direct:+1000",
686-}),
687-);
725+expectReplyResultFields(deliverReply, { text: "Before\n\nAfter" });
726+expectRememberSentContextFields(rememberSentText, "Before\n\nAfter", {
727+combinedBody: "hi",
728+combinedBodySessionKey: "agent:main:whatsapp:direct:+1000",
729+});
688730});
689731690732it("suppresses reasoning and compaction payloads before WhatsApp delivery", async () => {
@@ -774,9 +816,7 @@ describe("whatsapp inbound dispatch", () => {
774816msg: makeMsg({ from: "+15550001000", chatType: "direct" }),
775817});
776818777-expect(getCapturedReplyOptions()).toMatchObject({
778-disableBlockStreaming: false,
779-});
819+expect(getCapturedReplyOptions()?.disableBlockStreaming).toBe(false);
780820expect(getCapturedReplyOptions()?.sourceReplyDeliveryMode).toBeUndefined();
781821});
782822@@ -786,7 +826,7 @@ describe("whatsapp inbound dispatch", () => {
786826msg: makeMsg({ from: "120363000000000000@g.us", chatType: "group" }),
787827});
788828789-expect(getCapturedReplyOptions()).toMatchObject({
829+expectRecordFields(requireRecord(getCapturedReplyOptions(), "reply options"), {
790830sourceReplyDeliveryMode: "message_tool_only",
791831disableBlockStreaming: true,
792832});
@@ -802,7 +842,7 @@ describe("whatsapp inbound dispatch", () => {
802842msg: makeMsg({ from: "120363000000000000@g.us", chatType: "group" }),
803843});
804844805-expect(getCapturedReplyOptions()).toMatchObject({
845+expectRecordFields(requireRecord(getCapturedReplyOptions(), "reply options"), {
806846sourceReplyDeliveryMode: "automatic",
807847disableBlockStreaming: false,
808848});
@@ -873,13 +913,13 @@ describe("whatsapp inbound dispatch", () => {
873913874914expect(deliverReply).toHaveBeenCalledTimes(1);
875915expect(rememberSentText).not.toHaveBeenCalled();
876-expect(replyLogger.warn).toHaveBeenCalledWith(
877- expect.objectContaining({
878- replyKind: "final",
879- conversationId: "+1000",
880-}),
881- "auto-reply was not accepted by WhatsApp provider",
882-);
916+const warnMock = replyLogger.warn as unknown as { mock: { calls: unknown[][] } };
917+const warningContext = requireMockArg(warnMock, 0, 0, "warning context");
918+expectRecordFields(warningContext, {
919+replyKind: "final",
920+conversationId: "+1000",
921+});
922+expect(warnMock.mock.calls[0]?.[1]).toBe("auto-reply was not accepted by WhatsApp provider");
883923});
884924885925it("returns true for tool-only media turns after delivering media", async () => {
@@ -930,15 +970,11 @@ describe("whatsapp inbound dispatch", () => {
930970).resolves.toBe(true);
931971932972expect(deliverReply).toHaveBeenCalledTimes(1);
933-expect(deliverReply).toHaveBeenCalledWith(
934-expect.objectContaining({
935-replyResult: expect.objectContaining({
936-mediaUrls: ["/tmp/generated.jpg"],
937-text: undefined,
938-}),
939-}),
940-);
941-expect(rememberSentText).toHaveBeenCalledWith(undefined, expect.any(Object));
973+expectReplyResultFields(deliverReply, {
974+mediaUrls: ["/tmp/generated.jpg"],
975+text: undefined,
976+});
977+expectRememberSentContextFields(rememberSentText, undefined, {});
942978});
943979944980it("passes sendComposing through as the reply typing callback", async () => {
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。