import { describe, expect, it, vi } from "vitest"; import { mkdtemp, rm, writeFile } from "node:fs/promises"; import { tmpdir } from "node:os"; import { join } from "node:path"; import { sendFile, sendLongText, sendTextMessage, type FeishuRuntime } from "../../src/feishu/client.js"; describe("Feishu client helpers", () => { it("accepts top-level file_key from the Lark SDK file upload response", async () => { const dir = await mkdtemp(join(tmpdir(), "hub-feishu-client-")); try { const file = join(dir, "student.pdf"); await writeFile(file, "pdf bytes"); const fileCreate = vi.fn(async () => ({ file_key: "file-key-1" })); const messageCreate = vi.fn(async () => ({ data: { message_id: "message-1" } })); const rt = mockRuntime({ fileCreate, messageCreate }); await expect(sendFile(rt, "chat-1", file, "student.pdf")).resolves.toBe("message-1"); expect(messageCreate).toHaveBeenCalledWith({ params: { receive_id_type: "chat_id" }, data: { receive_id: "chat-1", msg_type: "file", content: JSON.stringify({ file_key: "file-key-1" }) }, }); } finally { await rm(dir, { recursive: true, force: true }); } }); it("accepts top-level message_id from message create responses", async () => { const rt = mockRuntime({ fileCreate: vi.fn(), messageCreate: vi.fn(async () => ({ message_id: "message-1" })), }); await expect(sendTextMessage(rt, "chat-1", "hello")).resolves.toBe("message-1"); }); it("sends long text in chunks and returns the last successful message id", async () => { let messageNumber = 0; const messageCreate = vi.fn(async () => ({ data: { message_id: `message-${++messageNumber}` } })); const rt = mockRuntime({ fileCreate: vi.fn(), messageCreate }); await expect(sendLongText(rt, "chat-1", "x".repeat(16_005))).resolves.toBe("message-3"); expect(messageCreate).toHaveBeenCalledTimes(3); const sentTexts = messageCreate.mock.calls.map(([payload]) => { const content = (payload as { data: { content: string } }).data.content; return (JSON.parse(content) as { text: string }).text; }); expect(sentTexts.map((text) => text.length)).toEqual([8000, 8000, 5]); }); }); function mockRuntime(options: { readonly fileCreate: (payload: unknown) => Promise; readonly messageCreate: (payload: unknown) => Promise; }): FeishuRuntime { return { client: { im: { v1: { file: { create: options.fileCreate }, message: { create: options.messageCreate }, }, }, } as unknown as FeishuRuntime["client"], logger: { info() {}, warn() {}, error() {}, debug() {}, fatal() {}, child() { return this; }, level: "silent", } as unknown as FeishuRuntime["logger"], }; }