import { chmod, mkdir, mkdtemp, readFile, rm, writeFile } from "node:fs/promises"; import { join } from "node:path"; import { tmpdir } from "node:os"; import { describe, expect, it } from "vitest"; import { createFeishuBotCli } from "../../src/feishu/botCli.js"; import type { PrismaClient } from "@prisma/client"; import type { LocalSecretEnvelope } from "../../src/security/secretEnvelope.js"; const itOnLinux = process.platform === "linux" ? it : it.skip; const fakeCredential = { connectionId: "connection-1", organizationId: "org-1", appId: "cli-test-app", appSecret: "cli-test-secret", botOpenId: "ou-test-bot", verificationToken: "verification-token", encryptKey: "encrypt-key", }; describe("Feishu bot CLI adapter", () => { itOnLinux("uses bot identity and writes the CLI result into the workspace", async () => { const root = await mkdtemp(join(tmpdir(), "hub-feishu-bot-cli-test-")); const workspaceDir = join(root, "workspace"); const binary = join(root, "fake-lark-cli"); await mkdir(workspaceDir); await writeFakeCli(binary); try { const cli = createFeishuBotCli({ organizationId: "org-1", prisma: {} as PrismaClient, secretEnvelope: {} as LocalSecretEnvelope, binary, resolveCredential: async () => fakeCredential, }); const result = await cli.downloadResource({ messageId: "message-1", fileKey: "file-1", resourceType: "file", workspaceRoot: root, workspaceDir, workspaceRelativePath: "inbox/resource.bin", maxBytes: 1024, }); await expect(readFile(result, "utf8")).resolves.toBe("resource bytes"); } finally { await rm(root, { recursive: true, force: true }); } }); it("rejects a resource above the configured limit", async () => { const root = await mkdtemp(join(tmpdir(), "hub-feishu-bot-cli-limit-")); const workspaceDir = join(root, "workspace"); const binary = join(root, "fake-lark-cli"); await mkdir(workspaceDir); await writeFakeCli(binary); try { const cli = createFeishuBotCli({ organizationId: "org-1", prisma: {} as PrismaClient, secretEnvelope: {} as LocalSecretEnvelope, binary, resolveCredential: async () => fakeCredential, }); await expect(cli.downloadResource({ messageId: "message-1", fileKey: "file-1", resourceType: "file", workspaceRoot: root, workspaceDir, workspaceRelativePath: "inbox/resource.bin", maxBytes: 4, })).rejects.toMatchObject({ reason: "limit" }); } finally { await rm(root, { recursive: true, force: true }); } }); }); async function writeFakeCli(path: string): Promise { await writeFile(path, `#!/usr/bin/env node import { writeFileSync } from "node:fs"; import { join } from "node:path"; const args = process.argv.slice(2); if (args[0] === "config" && args[1] === "init") { process.stdin.resume(); process.stdin.on("end", () => process.exit(0)); } else if (args.includes("+messages-resources-download")) { const asIndex = args.indexOf("--as"); const outputIndex = args.indexOf("--output"); if (asIndex < 0 || args[asIndex + 1] !== "bot" || outputIndex < 0) process.exit(2); writeFileSync(join(process.cwd(), args[outputIndex + 1]), "resource bytes"); process.exit(0); } else { process.exit(3); } `); await chmod(path, 0o755); }