Skip to content

Commit 4bc47a3

Browse files
committed
test: guard telegram delivery mock calls
1 parent 5ef8d1a commit 4bc47a3

1 file changed

Lines changed: 65 additions & 65 deletions

File tree

extensions/telegram/src/bot/delivery.test.ts

Lines changed: 65 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -114,7 +114,7 @@ function expectRecordFields(record: unknown, expected: Record<string, unknown>)
114114
}
115115

116116
function mockCallArg(mock: ReturnType<typeof vi.fn>, callIndex: number, argIndex: number) {
117-
const call = mock.mock.calls[callIndex];
117+
const call = mock.mock.calls.at(callIndex);
118118
if (!call) {
119119
throw new Error(`Expected mock call ${callIndex}`);
120120
}
@@ -229,7 +229,7 @@ describe("deliverReplies", () => {
229229

230230
expect(runtime.error).toHaveBeenCalledTimes(1);
231231
expect(sendMessage).toHaveBeenCalledTimes(1);
232-
expect(sendMessage.mock.calls[0]?.[1]).toBe("hello");
232+
expect(sendMessage.mock.calls.at(0)?.[1]).toBe("hello");
233233
});
234234

235235
it("mirrors delivered replies once after successful sends", async () => {
@@ -276,8 +276,8 @@ describe("deliverReplies", () => {
276276
bot,
277277
});
278278

279-
expect(sendMessage.mock.calls[0]?.[0]).toBe("123");
280-
expect(sendMessage.mock.calls[0]?.[1]).toBe("Plugin bind approval required");
279+
expect(sendMessage.mock.calls.at(0)?.[0]).toBe("123");
280+
expect(sendMessage.mock.calls.at(0)?.[1]).toBe("Plugin bind approval required");
281281
expectRecordFields(mockCallArg(sendMessage, 0, 2), {
282282
reply_markup: {
283283
inline_keyboard: [
@@ -309,8 +309,8 @@ describe("deliverReplies", () => {
309309
});
310310

311311
expect(runtime.error).not.toHaveBeenCalled();
312-
expect(sendMessage.mock.calls[0]?.[0]).toBe("123");
313-
expect(sendMessage.mock.calls[0]?.[1]).toContain("Retry");
312+
expect(sendMessage.mock.calls.at(0)?.[0]).toBe("123");
313+
expect(sendMessage.mock.calls.at(0)?.[1]).toContain("Retry");
314314
expectRecordFields(mockCallArg(sendMessage, 0, 2), {
315315
reply_markup: {
316316
inline_keyboard: [[{ text: "Retry", callback_data: "cmd:retry" }]],
@@ -392,8 +392,8 @@ describe("deliverReplies", () => {
392392
silent: true,
393393
});
394394

395-
expect(sendMessage.mock.calls[0]?.[0]).toBe("123");
396-
expect(typeof sendMessage.mock.calls[0]?.[1]).toBe("string");
395+
expect(sendMessage.mock.calls.at(0)?.[0]).toBe("123");
396+
expect(typeof sendMessage.mock.calls.at(0)?.[1]).toBe("string");
397397
expectRecordFields(mockCallArg(sendMessage, 0, 2), { disable_notification: true });
398398
});
399399

@@ -455,9 +455,9 @@ describe("deliverReplies", () => {
455455
});
456456

457457
expect(sendMessage).toHaveBeenCalledTimes(1);
458-
expect(sendMessage.mock.calls[0]?.[1]).toBeTypeOf("string");
459-
expect(sendMessage.mock.calls[0]?.[1]).not.toBe("");
460-
expect(sendMessage.mock.calls[0]?.[1]?.trim()).not.toBe("NO_REPLY");
458+
expect(sendMessage.mock.calls.at(0)?.[1]).toBeTypeOf("string");
459+
expect(sendMessage.mock.calls.at(0)?.[1]).not.toBe("");
460+
expect(sendMessage.mock.calls.at(0)?.[1]?.trim()).not.toBe("NO_REPLY");
461461
});
462462

463463
it("uses the policy session key for exact NO_REPLY policy", async () => {
@@ -474,9 +474,9 @@ describe("deliverReplies", () => {
474474
});
475475

476476
expect(sendMessage).toHaveBeenCalledTimes(1);
477-
expect(sendMessage.mock.calls[0]?.[1]).toBeTypeOf("string");
478-
expect(sendMessage.mock.calls[0]?.[1]).not.toBe("");
479-
expect(sendMessage.mock.calls[0]?.[1]?.trim()).not.toBe("NO_REPLY");
477+
expect(sendMessage.mock.calls.at(0)?.[1]).toBeTypeOf("string");
478+
expect(sendMessage.mock.calls.at(0)?.[1]).not.toBe("");
479+
expect(sendMessage.mock.calls.at(0)?.[1]?.trim()).not.toBe("NO_REPLY");
480480
});
481481

482482
it("suppresses exact NO_REPLY for group Telegram sessions", async () => {
@@ -631,8 +631,8 @@ describe("deliverReplies", () => {
631631
bot,
632632
});
633633

634-
expect(sendPhoto.mock.calls[0]?.[0]).toBe("123");
635-
if (sendPhoto.mock.calls[0]?.[1] === undefined) {
634+
expect(sendPhoto.mock.calls.at(0)?.[0]).toBe("123");
635+
if (sendPhoto.mock.calls.at(0)?.[1] === undefined) {
636636
throw new Error("Expected Telegram photo media");
637637
}
638638
expectRecordFields(mockCallArg(sendPhoto, 0, 2), {
@@ -659,8 +659,8 @@ describe("deliverReplies", () => {
659659
});
660660

661661
expect(probeVideoDimensions).toHaveBeenCalledWith(Buffer.from("video"));
662-
expect(sendVideo.mock.calls[0]?.[0]).toBe("123");
663-
if (sendVideo.mock.calls[0]?.[1] === undefined) {
662+
expect(sendVideo.mock.calls.at(0)?.[0]).toBe("123");
663+
if (sendVideo.mock.calls.at(0)?.[1] === undefined) {
664664
throw new Error("Expected Telegram video media");
665665
}
666666
expectRecordFields(mockCallArg(sendVideo, 0, 2), {
@@ -688,8 +688,8 @@ describe("deliverReplies", () => {
688688
});
689689

690690
expect(probeVideoDimensions).not.toHaveBeenCalled();
691-
expect(sendAnimation.mock.calls[0]?.[0]).toBe("123");
692-
if (sendAnimation.mock.calls[0]?.[1] === undefined) {
691+
expect(sendAnimation.mock.calls.at(0)?.[0]).toBe("123");
692+
if (sendAnimation.mock.calls.at(0)?.[1] === undefined) {
693693
throw new Error("Expected Telegram animation media");
694694
}
695695
const options = mockCallArg(sendAnimation, 0, 2) as Record<string, unknown>;
@@ -735,8 +735,8 @@ describe("deliverReplies", () => {
735735
linkPreview: false,
736736
});
737737

738-
expect(sendMessage.mock.calls[0]?.[0]).toBe("123");
739-
expect(typeof sendMessage.mock.calls[0]?.[1]).toBe("string");
738+
expect(sendMessage.mock.calls.at(0)?.[0]).toBe("123");
739+
expect(typeof sendMessage.mock.calls.at(0)?.[1]).toBe("string");
740740
expectRecordFields(mockCallArg(sendMessage, 0, 2), {
741741
link_preview_options: { is_disabled: true },
742742
});
@@ -752,8 +752,8 @@ describe("deliverReplies", () => {
752752
thread: { id: 42, scope: "dm" },
753753
});
754754

755-
expect(sendMessage.mock.calls[0]?.[0]).toBe("123");
756-
expect(typeof sendMessage.mock.calls[0]?.[1]).toBe("string");
755+
expect(sendMessage.mock.calls.at(0)?.[0]).toBe("123");
756+
expect(typeof sendMessage.mock.calls.at(0)?.[1]).toBe("string");
757757
expectRecordFields(mockCallArg(sendMessage, 0, 2), { message_thread_id: 42 });
758758
});
759759

@@ -772,7 +772,7 @@ describe("deliverReplies", () => {
772772
).rejects.toThrow("message thread not found");
773773

774774
expect(sendMessage).toHaveBeenCalledTimes(1);
775-
expectRecordFields(sendMessage.mock.calls[0]?.[2], { message_thread_id: 42 });
775+
expectRecordFields(mockCallArg(sendMessage, 0, 2), { message_thread_id: 42 });
776776
expect(runtime.error).toHaveBeenCalledTimes(1);
777777
});
778778

@@ -853,7 +853,7 @@ describe("deliverReplies", () => {
853853
).rejects.toThrow("message thread not found");
854854

855855
expect(sendPhoto).toHaveBeenCalledTimes(1);
856-
expectRecordFields(sendPhoto.mock.calls[0]?.[2], { message_thread_id: 42 });
856+
expectRecordFields(mockCallArg(sendPhoto, 0, 2), { message_thread_id: 42 });
857857
expect(runtime.error).toHaveBeenCalledTimes(1);
858858
});
859859

@@ -867,9 +867,9 @@ describe("deliverReplies", () => {
867867
linkPreview: true,
868868
});
869869

870-
expect(sendMessage.mock.calls[0]?.[0]).toBe("123");
871-
expect(typeof sendMessage.mock.calls[0]?.[1]).toBe("string");
872-
expect(sendMessage.mock.calls[0]?.[2]).not.toHaveProperty("link_preview_options");
870+
expect(sendMessage.mock.calls.at(0)?.[0]).toBe("123");
871+
expect(typeof sendMessage.mock.calls.at(0)?.[1]).toBe("string");
872+
expect(mockCallArg(sendMessage, 0, 2)).not.toHaveProperty("link_preview_options");
873873
});
874874

875875
it("falls back to plain text when markdown renders to empty HTML in threaded mode", async () => {
@@ -897,9 +897,9 @@ describe("deliverReplies", () => {
897897
});
898898

899899
expect(sendMessage).toHaveBeenCalledTimes(1);
900-
expect(sendMessage.mock.calls[0]?.[0]).toBe("123");
901-
expect(sendMessage.mock.calls[0]?.[1]).toBe(">");
902-
expectRecordFields(sendMessage.mock.calls[0]?.[2], { message_thread_id: 42 });
900+
expect(sendMessage.mock.calls.at(0)?.[0]).toBe("123");
901+
expect(sendMessage.mock.calls.at(0)?.[1]).toBe(">");
902+
expectRecordFields(mockCallArg(sendMessage, 0, 2), { message_thread_id: 42 });
903903
});
904904

905905
it("skips whitespace-only text replies without calling Telegram", async () => {
@@ -940,9 +940,9 @@ describe("deliverReplies", () => {
940940
replyQuoteEntities: [{ type: "bold", offset: 0, length: 6 }],
941941
});
942942

943-
expect(sendMessage.mock.calls[0]?.[0]).toBe("123");
944-
expect(typeof sendMessage.mock.calls[0]?.[1]).toBe("string");
945-
expectRecordFields(sendMessage.mock.calls[0]?.[2], {
943+
expect(sendMessage.mock.calls.at(0)?.[0]).toBe("123");
944+
expect(typeof sendMessage.mock.calls.at(0)?.[1]).toBe("string");
945+
expectRecordFields(mockCallArg(sendMessage, 0, 2), {
946946
reply_parameters: {
947947
message_id: 500,
948948
quote: " quoted text\n",
@@ -951,7 +951,7 @@ describe("deliverReplies", () => {
951951
allow_sending_without_reply: true,
952952
},
953953
});
954-
expect(sendMessage.mock.calls[0]?.[2]).not.toHaveProperty("reply_to_message_id");
954+
expect(mockCallArg(sendMessage, 0, 2)).not.toHaveProperty("reply_to_message_id");
955955
});
956956

957957
it("uses the native quote candidate that matches each reply target", async () => {
@@ -976,15 +976,15 @@ describe("deliverReplies", () => {
976976
},
977977
});
978978

979-
expectRecordFields(sendMessage.mock.calls[0]?.[2], {
979+
expectRecordFields(mockCallArg(sendMessage, 0, 2), {
980980
reply_parameters: {
981981
message_id: 500,
982982
quote: "first quote",
983983
quote_position: 0,
984984
allow_sending_without_reply: true,
985985
},
986986
});
987-
expectRecordFields(sendMessage.mock.calls[1]?.[2], {
987+
expectRecordFields(mockCallArg(sendMessage, 1, 2), {
988988
reply_parameters: {
989989
message_id: 501,
990990
quote: "second quote",
@@ -1020,18 +1020,18 @@ describe("deliverReplies", () => {
10201020
});
10211021

10221022
expect(sendMessage).toHaveBeenCalledTimes(2);
1023-
expectRecordFields(sendMessage.mock.calls[0][2], {
1023+
expectRecordFields(mockCallArg(sendMessage, 0, 2), {
10241024
reply_parameters: {
10251025
message_id: 500,
10261026
quote: " quoted text\n",
10271027
allow_sending_without_reply: true,
10281028
},
10291029
});
1030-
expectRecordFields(sendMessage.mock.calls[1][2], {
1030+
expectRecordFields(mockCallArg(sendMessage, 1, 2), {
10311031
reply_to_message_id: 500,
10321032
allow_sending_without_reply: true,
10331033
});
1034-
expect(sendMessage.mock.calls[1][2]).not.toHaveProperty("reply_parameters");
1034+
expect(mockCallArg(sendMessage, 1, 2)).not.toHaveProperty("reply_parameters");
10351035
}
10361036
});
10371037

@@ -1052,13 +1052,13 @@ describe("deliverReplies", () => {
10521052
replyQuoteText: "quoted text",
10531053
});
10541054

1055-
expect(sendMessage.mock.calls[0]?.[0]).toBe("123");
1056-
expect(typeof sendMessage.mock.calls[0]?.[1]).toBe("string");
1057-
expectRecordFields(sendMessage.mock.calls[0]?.[2], {
1055+
expect(sendMessage.mock.calls.at(0)?.[0]).toBe("123");
1056+
expect(typeof sendMessage.mock.calls.at(0)?.[1]).toBe("string");
1057+
expectRecordFields(mockCallArg(sendMessage, 0, 2), {
10581058
reply_to_message_id: 501,
10591059
allow_sending_without_reply: true,
10601060
});
1061-
expect(sendMessage.mock.calls[0][2]).not.toHaveProperty("reply_parameters");
1061+
expect(mockCallArg(sendMessage, 0, 2)).not.toHaveProperty("reply_parameters");
10621062
});
10631063

10641064
it("omits native quote parameters when reply mode suppresses the reply", async () => {
@@ -1078,8 +1078,8 @@ describe("deliverReplies", () => {
10781078
replyQuoteText: "quoted text",
10791079
});
10801080

1081-
expect(sendMessage.mock.calls[0][2]).not.toHaveProperty("reply_parameters");
1082-
expect(sendMessage.mock.calls[0][2]).not.toHaveProperty("reply_to_message_id");
1081+
expect(mockCallArg(sendMessage, 0, 2)).not.toHaveProperty("reply_parameters");
1082+
expect(mockCallArg(sendMessage, 0, 2)).not.toHaveProperty("reply_to_message_id");
10831083
});
10841084

10851085
it("uses legacy reply id when quote text has no quoted message id", async () => {
@@ -1098,13 +1098,13 @@ describe("deliverReplies", () => {
10981098
replyQuoteText: "quoted text",
10991099
});
11001100

1101-
expect(sendMessage.mock.calls[0]?.[0]).toBe("123");
1102-
expect(typeof sendMessage.mock.calls[0]?.[1]).toBe("string");
1103-
expectRecordFields(sendMessage.mock.calls[0]?.[2], {
1101+
expect(sendMessage.mock.calls.at(0)?.[0]).toBe("123");
1102+
expect(typeof sendMessage.mock.calls.at(0)?.[1]).toBe("string");
1103+
expectRecordFields(mockCallArg(sendMessage, 0, 2), {
11041104
reply_to_message_id: 501,
11051105
allow_sending_without_reply: true,
11061106
});
1107-
expect(sendMessage.mock.calls[0][2]).not.toHaveProperty("reply_parameters");
1107+
expect(mockCallArg(sendMessage, 0, 2)).not.toHaveProperty("reply_parameters");
11081108
});
11091109

11101110
it("falls back to text when sendVoice fails with VOICE_MESSAGES_FORBIDDEN", async () => {
@@ -1130,9 +1130,9 @@ describe("deliverReplies", () => {
11301130
expect(sendVoice).toHaveBeenCalledTimes(1);
11311131
// Fallback to text succeeded
11321132
expect(sendMessage).toHaveBeenCalledTimes(1);
1133-
expect(sendMessage.mock.calls[0]?.[0]).toBe("123");
1134-
expect(sendMessage.mock.calls[0]?.[1]).toContain("Hello there");
1135-
if (sendMessage.mock.calls[0]?.[2] === undefined) {
1133+
expect(sendMessage.mock.calls.at(0)?.[0]).toBe("123");
1134+
expect(sendMessage.mock.calls.at(0)?.[1]).toContain("Hello there");
1135+
if (sendMessage.mock.calls.at(0)?.[2] === undefined) {
11361136
throw new Error("Expected Telegram fallback text options");
11371137
}
11381138
});
@@ -1158,9 +1158,9 @@ describe("deliverReplies", () => {
11581158
});
11591159

11601160
expect(sendVoice).toHaveBeenCalledTimes(1);
1161-
expect(sendMessage.mock.calls[0]?.[0]).toBe("123");
1162-
expect(sendMessage.mock.calls[0]?.[1]).toContain("Hello there");
1163-
expectRecordFields(sendMessage.mock.calls[0]?.[2], { disable_notification: true });
1161+
expect(sendMessage.mock.calls.at(0)?.[0]).toBe("123");
1162+
expect(sendMessage.mock.calls.at(0)?.[1]).toContain("Hello there");
1163+
expectRecordFields(mockCallArg(sendMessage, 0, 2), { disable_notification: true });
11641164
});
11651165

11661166
it("voice fallback applies reply-to only on first chunk when replyToMode is first", async () => {
@@ -1198,7 +1198,7 @@ describe("deliverReplies", () => {
11981198

11991199
expect(sendVoice).toHaveBeenCalledTimes(1);
12001200
expect(sendMessage.mock.calls.length).toBeGreaterThanOrEqual(2);
1201-
expectRecordFields(sendMessage.mock.calls[0][2], {
1201+
expectRecordFields(mockCallArg(sendMessage, 0, 2), {
12021202
reply_parameters: {
12031203
message_id: 77,
12041204
quote: "quoted context",
@@ -1208,9 +1208,9 @@ describe("deliverReplies", () => {
12081208
inline_keyboard: [[{ text: "Ack", callback_data: "ack" }]],
12091209
},
12101210
});
1211-
expect(sendMessage.mock.calls[1][2]).not.toHaveProperty("reply_to_message_id", 77);
1212-
expect(sendMessage.mock.calls[1][2]).not.toHaveProperty("reply_parameters");
1213-
expect(sendMessage.mock.calls[1][2]).not.toHaveProperty("reply_markup");
1211+
expect(mockCallArg(sendMessage, 1, 2)).not.toHaveProperty("reply_to_message_id", 77);
1212+
expect(mockCallArg(sendMessage, 1, 2)).not.toHaveProperty("reply_parameters");
1213+
expect(mockCallArg(sendMessage, 1, 2)).not.toHaveProperty("reply_markup");
12141214
});
12151215

12161216
it("rethrows non-VOICE_MESSAGES_FORBIDDEN errors from sendVoice", async () => {
@@ -1255,12 +1255,12 @@ describe("deliverReplies", () => {
12551255

12561256
expect(sendMessage.mock.calls.length).toBeGreaterThanOrEqual(2);
12571257
// First chunk should have reply_to_message_id
1258-
expectRecordFields(sendMessage.mock.calls[0][2], {
1258+
expectRecordFields(mockCallArg(sendMessage, 0, 2), {
12591259
reply_to_message_id: 700,
12601260
allow_sending_without_reply: true,
12611261
});
12621262
// Second chunk should NOT have reply_to_message_id
1263-
expect(sendMessage.mock.calls[1][2]).not.toHaveProperty("reply_to_message_id");
1263+
expect(mockCallArg(sendMessage, 1, 2)).not.toHaveProperty("reply_to_message_id");
12641264
});
12651265

12661266
it("replyToMode 'all' applies reply-to to every text chunk", async () => {
@@ -1314,12 +1314,12 @@ describe("deliverReplies", () => {
13141314

13151315
expect(sendPhoto).toHaveBeenCalledTimes(2);
13161316
// First media should have reply_to_message_id
1317-
expectRecordFields(sendPhoto.mock.calls[0][2], {
1317+
expectRecordFields(mockCallArg(sendPhoto, 0, 2), {
13181318
reply_to_message_id: 900,
13191319
allow_sending_without_reply: true,
13201320
});
13211321
// Second media should NOT have reply_to_message_id
1322-
expect(sendPhoto.mock.calls[1][2]).not.toHaveProperty("reply_to_message_id");
1322+
expect(mockCallArg(sendPhoto, 1, 2)).not.toHaveProperty("reply_to_message_id");
13231323
});
13241324

13251325
it("pins the first delivered text message when telegram pin is requested", async () => {

0 commit comments

Comments
 (0)