Simulation logging, hopefully address function assertion lifetime issue

This commit is contained in:
Michael Zhu
2019-11-26 13:47:11 -08:00
parent d11cdcd5d2
commit faf306ad23
13 changed files with 227 additions and 194 deletions

View File

@@ -1,6 +1,6 @@
import { StakingPoolById, StoredBalance } from '@0x/contracts-staking';
import { expect } from '@0x/contracts-test-utils';
import { BigNumber, logUtils } from '@0x/utils';
import { BigNumber } from '@0x/utils';
import { TxData } from 'ethereum-types';
import { DeploymentManager } from '../deployment_manager';
@@ -20,41 +20,36 @@ export function validCreateStakingPoolAssertion(
): FunctionAssertion<[number, boolean], string, string> {
const { stakingWrapper } = deployment.staking;
return new FunctionAssertion<[number, boolean], string, string>(
stakingWrapper.createStakingPool.bind(stakingWrapper),
{
// Returns the expected ID of th created pool
before: async () => {
const lastPoolId = await stakingWrapper.lastPoolId().callAsync();
// Effectively the last poolId + 1, but as a bytestring
return `0x${new BigNumber(lastPoolId)
.plus(1)
.toString(16)
.padStart(64, '0')}`;
},
after: async (
expectedPoolId: string,
result: FunctionResult,
args: [number, boolean],
txData: Partial<TxData>,
) => {
const [operatorShare, shouldAddMakerAsOperator] = args;
logUtils.log(`createStakingPool(${operatorShare}, ${shouldAddMakerAsOperator}) => ${expectedPoolId}`);
// Checks the logs for the new poolId, verifies that it is as expected
const log = result.receipt!.logs[0];
const actualPoolId = (log as any).args.poolId;
expect(actualPoolId).to.equal(expectedPoolId);
// Adds the new pool to local state
pools[actualPoolId] = {
operator: txData.from!,
operatorShare,
delegatedStake: new StoredBalance(),
};
},
return new FunctionAssertion<[number, boolean], string, string>(stakingWrapper, 'createStakingPool', {
// Returns the expected ID of th created pool
before: async () => {
const lastPoolId = await stakingWrapper.lastPoolId().callAsync();
// Effectively the last poolId + 1, but as a bytestring
return `0x${new BigNumber(lastPoolId)
.plus(1)
.toString(16)
.padStart(64, '0')}`;
},
);
after: async (
expectedPoolId: string,
result: FunctionResult,
args: [number, boolean],
txData: Partial<TxData>,
) => {
const [operatorShare] = args;
// Checks the logs for the new poolId, verifies that it is as expected
const log = result.receipt!.logs[0];
const actualPoolId = (log as any).args.poolId;
expect(actualPoolId).to.equal(expectedPoolId);
// Adds the new pool to local state
pools[actualPoolId] = {
operator: txData.from!,
operatorShare,
delegatedStake: new StoredBalance(),
};
},
});
}
/* tslint:enable:no-non-null-assertion*/

View File

@@ -1,6 +1,5 @@
import { StakingPoolById } from '@0x/contracts-staking';
import { expect } from '@0x/contracts-test-utils';
import { logUtils } from '@0x/utils';
import { TxData } from 'ethereum-types';
import { DeploymentManager } from '../deployment_manager';
@@ -17,21 +16,16 @@ export function validDecreaseStakingPoolOperatorShareAssertion(
): FunctionAssertion<[string, number], {}, void> {
const { stakingWrapper } = deployment.staking;
return new FunctionAssertion<[string, number], {}, void>(
stakingWrapper.decreaseStakingPoolOperatorShare.bind(stakingWrapper),
{
after: async (_beforeInfo, _result: FunctionResult, args: [string, number], txData: Partial<TxData>) => {
const [poolId, expectedOperatorShare] = args;
return new FunctionAssertion<[string, number], {}, void>(stakingWrapper, 'decreaseStakingPoolOperatorShare', {
after: async (_beforeInfo, _result: FunctionResult, args: [string, number], _txData: Partial<TxData>) => {
const [poolId, expectedOperatorShare] = args;
logUtils.log(`decreaseStakingPoolOperatorShare(${poolId}, ${expectedOperatorShare})`);
// Checks that the on-chain pool's operator share has been updated.
const { operatorShare } = await stakingWrapper.getStakingPool(poolId).callAsync();
expect(operatorShare).to.bignumber.equal(expectedOperatorShare);
// Checks that the on-chain pool's operator share has been updated.
const { operatorShare } = await stakingWrapper.getStakingPool(poolId).callAsync();
expect(operatorShare).to.bignumber.equal(expectedOperatorShare);
// Updates the pool in local state.
pools[poolId].operatorShare = operatorShare;
},
// Updates the pool in local state.
pools[poolId].operatorShare = operatorShare;
},
);
});
}

View File

@@ -2,7 +2,7 @@ import { ERC20TokenEvents, ERC20TokenTransferEventArgs } from '@0x/contracts-erc
import { ExchangeEvents, ExchangeFillEventArgs } from '@0x/contracts-exchange';
import { constants, expect, orderHashUtils, verifyEvents } from '@0x/contracts-test-utils';
import { FillResults, Order } from '@0x/types';
import { BigNumber, logUtils } from '@0x/utils';
import { BigNumber } from '@0x/utils';
import { TransactionReceiptWithDecodedLogs, TxData } from 'ethereum-types';
import * as _ from 'lodash';
@@ -74,7 +74,7 @@ export function validFillOrderCompleteFillAssertion(
): FunctionAssertion<[Order, BigNumber, string], {}, FillResults> {
const exchange = deployment.exchange;
return new FunctionAssertion<[Order, BigNumber, string], {}, FillResults>(exchange.fillOrder.bind(exchange), {
return new FunctionAssertion<[Order, BigNumber, string], {}, FillResults>(exchange, 'fillOrder', {
after: async (
_beforeInfo,
result: FunctionResult,
@@ -89,8 +89,6 @@ export function validFillOrderCompleteFillAssertion(
// Ensure that the correct events were emitted.
verifyFillEvents(txData.from!, order, result.receipt!, deployment);
logUtils.log(`Order filled by ${txData.from}`);
// TODO: Add validation for on-chain state (like balances)
},
});

View File

@@ -1,7 +1,9 @@
import { ContractFunctionObj, ContractTxFunctionObj } from '@0x/base-contract';
import { BaseContract, ContractFunctionObj, ContractTxFunctionObj } from '@0x/base-contract';
import { TransactionReceiptWithDecodedLogs, TxData } from 'ethereum-types';
import * as _ from 'lodash';
import { logger } from '../logger';
// tslint:disable:max-classes-per-file
export type GenericContractFunction<T> = (...args: any[]) => ContractFunctionObj<T>;
@@ -48,29 +50,22 @@ export interface AssertionResult<TBefore = unknown> {
*/
export class FunctionAssertion<TArgs extends any[], TBefore, ReturnDataType> implements Assertion<TArgs> {
// A condition that will be applied to `wrapperFunction`.
public condition: Condition<TArgs, TBefore>;
// The wrapper function that will be wrapped in assertions.
public wrapperFunction: (
...args: TArgs // tslint:disable-line:trailing-comma
) => ContractTxFunctionObj<ReturnDataType> | ContractFunctionObj<ReturnDataType>;
public readonly condition: Condition<TArgs, TBefore>;
constructor(
wrapperFunction: (
...args: TArgs // tslint:disable-line:trailing-comma
) => ContractTxFunctionObj<ReturnDataType> | ContractFunctionObj<ReturnDataType>,
private readonly _contractWrapper: BaseContract,
private readonly _functionName: string,
condition: Partial<Condition<TArgs, TBefore>> = {},
) {
this.condition = {
before: async (args: TArgs, txData: Partial<TxData>) => {
before: async (_args: TArgs, _txData: Partial<TxData>) => {
return ({} as any) as TBefore;
},
after: async (beforeInfo: TBefore, result: FunctionResult, args: TArgs, txData: Partial<TxData>) => {
after: async (_beforeInfo: TBefore, _result: FunctionResult, _args: TArgs, _txData: Partial<TxData>) => {
return ({} as any) as TBefore;
},
...condition,
};
this.wrapperFunction = wrapperFunction;
}
/**
@@ -87,7 +82,10 @@ export class FunctionAssertion<TArgs extends any[], TBefore, ReturnDataType> imp
// Try to make the call to the function. If it is successful, pass the
// result and receipt to the after condition.
try {
const functionWithArgs = this.wrapperFunction(...args) as ContractTxFunctionObj<ReturnDataType>;
const functionWithArgs = (this._contractWrapper as any)[this._functionName](
...args,
) as ContractTxFunctionObj<ReturnDataType>;
logger.logFunctionAssertion(this._functionName, args, txData);
callResult.data = await functionWithArgs.callAsync(txData);
callResult.receipt =
functionWithArgs.awaitTransactionSuccessAsync !== undefined

View File

@@ -1,6 +1,5 @@
import { StakingEvents, StakingMakerStakingPoolSetEventArgs } from '@0x/contracts-staking';
import { expect, filterLogsToArguments } from '@0x/contracts-test-utils';
import { logUtils } from '@0x/utils';
import { TxData } from 'ethereum-types';
import { DeploymentManager } from '../deployment_manager';
@@ -15,7 +14,7 @@ import { FunctionAssertion, FunctionResult } from './function_assertion';
export function validJoinStakingPoolAssertion(deployment: DeploymentManager): FunctionAssertion<[string], {}, void> {
const { stakingWrapper } = deployment.staking;
return new FunctionAssertion<[string], {}, void>(stakingWrapper.joinStakingPoolAsMaker.bind(stakingWrapper), {
return new FunctionAssertion<[string], {}, void>(stakingWrapper, 'joinStakingPoolAsMaker', {
after: async (_beforeInfo, _result: FunctionResult, args: [string], txData: Partial<TxData>) => {
const [poolId] = args;
@@ -34,8 +33,6 @@ export function validJoinStakingPoolAssertion(deployment: DeploymentManager): Fu
]);
const joinedPoolId = await deployment.staking.stakingWrapper.poolIdByMaker(txData.from!).callAsync();
expect(joinedPoolId).to.be.eq(poolId);
logUtils.log(`Pool ${poolId} joined by ${txData.from}`);
},
});
}

View File

@@ -7,7 +7,7 @@ import {
StoredBalance,
} from '@0x/contracts-staking';
import { constants, expect } from '@0x/contracts-test-utils';
import { BigNumber, logUtils } from '@0x/utils';
import { BigNumber } from '@0x/utils';
import { TxData } from 'ethereum-types';
import * as _ from 'lodash';
@@ -86,61 +86,50 @@ export function validMoveStakeAssertion(
): FunctionAssertion<[StakeInfo, StakeInfo, BigNumber], {}, void> {
const { stakingWrapper } = deployment.staking;
return new FunctionAssertion<[StakeInfo, StakeInfo, BigNumber], {}, void>(
stakingWrapper.moveStake.bind(stakingWrapper),
{
after: async (
_beforeInfo: {},
_result: FunctionResult,
args: [StakeInfo, StakeInfo, BigNumber],
txData: Partial<TxData>,
) => {
const [from, to, amount] = args;
return new FunctionAssertion<[StakeInfo, StakeInfo, BigNumber], {}, void>(stakingWrapper, 'moveStake', {
after: async (
_beforeInfo: {},
_result: FunctionResult,
args: [StakeInfo, StakeInfo, BigNumber],
txData: Partial<TxData>,
) => {
const [from, to, amount] = args;
logUtils.log(
`moveStake({status: ${StakeStatus[from.status]}, poolId: ${from.poolId} }, { status: ${
StakeStatus[to.status]
}, poolId: ${to.poolId} }, ${amount})`,
);
const owner = txData.from!; // tslint:disable-line:no-non-null-assertion
const owner = txData.from!; // tslint:disable-line:no-non-null-assertion
// Update local balances to match the expected result of this `moveStake` operation
const updatedPools = updateNextEpochBalances(globalStake, ownerStake, pools, from, to, amount);
// Update local balances to match the expected result of this `moveStake` operation
const updatedPools = updateNextEpochBalances(globalStake, ownerStake, pools, from, to, amount);
// Fetches on-chain owner stake balances and checks against local balances
const ownerUndelegatedStake = {
...new StoredBalance(),
...(await stakingWrapper.getOwnerStakeByStatus(owner, StakeStatus.Undelegated).callAsync()),
};
const ownerDelegatedStake = {
...new StoredBalance(),
...(await stakingWrapper.getOwnerStakeByStatus(owner, StakeStatus.Delegated).callAsync()),
};
expect(ownerUndelegatedStake).to.deep.equal(ownerStake[StakeStatus.Undelegated]);
expect(ownerDelegatedStake).to.deep.equal(ownerStake[StakeStatus.Delegated].total);
// Fetches on-chain owner stake balances and checks against local balances
const ownerUndelegatedStake = {
...new StoredBalance(),
...(await stakingWrapper.getOwnerStakeByStatus(owner, StakeStatus.Undelegated).callAsync()),
};
const ownerDelegatedStake = {
...new StoredBalance(),
...(await stakingWrapper.getOwnerStakeByStatus(owner, StakeStatus.Delegated).callAsync()),
};
expect(ownerUndelegatedStake).to.deep.equal(ownerStake[StakeStatus.Undelegated]);
expect(ownerDelegatedStake).to.deep.equal(ownerStake[StakeStatus.Delegated].total);
// Fetches on-chain global stake balances and checks against local balances
const globalUndelegatedStake = await stakingWrapper
.getGlobalStakeByStatus(StakeStatus.Undelegated)
.callAsync();
const globalDelegatedStake = await stakingWrapper.getGlobalStakeByStatus(StakeStatus.Delegated).callAsync();
expect(globalUndelegatedStake).to.deep.equal(globalStake[StakeStatus.Undelegated]);
expect(globalDelegatedStake).to.deep.equal(globalStake[StakeStatus.Delegated]);
// Fetches on-chain global stake balances and checks against local balances
const globalUndelegatedStake = await stakingWrapper
.getGlobalStakeByStatus(StakeStatus.Undelegated)
// Fetches on-chain pool stake balances and checks against local balances
for (const poolId of updatedPools) {
const stakeDelegatedByOwner = await stakingWrapper
.getStakeDelegatedToPoolByOwner(owner, poolId)
.callAsync();
const globalDelegatedStake = await stakingWrapper
.getGlobalStakeByStatus(StakeStatus.Delegated)
.callAsync();
expect(globalUndelegatedStake).to.deep.equal(globalStake[StakeStatus.Undelegated]);
expect(globalDelegatedStake).to.deep.equal(globalStake[StakeStatus.Delegated]);
// Fetches on-chain pool stake balances and checks against local balances
for (const poolId of updatedPools) {
const stakeDelegatedByOwner = await stakingWrapper
.getStakeDelegatedToPoolByOwner(owner, poolId)
.callAsync();
const totalStakeDelegated = await stakingWrapper.getTotalStakeDelegatedToPool(poolId).callAsync();
expect(stakeDelegatedByOwner).to.deep.equal(ownerStake[StakeStatus.Delegated][poolId]);
expect(totalStakeDelegated).to.deep.equal(pools[poolId].delegatedStake);
}
},
const totalStakeDelegated = await stakingWrapper.getTotalStakeDelegatedToPool(poolId).callAsync();
expect(stakeDelegatedByOwner).to.deep.equal(ownerStake[StakeStatus.Delegated][poolId]);
expect(totalStakeDelegated).to.deep.equal(pools[poolId].delegatedStake);
}
},
);
});
}
/* tslint:enable:no-unnecessary-type-assertion */

View File

@@ -1,6 +1,6 @@
import { GlobalStakeByStatus, OwnerStakeByStatus, StakeStatus, StoredBalance } from '@0x/contracts-staking';
import { expect } from '@0x/contracts-test-utils';
import { BigNumber, logUtils } from '@0x/utils';
import { BigNumber } from '@0x/utils';
import { TxData } from 'ethereum-types';
import { BlockchainBalanceStore } from '../balances/blockchain_balance_store';
@@ -34,7 +34,7 @@ export function validStakeAssertion(
): FunctionAssertion<[BigNumber], LocalBalanceStore, void> {
const { stakingWrapper, zrxVault } = deployment.staking;
return new FunctionAssertion(stakingWrapper.stake.bind(stakingWrapper), {
return new FunctionAssertion(stakingWrapper, 'stake', {
before: async (args: [BigNumber], txData: Partial<TxData>) => {
const [amount] = args;
@@ -56,8 +56,6 @@ export function validStakeAssertion(
) => {
const [amount] = args;
logUtils.log(`stake(${amount})`);
// Checks that the ZRX transfer updated balances as expected.
await balanceStore.updateErc20BalancesAsync();
balanceStore.assertEquals(expectedBalances);

View File

@@ -1,6 +1,6 @@
import { GlobalStakeByStatus, OwnerStakeByStatus, StakeStatus, StoredBalance } from '@0x/contracts-staking';
import { expect } from '@0x/contracts-test-utils';
import { BigNumber, logUtils } from '@0x/utils';
import { BigNumber } from '@0x/utils';
import { TxData } from 'ethereum-types';
import { BlockchainBalanceStore } from '../balances/blockchain_balance_store';
@@ -35,7 +35,7 @@ export function validUnstakeAssertion(
): FunctionAssertion<[BigNumber], LocalBalanceStore, void> {
const { stakingWrapper, zrxVault } = deployment.staking;
return new FunctionAssertion(stakingWrapper.unstake.bind(stakingWrapper), {
return new FunctionAssertion(stakingWrapper, 'unstake', {
before: async (args: [BigNumber], txData: Partial<TxData>) => {
const [amount] = args;
@@ -57,8 +57,6 @@ export function validUnstakeAssertion(
) => {
const [amount] = args;
logUtils.log(`unstake(${amount})`);
// Checks that the ZRX transfer updated balances as expected.
await balanceStore.updateErc20BalancesAsync();
balanceStore.assertEquals(expectedBalances);

View File

@@ -1,5 +1,5 @@
import { BaseContract } from '@0x/base-contract';
import { constants, expect, TokenBalances } from '@0x/contracts-test-utils';
import { constants, expect, replaceKeysDeep, TokenBalances } from '@0x/contracts-test-utils';
import * as _ from 'lodash';
import { TokenAddresses, TokenContractsByName, TokenOwnersByName } from './types';
@@ -68,6 +68,14 @@ export class BalanceStore {
this._addressNames = _.cloneDeep(balanceStore._addressNames);
}
/**
* Returns a version of balances where keys are replaced with their readable counterparts, if
* they exist.
*/
public toReadable(): _.Dictionary<{}> {
return replaceKeysDeep(this.balances, this._readableAddressName.bind(this));
}
/**
* Returns the human-readable name for the given address, if it exists.
* @param address The address to get the name for.

View File

@@ -0,0 +1,55 @@
import { TxData } from 'ethereum-types';
import { Pseudorandom} from './pseudorandom';
// tslint:disable:no-console
class Logger {
private _step = 0;
constructor() {
console.warn(
JSON.stringify({
level: 'info',
time: new Date(),
msg: `Pseudorandom seed: ${Pseudorandom.seed}`,
}),
);
}
/*
* Logs the name of the function executed, the arguments and transaction data it was
* called with, and the current step of the simulation.
*/
public logFunctionAssertion(functionName: string, functionArgs: any[], txData: Partial<TxData>): void {
console.warn(
JSON.stringify({
level: 'info',
time: new Date(),
msg: `Function called: ${functionName}(${functionArgs
.map(arg => JSON.stringify(arg).replace(/"/g, "'"))
.join(', ')})`,
step: this._step++,
txData,
}),
);
}
/*
* Logs information about a assertion failure. Dumps the error thrown and arbitrary data from
* the calling context.
*/
public logFailure(error: Error, data: string): void {
console.warn(
JSON.stringify({
level: 'error',
time: new Date(),
step: this._step,
error,
data,
}),
);
}
}
export const logger = new Logger();

View File

@@ -1,10 +1,10 @@
import { GlobalStakeByStatus, StakeStatus, StakingPoolById, StoredBalance } from '@0x/contracts-staking';
import * as _ from 'lodash';
import { Maker } from './actors/maker';
import { AssertionResult } from './assertions/function_assertion';
import { BlockchainBalanceStore } from './balances/blockchain_balance_store';
import { DeploymentManager } from './deployment_manager';
import { logger } from './logger';
// tslint:disable:max-classes-per-file
@@ -20,6 +20,14 @@ export class SimulationEnvironment {
public balanceStore: BlockchainBalanceStore,
public marketMakers: Maker[] = [],
) {}
public state(): any {
return {
globalStake: this.globalStake,
stakingPools: this.stakingPools,
balanceStore: this.balanceStore.toReadable(),
};
}
}
export abstract class Simulation {
@@ -30,14 +38,23 @@ export abstract class Simulation {
public async fuzzAsync(steps?: number): Promise<void> {
if (steps !== undefined) {
for (let i = 0; i < steps; i++) {
await this.generator.next();
await this._stepAsync();
}
} else {
while (true) {
await this.generator.next();
await this._stepAsync();
}
}
}
protected abstract _assertionGenerator(): AsyncIterableIterator<AssertionResult | void>;
private async _stepAsync(): Promise<void> {
try {
await this.generator.next();
} catch (error) {
logger.logFailure(error, this.environment.state());
throw error;
}
}
}

View File

@@ -23,14 +23,11 @@ blockchainTests.resets('FunctionAssertion Unit Tests', env => {
describe('executeAsync', () => {
it('should call the before function with the provided arguments', async () => {
let sideEffectTarget = ZERO_AMOUNT;
const assertion = new FunctionAssertion<[BigNumber], void, BigNumber>(
exampleContract.returnInteger.bind(exampleContract),
{
before: async (args: [BigNumber], txData: Partial<TxData>) => {
sideEffectTarget = randomInput;
},
const assertion = new FunctionAssertion<[BigNumber], void, BigNumber>(exampleContract, 'returnInteger', {
before: async (args: [BigNumber], txData: Partial<TxData>) => {
sideEffectTarget = randomInput;
},
);
});
const randomInput = getRandomInteger(ZERO_AMOUNT, MAX_UINT256);
await assertion.executeAsync([randomInput], {});
expect(sideEffectTarget).bignumber.to.be.eq(randomInput);
@@ -38,26 +35,23 @@ blockchainTests.resets('FunctionAssertion Unit Tests', env => {
it('should call the after function with the provided arguments', async () => {
let sideEffectTarget = ZERO_AMOUNT;
const assertion = new FunctionAssertion<[BigNumber], void, BigNumber>(
exampleContract.returnInteger.bind(exampleContract),
{
after: async (
_beforeInfo: any,
_result: FunctionResult,
args: [BigNumber],
txData: Partial<TxData>,
) => {
[sideEffectTarget] = args;
},
const assertion = new FunctionAssertion<[BigNumber], void, BigNumber>(exampleContract, 'returnInteger', {
after: async (
_beforeInfo: any,
_result: FunctionResult,
args: [BigNumber],
txData: Partial<TxData>,
) => {
[sideEffectTarget] = args;
},
);
});
const randomInput = getRandomInteger(ZERO_AMOUNT, MAX_UINT256);
await assertion.executeAsync([randomInput], {});
expect(sideEffectTarget).bignumber.to.be.eq(randomInput);
});
it('should not fail immediately if the wrapped function fails', async () => {
const assertion = new FunctionAssertion<[], {}, void>(exampleContract.emptyRevert.bind(exampleContract));
const assertion = new FunctionAssertion<[], {}, void>(exampleContract, 'emptyRevert');
await assertion.executeAsync([], {});
});
@@ -65,7 +59,8 @@ blockchainTests.resets('FunctionAssertion Unit Tests', env => {
const randomInput = getRandomInteger(ZERO_AMOUNT, MAX_UINT256);
let sideEffectTarget = ZERO_AMOUNT;
const assertion = new FunctionAssertion<[BigNumber], BigNumber, BigNumber>(
exampleContract.returnInteger.bind(exampleContract),
exampleContract,
'returnInteger',
{
before: async (_args: [BigNumber], _txData: Partial<TxData>) => {
return randomInput;
@@ -86,19 +81,16 @@ blockchainTests.resets('FunctionAssertion Unit Tests', env => {
it('should pass the result from the function call to "after"', async () => {
let sideEffectTarget = ZERO_AMOUNT;
const assertion = new FunctionAssertion<[BigNumber], void, BigNumber>(
exampleContract.returnInteger.bind(exampleContract),
{
after: async (
_beforeInfo: any,
result: FunctionResult,
_args: [BigNumber],
_txData: Partial<TxData>,
) => {
sideEffectTarget = result.data;
},
const assertion = new FunctionAssertion<[BigNumber], void, BigNumber>(exampleContract, 'returnInteger', {
after: async (
_beforeInfo: any,
result: FunctionResult,
_args: [BigNumber],
_txData: Partial<TxData>,
) => {
sideEffectTarget = result.data;
},
);
});
const randomInput = getRandomInteger(ZERO_AMOUNT, MAX_UINT256);
await assertion.executeAsync([randomInput], {});
expect(sideEffectTarget).bignumber.to.be.eq(randomInput);
@@ -106,21 +98,13 @@ blockchainTests.resets('FunctionAssertion Unit Tests', env => {
it('should pass the receipt from the function call to "after"', async () => {
let sideEffectTarget: TransactionReceiptWithDecodedLogs;
const assertion = new FunctionAssertion<[string], void, void>(
exampleContract.emitEvent.bind(exampleContract),
{
after: async (
_beforeInfo: any,
result: FunctionResult,
_args: [string],
_txData: Partial<TxData>,
) => {
if (result.receipt) {
sideEffectTarget = result.receipt;
}
},
const assertion = new FunctionAssertion<[string], void, void>(exampleContract, 'emitEvent', {
after: async (_beforeInfo: any, result: FunctionResult, _args: [string], _txData: Partial<TxData>) => {
if (result.receipt) {
sideEffectTarget = result.receipt;
}
},
);
});
const input = 'emitted data';
await assertion.executeAsync([input], {});
@@ -135,19 +119,11 @@ blockchainTests.resets('FunctionAssertion Unit Tests', env => {
it('should pass the error to "after" if the function call fails', async () => {
let sideEffectTarget: Error;
const assertion = new FunctionAssertion<[string], void, void>(
exampleContract.stringRevert.bind(exampleContract),
{
after: async (
_beforeInfo: any,
result: FunctionResult,
_args: [string],
_txData: Partial<TxData>,
) => {
sideEffectTarget = result.data;
},
const assertion = new FunctionAssertion<[string], void, void>(exampleContract, 'stringRevert', {
after: async (_beforeInfo: any, result: FunctionResult, _args: [string], _txData: Partial<TxData>) => {
sideEffectTarget = result.data;
},
);
});
const message = 'error message';
await assertion.executeAsync([message], {});