signal-desktop/ts/test-node/jobs/helpers/sleepForRateLimitRetryAfterTime_test.ts
2022-02-24 16:26:58 -08:00

234 lines
5.5 KiB
TypeScript

// Copyright 2021-2022 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only
import { assert } from 'chai';
import * as sinon from 'sinon';
import { HTTPError } from '../../../textsecure/Errors';
import * as durations from '../../../util/durations';
import { sleepForRateLimitRetryAfterTime } from '../../../jobs/helpers/sleepForRateLimitRetryAfterTime';
describe('sleepFor413RetryAfterTimeIfApplicable', () => {
const createLogger = () => ({ info: sinon.spy() });
let sandbox: sinon.SinonSandbox;
let clock: sinon.SinonFakeTimers;
beforeEach(() => {
sandbox = sinon.createSandbox();
clock = sandbox.useFakeTimers();
});
afterEach(() => {
sandbox.restore();
});
it('does nothing if no time remains', async () => {
const log = createLogger();
await Promise.all(
[-1, 0].map(timeRemaining =>
sleepForRateLimitRetryAfterTime({
err: {},
log,
timeRemaining,
})
)
);
sinon.assert.notCalled(log.info);
});
it('waits for 1 minute if the error lacks Retry-After info', async () => {
let done = false;
(async () => {
await sleepForRateLimitRetryAfterTime({
err: {},
log: createLogger(),
timeRemaining: 12345678,
});
done = true;
})();
await clock.tickAsync(durations.MINUTE - 1);
assert.isFalse(done);
await clock.tickAsync(2);
assert.isTrue(done);
});
it('finds the Retry-After header on an HTTPError', async () => {
const err = new HTTPError('Slow down', {
code: 413,
headers: { 'retry-after': '200' },
response: {},
});
let done = false;
(async () => {
await sleepForRateLimitRetryAfterTime({
err,
log: createLogger(),
timeRemaining: 123456789,
});
done = true;
})();
await clock.tickAsync(199 * durations.SECOND);
assert.isFalse(done);
await clock.tickAsync(2 * durations.SECOND);
assert.isTrue(done);
});
it('finds the Retry-After header on an HTTPError', async () => {
const err = new HTTPError('Slow down', {
code: 429,
headers: { 'retry-after': '200' },
response: {},
});
let done = false;
(async () => {
await sleepForRateLimitRetryAfterTime({
err,
log: createLogger(),
timeRemaining: 123456789,
});
done = true;
})();
await clock.tickAsync(199 * durations.SECOND);
assert.isFalse(done);
await clock.tickAsync(2 * durations.SECOND);
assert.isTrue(done);
});
it('finds the Retry-After on an HTTPError nested under a wrapper error', async () => {
const httpError = new HTTPError('Slow down', {
code: 413,
headers: { 'retry-after': '200' },
response: {},
});
let done = false;
(async () => {
await sleepForRateLimitRetryAfterTime({
err: { httpError },
log: createLogger(),
timeRemaining: 123456789,
});
done = true;
})();
await clock.tickAsync(199 * durations.SECOND);
assert.isFalse(done);
await clock.tickAsync(2 * durations.SECOND);
assert.isTrue(done);
});
it('finds the Retry-After on an HTTPError nested under a wrapper error', async () => {
const httpError = new HTTPError('Slow down', {
code: 429,
headers: { 'retry-after': '200' },
response: {},
});
let done = false;
(async () => {
await sleepForRateLimitRetryAfterTime({
err: { httpError },
log: createLogger(),
timeRemaining: 123456789,
});
done = true;
})();
await clock.tickAsync(199 * durations.SECOND);
assert.isFalse(done);
await clock.tickAsync(2 * durations.SECOND);
assert.isTrue(done);
});
it("won't wait longer than the remaining time", async () => {
const err = new HTTPError('Slow down', {
code: 413,
headers: { 'retry-after': '99999' },
response: {},
});
let done = false;
(async () => {
await sleepForRateLimitRetryAfterTime({
err,
log: createLogger(),
timeRemaining: 3 * durations.SECOND,
});
done = true;
})();
await clock.tickAsync(4 * durations.SECOND);
assert.isTrue(done);
});
it("won't wait longer than the remaining time", async () => {
const err = new HTTPError('Slow down', {
code: 429,
headers: { 'retry-after': '99999' },
response: {},
});
let done = false;
(async () => {
await sleepForRateLimitRetryAfterTime({
err,
log: createLogger(),
timeRemaining: 3 * durations.SECOND,
});
done = true;
})();
await clock.tickAsync(4 * durations.SECOND);
assert.isTrue(done);
});
it('logs how long it will wait', async () => {
const log = createLogger();
const err = new HTTPError('Slow down', {
code: 413,
headers: { 'retry-after': '123' },
response: {},
});
sleepForRateLimitRetryAfterTime({ err, log, timeRemaining: 9999999 });
await clock.nextAsync();
sinon.assert.calledOnce(log.info);
sinon.assert.calledWith(log.info, sinon.match(/123000 millisecond\(s\)/));
});
it('logs how long it will wait', async () => {
const log = createLogger();
const err = new HTTPError('Slow down', {
code: 429,
headers: { 'retry-after': '123' },
response: {},
});
sleepForRateLimitRetryAfterTime({ err, log, timeRemaining: 9999999 });
await clock.nextAsync();
sinon.assert.calledOnce(log.info);
sinon.assert.calledWith(log.info, sinon.match(/123000 millisecond\(s\)/));
});
});