Merge pull request #2314 from 0xProject/tests/3.0/MixinSchedulerUnitTests

Unit tests for MixinScheduler
This commit is contained in:
Greg Hysz
2019-11-05 15:27:20 -08:00
committed by GitHub
7 changed files with 225 additions and 1 deletions

View File

@@ -25,6 +25,10 @@
{
"note": "Fix overflow w/ `LibFixedMath._mul(-1, -2*255)",
"pr": 2311
},
{
"note": "Unit tests for MixinScheduler",
"pr": 2314
}
]
},

View File

@@ -0,0 +1,96 @@
/*
Copyright 2019 ZeroEx Intl.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
pragma solidity ^0.5.9;
pragma experimental ABIEncoderV2;
import "./TestStaking.sol";
contract TestMixinScheduler is
TestStaking
{
uint256 public testDeployedTimestamp;
event GoToNextEpochTestInfo(
uint256 oldEpoch,
uint256 blockTimestamp
);
constructor(
address wethAddress,
address zrxVaultAddress
)
public
TestStaking(
wethAddress,
zrxVaultAddress
)
{
_addAuthorizedAddress(msg.sender);
init();
_removeAuthorizedAddressAtIndex(msg.sender, 0);
// Record time of deployment
// solhint-disable-next-line not-rely-on-time
testDeployedTimestamp = block.timestamp;
}
/// @dev Tests `_goToNextEpoch`.
/// Configures internal variables such taht `epochEndTime` will be
/// less-than, equal-to, or greater-than the block timestamp.
/// @param epochEndTimeDelta Set to desired `epochEndTime - block.timestamp`
function goToNextEpochTest(int256 epochEndTimeDelta)
public
{
// solhint-disable-next-line not-rely-on-time
uint256 blockTimestamp = block.timestamp;
// Emit info used by client-side test code
emit GoToNextEpochTestInfo(
currentEpoch,
blockTimestamp
);
// (i) In `_goToNextEpoch` we compute:
// `epochEndTime = currentEpochStartTimeInSeconds + epochDurationInSeconds`
// (ii) We want adjust internal state such that:
// `epochEndTime - block.timestamp = epochEndTimeDelta`, or
// `currentEpochStartTimeInSeconds + epochDurationInSeconds - block.timestamp = epochEndTimeDelta`
//
// To do this, we:
// (i) Set `epochDurationInSeconds` to a constant value of 1, and
// (ii) Rearrange the eqn above to get:
// `currentEpochStartTimeInSeconds = epochEndTimeDelta + block.timestamp - epochDurationInSeconds`
epochDurationInSeconds = 1;
currentEpochStartTimeInSeconds =
uint256(epochEndTimeDelta + int256(blockTimestamp) - int256(epochDurationInSeconds));
// Test internal function
_goToNextEpoch();
}
/// @dev Tests `_initMixinScheduler`
/// @param _currentEpochStartTimeInSeconds Sets `currentEpochStartTimeInSeconds` to this value before test.
function initMixinSchedulerTest(uint256 _currentEpochStartTimeInSeconds)
public
{
currentEpochStartTimeInSeconds = _currentEpochStartTimeInSeconds;
_initMixinScheduler();
}
}

View File

@@ -37,7 +37,7 @@
},
"config": {
"abis:comment": "This list is auto-generated by contracts-gen. Don't edit manually.",
"abis": "./generated-artifacts/@(IStaking|IStakingEvents|IStakingProxy|IStorage|IStorageInit|IStructs|IZrxVault|LibCobbDouglas|LibFixedMath|LibFixedMathRichErrors|LibSafeDowncast|LibStakingRichErrors|MixinAbstract|MixinConstants|MixinCumulativeRewards|MixinDeploymentConstants|MixinExchangeFees|MixinExchangeManager|MixinFinalizer|MixinParams|MixinScheduler|MixinStake|MixinStakeBalances|MixinStakeStorage|MixinStakingPool|MixinStakingPoolRewards|MixinStorage|Staking|StakingProxy|TestAssertStorageParams|TestCobbDouglas|TestCumulativeRewardTracking|TestDelegatorRewards|TestExchangeManager|TestFinalizer|TestInitTarget|TestLibFixedMath|TestLibSafeDowncast|TestMixinParams|TestMixinStake|TestMixinStakeBalances|TestMixinStakeStorage|TestMixinStakingPool|TestMixinStakingPoolRewards|TestProtocolFees|TestProxyDestination|TestStaking|TestStakingNoWETH|TestStakingProxy|TestStakingProxyUnit|TestStorageLayoutAndConstants|ZrxVault).json"
"abis": "./generated-artifacts/@(IStaking|IStakingEvents|IStakingProxy|IStorage|IStorageInit|IStructs|IZrxVault|LibCobbDouglas|LibFixedMath|LibFixedMathRichErrors|LibSafeDowncast|LibStakingRichErrors|MixinAbstract|MixinConstants|MixinCumulativeRewards|MixinDeploymentConstants|MixinExchangeFees|MixinExchangeManager|MixinFinalizer|MixinParams|MixinScheduler|MixinStake|MixinStakeBalances|MixinStakeStorage|MixinStakingPool|MixinStakingPoolRewards|MixinStorage|Staking|StakingProxy|TestAssertStorageParams|TestCobbDouglas|TestCumulativeRewardTracking|TestDelegatorRewards|TestExchangeManager|TestFinalizer|TestInitTarget|TestLibFixedMath|TestLibSafeDowncast|TestMixinParams|TestMixinScheduler|TestMixinStake|TestMixinStakeBalances|TestMixinStakeStorage|TestMixinStakingPool|TestMixinStakingPoolRewards|TestProtocolFees|TestProxyDestination|TestStaking|TestStakingNoWETH|TestStakingProxy|TestStakingProxyUnit|TestStorageLayoutAndConstants|ZrxVault).json"
},
"repository": {
"type": "git",

View File

@@ -44,6 +44,7 @@ import * as TestInitTarget from '../generated-artifacts/TestInitTarget.json';
import * as TestLibFixedMath from '../generated-artifacts/TestLibFixedMath.json';
import * as TestLibSafeDowncast from '../generated-artifacts/TestLibSafeDowncast.json';
import * as TestMixinParams from '../generated-artifacts/TestMixinParams.json';
import * as TestMixinScheduler from '../generated-artifacts/TestMixinScheduler.json';
import * as TestMixinStake from '../generated-artifacts/TestMixinStake.json';
import * as TestMixinStakeBalances from '../generated-artifacts/TestMixinStakeBalances.json';
import * as TestMixinStakeStorage from '../generated-artifacts/TestMixinStakeStorage.json';
@@ -98,6 +99,7 @@ export const artifacts = {
TestLibFixedMath: TestLibFixedMath as ContractArtifact,
TestLibSafeDowncast: TestLibSafeDowncast as ContractArtifact,
TestMixinParams: TestMixinParams as ContractArtifact,
TestMixinScheduler: TestMixinScheduler as ContractArtifact,
TestMixinStake: TestMixinStake as ContractArtifact,
TestMixinStakeBalances: TestMixinStakeBalances as ContractArtifact,
TestMixinStakeStorage: TestMixinStakeStorage as ContractArtifact,

View File

@@ -42,6 +42,7 @@ export * from '../generated-wrappers/test_init_target';
export * from '../generated-wrappers/test_lib_fixed_math';
export * from '../generated-wrappers/test_lib_safe_downcast';
export * from '../generated-wrappers/test_mixin_params';
export * from '../generated-wrappers/test_mixin_scheduler';
export * from '../generated-wrappers/test_mixin_stake';
export * from '../generated-wrappers/test_mixin_stake_balances';
export * from '../generated-wrappers/test_mixin_stake_storage';

View File

@@ -0,0 +1,120 @@
import { blockchainTests, constants, expect, verifyEventsFromLogs } from '@0x/contracts-test-utils';
import { StakingRevertErrors } from '@0x/order-utils';
import { BigNumber } from '@0x/utils';
import { LogWithDecodedArgs } from 'ethereum-types';
import {
artifacts,
TestMixinSchedulerContract,
TestMixinSchedulerEvents,
TestMixinSchedulerGoToNextEpochTestInfoEventArgs,
} from '../../src';
import { constants as stakingConstants } from '../utils/constants';
blockchainTests.resets('MixinScheduler unit tests', env => {
let testContract: TestMixinSchedulerContract;
before(async () => {
// Deploy contracts
testContract = await TestMixinSchedulerContract.deployFrom0xArtifactAsync(
artifacts.TestMixinScheduler,
env.provider,
env.txDefaults,
artifacts,
stakingConstants.NIL_ADDRESS,
stakingConstants.NIL_ADDRESS,
);
});
describe('getCurrentEpochEarliestEndTimeInSeconds', () => {
it('Should return the sum of `epoch start time + epoch duration`', async () => {
const testDeployedTimestamp = await testContract.testDeployedTimestamp.callAsync();
const epochDurationInSeconds = await testContract.epochDurationInSeconds.callAsync();
const expectedCurrentEpochEarliestEndTimeInSeconds = testDeployedTimestamp.plus(epochDurationInSeconds);
const currentEpochEarliestEndTimeInSeconds = await testContract.getCurrentEpochEarliestEndTimeInSeconds.callAsync();
expect(currentEpochEarliestEndTimeInSeconds).to.bignumber.equal(
expectedCurrentEpochEarliestEndTimeInSeconds,
);
});
});
describe('_initMixinScheduler', () => {
it('Should succeed if scheduler is not yet initialized (`currentEpochStartTimeInSeconds == 0`)', async () => {
const initCurrentEpochStartTimeInSeconds = constants.ZERO_AMOUNT;
const txReceipt = await testContract.initMixinSchedulerTest.awaitTransactionSuccessAsync(
initCurrentEpochStartTimeInSeconds,
);
// Assert `currentEpochStartTimeInSeconds` was properly initialized
const blockTimestamp = await env.web3Wrapper.getBlockTimestampAsync(txReceipt.blockNumber);
const currentEpochStartTimeInSeconds = await testContract.currentEpochStartTimeInSeconds.callAsync();
expect(currentEpochStartTimeInSeconds).to.bignumber.equal(blockTimestamp);
// Assert `currentEpoch` was properly initialized
const currentEpoch = await testContract.currentEpoch.callAsync();
expect(currentEpoch).to.bignumber.equal(1);
});
it('Should revert if scheduler is already initialized (`currentEpochStartTimeInSeconds != 0`)', async () => {
const initCurrentEpochStartTimeInSeconds = new BigNumber(10);
const tx = testContract.initMixinSchedulerTest.awaitTransactionSuccessAsync(
initCurrentEpochStartTimeInSeconds,
);
return expect(tx).to.revertWith(
new StakingRevertErrors.InitializationError(
StakingRevertErrors.InitializationErrorCodes.MixinSchedulerAlreadyInitialized,
),
);
});
});
describe('_goToNextEpoch', () => {
it('Should succeed if epoch end time is strictly less than to block timestamp', async () => {
const epochEndTimeDelta = new BigNumber(-10);
const txReceipt = await testContract.goToNextEpochTest.awaitTransactionSuccessAsync(epochEndTimeDelta);
const currentEpoch = await testContract.currentEpoch.callAsync();
const currentEpochStartTimeInSeconds = await testContract.currentEpochStartTimeInSeconds.callAsync();
verifyEventsFromLogs(
txReceipt.logs,
[
{
oldEpoch: currentEpoch.minus(1),
blockTimestamp: currentEpochStartTimeInSeconds,
},
],
TestMixinSchedulerEvents.GoToNextEpochTestInfo,
);
});
it('Should succeed if epoch end time is equal to block timestamp', async () => {
const epochEndTimeDelta = constants.ZERO_AMOUNT;
const txReceipt = await testContract.goToNextEpochTest.awaitTransactionSuccessAsync(epochEndTimeDelta);
// tslint:disable-next-line no-unnecessary-type-assertion
const testLog: TestMixinSchedulerGoToNextEpochTestInfoEventArgs = (txReceipt.logs[0] as LogWithDecodedArgs<
TestMixinSchedulerGoToNextEpochTestInfoEventArgs
>).args;
const currentEpoch = await testContract.currentEpoch.callAsync();
const currentEpochStartTimeInSeconds = await testContract.currentEpochStartTimeInSeconds.callAsync();
expect(currentEpoch).to.bignumber.equal(testLog.oldEpoch.plus(1));
expect(currentEpochStartTimeInSeconds).to.bignumber.equal(testLog.blockTimestamp);
});
it('Should revert if epoch end time is strictly greater than block timestamp', async () => {
const epochEndTimeDelta = new BigNumber(10);
const tx = testContract.goToNextEpochTest.awaitTransactionSuccessAsync(epochEndTimeDelta);
try {
await tx;
// tslint:disable-next-line no-empty
} catch (e) {}
// Mine the block that this tx would've been in.
await env.web3Wrapper.mineBlockAsync();
const blockNumber = await env.web3Wrapper.getBlockNumberAsync();
const blockTimestampAsNumber = await env.web3Wrapper.getBlockTimestampAsync(blockNumber);
const blockTimestamp = new BigNumber(blockTimestampAsNumber);
const epochEndTime = blockTimestamp.plus(epochEndTimeDelta);
return expect(tx).to.revertWith(
new StakingRevertErrors.BlockTimestampTooLowError(epochEndTime, blockTimestamp),
);
});
});
});

View File

@@ -42,6 +42,7 @@
"generated-artifacts/TestLibFixedMath.json",
"generated-artifacts/TestLibSafeDowncast.json",
"generated-artifacts/TestMixinParams.json",
"generated-artifacts/TestMixinScheduler.json",
"generated-artifacts/TestMixinStake.json",
"generated-artifacts/TestMixinStakeBalances.json",
"generated-artifacts/TestMixinStakeStorage.json",