-
-
Notifications
You must be signed in to change notification settings - Fork 185
Add negative and boundary-condition tests for Election lifecycle #227
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
Open
harshkumarrai
wants to merge
1
commit into
AOSSIE-Org:main
Choose a base branch
from
harshkumarrai:fix-contract-tests
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
95 changes: 95 additions & 0 deletions
95
blockchain/test/lifecycle/Election.lifecycle.negative.test.js
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,95 @@ | ||
| const { expect } = require("chai"); | ||
| const { ethers } = require("hardhat"); | ||
|
|
||
| describe("Election — Negative & Boundary Lifecycle Tests", function () { | ||
| async function deployElectionFixture(startOffset, endOffset) { | ||
| const [owner, voter, other] = await ethers.getSigners(); | ||
|
|
||
| const now = (await ethers.provider.getBlock("latest")).timestamp; | ||
|
|
||
| const Election = await ethers.getContractFactory("Election"); | ||
| const election = await Election.deploy(); | ||
| await election.waitForDeployment(); | ||
|
|
||
| const electionInfo = { | ||
| startTime: now + startOffset, | ||
| endTime: now + endOffset, | ||
| name: "Test Election", | ||
| description: "Test Description", | ||
| }; | ||
|
|
||
| const candidates = [ | ||
| { candidateID: 0, name: "A", description: "A" }, | ||
| { candidateID: 1, name: "B", description: "B" }, | ||
| ]; | ||
|
|
||
| await election.initialize( | ||
| electionInfo, | ||
| candidates, | ||
| 0, // resultType | ||
| 0, // electionId | ||
| ethers.ZeroAddress, // ballot (unused in negative tests) | ||
| owner.address, | ||
| ethers.ZeroAddress // resultCalculator (unused) | ||
| ); | ||
|
|
||
| return { election, owner, voter, other, now }; | ||
| } | ||
| it("reverts userVote before election start", async function () { | ||
| const { election, voter } = await deployElectionFixture(1000, 2000); | ||
|
|
||
| await expect( | ||
| election.connect(voter).userVote([0]) | ||
| ).to.be.revertedWithCustomError(election, "ElectionInactive"); | ||
| }); | ||
| it("reverts userVote after election end", async function () { | ||
| const { election, voter, now } = await deployElectionFixture(10, 20); | ||
|
|
||
| await ethers.provider.send("evm_setNextBlockTimestamp", [now + 30]); | ||
| await ethers.provider.send("evm_mine"); | ||
|
|
||
| await expect( | ||
| election.connect(voter).userVote([0]) | ||
| ).to.be.revertedWithCustomError(election, "ElectionInactive"); | ||
| }); | ||
| it("reverts when user votes twice", async function () { | ||
| const { election, voter } = await deployElectionFixture(0, 1000); | ||
|
|
||
| const latest = await ethers.provider.getBlock("latest"); | ||
| await ethers.provider.send("evm_setNextBlockTimestamp", [latest.timestamp + 1]); | ||
| await ethers.provider.send("evm_mine"); | ||
|
|
||
| await expect( | ||
| election.connect(voter).userVote([0]) | ||
| ).to.be.reverted; | ||
|
|
||
| await expect( | ||
| election.connect(voter).userVote([0]) | ||
| ).to.be.reverted; | ||
| }); | ||
|
|
||
| it("reverts addCandidate after election start", async function () { | ||
| const { election, owner, now } = await deployElectionFixture(5, 1000); | ||
|
|
||
| await ethers.provider.send("evm_setNextBlockTimestamp", [now + 10]); | ||
| await ethers.provider.send("evm_mine"); | ||
|
|
||
| await expect( | ||
| election.connect(owner).addCandidate("X", "X") | ||
| ).to.be.revertedWithCustomError(election, "ElectionInactive"); | ||
| }); | ||
| it("reverts removeCandidate with invalid candidate id", async function () { | ||
| const { election, owner } = await deployElectionFixture(1000, 2000); | ||
|
|
||
| await expect( | ||
| election.connect(owner).removeCandidate(99) | ||
| ).to.be.revertedWithCustomError(election, "InvalidCandidateID"); | ||
| }); | ||
| it("reverts getResult before election end", async function () { | ||
| const { election } = await deployElectionFixture(0, 1000); | ||
|
|
||
| await expect( | ||
| election.getResult() | ||
| ).to.be.revertedWithCustomError(election, "ElectionIncomplete"); | ||
| }); | ||
| }); | ||
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.
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.
🧩 Analysis chain
🏁 Script executed:
Repository: AOSSIE-Org/Agora-Blockchain
Length of output: 330
🏁 Script executed:
Repository: AOSSIE-Org/Agora-Blockchain
Length of output: 103
🏁 Script executed:
Repository: AOSSIE-Org/Agora-Blockchain
Length of output: 1908
🏁 Script executed:
Repository: AOSSIE-Org/Agora-Blockchain
Length of output: 4922
🏁 Script executed:
Repository: AOSSIE-Org/Agora-Blockchain
Length of output: 103
🏁 Script executed:
Repository: AOSSIE-Org/Agora-Blockchain
Length of output: 1247
🏁 Script executed:
Repository: AOSSIE-Org/Agora-Blockchain
Length of output: 7459
🏁 Script executed:
Repository: AOSSIE-Org/Agora-Blockchain
Length of output: 891
🏁 Script executed:
Repository: AOSSIE-Org/Agora-Blockchain
Length of output: 53
Test logic error: first vote should succeed, not revert.
This test is named "reverts when user votes twice" but expects both votes to revert. For a valid double-voting test, the first vote should succeed, and only the second vote should revert with a specific error (
AlreadyVoted).Additionally, use
.to.be.revertedWithCustomError(election, "AlreadyVoted")instead of generic.to.be.reverted, consistent with other tests in this file.🐛 Proposed fix
it("reverts when user votes twice", async function () { - const { election, voter } = await deployElectionFixture(0, 1000); + const { election, voter } = await deployElectionFixture(10, 2000); const latest = await ethers.provider.getBlock("latest"); - await ethers.provider.send("evm_setNextBlockTimestamp", [latest.timestamp + 1]); + await ethers.provider.send("evm_setNextBlockTimestamp", [latest.timestamp + 15]); await ethers.provider.send("evm_mine"); - await expect( - election.connect(voter).userVote([0]) - ).to.be.reverted; + // First vote should succeed + await election.connect(voter).userVote([0]); + // Second vote should revert with specific error await expect( election.connect(voter).userVote([0]) - ).to.be.reverted; + ).to.be.revertedWithCustomError(election, "AlreadyVoted"); });🤖 Prompt for AI Agents
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.
In this test, the election is intentionally initialized with a zero address ballot to keep the PR scoped to lifecycle boundary checks only. Because of this, the first userVote reverts before reaching the AlreadyVoted check.
I avoided introducing a mock ballot here to keep the PR minimal. If maintainers prefer strict AlreadyVoted validation, I am happy to add that in a follow-up PR with proper mocks.
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.
@harshkumarrai, thank you for the clarification! Your scoping decision makes sense—keeping the PR focused on lifecycle boundary checks without introducing mock ballot infrastructure is reasonable.The test is valid as-is for verifying that voting fails with an invalid ballot configuration. To make the test's actual behavior clearer to future readers, consider adding a brief comment above the test explaining that the zero-address ballot causes the revert, so it's testing invalid configuration rather than pure double-voting logic:
A follow-up PR with proper ballot mocks to validate the
AlreadyVotederror specifically would be valuable for comprehensive coverage, but it's not blocking for this PR's objectives.٩(◕‿◕)۶
✏️ Learnings added