From 39053bddd7f51212cf749d9019ed49102b09dc91 Mon Sep 17 00:00:00 2001 From: Peter Steinberger Date: Fri, 20 Mar 2026 19:02:07 +0000 Subject: [PATCH] test: decouple zalo outbound payload contract from channel runtime --- .../outbound-payload.contract.test.ts | 146 ++++++++++++------ 1 file changed, 103 insertions(+), 43 deletions(-) diff --git a/src/channels/plugins/contracts/outbound-payload.contract.test.ts b/src/channels/plugins/contracts/outbound-payload.contract.test.ts index 761d1274091..5488b918510 100644 --- a/src/channels/plugins/contracts/outbound-payload.contract.test.ts +++ b/src/channels/plugins/contracts/outbound-payload.contract.test.ts @@ -1,11 +1,14 @@ import { describe, vi } from "vitest"; import { discordOutbound } from "../../../../extensions/discord/src/outbound-adapter.js"; import { whatsappOutbound } from "../../../../extensions/whatsapp/src/outbound-adapter.js"; -import { zaloPlugin } from "../../../../extensions/zalo/src/channel.js"; import { sendMessageZalo } from "../../../../extensions/zalo/src/send.js"; -import { zalouserPlugin } from "../../../../extensions/zalouser/src/channel.js"; -import { setZalouserRuntime } from "../../../../extensions/zalouser/src/runtime.js"; import { sendMessageZalouser } from "../../../../extensions/zalouser/src/send.js"; +import { parseZalouserOutboundTarget } from "../../../../extensions/zalouser/src/session-route.js"; +import { + chunkTextForOutbound as chunkZaloTextForOutbound, + sendPayloadWithChunkedTextAndMedia as sendZaloPayloadWithChunkedTextAndMedia, +} from "../../../../src/plugin-sdk/zalo.js"; +import { sendPayloadWithChunkedTextAndMedia as sendZalouserPayloadWithChunkedTextAndMedia } from "../../../../src/plugin-sdk/zalouser.js"; import { slackOutbound } from "../../../../test/channel-outbounds.js"; import type { ReplyPayload } from "../../../auto-reply/types.js"; import { createDirectTextMediaOutbound } from "../outbound/direct-text-media.js"; @@ -69,6 +72,13 @@ type PayloadHarnessParams = { sendResults?: Array<{ messageId: string }>; }; +function buildChannelSendResult(channel: string, result: Record) { + return { + channel, + messageId: typeof result.messageId === "string" ? result.messageId : "", + }; +} + const mockedSendZalo = vi.mocked(sendMessageZalo); const mockedSendZalouser = vi.mocked(sendMessageZalouser); @@ -160,6 +170,94 @@ function createDirectTextMediaHarness(params: PayloadHarnessParams) { }; } +function createZaloHarness(params: PayloadHarnessParams) { + primeChannelOutboundSendMock(mockedSendZalo, { ok: true, messageId: "zl-1" }, params.sendResults); + const ctx = { + cfg: {}, + to: "123456789", + text: "", + payload: params.payload, + }; + return { + run: async () => + await sendZaloPayloadWithChunkedTextAndMedia({ + ctx, + textChunkLimit: 2000, + chunker: chunkZaloTextForOutbound, + sendText: async (nextCtx) => + buildChannelSendResult( + "zalo", + await mockedSendZalo(nextCtx.to, nextCtx.text, { + accountId: undefined, + cfg: nextCtx.cfg, + }), + ), + sendMedia: async (nextCtx) => + buildChannelSendResult( + "zalo", + await mockedSendZalo(nextCtx.to, nextCtx.text, { + accountId: undefined, + cfg: nextCtx.cfg, + mediaUrl: nextCtx.mediaUrl, + }), + ), + emptyResult: { channel: "zalo", messageId: "" }, + }), + sendMock: mockedSendZalo, + to: ctx.to, + }; +} + +function createZalouserHarness(params: PayloadHarnessParams) { + primeChannelOutboundSendMock( + mockedSendZalouser, + { ok: true, messageId: "zlu-1" }, + params.sendResults, + ); + const ctx = { + cfg: {}, + to: "user:987654321", + text: "", + payload: params.payload, + }; + return { + run: async () => + await sendZalouserPayloadWithChunkedTextAndMedia({ + ctx, + sendText: async (nextCtx) => { + const target = parseZalouserOutboundTarget(nextCtx.to); + return buildChannelSendResult( + "zalouser", + await mockedSendZalouser(target.threadId, nextCtx.text, { + profile: "default", + isGroup: target.isGroup, + textMode: "markdown", + textChunkMode: "length", + textChunkLimit: 1200, + }), + ); + }, + sendMedia: async (nextCtx) => { + const target = parseZalouserOutboundTarget(nextCtx.to); + return buildChannelSendResult( + "zalouser", + await mockedSendZalouser(target.threadId, nextCtx.text, { + profile: "default", + isGroup: target.isGroup, + mediaUrl: nextCtx.mediaUrl, + textMode: "markdown", + textChunkMode: "length", + textChunkLimit: 1200, + }), + ); + }, + emptyResult: { channel: "zalouser", messageId: "" }, + }), + sendMock: mockedSendZalouser, + to: "987654321", + }; +} + describe("channel outbound payload contract", () => { describe("slack", () => { installChannelOutboundPayloadContractSuite({ @@ -189,20 +287,7 @@ describe("channel outbound payload contract", () => { installChannelOutboundPayloadContractSuite({ channel: "zalo", chunking: { mode: "split", longTextLength: 3000, maxChunkLength: 2000 }, - createHarness: ({ payload, sendResults }) => { - primeChannelOutboundSendMock(mockedSendZalo, { ok: true, messageId: "zl-1" }, sendResults); - return { - run: async () => - await zaloPlugin.outbound!.sendPayload!({ - cfg: {}, - to: "123456789", - text: "", - payload, - }), - sendMock: mockedSendZalo, - to: "123456789", - }; - }, + createHarness: createZaloHarness, }); }); @@ -210,32 +295,7 @@ describe("channel outbound payload contract", () => { installChannelOutboundPayloadContractSuite({ channel: "zalouser", chunking: { mode: "passthrough", longTextLength: 3000 }, - createHarness: ({ payload, sendResults }) => { - setZalouserRuntime({ - channel: { - text: { - resolveChunkMode: vi.fn(() => "length"), - resolveTextChunkLimit: vi.fn(() => 1200), - }, - }, - } as never); - primeChannelOutboundSendMock( - mockedSendZalouser, - { ok: true, messageId: "zlu-1" }, - sendResults, - ); - return { - run: async () => - await zalouserPlugin.outbound!.sendPayload!({ - cfg: {}, - to: "user:987654321", - text: "", - payload, - }), - sendMock: mockedSendZalouser, - to: "987654321", - }; - }, + createHarness: createZalouserHarness, }); });