-
Notifications
You must be signed in to change notification settings - Fork 6
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat/validator tests #388
base: dev
Are you sure you want to change the base?
feat/validator tests #388
Changes from 7 commits
a1b4008
295a2e7
2d2c850
25b88b6
759fd1f
a38d3ea
c4fa598
76dc985
f9a91e9
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
import type { Config } from "jest"; | ||
|
||
const config: Config = { | ||
preset: "ts-jest", | ||
testEnvironment: "node", | ||
collectCoverage: true, | ||
collectCoverageFrom: ["**/*.ts"], | ||
}; | ||
|
||
export default config; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,307 @@ | ||
import { ArbToEthTransactionHandler, ContractType } from "./transactionHandler"; | ||
import { MockEmitter, defaultEmitter } from "../utils/emitter"; | ||
import { BotEvents } from "../utils/botEvents"; | ||
import { ClaimNotSetError } from "../utils/errors"; | ||
import { ClaimStruct } from "@kleros/vea-contracts/typechain-types/arbitrumToEth/VeaInboxArbToEth"; | ||
|
||
describe("ArbToEthTransactionHandler", () => { | ||
let epoch: number = 100; | ||
let veaInbox: any; | ||
let veaOutbox: any; | ||
let veaInboxProvider: any; | ||
let veaOutboxProvider: any; | ||
let claim: ClaimStruct = null; | ||
|
||
beforeEach(() => { | ||
veaInboxProvider = { | ||
getTransactionReceipt: jest.fn(), | ||
getBlock: jest.fn(), | ||
}; | ||
veaOutbox = { | ||
estimateGas: jest.fn(), | ||
withdrawChallengeDeposit: jest.fn(), | ||
["challenge(uint256,(bytes32,address,uint32,uint32,uint32,uint8,address))"]: jest.fn(), | ||
}; | ||
veaInbox = { | ||
sendSnapshot: jest.fn(), | ||
}; | ||
claim = { | ||
stateRoot: "0x1234", | ||
claimer: "0x1234", | ||
timestampClaimed: 1234, | ||
timestampVerification: 0, | ||
blocknumberVerification: 0, | ||
honest: 0, | ||
challenger: "0x1234", | ||
}; | ||
}); | ||
describe("constructor", () => { | ||
it("should create a new TransactionHandler without claim", () => { | ||
const transactionHandler = new ArbToEthTransactionHandler( | ||
epoch, | ||
veaInbox, | ||
veaOutbox, | ||
veaInboxProvider, | ||
veaOutboxProvider | ||
); | ||
expect(transactionHandler).toBeDefined(); | ||
expect(transactionHandler.epoch).toEqual(epoch); | ||
expect(transactionHandler.veaOutbox).toEqual(veaOutbox); | ||
expect(transactionHandler.emitter).toEqual(defaultEmitter); | ||
}); | ||
|
||
it("should create a new TransactionHandler with claim", () => { | ||
const transactionHandler = new ArbToEthTransactionHandler( | ||
epoch, | ||
veaInbox, | ||
veaOutbox, | ||
veaInboxProvider, | ||
veaOutboxProvider, | ||
defaultEmitter, | ||
claim | ||
); | ||
expect(transactionHandler).toBeDefined(); | ||
expect(transactionHandler.epoch).toEqual(epoch); | ||
expect(transactionHandler.veaOutbox).toEqual(veaOutbox); | ||
expect(transactionHandler.claim).toEqual(claim); | ||
expect(transactionHandler.emitter).toEqual(defaultEmitter); | ||
}); | ||
}); | ||
|
||
describe("checkTransactionStatus", () => { | ||
let transactionHandler: ArbToEthTransactionHandler; | ||
let finalityBlock: number = 100; | ||
const mockEmitter = new MockEmitter(); | ||
beforeEach(() => { | ||
transactionHandler = new ArbToEthTransactionHandler( | ||
epoch, | ||
veaInbox, | ||
veaOutbox, | ||
veaInboxProvider, | ||
veaOutboxProvider, | ||
mockEmitter | ||
); | ||
veaInboxProvider.getBlock.mockResolvedValue({ number: finalityBlock }); | ||
}); | ||
|
||
it("should return false if transaction is not final", async () => { | ||
jest.spyOn(mockEmitter, "emit"); | ||
veaInboxProvider.getTransactionReceipt.mockResolvedValue({ | ||
blockNumber: finalityBlock - (transactionHandler.requiredConfirmations - 1), | ||
}); | ||
const trnxHash = "0x123456"; | ||
const status = await transactionHandler.checkTransactionStatus(trnxHash, ContractType.INBOX); | ||
expect(status).toBeTruthy(); | ||
expect(mockEmitter.emit).toHaveBeenCalledWith( | ||
BotEvents.TXN_NOT_FINAL, | ||
trnxHash, | ||
transactionHandler.requiredConfirmations - 1 | ||
); | ||
}); | ||
|
||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Mismatch Between Test Name and Assertions The test title says "should return false if transaction is not final", but it checks Use one of the following fixes to align the test name or logic: -it("should return false if transaction is not final", async () => {
+it("should return true if transaction is not final", async () => { Or invert the assertion: -expect(status).toBeTruthy();
+expect(status).toBeFalsy();
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Fixed it 👍 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @mani99brar, thanks for confirming the fix! Let me know if you need further assistance. |
||
it("should return true if transaction is pending", async () => { | ||
jest.spyOn(mockEmitter, "emit"); | ||
veaInboxProvider.getTransactionReceipt.mockResolvedValue(null); | ||
const trnxHash = "0x123456"; | ||
const status = await transactionHandler.checkTransactionStatus(trnxHash, ContractType.INBOX); | ||
expect(status).toBeTruthy(); | ||
expect(mockEmitter.emit).toHaveBeenCalledWith(BotEvents.TXN_PENDING, trnxHash); | ||
}); | ||
|
||
it("should return false if transaction is final", async () => { | ||
jest.spyOn(mockEmitter, "emit"); | ||
veaInboxProvider.getTransactionReceipt.mockResolvedValue({ | ||
blockNumber: finalityBlock - transactionHandler.requiredConfirmations, | ||
}); | ||
const trnxHash = "0x123456"; | ||
const status = await transactionHandler.checkTransactionStatus(trnxHash, ContractType.INBOX); | ||
expect(status).toBeFalsy(); | ||
expect(mockEmitter.emit).toHaveBeenCalledWith( | ||
BotEvents.TXN_FINAL, | ||
trnxHash, | ||
transactionHandler.requiredConfirmations | ||
); | ||
}); | ||
|
||
it("should return false if transaction hash is null", async () => { | ||
const trnxHash = null; | ||
const status = await transactionHandler.checkTransactionStatus(trnxHash, ContractType.INBOX); | ||
expect(status).toBeFalsy(); | ||
}); | ||
}); | ||
|
||
describe("challengeClaim", () => { | ||
let transactionHandler: ArbToEthTransactionHandler; | ||
const mockEmitter = new MockEmitter(); | ||
beforeEach(() => { | ||
transactionHandler = new ArbToEthTransactionHandler( | ||
epoch, | ||
veaInbox, | ||
veaOutbox, | ||
veaInboxProvider, | ||
veaOutboxProvider, | ||
mockEmitter | ||
); | ||
transactionHandler.claim = claim; | ||
}); | ||
|
||
it("should emit CHALLENGING event and throw error if claim is not set", async () => { | ||
jest.spyOn(mockEmitter, "emit"); | ||
transactionHandler.claim = null; | ||
await expect(transactionHandler.challengeClaim()).rejects.toThrow(ClaimNotSetError); | ||
expect(mockEmitter.emit).toHaveBeenCalledWith(BotEvents.CHALLENGING); | ||
}); | ||
|
||
it("should not challenge claim if txn is pending", async () => { | ||
jest.spyOn(transactionHandler, "checkTransactionStatus").mockResolvedValue(true); | ||
transactionHandler.transactions.challengeTxn = "0x1234"; | ||
await transactionHandler.challengeClaim(); | ||
expect(transactionHandler.checkTransactionStatus).toHaveBeenCalledWith( | ||
transactionHandler.transactions.challengeTxn, | ||
ContractType.OUTBOX | ||
); | ||
expect(veaOutbox.estimateGas).not.toHaveBeenCalled(); | ||
}); | ||
|
||
it("should challenge claim", async () => { | ||
jest.spyOn(transactionHandler, "checkTransactionStatus").mockResolvedValue(false); | ||
const mockChallenge = jest.fn().mockResolvedValue({ hash: "0x1234" }) as any; | ||
(mockChallenge as any).estimateGas = jest.fn().mockResolvedValue(BigInt(100000)); | ||
veaOutbox["challenge(uint256,(bytes32,address,uint32,uint32,uint32,uint8,address))"] = mockChallenge; | ||
await transactionHandler.challengeClaim(); | ||
expect(transactionHandler.checkTransactionStatus).toHaveBeenCalledWith(null, ContractType.OUTBOX); | ||
expect(transactionHandler.transactions.challengeTxn).toEqual("0x1234"); | ||
}); | ||
|
||
it.todo("should set challengeTxn as completed when txn is final"); | ||
}); | ||
|
||
describe("withdrawDeposit", () => { | ||
let transactionHandler: ArbToEthTransactionHandler; | ||
const mockEmitter = new MockEmitter(); | ||
beforeEach(() => { | ||
transactionHandler = new ArbToEthTransactionHandler( | ||
epoch, | ||
veaInbox, | ||
veaOutbox, | ||
veaInboxProvider, | ||
veaOutboxProvider, | ||
mockEmitter | ||
); | ||
veaOutbox.withdrawChallengeDeposit.mockResolvedValue("0x1234"); | ||
transactionHandler.claim = claim; | ||
}); | ||
|
||
it("should withdraw deposit", async () => { | ||
jest.spyOn(transactionHandler, "checkTransactionStatus").mockResolvedValue(false); | ||
veaOutbox.withdrawChallengeDeposit.mockResolvedValue({ hash: "0x1234" }); | ||
await transactionHandler.withdrawChallengeDeposit(); | ||
expect(transactionHandler.checkTransactionStatus).toHaveBeenCalledWith(null, ContractType.OUTBOX); | ||
expect(transactionHandler.transactions.withdrawChallengeDepositTxn).toEqual("0x1234"); | ||
}); | ||
|
||
it("should not withdraw deposit if txn is pending", async () => { | ||
jest.spyOn(transactionHandler, "checkTransactionStatus").mockResolvedValue(true); | ||
transactionHandler.transactions.withdrawChallengeDepositTxn = "0x1234"; | ||
await transactionHandler.withdrawChallengeDeposit(); | ||
expect(transactionHandler.checkTransactionStatus).toHaveBeenCalledWith( | ||
transactionHandler.transactions.withdrawChallengeDepositTxn, | ||
ContractType.OUTBOX | ||
); | ||
}); | ||
|
||
it("should throw an error if claim is not set", async () => { | ||
transactionHandler.claim = null; | ||
await expect(transactionHandler.withdrawChallengeDeposit()).rejects.toThrow(ClaimNotSetError); | ||
}); | ||
|
||
it("should emit WITHDRAWING event", async () => { | ||
jest.spyOn(mockEmitter, "emit"); | ||
await transactionHandler.withdrawChallengeDeposit(); | ||
expect(mockEmitter.emit).toHaveBeenCalledWith(BotEvents.WITHDRAWING); | ||
}); | ||
}); | ||
|
||
describe("sendSnapshot", () => { | ||
let transactionHandler: ArbToEthTransactionHandler; | ||
const mockEmitter = new MockEmitter(); | ||
beforeEach(() => { | ||
transactionHandler = new ArbToEthTransactionHandler( | ||
epoch, | ||
veaInbox, | ||
veaOutbox, | ||
veaInboxProvider, | ||
veaOutboxProvider, | ||
mockEmitter | ||
); | ||
transactionHandler.claim = claim; | ||
}); | ||
|
||
it("should send snapshot", async () => { | ||
jest.spyOn(transactionHandler, "checkTransactionStatus").mockResolvedValue(false); | ||
veaInbox.sendSnapshot.mockResolvedValue({ hash: "0x1234" }); | ||
await transactionHandler.sendSnapshot(); | ||
expect(transactionHandler.checkTransactionStatus).toHaveBeenCalledWith(null, ContractType.INBOX); | ||
expect(transactionHandler.transactions.sendSnapshotTxn).toEqual("0x1234"); | ||
}); | ||
|
||
it("should not send snapshot if txn is pending", async () => { | ||
jest.spyOn(transactionHandler, "checkTransactionStatus").mockResolvedValue(true); | ||
transactionHandler.transactions.sendSnapshotTxn = "0x1234"; | ||
await transactionHandler.sendSnapshot(); | ||
expect(transactionHandler.checkTransactionStatus).toHaveBeenCalledWith( | ||
transactionHandler.transactions.sendSnapshotTxn, | ||
ContractType.INBOX | ||
); | ||
expect(veaInbox.sendSnapshot).not.toHaveBeenCalled(); | ||
}); | ||
|
||
it("should throw an error if claim is not set", async () => { | ||
jest.spyOn(mockEmitter, "emit"); | ||
transactionHandler.claim = null; | ||
await expect(transactionHandler.sendSnapshot()).rejects.toThrow(ClaimNotSetError); | ||
expect(mockEmitter.emit).toHaveBeenCalledWith(BotEvents.SENDING_SNAPSHOT, epoch); | ||
}); | ||
}); | ||
|
||
describe("resolveChallengedClaim", () => { | ||
let mockMessageExecutor: any; | ||
let transactionHandler: ArbToEthTransactionHandler; | ||
const mockEmitter = new MockEmitter(); | ||
beforeEach(() => { | ||
mockMessageExecutor = jest.fn(); | ||
transactionHandler = new ArbToEthTransactionHandler( | ||
epoch, | ||
veaInbox, | ||
veaOutbox, | ||
veaInboxProvider, | ||
veaOutboxProvider, | ||
mockEmitter | ||
); | ||
}); | ||
it("should resolve challenged claim", async () => { | ||
jest.spyOn(transactionHandler, "checkTransactionStatus").mockResolvedValue(false); | ||
transactionHandler.transactions.sendSnapshotTxn = "0x1234"; | ||
mockMessageExecutor.mockResolvedValue({ hash: "0x1234" }); | ||
await transactionHandler.resolveChallengedClaim( | ||
transactionHandler.transactions.sendSnapshotTxn, | ||
mockMessageExecutor | ||
); | ||
expect(transactionHandler.checkTransactionStatus).toHaveBeenCalledWith( | ||
transactionHandler.transactions.sendSnapshotTxn, | ||
ContractType.OUTBOX | ||
); | ||
expect(transactionHandler.transactions.executeSnapshotTxn).toEqual("0x1234"); | ||
}); | ||
|
||
it("should not resolve challenged claim if txn is pending", async () => { | ||
jest.spyOn(transactionHandler, "checkTransactionStatus").mockResolvedValue(true); | ||
transactionHandler.transactions.sendSnapshotTxn = "0x1234"; | ||
await transactionHandler.resolveChallengedClaim(mockMessageExecutor); | ||
expect(transactionHandler.checkTransactionStatus).toHaveBeenCalledWith( | ||
transactionHandler.transactions.sendSnapshotTxn, | ||
ContractType.OUTBOX | ||
); | ||
}); | ||
}); | ||
}); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Consolidate duplicate RPC endpoints.
The same RPC URLs are defined multiple times:
RPC_ARB_SEPOLIA
andRPC_ARB
both point tohttps://sepolia-rollup.arbitrum.io/rpc
RPC_CHIADO
andRPC_GNOSIS
both point tohttps://rpc.chiadochain.net
Consider consolidating these into single variables with clear naming to avoid confusion:
📝 Committable suggestion
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
RPC urls are same but the env vars are different for devnet and testnet/mainnet. So it's fine.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@mani99brar, understood. Thanks for clarifying your approach regarding separate environment variables for devnet vs. testnet/mainnet. No changes needed here!
✏️ Learnings added