Skip to content

Commit aa97993

Browse files
committed
test: guard telegram bot mock calls
1 parent 2d00bed commit aa97993

1 file changed

Lines changed: 63 additions & 44 deletions

File tree

extensions/telegram/src/bot.create-telegram-bot.test.ts

Lines changed: 63 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -186,7 +186,10 @@ function expectRecordFields(
186186
}
187187

188188
function getBotCtorOptions(callIndex = 0): Record<string, unknown> {
189-
const call = requireValue(botCtorSpy.mock.calls[callIndex], `bot constructor call ${callIndex}`);
189+
const call = requireValue(
190+
botCtorSpy.mock.calls.at(callIndex),
191+
`bot constructor call ${callIndex}`,
192+
);
190193
expect(call[0]).toBe("tok");
191194
return requireRecord(call[1], `bot constructor options ${callIndex}`);
192195
}
@@ -246,7 +249,7 @@ describe("createTelegramBot", () => {
246249
const catchMock = bot.catch as unknown as {
247250
mock: { calls: Array<[(err: unknown) => void]> };
248251
};
249-
const errorHandler = catchMock.mock.calls[0]?.[0];
252+
const errorHandler = catchMock.mock.calls.at(0)?.[0];
250253

251254
expect(errorHandler).toBeTypeOf("function");
252255
errorHandler?.(new Error("handler boom"));
@@ -265,7 +268,7 @@ describe("createTelegramBot", () => {
265268
const fetchImpl = resolveTelegramFetch();
266269
expect(fetchImpl).toBeTypeOf("function");
267270
expect(fetchImpl).not.toBe(fetchSpy);
268-
const clientFetch = (botCtorSpy.mock.calls[0]?.[1] as { client?: { fetch?: unknown } })
271+
const clientFetch = (botCtorSpy.mock.calls.at(0)?.[1] as { client?: { fetch?: unknown } })
269272
?.client?.fetch;
270273
expect(clientFetch).toBeTypeOf("function");
271274
expect(clientFetch).not.toBe(fetchSpy);
@@ -753,7 +756,7 @@ describe("createTelegramBot", () => {
753756
});
754757

755758
expect(replySpy).toHaveBeenCalledTimes(1);
756-
const payload = replySpy.mock.calls[0][0];
759+
const payload = requireValue(replySpy.mock.calls.at(0), "replySpy call")[0];
757760
expect(payload.Body).toContain("cmd:option_a");
758761
expect(answerCallbackQuerySpy).toHaveBeenCalledWith("cbq-1");
759762
});
@@ -825,7 +828,9 @@ describe("createTelegramBot", () => {
825828
});
826829

827830
expect(replySpy).toHaveBeenCalledTimes(1);
828-
expect(replySpy.mock.calls[0][0].Body).toContain("Multi-select submitted: env|prod");
831+
expect(requireValue(replySpy.mock.calls.at(0), "replySpy call")[0].Body).toContain(
832+
"Multi-select submitted: env|prod",
833+
);
829834
});
830835

831836
it("submits OC_SELECT values as a synthetic inbound message and clears buttons", async () => {
@@ -859,7 +864,9 @@ describe("createTelegramBot", () => {
859864
reply_markup: { inline_keyboard: [] },
860865
});
861866
expect(replySpy).toHaveBeenCalledTimes(1);
862-
expect(replySpy.mock.calls[0][0].Body).toContain("Single-select submitted: env|canary");
867+
expect(requireValue(replySpy.mock.calls.at(0), "replySpy call")[0].Body).toContain(
868+
"Single-select submitted: env|canary",
869+
);
863870
});
864871

865872
it("preserves native command source for prefixed callback_query payloads", async () => {
@@ -894,7 +901,7 @@ describe("createTelegramBot", () => {
894901
});
895902

896903
expect(replySpy).toHaveBeenCalledTimes(1);
897-
const payload = replySpy.mock.calls[0][0];
904+
const payload = requireValue(replySpy.mock.calls.at(0), "replySpy call")[0];
898905
expect(payload.CommandBody).toBe("/fast status");
899906
expect(payload.CommandSource).toBe("native");
900907
expect(answerCallbackQuerySpy).toHaveBeenCalledWith("cbq-native-1");
@@ -976,7 +983,7 @@ describe("createTelegramBot", () => {
976983
});
977984

978985
expect(replySpy).toHaveBeenCalledTimes(1);
979-
const payload = replySpy.mock.calls[0][0];
986+
const payload = requireValue(replySpy.mock.calls.at(0), "replySpy call")[0];
980987
const expectedTimestamp = formatEnvelopeTimestamp(new Date("2025-01-09T00:00:00Z"));
981988
const timestampPattern = escapeRegExp(expectedTimestamp);
982989
expect(payload.Body).toMatch(
@@ -1043,12 +1050,16 @@ describe("createTelegramBot", () => {
10431050

10441051
expect(replySpy, testCase.name).not.toHaveBeenCalled();
10451052
expect(sendMessageSpy, testCase.name).toHaveBeenCalledTimes(testCase.expectedSendCount);
1046-
expect(sendMessageSpy.mock.calls[0]?.[0], testCase.name).toBe(1234);
1047-
const pairingText = String(sendMessageSpy.mock.calls[0]?.[1]);
1053+
expect(sendMessageSpy.mock.calls.at(0)?.[0], testCase.name).toBe(1234);
1054+
const pairingText = String(sendMessageSpy.mock.calls.at(0)?.[1]);
10481055
expect(pairingText, testCase.name).toContain(`Your Telegram user id: ${senderId}`);
10491056
expect(pairingText, testCase.name).toContain("Pairing code:");
10501057
expect(pairingText, testCase.name).toContain("openclaw pairing approve telegram");
1051-
expectRecordFields(sendMessageSpy.mock.calls[0]?.[2], { parse_mode: "HTML" }, testCase.name);
1058+
expectRecordFields(
1059+
sendMessageSpy.mock.calls.at(0)?.[2],
1060+
{ parse_mode: "HTML" },
1061+
testCase.name,
1062+
);
10521063
}
10531064
});
10541065

@@ -1125,11 +1136,11 @@ describe("createTelegramBot", () => {
11251136
expect(getFileSpy).not.toHaveBeenCalled();
11261137
expect(fetchSpy).not.toHaveBeenCalled();
11271138
expect(sendMessageSpy).toHaveBeenCalledTimes(1);
1128-
const pairingText = String(sendMessageSpy.mock.calls[0]?.[1]);
1139+
const pairingText = String(sendMessageSpy.mock.calls.at(0)?.[1]);
11291140
expect(pairingText).toContain("Pairing code:");
11301141
expect(pairingText).toContain("<pre><code>");
11311142
expectRecordFields(
1132-
sendMessageSpy.mock.calls[0]?.[2],
1143+
sendMessageSpy.mock.calls.at(0)?.[2],
11331144
{ parse_mode: "HTML" },
11341145
"pairing reply options",
11351146
);
@@ -1247,11 +1258,11 @@ describe("createTelegramBot", () => {
12471258
expect(getFileSpy).not.toHaveBeenCalled();
12481259
expect(fetchSpy).not.toHaveBeenCalled();
12491260
expect(sendMessageSpy).toHaveBeenCalledTimes(1);
1250-
const pairingText = String(sendMessageSpy.mock.calls[0]?.[1]);
1261+
const pairingText = String(sendMessageSpy.mock.calls.at(0)?.[1]);
12511262
expect(pairingText).toContain("Pairing code:");
12521263
expect(pairingText).toContain("<pre><code>");
12531264
expectRecordFields(
1254-
sendMessageSpy.mock.calls[0]?.[2],
1265+
sendMessageSpy.mock.calls.at(0)?.[2],
12551266
{ parse_mode: "HTML" },
12561267
"album pairing reply options",
12571268
);
@@ -2004,7 +2015,7 @@ describe("createTelegramBot", () => {
20042015
});
20052016

20062017
expect(replySpy).toHaveBeenCalledTimes(1);
2007-
const payload = replySpy.mock.calls[0][0];
2018+
const payload = requireValue(replySpy.mock.calls.at(0), "replySpy call")[0];
20082019
expect(payload.AccountId).toBe("opie");
20092020
expect(payload.SessionKey).toBe("agent:opie:main");
20102021
});
@@ -2060,14 +2071,14 @@ describe("createTelegramBot", () => {
20602071

20612072
await sendDm(42, "hello one");
20622073
expect(replySpy).toHaveBeenCalledTimes(1);
2063-
expect(replySpy.mock.calls[0]?.[0].AccountId).toBe("opie");
2064-
expect(replySpy.mock.calls[0]?.[0].SessionKey).toContain("agent:agent-a:");
2074+
expect(replySpy.mock.calls.at(0)?.[0].AccountId).toBe("opie");
2075+
expect(replySpy.mock.calls.at(0)?.[0].SessionKey).toContain("agent:agent-a:");
20652076

20662077
boundAgentId = "agent-b";
20672078
await sendDm(43, "hello two");
20682079
expect(replySpy).toHaveBeenCalledTimes(2);
2069-
expect(replySpy.mock.calls[1]?.[0].AccountId).toBe("opie");
2070-
expect(replySpy.mock.calls[1]?.[0].SessionKey).toContain("agent:agent-b:");
2080+
expect(replySpy.mock.calls.at(1)?.[0].AccountId).toBe("opie");
2081+
expect(replySpy.mock.calls.at(1)?.[0].SessionKey).toContain("agent:agent-b:");
20712082
});
20722083

20732084
it("reloads topic agent overrides between messages without recreating the bot", async () => {
@@ -2113,12 +2124,12 @@ describe("createTelegramBot", () => {
21132124

21142125
await sendTopicMessage(301);
21152126
expect(replySpy).toHaveBeenCalledTimes(1);
2116-
expect(replySpy.mock.calls[0]?.[0].SessionKey).toContain("agent:topic-a:");
2127+
expect(replySpy.mock.calls.at(0)?.[0].SessionKey).toContain("agent:topic-a:");
21172128

21182129
topicAgentId = "topic-b";
21192130
await sendTopicMessage(302);
21202131
expect(replySpy).toHaveBeenCalledTimes(2);
2121-
expect(replySpy.mock.calls[1]?.[0].SessionKey).toContain("agent:topic-b:");
2132+
expect(replySpy.mock.calls.at(1)?.[0].SessionKey).toContain("agent:topic-b:");
21222133
});
21232134

21242135
it("routes non-default account DMs to the per-account fallback session without explicit bindings", async () => {
@@ -2158,7 +2169,7 @@ describe("createTelegramBot", () => {
21582169
});
21592170

21602171
expect(replySpy).toHaveBeenCalledTimes(1);
2161-
const payload = replySpy.mock.calls[0]?.[0];
2172+
const payload = requireValue(replySpy.mock.calls.at(0), "reply call")[0];
21622173
expect(payload.AccountId).toBe("opie");
21632174
expect(payload.SessionKey).toContain("agent:main:telegram:opie:");
21642175
});
@@ -2342,7 +2353,7 @@ describe("createTelegramBot", () => {
23422353
},
23432354
});
23442355
expect(replySpy).toHaveBeenCalledTimes(1);
2345-
const payload = replySpy.mock.calls[0][0];
2356+
const payload = requireValue(replySpy.mock.calls.at(0), "replySpy call")[0];
23462357
expect(payload.SessionKey).toContain(testCase.expectedSessionKeyFragment);
23472358
expect(payload.BodyForAgent).toBe(testCase.text);
23482359
expect(payload.BodyForAgent).not.toContain("t.me/c/");
@@ -2375,7 +2386,7 @@ describe("createTelegramBot", () => {
23752386
});
23762387

23772388
expect(sendAnimationSpy).toHaveBeenCalledTimes(1);
2378-
const animationCall = requireValue(sendAnimationSpy.mock.calls[0], "animation send call");
2389+
const animationCall = requireValue(sendAnimationSpy.mock.calls.at(0), "animation send call");
23792390
expect(animationCall[0]).toBe("1234");
23802391
requireValue(animationCall[1], "animation payload");
23812392
expect(animationCall[2]).toEqual({
@@ -2385,7 +2396,7 @@ describe("createTelegramBot", () => {
23852396
});
23862397
expect(sendPhotoSpy).not.toHaveBeenCalled();
23872398
expect(loadWebMedia).toHaveBeenCalledTimes(1);
2388-
expect(loadWebMedia.mock.calls[0]?.[0]).toBe("https://example.com/fun");
2399+
expect(loadWebMedia.mock.calls.at(0)?.[0]).toBe("https://example.com/fun");
23892400
});
23902401

23912402
function resetHarnessSpies() {
@@ -2461,7 +2472,7 @@ describe("createTelegramBot", () => {
24612472
});
24622473

24632474
expect(replySpy.mock.calls.length, testCase.name).toBe(1);
2464-
const payload = replySpy.mock.calls[0][0];
2475+
const payload = requireValue(replySpy.mock.calls.at(0), "replySpy call")[0];
24652476
expect(payload.WasMentioned, testCase.name).toBe(true);
24662477
if (testCase.assertEnvelope) {
24672478
expect(payload.SenderName).toBe("Ada");
@@ -2507,7 +2518,7 @@ describe("createTelegramBot", () => {
25072518
});
25082519

25092520
expect(replySpy).toHaveBeenCalledTimes(1);
2510-
const payload = replySpy.mock.calls[0][0];
2521+
const payload = requireValue(replySpy.mock.calls.at(0), "replySpy call")[0];
25112522
expect(payload.SenderName).toBe("Ada Lovelace");
25122523
expect(payload.SenderId).toBe("99");
25132524
expect(payload.SenderUsername).toBe("ada");
@@ -2604,7 +2615,7 @@ describe("createTelegramBot", () => {
26042615

26052616
expect(replySpy.mock.calls.length, testCase.name).toBe(testCase.expectedReplyCount);
26062617
if (testCase.expectedWasMentioned != null) {
2607-
const payload = replySpy.mock.calls[0][0];
2618+
const payload = requireValue(replySpy.mock.calls.at(0), "replySpy call")[0];
26082619
expect(payload.WasMentioned, testCase.name).toBe(testCase.expectedWasMentioned);
26092620
}
26102621
}
@@ -2626,7 +2637,7 @@ describe("createTelegramBot", () => {
26262637
});
26272638

26282639
expect(replySpy).toHaveBeenCalledTimes(1);
2629-
const payload = replySpy.mock.calls[0][0];
2640+
const payload = requireValue(replySpy.mock.calls.at(0), "replySpy call")[0];
26302641
expect(payload.Body).toContain("[Reply chain - nearest first]");
26312642
expect(payload.Body).toContain("[1. Ada id:9001]");
26322643
expect(payload.Body).toContain("Can you summarize this?");
@@ -2874,7 +2885,7 @@ describe("createTelegramBot", () => {
28742885
await handler(makeForumGroupMessageCtx({ threadId: testCase.threadId }));
28752886

28762887
expect(sendMessageSpy.mock.calls.length, testCase.name).toBe(1);
2877-
const sendParams = sendMessageSpy.mock.calls[0]?.[2] as { message_thread_id?: number };
2888+
const sendParams = sendMessageSpy.mock.calls.at(0)?.[2] as { message_thread_id?: number };
28782889
if (testCase.expectedMessageThreadId == null) {
28792890
expect(sendParams?.message_thread_id, testCase.name).toBeUndefined();
28802891
} else {
@@ -3078,7 +3089,9 @@ describe("createTelegramBot", () => {
30783089
});
30793090

30803091
expect(sendMessageSpy).toHaveBeenCalledTimes(1);
3081-
expect(sendMessageSpy.mock.calls[0][1]).toBe("PFX final reply");
3092+
expect(requireValue(sendMessageSpy.mock.calls.at(0), "sendMessageSpy call")[1]).toBe(
3093+
"PFX final reply",
3094+
);
30823095
});
30833096

30843097
it("sends Codex usage-limit reset details as the Telegram reply body", async () => {
@@ -3104,11 +3117,15 @@ describe("createTelegramBot", () => {
31043117
});
31053118

31063119
expect(sendMessageSpy).toHaveBeenCalledTimes(1);
3107-
expect(String(sendMessageSpy.mock.calls[0][0])).toBe("5");
3108-
expect(sendMessageSpy.mock.calls[0][1]).toBe(codexRateLimitText);
3109-
expect(String(sendMessageSpy.mock.calls[0][1])).not.toContain(
3110-
"All models are temporarily rate-limited",
3120+
expect(String(requireValue(sendMessageSpy.mock.calls.at(0), "sendMessageSpy call")[0])).toBe(
3121+
"5",
3122+
);
3123+
expect(requireValue(sendMessageSpy.mock.calls.at(0), "sendMessageSpy call")[1]).toBe(
3124+
codexRateLimitText,
31113125
);
3126+
expect(
3127+
String(requireValue(sendMessageSpy.mock.calls.at(0), "sendMessageSpy call")[1]),
3128+
).not.toContain("All models are temporarily rate-limited");
31123129
});
31133130

31143131
it("honors threaded replies for replyToMode=first/all", async () => {
@@ -3271,14 +3288,16 @@ describe("createTelegramBot", () => {
32713288

32723289
createTelegramBot({ token: "tok" });
32733290
expect(commandSpy).toHaveBeenCalled();
3274-
const handler = commandSpy.mock.calls[0][1] as (ctx: Record<string, unknown>) => Promise<void>;
3291+
const handler = requireValue(commandSpy.mock.calls.at(0), "commandSpy call")[1] as (
3292+
ctx: Record<string, unknown>,
3293+
) => Promise<void>;
32753294

32763295
await handler({
32773296
...makeForumGroupMessageCtx({ threadId: 99, text: "/status" }),
32783297
match: "",
32793298
});
32803299

3281-
const statusCall = requireValue(sendMessageSpy.mock.calls[0], "status reply call");
3300+
const statusCall = requireValue(sendMessageSpy.mock.calls.at(0), "status reply call");
32823301
expect(statusCall[0]).toBe("-1001234567890");
32833302
expect(statusCall[1]).toBeTypeOf("string");
32843303
expectRecordFields(
@@ -3334,12 +3353,12 @@ describe("createTelegramBot", () => {
33343353

33353354
await invokeStatus(401);
33363355
expect(replySpy).toHaveBeenCalledTimes(1);
3337-
expect(replySpy.mock.calls[0]?.[0].SessionKey).toContain("agent:agent-a:");
3356+
expect(replySpy.mock.calls.at(0)?.[0].SessionKey).toContain("agent:agent-a:");
33383357

33393358
boundAgentId = "agent-b";
33403359
await invokeStatus(402);
33413360
expect(replySpy).toHaveBeenCalledTimes(2);
3342-
expect(replySpy.mock.calls[1]?.[0].SessionKey).toContain("agent:agent-b:");
3361+
expect(replySpy.mock.calls.at(1)?.[0].SessionKey).toContain("agent:agent-b:");
33433362
});
33443363
it("skips tool summaries for native slash commands", async () => {
33453364
commandSpy.mockClear();
@@ -3378,7 +3397,7 @@ describe("createTelegramBot", () => {
33783397
});
33793398

33803399
expect(sendMessageSpy).toHaveBeenCalledTimes(1);
3381-
expect(sendMessageSpy.mock.calls[0]?.[1]).toContain("final reply");
3400+
expect(sendMessageSpy.mock.calls.at(0)?.[1]).toContain("final reply");
33823401
});
33833402
it("dedupes duplicate message updates by update_id", async () => {
33843403
onSpy.mockReset();
@@ -3665,10 +3684,10 @@ describe("createTelegramBot", () => {
36653684

36663685
expect(buildModelsProviderDataMock).toHaveBeenCalledTimes(2);
36673686
expect(editMessageTextSpy).toHaveBeenCalledTimes(1);
3668-
expect(editMessageTextSpy.mock.calls[0]?.[2]).toContain("Select a provider:");
3687+
expect(editMessageTextSpy.mock.calls.at(0)?.[2]).toContain("Select a provider:");
36693688
expect(
36703689
(
3671-
editMessageTextSpy.mock.calls[0]?.[3] as {
3690+
editMessageTextSpy.mock.calls.at(0)?.[3] as {
36723691
reply_markup?: { inline_keyboard?: unknown[][] };
36733692
}
36743693
)?.reply_markup?.inline_keyboard?.[0]?.[0],
@@ -3936,7 +3955,7 @@ describe("createTelegramBot", () => {
39363955

39373956
expect(editMessageReplyMarkupSpy).toHaveBeenCalledTimes(1);
39383957
expect(sendMessageSpy).toHaveBeenCalledTimes(1);
3939-
expect(sendMessageSpy.mock.calls[0]?.[1]).toContain("plugin bind approval");
3958+
expect(sendMessageSpy.mock.calls.at(0)?.[1]).toContain("plugin bind approval");
39403959
});
39413960

39423961
it("retries exec approval callbacks after a bubbled resolution failure", async () => {

0 commit comments

Comments
 (0)