test: keep Zalo outbound contracts lightweight

Use shared SDK payload helpers directly in the outbound payload contract helper
and narrow ZaloUser target parsing to its session-route module. This preserves
the contract proof without loading broad extension runtime/test barrels.
This commit is contained in:
Gustavo Madeira Santana
2026-04-17 19:01:25 -04:00
parent 3ca8ad3845
commit 8e0bcd0585

View File

@@ -3,43 +3,16 @@ import type { ReplyPayload } from "../../../src/auto-reply/types.js";
import { primeChannelOutboundSendMock } from "../../../src/channels/plugins/contracts/test-helpers.js";
import { createDirectTextMediaOutbound } from "../../../src/channels/plugins/outbound/direct-text-media.js";
import type { ChannelOutboundAdapter } from "../../../src/channels/plugins/types.js";
import { sendPayloadWithChunkedTextAndMedia } from "../../../src/plugin-sdk/reply-payload.js";
import { chunkTextForOutbound } from "../../../src/plugin-sdk/text-chunking.js";
import { resetGlobalHookRunner } from "../../../src/plugins/hook-runner-global.js";
import {
loadBundledPluginPublicSurfaceSync,
loadBundledPluginTestApiSync,
resolveRelativeBundledPluginPublicModuleId,
} from "../../../src/test-utils/bundled-plugin-public-surface.js";
import { resolveRelativeBundledPluginPublicModuleId } from "../../../src/test-utils/bundled-plugin-public-surface.js";
type ParseZalouserOutboundTarget = (raw: string) => { threadId: string; isGroup: boolean };
type CreateSlackOutboundPayloadHarness = (params: PayloadHarnessParams) => {
run: () => Promise<Record<string, unknown>>;
sendMock: Mock;
to: string;
};
type ChunkZaloTextForOutbound = (text: string, maxLength?: number) => string[];
type SendPayloadWithChunkedTextAndMedia = (params: {
ctx: {
cfg: unknown;
to: string;
text: string;
payload: ReplyPayload;
};
sendText: (ctx: {
cfg: unknown;
to: string;
text: string;
payload: ReplyPayload;
}) => Promise<{ channel: string; messageId: string }>;
sendMedia: (ctx: {
cfg: unknown;
to: string;
text: string;
payload: ReplyPayload;
mediaUrl?: string;
}) => Promise<{ channel: string; messageId: string }>;
emptyResult: { channel: string; messageId: string };
textChunkLimit?: number;
chunker?: ChunkZaloTextForOutbound | null;
}) => Promise<{ channel: string; messageId: string }>;
const discordOutboundAdapterModuleId = resolveRelativeBundledPluginPublicModuleId({
fromModuleUrl: import.meta.url,
@@ -56,9 +29,18 @@ const whatsappTestApiModuleId = resolveRelativeBundledPluginPublicModuleId({
pluginId: "whatsapp",
artifactBasename: "test-api.js",
});
const zalouserSessionRouteModuleId = resolveRelativeBundledPluginPublicModuleId({
fromModuleUrl: import.meta.url,
pluginId: "zalouser",
artifactBasename: "src/session-route.js",
});
let discordOutboundCache: Promise<ChannelOutboundAdapter> | undefined;
let parseZalouserOutboundTargetCache: ParseZalouserOutboundTarget | undefined;
let parseZalouserOutboundTargetPromise:
| Promise<{
parseZalouserOutboundTarget: ParseZalouserOutboundTarget;
}>
| undefined;
let slackTestApiPromise:
| Promise<{
createSlackOutboundPayloadHarness: CreateSlackOutboundPayloadHarness;
@@ -69,9 +51,6 @@ let whatsappTestApiPromise:
whatsappOutbound: ChannelOutboundAdapter;
}>
| undefined;
let chunkZaloTextForOutboundCache: ChunkZaloTextForOutbound | undefined;
let sendZaloPayloadWithChunkedTextAndMediaCache: SendPayloadWithChunkedTextAndMedia | undefined;
let sendZalouserPayloadWithChunkedTextAndMediaCache: SendPayloadWithChunkedTextAndMedia | undefined;
async function getDiscordOutbound(): Promise<ChannelOutboundAdapter> {
discordOutboundCache ??= (async () => {
@@ -99,52 +78,12 @@ async function getWhatsAppOutboundAsync(): Promise<ChannelOutboundAdapter> {
return whatsappOutbound;
}
function getChunkZaloTextForOutbound(): ChunkZaloTextForOutbound {
if (!chunkZaloTextForOutboundCache) {
({ chunkTextForOutbound: chunkZaloTextForOutboundCache } = loadBundledPluginPublicSurfaceSync<{
chunkTextForOutbound: ChunkZaloTextForOutbound;
}>({
pluginId: "zalo",
artifactBasename: "runtime-api.js",
}));
}
return chunkZaloTextForOutboundCache;
}
function getSendZaloPayloadWithChunkedTextAndMedia(): SendPayloadWithChunkedTextAndMedia {
if (!sendZaloPayloadWithChunkedTextAndMediaCache) {
({ sendPayloadWithChunkedTextAndMedia: sendZaloPayloadWithChunkedTextAndMediaCache } =
loadBundledPluginPublicSurfaceSync<{
sendPayloadWithChunkedTextAndMedia: SendPayloadWithChunkedTextAndMedia;
}>({
pluginId: "zalo",
artifactBasename: "runtime-api.js",
}));
}
return sendZaloPayloadWithChunkedTextAndMediaCache;
}
function getParseZalouserOutboundTarget(): ParseZalouserOutboundTarget {
if (!parseZalouserOutboundTargetCache) {
({ parseZalouserOutboundTarget: parseZalouserOutboundTargetCache } =
loadBundledPluginTestApiSync<{
parseZalouserOutboundTarget: ParseZalouserOutboundTarget;
}>("zalouser"));
}
return parseZalouserOutboundTargetCache;
}
function getSendZalouserPayloadWithChunkedTextAndMedia(): SendPayloadWithChunkedTextAndMedia {
if (!sendZalouserPayloadWithChunkedTextAndMediaCache) {
({ sendPayloadWithChunkedTextAndMedia: sendZalouserPayloadWithChunkedTextAndMediaCache } =
loadBundledPluginPublicSurfaceSync<{
sendPayloadWithChunkedTextAndMedia: SendPayloadWithChunkedTextAndMedia;
}>({
pluginId: "zalouser",
artifactBasename: "runtime-api.js",
}));
}
return sendZalouserPayloadWithChunkedTextAndMediaCache;
async function getParseZalouserOutboundTarget(): Promise<ParseZalouserOutboundTarget> {
parseZalouserOutboundTargetPromise ??= import(zalouserSessionRouteModuleId) as Promise<{
parseZalouserOutboundTarget: ParseZalouserOutboundTarget;
}>;
const { parseZalouserOutboundTarget } = await parseZalouserOutboundTargetPromise;
return parseZalouserOutboundTarget;
}
type PayloadHarnessParams = {
@@ -367,10 +306,10 @@ function createZaloHarness(params: PayloadHarnessParams) {
};
return {
run: async () =>
await getSendZaloPayloadWithChunkedTextAndMedia()({
await sendPayloadWithChunkedTextAndMedia({
ctx,
textChunkLimit: 2000,
chunker: getChunkZaloTextForOutbound(),
chunker: chunkTextForOutbound,
sendText: async (nextCtx) =>
buildChannelSendResult(
"zalo",
@@ -406,10 +345,10 @@ function createZalouserHarness(params: PayloadHarnessParams) {
};
return {
run: async () =>
await getSendZalouserPayloadWithChunkedTextAndMedia()({
await sendPayloadWithChunkedTextAndMedia({
ctx,
sendText: async (nextCtx) => {
const target = getParseZalouserOutboundTarget()(nextCtx.to);
const target = (await getParseZalouserOutboundTarget())(nextCtx.to);
return buildChannelSendResult(
"zalouser",
await sendZalouser(target.threadId, nextCtx.text, {
@@ -422,7 +361,7 @@ function createZalouserHarness(params: PayloadHarnessParams) {
);
},
sendMedia: async (nextCtx) => {
const target = getParseZalouserOutboundTarget()(nextCtx.to);
const target = (await getParseZalouserOutboundTarget())(nextCtx.to);
return buildChannelSendResult(
"zalouser",
await sendZalouser(target.threadId, nextCtx.text, {