add tests for crowdsale contracts

This commit is contained in:
Francisco Giordano
2017-06-30 14:57:58 -03:00
parent 4d55d8fa8e
commit fadb2cf47e
12 changed files with 504 additions and 0 deletions

81
test/CappedCrowdsale.js Normal file
View File

@ -0,0 +1,81 @@
import ether from './helpers/ether'
import advanceToBlock from './helpers/advanceToBlock'
import EVMThrow from './helpers/EVMThrow'
const BigNumber = web3.BigNumber
require('chai')
.use(require('chai-as-promised'))
.use(require('chai-bignumber')(BigNumber))
.should()
const CappedCrowdsale = artifacts.require('./helpers/CappedCrowdsaleImpl.sol')
const MintableToken = artifacts.require('MintableToken')
contract('CappedCrowdsale', function ([_, wallet]) {
const rate = new BigNumber(1000)
const cap = ether(300)
const lessThanCap = ether(60)
beforeEach(async function () {
this.startBlock = web3.eth.blockNumber + 10
this.endBlock = web3.eth.blockNumber + 20
this.crowdsale = await CappedCrowdsale.new(this.startBlock, this.endBlock, rate, wallet, cap)
this.token = MintableToken.at(await this.crowdsale.token())
})
describe('accepting payments', function () {
beforeEach(async function () {
await advanceToBlock(this.startBlock - 1)
})
it('should accept payments within cap', async function () {
await this.crowdsale.send(cap.minus(lessThanCap)).should.be.fulfilled
await this.crowdsale.send(lessThanCap).should.be.fulfilled
})
it('should reject payments outside cap', async function () {
await this.crowdsale.send(cap)
await this.crowdsale.send(1).should.be.rejectedWith(EVMThrow)
})
it('should reject payments that exceed cap', async function () {
await this.crowdsale.send(cap.plus(1)).should.be.rejectedWith(EVMThrow)
})
})
describe('ending', function () {
beforeEach(async function () {
await advanceToBlock(this.startBlock - 1)
})
it('should not be ended if under cap', async function () {
let hasEnded = await this.crowdsale.hasEnded()
hasEnded.should.equal(false)
await this.crowdsale.send(lessThanCap)
hasEnded = await this.crowdsale.hasEnded()
hasEnded.should.equal(false)
})
it('should not be ended if just under cap', async function () {
await this.crowdsale.send(cap.minus(1))
let hasEnded = await this.crowdsale.hasEnded()
hasEnded.should.equal(false)
})
it('should be ended if cap reached', async function () {
await this.crowdsale.send(cap)
let hasEnded = await this.crowdsale.hasEnded()
hasEnded.should.equal(true)
})
})
})

143
test/Crowdsale.js Normal file
View File

@ -0,0 +1,143 @@
import ether from './helpers/ether'
import advanceToBlock from './helpers/advanceToBlock'
import EVMThrow from './helpers/EVMThrow'
const BigNumber = web3.BigNumber
const should = require('chai')
.use(require('chai-as-promised'))
.use(require('chai-bignumber')(BigNumber))
.should()
const Crowdsale = artifacts.require('Crowdsale')
const MintableToken = artifacts.require('MintableToken')
contract('Crowdsale', function ([_, investor, wallet, purchaser]) {
const rate = new BigNumber(1000)
const value = ether(42)
const expectedTokenAmount = rate.mul(value)
beforeEach(async function () {
this.startBlock = web3.eth.blockNumber + 10
this.endBlock = web3.eth.blockNumber + 20
this.crowdsale = await Crowdsale.new(this.startBlock, this.endBlock, rate, wallet)
this.token = MintableToken.at(await this.crowdsale.token())
})
it('should be token owner', async function () {
const owner = await this.token.owner()
owner.should.equal(this.crowdsale.address)
})
it('should be ended only after end', async function () {
let ended = await this.crowdsale.hasEnded()
ended.should.equal(false)
await advanceToBlock(this.endBlock + 1)
ended = await this.crowdsale.hasEnded()
ended.should.equal(true)
})
describe('accepting payments', function () {
it('should reject payments before start', async function () {
await this.crowdsale.send(value).should.be.rejectedWith(EVMThrow)
await this.crowdsale.buyTokens(investor, value, {from: purchaser}).should.be.rejectedWith(EVMThrow)
})
it('should accept payments after start', async function () {
await advanceToBlock(this.startBlock - 1)
await this.crowdsale.send(value).should.be.fulfilled
await this.crowdsale.buyTokens(investor, {value: value, from: purchaser}).should.be.fulfilled
})
it('should reject payments after end', async function () {
await advanceToBlock(this.endBlock)
await this.crowdsale.send(value).should.be.rejectedWith(EVMThrow)
await this.crowdsale.buyTokens(investor, {value: value, from: purchaser}).should.be.rejectedWith(EVMThrow)
})
})
describe('high-level purchase', function () {
beforeEach(async function() {
await advanceToBlock(this.startBlock)
})
it('should log purchase', async function () {
const {logs} = await this.crowdsale.sendTransaction({value: value, from: investor})
const event = logs.find(e => e.event === 'TokenPurchase')
should.exist(event)
event.args.purchaser.should.equal(investor)
event.args.beneficiary.should.equal(investor)
event.args.value.should.be.bignumber.equal(value)
event.args.amount.should.be.bignumber.equal(expectedTokenAmount)
})
it('should increase totalSupply', async function () {
await this.crowdsale.send(value)
const totalSupply = await this.token.totalSupply()
totalSupply.should.be.bignumber.equal(expectedTokenAmount)
})
it('should assign tokens to sender', async function () {
await this.crowdsale.sendTransaction({value: value, from: investor})
let balance = await this.token.balanceOf(investor);
balance.should.be.bignumber.equal(expectedTokenAmount)
})
it('should forward funds to wallet', async function () {
const pre = web3.eth.getBalance(wallet)
await this.crowdsale.sendTransaction({value, from: investor})
const post = web3.eth.getBalance(wallet)
post.minus(pre).should.be.bignumber.equal(value)
})
})
describe('low-level purchase', function () {
beforeEach(async function() {
await advanceToBlock(this.startBlock)
})
it('should log purchase', async function () {
const {logs} = await this.crowdsale.buyTokens(investor, {value: value, from: purchaser})
const event = logs.find(e => e.event === 'TokenPurchase')
should.exist(event)
event.args.purchaser.should.equal(purchaser)
event.args.beneficiary.should.equal(investor)
event.args.value.should.be.bignumber.equal(value)
event.args.amount.should.be.bignumber.equal(expectedTokenAmount)
})
it('should increase totalSupply', async function () {
await this.crowdsale.buyTokens(investor, {value, from: purchaser})
const totalSupply = await this.token.totalSupply()
totalSupply.should.be.bignumber.equal(expectedTokenAmount)
})
it('should assign tokens to beneficiary', async function () {
await this.crowdsale.buyTokens(investor, {value, from: purchaser})
const balance = await this.token.balanceOf(investor)
balance.should.be.bignumber.equal(expectedTokenAmount)
})
it('should forward funds to wallet', async function () {
const pre = web3.eth.getBalance(wallet)
await this.crowdsale.buyTokens(investor, {value, from: purchaser})
const post = web3.eth.getBalance(wallet)
post.minus(pre).should.be.bignumber.equal(value)
})
})
})

View File

@ -0,0 +1,61 @@
import advanceToBlock from './helpers/advanceToBlock'
import EVMThrow from './helpers/EVMThrow'
const BigNumber = web3.BigNumber
const should = require('chai')
.use(require('chai-as-promised'))
.use(require('chai-bignumber')(BigNumber))
.should()
const FinalizableCrowdsale = artifacts.require('./helpers/FinalizableCrowdsaleImpl.sol')
const MintableToken = artifacts.require('MintableToken')
contract('FinalizableCrowdsale', function ([_, owner, wallet, thirdparty]) {
const rate = new BigNumber(1000)
beforeEach(async function () {
this.startBlock = web3.eth.blockNumber + 10
this.endBlock = web3.eth.blockNumber + 20
this.crowdsale = await FinalizableCrowdsale.new(this.startBlock, this.endBlock, rate, wallet, {from: owner})
this.token = MintableToken.at(await this.crowdsale.token())
})
it('cannot be finalized before ending', async function () {
await this.crowdsale.finalize({from: owner}).should.be.rejectedWith(EVMThrow)
})
it('cannot be finalized by third party after ending', async function () {
await advanceToBlock(this.endBlock)
await this.crowdsale.finalize({from: thirdparty}).should.be.rejectedWith(EVMThrow)
})
it('can be finalized by owner after ending', async function () {
await advanceToBlock(this.endBlock)
await this.crowdsale.finalize({from: owner}).should.be.fulfilled
})
it('cannot be finalized twice', async function () {
await advanceToBlock(this.endBlock + 1)
await this.crowdsale.finalize({from: owner})
await this.crowdsale.finalize({from: owner}).should.be.rejectedWith(EVMThrow)
})
it('logs finalized', async function () {
await advanceToBlock(this.endBlock)
const {logs} = await this.crowdsale.finalize({from: owner})
const event = logs.find(e => e.event === 'Finalized')
should.exist(event)
})
it('finishes minting of token', async function () {
await advanceToBlock(this.endBlock)
await this.crowdsale.finalize({from: owner})
const finished = await this.token.mintingFinished()
finished.should.equal(true)
})
})

61
test/RefundVault.js Normal file
View File

@ -0,0 +1,61 @@
const BigNumber = web3.BigNumber
require('chai')
.use(require('chai-as-promised'))
.use(require('chai-bignumber')(BigNumber))
.should()
import ether from './helpers/ether'
import EVMThrow from './helpers/EVMThrow'
const RefundVault = artifacts.require('RefundVault')
contract('RefundVault', function ([_, owner, wallet, investor]) {
const value = ether(42)
beforeEach(async function () {
this.vault = await RefundVault.new(wallet, {from: owner})
})
it('should accept contributions', async function () {
await this.vault.deposit(investor, {value, from: owner}).should.be.fulfilled
})
it('should not refund contribution during active state', async function () {
await this.vault.deposit(investor, {value, from: owner})
await this.vault.refund(investor).should.be.rejectedWith(EVMThrow)
})
it('only owner can enter refund mode', async function () {
await this.vault.enableRefunds({from: _}).should.be.rejectedWith(EVMThrow)
await this.vault.enableRefunds({from: owner}).should.be.fulfilled
})
it('should refund contribution after entering refund mode', async function () {
await this.vault.deposit(investor, {value, from: owner})
await this.vault.enableRefunds({from: owner})
const pre = web3.eth.getBalance(investor)
await this.vault.refund(investor)
const post = web3.eth.getBalance(investor)
post.minus(pre).should.be.bignumber.equal(value)
})
it('only owner can close', async function () {
await this.vault.close({from: _}).should.be.rejectedWith(EVMThrow)
await this.vault.close({from: owner}).should.be.fulfilled
})
it('should forward funds to wallet after closing', async function () {
await this.vault.deposit(investor, {value, from: owner})
const pre = web3.eth.getBalance(wallet)
await this.vault.close({from: owner})
const post = web3.eth.getBalance(wallet)
post.minus(pre).should.be.bignumber.equal(value)
})
})

View File

@ -0,0 +1,67 @@
import ether from './helpers/ether'
import advanceToBlock from './helpers/advanceToBlock'
import EVMThrow from './helpers/EVMThrow'
const BigNumber = web3.BigNumber
require('chai')
.use(require('chai-as-promised'))
.use(require('chai-bignumber')(BigNumber))
.should()
const RefundableCrowdsale = artifacts.require('./helpers/RefundableCrowdsaleImpl.sol')
contract('RefundableCrowdsale', function ([_, owner, wallet, investor]) {
const rate = new BigNumber(1000)
const goal = ether(800)
const lessThanGoal = ether(750)
beforeEach(async function () {
this.startBlock = web3.eth.blockNumber + 10
this.endBlock = web3.eth.blockNumber + 20
this.crowdsale = await RefundableCrowdsale.new(this.startBlock, this.endBlock, rate, wallet, goal, {from: owner})
})
it('should deny refunds before end', async function () {
await this.crowdsale.claimRefund({from: investor}).should.be.rejectedWith(EVMThrow)
await advanceToBlock(this.endBlock - 1)
await this.crowdsale.claimRefund({from: investor}).should.be.rejectedWith(EVMThrow)
})
it('should deny refunds after end if goal was reached', async function () {
await advanceToBlock(this.startBlock - 1)
await this.crowdsale.sendTransaction({value: goal, from: investor})
await advanceToBlock(this.endBlock)
await this.crowdsale.claimRefund({from: investor}).should.be.rejectedWith(EVMThrow)
})
it('should allow refunds after end if goal was not reached', async function () {
await advanceToBlock(this.startBlock - 1)
await this.crowdsale.sendTransaction({value: lessThanGoal, from: investor})
await advanceToBlock(this.endBlock)
await this.crowdsale.finalize({from: owner})
const pre = web3.eth.getBalance(investor)
await this.crowdsale.claimRefund({from: investor, gasPrice: 0})
.should.be.fulfilled
const post = web3.eth.getBalance(investor)
post.minus(pre).should.be.bignumber.equal(lessThanGoal)
})
it('should forward funds to wallet after end if goal was reached', async function () {
await advanceToBlock(this.startBlock - 1)
await this.crowdsale.sendTransaction({value: goal, from: investor})
await advanceToBlock(this.endBlock)
const pre = web3.eth.getBalance(wallet)
await this.crowdsale.finalize({from: owner})
const post = web3.eth.getBalance(wallet)
post.minus(pre).should.be.bignumber.equal(goal)
})
})

View File

@ -0,0 +1,21 @@
pragma solidity ^0.4.11;
import '../../contracts/crowdsale/CappedCrowdsale.sol';
contract CappedCrowdsaleImpl is CappedCrowdsale {
function CappedCrowdsaleImpl (
uint256 _startBlock,
uint256 _endBlock,
uint256 _rate,
address _wallet,
uint256 _cap
)
Crowdsale(_startBlock, _endBlock, _rate, _wallet)
CappedCrowdsale(_cap)
{
}
}

1
test/helpers/EVMThrow.js Normal file
View File

@ -0,0 +1 @@
export default 'invalid opcode'

View File

@ -0,0 +1,20 @@
pragma solidity ^0.4.11;
import '../../contracts/crowdsale/FinalizableCrowdsale.sol';
contract FinalizableCrowdsaleImpl is FinalizableCrowdsale {
function FinalizableCrowdsaleImpl (
uint256 _startBlock,
uint256 _endBlock,
uint256 _rate,
address _wallet
)
Crowdsale(_startBlock, _endBlock, _rate, _wallet)
FinalizableCrowdsale()
{
}
}

View File

@ -0,0 +1,21 @@
pragma solidity ^0.4.11;
import '../../contracts/crowdsale/RefundableCrowdsale.sol';
contract RefundableCrowdsaleImpl is RefundableCrowdsale {
function RefundableCrowdsaleImpl (
uint256 _startBlock,
uint256 _endBlock,
uint256 _rate,
address _wallet,
uint256 _goal
)
Crowdsale(_startBlock, _endBlock, _rate, _wallet)
RefundableCrowdsale(_goal)
{
}
}

View File

@ -0,0 +1,22 @@
export function advanceBlock() {
return new Promise((resolve, reject) => {
web3.currentProvider.sendAsync({
jsonrpc: '2.0',
method: 'evm_mine',
id: Date.now(),
}, (err, res) => {
return err ? reject(err) : resolve(res)
})
})
}
// Advances the block number so that the last mined block is `number`.
export default async function advanceToBlock(number) {
if (web3.eth.blockNumber > number) {
throw Error(`block number ${number} is in the past (current is ${web3.eth.blockNumber})`)
}
while (web3.eth.blockNumber < number) {
await advanceBlock()
}
}

3
test/helpers/ether.js Normal file
View File

@ -0,0 +1,3 @@
export default function ether(n) {
return new web3.BigNumber(web3.toWei(n, 'ether'))
}