Files
openclaw/extensions/googlechat/src/api.test.ts
Peter Steinberger cdbed3c9b1 fix(googlechat): land #30965 thread reply option support (@novan)
Landed from contributor PR #30965 by @novan.

Co-authored-by: novan <novan@users.noreply.github.com>
2026-03-02 03:16:48 +00:00

109 lines
3.1 KiB
TypeScript

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;
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" },
});
vi.stubGlobal("fetch", vi.fn().mockResolvedValue(response));
await expect(
downloadGoogleChatMedia({ account, resourceName: "media/123", maxBytes: 10 }),
).rejects.toThrow(/max bytes/i);
});
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" },
});
vi.stubGlobal("fetch", vi.fn().mockResolvedValue(response));
await expect(
downloadGoogleChatMedia({ account, resourceName: "media/123", maxBytes: 10 }),
).rejects.toThrow(/max bytes/i);
});
});
describe("sendGoogleChatMessage", () => {
afterEach(() => {
vi.unstubAllGlobals();
});
it("adds messageReplyOption when sending to an existing thread", async () => {
const fetchMock = vi
.fn()
.mockResolvedValue(
new Response(JSON.stringify({ name: "spaces/AAA/messages/123" }), { status: 200 }),
);
vi.stubGlobal("fetch", fetchMock);
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 = vi
.fn()
.mockResolvedValue(
new Response(JSON.stringify({ name: "spaces/AAA/messages/124" }), { status: 200 }),
);
vi.stubGlobal("fetch", fetchMock);
await sendGoogleChatMessage({
account,
space: "spaces/AAA",
text: "hello",
});
const [url] = fetchMock.mock.calls[0] ?? [];
expect(String(url)).not.toContain("messageReplyOption=");
});
});