// // Copyright 2020-2021 Signal Messenger, LLC. // SPDX-License-Identifier: AGPL-3.0-only // /* eslint-env es2017 */ import { use, assert } from 'chai'; import chaiAsPromised from 'chai-as-promised'; import { createRequire } from 'node:module'; use(chaiAsPromised); const require = createRequire(import.meta.url) const native = require(process.env.SIGNAL_NEON_FUTURES_TEST_LIB); function promisify(operation) { return function() { return new Promise((resolve, reject) => { try { operation(...arguments, resolve); } catch (error) { reject(error); } }); }; } describe('native', () => { it('can fulfill promises', async () => { const result = await promisify(native.incrementAsync)(Promise.resolve(5)); assert.equal(result, 6); }); it('can handle rejection', async () => { const result = await promisify(native.incrementAsync)( Promise.reject('badness') ); assert.equal(result, 'error: badness'); }); it("doesn't crash on bad promises", async () => { const result = await promisify(native.incrementAsync)({ then: function(resolve, reject) { queueMicrotask(() => { resolve(5); assert.throws(() => resolve(-5), /promise settled twice/); }); }, }); assert.equal(result, 6); }); it('can handle store-like callbacks', async () => { const result = await native.doubleNameFromStore({ getName: () => Promise.resolve('Moxie'), }); assert.equal(result, 'Moxie Moxie'); }); it('can handle store-like callbacks that fail', async () => { const promise = native.doubleNameFromStore({ getName: () => Promise.reject('uh oh'), }); await assert.isRejected(promise, /rejected: uh oh/); }); it('can handle parallel store-like callbacks', async () => { const result = await native.doubleNameFromStoreUsingJoin({ getName: () => Promise.resolve('Moxie'), }); assert.equal(result, 'Moxie Moxie'); }); it('can handle parallel store-like callbacks that fail', async () => { const promise = native.doubleNameFromStoreUsingJoin({ getName: () => Promise.reject('uh oh'), }); await assert.isRejected(promise, /rejected: uh oh/); }); describe('promises', () => { it('can fulfill promises', async () => { const result = await native.incrementPromise(Promise.resolve(5)); assert.equal(result, 6); const result2 = await native.incrementCallbackPromise(async () => 7); assert.equal(result2, 8); }); it('can handle rejection', async () => { const promise = native.incrementPromise(Promise.reject('badness')); await assert.isRejected(promise, /badness/); }); it('recovers from non-promises', async () => { assert.throws( () => native.incrementPromise('badness'), /failed to downcast .+ to object/ ); }); }); describe('panic recovery', () => { it('handles pre-await panics', async () => { const promise = native.panicPreAwait(Promise.resolve(6)); await assert.isRejected(promise, /unexpected panic: check for this/); }); it('handles callback panics', async () => { const promise = native.panicDuringCallback(Promise.resolve(6)); await assert.isRejected(promise, /unexpected panic: check for this/); }); it('handles post-await panics', async () => { const promise = native.panicPostAwait(Promise.resolve(6)); await assert.isRejected(promise, /unexpected panic: check for this/); }); it('handles settle panics', async () => { const promise = native.panicDuringSettle(Promise.resolve(6)); await assert.isRejected(promise, /unexpected panic: check for this/); }); }); describe('exception recovery', () => { it('handles pre-await throws', async () => { const promise = native.throwPreAwait(Promise.resolve(6)); await assert.isRejected(promise, /^check for this$/); }); it('handles callback throws', async () => { const promise = native.throwDuringCallback(Promise.resolve(6)); await assert.isRejected(promise, /^check for this$/); }); it('handles post-await throws', async () => { const promise = native.throwPostAwait(Promise.resolve(6)); await assert.isRejected(promise, /^check for this$/); }); it('handles settle throws', async () => { const promise = native.throwDuringSettle(Promise.resolve(6)); await assert.isRejected(promise, /^check for this$/); }); }); });