109 lines · 3.7 KB · TypeScript
RawDownload
import { describe, expect, it } from "@effect/vitest";import type { EffectivePolicy, Owner } from "@executor-js/sdk/shared"; import { buildAccountGroups, type ToolSummary } from "./tool-tree"; // A trivial always-approve plugin default — the policy field is required on a// ToolSummary but irrelevant to grouping.const approve: EffectivePolicy = {  action: "approve",  source: "plugin-default",  pattern: "*",}; const tool = (input: {  readonly id: string;  readonly name: string;  readonly owner?: Owner;  readonly connection?: string;}): ToolSummary => ({  id: input.id,  name: input.name,  description: undefined,  policy: approve,  owner: input.owner,  connection: input.connection,}); describe("buildAccountGroups", () => {  it("groups tools by (owner, connection) and badges each section by owner", () => {    const groups = buildAccountGroups([      tool({        id: "u:axiom-mcp:axiom.query",        name: "axiom.query",        owner: "user",        connection: "axiom-mcp",      }),      tool({        id: "o:vercel-api:vercel.deploy",        name: "vercel.deploy",        owner: "org",        connection: "vercel-api",      }),    ]);     // Workspace (org) sorts before Personal (user).    expect(groups.map((g) => g.label)).toEqual(["Workspace · vercel-api", "Personal · axiom-mcp"]);    expect(groups.map((g) => g.owner)).toEqual(["org", "user"]);    expect(groups[0]!.tools.map((t) => t.name)).toEqual(["vercel.deploy"]);    expect(groups[1]!.tools.map((t) => t.name)).toEqual(["axiom.query"]);  });   it("Axiom regression: a user-owned connection's tools render in the merged, account-grouped view", () => {    // 18 user-owned tools, zero org rows — the case that showed 0 under the old    // owner=org default. Here they all land in one Personal section.    const tools = Array.from({ length: 18 }, (_, i) =>      tool({        id: `u:axiom-mcp:axiom.t${i}`,        name: `axiom.t${i}`,        owner: "user",        connection: "axiom-mcp",      }),    );    const groups = buildAccountGroups(tools);     expect(groups).toHaveLength(1);    expect(groups[0]!.owner).toBe("user");    expect(groups[0]!.label).toBe("Personal · axiom-mcp");    expect(groups[0]!.tools).toHaveLength(18);  });   it("does NOT dedupe the same tool name across two connections — it appears under each", () => {    const groups = buildAccountGroups([      tool({ id: "o:prod:vercel.deploy", name: "vercel.deploy", owner: "org", connection: "prod" }),      tool({        id: "u:scratch:vercel.deploy",        name: "vercel.deploy",        owner: "user",        connection: "scratch",      }),    ]);     expect(groups).toHaveLength(2);    // Same tool name, once per account.    expect(groups.flatMap((g) => g.tools).map((t) => t.name)).toEqual([      "vercel.deploy",      "vercel.deploy",    ]);    expect(groups.map((g) => g.label)).toEqual(["Workspace · prod", "Personal · scratch"]);  });   it("sorts multiple connections within an owner by connection name", () => {    const groups = buildAccountGroups([      tool({ id: "o:zeta:i.a", name: "i.a", owner: "org", connection: "zeta" }),      tool({ id: "o:alpha:i.b", name: "i.b", owner: "org", connection: "alpha" }),    ]);    expect(groups.map((g) => g.connection)).toEqual(["alpha", "zeta"]);  });   it("falls back to a single owner-only label when tools carry no owner/connection (flat case)", () => {    const groups = buildAccountGroups([      tool({ id: "vercel.deploy", name: "vercel.deploy" }),      tool({ id: "vercel.list", name: "vercel.list" }),    ]);    // Default owner is org; no connection → owner-only label, single group.    expect(groups).toHaveLength(1);    expect(groups[0]!.label).toBe("Workspace");    expect(groups[0]!.tools).toHaveLength(2);  });});