import { vi } from "vitest"; import type { ResumableUploadCheckpoint, ResumableUploadCheckpointStore, ResumableUploadControlPlane, ResumableUploadSource, UploadPartExecutor, UploadPartReceipt, UploadProviderResult, UploadSession, } from "../../src/application/ports/browser-transfer/resumable-upload.ts"; import { RESUMABLE_UPLOAD_PROTOCOL } from "../../src/application/ports/browser-transfer/resumable-upload.ts"; import type { BrowserDataResult, } from "../../src/application/ports/browser-file-storage/shared.ts"; import { browserDataFailure, browserDataSuccess, } from "../../src/adapters/browser-file-storage/result.ts"; import { resolveResumableUploadRuntimePolicy } from "../../src/adapters/browser-transfer/resumable-upload/runtime-policy.ts"; import type { UploadCancellationChannel, UploadCancellationListener, } from "../../src/adapters/browser-transfer/resumable-upload/upload-cancellation-channel.ts"; import type { UploadMutationLock } from "../../src/adapters/browser-transfer/resumable-upload/upload-mutation-lock.ts"; export type TestCapability = Readonly<{ id: string }>; export const activeSignal = new AbortController().signal; export const noContentionLock: UploadMutationLock = Object.freeze({ async run( _uploadKey: string, _signal: AbortSignal, task: () => Promise, ): Promise { return await task(); }, }); export function createSerialMutationLock(): UploadMutationLock { let tail = Promise.resolve(); return Object.freeze({ run( _uploadKey: string, signal: AbortSignal, task: () => Promise, ): Promise { const result = tail.then(async () => { if (signal.aborted) { throw new DOMException( "The operation was aborted.", "AbortError", ); } return await task(); }); tail = result.then( () => undefined, () => undefined, ); return result; }, }); } export function createMemoryCancellationPair(): readonly [ UploadCancellationChannel, UploadCancellationChannel, ] { const listeners = [ new Set(), new Set(), ] as const; const channels = listeners.map((ownListeners, ownIndex) => { let closed = false; return Object.freeze({ publish(uploadKey: string) { if (closed) return false; for (const [index, peerListeners] of listeners.entries()) { if (index === ownIndex) continue; for (const listener of [...peerListeners]) { listener(uploadKey); } } return true; }, subscribe(listener: UploadCancellationListener) { if (closed) throw new TypeError("closed"); ownListeners.add(listener); return () => ownListeners.delete(listener); }, close() { closed = true; ownListeners.clear(); }, }); }); return channels as unknown as readonly [ UploadCancellationChannel, UploadCancellationChannel, ]; } export class MemoryCheckpointStore implements ResumableUploadCheckpointStore { readonly rows = new Map(); closed = false; async read( uploadKey: string, ): Promise> { return browserDataSuccess( structuredClone(this.rows.get(uploadKey) ?? null), ); } async compareAndSwap( input: Parameters< ResumableUploadCheckpointStore["compareAndSwap"] >[0], ): Promise> { const current = this.rows.get(input.checkpoint.uploadKey); if ( (input.expectedRevision === null && current) || (input.expectedRevision !== null && current?.revision !== input.expectedRevision) ) { return browserDataFailure("CONFLICT", "UPLOAD_RECONCILE", { recovery: "RECONCILE", }); } const snapshot = structuredClone(input.checkpoint); this.rows.set(snapshot.uploadKey, snapshot); return browserDataSuccess(snapshot); } async remove( input: Parameters[0], ): Promise> { const current = this.rows.get(input.uploadKey); if (current?.revision !== input.expectedRevision) { return browserDataFailure("CONFLICT", "UPLOAD_RECONCILE", { recovery: "RECONCILE", }); } this.rows.delete(input.uploadKey); return browserDataSuccess(undefined); } close(): void { this.closed = true; } } export function rangeSource(bytes: Uint8Array): ResumableUploadSource { return Object.freeze({ kind: "RANGE_READER" as const, reader: Object.freeze({ byteLength: bytes.byteLength, async readRange(input: Readonly<{ offset: number; length: number; signal: AbortSignal; }>) { if (input.signal.aborted) { return browserDataFailure("ABORTED", "FILE_READ"); } return browserDataSuccess( bytes.slice(input.offset, input.offset + input.length), ); }, }), }); } export function byteStreamSource(bytes: Uint8Array): ResumableUploadSource { return Object.freeze({ kind: "FILE_BYTE_SOURCE" as const, bytes: Object.freeze({ byteLength: bytes.byteLength, async *stream(signal: AbortSignal) { if (signal.aborted) { yield browserDataFailure("ABORTED", "FILE_READ"); return; } yield browserDataSuccess(bytes.slice(0, 3)); yield browserDataSuccess(bytes.slice(3)); }, }), }); } export function runtimePolicy( overrides: Partial< Parameters[0] > = {}, ) { return { partSizeBytes: 4, maxFileBytes: 100, maxPartCount: 25, maxConcurrency: 3, maxInFlightBytes: 48, partBufferCopyFactor: 4, maxSourceChunkBytes: 8, maxRetries: 2, retryBaseDelayMs: 1, retryMaxDelayMs: 10, maxRetryAfterMs: 100, capabilityRefreshSkewMs: 5, maxSessionLifetimeMs: 10_000, providerAttemptTimeoutMs: 100, ...overrides, }; } export type ControlHarness = Readonly<{ control: ResumableUploadControlPlane; accepted: Map; issued: ReturnType; completedParts: UploadPartReceipt[][]; getSession(): UploadSession | null; }>; export function createControlHarness(options: Readonly<{ now?: number; serverMaxConcurrency?: number; sessionId?: (createIndex: number) => string; statusParts?: ( session: UploadSession, accepted: Map, ) => readonly UploadPartReceipt[]; issueCapability?: ( input: Parameters< ResumableUploadControlPlane["issuePartCapability"] >[0], callIndex: number, ) => UploadProviderResult>; }> = {}): ControlHarness { const now = options.now ?? 1_000; const accepted = new Map(); const completedParts: UploadPartReceipt[][] = []; let session: UploadSession | null = null; let createCount = 0; let issueCount = 0; const issued = vi.fn(); const control: ResumableUploadControlPlane = { async createSession(input) { createCount += 1; session = Object.freeze({ protocol: RESUMABLE_UPLOAD_PROTOCOL, sessionId: options.sessionId?.(createCount) ?? "session_01", requestBindingSha256: input.requestBindingSha256, fingerprint: input.fingerprint, partSizeBytes: input.requestedPartSizeBytes, partCount: input.fingerprint.partCount, maxConcurrency: options.serverMaxConcurrency ?? 2, expiresAtEpochMs: now + 5_000, }); return browserDataSuccess(session); }, async getStatus() { if (!session) { return browserDataFailure("NOT_FOUND", "UPLOAD_RECONCILE"); } const parts = options.statusParts?.(session, accepted) ?? [...accepted.values()].sort( (left, right) => left.partNumber - right.partNumber, ); return browserDataSuccess({ state: "ACTIVE", session, acceptedParts: parts, }); }, async issuePartCapability(input) { issueCount += 1; issued(input); return ( options.issueCapability?.(input, issueCount) ?? browserDataSuccess({ capability: Object.freeze({ id: `cap-${issueCount}` }), uploadBindingSha256: input.uploadBindingSha256, expiresAtEpochMs: now + 4_000, }) ); }, async complete(input) { completedParts.push([...input.orderedParts]); return browserDataSuccess({ state: "QUARANTINED", protocol: RESUMABLE_UPLOAD_PROTOCOL, sessionId: input.sessionId, requestBindingSha256: input.requestBindingSha256, fingerprint: input.fingerprint, resourceId: "resource_01", }); }, async abort() { return browserDataSuccess({ state: "ABORTED" }); }, }; return { control, accepted, issued, completedParts, getSession: () => session, }; } export function executorFor( harness: ControlHarness, options: Readonly<{ delay?: () => Promise; onActive?: (active: number) => void; }> = {}, ): UploadPartExecutor { let active = 0; return { async uploadPart(input) { active += 1; options.onActive?.(active); await options.delay?.(); active -= 1; const receipt = Object.freeze({ ...input.part, receiptToken: `etag-part-${input.part.partNumber}`, }); harness.accepted.set(input.part.partNumber, receipt); return browserDataSuccess(receipt); }, }; }