embark/packages/embarkjs/test/blockchain_test.js

78 lines
2.4 KiB
JavaScript
Raw Normal View History

/* global before describe it require */
const {startRPCMockServer, TestProvider} = require('./test');
const {assert} = require('chai');
const Blockchain = require('../dist/blockchain');
refactor: be consistent with callbacks and promises If a function receives a callback argument then it should not return a promise if the caller's callback will be invoked. Both invoking a callback and returning a promise can lead to at best confusion (in code review and at runtime) and at worst non-deterministic behavior, such as race conditions. Also, a caller supplying a callback may not handle a returned promise, leading to unhandled rejection errors. Refactor all readily identified functions where a callback argument can be supplied but the function returns a promise regardless. Make use of `callbackify` and `promisify` where it made sense to do so during the refactoring. Some callsites of the revised functions may have been accidentally overlooked and still need to be updated. Some functions that take callback arguments may execute them synchronously, at odds with control flow of a returned promise (if a callback wasn't supplied). Such cases should be identified and fixed so that asynchronous behavior is fully consistent whether the caller supplies a callback or receives a promise. Make sure promises that pass control flow to a callback ignore rejections, since those should be handled by the callback. Don't return promise instances unnecessarily from async functions (since they always return promises) and change some functions that return promises to async functions (where it's simple to do so). Whisper was using an ad hoc promise-like `messageEvents` object. However, that object behaved more like an observable, since promises either resolve or reject, and only do so one time. `messageEvents` was also intertwined with callbacks. Replace `messageEvents` with RxJS Observable. `listenTo` now returns Observable instances and callers can subscribe to them. `Blockchain.connect` of embarkjs could suffer from a race condition where tasks associated with `execWhenReady` might be ongoing when `connect`'s returned promise resolves/rejects (or a caller supplied callback fires). Attempt to ensure that returned-promise / supplied-callback control flow proceeds only after `execWhenReady` tasks have finished. The control flow involved is... rather involved, and it could use some further review and refactoring. Bump webpack and the hard-source-plugin for webpack. [util]: https://www.npmjs.com/package/util
2019-02-11 06:08:01 +00:00
const {promisify} = require('util');
describe('Blockchain', () => {
describe('#connect', () => {
before(() => {
Blockchain.default.registerProvider('web3', TestProvider);
Blockchain.default.setProvider('web3', {});
});
const scenarios = [
{
description: 'should not keep trying other connections if connected',
servers: [true, true],
visited: [true, false],
error: false
},
{
description: 'should keep trying other connections if not connected',
servers: [false, true],
visited: [true, true],
error: false
},
{
description: 'should return error if no connections succeed',
servers: [false, false],
visited: [true, true],
error: true
}
];
scenarios.forEach(({description, ...scenario}) => {
it(description, async () => {
const makeServers = async () => {
const servers = await Promise.all(
refactor: be consistent with callbacks and promises If a function receives a callback argument then it should not return a promise if the caller's callback will be invoked. Both invoking a callback and returning a promise can lead to at best confusion (in code review and at runtime) and at worst non-deterministic behavior, such as race conditions. Also, a caller supplying a callback may not handle a returned promise, leading to unhandled rejection errors. Refactor all readily identified functions where a callback argument can be supplied but the function returns a promise regardless. Make use of `callbackify` and `promisify` where it made sense to do so during the refactoring. Some callsites of the revised functions may have been accidentally overlooked and still need to be updated. Some functions that take callback arguments may execute them synchronously, at odds with control flow of a returned promise (if a callback wasn't supplied). Such cases should be identified and fixed so that asynchronous behavior is fully consistent whether the caller supplies a callback or receives a promise. Make sure promises that pass control flow to a callback ignore rejections, since those should be handled by the callback. Don't return promise instances unnecessarily from async functions (since they always return promises) and change some functions that return promises to async functions (where it's simple to do so). Whisper was using an ad hoc promise-like `messageEvents` object. However, that object behaved more like an observable, since promises either resolve or reject, and only do so one time. `messageEvents` was also intertwined with callbacks. Replace `messageEvents` with RxJS Observable. `listenTo` now returns Observable instances and callers can subscribe to them. `Blockchain.connect` of embarkjs could suffer from a race condition where tasks associated with `execWhenReady` might be ongoing when `connect`'s returned promise resolves/rejects (or a caller supplied callback fires). Attempt to ensure that returned-promise / supplied-callback control flow proceeds only after `execWhenReady` tasks have finished. The control flow involved is... rather involved, and it could use some further review and refactoring. Bump webpack and the hard-source-plugin for webpack. [util]: https://www.npmjs.com/package/util
2019-02-11 06:08:01 +00:00
scenario.servers.map(server => (
promisify(startRPCMockServer)({successful: server})
))
);
const dappConnection = servers.map(server => server.connectionString);
return {servers, dappConnection};
};
// test Blockchain.connect() using callback
let {servers, dappConnection} = await makeServers();
await new Promise((resolve, reject) => {
Blockchain.default.connect({dappConnection}, err => {
try {
refactor: be consistent with callbacks and promises If a function receives a callback argument then it should not return a promise if the caller's callback will be invoked. Both invoking a callback and returning a promise can lead to at best confusion (in code review and at runtime) and at worst non-deterministic behavior, such as race conditions. Also, a caller supplying a callback may not handle a returned promise, leading to unhandled rejection errors. Refactor all readily identified functions where a callback argument can be supplied but the function returns a promise regardless. Make use of `callbackify` and `promisify` where it made sense to do so during the refactoring. Some callsites of the revised functions may have been accidentally overlooked and still need to be updated. Some functions that take callback arguments may execute them synchronously, at odds with control flow of a returned promise (if a callback wasn't supplied). Such cases should be identified and fixed so that asynchronous behavior is fully consistent whether the caller supplies a callback or receives a promise. Make sure promises that pass control flow to a callback ignore rejections, since those should be handled by the callback. Don't return promise instances unnecessarily from async functions (since they always return promises) and change some functions that return promises to async functions (where it's simple to do so). Whisper was using an ad hoc promise-like `messageEvents` object. However, that object behaved more like an observable, since promises either resolve or reject, and only do so one time. `messageEvents` was also intertwined with callbacks. Replace `messageEvents` with RxJS Observable. `listenTo` now returns Observable instances and callers can subscribe to them. `Blockchain.connect` of embarkjs could suffer from a race condition where tasks associated with `execWhenReady` might be ongoing when `connect`'s returned promise resolves/rejects (or a caller supplied callback fires). Attempt to ensure that returned-promise / supplied-callback control flow proceeds only after `execWhenReady` tasks have finished. The control flow involved is... rather involved, and it could use some further review and refactoring. Bump webpack and the hard-source-plugin for webpack. [util]: https://www.npmjs.com/package/util
2019-02-11 06:08:01 +00:00
assert(scenario.error ? err : !err);
servers.forEach((server, idx) => {
assert.strictEqual(server.visited, scenario.visited[idx]);
});
resolve();
} catch (e) {
reject(e);
}
});
});
// test Blockchain.connect() without callback
({servers, dappConnection} = await makeServers());
try {
await Blockchain.default.connect({dappConnection});
} catch (e) {
if (!scenario.error) throw e;
}
servers.forEach((server, idx) => {
assert.strictEqual(server.visited, scenario.visited[idx]);
});
});
});
});
});