import { test } from 'node:test'; import assert from 'node:assert/strict'; import { MatrixClient, MatrixError } from 'matrix-js-sdk'; import { uploadContent } from './matrix'; // Drives `uploadContent` with a fake client whose uploads fail per `plan` // (one entry per attempt: an Error to reject with, or 'ok'). Back-off sleeps // are real timers, so failures are kept to 1-2 retries per test. const run = async (plan: Array, cancelAfterAttempt?: number) => { let attempt = 0; const controller = new AbortController(); const mx = { uploadContent: () => { attempt += 1; const step = plan[attempt - 1]; if (cancelAfterAttempt === attempt) controller.abort(); return step === 'ok' ? Promise.resolve({ content_uri: 'mxc://x/y' }) : Promise.reject(step); }, } as unknown as MatrixClient; let outcome: { mxc?: string; error?: MatrixError } = {}; await uploadContent(mx, new Blob(['x']) as unknown as File, { onProgress: () => undefined, onSuccess: (mxc) => { outcome = { mxc }; }, onError: (error) => { outcome = { error }; }, signal: controller.signal, }); return { attempts: attempt, ...outcome }; }; const abortError = () => new DOMException('request failed', 'AbortError'); test('a dropped connection (SDK reports it as AbortError) is retried', async () => { const r = await run([abortError(), 'ok']); assert.equal(r.attempts, 2); assert.equal(r.mxc, 'mxc://x/y'); }); test('a user cancel (our signal aborted) is NOT retried even though it is also an AbortError', async () => { const r = await run([abortError(), 'ok'], 1); assert.equal(r.attempts, 1); assert.equal(r.mxc, undefined); assert.ok(r.error); }); test('5xx is retried, 4xx fails fast', async () => { const server = new MatrixError({ errcode: 'M_UNKNOWN', error: 'boom' }, 502); const r = await run([server, 'ok']); assert.equal(r.attempts, 2); assert.equal(r.mxc, 'mxc://x/y'); const tooLarge = new MatrixError({ errcode: 'M_TOO_LARGE', error: 'big' }, 413); const s = await run([tooLarge, 'ok']); assert.equal(s.attempts, 1); assert.equal(s.error?.httpStatus, 413); });