Something went wrong. Try again.
Monorepo for Tangled
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186import type { Did } from "@atcute/lexicons/syntax";import type { OAuthUserAgent } from "@atcute/oauth-browser-client";import { beforeEach, describe, expect, it, vi } from "vitest";import { getOrCreateDelegatedSession } from "$lib/api/actAs";import { addOrgMembers, createOrg, createOrgAccount, ORG_HANDLE_SUFFIX, writeOrgProfile} from "$lib/api/orgCreate";import { putProfile } from "$lib/api/profile";import { addController, createDelegatedAccount, getScopePresets, listControlledAccounts} from "$lib/api/tranquil";
vi.mock("$lib/api/actAs", () => ({ getOrCreateDelegatedSession: vi.fn() }));vi.mock("$lib/api/profile", () => ({ uploadProfileAvatar: vi.fn(), putProfile: vi.fn() }));vi.mock("$lib/api/tranquil", () => ({ addController: vi.fn(), createDelegatedAccount: vi.fn(), getScopePresets: vi.fn(), listControlledAccounts: vi.fn()}));
const ownerScopes = "owner-scopes";const editorScopes = "atproto repo:*?action=create repo:*?action=update repo:*?action=delete blob:*/* rpc:*";const controllerDid = "did:plc:controller" as Did;const controller = { sub: controllerDid } as OAuthUserAgent;const orgAgent = {} as OAuthUserAgent;const orgDid = "did:plc:org" as Did;const memberDid = "did:plc:member" as Did;const fullHandle = `acme${ORG_HANDLE_SUFFIX}`;
beforeEach(() => { vi.resetAllMocks(); vi.mocked(getScopePresets).mockResolvedValue([ { name: "owner", label: "Owner", description: "", scopes: ownerScopes }, { name: "editor", label: "Editor", description: "", scopes: editorScopes } ]); vi.mocked(listControlledAccounts).mockResolvedValue([]); vi.mocked(createDelegatedAccount).mockResolvedValue({ did: orgDid, handle: fullHandle }); vi.mocked(getOrCreateDelegatedSession).mockResolvedValue(orgAgent); vi.mocked(addController).mockResolvedValue({ success: true });});
describe("createOrgAccount", () => { it("creates only the delegated account with the owner preset", async () => { const org = await createOrgAccount(controller, " ACME ");
expect(createDelegatedAccount).toHaveBeenCalledWith(controller, { handle: fullHandle, controllerScopes: ownerScopes }); expect(org).toEqual({ did: orgDid, handle: fullHandle }); expect(putProfile).not.toHaveBeenCalled(); expect(addController).not.toHaveBeenCalled(); });
it("rejects an existing controlled handle", async () => { vi.mocked(listControlledAccounts).mockResolvedValue([ { did: orgDid, handle: fullHandle, grantedScopes: ownerScopes, grantedAt: "now" } ]);
await expect(createOrgAccount(controller, "acme")).rejects.toThrow(/already in use/); expect(createDelegatedAccount).not.toHaveBeenCalled(); });});
describe("writeOrgProfile", () => { it("writes the organization profile through its delegated agent", async () => { await writeOrgProfile(orgAgent, { description: " A software team ", website: "https://example.com" });
expect(putProfile).toHaveBeenCalledWith(orgAgent, { $type: "sh.tangled.actor.profile", bluesky: false, isOrganization: true, description: "A software team", links: ["https://example.com"] }); });});
describe("addOrgMembers", () => { it("grants members Tranquil's repository editor preset", async () => { await addOrgMembers(orgAgent, { ownerDid: controllerDid, members: [{ did: memberDid, handle: "member.example" }] });
expect(addController).toHaveBeenCalledWith(orgAgent, { controllerDid: memberDid, grantedScopes: editorScopes }); });
it("ignores the owner when they are also included in the members list", async () => { const memberErrors = await addOrgMembers(orgAgent, { ownerDid: controllerDid, members: [{ did: controllerDid, handle: "owner.example" }] });
expect(getScopePresets).not.toHaveBeenCalled(); expect(addController).not.toHaveBeenCalled(); expect(memberErrors).toEqual([]); });
it("returns member failures to the page flow", async () => { vi.mocked(addController).mockRejectedValue(new Error("insufficient scope"));
const memberErrors = await addOrgMembers(orgAgent, { ownerDid: controllerDid, members: [{ did: memberDid, handle: "member.example" }] });
expect(memberErrors).toEqual([ "Unable to add member.example as a member. Try again later." ]); });});
describe("createOrg", () => { it("orchestrates account, session, profile, and members in order", async () => { const events: string[] = []; vi.mocked(createDelegatedAccount).mockImplementation(async () => { events.push("account"); return { did: orgDid, handle: fullHandle }; }); vi.mocked(getOrCreateDelegatedSession).mockImplementation(async () => { events.push("session"); return orgAgent; }); vi.mocked(putProfile).mockImplementation(async () => { events.push("profile"); }); vi.mocked(addController).mockImplementation(async () => { events.push("member"); return { success: true }; });
const org = await createOrg(controller, { handle: "acme", description: "A team", members: [{ did: memberDid, handle: "member.example" }] });
expect(events).toEqual(["account", "session", "profile", "member"]); expect(org).toEqual({ did: orgDid, handle: fullHandle, warnings: [] }); });
it("continues independent setup after a partial failure and returns warnings", async () => { vi.mocked(putProfile).mockRejectedValue(new Error("profile unavailable")); vi.mocked(addController).mockRejectedValue(new Error("controller rejected"));
const org = await createOrg(controller, { handle: "acme", members: [{ did: memberDid, handle: "member.example" }] });
expect(addController).toHaveBeenCalled(); expect(org.warnings).toEqual([ "Unable to save the organization's profile. Try again later.", "Unable to add member.example as a member. Try again later." ]); });
it("reports authorization failure without attempting dependent setup", async () => { vi.mocked(getOrCreateDelegatedSession).mockRejectedValue(new Error("grant unavailable"));
const org = await createOrg(controller, { handle: "acme", members: [] });
expect(org.warnings).toEqual([ "Unable to authorize the organization, so its profile and members were not saved. Try again later." ]); expect(putProfile).not.toHaveBeenCalled(); expect(addController).not.toHaveBeenCalled(); });});