UNPKG

lk-test-helpers

Version:

Test helpers for smart contract development

605 lines (490 loc) 15.9 kB
'use strict'; function _interopDefault (ex) { return (ex && (typeof ex === 'object') && 'default' in ex) ? ex['default'] : ex; } var lkWeb3Utils = require('lk-web3-utils'); var moment = _interopDefault(require('moment')); var chai = _interopDefault(require('chai')); var chaiBigNumber = _interopDefault(require('chai-bignumber')); var utils = _interopDefault(require('ethereumjs-util')); var asyncToGenerator = function (fn) { return function () { var gen = fn.apply(this, arguments); return new Promise(function (resolve, reject) { function step(key, arg) { try { var info = gen[key](arg); var value = info.value; } catch (error) { reject(error); return; } if (info.done) { resolve(value); } else { return Promise.resolve(value).then(function (value) { step("next", value); }, function (err) { step("throw", err); }); } } return step("next"); }); }; }; var slicedToArray = function () { function sliceIterator(arr, i) { var _arr = []; var _n = true; var _d = false; var _e = undefined; try { for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i["return"]) _i["return"](); } finally { if (_d) throw _e; } } return _arr; } return function (arr, i) { if (Array.isArray(arr)) { return arr; } else if (Symbol.iterator in Object(arr)) { return sliceIterator(arr, i); } else { throw new TypeError("Invalid attempt to destructure non-iterable instance"); } }; }(); function advanceBlockProvider(web3) { return function advanceBlock() { return new Promise(function (resolve, reject) { web3.currentProvider.sendAsync({ jsonrpc: '2.0', method: 'evm_mine', id: Date.now() }, function (err, res) { return err ? reject(err) : resolve(res); }); }); }; } // Advances the block number so that the last mined block is `number`. function advanceToBlockProvider(web3) { var advanceBlock = advanceBlockProvider(web3); return function () { var _ref = asyncToGenerator( /*#__PURE__*/regeneratorRuntime.mark(function _callee(number) { return regeneratorRuntime.wrap(function _callee$(_context) { while (1) { switch (_context.prev = _context.next) { case 0: if (!(web3.eth.blockNumber > number)) { _context.next = 2; break; } throw Error('block number ' + number + ' is in the past (current is ' + web3.eth.blockNumber + ')'); case 2: if (!(web3.eth.blockNumber < number)) { _context.next = 7; break; } _context.next = 5; return advanceBlock(); case 5: _context.next = 2; break; case 7: case 'end': return _context.stop(); } } }, _callee, this); })); function advanceToBlock(_x) { return _ref.apply(this, arguments); } return advanceToBlock; }(); } function assertJump(error) { assert.isAbove(error.message.search('invalid opcode'), -1, 'Invalid opcode error must be returned'); } var asyncReturnErr = (function () { var _ref = asyncToGenerator( /*#__PURE__*/regeneratorRuntime.mark(function _callee(asyncFn) { return regeneratorRuntime.wrap(function _callee$(_context) { while (1) { switch (_context.prev = _context.next) { case 0: _context.prev = 0; _context.next = 3; return asyncFn; case 3: _context.next = 8; break; case 5: _context.prev = 5; _context.t0 = _context["catch"](0); return _context.abrupt("return", _context.t0); case 8: case "end": return _context.stop(); } } }, _callee, this, [[0, 5]]); })); function asyncReturnErr(_x) { return _ref.apply(this, arguments); } return asyncReturnErr; })(); var constants = { ZERO_ADDRESS: '0x0000000000000000000000000000000000000000', ZERO_BYTES32: "0x0000000000000000000000000000000000000000000000000000000000000000", MAX_UINT256: new lkWeb3Utils.BigNumber(2).pow(256).minus(1) }; function ether(n) { return new lkWeb3Utils.BigNumber(lkWeb3Utils.toWei(n, 'ether')); } // Returns a moment.js instance representing the time of the last mined block var latestTimeProvider = (function (web3) { return function latestTime() { return moment.unix(web3.eth.getBlock('latest').timestamp); }; }); // Increases testrpc time by the passed duration in seconds function increaseTimeProvider(web3) { return function increaseTime(duration) { var id = Date.now(); return new Promise(function (resolve, reject) { web3.currentProvider.sendAsync({ jsonrpc: '2.0', method: 'evm_increaseTime', params: [duration], id: id }, function (err1) { if (err1) return reject(err1); web3.currentProvider.sendAsync({ jsonrpc: '2.0', method: 'evm_mine', id: id + 1 }, function (err2, res) { return err2 ? reject(err2) : resolve(res); }); }); }); }; } /** * Beware that due to the need of calling two separate testrpc methods and rpc calls overhead * it's hard to increase time precisely to a target point so design your test to tolerate * small fluctuations from time to time. * * @param target time in seconds */ function increaseTimeToProvider(web3) { var increaseTime = increaseTimeProvider(web3); var latestTime = latestTimeProvider(web3); return function increaseTimeTo(target) { var now = latestTime(); if (target < now) throw Error('Cannot increase current time(' + now + ') to a moment in the past(' + target + ')'); var diff = target - now; return increaseTime(diff); }; } function increaseTestrpcTimeProvider(web3) { var increaseTime = increaseTimeProvider(web3); var latestTime = latestTimeProvider(web3); return function () { var _ref = asyncToGenerator( /*#__PURE__*/regeneratorRuntime.mark(function _callee(duration) { var _latestTime; return regeneratorRuntime.wrap(function _callee$(_context) { while (1) { switch (_context.prev = _context.next) { case 0: _context.next = 2; return increaseTime(duration); case 2: _context.next = 4; return latestTime().unix(); case 4: _latestTime = _context.sent; return _context.abrupt('return', _latestTime); case 6: case 'end': return _context.stop(); } } }, _callee, this); })); function increaseTestrpcTime(_x) { return _ref.apply(this, arguments); } return increaseTestrpcTime; }(); } var should = chai.use(chaiBigNumber(lkWeb3Utils.BigNumber)).should(); function inLogs(logs, eventName) { var eventArgs = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {}; var event = logs.find(function (e) { if (e.event === eventName) { var _iteratorNormalCompletion = true; var _didIteratorError = false; var _iteratorError = undefined; try { for (var _iterator = Object.entries(eventArgs)[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) { var _ref3 = _step.value; var _ref2 = slicedToArray(_ref3, 2); var k = _ref2[0]; var v = _ref2[1]; contains(e.args, k, v); } } catch (err) { _didIteratorError = true; _iteratorError = err; } finally { try { if (!_iteratorNormalCompletion && _iterator.return) { _iterator.return(); } } finally { if (_didIteratorError) { throw _iteratorError; } } } return true; } }); should.exist(event); return event; } var inTransaction = function () { var _ref4 = asyncToGenerator( /*#__PURE__*/regeneratorRuntime.mark(function _callee(tx, eventName) { var eventArgs = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {}; var _ref5, logs; return regeneratorRuntime.wrap(function _callee$(_context) { while (1) { switch (_context.prev = _context.next) { case 0: _context.next = 2; return tx; case 2: _ref5 = _context.sent; logs = _ref5.logs; return _context.abrupt('return', inLogs(logs, eventName, eventArgs)); case 5: case 'end': return _context.stop(); } } }, _callee, this); })); return function inTransaction(_x3, _x4) { return _ref4.apply(this, arguments); }; }(); function contains(args, key, value) { if (isBigNumber(args[key])) { args[key].should.be.bignumber.equal(value); } else { args[key].should.be.equal(value); } } function isBigNumber(object) { return object.isBigNumber || object instanceof lkWeb3Utils.BigNumber || object.constructor && object.constructor.name === 'BigNumber'; } var expectEvent = { inLogs: inLogs, inTransaction: inTransaction }; // Hash and add same prefix to the hash that testrpc use. function hashMessage(message) { var messageHex = new Buffer(utils.sha3(message).toString('hex'), 'hex'); var prefix = utils.toBuffer('\x19Ethereum Signed Message:\n' + messageHex.length.toString()); return utils.bufferToHex(utils.sha3(Buffer.concat([prefix, messageHex]))); } var shouldFailWithMessage = function () { var _ref = asyncToGenerator( /*#__PURE__*/regeneratorRuntime.mark(function _callee(promise, message) { return regeneratorRuntime.wrap(function _callee$(_context) { while (1) { switch (_context.prev = _context.next) { case 0: _context.prev = 0; _context.next = 3; return promise; case 3: _context.next = 9; break; case 5: _context.prev = 5; _context.t0 = _context['catch'](0); _context.t0.message.should.include(message, 'Wrong failure type'); return _context.abrupt('return'); case 9: should$1.fail('Expected \'' + message + '\' failure not received'); case 10: case 'end': return _context.stop(); } } }, _callee, this, [[0, 5]]); })); return function shouldFailWithMessage(_x, _x2) { return _ref.apply(this, arguments); }; }(); var reverting = function () { var _ref2 = asyncToGenerator( /*#__PURE__*/regeneratorRuntime.mark(function _callee2(promise) { return regeneratorRuntime.wrap(function _callee2$(_context2) { while (1) { switch (_context2.prev = _context2.next) { case 0: _context2.next = 2; return shouldFailWithMessage(promise, 'revert'); case 2: case 'end': return _context2.stop(); } } }, _callee2, this); })); return function reverting(_x3) { return _ref2.apply(this, arguments); }; }(); var throwing = function () { var _ref3 = asyncToGenerator( /*#__PURE__*/regeneratorRuntime.mark(function _callee3(promise) { return regeneratorRuntime.wrap(function _callee3$(_context3) { while (1) { switch (_context3.prev = _context3.next) { case 0: _context3.next = 2; return shouldFailWithMessage(promise, 'invalid opcode'); case 2: case 'end': return _context3.stop(); } } }, _callee3, this); })); return function throwing(_x4) { return _ref3.apply(this, arguments); }; }(); var outOfGas = function () { var _ref4 = asyncToGenerator( /*#__PURE__*/regeneratorRuntime.mark(function _callee4(promise) { return regeneratorRuntime.wrap(function _callee4$(_context4) { while (1) { switch (_context4.prev = _context4.next) { case 0: _context4.next = 2; return shouldFailWithMessage(promise, 'out of gas'); case 2: case 'end': return _context4.stop(); } } }, _callee4, this); })); return function outOfGas(_x5) { return _ref4.apply(this, arguments); }; }(); var should$1 = chai.should(); var shouldFail = { reverting: reverting, throwing: throwing, outOfGas: outOfGas }; // timer for tests specific to testrpc function timerProvider(web3) { return function timer(s) { return new Promise(function (resolve, reject) { web3.currentProvider.sendAsync({ jsonrpc: '2.0', method: 'evm_increaseTime', params: [s], // 60 seaconds, may need to be hex, I forget id: new Date().getTime() // Id of the request; anything works, really }, function (err) { if (err) return reject(err); resolve(); }); //setTimeout(() => resolve(), s * 1000 + 600) // 600ms breathing room for testrpc to sync }); }; } var toPromise = (function (func) { return function () { for (var _len = arguments.length, args = Array(_len), _key = 0; _key < _len; _key++) { args[_key] = arguments[_key]; } return new Promise(function (accept, reject) { return func.apply(undefined, args.concat([function (error, data) { return error ? reject(error) : accept(data); }])); }); }; }); function transactionMinedProvider(web3) { //from https://gist.github.com/xavierlepretre/88682e871f4ad07be4534ae560692ee6 var transactionMined = web3.eth.transactionMined = function (txnHash, interval) { var _transactionReceiptAsync; interval = interval ? interval : 500; _transactionReceiptAsync = function transactionReceiptAsync(txnHash, resolve, reject) { try { var receipt = web3.eth.getTransactionReceipt(txnHash); if (receipt === null) { setTimeout(function () { _transactionReceiptAsync(txnHash, resolve, reject); }, interval); } else { resolve(receipt); } } catch (e) { reject(e); } }; if (Array.isArray(txnHash)) { var promises = []; txnHash.forEach(function (oneTxHash) { promises.push(web3.eth.getTransactionReceiptMined(oneTxHash, interval)); }); return Promise.all(promises); } else { return new Promise(function (resolve, reject) { _transactionReceiptAsync(txnHash, resolve, reject); }); } }; return transactionMined; } var main = (function (web3) { return { advanceBlock: advanceBlockProvider(web3), advanceToBlock: advanceToBlockProvider(web3), assertJump: assertJump, asyncReturnErr: asyncReturnErr, constants: constants, ether: ether, expectEvent: expectEvent, latestTime: latestTimeProvider(web3), increaseTestrpcTime: increaseTestrpcTimeProvider(web3), increaseTime: increaseTimeProvider(web3), increaseTimeTo: increaseTimeToProvider(web3), hashMessage: hashMessage, shouldFail: shouldFail, timer: timerProvider(web3), toPromise: toPromise, transactionMined: transactionMinedProvider(web3) }; }); module.exports = main; //# sourceMappingURL=main.js.map