• Mark Tyneway's avatar
    core-utils: add GenesisJsonProvider and fix tests · 7c352b1e
    Mark Tyneway authored
    The `GenesisJsonProvider` implements the `ethers.Provider`
    interface and is constructed with a geth genesis file, either
    as an object or as a file to be read from disk. It implements
    a subset of the RPC methods that use the genesis file
    as the backing storage. It includes tests for its correctness.
    Not all methods are implemented, just the ones for the regenesis
    testing.
    
    This PR also moves the tests around in the `core-utils` package
    as some of the tests were being skipped. The `tests` directory is
    flattened, having so many subdirectories was not needed. The
    `package.json` test script is updated to ensure that all tests
    are run.
    
    Also add some deps that are required for the `GenesisJsonProvider`.
    7c352b1e
test-utils.ts 2.81 KB
import { expect } from 'chai'
import { BigNumber } from 'ethers'
import { sleep } from './misc'

interface deviationRanges {
  percentUpperDeviation?: number
  percentLowerDeviation?: number
  absoluteUpperDeviation?: number
  absoluteLowerDeviation?: number
}

export const awaitCondition = async (
  cond: () => Promise<boolean>,
  rate = 1000,
  attempts = 10
) => {
  for (let i = 0; i < attempts; i++) {
    const ok = await cond()
    if (ok) {
      return
    }

    await sleep(rate)
  }

  throw new Error('Timed out.')
}

/**
 * Assert that a number lies within a custom defined range of the target.
 */
export const expectApprox = (
  actual: BigNumber | number,
  target: BigNumber | number,
  {
    percentUpperDeviation,
    percentLowerDeviation,
    absoluteUpperDeviation,
    absoluteLowerDeviation,
  }: deviationRanges
): void => {
  actual = BigNumber.from(actual)
  target = BigNumber.from(target)

  // Ensure at least one deviation parameter is defined
  const nonNullDeviations =
    percentUpperDeviation ||
    percentLowerDeviation ||
    absoluteUpperDeviation ||
    absoluteLowerDeviation
  if (!nonNullDeviations) {
    throw new Error(
      'Must define at least one parameter to limit the deviation of the actual value.'
    )
  }

  // Upper bound calculation.
  let upper: BigNumber
  // Set the two possible upper bounds if and only if they are defined.
  const upperPcnt: BigNumber = !percentUpperDeviation
    ? null
    : target.mul(100 + percentUpperDeviation).div(100)
  const upperAbs: BigNumber = !absoluteUpperDeviation
    ? null
    : target.add(absoluteUpperDeviation)

  if (upperPcnt && upperAbs) {
    // If both are set, take the lesser of the two upper bounds.
    upper = upperPcnt.lte(upperAbs) ? upperPcnt : upperAbs
  } else {
    // Else take whichever is not undefined or set to null.
    upper = upperPcnt || upperAbs
  }

  // Lower bound calculation.
  let lower: BigNumber
  // Set the two possible lower bounds if and only if they are defined.
  const lowerPcnt: BigNumber = !percentLowerDeviation
    ? null
    : target.mul(100 - percentLowerDeviation).div(100)
  const lowerAbs: BigNumber = !absoluteLowerDeviation
    ? null
    : target.sub(absoluteLowerDeviation)
  if (lowerPcnt && lowerAbs) {
    // If both are set, take the greater of the two lower bounds.
    lower = lowerPcnt.gte(lowerAbs) ? lowerPcnt : lowerAbs
  } else {
    // Else take whichever is not undefined or set to null.
    lower = lowerPcnt || lowerAbs
  }

  // Apply the assertions if they are non-null.
  if (upper) {
    expect(
      actual.lte(upper),
      `Actual value (${actual}) is greater than the calculated upper bound of (${upper})`
    ).to.be.true
  }
  if (lower) {
    expect(
      actual.gte(lower),
      `Actual value (${actual}) is less than the calculated lower bound of (${lower})`
    ).to.be.true
  }
}