Increase test coverage (#1237)

* Fixed a SplitPayment test

* Deleted unnecessary function.

* Improved PostDeliveryCrowdsale tests.

* Improved RefundableCrowdsale tests.

* Improved MintedCrowdsale tests.

* Improved IncreasingPriceCrowdsale tests.

* Fixed a CappedCrowdsale test.

* Improved TimedCrowdsale tests.

* Improved descriptions of added tests.
This commit is contained in:
Nicolás Venturo
2018-08-24 19:23:11 -03:00
committed by Francisco Giordano
parent a466e76d26
commit a9f910d34f
9 changed files with 264 additions and 178 deletions

View File

@ -22,8 +22,8 @@ contract IncreasingPriceCrowdsale is TimedCrowdsale {
* @param _finalRate Number of tokens a buyer gets per wei at the end of the crowdsale
*/
constructor(uint256 _initialRate, uint256 _finalRate) public {
require(_initialRate >= _finalRate);
require(_finalRate > 0);
require(_initialRate >= _finalRate);
initialRate = _initialRate;
finalRate = _finalRate;
}

View File

@ -47,7 +47,7 @@ contract Heritable is Ownable {
* before the heir can take ownership.
*/
constructor(uint256 _heartbeatTimeout) public {
setHeartbeatTimeout(_heartbeatTimeout);
heartbeatTimeout_ = _heartbeatTimeout;
}
function setHeir(address _newHeir) public onlyOwner {
@ -113,13 +113,6 @@ contract Heritable is Ownable {
timeOfDeath_ = 0;
}
function setHeartbeatTimeout(uint256 _newHeartbeatTimeout)
internal onlyOwner
{
require(ownerLives());
heartbeatTimeout_ = _newHeartbeatTimeout;
}
function ownerLives() internal view returns (bool) {
return timeOfDeath_ == 0;
}

View File

@ -19,17 +19,19 @@ contract('CappedCrowdsale', function ([_, wallet]) {
beforeEach(async function () {
this.token = await SimpleToken.new();
this.crowdsale = await CappedCrowdsale.new(rate, wallet, this.token.address, cap);
await this.token.transfer(this.crowdsale.address, tokenSupply);
});
describe('creating a valid crowdsale', function () {
it('should fail with zero cap', async function () {
it('rejects a cap of zero', async function () {
await expectThrow(
CappedCrowdsale.new(rate, wallet, 0, this.token.address),
CappedCrowdsale.new(rate, wallet, this.token.address, 0),
EVMRevert,
);
});
context('with crowdsale', function () {
beforeEach(async function () {
this.crowdsale = await CappedCrowdsale.new(rate, wallet, this.token.address, cap);
await this.token.transfer(this.crowdsale.address, tokenSupply);
});
describe('accepting payments', function () {
@ -71,3 +73,4 @@ contract('CappedCrowdsale', function ([_, wallet]) {
});
});
});
});

View File

@ -2,6 +2,7 @@ const { ether } = require('../helpers/ether');
const { advanceBlock } = require('../helpers/advanceToBlock');
const { increaseTimeTo, duration } = require('../helpers/increaseTime');
const { latestTime } = require('../helpers/latestTime');
const { assertRevert } = require('../helpers/assertRevert');
const BigNumber = web3.BigNumber;
@ -32,6 +33,22 @@ contract('IncreasingPriceCrowdsale', function ([_, investor, wallet, purchaser])
this.closingTime = this.startTime + duration.weeks(1);
this.afterClosingTime = this.closingTime + duration.seconds(1);
this.token = await SimpleToken.new();
});
it('rejects a final rate larger than the initial rate', async function () {
await assertRevert(IncreasingPriceCrowdsale.new(
this.startTime, this.closingTime, wallet, this.token.address, initialRate, initialRate.plus(1)
));
});
it('rejects a final rate of zero', async function () {
await assertRevert(IncreasingPriceCrowdsale.new(
this.startTime, this.closingTime, wallet, this.token.address, initialRate, 0
));
});
context('with crowdsale', function () {
beforeEach(async function () {
this.crowdsale = await IncreasingPriceCrowdsale.new(
this.startTime, this.closingTime, wallet, this.token.address, initialRate, finalRate
);
@ -81,3 +98,4 @@ contract('IncreasingPriceCrowdsale', function ([_, investor, wallet, purchaser])
});
});
});
});

View File

@ -1,11 +1,13 @@
const { shouldBehaveLikeMintedCrowdsale } = require('./MintedCrowdsale.behavior');
const { ether } = require('../helpers/ether');
const { assertRevert } = require('../helpers/assertRevert');
const BigNumber = web3.BigNumber;
const MintedCrowdsale = artifacts.require('MintedCrowdsaleImpl');
const MintableToken = artifacts.require('MintableToken');
const RBACMintableToken = artifacts.require('RBACMintableToken');
const StandardToken = artifacts.require('StandardToken');
contract('MintedCrowdsale', function ([_, investor, wallet, purchaser]) {
const rate = new BigNumber(1000);
@ -40,4 +42,19 @@ contract('MintedCrowdsale', function ([_, investor, wallet, purchaser]) {
shouldBehaveLikeMintedCrowdsale([_, investor, wallet, purchaser], rate, value);
});
describe('using non-mintable token', function () {
beforeEach(async function () {
this.token = await StandardToken.new();
this.crowdsale = await MintedCrowdsale.new(rate, wallet, this.token.address);
});
it('rejects bare payments', async function () {
await assertRevert(this.crowdsale.send(value));
});
it('rejects token purchases', async function () {
await assertRevert(this.crowdsale.buyTokens(investor, { value: value, from: purchaser }));
});
});
});

View File

@ -16,7 +16,6 @@ const SimpleToken = artifacts.require('SimpleToken');
contract('PostDeliveryCrowdsale', function ([_, investor, wallet, purchaser]) {
const rate = new BigNumber(1);
const value = ether(42);
const tokenSupply = new BigNumber('1e22');
before(async function () {
@ -27,7 +26,6 @@ contract('PostDeliveryCrowdsale', function ([_, investor, wallet, purchaser]) {
beforeEach(async function () {
this.openingTime = (await latestTime()) + duration.weeks(1);
this.closingTime = this.openingTime + duration.weeks(1);
this.beforeEndTime = this.closingTime - duration.hours(1);
this.afterClosingTime = this.closingTime + duration.seconds(1);
this.token = await SimpleToken.new();
this.crowdsale = await PostDeliveryCrowdsale.new(
@ -36,30 +34,41 @@ contract('PostDeliveryCrowdsale', function ([_, investor, wallet, purchaser]) {
await this.token.transfer(this.crowdsale.address, tokenSupply);
});
it('should not immediately assign tokens to beneficiary', async function () {
context('after opening time', function () {
beforeEach(async function () {
await increaseTimeTo(this.openingTime);
});
context('with bought tokens', function () {
const value = ether(42);
beforeEach(async function () {
await this.crowdsale.buyTokens(investor, { value: value, from: purchaser });
});
it('does not immediately assign tokens to beneficiaries', async function () {
(await this.token.balanceOf(investor)).should.be.bignumber.equal(0);
});
it('should not allow beneficiaries to withdraw tokens before crowdsale ends', async function () {
await increaseTimeTo(this.beforeEndTime);
await this.crowdsale.buyTokens(investor, { value: value, from: purchaser });
it('does not allow beneficiaries to withdraw tokens before crowdsale ends', async function () {
await expectThrow(this.crowdsale.withdrawTokens({ from: investor }), EVMRevert);
});
it('should allow beneficiaries to withdraw tokens after crowdsale ends', async function () {
await increaseTimeTo(this.openingTime);
await this.crowdsale.buyTokens(investor, { value: value, from: purchaser });
context('after closing time', function () {
beforeEach(async function () {
await increaseTimeTo(this.afterClosingTime);
await this.crowdsale.withdrawTokens({ from: investor });
});
it('should return the amount of tokens bought', async function () {
await increaseTimeTo(this.openingTime);
await this.crowdsale.buyTokens(investor, { value: value, from: purchaser });
await increaseTimeTo(this.afterClosingTime);
it('allows beneficiaries to withdraw tokens', async function () {
await this.crowdsale.withdrawTokens({ from: investor });
(await this.token.balanceOf(investor)).should.be.bignumber.equal(value);
});
it('rejects multiple withdrawals', async function () {
await this.crowdsale.withdrawTokens({ from: investor });
await expectThrow(this.crowdsale.withdrawTokens({ from: investor }), EVMRevert);
});
});
});
});
});

View File

@ -30,16 +30,12 @@ contract('RefundableCrowdsale', function ([_, owner, wallet, investor, purchaser
this.openingTime = (await latestTime()) + duration.weeks(1);
this.closingTime = this.openingTime + duration.weeks(1);
this.afterClosingTime = this.closingTime + duration.seconds(1);
this.preWalletBalance = await ethGetBalance(wallet);
this.token = await SimpleToken.new();
this.crowdsale = await RefundableCrowdsale.new(
this.openingTime, this.closingTime, rate, wallet, this.token.address, goal, { from: owner }
);
await this.token.transfer(this.crowdsale.address, tokenSupply);
});
describe('creating a valid crowdsale', function () {
it('should fail with zero goal', async function () {
it('rejects a goal of zero', async function () {
await expectThrow(
RefundableCrowdsale.new(
this.openingTime, this.closingTime, rate, wallet, this.token.address, 0, { from: owner }
@ -47,39 +43,72 @@ contract('RefundableCrowdsale', function ([_, owner, wallet, investor, purchaser
EVMRevert,
);
});
context('with crowdsale', function () {
beforeEach(async function () {
this.crowdsale = await RefundableCrowdsale.new(
this.openingTime, this.closingTime, rate, wallet, this.token.address, goal, { from: owner }
);
await this.token.transfer(this.crowdsale.address, tokenSupply);
});
it('should deny refunds before end', async function () {
context('before opening time', function () {
it('denies refunds', async function () {
await expectThrow(this.crowdsale.claimRefund({ from: investor }), EVMRevert);
});
});
context('after opening time', function () {
beforeEach(async function () {
await increaseTimeTo(this.openingTime);
});
it('denies refunds', async function () {
await expectThrow(this.crowdsale.claimRefund({ from: investor }), EVMRevert);
});
it('should deny refunds after end if goal was reached', async function () {
await increaseTimeTo(this.openingTime);
await this.crowdsale.sendTransaction({ value: goal, from: investor });
await increaseTimeTo(this.afterClosingTime);
await expectThrow(this.crowdsale.claimRefund({ from: investor }), EVMRevert);
});
it('should allow refunds after end if goal was not reached', async function () {
await increaseTimeTo(this.openingTime);
context('with unreached goal', function () {
beforeEach(async function () {
await this.crowdsale.sendTransaction({ value: lessThanGoal, from: investor });
});
context('after closing time and finalization', function () {
beforeEach(async function () {
await increaseTimeTo(this.afterClosingTime);
await this.crowdsale.finalize({ from: owner });
});
it('refunds', async function () {
const pre = await ethGetBalance(investor);
await this.crowdsale.claimRefund({ from: investor, gasPrice: 0 });
const post = await ethGetBalance(investor);
post.minus(pre).should.be.bignumber.equal(lessThanGoal);
});
});
});
it('should forward funds to wallet after end if goal was reached', async function () {
await increaseTimeTo(this.openingTime);
context('with reached goal', function () {
beforeEach(async function () {
await this.crowdsale.sendTransaction({ value: goal, from: investor });
});
context('after closing time and finalization', function () {
beforeEach(async function () {
await increaseTimeTo(this.afterClosingTime);
const pre = await ethGetBalance(wallet);
await this.crowdsale.finalize({ from: owner });
const post = await ethGetBalance(wallet);
post.minus(pre).should.be.bignumber.equal(goal);
});
it('denies refunds', async function () {
await expectThrow(this.crowdsale.claimRefund({ from: investor }), EVMRevert);
});
it('forwards funds to wallet', async function () {
const postWalletBalance = await ethGetBalance(wallet);
postWalletBalance.minus(this.preWalletBalance).should.be.bignumber.equal(goal);
});
});
});
});
});
});

View File

@ -29,6 +29,22 @@ contract('TimedCrowdsale', function ([_, investor, wallet, purchaser]) {
this.closingTime = this.openingTime + duration.weeks(1);
this.afterClosingTime = this.closingTime + duration.seconds(1);
this.token = await SimpleToken.new();
});
it('rejects an opening time in the past', async function () {
await expectThrow(TimedCrowdsale.new(
(await latestTime()) - duration.days(1), this.closingTime, rate, wallet, this.token.address
), EVMRevert);
});
it('rejects a closing time before the opening time', async function () {
await expectThrow(TimedCrowdsale.new(
this.openingTime, this.openingTime - duration.seconds(1), rate, wallet, this.token.address
), EVMRevert);
});
context('with crowdsale', function () {
beforeEach(async function () {
this.crowdsale = await TimedCrowdsale.new(this.openingTime, this.closingTime, rate, wallet, this.token.address);
await this.token.transfer(this.crowdsale.address, tokenSupply);
});
@ -58,3 +74,4 @@ contract('TimedCrowdsale', function ([_, investor, wallet, purchaser]) {
});
});
});
});

View File

@ -14,28 +14,28 @@ contract('SplitPayment', function ([_, owner, payee1, payee2, payee3, nonpayee1,
const amount = web3.toWei(1.0, 'ether');
const ZERO_ADDRESS = '0x0000000000000000000000000000000000000000';
it('cannot be created with no payees', async function () {
it('rejects an empty set of payees', async function () {
await expectThrow(SplitPayment.new([], []), EVMRevert);
});
it('requires shares for each payee', async function () {
it('rejects more payees than shares', async function () {
await expectThrow(SplitPayment.new([payee1, payee2, payee3], [20, 30]), EVMRevert);
});
it('requires a payee for each share', async function () {
it('rejects more shares than payees', async function () {
await expectThrow(SplitPayment.new([payee1, payee2], [20, 30, 40]), EVMRevert);
});
it('requires non-null payees', async function () {
it('rejects null payees', async function () {
await expectThrow(SplitPayment.new([payee1, ZERO_ADDRESS], [20, 30]), EVMRevert);
});
it('requires non-zero shares', async function () {
it('rejects zero-valued shares', async function () {
await expectThrow(SplitPayment.new([payee1, payee2], [20, 0]), EVMRevert);
});
it('rejects repeated payees', async function () {
await expectThrow(SplitPayment.new([payee1, payee1], [20, 0]), EVMRevert);
await expectThrow(SplitPayment.new([payee1, payee1], [20, 30]), EVMRevert);
});
context('once deployed', function () {