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],
``
);
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}`);
});
});