import Fastify from "fastify"; import { afterAll, beforeEach, describe, expect, it, vi } from "vitest"; import { mintSessionToken, sessionCookieHeader } from "../../src/admin/routes/authRoutes.js"; import { registerAdminPlugin } from "../../src/admin/plugin.js"; import { FeishuReadinessError } from "../../src/connections/feishuReadiness.js"; import { DEFAULT_ORG_ID, prisma, resetDb, testSecretEnvelope } from "./helpers.js"; const SESSION_SECRET = "integration-test-session-secret"; async function buildApp(readinessProbe: (input: { readonly appId: string; readonly appSecret: string; readonly botOpenId: string; }) => Promise) { const app = Fastify({ logger: false }); await registerAdminPlugin(app, { prisma, sessionSecret: SESSION_SECRET, publicBaseUrl: "http://127.0.0.1:8788", feishuAppId: "cli_legacy_auth_only", feishuAppSecret: "legacy-auth-secret", projectWorkspaceRoot: "/tmp/cph-test-workspaces", secretEnvelope: testSecretEnvelope, feishuConnectionReadinessProbe: readinessProbe, cookieSecure: false, }); await app.ready(); return app; } async function seedUser(role: "OWNER" | "ADMIN" | "MEMBER"): Promise { const id = `feishu-${role.toLowerCase()}`; const openId = `ou_feishu_${role.toLowerCase()}`; await prisma.user.create({ data: { id, feishuOpenId: openId, displayName: id } }); await prisma.organizationMembership.create({ data: { organizationId: DEFAULT_ORG_ID, userId: id, role }, }); return mintSessionToken({ userId: id, feishuOpenId: openId }, SESSION_SECRET); } describe("Organization Feishu Application Connection API", () => { beforeEach(async () => { await resetDb(); }); afterAll(async () => { await prisma.$disconnect(); }); it("accepts write-only credentials and returns only redacted metadata", async () => { const token = await seedUser("OWNER"); const readiness = vi.fn(async () => {}); const app = await buildApp(readiness); const credential = { appId: "cli_customer", appSecret: "api-secret-never-return", botOpenId: "ou_customer_bot", verificationToken: "verification-never-return", encryptKey: "encrypt-key-never-return", }; try { const created = await app.inject({ method: "PUT", url: "/api/org/test-default/feishu-application-connection", headers: { cookie: sessionCookieHeader(token), "content-type": "application/json", }, payload: credential, }); expect(created.statusCode).toBe(201); expect(created.json()).toMatchObject({ status: "ACTIVE", activeVersion: 1, keyId: "test-active", }); for (const plaintext of Object.values(credential)) { expect(created.body).not.toContain(plaintext); } expect(readiness).toHaveBeenCalledWith({ appId: credential.appId, appSecret: credential.appSecret, botOpenId: credential.botOpenId, verificationToken: credential.verificationToken, encryptKey: credential.encryptKey, schemaVersion: 1, }); const read = await app.inject({ method: "GET", url: "/api/org/test-default/feishu-application-connection", headers: { cookie: sessionCookieHeader(token) }, }); expect(read.statusCode).toBe(200); expect(read.json()).toEqual({ connection: expect.objectContaining({ activeVersion: 1 }) }); for (const plaintext of Object.values(credential)) { expect(read.body).not.toContain(plaintext); } const disabled = await app.inject({ method: "DELETE", url: "/api/org/test-default/feishu-application-connection", headers: { cookie: sessionCookieHeader(token) }, }); expect(disabled.statusCode).toBe(200); expect(disabled.json()).toMatchObject({ status: "DISABLED", activeVersion: 1 }); for (const plaintext of Object.values(credential)) { expect(disabled.body).not.toContain(plaintext); } } finally { await app.close(); } }); it("rejects members and does not persist credentials rejected by Feishu", async () => { const memberToken = await seedUser("MEMBER"); const app = await buildApp(async () => { throw new FeishuReadinessError( "feishu_readiness_rejected", "Feishu bot identity does not match the configured botOpenId", "provider_rejection", ); }); const payload = { appId: "cli_rejected", appSecret: "rejected-secret", botOpenId: "ou_rejected", }; try { const denied = await app.inject({ method: "PUT", url: "/api/org/test-default/feishu-application-connection", headers: { cookie: sessionCookieHeader(memberToken), "content-type": "application/json", }, payload, }); expect(denied.statusCode).toBe(403); const adminToken = await seedUser("ADMIN"); const rejected = await app.inject({ method: "PUT", url: "/api/org/test-default/feishu-application-connection", headers: { cookie: sessionCookieHeader(adminToken), "content-type": "application/json", }, payload, }); expect(rejected.statusCode).toBe(400); expect(rejected.json()).toMatchObject({ error: { code: "feishu_credential_rejected" } }); await expect(prisma.organizationFeishuApplicationConnection.count()).resolves.toBe(0); await expect(prisma.feishuApplicationCredentialVersion.count()).resolves.toBe(0); } finally { await app.close(); } }); });