import { describe, expect, it } from "vitest"; import { readFeishuContext } from "../../src/feishu/read.js"; import type { FeishuRuntime } from "../../src/feishu/client.js"; describe("readFeishuContext", () => { it("compacts Feishu message.get replies with parent_id, thread_id, and body.content", async () => { const rt = { client: { im: { v1: { message: { get: async () => ({ data: { items: [ { message_id: "m-child", root_id: "m-root", parent_id: "m-parent", thread_id: "thread-1", chat_id: "chat-1", msg_type: "text", body: { content: JSON.stringify({ text: "parent text" }) }, }, ], }, }), list: async () => ({ data: { items: [] } }), }, }, }, }, logger: { warn() {}, info() {}, error() {}, debug() {} }, } as unknown as FeishuRuntime; const raw = await readFeishuContext( { chat_id: "chat-1", anchor: "reply", id: "m-child" }, { runId: "run-1", projectId: "proj-1", boundChatId: "chat-1", workspaceDir: "/tmp/proj-1" }, rt, ); expect(JSON.parse(raw)).toMatchObject({ message_id: "m-child", content: JSON.stringify({ text: "parent text" }), parent_id: "m-parent", parent_message_id: "m-parent", root_id: "m-root", thread_id: "thread-1", }); }); it("refuses a single-message result from another chat", async () => { const rt = runtimeWithMessages({ get: [{ message_id: "m-other", chat_id: "chat-other", body: { content: "secret" } }], list: [], }); const raw = await readFeishuContext( { chat_id: "chat-1", anchor: "trigger_message", id: "m-other" }, { runId: "run-1", projectId: "proj-1", boundChatId: "chat-1", workspaceDir: "/tmp/proj-1" }, rt, ); expect(JSON.parse(raw)).toMatchObject({ error: expect.stringContaining("bound chat") }); expect(raw).not.toContain("secret"); }); it("refuses an entire thread result when any item lacks the bound chat id", async () => { const rt = runtimeWithMessages({ get: [], list: [ { message_id: "m-allowed", chat_id: "chat-1", body: { content: "allowed" } }, { message_id: "m-unscoped", body: { content: "must-not-leak" } }, ], }); const raw = await readFeishuContext( { chat_id: "chat-1", anchor: "thread", id: "thread-1" }, { runId: "run-1", projectId: "proj-1", boundChatId: "chat-1", workspaceDir: "/tmp/proj-1" }, rt, ); expect(JSON.parse(raw)).toMatchObject({ error: expect.stringContaining("bound chat") }); expect(raw).not.toContain("must-not-leak"); }); }); function runtimeWithMessages(input: { get: Array>; list: Array>; }): FeishuRuntime { return { client: { im: { v1: { message: { get: async () => ({ data: { items: input.get } }), list: async () => ({ data: { items: input.list } }), }, }, }, }, logger: { warn() {}, info() {}, error() {}, debug() {} }, } as unknown as FeishuRuntime; }