lk-test-helpers
Version:
Test helpers for smart contract development
605 lines (490 loc) • 15.9 kB
JavaScript
;
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