import { afterEach, describe, expect, it, vi } from "vitest"; import type { OAuthUserAgent } from "@atcute/oauth-browser-client"; import type { BobbinContext } from "#lib/api/client.ts"; import { didOf } from "#lib/api/syntax.ts"; import type * as MarkupBlobs from "#lib/api/markupBlobs.ts"; const agent = { sub: "did:plc:limpet" } as unknown as OAuthUserAgent; const repo = didOf("did:plc:barnacle"); const bobbin = {} as BobbinContext; const load = async () => { vi.resetModules(); const minted: { aud: string; lxm: string }[] = []; vi.doMock("#lib/auth/agent.ts", () => ({ createClient: () => { throw new Error("the signed in account's pds shouldn't be used"); }, mintServiceAuth: async (_agent: unknown, scope: { aud: string; lxm: string }) => { minted.push(scope); return "token"; } })); vi.doMock("#lib/api/identity.ts", () => ({ resolveMiniDoc: async () => ({ pds: "https://knot.example.com" }) })); const blobs = (await import("#lib/api/markupBlobs.ts")) as typeof MarkupBlobs; const calls: { url: string; init: RequestInit }[] = []; const answer = (link: string) => vi.stubGlobal("fetch", async (input: URL | RequestInfo, init?: RequestInit) => { calls.push({ url: String(input), init: init ?? {} }); const blob = { $type: "blob", ref: { $link: link }, mimeType: "image/png", size: 3 }; return Response.json({ blob }); }); return { blobs, minted, calls, answer }; }; const png = (bytes: number[]) => new File([new Uint8Array(bytes)], "seal.png", { type: "image/png" }); afterEach(() => { vi.unstubAllGlobals(); }); describe("uploadPending", () => { it("uploads what the text still links to the owner's pds, once", async () => { const { blobs, minted, calls, answer } = await load(); const file = png([1, 2, 3]); const cid = await blobs.blobCidOf(file); const kept = { cid, file, url: "blob:kept" }; const dropped = { cid: "bafkreidropped", file: png([4]), url: "blob:dropped" }; answer(cid); const uploaded = await blobs.uploadPending( agent, bobbin, repo, [kept, kept, dropped], `![Image](https://tangled.org/blob/${repo}/${cid})` ); expect(uploaded.map((blob) => blob.ref.$link)).toEqual([cid]); expect(calls).toHaveLength(1); expect(calls[0].url).toBe("https://knot.example.com/xrpc/com.atproto.repo.uploadBlob"); expect(minted).toEqual([{ aud: repo, lxm: "com.atproto.repo.uploadBlob" }]); }); it("refuses a blob stored under another cid than the one linked", async () => { const { blobs, answer } = await load(); const file = png([1, 2, 3]); const cid = await blobs.blobCidOf(file); const other = await blobs.blobCidOf(png([9])); answer(other); await expect( blobs.uploadPending(agent, bobbin, repo, [{ cid, file, url: "blob:x" }], cid) ).rejects.toThrow(`stored as ${other}`); }); });