| import { afterEach, describe, expect, it, vi } from "vitest"; |
| import type { ResolvedGoogleChatAccount } from "./accounts.js"; |
| import { downloadGoogleChatMedia, sendGoogleChatMessage } from "./api.js"; |
|
|
| vi.mock("./auth.js", () => ({ |
| getGoogleChatAccessToken: vi.fn().mockResolvedValue("token"), |
| })); |
|
|
| const account = { |
| accountId: "default", |
| enabled: true, |
| credentialSource: "inline", |
| config: {}, |
| } as ResolvedGoogleChatAccount; |
|
|
| function stubSuccessfulSend(name: string) { |
| const fetchMock = vi |
| .fn() |
| .mockResolvedValue(new Response(JSON.stringify({ name }), { status: 200 })); |
| vi.stubGlobal("fetch", fetchMock); |
| return fetchMock; |
| } |
|
|
| async function expectDownloadToRejectForResponse(response: Response) { |
| vi.stubGlobal("fetch", vi.fn().mockResolvedValue(response)); |
| await expect( |
| downloadGoogleChatMedia({ account, resourceName: "media/123", maxBytes: 10 }), |
| ).rejects.toThrow(/max bytes/i); |
| } |
|
|
| describe("downloadGoogleChatMedia", () => { |
| afterEach(() => { |
| vi.unstubAllGlobals(); |
| }); |
|
|
| it("rejects when content-length exceeds max bytes", async () => { |
| const body = new ReadableStream({ |
| start(controller) { |
| controller.enqueue(new Uint8Array([1, 2, 3])); |
| controller.close(); |
| }, |
| }); |
| const response = new Response(body, { |
| status: 200, |
| headers: { "content-length": "50", "content-type": "application/octet-stream" }, |
| }); |
| await expectDownloadToRejectForResponse(response); |
| }); |
|
|
| it("rejects when streamed payload exceeds max bytes", async () => { |
| const chunks = [new Uint8Array(6), new Uint8Array(6)]; |
| let index = 0; |
| const body = new ReadableStream({ |
| pull(controller) { |
| if (index < chunks.length) { |
| controller.enqueue(chunks[index++]); |
| } else { |
| controller.close(); |
| } |
| }, |
| }); |
| const response = new Response(body, { |
| status: 200, |
| headers: { "content-type": "application/octet-stream" }, |
| }); |
| await expectDownloadToRejectForResponse(response); |
| }); |
| }); |
|
|
| describe("sendGoogleChatMessage", () => { |
| afterEach(() => { |
| vi.unstubAllGlobals(); |
| }); |
|
|
| it("adds messageReplyOption when sending to an existing thread", async () => { |
| const fetchMock = stubSuccessfulSend("spaces/AAA/messages/123"); |
|
|
| await sendGoogleChatMessage({ |
| account, |
| space: "spaces/AAA", |
| text: "hello", |
| thread: "spaces/AAA/threads/xyz", |
| }); |
|
|
| const [url, init] = fetchMock.mock.calls[0] ?? []; |
| expect(String(url)).toContain("messageReplyOption=REPLY_MESSAGE_FALLBACK_TO_NEW_THREAD"); |
| expect(JSON.parse(String(init?.body))).toMatchObject({ |
| text: "hello", |
| thread: { name: "spaces/AAA/threads/xyz" }, |
| }); |
| }); |
|
|
| it("does not set messageReplyOption for non-thread sends", async () => { |
| const fetchMock = stubSuccessfulSend("spaces/AAA/messages/124"); |
|
|
| await sendGoogleChatMessage({ |
| account, |
| space: "spaces/AAA", |
| text: "hello", |
| }); |
|
|
| const [url] = fetchMock.mock.calls[0] ?? []; |
| expect(String(url)).not.toContain("messageReplyOption="); |
| }); |
| }); |
|
|