import type { OAuthUserAgent } from "@atcute/oauth-browser-client"; import { describe, expect, it, vi } from "vitest"; import { createBobbinClient } from "$lib/api/client"; import { FOLLOW_VIEWER, STAR_VIEWER } from "$lib/api/descriptors"; import { TYPE_VIEWER } from "$lib/api/enrich"; import { ensureProfile, getTrendingWithViewerState, hasProfile } from "$lib/api/profile"; it("gets viewer-neutral trending data with relationship state in the enrichment sidecar", async () => { const fetchMock = vi.fn().mockResolvedValue( new Response(JSON.stringify({ output: { people: [], repos: [] }, data: {} }), { status: 200, headers: { "content-type": "application/json" } }) ); const ctx = createBobbinClient({ serviceUrl: "https://bobbin.test", fetch: fetchMock }); await getTrendingWithViewerState(ctx, "did:plc:viewer"); const [url, request] = fetchMock.mock.calls[0]; expect(String(url)).toBe("https://bobbin.test/xrpc/sh.tangled.query.enrichResponse"); expect(JSON.parse(String(request?.body))).toEqual({ xrpc: "sh.tangled.actor.getTrending", viewer: "did:plc:viewer", enrich: [ { source: FOLLOW_VIEWER.source, type: TYPE_VIEWER, targets: ["people[].did"] }, { source: STAR_VIEWER.source, type: TYPE_VIEWER, targets: ["repos[].did"] } ] }); }); describe("profile existence", () => { const pds = "https://pds.example"; const cid = "bafyreidfayvfuwqa7qlnopdjiqrxzs6blmoeu4rujcjtnci5beludirz2a"; const agent = (handle: ReturnType) => ({ sub: "did:plc:org", session: { info: { aud: pds } }, handle }) as unknown as OAuthUserAgent; const found = () => Response.json({ uri: "at://did:plc:org/sh.tangled.actor.profile/self", cid, value: { $type: "sh.tangled.actor.profile", bluesky: false } }); const missing = () => Response.json( { error: "RecordNotFound", message: "Could not locate record" }, { status: 400 } ); it("reads the record from the account's own pds", async () => { const handle = vi.fn(async () => found()); expect(await hasProfile(agent(handle))).toBe(true); const [path] = handle.mock.calls[0] as unknown as [string]; const url = new URL(path, pds); expect(url.pathname).toBe("/xrpc/com.atproto.repo.getRecord"); expect(url.searchParams.get("repo")).toBe("did:plc:org"); expect(url.searchParams.get("collection")).toBe("sh.tangled.actor.profile"); expect(url.searchParams.get("rkey")).toBe("self"); }); it("treats a missing record as no profile, and anything else as an error", async () => { expect(await hasProfile(agent(vi.fn(async () => missing())))).toBe(false); await expect( hasProfile( agent( vi.fn(async () => Response.json({ error: "UpstreamFailure", message: "" }, { status: 502 }) ) ) ) ).rejects.toThrow(); }); it("writes the record only when it is missing", async () => { const record = { $type: "sh.tangled.actor.profile", bluesky: false, isOrganization: true } as const; const written = vi .fn() .mockResolvedValueOnce(missing()) .mockResolvedValueOnce( Response.json({ uri: "at://did:plc:org/sh.tangled.actor.profile/self", cid }) ); await ensureProfile(agent(written), record); expect(written).toHaveBeenCalledTimes(2); const [path, init] = written.mock.calls[1] as unknown as [string, RequestInit]; expect(new URL(path, pds).pathname).toBe("/xrpc/com.atproto.repo.putRecord"); expect(JSON.parse(String(init.body))).toMatchObject({ rkey: "self", record }); const untouched = vi.fn(async () => found()); await ensureProfile(agent(untouched), record); expect(untouched).toHaveBeenCalledTimes(1); }); });