add tests for crowdsale contracts
This commit is contained in:
81
test/CappedCrowdsale.js
Normal file
81
test/CappedCrowdsale.js
Normal 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
143
test/Crowdsale.js
Normal 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)
|
||||
})
|
||||
|
||||
})
|
||||
|
||||
})
|
||||
61
test/FinalizableCrowdsale.js
Normal file
61
test/FinalizableCrowdsale.js
Normal 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
61
test/RefundVault.js
Normal 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)
|
||||
})
|
||||
|
||||
})
|
||||
67
test/RefundableCrowdsale.js
Normal file
67
test/RefundableCrowdsale.js
Normal 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)
|
||||
})
|
||||
|
||||
})
|
||||
21
test/helpers/CappedCrowdsaleImpl.sol
Normal file
21
test/helpers/CappedCrowdsaleImpl.sol
Normal 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
1
test/helpers/EVMThrow.js
Normal file
@ -0,0 +1 @@
|
||||
export default 'invalid opcode'
|
||||
20
test/helpers/FinalizableCrowdsaleImpl.sol
Normal file
20
test/helpers/FinalizableCrowdsaleImpl.sol
Normal 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()
|
||||
{
|
||||
}
|
||||
|
||||
}
|
||||
21
test/helpers/RefundableCrowdsaleImpl.sol
Normal file
21
test/helpers/RefundableCrowdsaleImpl.sol
Normal 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)
|
||||
{
|
||||
}
|
||||
|
||||
}
|
||||
22
test/helpers/advanceToBlock.js
Normal file
22
test/helpers/advanceToBlock.js
Normal 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
3
test/helpers/ether.js
Normal file
@ -0,0 +1,3 @@
|
||||
export default function ether(n) {
|
||||
return new web3.BigNumber(web3.toWei(n, 'ether'))
|
||||
}
|
||||
Reference in New Issue
Block a user