// Copyright 2017-2021 Signal Messenger, LLC // SPDX-License-Identifier: AGPL-3.0-only import { assert } from 'chai'; import * as sinon from 'sinon'; import { sleep } from '../util/sleep'; import { explodePromise } from '../util/explodePromise'; import createTaskWithTimeout, { suspendTasksWithTimeout, resumeTasksWithTimeout, } from '../textsecure/TaskWithTimeout'; describe('createTaskWithTimeout', () => { let sandbox: sinon.SinonSandbox; beforeEach(() => { sandbox = sinon.createSandbox(); }); afterEach(() => { sandbox.restore(); }); it('resolves when promise resolves', async () => { const task = () => Promise.resolve('hi!'); const taskWithTimeout = createTaskWithTimeout(task, 'resolving-task'); const result = await taskWithTimeout(); assert.strictEqual(result, 'hi!'); }); it('flows error from promise back', async () => { const error = new Error('original'); const task = () => Promise.reject(error); const taskWithTimeout = createTaskWithTimeout(task, 'rejecting-task'); await assert.isRejected(taskWithTimeout(), 'original'); }); it('rejects if promise takes too long (this one logs error to console)', async () => { const clock = sandbox.useFakeTimers(); const { promise: pause } = explodePromise(); // Never resolves const task = () => pause; const taskWithTimeout = createTaskWithTimeout(task, 'slow-task'); const promise = assert.isRejected(taskWithTimeout()); await clock.runToLastAsync(); await promise; }); it('rejects if task throws (and does not log about taking too long)', async () => { const clock = sandbox.useFakeTimers(); const error = new Error('Task is throwing!'); const task = () => { throw error; }; const taskWithTimeout = createTaskWithTimeout(task, 'throwing-task'); await clock.runToLastAsync(); await assert.isRejected(taskWithTimeout(), 'Task is throwing!'); }); it('passes arguments to the underlying function', async () => { const task = (arg: string) => Promise.resolve(arg); const taskWithTimeout = createTaskWithTimeout(task, 'arguments-task'); const result = await taskWithTimeout('hi!'); assert.strictEqual(result, 'hi!'); }); it('suspends and resumes tasks', async () => { const clock = sandbox.useFakeTimers(); let state = 0; const task = async () => { state = 1; await sleep(900); state = 2; await sleep(900); state = 3; }; const taskWithTimeout = createTaskWithTimeout(task, 'suspend-task', { timeout: 1000, }); const promise = taskWithTimeout(); assert.strictEqual(state, 1); suspendTasksWithTimeout(); await clock.tickAsync(900); assert.strictEqual(state, 2); resumeTasksWithTimeout(); await clock.tickAsync(900); assert.strictEqual(state, 3); await promise; }); it('suspends and resumes timing out task', async () => { const clock = sandbox.useFakeTimers(); const { promise: pause } = explodePromise(); // Never resolves const task = () => pause; const taskWithTimeout = createTaskWithTimeout(task, 'suspend-slow-task'); const promise = assert.isRejected(taskWithTimeout()); suspendTasksWithTimeout(); await clock.runToLastAsync(); resumeTasksWithTimeout(); await clock.runToLastAsync(); await promise; }); });