






















@@ -13,6 +13,29 @@ await installDiscordOutboundModuleSpies(hoisted);
1313let normalizeDiscordOutboundTarget: typeof import("./normalize.js").normalizeDiscordOutboundTarget;
1414let discordOutbound: typeof import("./outbound-adapter.js").discordOutbound;
151516+type MockCallSource = { mock: { calls: Array<Array<unknown>> } };
17+18+function mockCall(source: MockCallSource, label: string, callIndex = 0): Array<unknown> {
19+const call = source.mock.calls[callIndex];
20+if (!call) {
21+throw new Error(`expected ${label} call ${callIndex}`);
22+}
23+return call;
24+}
25+26+function mockObjectArg(
27+source: MockCallSource,
28+label: string,
29+callIndex: number,
30+argIndex: number,
31+): Record<string, unknown> {
32+const value = mockCall(source, label, callIndex)[argIndex];
33+if (!value || typeof value !== "object") {
34+throw new Error(`expected ${label} call ${callIndex} argument ${argIndex} to be an object`);
35+}
36+return value as Record<string, unknown>;
37+}
38+1639beforeAll(async () => {
1740({ normalizeDiscordOutboundTarget } = await import("./normalize.js"));
1841({ discordOutbound } = await import("./outbound-adapter.js"));
@@ -125,16 +148,14 @@ describe("discordOutbound", () => {
125148},
126149});
127150128-expect(hoisted.sendMessageDiscordMock).toHaveBeenCalledWith(
129-"channel:123456",
130-"formatted",
131-expect.objectContaining({
132-textLimit: 1234,
133-maxLinesPerMessage: 7,
134-tableMode: "off",
135-chunkMode: "newline",
136-}),
137-);
151+const call = mockCall(hoisted.sendMessageDiscordMock, "sendMessageDiscord");
152+expect(call[0]).toBe("channel:123456");
153+expect(call[1]).toBe("formatted");
154+const options = mockObjectArg(hoisted.sendMessageDiscordMock, "sendMessageDiscord", 0, 2);
155+expect(options.textLimit).toBe(1234);
156+expect(options.maxLinesPerMessage).toBe(7);
157+expect(options.tableMode).toBe("off");
158+expect(options.chunkMode).toBe("newline");
138159});
139160140161it.each([500, 429])("retries transient Discord text send status %i", async (status) => {
@@ -190,22 +211,22 @@ describe("discordOutbound", () => {
190211},
191212});
192213193-expect(hoisted.sendWebhookMessageDiscordMock).toHaveBeenCalledWith(
194-"hello from persona",
195-expect.objectContaining({
196-webhookId: "wh-1",
197-webhookToken: "tok-1",
198-accountId: "default",
199-threadId: "thread-1",
200-replyTo: "reply-1",
201-username: "Codex",
202-avatarUrl: "https://example.com/avatar.png",
203-}),
214+const call = mockCall(hoisted.sendWebhookMessageDiscordMock, "sendWebhookMessageDiscord");
215+expect(call[0]).toBe("hello from persona");
216+const options = mockObjectArg(
217+hoisted.sendWebhookMessageDiscordMock,
218+"sendWebhookMessageDiscord",
219+0,
220+1,
204221);
205-expect(
206-(hoisted.sendWebhookMessageDiscordMock.mock.calls[0]?.[1] as { cfg?: unknown } | undefined)
207-?.cfg,
208-).toBe(cfg);
222+expect(options.webhookId).toBe("wh-1");
223+expect(options.webhookToken).toBe("tok-1");
224+expect(options.accountId).toBe("default");
225+expect(options.threadId).toBe("thread-1");
226+expect(options.replyTo).toBe("reply-1");
227+expect(options.username).toBe("Codex");
228+expect(options.avatarUrl).toBe("https://example.com/avatar.png");
229+expect(options.cfg).toBe(cfg);
209230expect(hoisted.sendMessageDiscordMock).not.toHaveBeenCalled();
210231expect(result).toEqual({
211232channel: "discord",
@@ -267,15 +288,14 @@ describe("discordOutbound", () => {
267288threadId: "thread-1",
268289});
269290270-expect(hoisted.sendPollDiscordMock).toHaveBeenCalledWith(
271-"channel:thread-1",
272-{
273-question: "Best snack?",
274-options: ["banana", "apple"],
275-},
276-expect.objectContaining({
277-accountId: "default",
278-}),
291+const call = mockCall(hoisted.sendPollDiscordMock, "sendPollDiscord");
292+expect(call[0]).toBe("channel:thread-1");
293+expect(call[1]).toEqual({
294+question: "Best snack?",
295+options: ["banana", "apple"],
296+});
297+expect(mockObjectArg(hoisted.sendPollDiscordMock, "sendPollDiscord", 0, 2).accountId).toBe(
298+"default",
279299);
280300expect(result).toEqual({
281301channel: "discord",
@@ -299,31 +319,37 @@ describe("discordOutbound", () => {
299319replyToMode: "first",
300320});
301321302-expect(hoisted.sendVoiceMessageDiscordMock).toHaveBeenCalledWith(
303-"channel:123456",
304-"https://example.com/voice.ogg",
305-expect.objectContaining({
306-accountId: "default",
307-replyTo: "reply-1",
308-}),
322+const voiceCall = mockCall(hoisted.sendVoiceMessageDiscordMock, "sendVoiceMessageDiscord");
323+expect(voiceCall[0]).toBe("channel:123456");
324+expect(voiceCall[1]).toBe("https://example.com/voice.ogg");
325+const voiceOptions = mockObjectArg(
326+hoisted.sendVoiceMessageDiscordMock,
327+"sendVoiceMessageDiscord",
328+0,
329+2,
309330);
310-expect(hoisted.sendMessageDiscordMock).toHaveBeenCalledWith(
311-"channel:123456",
312-"voice note",
313-expect.objectContaining({
314-accountId: "default",
315-replyTo: undefined,
316-}),
317-);
318-expect(hoisted.sendMessageDiscordMock).toHaveBeenCalledWith(
319-"channel:123456",
320-"",
321-expect.objectContaining({
322-accountId: "default",
323-mediaUrl: "https://example.com/extra.png",
324-replyTo: undefined,
325-}),
331+expect(voiceOptions.accountId).toBe("default");
332+expect(voiceOptions.replyTo).toBe("reply-1");
333+334+const messageCall = mockCall(hoisted.sendMessageDiscordMock, "sendMessageDiscord", 0);
335+expect(messageCall[0]).toBe("channel:123456");
336+expect(messageCall[1]).toBe("voice note");
337+const messageOptions = mockObjectArg(
338+hoisted.sendMessageDiscordMock,
339+"sendMessageDiscord",
340+0,
341+2,
326342);
343+expect(messageOptions.accountId).toBe("default");
344+expect(messageOptions.replyTo).toBeUndefined();
345+346+const mediaCall = mockCall(hoisted.sendMessageDiscordMock, "sendMessageDiscord", 1);
347+expect(mediaCall[0]).toBe("channel:123456");
348+expect(mediaCall[1]).toBe("");
349+const mediaOptions = mockObjectArg(hoisted.sendMessageDiscordMock, "sendMessageDiscord", 1, 2);
350+expect(mediaOptions.accountId).toBe("default");
351+expect(mediaOptions.mediaUrl).toBe("https://example.com/extra.png");
352+expect(mediaOptions.replyTo).toBeUndefined();
327353expect(result).toEqual({
328354channel: "discord",
329355messageId: "msg-1",
@@ -393,22 +419,24 @@ describe("discordOutbound", () => {
393419replyToId: "reply-1",
394420});
395421396-expect(hoisted.sendMessageDiscordMock).toHaveBeenCalledWith(
397-"channel:123456",
398-"rendered clip",
399-expect.objectContaining({
400-accountId: "default",
401-replyTo: "reply-1",
402-}),
403-);
404-expect(hoisted.sendMessageDiscordMock).toHaveBeenCalledWith(
405-"channel:123456",
406-"",
407-expect.objectContaining({
408-accountId: "default",
409-mediaUrl: "/tmp/render.mp4",
410-}),
422+const captionCall = mockCall(hoisted.sendMessageDiscordMock, "sendMessageDiscord", 0);
423+expect(captionCall[0]).toBe("channel:123456");
424+expect(captionCall[1]).toBe("rendered clip");
425+const captionOptions = mockObjectArg(
426+hoisted.sendMessageDiscordMock,
427+"sendMessageDiscord",
428+0,
429+2,
411430);
431+expect(captionOptions.accountId).toBe("default");
432+expect(captionOptions.replyTo).toBe("reply-1");
433+434+const mediaCall = mockCall(hoisted.sendMessageDiscordMock, "sendMessageDiscord", 1);
435+expect(mediaCall[0]).toBe("channel:123456");
436+expect(mediaCall[1]).toBe("");
437+const mediaOptions = mockObjectArg(hoisted.sendMessageDiscordMock, "sendMessageDiscord", 1, 2);
438+expect(mediaOptions.accountId).toBe("default");
439+expect(mediaOptions.mediaUrl).toBe("/tmp/render.mp4");
412440});
413441414442it("touches bound thread activity after shared outbound delivery succeeds", async () => {
@@ -472,26 +500,39 @@ describe("discordOutbound", () => {
472500replyToMode: "first",
473501});
474502475-expect(hoisted.sendDiscordComponentMessageMock).toHaveBeenCalledWith(
476-"channel:123456",
477-expect.objectContaining({ text: "hello" }),
478-expect.objectContaining({
479-mediaUrl: "https://example.com/1.png",
480-mediaLocalRoots: ["/tmp/media"],
481-accountId: "default",
482-replyTo: "reply-1",
483-}),
503+const componentCall = mockCall(
504+hoisted.sendDiscordComponentMessageMock,
505+"sendDiscordComponentMessage",
484506);
485-expect(hoisted.sendMessageDiscordMock).toHaveBeenCalledWith(
486-"channel:123456",
487-"",
488-expect.objectContaining({
489-mediaUrl: "https://example.com/2.png",
490-mediaLocalRoots: ["/tmp/media"],
491-accountId: "default",
492-replyTo: undefined,
493-}),
507+expect(componentCall[0]).toBe("channel:123456");
508+expect(
509+mockObjectArg(hoisted.sendDiscordComponentMessageMock, "sendDiscordComponentMessage", 0, 1)
510+.text,
511+).toBe("hello");
512+const componentOptions = mockObjectArg(
513+hoisted.sendDiscordComponentMessageMock,
514+"sendDiscordComponentMessage",
515+0,
516+2,
494517);
518+expect(componentOptions.mediaUrl).toBe("https://example.com/1.png");
519+expect(componentOptions.mediaLocalRoots).toEqual(["/tmp/media"]);
520+expect(componentOptions.accountId).toBe("default");
521+expect(componentOptions.replyTo).toBe("reply-1");
522+523+const messageCall = mockCall(hoisted.sendMessageDiscordMock, "sendMessageDiscord");
524+expect(messageCall[0]).toBe("channel:123456");
525+expect(messageCall[1]).toBe("");
526+const messageOptions = mockObjectArg(
527+hoisted.sendMessageDiscordMock,
528+"sendMessageDiscord",
529+0,
530+2,
531+);
532+expect(messageOptions.mediaUrl).toBe("https://example.com/2.png");
533+expect(messageOptions.mediaLocalRoots).toEqual(["/tmp/media"]);
534+expect(messageOptions.accountId).toBe("default");
535+expect(messageOptions.replyTo).toBeUndefined();
495536expect(result).toEqual({
496537channel: "discord",
497538messageId: "msg-2",
@@ -560,17 +601,15 @@ describe("discordOutbound", () => {
560601replyToId: "reply-1",
561602});
562603563-expect(hoisted.sendMessageDiscordMock).toHaveBeenCalledWith(
564-"channel:123456",
565-"hello",
566-expect.objectContaining({
567-mediaUrl: "https://example.com/photo.png",
568-components: [{ type: 1, components: [] }],
569-filename: "photo.png",
570-accountId: "default",
571-replyTo: "reply-1",
572-}),
573-);
604+const call = mockCall(hoisted.sendMessageDiscordMock, "sendMessageDiscord");
605+expect(call[0]).toBe("channel:123456");
606+expect(call[1]).toBe("hello");
607+const options = mockObjectArg(hoisted.sendMessageDiscordMock, "sendMessageDiscord", 0, 2);
608+expect(options.mediaUrl).toBe("https://example.com/photo.png");
609+expect(options.components).toEqual([{ type: 1, components: [] }]);
610+expect(options.filename).toBe("photo.png");
611+expect(options.accountId).toBe("default");
612+expect(options.replyTo).toBe("reply-1");
574613});
575614576615it("preserves explicit component payload replies when replyToMode is off", async () => {
@@ -641,14 +680,20 @@ describe("discordOutbound", () => {
641680accountId: "default",
642681});
643682644-expect(hoisted.sendDiscordComponentMessageMock).toHaveBeenCalledWith(
645- "channel:123456",
646- expect.objectContaining({
647- text: "native component text",
648- blocks: [{ type: "text", text: "Native component body" }],
649-}),
650-expect.objectContaining({ accountId: "default" }),
683+const call = mockCall(hoisted.sendDiscordComponentMessageMock, "sendDiscordComponentMessage");
684+expect(call[0]).toBe("channel:123456");
685+const payload = mockObjectArg(
686+hoisted.sendDiscordComponentMessageMock,
687+"sendDiscordComponentMessage",
688+0,
689+1,
651690);
691+expect(payload.text).toBe("native component text");
692+expect(payload.blocks).toEqual([{ type: "text", text: "Native component body" }]);
693+expect(
694+mockObjectArg(hoisted.sendDiscordComponentMessageMock, "sendDiscordComponentMessage", 0, 2)
695+.accountId,
696+).toBe("default");
652697expect(hoisted.sendMessageDiscordMock).not.toHaveBeenCalled();
653698});
654699@@ -669,13 +714,12 @@ describe("discordOutbound", () => {
669714accountId: "default",
670715});
671716672-expect(hoisted.sendMessageDiscordMock).toHaveBeenCalledWith(
673-"channel:123456",
674-"Approval @\u200beveryone <@\u200b123> <#\u200b456>",
675-expect.objectContaining({
676-accountId: "default",
677-}),
678-);
717+const call = mockCall(hoisted.sendMessageDiscordMock, "sendMessageDiscord");
718+expect(call[0]).toBe("channel:123456");
719+expect(call[1]).toBe("Approval @\u200beveryone <@\u200b123> <#\u200b456>");
720+expect(
721+mockObjectArg(hoisted.sendMessageDiscordMock, "sendMessageDiscord", 0, 2).accountId,
722+).toBe("default");
679723});
680724681725it("uses a single implicit reply for chunked approval payload fallbacks", async () => {
@@ -717,12 +761,11 @@ describe("discordOutbound", () => {
717761accountId: "default",
718762});
719763720-expect(hoisted.sendMessageDiscordMock).toHaveBeenCalledWith(
721-"channel:123456",
722-"Hello @everyone",
723-expect.objectContaining({
724-accountId: "default",
725-}),
726-);
764+const call = mockCall(hoisted.sendMessageDiscordMock, "sendMessageDiscord");
765+expect(call[0]).toBe("channel:123456");
766+expect(call[1]).toBe("Hello @everyone");
767+expect(
768+mockObjectArg(hoisted.sendMessageDiscordMock, "sendMessageDiscord", 0, 2).accountId,
769+).toBe("default");
727770});
728771});
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。