import { describe, expect, it } from "vitest"; import type { ResumableUploadCheckpoint } from "../../src/application/ports/browser-transfer/resumable-upload.ts"; import { RESUMABLE_UPLOAD_PROTOCOL } from "../../src/application/ports/browser-transfer/resumable-upload.ts"; import { createIndexedDbResumableUploadCheckpointRuntime, createIndexedDbResumableUploadCheckpointStore, uploadCheckpointDatabaseName, } from "../../src/adapters/browser-transfer/resumable-upload/indexeddb-checkpoint-store.ts"; import { MemoryIndexedDbFactory } from "../helpers/memory-indexeddb.ts"; const scope = Object.freeze({ authorityToken: "authority_token_01", namespaceToken: "namespace_token_01", partitionToken: "partition_token_01", }); function checkpoint( revision: number, overrides: Partial = {}, ): ResumableUploadCheckpoint { return { schemaVersion: 1, protocol: RESUMABLE_UPLOAD_PROTOCOL, revision, state: "ACTIVE", uploadKey: "upload_key_01", requestBindingSha256: "a".repeat(64), fingerprint: { algorithm: "SHA-256-PARTS-V1", digestHex: "b".repeat(64), byteLength: 4, partSizeBytes: 4, partCount: 1, }, sessionId: "session_01", sessionExpiresAtEpochMs: 5_000, sessionMaxConcurrency: 1, acceptedParts: [], updatedAtEpochMs: 1_000, ...overrides, }; } function deletingFactory( memory: MemoryIndexedDbFactory, mode: "SUCCESS" | "BLOCKED", ): IDBFactory { return { open: memory.factory.open.bind(memory.factory), cmp: memory.factory.cmp.bind(memory.factory), deleteDatabase: () => { const request = { result: undefined, error: null, transaction: null, source: null, readyState: "pending", onsuccess: null, onerror: null, onblocked: null, onupgradeneeded: null, addEventListener() {}, removeEventListener() {}, dispatchEvent: () => true, } as unknown as IDBOpenDBRequest; queueMicrotask(() => { if (mode === "SUCCESS") { request.onsuccess?.(new Event("success")); } else { request.onblocked?.({ oldVersion: 1, newVersion: null, } as IDBVersionChangeEvent); } }); return request; }, databases: async () => [], } as IDBFactory; } describe("IndexedDB resumable upload checkpoint", () => { it("length-prefixes scope tuples so delimiter placement cannot collide", () => { const first = uploadCheckpointDatabaseName({ authorityToken: "aaaaaaaa-bbbbbbbb", namespaceToken: "cccccccc", partitionToken: "dddddddd", }); const second = uploadCheckpointDatabaseName({ authorityToken: "aaaaaaaa", namespaceToken: "bbbbbbbb-cccccccc", partitionToken: "dddddddd", }); expect(first).not.toBe(second); expect(first).toContain("17:aaaaaaaa-bbbbbbbb"); expect(second).toContain("8:aaaaaaaa"); }); it("commits CAS only at transaction completion and rejects stale revisions", async () => { const memory = new MemoryIndexedDbFactory(); const store = createIndexedDbResumableUploadCheckpointStore({ scope, factory: memory.factory, }); const first = checkpoint(1); expect( await store.compareAndSwap({ expectedRevision: null, checkpoint: first, }), ).toEqual({ ok: true, value: first }); expect(await store.read(first.uploadKey)).toEqual({ ok: true, value: first, }); memory.failNextWriteCommit( new DOMException("commit failed", "UnknownError"), ); const failed = await store.compareAndSwap({ expectedRevision: 1, checkpoint: checkpoint(2, { state: "ABORT_PENDING" }), }); expect(failed).toMatchObject({ ok: false, error: { code: "UNAVAILABLE" }, }); expect(await store.read(first.uploadKey)).toEqual({ ok: true, value: first, }); expect( await store.compareAndSwap({ expectedRevision: 2, checkpoint: checkpoint(3), }), ).toMatchObject({ ok: false, error: { code: "CONFLICT", recovery: "RECONCILE" }, }); }); it("rejects unknown persisted fields so URLs and credentials cannot enter a checkpoint", async () => { const memory = new MemoryIndexedDbFactory(); const store = createIndexedDbResumableUploadCheckpointStore({ scope, factory: memory.factory, }); const smuggled = { ...checkpoint(1), signedUrl: "https://object.invalid/secret?signature=value", } as unknown as ResumableUploadCheckpoint; expect( await store.compareAndSwap({ expectedRevision: null, checkpoint: smuggled, }), ).toMatchObject({ ok: false, error: { code: "INVALID_INPUT" }, }); expect(await store.read("upload_key_01")).toEqual({ ok: true, value: null, }); }); it("closes the bound partition before successful lifecycle deletion", async () => { const memory = new MemoryIndexedDbFactory(); const runtime = createIndexedDbResumableUploadCheckpointRuntime({ scope, factory: deletingFactory(memory, "SUCCESS"), blockedTimeoutMs: 10, }); expect( await runtime.store.compareAndSwap({ expectedRevision: null, checkpoint: checkpoint(1), }), ).toMatchObject({ ok: true }); expect(await runtime.admin.deletePartition()).toEqual({ ok: true, value: { state: "DELETED" }, }); expect(await runtime.store.read("upload_key_01")).toMatchObject({ ok: false, error: { code: "UNAVAILABLE", recovery: "RESUME" }, }); }); it("bounds a partition deletion blocked by another browser context", async () => { const memory = new MemoryIndexedDbFactory(); const runtime = createIndexedDbResumableUploadCheckpointRuntime({ scope, factory: deletingFactory(memory, "BLOCKED"), blockedTimeoutMs: 1, }); expect(await runtime.admin.deletePartition()).toMatchObject({ ok: false, error: { code: "BLOCKED", retryable: true, recovery: "RELOAD_OTHER_CONTEXTS", }, }); }); it("never reports a false abort after irreversible deleteDatabase dispatch", async () => { const memory = new MemoryIndexedDbFactory(); const runtime = createIndexedDbResumableUploadCheckpointRuntime({ scope, factory: deletingFactory(memory, "SUCCESS"), }); const controller = new AbortController(); const deletion = runtime.admin.deletePartition(controller.signal); controller.abort(); expect(await deletion).toEqual({ ok: true, value: { state: "DELETED" }, }); const preAborted = new AbortController(); preAborted.abort(); const second = createIndexedDbResumableUploadCheckpointRuntime({ scope: { ...scope, partitionToken: "partition_token_02" }, factory: deletingFactory(new MemoryIndexedDbFactory(), "SUCCESS"), }); expect( await second.admin.deletePartition(preAborted.signal), ).toMatchObject({ ok: false, error: { code: "ABORTED" }, }); }); });