import type * as db from "@rakazo/db"; import { describe, expect, it, vi } from "vitest"; import { chooseFocus, markAppConnected } from "./onboarding.js"; import { focusCard } from "./onboarding-intake.js"; const posted = vi.hoisted(() => [] as Array<{ blocks: unknown[] }>); vi.mock("@rakazo/db", async (original) => ({ ...(await original()), createThreadMessageInTransaction: vi.fn(async (_tx, input) => { posted.push(input); return { id: "posted" }; }), appendEventInTransaction: vi.fn(async () => ({ seq: 1 })), })); function fixture(catalog: unknown[]) { posted.length = 0; // A specialist's focus card, waiting for an answer. const pending = vi.fn(async () => [{ id: "choice", blocks: [focusCard()] }]); const tx = { $executeRaw: vi.fn(), message: { findMany: pending, update: vi.fn() }, }; const deps = { prisma: { bot: { findFirst: vi.fn(async () => ({ id: "bot", thread: { id: "thread" } })) }, message: { findMany: pending }, $transaction: vi.fn(async (fn: (tx: unknown) => unknown) => fn(tx)), }, events: { notify: vi.fn() }, connectors: { managedProviders: () => [{ catalog: async () => catalog }] }, } as unknown as Parameters[0]; const actor = { userId: "user", spaceId: "space", email: "user@rakazo.test", isDeploymentOwner: true, }; return { deps, actor, tx }; } describe("onboarding connection suggestions", () => { it("does not invent authorization cards when no connector has an app catalog", async () => { const { deps, actor } = fixture([]); await chooseFocus(deps, actor, "bot", { optionId: "day" }); // The answered card is all that changes; nothing is said around it. expect(posted).toEqual([]); expect(deps.events.notify).toHaveBeenCalled(); }); it("uses the available connector and omits unavailable apps", async () => { const { deps, actor } = fixture([ { connectorId: "pipedream", slug: "slack", name: "Slack", connected: false, logo: null }, ]); await chooseFocus(deps, actor, "bot", { optionId: "day" }); expect( posted .flatMap((message) => message.blocks) .filter((block) => (block as { kind: string }).kind === "app_connect"), ).toEqual([ expect.objectContaining({ connectorId: "pipedream", provider: "slack", name: "Slack" }), ]); // The cards speak for themselves. expect(posted).toHaveLength(1); }); }); it("marks only the authorized connector when provider slugs collide", async () => { const { deps, actor, tx } = fixture([]); const blocks = ["composio", "pipedream"].map((connectorId) => ({ kind: "app_connect", connectorId, provider: "slack", name: "Slack", status: "pending", })); deps.prisma.message = { findMany: vi.fn(async () => [{ id: "cards", blocks }]) } as never; await markAppConnected(deps, actor, "bot", "slack", "pipedream"); expect(tx.message.update).toHaveBeenCalledWith({ where: { id: "cards" }, data: { blocks: [blocks[0], { ...blocks[1], status: "connected" }] }, }); });