-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
First version of Ministro contract tool.
- Loading branch information
Showing
23 changed files
with
6,137 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
{ | ||
"presets": ["env"] | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
# Unifying editor configurations for all developers: | ||
# For details see http://editorconfig.org/ | ||
|
||
root = true | ||
|
||
[*] | ||
end_of_line = lf | ||
indent_size = 2 | ||
indent_style = space | ||
insert_final_newline = true | ||
trim_trailing_whitespace = true |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
{ | ||
"extends": "airbnb-base", | ||
"env": { | ||
"mocha": true | ||
}, | ||
"globals": { | ||
"artifacts": true, | ||
"assert": true, | ||
"beforeEach": true, | ||
"contract": true, | ||
"describe": true, | ||
"it": true, | ||
"web3": true | ||
}, | ||
"rules": { | ||
"no-console": "off", | ||
"prefer-destructuring": ["error", { | ||
"VariableDeclarator": { | ||
"array": true, | ||
"object": true | ||
}, | ||
"AssignmentExpression": { | ||
"array": false, // this will allow for: var bar = foo[0] <- access array with index | ||
"object": true | ||
} | ||
}, { | ||
"enforceForRenamedProperties": false | ||
}] | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
.idea/ | ||
node_modules/ | ||
build/ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
# Changelog | ||
All notable changes to this project will be documented in this file. | ||
|
||
The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) | ||
and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html). | ||
|
||
## [0.1.0] - 2018-11-06 | ||
|
||
First version of Ministro contract tool. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,2 +1,87 @@ | ||
# ministro-tool | ||
JavaScript tool created for helping in process of testing solidity smart contracts. | ||
# Ministro Tool for Ministro Contract | ||
|
||
JavaScript tool created for helping in process of testing solidity smart contracts. | ||
|
||
## What MinistroTool is? | ||
|
||
Short answer: it is a tool that helps you create ministro contract. | ||
Now let's explain what ministro contract is. | ||
|
||
When you testing solidity contracts, you have a lot of repeated code like: | ||
|
||
``` | ||
it('should do something', () => { | ||
await instance.foo() | ||
assert(foo) | ||
} | ||
it('should do something again', () => { | ||
await instance.foo() | ||
assert(foo) | ||
} | ||
``` | ||
|
||
Once you tested your `foo()` in one scenario, in most cases you not checking it | ||
next time, you using `foo()` (in some other scenarios...) - and this is not good. | ||
|
||
*MinistroContract* allows you to write test conditions once and use them | ||
in each case scenario with just one line of code. | ||
Thanks to this, your tests are very strong, easy to understand | ||
and you have clean, short code. | ||
|
||
## How can I use ministro tool/contract? | ||
|
||
The best way to understand how to use it, is to review the simple example in `test` directory. | ||
|
||
Basically you need to create `ministroContract` that reflect all methods | ||
that real smart contract has. | ||
Each `ministroContract` must have all methods from smart contract (including public readers). | ||
Each method must have all possible test you can perform base | ||
**ONLY (!)** on input (arguments) and output (events, return values) data. | ||
You also must cover scenario, when method throw (if this is a case). | ||
|
||
When you do all that, you just execute a method on `ministroContract` | ||
and ech time you do it in your tests scenarios, all this tests that you wrote | ||
(in ministro method) will be executed and checked. | ||
|
||
There are also some additional helpers mechanisms here like: | ||
* checking is transaction was successful or throw | ||
* read all events for transaction | ||
|
||
### Ministro Methods | ||
|
||
Each ministro method must have: | ||
* parameters that are equal to smart contract instance (required) | ||
* object with transaction parameters (optional) | ||
* parameter that inform us, if this execution is expected to throw (optional) | ||
|
||
## How to use it in tests? | ||
|
||
Please review the code of example `test/ministro-contracts/ministroOwnable.js` - | ||
its pretty simple, so you should be able to understand how to use it. | ||
|
||
**Note:** this test might not be enough for each cases, if some external | ||
data are present and ministro contract do not have access to them, | ||
you need to check them directly in test file (outside ministro). | ||
|
||
**DO NOT** modify ministro by adding additional params to the method, | ||
because this is not how it should work. | ||
**MinistroContract test should be the same for ALL cases** and they should | ||
work the same each time, so anybody can use it in any scenario | ||
and not worry abut *your special case*. | ||
|
||
|
||
## Installation | ||
|
||
``` | ||
git clone <this-repo> | ||
git hf init | ||
npm install | ||
``` | ||
|
||
### Run test | ||
|
||
``` | ||
npm run lint | ||
npm run test | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
pragma solidity ^0.4.24; | ||
|
||
contract Migrations { | ||
address public owner; | ||
uint public last_completed_migration; | ||
|
||
modifier restricted() { | ||
if (msg.sender == owner) _; | ||
} | ||
|
||
constructor () public { | ||
owner = msg.sender; | ||
} | ||
|
||
function setCompleted(uint completed) public restricted { | ||
last_completed_migration = completed; | ||
} | ||
|
||
function upgrade(address new_address) public restricted { | ||
Migrations upgraded = Migrations(new_address); | ||
upgraded.setCompleted(last_completed_migration); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,78 @@ | ||
pragma solidity ^0.4.24; | ||
|
||
|
||
/** | ||
* @title Ownable | ||
* @dev The Ownable contract has an owner address, and provides basic authorization control | ||
* functions, this simplifies the implementation of "user permissions". | ||
*/ | ||
contract Ownable { | ||
address private _owner; | ||
|
||
|
||
event OwnershipRenounced(address indexed previousOwner); | ||
event OwnershipTransferred( | ||
address indexed previousOwner, | ||
address indexed newOwner | ||
); | ||
|
||
|
||
/** | ||
* @dev The Ownable constructor sets the original `owner` of the contract to the sender | ||
* account. | ||
*/ | ||
constructor() public { | ||
_owner = msg.sender; | ||
} | ||
|
||
/** | ||
* @return the address of the owner. | ||
*/ | ||
function owner() public view returns(address) { | ||
return _owner; | ||
} | ||
|
||
/** | ||
* @dev Throws if called by any account other than the owner. | ||
*/ | ||
modifier onlyOwner() { | ||
require(isOwner()); | ||
_; | ||
} | ||
|
||
/** | ||
* @return true if `msg.sender` is the owner of the contract. | ||
*/ | ||
function isOwner() public view returns(bool) { | ||
return msg.sender == _owner; | ||
} | ||
|
||
/** | ||
* @dev Allows the current owner to relinquish control of the contract. | ||
* @notice Renouncing to ownership will leave the contract without an owner. | ||
* It will not be possible to call the functions with the `onlyOwner` | ||
* modifier anymore. | ||
*/ | ||
function renounceOwnership() public onlyOwner { | ||
emit OwnershipRenounced(_owner); | ||
_owner = address(0); | ||
} | ||
|
||
/** | ||
* @dev Allows the current owner to transfer control of the contract to a newOwner. | ||
* @param newOwner The address to transfer ownership to. | ||
*/ | ||
function transferOwnership(address newOwner) public onlyOwner { | ||
_transferOwnership(newOwner); | ||
} | ||
|
||
/** | ||
* @dev Transfers control of the contract to a newOwner. | ||
* @param newOwner The address to transfer ownership to. | ||
*/ | ||
function _transferOwnership(address newOwner) internal { | ||
require(newOwner != address(0)); | ||
emit OwnershipTransferred(_owner, newOwner); | ||
_owner = newOwner; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
const Migrations = artifacts.require('./Migrations.sol'); | ||
|
||
module.exports = (deployer) => { | ||
deployer.deploy(Migrations); | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
const Ownable = artifacts.require('./Ownable.sol'); | ||
|
||
module.exports = (deployer) => { | ||
deployer.deploy(Ownable); | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,55 @@ | ||
/** | ||
* @param {!Function.<!Promise>} action. | ||
* @param {!Number | !string | !BigNumber} gasToUse. | ||
* @returns {!Promise} which throws unless it hit a valid error. | ||
*/ | ||
module.exports = function expectedExceptionPromise(action, gasToUse) { | ||
let didThrow = false; | ||
return new Promise(((resolve, reject) => { | ||
try { | ||
resolve(action()); | ||
} catch (e) { | ||
didThrow = true; | ||
reject(e); | ||
} | ||
})) | ||
.then((txObj) => { | ||
if (didThrow === false) throw new Error(`Action should throw: ${action.toString()}`); | ||
|
||
if (typeof txn === 'string') { return web3.eth.getTransactionReceiptMined(txObj); } // regular tx hash | ||
|
||
if (typeof txObj.receipt !== 'undefined') { return txObj.receipt; } // truffle-contract function call | ||
|
||
if (typeof txObj.transactionHash === 'string') { return web3.eth.getTransactionReceiptMined(txObj.transactionHash); } // deployment | ||
|
||
return txObj; // Unknown last case | ||
}) | ||
.then( | ||
(receipt) => { | ||
// We are in Geth | ||
if (typeof receipt.status !== 'undefined') { | ||
// Byzantium | ||
assert.strictEqual(parseInt(receipt.status, 10), 0, 'should have reverted'); | ||
} else { | ||
// Pre Byzantium | ||
assert.equal(receipt.gasUsed, gasToUse, 'should have used all the gas'); | ||
} | ||
}, | ||
(e) => { | ||
if ((`${e}`).indexOf('invalid JUMP') > -1 | ||
|| (`${e}`).indexOf('out of gas') > -1 | ||
|| (`${e}`).indexOf('invalid opcode') > -1 | ||
|| (`${e}`).indexOf('revert') > -1) { | ||
// We are in TestRPC | ||
} else if ((`${e}`).indexOf('please check your gas amount') > -1) { | ||
// We are in Geth for a deployment | ||
} else if ((`${e}`).indexOf('Cannot send value to non-payable function') > -1) { | ||
// We are in ganache | ||
} else if ((`${e}`).indexOf('account not recognized') > -1) { | ||
// Check if msg.sender exists and its not generated by random numbers | ||
} else { | ||
throw e; | ||
} | ||
}, | ||
); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
module.exports = function getTransactionReceiptMined(txHash, interval) { | ||
const self = this; | ||
const transactionReceiptAsync = (resolve, reject) => { | ||
self.getTransactionReceipt(txHash, (error, receipt) => { | ||
if (error) { | ||
reject(error); | ||
} else if (receipt === null) { | ||
setTimeout( | ||
() => transactionReceiptAsync(resolve, reject), | ||
interval || 500, | ||
); | ||
} else { | ||
resolve(receipt); | ||
} | ||
}); | ||
}; | ||
|
||
if (Array.isArray(txHash)) { | ||
return Promise.all(txHash.map(oneTxHash => getTransactionReceiptMined(oneTxHash, interval))); | ||
} if (typeof txHash === 'string') { | ||
return new Promise(transactionReceiptAsync); | ||
} | ||
throw new Error(`Invalid Type: ${txHash}`); | ||
}; | ||
|
||
web3.eth.getTransactionReceiptMined = getTransactionReceiptMined; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
require('babel-register'); | ||
require('babel-polyfill'); | ||
|
||
module.exports = require('./ministroExecute'); |
Oops, something went wrong.