diff --git a/packages/fork-choice/package.json b/packages/fork-choice/package.json index dbbbcb6d3dbd..54fe40063d85 100644 --- a/packages/fork-choice/package.json +++ b/packages/fork-choice/package.json @@ -34,7 +34,7 @@ "lint:fix": "yarn run lint --fix", "pretest": "yarn run check-types", "test": "yarn test:unit", - "test:unit": "mocha --colors -r ts-node/register 'test/unit/**/*.test.ts'", + "test:unit": "vitest --run --dir test/unit/ --coverage", "check-readme": "typescript-docs-verifier" }, "dependencies": { diff --git a/packages/fork-choice/test/globalSetup.ts b/packages/fork-choice/test/globalSetup.ts new file mode 100644 index 000000000000..0ab57c057472 --- /dev/null +++ b/packages/fork-choice/test/globalSetup.ts @@ -0,0 +1,2 @@ +export async function setup(): Promise {} +export async function teardown(): Promise {} diff --git a/packages/fork-choice/test/unit/forkChoice/forkChoice.test.ts b/packages/fork-choice/test/unit/forkChoice/forkChoice.test.ts index fe4f9a7afaad..fe11532dbb6f 100644 --- a/packages/fork-choice/test/unit/forkChoice/forkChoice.test.ts +++ b/packages/fork-choice/test/unit/forkChoice/forkChoice.test.ts @@ -1,4 +1,4 @@ -import {expect} from "chai"; +import {describe, it, expect, beforeEach, beforeAll} from "vitest"; import {fromHexString} from "@chainsafe/ssz"; import {config} from "@lodestar/config/default"; import {RootHex, Slot} from "@lodestar/types"; @@ -119,12 +119,12 @@ describe("Forkchoice", function () { const forkchoice = new ForkChoice(config, fcStore, protoArr); const summaries = forkchoice.getAllAncestorBlocks(getBlockRoot(genesisSlot + 1)); // there are 2 blocks in protoArray but iterateAncestorBlocks should only return non-finalized blocks - expect(summaries.length).to.be.equals(1, "should not return the finalized block"); - expect(summaries[0]).to.be.deep.include(block, "the block summary is not correct"); + expect(summaries).toHaveLength(1); + expect(summaries[0]).toEqual({...block, bestChild: undefined, bestDescendant: undefined, parent: 0, weight: 0}); }); - before("Assert SLOTS_PER_EPOCH", () => { - expect(SLOTS_PER_EPOCH).equals(32, "Unexpected SLOTS_PER_EPOCH value"); + beforeAll(() => { + expect(SLOTS_PER_EPOCH).toBe(32); }); const dependentRootTestCases: {atSlot: Slot; pivotSlot: Slot; epoch: EpochDifference; skipped: Slot[]}[] = [ @@ -162,10 +162,7 @@ describe("Forkchoice", function () { const expectedDependentRoot = getBlockRoot(pivotSlot); - expect(forkchoice.getDependentRoot(block, epoch)).to.be.equal( - expectedDependentRoot, - "incorrect attester dependent root" - ); + expect(forkchoice.getDependentRoot(block, epoch)).toBe(expectedDependentRoot); }); } diff --git a/packages/fork-choice/test/unit/forkChoice/utils.test.ts b/packages/fork-choice/test/unit/forkChoice/utils.test.ts index 3cf497ac38a1..3f315d079842 100644 --- a/packages/fork-choice/test/unit/forkChoice/utils.test.ts +++ b/packages/fork-choice/test/unit/forkChoice/utils.test.ts @@ -1,4 +1,4 @@ -import {expect} from "chai"; +import {describe, it, expect} from "vitest"; import {createChainForkConfig} from "@lodestar/config"; import {ssz} from "@lodestar/types"; import {assertValidTerminalPowBlock, ExecutionStatus} from "../../../src/index.js"; @@ -17,7 +17,7 @@ describe("assertValidTerminalPowBlock", function () { }; expect(() => assertValidTerminalPowBlock(config, block, {executionStatus, powBlockParent: null, powBlock}) - ).to.not.throw(); + ).not.toThrow(); }); it("should require powBlockParent if powBlock not genesis", function () { @@ -29,7 +29,7 @@ describe("assertValidTerminalPowBlock", function () { }; expect(() => assertValidTerminalPowBlock(config, block, {executionStatus, powBlockParent: null, powBlock}) - ).to.throw(); + ).toThrow(); }); it("should require powBlock >= ttd", function () { @@ -41,7 +41,7 @@ describe("assertValidTerminalPowBlock", function () { }; expect(() => assertValidTerminalPowBlock(config, block, {executionStatus, powBlockParent: powBlock, powBlock}) - ).to.throw(); + ).toThrow(); }); it("should require powBlockParent < ttd", function () { @@ -53,7 +53,7 @@ describe("assertValidTerminalPowBlock", function () { }; expect(() => assertValidTerminalPowBlock(config, block, {executionStatus, powBlockParent: powBlock, powBlock}) - ).to.throw(); + ).toThrow(); }); it("should accept powBlockParent < ttd and powBlock >= ttd", function () { @@ -67,8 +67,6 @@ describe("assertValidTerminalPowBlock", function () { ...powBlock, totalDifficulty: BigInt(9), }; - expect(() => - assertValidTerminalPowBlock(config, block, {executionStatus, powBlockParent, powBlock}) - ).to.not.throw(); + expect(() => assertValidTerminalPowBlock(config, block, {executionStatus, powBlockParent, powBlock})).not.toThrow(); }); }); diff --git a/packages/fork-choice/test/unit/protoArray/computeDeltas.test.ts b/packages/fork-choice/test/unit/protoArray/computeDeltas.test.ts index 3981ef84ff4c..54b8a900d05c 100644 --- a/packages/fork-choice/test/unit/protoArray/computeDeltas.test.ts +++ b/packages/fork-choice/test/unit/protoArray/computeDeltas.test.ts @@ -1,4 +1,4 @@ -import {expect} from "chai"; +import {describe, it, expect} from "vitest"; import {getEffectiveBalanceIncrementsZeroed} from "@lodestar/state-transition"; import {computeDeltas} from "../../../src/protoArray/computeDeltas.js"; @@ -25,11 +25,11 @@ describe("computeDeltas", () => { const deltas = computeDeltas(indices.size, votes, oldBalances, newBalances, new Set()); - expect(deltas.length).to.eql(validatorCount); - expect(deltas).to.deep.equal(Array.from({length: validatorCount}, () => 0)); + expect(deltas.length).toEqual(validatorCount); + expect(deltas).toEqual(Array.from({length: validatorCount}, () => 0)); for (const vote of votes) { - expect(vote.currentIndex).to.eql(vote.nextIndex); + expect(vote.currentIndex).toEqual(vote.nextIndex); } }); @@ -55,13 +55,13 @@ describe("computeDeltas", () => { const deltas = computeDeltas(indices.size, votes, oldBalances, newBalances, new Set()); - expect(deltas.length).to.eql(validatorCount); + expect(deltas.length).toEqual(validatorCount); for (const [i, delta] of deltas.entries()) { if (i === 0) { - expect(delta.toString()).to.equal((balance * validatorCount).toString()); + expect(delta.toString()).toBe((balance * validatorCount).toString()); } else { - expect(delta.toString()).to.equal("0"); + expect(delta.toString()).toBe("0"); } } }); @@ -88,10 +88,10 @@ describe("computeDeltas", () => { const deltas = computeDeltas(indices.size, votes, oldBalances, newBalances, new Set()); - expect(deltas.length).to.eql(validatorCount); + expect(deltas.length).toEqual(validatorCount); for (const delta of deltas) { - expect(delta.toString()).to.equal(balance.toString()); + expect(delta.toString()).toBe(balance.toString()); } }); @@ -117,17 +117,17 @@ describe("computeDeltas", () => { const deltas = computeDeltas(indices.size, votes, oldBalances, newBalances, new Set()); - expect(deltas.length).to.eql(validatorCount); + expect(deltas.length).toEqual(validatorCount); const totalDelta = balance * validatorCount; for (const [i, delta] of deltas.entries()) { if (i === 0) { - expect(delta.toString()).to.equal((0 - totalDelta).toString()); + expect(delta.toString()).toBe((0 - totalDelta).toString()); } else if (i === 1) { - expect(delta.toString()).to.equal(totalDelta.toString()); + expect(delta.toString()).toBe(totalDelta.toString()); } else { - expect(delta.toString()).to.equal("0"); + expect(delta.toString()).toBe("0"); } } }); @@ -201,15 +201,15 @@ describe("computeDeltas", () => { const deltas = computeDeltas(indices.size, votes, oldBalances, newBalances, new Set()); - expect(deltas.length).to.eql(validatorCount); + expect(deltas.length).toEqual(validatorCount); for (const [i, delta] of deltas.entries()) { if (i === 0) { - expect(delta.toString()).to.equal((0 - oldBalance * validatorCount).toString()); + expect(delta.toString()).toBe((0 - oldBalance * validatorCount).toString()); } else if (i === 1) { - expect(delta.toString()).to.equal((newBalance * validatorCount).toString()); + expect(delta.toString()).toBe((newBalance * validatorCount).toString()); } else { - expect(delta.toString()).to.equal("0"); + expect(delta.toString()).toBe("0"); } } }); @@ -239,13 +239,13 @@ describe("computeDeltas", () => { const deltas = computeDeltas(indices.size, votes, oldBalances, newBalances, new Set()); - expect(deltas.length).to.eql(2); + expect(deltas.length).toEqual(2); - expect(deltas[0].toString()).to.eql((0 - balance).toString()); - expect(deltas[1].toString()).to.eql((balance * 2).toString()); + expect(deltas[0].toString()).toEqual((0 - balance).toString()); + expect(deltas[1].toString()).toEqual((balance * 2).toString()); for (const vote of votes) { - expect(vote.currentIndex).to.equal(vote.nextIndex); + expect(vote.currentIndex).toBe(vote.nextIndex); } }); @@ -273,13 +273,13 @@ describe("computeDeltas", () => { const deltas = computeDeltas(indices.size, votes, oldBalances, newBalances, new Set()); - expect(deltas.length).to.eql(2); + expect(deltas.length).toEqual(2); - expect(deltas[0].toString()).to.eql((0 - balance * 2).toString()); - expect(deltas[1].toString()).to.eql(balance.toString()); + expect(deltas[0].toString()).toEqual((0 - balance * 2).toString()); + expect(deltas[1].toString()).toEqual(balance.toString()); for (const vote of votes) { - expect(vote.currentIndex).to.equal(vote.nextIndex); + expect(vote.currentIndex).toBe(vote.nextIndex); } }); @@ -303,12 +303,12 @@ describe("computeDeltas", () => { // 1st validator is part of an attester slashing const equivocatingIndices = new Set([0]); let deltas = computeDeltas(indices.size, votes, balances, balances, equivocatingIndices); - expect(deltas[0]).to.be.equals( + expect(deltas[0]).toBeWithMessage( -1 * (firstBalance + secondBalance), "should disregard the 1st validator due to attester slashing" ); - expect(deltas[1]).to.be.equals(secondBalance, "should move 2nd balance from 1st root to 2nd root"); + expect(deltas[1]).toBeWithMessage(secondBalance, "should move 2nd balance from 1st root to 2nd root"); deltas = computeDeltas(indices.size, votes, balances, balances, equivocatingIndices); - expect(deltas).to.be.deep.equals([0, 0], "calling computeDeltas again should not have any affect on the weight"); + expect(deltas).toEqualWithMessage([0, 0], "calling computeDeltas again should not have any affect on the weight"); }); }); diff --git a/packages/fork-choice/test/unit/protoArray/executionStatusUpdates.test.ts b/packages/fork-choice/test/unit/protoArray/executionStatusUpdates.test.ts index e1dda450aa46..94e5cd3ac9a0 100644 --- a/packages/fork-choice/test/unit/protoArray/executionStatusUpdates.test.ts +++ b/packages/fork-choice/test/unit/protoArray/executionStatusUpdates.test.ts @@ -1,4 +1,4 @@ -import {expect} from "chai"; +import {describe, it, expect} from "vitest"; import { ProtoBlock, ProtoArray, @@ -135,7 +135,7 @@ describe("executionStatus / normal updates", () => { */ const preValidation = collectProtoarrayValidationStatus(fc); it("preValidation forkchoice setup should be correct", () => { - expect(preValidation).to.be.deep.equal(expectedPreValidationFC); + expect(preValidation).toEqual(expectedPreValidationFC); }); /** @@ -156,7 +156,7 @@ describe("executionStatus / normal updates", () => { const invalidate3CValidate2CForkChoice = collectProtoarrayValidationStatus(fc); it("correcly invalidate 3C and validate 2C only", () => { - expect(invalidate3CValidate2CForkChoice).to.be.deep.equal( + expect(invalidate3CValidate2CForkChoice).toEqual( toFcTestCase([ ["0", "1A", "3B", ExecutionStatus.PreMerge], ["1A", "2B", "3B", ExecutionStatus.Syncing], @@ -186,7 +186,7 @@ describe("executionStatus / normal updates", () => { ); const validate3B2B1A = collectProtoarrayValidationStatus(fc); it("Validate 3B, 2B, 1A", () => { - expect(validate3B2B1A).to.be.deep.equal( + expect(validate3B2B1A).toEqual( toFcTestCase([ ["0", "1A", "3B", ExecutionStatus.PreMerge], ["1A", "2B", "3B", ExecutionStatus.Valid], @@ -218,7 +218,7 @@ describe("executionStatus / normal updates", () => { ); const invalidate3A2A = collectProtoarrayValidationStatus(fc); it("Invalidate 3A, 2A with 2A loosing its bestChild, bestDescendant", () => { - expect(invalidate3A2A).to.be.deep.equal( + expect(invalidate3A2A).toEqual( toFcTestCase([ ["0", "1A", "3B", ExecutionStatus.PreMerge], ["1A", "2B", "3B", ExecutionStatus.Valid], @@ -245,7 +245,7 @@ describe("executionStatus / invalidate all postmerge chain", () => { */ const preValidation = collectProtoarrayValidationStatus(fc); it("preValidation forkchoice setup should be correct", () => { - expect(preValidation).to.be.deep.equal(expectedPreValidationFC); + expect(preValidation).toEqual(expectedPreValidationFC); }); /** @@ -265,7 +265,7 @@ describe("executionStatus / invalidate all postmerge chain", () => { ); const postMergeInvalidated = collectProtoarrayValidationStatus(fc); it("all post merge blocks should be invalidated except Cs", () => { - expect(postMergeInvalidated).to.be.deep.equal( + expect(postMergeInvalidated).toEqual( toFcTestCase([ ["0", undefined, undefined, ExecutionStatus.PreMerge], ["1A", undefined, undefined, ExecutionStatus.Invalid], @@ -281,7 +281,7 @@ describe("executionStatus / invalidate all postmerge chain", () => { const fcHead = fc.findHead("0", 3); it("pre merge block should be the FC head", () => { - expect(fcHead).to.be.equal("0"); + expect(fcHead).toBe("0"); }); }); @@ -297,7 +297,7 @@ describe("executionStatus / poision forkchoice if we invalidate previous valid", */ const preValidation = collectProtoarrayValidationStatus(fc); it("preValidation forkchoice setup should be correct", () => { - expect(preValidation).to.be.deep.equal(expectedPreValidationFC); + expect(preValidation).toEqual(expectedPreValidationFC); }); /** @@ -316,7 +316,7 @@ describe("executionStatus / poision forkchoice if we invalidate previous valid", ); const validate3B2B1A = collectProtoarrayValidationStatus(fc); it("Validate 3B, 2B, 1A", () => { - expect(validate3B2B1A).to.be.deep.equal( + expect(validate3B2B1A).toEqual( toFcTestCase([ ["0", "1A", "3B", ExecutionStatus.PreMerge], ["1A", "2B", "3B", ExecutionStatus.Valid], @@ -340,10 +340,10 @@ describe("executionStatus / poision forkchoice if we invalidate previous valid", }, 3 ) - ).to.throw(Error); + ).toThrow(Error); - expect(fc.lvhError).to.be.deep.equal({lvhCode: LVHExecErrorCode.ValidToInvalid, blockRoot: "1A", execHash: "1A"}); - expect(() => fc.findHead("0", 3)).to.throw(Error); + expect(fc.lvhError).toEqual({lvhCode: LVHExecErrorCode.ValidToInvalid, blockRoot: "1A", execHash: "1A"}); + expect(() => fc.findHead("0", 3)).toThrow(Error); }); }); @@ -359,7 +359,7 @@ describe("executionStatus / poision forkchoice if we validate previous invalid", */ const preValidation = collectProtoarrayValidationStatus(fc); it("preValidation forkchoice setup should be correct", () => { - expect(preValidation).to.be.deep.equal(expectedPreValidationFC); + expect(preValidation).toEqual(expectedPreValidationFC); }); /** @@ -379,7 +379,7 @@ describe("executionStatus / poision forkchoice if we validate previous invalid", ); const validate3B2B1A = collectProtoarrayValidationStatus(fc); it("Inalidate 3B, 2B, 1A", () => { - expect(validate3B2B1A).to.be.deep.equal( + expect(validate3B2B1A).toEqual( toFcTestCase([ ["0", undefined, undefined, ExecutionStatus.PreMerge], ["1A", undefined, undefined, ExecutionStatus.Invalid], @@ -402,10 +402,10 @@ describe("executionStatus / poision forkchoice if we validate previous invalid", }, 3 ) - ).to.throw(Error); + ).toThrow(Error); - expect(fc.lvhError).to.be.deep.equal({lvhCode: LVHExecErrorCode.InvalidToValid, blockRoot: "2A", execHash: "2A"}); - expect(() => fc.findHead("0", 3)).to.throw(Error); + expect(fc.lvhError).toEqual({lvhCode: LVHExecErrorCode.InvalidToValid, blockRoot: "2A", execHash: "2A"}); + expect(() => fc.findHead("0", 3)).toThrow(Error); }); }); diff --git a/packages/fork-choice/test/unit/protoArray/getCommonAncestor.test.ts b/packages/fork-choice/test/unit/protoArray/getCommonAncestor.test.ts index 3d47d906f74a..766c02a15a23 100644 --- a/packages/fork-choice/test/unit/protoArray/getCommonAncestor.test.ts +++ b/packages/fork-choice/test/unit/protoArray/getCommonAncestor.test.ts @@ -1,4 +1,4 @@ -import {expect} from "chai"; +import {describe, it, expect} from "vitest"; import {ProtoArray, ExecutionStatus} from "../../../src/index.js"; describe("getCommonAncestor", () => { @@ -73,7 +73,7 @@ describe("getCommonAncestor", () => { it(`${nodeA} & ${nodeB} -> ${ancestor}`, () => { // eslint-disable-next-line @typescript-eslint/no-non-null-assertion const ancestorNode = fc.getCommonAncestor(fc.getNode(nodeA)!, fc.getNode(nodeB)!); - expect(ancestorNode && ancestorNode.blockRoot).to.equal(ancestor); + expect(ancestorNode && ancestorNode.blockRoot).toBe(ancestor); }); } @@ -104,5 +104,5 @@ describe("getCommonAncestor", () => { // multiple calls to applyScoreChanges don't keep on adding boosts to weight over // and over again, and applyScoreChanges can be safely called after onAttestations - expect(weightsAfterCall1).to.deep.equal(weightsAfterCall2); + expect(weightsAfterCall1).toEqual(weightsAfterCall2); }); diff --git a/packages/fork-choice/test/unit/protoArray/protoArray.test.ts b/packages/fork-choice/test/unit/protoArray/protoArray.test.ts index 88d6453e6204..c3bf8a0f439a 100644 --- a/packages/fork-choice/test/unit/protoArray/protoArray.test.ts +++ b/packages/fork-choice/test/unit/protoArray/protoArray.test.ts @@ -1,4 +1,4 @@ -import {expect} from "chai"; +import {describe, it, expect} from "vitest"; import {RootHex} from "@lodestar/types"; import {ProtoArray, ExecutionStatus} from "../../../src/index.js"; @@ -107,7 +107,7 @@ describe("ProtoArray", () => { ]; for (const [ancestorRoot, descendantRoot, isDescendant] of assertions) { - expect(fc.isDescendant(ancestorRoot, descendantRoot)).to.equal( + expect(fc.isDescendant(ancestorRoot, descendantRoot)).toBeWithMessage( isDescendant, `${descendantRoot} must be ${isDescendant ? "descendant" : "not descendant"} of ${ancestorRoot}` ); diff --git a/packages/fork-choice/vitest.config.ts b/packages/fork-choice/vitest.config.ts new file mode 100644 index 000000000000..1df0de848936 --- /dev/null +++ b/packages/fork-choice/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"], + }, + }) +); diff --git a/scripts/vitest/customMatchers.ts b/scripts/vitest/customMatchers.ts index 72735869b1c3..ef569dd23686 100644 --- a/scripts/vitest/customMatchers.ts +++ b/scripts/vitest/customMatchers.ts @@ -2,14 +2,14 @@ import {expect} from "vitest"; expect.extend({ - toBeValidEpochCommittee: ( + toBeValidEpochCommittee( committee: {index: number; slot: number; validators: unknown[]}, { committeeCount, validatorsPerCommittee, slotsPerEpoch, }: {committeeCount: number; validatorsPerCommittee: number; slotsPerEpoch: number} - ) => { + ) { if (committee.index < 0 || committee.index > committeeCount - 1) { return { message: () => @@ -39,8 +39,8 @@ expect.extend({ pass: true, }; }, - toBeWithMessage: (received: unknown, expected: unknown, message: string) => { - if (received === expected) { + toBeWithMessage(received: unknown, expected: unknown, message: string) { + if (Object.is(received, expected)) { return { message: () => "Expected value is truthy", pass: true, @@ -52,7 +52,7 @@ expect.extend({ message: () => message, }; }, - toSatisfy: (received: unknown, func: (received: unknown) => boolean) => { + toSatisfy(received: unknown, func: (received: unknown) => boolean) { if (func(received)) { return { message: () => "Expected value satisfied the condition", @@ -65,4 +65,17 @@ expect.extend({ message: () => "Expected value did not satisfy the condition", }; }, + toEqualWithMessage(received: unknown, expected: unknown, message: string) { + if (this.equals(received, expected)) { + return { + message: () => "Expected value is truthy", + pass: true, + }; + } + + return { + pass: false, + message: () => message, + }; + }, }); diff --git a/types/vitest/index.d.ts b/types/vitest/index.d.ts index effc9507161c..387edcfa5279 100644 --- a/types/vitest/index.d.ts +++ b/types/vitest/index.d.ts @@ -26,6 +26,12 @@ interface CustomMatchers { * ``` * */ toBeWithMessage(expected: unknown, message: string): R; + /** + * @deprecated + * We highly recommend to not use this matcher instead use detail test case with .toEqual + * where you don't need message to explain assertion + * */ + toEqualWithMessage(expected: unknown, message: string): R; } interface CustomAsymmetricMatchers extends CustomMatchers {