From e09702d5b2f20e6e7e3c3ad1783cca46011892cc Mon Sep 17 00:00:00 2001 From: Nazar Hussain Date: Mon, 18 Dec 2023 10:22:34 +0100 Subject: [PATCH] test: migrate state-transition unit tests to vitest (#6173) * Move state-transition unit tests to vitest * Update the package.json and remove the segfault retry * Update packages/state-transition/test/globalSetup.ts * Update packages/state-transition/test/globalSetup.ts * Update the run command * Fix perf test issue --------- Co-authored-by: Cayman --- .../perf/chain/verifyImportBlocks.test.ts | 7 ++- packages/state-transition/.mocharc.yaml | 6 --- packages/state-transition/package.json | 2 +- packages/state-transition/test/globalSetup.ts | 2 + .../test/perf/epoch/epochAltair.test.ts | 3 +- .../test/perf/epoch/epochCapella.test.ts | 3 +- .../test/perf/epoch/epochPhase0.test.ts | 3 +- .../block/isValidIndexedAttestation.test.ts | 26 +++++------ .../unit/block/processWithdrawals.test.ts | 6 +-- .../test/unit/cachedBeaconState.test.ts | 32 ++++--------- .../test/unit/constants.test.ts | 4 +- .../unit/signatureSets/signatureSets.test.ts | 4 +- .../test/unit/upgradeState.test.ts | 4 +- .../test/unit/util/aggregator.test.ts | 19 ++++---- .../test/unit/util/balance.test.ts | 21 ++++----- .../test/unit/util/cachedBeaconState.test.ts | 1 + .../test/unit/util/epoch.test.ts | 46 ++++++++----------- .../test/unit/util/flags.test.ts | 4 +- .../findModifiedInactivityScores.test.ts | 4 +- .../loadState/findModifiedValidators.test.ts | 4 +- .../unit/util/loadState/loadValidator.test.ts | 24 ++++------ .../test/unit/util/misc.test.ts | 8 ++-- .../test/unit/util/seed.test.ts | 6 +-- .../test/unit/util/shuffle.test.ts | 12 ++--- .../test/unit/util/slashing.test.ts | 14 +++--- .../test/unit/util/slot.test.ts | 6 +-- .../test/unit/util/validator.test.ts | 31 +++++++------ .../test/unit/util/weakSubjectivity.test.ts | 13 +++--- .../test/utils/beforeValue.ts | 7 +-- .../test/utils/beforeValueMocha.ts | 36 +++++++++++++++ packages/state-transition/test/utils/index.ts | 2 - packages/state-transition/vitest.config.ts | 11 +++++ 32 files changed, 194 insertions(+), 177 deletions(-) delete mode 100644 packages/state-transition/.mocharc.yaml create mode 100644 packages/state-transition/test/globalSetup.ts create mode 100644 packages/state-transition/test/utils/beforeValueMocha.ts delete mode 100644 packages/state-transition/test/utils/index.ts create mode 100644 packages/state-transition/vitest.config.ts diff --git a/packages/beacon-node/test/perf/chain/verifyImportBlocks.test.ts b/packages/beacon-node/test/perf/chain/verifyImportBlocks.test.ts index 21b70c69a425..923436d6f96d 100644 --- a/packages/beacon-node/test/perf/chain/verifyImportBlocks.test.ts +++ b/packages/beacon-node/test/perf/chain/verifyImportBlocks.test.ts @@ -7,11 +7,14 @@ import {defaultOptions as defaultValidatorOptions} from "@lodestar/validator"; // eslint-disable-next-line import/no-relative-packages import {rangeSyncTest} from "../../../../state-transition/test/perf/params.js"; import { - beforeValue, getNetworkCachedState, getNetworkCachedBlock, // eslint-disable-next-line import/no-relative-packages -} from "../../../../state-transition/test/utils/index.js"; +} from "../../../../state-transition/test/utils/testFileCache.js"; +import { + beforeValue, + // eslint-disable-next-line import/no-relative-packages +} from "../../../../state-transition/test/utils/beforeValueMocha.js"; import {BeaconChain} from "../../../src/chain/index.js"; import {ExecutionEngineDisabled} from "../../../src/execution/engine/index.js"; import {Eth1ForBlockProductionDisabled} from "../../../src/eth1/index.js"; diff --git a/packages/state-transition/.mocharc.yaml b/packages/state-transition/.mocharc.yaml deleted file mode 100644 index f28ebdf663a0..000000000000 --- a/packages/state-transition/.mocharc.yaml +++ /dev/null @@ -1,6 +0,0 @@ -colors: true -timeout: 5000 -exit: true -extension: ["ts"] -node-option: - - "loader=ts-node/esm" diff --git a/packages/state-transition/package.json b/packages/state-transition/package.json index 4d6a0f9cfffe..0e4d51cf79d9 100644 --- a/packages/state-transition/package.json +++ b/packages/state-transition/package.json @@ -52,7 +52,7 @@ "check-types": "tsc", "lint": "eslint --color --ext .ts src/ test/", "lint:fix": "yarn run lint --fix", - "test:unit": "mocha 'test/unit/**/*.test.ts'", + "test:unit": "vitest --run --dir test/unit/ --coverage", "check-readme": "typescript-docs-verifier" }, "types": "lib/index.d.ts", diff --git a/packages/state-transition/test/globalSetup.ts b/packages/state-transition/test/globalSetup.ts new file mode 100644 index 000000000000..0ab57c057472 --- /dev/null +++ b/packages/state-transition/test/globalSetup.ts @@ -0,0 +1,2 @@ +export async function setup(): Promise {} +export async function teardown(): Promise {} diff --git a/packages/state-transition/test/perf/epoch/epochAltair.test.ts b/packages/state-transition/test/perf/epoch/epochAltair.test.ts index 7e84a533e6c6..273353d8632b 100644 --- a/packages/state-transition/test/perf/epoch/epochAltair.test.ts +++ b/packages/state-transition/test/perf/epoch/epochAltair.test.ts @@ -6,7 +6,8 @@ import { CachedBeaconStateAltair, beforeProcessEpoch, } from "../../../src/index.js"; -import {getNetworkCachedState, beforeValue, LazyValue} from "../../utils/index.js"; +import {beforeValue, LazyValue} from "../../utils/beforeValueMocha.js"; +import {getNetworkCachedState} from "../../utils/testFileCache.js"; import {StateEpoch} from "../types.js"; import {altairState} from "../params.js"; import {processJustificationAndFinalization} from "../../../src/epoch/processJustificationAndFinalization.js"; diff --git a/packages/state-transition/test/perf/epoch/epochCapella.test.ts b/packages/state-transition/test/perf/epoch/epochCapella.test.ts index 36d5caf11a99..eeaf8bfc5400 100644 --- a/packages/state-transition/test/perf/epoch/epochCapella.test.ts +++ b/packages/state-transition/test/perf/epoch/epochCapella.test.ts @@ -7,7 +7,8 @@ import { CachedBeaconStateAltair, beforeProcessEpoch, } from "../../../src/index.js"; -import {getNetworkCachedState, beforeValue, LazyValue} from "../../utils/index.js"; +import {beforeValue, LazyValue} from "../../utils/beforeValueMocha.js"; +import {getNetworkCachedState} from "../../utils/testFileCache.js"; import {StateEpoch} from "../types.js"; import {capellaState} from "../params.js"; import {processJustificationAndFinalization} from "../../../src/epoch/processJustificationAndFinalization.js"; diff --git a/packages/state-transition/test/perf/epoch/epochPhase0.test.ts b/packages/state-transition/test/perf/epoch/epochPhase0.test.ts index ae6b5a536be2..4e43634b1669 100644 --- a/packages/state-transition/test/perf/epoch/epochPhase0.test.ts +++ b/packages/state-transition/test/perf/epoch/epochPhase0.test.ts @@ -6,7 +6,8 @@ import { CachedBeaconStatePhase0, beforeProcessEpoch, } from "../../../src/index.js"; -import {getNetworkCachedState, beforeValue, LazyValue} from "../../utils/index.js"; +import {beforeValue, LazyValue} from "../../utils/beforeValueMocha.js"; +import {getNetworkCachedState} from "../../utils/testFileCache.js"; import {StateEpoch} from "../types.js"; import {phase0State} from "../params.js"; import {processEpoch} from "../../../src/epoch/index.js"; diff --git a/packages/state-transition/test/unit/block/isValidIndexedAttestation.test.ts b/packages/state-transition/test/unit/block/isValidIndexedAttestation.test.ts index c526ab13c17c..c219943b940f 100644 --- a/packages/state-transition/test/unit/block/isValidIndexedAttestation.test.ts +++ b/packages/state-transition/test/unit/block/isValidIndexedAttestation.test.ts @@ -1,4 +1,4 @@ -import {expect} from "chai"; +import {describe, it, expect} from "vitest"; import {config} from "@lodestar/config/default"; import {FAR_FUTURE_EPOCH, MAX_EFFECTIVE_BALANCE} from "@lodestar/params"; import {phase0, ssz} from "@lodestar/types"; @@ -35,18 +35,16 @@ describe("validate indexed attestation", () => { }, ]; - for (const testValue of testValues) { - it(testValue.name, function () { - const attestationData = ssz.phase0.AttestationData.defaultValue(); - attestationData.source.epoch = 0; - attestationData.target.epoch = 1; + it.each(testValues)("$name", ({indices, expectedValue}) => { + const attestationData = ssz.phase0.AttestationData.defaultValue(); + attestationData.source.epoch = 0; + attestationData.target.epoch = 1; - const indexedAttestation: phase0.IndexedAttestation = { - attestingIndices: testValue.indices, - data: attestationData, - signature: EMPTY_SIGNATURE, - }; - expect(isValidIndexedAttestation(state, indexedAttestation, false)).to.be.equal(testValue.expectedValue); - }); - } + const indexedAttestation: phase0.IndexedAttestation = { + attestingIndices: indices, + data: attestationData, + signature: EMPTY_SIGNATURE, + }; + expect(isValidIndexedAttestation(state, indexedAttestation, false)).toBe(expectedValue); + }); }); diff --git a/packages/state-transition/test/unit/block/processWithdrawals.test.ts b/packages/state-transition/test/unit/block/processWithdrawals.test.ts index 628aae9496da..2841da635472 100644 --- a/packages/state-transition/test/unit/block/processWithdrawals.test.ts +++ b/packages/state-transition/test/unit/block/processWithdrawals.test.ts @@ -1,4 +1,4 @@ -import {expect} from "chai"; +import {describe, it, expect} from "vitest"; import {getExpectedWithdrawals} from "../../../src/block/processWithdrawals.js"; import {numValidators} from "../../perf/util.js"; import {getExpectedWithdrawalsTestData, WithdrawalOpts} from "../../utils/capella.js"; @@ -38,8 +38,8 @@ describe("getExpectedWithdrawals", () => { it(`getExpectedWithdrawals ${vc} ${caseID}`, () => { const {sampledValidators, withdrawals} = getExpectedWithdrawals(state.value); - expect(sampledValidators).equals(opts.sampled, "Wrong sampledValidators"); - expect(withdrawals.length).equals(opts.withdrawals, "Wrong withdrawals"); + expect(sampledValidators).toBe(opts.sampled); + expect(withdrawals.length).toBe(opts.withdrawals); }); } }); diff --git a/packages/state-transition/test/unit/cachedBeaconState.test.ts b/packages/state-transition/test/unit/cachedBeaconState.test.ts index 072261c1000e..f3089f39d913 100644 --- a/packages/state-transition/test/unit/cachedBeaconState.test.ts +++ b/packages/state-transition/test/unit/cachedBeaconState.test.ts @@ -1,4 +1,4 @@ -import {expect} from "chai"; +import {describe, it, expect} from "vitest"; import {ssz} from "@lodestar/types"; import {toHexString} from "@lodestar/utils"; import {config} from "@lodestar/config/default"; @@ -16,18 +16,15 @@ describe("CachedBeaconState", () => { const state2 = state1.clone(); state1.slot = 1; - expect(state2.slot).to.equal(0, "state2.slot was mutated"); + expect(state2.slot).toBe(0); const prevRoot = state2.currentJustifiedCheckpoint.root; const newRoot = Buffer.alloc(32, 1); state1.currentJustifiedCheckpoint.root = newRoot; - expect(toHexString(state2.currentJustifiedCheckpoint.root)).to.equal( - toHexString(prevRoot), - "state2.currentJustifiedCheckpoint.root was mutated" - ); + expect(toHexString(state2.currentJustifiedCheckpoint.root)).toBe(toHexString(prevRoot)); state1.epochCtx.epoch = 1; - expect(state2.epochCtx.epoch).to.equal(0, "state2.epochCtx.epoch was mutated"); + expect(state2.epochCtx.epoch).toBe(0); }); it("Auto-commit on hashTreeRoot", () => { @@ -40,10 +37,7 @@ describe("CachedBeaconState", () => { // Only commit state1 beforehand cp1.commit(); - expect(toHexString(cp1.hashTreeRoot())).to.equal( - toHexString(cp2.hashTreeRoot()), - ".hashTreeRoot() does not automatically commit" - ); + expect(toHexString(cp1.hashTreeRoot())).toBe(toHexString(cp2.hashTreeRoot())); }); it("Auto-commit on serialize", () => { @@ -55,10 +49,7 @@ describe("CachedBeaconState", () => { // Only commit state1 beforehand cp1.commit(); - expect(toHexString(cp1.serialize())).to.equal( - toHexString(cp2.serialize()), - ".serialize() does not automatically commit" - ); + expect(toHexString(cp1.serialize())).toBe(toHexString(cp2.serialize())); }); describe("loadCachedBeaconState", () => { @@ -138,16 +129,13 @@ describe("CachedBeaconState", () => { const stateBytes = state.serialize(); const newCachedState = loadUnfinalizedCachedBeaconState(seedState, stateBytes, {skipSyncCommitteeCache: true}); const newStateBytes = newCachedState.serialize(); - expect(newStateBytes).to.be.deep.equal(stateBytes, "loadState: state bytes are not equal"); - expect(newCachedState.hashTreeRoot()).to.be.deep.equal( - state.hashTreeRoot(), - "loadState: state root is not equal" - ); + expect(newStateBytes).toEqual(stateBytes); + expect(newCachedState.hashTreeRoot()).toEqual(state.hashTreeRoot()); // confirm loadUnfinalizedCachedBeaconState() result for (let i = 0; i < newCachedState.validators.length; i++) { - expect(newCachedState.epochCtx.pubkey2index.get(newCachedState.validators.get(i).pubkey)).to.be.equal(i); - expect(newCachedState.epochCtx.index2pubkey[i].toBytes()).to.be.deep.equal(pubkeys[i]); + expect(newCachedState.epochCtx.pubkey2index.get(newCachedState.validators.get(i).pubkey)).toBe(i); + expect(newCachedState.epochCtx.index2pubkey[i].toBytes()).toEqual(pubkeys[i]); } }); } diff --git a/packages/state-transition/test/unit/constants.test.ts b/packages/state-transition/test/unit/constants.test.ts index d4975a8a86f5..5b8cc66da73a 100644 --- a/packages/state-transition/test/unit/constants.test.ts +++ b/packages/state-transition/test/unit/constants.test.ts @@ -1,10 +1,10 @@ -import {expect} from "chai"; +import {describe, it, expect} from "vitest"; import * as blst from "@chainsafe/blst"; import {G2_POINT_AT_INFINITY} from "../../src/index.js"; describe("constants", () => { it("G2_POINT_AT_INFINITY", () => { const p2 = blst.Signature.fromBytes(G2_POINT_AT_INFINITY); - expect(p2.value.is_inf()).to.equal(true, "is not infinity"); + expect(p2.value.is_inf()).toBe(true); }); }); diff --git a/packages/state-transition/test/unit/signatureSets/signatureSets.test.ts b/packages/state-transition/test/unit/signatureSets/signatureSets.test.ts index 9e084dc783a3..1a5b15e1b041 100644 --- a/packages/state-transition/test/unit/signatureSets/signatureSets.test.ts +++ b/packages/state-transition/test/unit/signatureSets/signatureSets.test.ts @@ -1,5 +1,5 @@ import crypto from "node:crypto"; -import {expect} from "chai"; +import {describe, it, expect} from "vitest"; import bls from "@chainsafe/bls"; import {BitArray} from "@chainsafe/ssz"; import {config} from "@lodestar/config/default"; @@ -67,7 +67,7 @@ describe("signatureSets", () => { const state = generateCachedState(config, {validators}); const signatureSets = getBlockSignatureSets(state, signedBlock); - expect(signatureSets.length).to.equal( + expect(signatureSets.length).toBe( // block signature 1 + // randao reveal diff --git a/packages/state-transition/test/unit/upgradeState.test.ts b/packages/state-transition/test/unit/upgradeState.test.ts index ba9ff187a26c..2ea8eef182ac 100644 --- a/packages/state-transition/test/unit/upgradeState.test.ts +++ b/packages/state-transition/test/unit/upgradeState.test.ts @@ -1,4 +1,4 @@ -import {expect} from "chai"; +import {expect, describe, it} from "vitest"; import {ssz} from "@lodestar/types"; import {ForkName} from "@lodestar/params"; import {createBeaconConfig, ChainForkConfig, createChainForkConfig} from "@lodestar/config"; @@ -22,7 +22,7 @@ describe("upgradeState", () => { {skipSyncCommitteeCache: true} ); const newState = upgradeStateToDeneb(stateView); - expect(() => newState.toValue()).to.not.throw(); + expect(() => newState.toValue()).not.toThrow(); }); }); diff --git a/packages/state-transition/test/unit/util/aggregator.test.ts b/packages/state-transition/test/unit/util/aggregator.test.ts index f6e7bbd4a6ed..07fd3172926c 100644 --- a/packages/state-transition/test/unit/util/aggregator.test.ts +++ b/packages/state-transition/test/unit/util/aggregator.test.ts @@ -1,4 +1,4 @@ -import {expect} from "chai"; +import {describe, it, expect, beforeAll} from "vitest"; import {fromHexString} from "@chainsafe/ssz"; import { SYNC_COMMITTEE_SIZE, @@ -13,10 +13,10 @@ import {isAggregatorFromCommitteeLength, isSyncCommitteeAggregator} from "../../ describe("isAttestationAggregator", function () { const committeeLength = 130; - before("Ensure constants don't change", () => { + beforeAll(() => { expect({ TARGET_AGGREGATORS_PER_COMMITTEE, - }).to.deep.equal({ + }).toEqual({ TARGET_AGGREGATORS_PER_COMMITTEE: 16, }); }); @@ -28,8 +28,9 @@ describe("isAttestationAggregator", function () { "0x8191d16330837620f0ed85d0d3d52af5b56f7cec12658fa391814251d4b32977eb2e6ca055367354fd63175f8d1d2d7b0678c3c482b738f96a0df40bd06450d99c301a659b8396c227ed781abb37a1604297922219374772ab36b46b84817036" ) ); - expect(result).to.be.equal(false); + expect(result).toBe(false); }); + it("should be true", function () { const result = isAggregatorFromCommitteeLength( committeeLength, @@ -37,17 +38,17 @@ describe("isAttestationAggregator", function () { "0xa8f8bb92931234ca6d8a34530526bcd6a4cfa3bf33bd0470200dc8fa3ebdc3ba24bc8c6e994d58a0f884eb24336d746c01a29693ed0354c0862c2d5de5859e3f58747045182844d267ba232058f7df1867a406f63a1eb8afec0cf3f00a115125" ) ); - expect(result).to.be.equal(true); + expect(result).toBe(true); }); }); describe("isSyncCommitteeAggregator", function () { - before("Ensure constants don't change", () => { + beforeAll(() => { expect({ SYNC_COMMITTEE_SIZE, SYNC_COMMITTEE_SUBNET_COUNT, TARGET_AGGREGATORS_PER_SYNC_SUBCOMMITTEE, - }).to.deep.equal({ + }).toEqual({ SYNC_COMMITTEE_SIZE: 512, SYNC_COMMITTEE_SUBNET_COUNT: 4, TARGET_AGGREGATORS_PER_SYNC_SUBCOMMITTEE: 16, @@ -60,7 +61,7 @@ describe("isSyncCommitteeAggregator", function () { "0x8191d16330837620f0ed85d0d3d52af5b56f7cec12658fa391814251d4b32977eb2e6ca055367354fd63175f8d1d2d7b0678c3c482b738f96a0df40bd06450d99c301a659b8396c227ed781abb37a1604297922219374772ab36b46b84817036" ) ); - expect(result).to.be.equal(false); + expect(result).toBe(false); }); // NOTE: Invalid sig, bruteforced last characters to get a true result @@ -70,6 +71,6 @@ describe("isSyncCommitteeAggregator", function () { "0xa8f8bb92931234ca6d8a34530526bcd6a4cfa3bf33bd0470200dc8fa3ebdc3ba24bc8c6e994d58a0f884eb24336d746c01a29693ed0354c0862c2d5de5859e3f58747045182844d267ba232058f7df1867a406f63a1eb8afec0cf3f00a115142" ) ); - expect(result).to.be.equal(true); + expect(result).toBe(true); }); }); diff --git a/packages/state-transition/test/unit/util/balance.test.ts b/packages/state-transition/test/unit/util/balance.test.ts index dedb4023c5ca..5b666cb0524e 100644 --- a/packages/state-transition/test/unit/util/balance.test.ts +++ b/packages/state-transition/test/unit/util/balance.test.ts @@ -1,4 +1,4 @@ -import {assert, expect} from "chai"; +import {describe, it, expect} from "vitest"; import {config as minimalConfig} from "@lodestar/config/default"; import {EFFECTIVE_BALANCE_INCREMENT} from "@lodestar/params"; @@ -23,7 +23,7 @@ describe("getTotalBalance", () => { const result = getTotalBalance(state, validatorIndices); const expected = BigInt(num * validatorBalance); - assert(result === expected, `Expected: ${expected} :: Result: ${result}`); + expect(result).toEqual(expected); }); it("should return correct balances - 5 validators", () => { @@ -34,8 +34,8 @@ describe("getTotalBalance", () => { const validatorIndices: ValidatorIndex[] = Array.from({length: num}, (_, i) => i); const result = getTotalBalance(state, validatorIndices); - const expected = EFFECTIVE_BALANCE_INCREMENT; - assert(result === BigInt(expected), `Expected: ${expected} :: Result: ${result}`); + const expected = BigInt(EFFECTIVE_BALANCE_INCREMENT); + expect(result).toEqual(expected); }); }); @@ -43,12 +43,12 @@ describe("increaseBalance", () => { it("should add to a validators balance", () => { const state = generateCachedState(); state.balances.push(0); - expect(state.balances.get(0)).to.be.equal(0); + expect(state.balances.get(0)).toBe(0); const delta = 5; for (let i = 1; i < 10; i++) { increaseBalance(state, 0, delta); - expect(state.balances.get(0)).to.be.equal(delta * i); + expect(state.balances.get(0)).toBe(delta * i); } }); }); @@ -62,7 +62,7 @@ describe("decreaseBalance", () => { const delta = 5; for (let i = 1; i < 10; i++) { decreaseBalance(state, 0, delta); - expect(state.balances.get(0)).to.be.equal(initial - delta * i); + expect(state.balances.get(0)).toBe(initial - delta * i); } }); @@ -72,7 +72,7 @@ describe("decreaseBalance", () => { state.balances.push(initial); const delta = 11; decreaseBalance(state, 0, delta); - expect(state.balances.get(0)).to.be.equal(0); + expect(state.balances.get(0)).toBe(0); }); }); @@ -99,9 +99,6 @@ describe("getEffectiveBalanceIncrementsZeroInactive", () => { : 0; } - expect(getEffectiveBalanceIncrementsZeroInactive(justifiedState)).to.be.deep.equal( - effectiveBalances, - "wrong effectiveBalances" - ); + expect(getEffectiveBalanceIncrementsZeroInactive(justifiedState)).toEqual(effectiveBalances); }); }); diff --git a/packages/state-transition/test/unit/util/cachedBeaconState.test.ts b/packages/state-transition/test/unit/util/cachedBeaconState.test.ts index 1a34e2472b50..654e0752adb8 100644 --- a/packages/state-transition/test/unit/util/cachedBeaconState.test.ts +++ b/packages/state-transition/test/unit/util/cachedBeaconState.test.ts @@ -1,3 +1,4 @@ +import {describe, it} from "vitest"; import {createBeaconConfig} from "@lodestar/config"; import {config} from "@lodestar/config/default"; import {ssz} from "@lodestar/types"; diff --git a/packages/state-transition/test/unit/util/epoch.test.ts b/packages/state-transition/test/unit/util/epoch.test.ts index fe404f93f852..e86a41875e1d 100644 --- a/packages/state-transition/test/unit/util/epoch.test.ts +++ b/packages/state-transition/test/unit/util/epoch.test.ts @@ -1,4 +1,4 @@ -import {assert} from "chai"; +import {describe, it, expect} from "vitest"; import {GENESIS_SLOT, MAX_SEED_LOOKAHEAD} from "@lodestar/params"; import {Epoch, Slot} from "@lodestar/types"; @@ -12,7 +12,7 @@ import { import {generateState} from "../../utils/state.js"; describe("computeEpochAtSlot", () => { - const pairs = [ + it.each([ {test: 0, expected: 0}, {test: 1, expected: 0}, {test: 10, expected: 0}, @@ -21,17 +21,14 @@ describe("computeEpochAtSlot", () => { {test: 10000, expected: 312}, {test: 100000, expected: 3125}, {test: 1000000, expected: 31250}, - ]; - for (const pair of pairs) { - it(`Slot ${pair.test} should map to epoch ${pair.expected}`, () => { - const result: Epoch = computeEpochAtSlot(pair.test); - assert.equal(result, pair.expected); - }); - } + ])("Slot $test should map to epoch $expected", ({test, expected}) => { + const result: Epoch = computeEpochAtSlot(test); + expect(result).toEqual(expected); + }); }); describe("computeStartSlotAtEpoch", () => { - const pairs = [ + it.each([ {test: 0, expected: 0}, {test: 1, expected: 32}, {test: 10, expected: 320}, @@ -40,38 +37,31 @@ describe("computeStartSlotAtEpoch", () => { {test: 10000, expected: 320000}, {test: 100000, expected: 3200000}, {test: 1000000, expected: 32000000}, - ]; - for (const pair of pairs) { - it(`Epoch ${pair.test} should map to slot ${pair.expected}`, () => { - const result: Slot = computeStartSlotAtEpoch(pair.test); - assert.equal(result, pair.expected); - }); - } + ])("Epoch $test should map to slot $expected", ({test, expected}) => { + const result: Slot = computeStartSlotAtEpoch(test); + expect(result).toEqual(expected); + }); }); describe("getPreviousEpoch", () => { - const testValues = [ + it.each([ {slot: 512, expectedEpoch: 15}, {slot: 256, expectedEpoch: 7}, { slot: GENESIS_SLOT, expectedEpoch: computeEpochAtSlot(GENESIS_SLOT), }, - ]; - - for (const testValue of testValues) { - it("epoch should return previous epoch", () => { - const state = generateState({slot: testValue.slot}); - const result = getPreviousEpoch(state); - assert.equal(result, testValue.expectedEpoch); - }); - } + ])("epoch should return previous epoch", ({slot, expectedEpoch}) => { + const state = generateState({slot}); + const result = getPreviousEpoch(state); + expect(result).toEqual(expectedEpoch); + }); }); describe("computeActivationExitEpoch", () => { it("epoch is always equal to the epoch after the exit delay", () => { for (let e: Epoch = 0; e < 1000; e++) { - assert.equal(computeActivationExitEpoch(e), e + 1 + MAX_SEED_LOOKAHEAD); + expect(computeActivationExitEpoch(e)).toEqual(e + 1 + MAX_SEED_LOOKAHEAD); } }); }); diff --git a/packages/state-transition/test/unit/util/flags.test.ts b/packages/state-transition/test/unit/util/flags.test.ts index 6566ef8205be..07a8ce3fe097 100644 --- a/packages/state-transition/test/unit/util/flags.test.ts +++ b/packages/state-transition/test/unit/util/flags.test.ts @@ -1,4 +1,4 @@ -import {expect} from "chai"; +import {describe, it, expect} from "vitest"; describe("Altair status flags", () => { for (let prev = 0b000; prev <= 0b111; prev++) { @@ -7,7 +7,7 @@ describe("Altair status flags", () => { expect( // Actual function toStr(getResFlags(prev, att)) - ).to.equal( + ).toBe( // Naive but correct implementation toStr(getResFlagsNaive(prev, att)) ); diff --git a/packages/state-transition/test/unit/util/loadState/findModifiedInactivityScores.test.ts b/packages/state-transition/test/unit/util/loadState/findModifiedInactivityScores.test.ts index e1ad0cf972da..85697af2b7c1 100644 --- a/packages/state-transition/test/unit/util/loadState/findModifiedInactivityScores.test.ts +++ b/packages/state-transition/test/unit/util/loadState/findModifiedInactivityScores.test.ts @@ -1,4 +1,4 @@ -import {expect} from "chai"; +import {describe, it, expect} from "vitest"; import { INACTIVITY_SCORE_SIZE, findModifiedInactivityScores, @@ -27,7 +27,7 @@ describe("findModifiedInactivityScores", () => { } const modifiedValidators: number[] = []; findModifiedInactivityScores(inactivityScoresBytes, inactivityScoresBytes2, modifiedValidators); - expect(modifiedValidators.sort((a, b) => a - b)).to.be.deep.equal(expectedModifiedValidators); + expect(modifiedValidators.sort((a, b) => a - b)).toEqual(expectedModifiedValidators); }); } }); diff --git a/packages/state-transition/test/unit/util/loadState/findModifiedValidators.test.ts b/packages/state-transition/test/unit/util/loadState/findModifiedValidators.test.ts index aa2378276d22..25c6233d2738 100644 --- a/packages/state-transition/test/unit/util/loadState/findModifiedValidators.test.ts +++ b/packages/state-transition/test/unit/util/loadState/findModifiedValidators.test.ts @@ -1,4 +1,4 @@ -import {expect} from "chai"; +import {describe, it, expect} from "vitest"; import {fromHexString} from "@chainsafe/ssz"; import {findModifiedValidators} from "../../../../src/util/loadState/findModifiedValidators.js"; import {generateState} from "../../../utils/state.js"; @@ -35,7 +35,7 @@ describe("findModifiedValidators", () => { const validatorsBytes2 = clonedState.validators.serialize(); const modifiedValidators: number[] = []; findModifiedValidators(validatorsBytes, validatorsBytes2, modifiedValidators); - expect(modifiedValidators.sort((a, b) => a - b)).to.be.deep.equal(expectedModifiedValidators); + expect(modifiedValidators.sort((a, b) => a - b)).toEqual(expectedModifiedValidators); }); } }); diff --git a/packages/state-transition/test/unit/util/loadState/loadValidator.test.ts b/packages/state-transition/test/unit/util/loadState/loadValidator.test.ts index 7c3112537490..9a2094531813 100644 --- a/packages/state-transition/test/unit/util/loadState/loadValidator.test.ts +++ b/packages/state-transition/test/unit/util/loadState/loadValidator.test.ts @@ -1,4 +1,4 @@ -import {expect} from "chai"; +import {describe, it, expect} from "vitest"; import {CompositeViewDU} from "@chainsafe/ssz"; import {phase0, ssz} from "@lodestar/types"; import {loadValidator} from "../../../../src/util/loadState/loadValidator.js"; @@ -105,19 +105,11 @@ describe("loadValidator", () => { }, ]; - for (const {name, getValidator} of testCases) { - it(name, () => { - const newValidator = getValidator(); - const newValidatorBytes = newValidator.serialize(); - const loadedValidator = loadValidator(validator, newValidatorBytes); - expect(Buffer.compare(loadedValidator.hashTreeRoot(), newValidator.hashTreeRoot())).to.be.equal( - 0, - "root is not correct" - ); - expect(Buffer.compare(loadedValidator.serialize(), newValidator.serialize())).to.be.equal( - 0, - "serialized value is not correct" - ); - }); - } + it.each(testCases)("$name", ({getValidator}) => { + const newValidator = getValidator(); + const newValidatorBytes = newValidator.serialize(); + const loadedValidator = loadValidator(validator, newValidatorBytes); + expect(Buffer.compare(loadedValidator.hashTreeRoot(), newValidator.hashTreeRoot())).toBe(0); + expect(Buffer.compare(loadedValidator.serialize(), newValidator.serialize())).toBe(0); + }); }); diff --git a/packages/state-transition/test/unit/util/misc.test.ts b/packages/state-transition/test/unit/util/misc.test.ts index f487ad41aca2..5651da5ac5d1 100644 --- a/packages/state-transition/test/unit/util/misc.test.ts +++ b/packages/state-transition/test/unit/util/misc.test.ts @@ -1,4 +1,4 @@ -import {assert} from "chai"; +import {describe, it, expect} from "vitest"; import {toBigIntLE} from "bigint-buffer"; import {GENESIS_SLOT, SLOTS_PER_HISTORICAL_ROOT} from "@lodestar/params"; @@ -13,14 +13,14 @@ describe("getBlockRoot", () => { }); const res = Buffer.from(getBlockRoot(state, GENESIS_SLOT)); const expectedRes = BigInt("0xab"); - assert(toBigIntLE(res) === expectedRes, `got: ${toBigIntLE(res)}, expected: ${expectedRes.toString(16)}`); + expect(toBigIntLE(res)).toEqual(expectedRes); }); it("should fail if slot is current slot", () => { const state = generateState({slot: GENESIS_SLOT}); - assert.throws(() => getBlockRoot(state, GENESIS_SLOT), ""); + expect(() => getBlockRoot(state, GENESIS_SLOT)).toThrow(""); }); it("should fail if slot is not within SLOTS_PER_HISTORICAL_ROOT of current slot", () => { const state = generateState({slot: GENESIS_SLOT + SLOTS_PER_HISTORICAL_ROOT + 1}); - assert.throws(() => getBlockRoot(state, GENESIS_SLOT), ""); + expect(() => getBlockRoot(state, GENESIS_SLOT)).toThrow(""); }); }); diff --git a/packages/state-transition/test/unit/util/seed.test.ts b/packages/state-transition/test/unit/util/seed.test.ts index ccdc3332cdba..7f7c0e1f8bc7 100644 --- a/packages/state-transition/test/unit/util/seed.test.ts +++ b/packages/state-transition/test/unit/util/seed.test.ts @@ -1,4 +1,4 @@ -import {expect} from "chai"; +import {describe, it, expect} from "vitest"; import {toHexString} from "@chainsafe/ssz"; import {GENESIS_EPOCH, GENESIS_SLOT, SLOTS_PER_EPOCH} from "@lodestar/params"; @@ -16,7 +16,7 @@ describe("getRandaoMix", () => { state.randaoMixes.set(0, randaoMix1); const res = getRandaoMix(state, GENESIS_EPOCH); - expect(toHexString(res)).to.equal(toHexString(randaoMix1)); + expect(toHexString(res)).toBe(toHexString(randaoMix1)); }); it("should return second randao mix for GENESIS_EPOCH + 1", () => { // Empty state in 2nd epoch @@ -25,6 +25,6 @@ describe("getRandaoMix", () => { state.randaoMixes.set(1, randaoMix2); const res = getRandaoMix(state, GENESIS_EPOCH + 1); - expect(toHexString(res)).to.equal(toHexString(randaoMix2)); + expect(toHexString(res)).toBe(toHexString(randaoMix2)); }); }); diff --git a/packages/state-transition/test/unit/util/shuffle.test.ts b/packages/state-transition/test/unit/util/shuffle.test.ts index 7cb1e54e2619..9186968674ae 100644 --- a/packages/state-transition/test/unit/util/shuffle.test.ts +++ b/packages/state-transition/test/unit/util/shuffle.test.ts @@ -1,4 +1,4 @@ -import {expect} from "chai"; +import {expect, describe, it} from "vitest"; import {unshuffleList} from "../../../src/index.js"; describe("util / shuffle", () => { @@ -22,10 +22,8 @@ describe("util / shuffle", () => { const seed = new Uint8Array([42, 32]); - for (const {id, input, res} of testCases) { - it(id, () => { - unshuffleList(input, seed); - expect(input).to.deep.equal(res); - }); - } + it.each(testCases)("$id", ({input, res}) => { + unshuffleList(input, seed); + expect(input).toEqual(res); + }); }); diff --git a/packages/state-transition/test/unit/util/slashing.test.ts b/packages/state-transition/test/unit/util/slashing.test.ts index 411933080792..49a7b6454c25 100644 --- a/packages/state-transition/test/unit/util/slashing.test.ts +++ b/packages/state-transition/test/unit/util/slashing.test.ts @@ -1,4 +1,4 @@ -import {assert} from "chai"; +import {expect, it, describe} from "vitest"; import {SLOTS_PER_EPOCH} from "@lodestar/params"; import {Epoch, phase0, ssz} from "@lodestar/types"; @@ -11,7 +11,7 @@ describe("isSlashableAttestationData", () => { const epoch2 = epoch1 + 1; const a1 = getAttestationDataAt(epoch1, epoch2); const a2 = getAttestationDataAt(epoch1 - 1, epoch2); - assert.isTrue(isSlashableAttestationData(a1, a2)); + expect(isSlashableAttestationData(a1, a2)).toBe(true); }); it("Attestation data with disjoint source/target epochs should return false", () => { @@ -21,7 +21,7 @@ describe("isSlashableAttestationData", () => { const epoch4 = epoch1 + 3; const a1 = getAttestationDataAt(epoch1, epoch2); const a2 = getAttestationDataAt(epoch3, epoch4); - assert.isFalse(isSlashableAttestationData(a1, a2)); + expect(isSlashableAttestationData(a1, a2)).toBe(false); }); it("Should return false if the second attestation does not have a greater source epoch", () => { @@ -35,12 +35,12 @@ describe("isSlashableAttestationData", () => { const a1 = getAttestationDataAt(sourceEpoch1, targetEpoch1); const a2Hi = getAttestationDataAt(sourceEpoch2Hi, targetEpoch2); - assert.isFalse(isSlashableAttestationData(a1, a2Hi)); + expect(isSlashableAttestationData(a1, a2Hi)).toBe(false); // Second attestation has a smaller source epoch. const sourceEpoch2Lo = sourceEpoch1 - 1; const a2Lo = getAttestationDataAt(sourceEpoch2Lo, targetEpoch2); - assert.isFalse(isSlashableAttestationData(a1, a2Lo)); + expect(isSlashableAttestationData(a1, a2Lo)).toBe(false); }); it("Should return false if the second attestation does not have a smaller target epoch", () => { @@ -58,14 +58,14 @@ describe("isSlashableAttestationData", () => { let a1 = getAttestationDataAt(targetSlot1, sourceEpoch1); let a2 = getAttestationDataAt(targetSlot2, sourceEpoch2); - assert.isFalse(isSlashableAttestationData(a1, a2)); + expect(isSlashableAttestationData(a1, a2)).toBe(false); // Second attestation has a greater target epoch. targetSlot1 = targetEpoch * SLOTS_PER_EPOCH; targetSlot2 = (targetEpoch + 1) * SLOTS_PER_EPOCH; a1 = getAttestationDataAt(targetSlot1, sourceEpoch1); a2 = getAttestationDataAt(targetSlot2, sourceEpoch2); - assert.isFalse(isSlashableAttestationData(a1, a2)); + expect(isSlashableAttestationData(a1, a2)).toBe(false); }); }); diff --git a/packages/state-transition/test/unit/util/slot.test.ts b/packages/state-transition/test/unit/util/slot.test.ts index f25a1f7b9fe9..c9546ad60043 100644 --- a/packages/state-transition/test/unit/util/slot.test.ts +++ b/packages/state-transition/test/unit/util/slot.test.ts @@ -1,4 +1,4 @@ -import {assert} from "chai"; +import {describe, it, expect} from "vitest"; import {Slot} from "@lodestar/types"; import {computeSlotsSinceEpochStart} from "../../../src/util/index.js"; @@ -14,7 +14,7 @@ describe("computeSlotsSinceEpochStart", () => { for (const pair of pairs) { it(`Slot ${pair.test} is ${pair.expected} from current Epoch start`, () => { const result: Slot = computeSlotsSinceEpochStart(pair.test); - assert.equal(result, pair.expected); + expect(result).toEqual(pair.expected); }); } @@ -23,6 +23,6 @@ describe("computeSlotsSinceEpochStart", () => { const slot = 70; const result = computeSlotsSinceEpochStart(slot, epoch); // 70 - NUM_SLOT_PER_EPOCH - assert.equal(result, 38); + expect(result).toEqual(38); }); }); diff --git a/packages/state-transition/test/unit/util/validator.test.ts b/packages/state-transition/test/unit/util/validator.test.ts index 7a79ce8474f7..65727126742d 100644 --- a/packages/state-transition/test/unit/util/validator.test.ts +++ b/packages/state-transition/test/unit/util/validator.test.ts @@ -1,4 +1,4 @@ -import {assert, expect} from "chai"; +import {describe, it, expect, beforeEach} from "vitest"; import {phase0, ssz} from "@lodestar/types"; @@ -10,7 +10,7 @@ import {generateState} from "../../utils/state.js"; describe("getActiveValidatorIndices", () => { it("empty list of validators should return no indices (empty list)", () => { - assert.deepEqual(getActiveValidatorIndices(generateState(), randBetween(0, 4)), []); + expect(getActiveValidatorIndices(generateState(), randBetween(0, 4))).toStrictEqual([]); }); it("list of cloned validators should return all or none", () => { const state = generateState(); @@ -22,8 +22,8 @@ describe("getActiveValidatorIndices", () => { const allActiveIndices = state.validators.getAllReadonlyValues().map((_, i) => i); const allInactiveIndices: any = []; - assert.deepEqual(getActiveValidatorIndices(state, activationEpoch), allActiveIndices); - assert.deepEqual(getActiveValidatorIndices(state, exitEpoch), allInactiveIndices); + expect(getActiveValidatorIndices(state, activationEpoch)).toStrictEqual(allActiveIndices); + expect(getActiveValidatorIndices(state, exitEpoch)).toStrictEqual(allInactiveIndices); }); }); @@ -41,7 +41,7 @@ describe("isActiveValidator", () => { it(`should be ${testValue.expected ? "" : "not "}active`, () => { const v: phase0.Validator = generateValidator(testValue.validatorOpts); const result: boolean = isActiveValidator(v, testValue.epoch); - expect(result).to.be.equal(testValue.expected); + expect(result).toBe(testValue.expected); }); } }); @@ -57,28 +57,31 @@ describe("isSlashableValidator", () => { validator.activationEpoch = 0; validator.withdrawableEpoch = Infinity; validator.slashed = false; - assert(isSlashableValidator(validator, 0), "unslashed validator should be slashable"); + expect(isSlashableValidator(validator, 0)).toBeWithMessage(true, "unslashed validator should be slashable"); validator.slashed = true; - assert(!isSlashableValidator(validator, 0), "slashed validator should not be slashable"); + expect(!isSlashableValidator(validator, 0)).toBeWithMessage(true, "slashed validator should not be slashable"); }); it("should check validator.activationEpoch", () => { validator.activationEpoch = 10; validator.withdrawableEpoch = Infinity; - assert( - !isSlashableValidator(validator, validator.activationEpoch - 1), + expect(!isSlashableValidator(validator, validator.activationEpoch - 1)).toBeWithMessage( + true, "unactivated validator should not be slashable" ); - assert(isSlashableValidator(validator, validator.activationEpoch), "activated validator should be slashable"); + expect(isSlashableValidator(validator, validator.activationEpoch)).toBeWithMessage( + true, + "activated validator should be slashable" + ); }); it("should check validator.withdrawableEpoch", () => { validator.activationEpoch = 0; validator.withdrawableEpoch = 10; - assert( - isSlashableValidator(validator, validator.withdrawableEpoch - 1), + expect(isSlashableValidator(validator, validator.withdrawableEpoch - 1)).toBeWithMessage( + true, "nonwithdrawable validator should be slashable" ); - assert( - !isSlashableValidator(validator, validator.withdrawableEpoch), + expect(!isSlashableValidator(validator, validator.withdrawableEpoch)).toBeWithMessage( + true, "withdrawable validator should not be slashable" ); }); diff --git a/packages/state-transition/test/unit/util/weakSubjectivity.test.ts b/packages/state-transition/test/unit/util/weakSubjectivity.test.ts index a421caed64ed..5f5c784e975a 100644 --- a/packages/state-transition/test/unit/util/weakSubjectivity.test.ts +++ b/packages/state-transition/test/unit/util/weakSubjectivity.test.ts @@ -1,4 +1,4 @@ -import {expect} from "chai"; +import {describe, it, expect} from "vitest"; import {config} from "@lodestar/config/default"; import {computeWeakSubjectivityPeriodFromConstituents} from "../../../src/util/weakSubjectivity.js"; import {getChurnLimit} from "../../../src/util/validator.js"; @@ -23,16 +23,17 @@ describe("weak subjectivity tests", () => { {avgValBalance: balance32, valCount: 1048576, wsPeriod: 3532}, ]; - for (const {avgValBalance, valCount, wsPeriod} of testValues) { - it(`should have wsPeriod: ${wsPeriod} with avgValBalance: ${avgValBalance} and valCount: ${valCount}`, () => { + it.each(testValues)( + "should have wsPeriod: $wsPeriod with avgValBalance: $avgValBalance and valCount: $valCount", + ({valCount, avgValBalance}) => { const wsPeriod = computeWeakSubjectivityPeriodFromConstituents( valCount, avgValBalance * valCount, getChurnLimit(config, valCount), config.MIN_VALIDATOR_WITHDRAWABILITY_DELAY ); - expect(wsPeriod).to.equal(wsPeriod); - }); - } + expect(wsPeriod).toBe(wsPeriod); + } + ); }); }); diff --git a/packages/state-transition/test/utils/beforeValue.ts b/packages/state-transition/test/utils/beforeValue.ts index 0d5f8f77d203..61ae3daa32a1 100644 --- a/packages/state-transition/test/utils/beforeValue.ts +++ b/packages/state-transition/test/utils/beforeValue.ts @@ -1,3 +1,5 @@ +import {beforeAll} from "vitest"; + export type LazyValue = {value: T}; /** @@ -12,10 +14,9 @@ export type LazyValue = {value: T}; export function beforeValue(fn: () => T | Promise, timeout?: number): LazyValue { let value: T = null as unknown as T; - before(async function () { - this.timeout(timeout ?? 300_000); + beforeAll(async function () { value = await fn(); - }); + }, timeout ?? 300_000); return new Proxy<{value: T}>( {value}, diff --git a/packages/state-transition/test/utils/beforeValueMocha.ts b/packages/state-transition/test/utils/beforeValueMocha.ts new file mode 100644 index 000000000000..0d5f8f77d203 --- /dev/null +++ b/packages/state-transition/test/utils/beforeValueMocha.ts @@ -0,0 +1,36 @@ +export type LazyValue = {value: T}; + +/** + * Register a callback to compute a value in the before() block of mocha tests + * ```ts + * const state = beforeValue(() => getState()) + * it("test", () => { + * doTest(state.value) + * }) + * ``` + */ +export function beforeValue(fn: () => T | Promise, timeout?: number): LazyValue { + let value: T = null as unknown as T; + + before(async function () { + this.timeout(timeout ?? 300_000); + value = await fn(); + }); + + return new Proxy<{value: T}>( + {value}, + { + get: function (target, prop) { + if (prop === "value") { + if (value === null) { + throw Error("beforeValue has not yet run the before() block"); + } else { + return value; + } + } else { + return undefined; + } + }, + } + ); +} diff --git a/packages/state-transition/test/utils/index.ts b/packages/state-transition/test/utils/index.ts deleted file mode 100644 index 4a79d4371787..000000000000 --- a/packages/state-transition/test/utils/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export * from "./beforeValue.js"; -export * from "./testFileCache.js"; diff --git a/packages/state-transition/vitest.config.ts b/packages/state-transition/vitest.config.ts new file mode 100644 index 000000000000..1df0de848936 --- /dev/null +++ b/packages/state-transition/vitest.config.ts @@ -0,0 +1,11 @@ +import {defineConfig, mergeConfig} from "vitest/config"; +import vitestConfig from "../../vitest.base.config"; + +export default mergeConfig( + vitestConfig, + defineConfig({ + test: { + globalSetup: ["./test/globalSetup.ts"], + }, + }) +);