Something went wrong. Try again.
Monorepo for Tangled tangled.org
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";import type { OAuthUserAgent } from "@atcute/oauth-browser-client";import type { AttestationSubject } from "$lib/api/attestation";import { atUriOf, cidOf, didOf, knotOf } from "$lib/api/syntax";
vi.mock("$app/environment", () => ({ browser: true }));
const agent = { sub: "did:plc:writer" } as unknown as OAuthUserAgent;
const store = new Map<string, string>();const fakeStorage = { getItem: (key: string) => store.get(key) ?? null, setItem: (key: string, value: string) => void store.set(key, value), removeItem: (key: string) => void store.delete(key), key: (index: number) => [...store.keys()][index] ?? null, get length() { return store.size; }};
const settledPrefix = "tangled:attestations:settled:did:plc:writer:";
const subject: AttestationSubject = { uri: atUriOf("at://did:web:knot.test:repo/org.tangled.track.ticket/3labc"), cid: cidOf("bafyreimister" + "a".repeat(46))};const other: AttestationSubject = { uri: atUriOf(`${subject.uri}2`), cid: cidOf("bafyreimagoo" + "a".repeat(47))};
interface Written { rkey: string; record: Record<string, unknown>;}
type PutRecord = (rkey?: string, record?: unknown) => Promise<unknown>;
const loadWith = async (putRecord: PutRecord) => { vi.resetModules(); const writes: Written[] = []; const attempts = { count: 0 }; vi.doMock("$lib/api/write", () => ({ putRecord: vi.fn( async (_agent: unknown, _collection: unknown, rkey: string, record: unknown) => { try { await putRecord(rkey, record); writes.push({ rkey, record: record as Record<string, unknown> }); } finally { attempts.count += 1; } } ) })); vi.doMock("$lib/api/cache", () => ({ clear: () => {} })); const attestation = await import("./attestation"); const queued = async (count: number) => { await vi.waitFor(() => { expect(attestation.pendingAttestations(didOf(agent.sub)).length).toBe(count); }); }; return { attestation, writes, attempts, queued };};
const succeed: PutRecord = async () => undefined;const fail: PutRecord = async () => { throw new Error("The PDS is unreachable.");};const failFirst = (() => { let first = true; return async (...args: Parameters<PutRecord>) => { if (first) { first = false; return fail(...args); } return succeed(...args); };})();
beforeEach(() => { store.clear(); vi.stubGlobal("localStorage", fakeStorage);});
afterEach(() => { vi.unstubAllGlobals();});
describe("attest and flushAttestations", () => { it("queues one subject once, retries with its minted rkey, and never requeues settled subjects", async () => { const { attestation, writes, attempts, queued } = await loadWith(failFirst); attestation.attest(agent, subject); await vi.waitFor(() => expect(attempts.count).toBe(1)); attestation.attest(agent, subject); expect(attestation.pendingAttestations(didOf(agent.sub)).length).toBe(1); const failures = await attestation.flushAttestations(agent); expect(failures).toEqual([]); await queued(0); expect(writes).toHaveLength(1); attestation.attest(agent, subject); await queued(0); expect(writes).toHaveLength(1); expect([...store.keys()].filter((key) => key.startsWith(settledPrefix))).toHaveLength(1); });
it("reports each failure message, keeps failed entries queued, and survives corrupt rows and full quotas", async () => { store.set("tangled:attestations:did:plc:writer:3lacorrupt", "{not json"); const { attestation, queued } = await loadWith(fail); attestation.attest(agent, subject); await queued(1); const failures = await attestation.flushAttestations(agent); expect(failures[0]).toBe("The PDS is unreachable."); expect(attestation.pendingAttestations(didOf(agent.sub)).length).toBe(1);
vi.stubGlobal("localStorage", { ...fakeStorage, setItem: () => { throw new DOMException("Local storage is full.", "QuotaExceededError"); } }); expect(() => attestation.attest(agent, subject)).not.toThrow(); });
it("attaches change git object IDs by paging listCobVersions, and writes without them when knot version listings fail", async () => { const pages = [ { versions: [{ cid: "bafyreirecent", changeId: "1c0ffee" }], cursor: "500" }, { versions: [ { cid: subject.cid, changeId: "5d1f0a3c9b8e47d2a1c6f4e8b3d7a2c5e9f1b6d8" } ] } ]; const asked: string[] = []; vi.stubGlobal( "fetch", vi.fn(async (input: URL) => { if (String(input).includes("knot.other")) return { ok: false } as unknown as Response; asked.push(`${new URL(String(input)).searchParams.get("cursor")}`); return { ok: true, json: async () => pages[asked.length - 1] } as unknown as Response; }) ); const { attestation, writes, queued } = await loadWith(succeed); attestation.attest(agent, subject, knotOf("knot.test")); await queued(0); attestation.attest(agent, other, knotOf("knot.other")); await queued(0); expect(writes[0]?.record.change).toBe("5d1f0a3c9b8e47d2a1c6f4e8b3d7a2c5e9f1b6d8"); expect(writes[1]?.record.change).toBeUndefined(); expect(asked).toEqual(["0", "500"]); });
it("dismissal mid-flush drops queued entries and lets in-flight writes finish", async () => { let go: (() => void) | undefined; const until = new Promise<void>((resolve) => { go = resolve; }); let dispatched = 0; const { attestation, writes, queued } = await loadWith(async () => { dispatched += 1; if (dispatched === 1) await until; return succeed(); }); attestation.attest(agent, subject); await vi.waitFor(() => expect(dispatched).toBe(1)); attestation.attest(agent, other); await queued(2); attestation.dismissAttestations(didOf(agent.sub)); go?.(); await queued(0); await vi.waitFor(() => expect(writes).toHaveLength(1)); });});