60 lines
2.1 KiB
TypeScript
60 lines
2.1 KiB
TypeScript
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<Error | 'ok'>, 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);
|
||
|
|
});
|