From fb15b32df2447589ccd258b86f9dda10a9daef77 Mon Sep 17 00:00:00 2001 From: Peter Steinberger Date: Tue, 12 May 2026 01:52:52 +0100 Subject: [PATCH] test: guard msteams oauth mock calls --- .../src/monitor-handler/inbound-media.test.ts | 20 +++++++++++++++++-- extensions/msteams/src/oauth.test.ts | 12 +++++++++-- extensions/msteams/src/outbound.test.ts | 19 +++++++++++++++++- 3 files changed, 46 insertions(+), 5 deletions(-) diff --git a/extensions/msteams/src/monitor-handler/inbound-media.test.ts b/extensions/msteams/src/monitor-handler/inbound-media.test.ts index 26270eb2e22..98afe4f05ea 100644 --- a/extensions/msteams/src/monitor-handler/inbound-media.test.ts +++ b/extensions/msteams/src/monitor-handler/inbound-media.test.ts @@ -34,6 +34,22 @@ const baseParams = { log: { debug: vi.fn() }, }; +function firstGraphMediaCall() { + const [call] = vi.mocked(downloadMSTeamsGraphMedia).mock.calls; + if (!call) { + throw new Error("expected Graph media download call"); + } + return call[0]; +} + +function firstBotFrameworkAttachmentCall() { + const [call] = vi.mocked(downloadMSTeamsBotFrameworkAttachments).mock.calls; + if (!call) { + throw new Error("expected Bot Framework attachment download call"); + } + return call[0]; +} + describe("resolveMSTeamsInboundMedia graph fallback trigger", () => { it("triggers Graph fallback when HTML contains tags", async () => { vi.mocked(downloadMSTeamsAttachments).mockResolvedValue([]); @@ -133,7 +149,7 @@ describe("resolveMSTeamsInboundMedia graph fallback trigger", () => { ], }); - const call = vi.mocked(downloadMSTeamsGraphMedia).mock.calls[0]?.[0]; + const call = firstGraphMediaCall(); // The monitor handler's logger is forwarded so graph.ts can report // message fetch failures instead of swallowing them (#51749). expect(call?.logger).toBe(log); @@ -187,7 +203,7 @@ describe("resolveMSTeamsInboundMedia bot framework DM routing", () => { }); expect(downloadMSTeamsBotFrameworkAttachments).toHaveBeenCalledTimes(1); - const call = vi.mocked(downloadMSTeamsBotFrameworkAttachments).mock.calls[0]?.[0]; + const call = firstBotFrameworkAttachmentCall(); expect(call?.serviceUrl).toBe(dmParams.serviceUrl); expect(call?.attachmentIds).toEqual(["att-0", "att-1"]); expect(downloadMSTeamsGraphMedia).not.toHaveBeenCalled(); diff --git a/extensions/msteams/src/oauth.test.ts b/extensions/msteams/src/oauth.test.ts index e0f2afa7d14..0af342b937e 100644 --- a/extensions/msteams/src/oauth.test.ts +++ b/extensions/msteams/src/oauth.test.ts @@ -38,6 +38,14 @@ function responseJson(body: unknown, status = 200): Response { }); } +function firstFetchCall(fetchSpy: ReturnType): [string, RequestInit] { + const [call] = fetchSpy.mock.calls; + if (!call) { + throw new Error("expected fetch call"); + } + return call as [string, RequestInit]; +} + describe("generatePkce", () => { it("produces a 64-char hex verifier and a base64url SHA-256 challenge", () => { const { verifier, challenge } = generatePkce(); @@ -191,7 +199,7 @@ describe("exchangeMSTeamsCodeForTokens", () => { // Verify the request was well-formed expect(fetchSpy).toHaveBeenCalledOnce(); - const [url, init] = fetchSpy.mock.calls[0] as [string, RequestInit]; + const [url, init] = firstFetchCall(fetchSpy); expect(url).toBe(buildMSTeamsTokenEndpoint("tenant-1")); const body = new URLSearchParams(init.body as string); expect(body.get("client_id")).toBe("client-1"); @@ -259,7 +267,7 @@ describe("refreshMSTeamsDelegatedTokens", () => { expect(tokens.expiresAt).toBeGreaterThanOrEqual(now + 3300 * 1000 - 1000); // Verify the request body includes refresh_token grant type - const [, init] = fetchSpy.mock.calls[0] as [string, RequestInit]; + const [, init] = firstFetchCall(fetchSpy); const body = new URLSearchParams(init.body as string); expect(body.get("grant_type")).toBe("refresh_token"); expect(body.get("refresh_token")).toBe("original-rt"); diff --git a/extensions/msteams/src/outbound.test.ts b/extensions/msteams/src/outbound.test.ts index 7fb690b7231..3a70a68c048 100644 --- a/extensions/msteams/src/outbound.test.ts +++ b/extensions/msteams/src/outbound.test.ts @@ -48,6 +48,23 @@ function requireSendPoll(): MSTeamsSendPoll { return sendPoll; } +type PollRecord = Record & { createdAt: string }; + +function firstPollRecord(): PollRecord { + const [call] = mocks.createPoll.mock.calls; + if (!call) { + throw new Error("expected createPoll call"); + } + const [pollRecord] = call; + if (!pollRecord || typeof pollRecord !== "object" || Array.isArray(pollRecord)) { + throw new Error("expected createPoll record"); + } + if (typeof (pollRecord as { createdAt?: unknown }).createdAt !== "string") { + throw new Error("expected createPoll record timestamp"); + } + return pollRecord as PollRecord; +} + describe("msteamsOutbound cfg threading", () => { beforeEach(() => { mocks.sendMessageMSTeams.mockReset(); @@ -138,7 +155,7 @@ describe("msteamsOutbound cfg threading", () => { options: ["Pizza", "Sushi"], maxSelections: 1, }); - const [pollRecord] = mocks.createPoll.mock.calls[0] ?? []; + const pollRecord = firstPollRecord(); expect(pollRecord).toEqual({ id: "poll-1", question: "Snack?",