66 comprehensive tests covering complete DAO governance system with voting, community features, and deadline management
Our DAO test suite validates a governance system where:
- Deployer holds 400,000 DAPP tokens (40% - founding stake)
- 3 investors each hold 200,000 DAPP tokens (20% each - equal voting power)
- 1 recipient holds 0 tokens (demonstrates non-voting participant)
- Quorum: 500,000+ tokens needed to finalize/cancel proposals (majority rule)
- Voting: Support for For/Against/Abstain voting with net vote calculation
// Token distribution: 1M total supply
// Deployer: 400K tokens, Investors 1-3: 200K each, Recipient: 0 tokens
// Quorum = 50% + 1 token for democratic decisions = 500,000 tokens + 1 wei
dao = await DAO.deploy(token.address, '500000000000000000000001')| Category | Tests | Purpose | Learning Focus |
|---|---|---|---|
| π Deployment | 3 | Basic setup validation | Contract initialization, token distribution |
| π Proposals | 5 | Creation & validation | Proposal lifecycle, validation rules |
| π³οΈ Voting | 5 | Standard voting mechanics | Democratic participation, vote counting |
| β Vote Against | 3 | Negative voting system | Opposition mechanics, balanced governance |
| β° Deadline Features | 12 | Time-based constraints | Temporal governance, deadline enforcement |
| π₯ Community Features | 12 | User engagement tracking | Participation metrics, community building |
| π« Cancellation | 6 | Proposal cancellation logic | Governance flexibility, proposal lifecycle |
| βοΈ Governance | 8 | Finalization & treasury | Decision execution, fund management |
| πͺ Token Suite | 12 | ERC-20 functionality | Token mechanics, transfer validation |
| π Security | 4 | Access control & validation | Permission systems, attack prevention |
Total: 66 Tests covering every aspect of DAO governance from basic token operations to complex community engagement patterns.
- Votes: Net result (For votes - Against votes)
- Abstain votes: Don't affect net vote count but count toward participation
- Quorum: Based on total token participation, not vote direction
β Success: Deployer + 1 investor vote (600K tokens) - Reaching Quorum for Finalization
deployer.vote(proposalId, true) // 400K tokens
investor1.vote(proposalId, true) // 200K tokens
// Total: 600K > 500K quorum ββ Failure: 2 investors vote (400K tokens) - Insufficient Votes
investor1.vote(proposalId, true) // 200K tokens
investor2.vote(proposalId, true) // 200K tokens
// Total: 400K < 500K quorum βπ« Cancellation: 3 vote against (600K negative)
investor1.vote(proposalId, false) // -200K tokens
investor2.vote(proposalId, false) // -200K tokens
investor3.vote(proposalId, false) // -200K tokens
// Negative votes: 600K > 500K quorum β CANCELLED// Legacy vote (always in favor)
dao.connect(investor1)["vote(uint256)"](proposalId)
// Enhanced vote (specify direction)
dao.connect(investor1)["vote(uint256,bool)"](proposalId, false)Β Β Β Β Understanding Proposal IDs in Tests
Β Β Β Β How Proposal IDs Are Assigned
uint256 public proposalCount; // Starts at 0
function createProposal(...) {
proposalCount++; // Increment first (1, 2, 3...)
proposals[proposalCount] = Proposal(...); // Store at that ID
}Β Β Β Β Each test gets a fresh blockchain state, but within a single test:
describe('Governance', () => {
beforeEach(async () => {
// Creates Proposal 1 for this test suite
transaction = await dao.connect(investor1).createProposal('Proposal 1', ...)
})
it('rejects finalization of cancelled proposal', async () => {
// Need separate proposal to test cancellation scenario
// Creates Proposal 2 (proposalCount becomes 2)
transaction = await dao.connect(investor1).createProposal('Proposal 2', ...)
// Work with proposalId=2 for this specific test
await dao.connect(investor1).vote(2, false)
await dao.connect(investor1).cancelProposal(2)
await expect(dao.connect(investor1).finalizeProposal(2))
.to.be.revertedWith('proposal was cancelled')
})
})| Scenario | Proposal ID | Purpose |
|---|---|---|
| Standard tests | 1 |
Use proposal from beforeEach |
| Isolation needed | 2 |
Create fresh proposal for specific test |
| Multiple proposals | 1, 2, 3... |
Test interactions between proposals |
// β
Good - Clear separation of concerns
it('specific test scenario', async () => {
// Create dedicated proposal for this test
await dao.createProposal('Test Proposal', ...)
const newProposalId = await dao.proposalCount() // Get latest ID
// Test specific scenario with clean state
await dao.vote(newProposalId, false)
await dao.cancelProposal(newProposalId)
})
// β Avoid - Reusing proposals can cause test interdependence
it('test that modifies existing proposal', async () => {
await dao.vote(1, true) // Modifies proposal from beforeEach
// This could affect other tests
})Using different proposal IDs (like proposalId=2) ensures test isolation and allows testing specific scenarios without interference from setup data. Each proposal ID represents a separate governance decision with its own voting history and state.
- Double voting prevention
- Non-token holder restrictions
- Insufficient fund rejections
- State Transitions: Active β Finalized, Active β Cancelled
- Cross-Feature Interactions: Cancelled proposals can't be finalized
// Check multiple state changes in one test
const proposal = await dao.proposals(1)
expect(proposal.finalized).to.equal(true)
expect(proposal.cancelled).to.equal(false)- Specific error messages for all failure modes
- Access control validation
- Business logic enforcement
- State consistency checks
// β
Specific error testing
await expect(dao.connect(user).vote(1, true))
.to.be.revertedWith('must be token holder')
// β Generic error testing
await expect(dao.connect(user).vote(1, true))
.to.be.revertedawait expect(transaction).to.emit(dao, "Vote")
.withArgs(proposalId, voterAddress, inFavor)The test suite also supports our ProposalAnalytics component by ensuring:
- Proposal state tracking (active/finalized/cancelled)
- Vote counting (positive/negative/net)
- Quorum success rates
- Participation metrics
npx hardhat test
# Expected: 66 passing tests- Comprehensive Coverage: tests validate all governance scenarios
- Enhanced Voting: Supports both legacy and directional voting
- Proposal Isolation: Each test uses appropriate proposal IDs
- Specific Testing: Error messages and state changes verified precisely
- Analytics Ready: Tests support real-time dashboard metrics
- Verifies DAO receives initial ETH funding
- Confirms token address is correctly set
- Validates quorum value
Success Cases:
- Updates proposal count
- Stores proposal data correctly
- Emits Propose event
Failure Cases:
- Rejects proposals exceeding treasury balance
- Blocks non-token holders from creating proposals
Success Cases:
- Updates
positiveVotescounter - Emits Vote event with correct parameters
Failure Cases:
- Prevents non-token holders from voting
- Blocks double voting attempts
Success Cases:
- Updates
negativeVotescounter - Calculates net votes correctly (positive - negative)
- Emits Vote event with
falseparameter
Success Cases:
- Marks proposal as cancelled when negative votes reach quorum
- Emits Cancel event
Failure Cases:
- Rejects cancellation with insufficient negative votes
- Blocks non-token holders from cancelling
- Prevents double cancellation
Success Cases:
- Transfers funds to recipient when finalized
- Updates proposal status to finalized
- Emits Finalize event
Failure Cases:
- Blocks finalization without sufficient votes
- Prevents non-token holders from finalizing
- Blocks re-finalization of completed proposals
- Prevents finalization of cancelled proposals
- Complete ERC-20 functionality validation
- Transfer, approval, and allowance mechanics
- Error handling for invalid operations
Learning Focus: Time-based governance constraints and deadline management
Key Concepts:
- Proposal Deadlines: Time limits for voting periods
- Deadline Enforcement: Automatic proposal closure after deadline
- Time-based Validation: Preventing votes after deadline expiration
- Deadline Extension: Governance flexibility for critical proposals
Test Scenarios:
// Create proposal with deadline
await dao.createProposal("Urgent Security Update", "Fix critical vulnerability", recipient.address, tokens(100), deadline)
// Test deadline enforcement
await network.provider.send("evm_increaseTime", [86400]) // Advance 1 day
await expect(dao.vote(proposalId, true)).to.be.revertedWith("Voting period has ended")Success Cases:
- Creates proposals with valid deadlines
- Allows voting before deadline
- Enforces deadline restrictions
- Handles deadline edge cases
Failure Cases:
- Blocks voting after deadline
- Prevents invalid deadline creation
- Handles time manipulation attacks
Learning Focus: User engagement tracking and community building
Key Concepts:
- Participation Tracking: Monitor user voting activity
- Community Metrics: Calculate engagement statistics
- Achievement Systems: Reward active participants
- Social Governance: Encourage community involvement
Test Scenarios:
// Track user participation
const participation = await dao.getUserParticipation(user.address)
expect(participation.proposalsVoted).to.equal(3)
expect(participation.participationRate).to.equal(75) // 3 out of 4 proposals
// Community leaderboard
const topVoters = await dao.getTopParticipants(5)
expect(topVoters[0].votes).to.be.gte(topVoters[1].votes)Success Cases:
- Tracks individual user participation
- Calculates community-wide statistics
- Maintains participation leaderboards
- Rewards consistent participation
Failure Cases:
- Handles users with no participation
- Prevents manipulation of participation metrics
- Validates participation calculations
Learning Focus: Access control and attack prevention
Key Concepts:
- Permission Systems: Role-based access control
- Economic Security: Token-based security models
Test Scenarios:
// Access control validation
await expect(dao.connect(nonTokenHolder).vote(proposalId, true))
.to.be.revertedWith("Insufficient token balance")
// Reentrancy protection
await expect(maliciousContract.attemptReentrancy())
.to.be.revertedWith("ReentrancyGuard: reentrant call")Success Cases:
- Enforces token-based permissions
- Prevents unauthorized access
- Validates all user inputs
- Protects against common attacks
Failure Cases:
- Blocks non-token holder actions
- Prevents reentrancy attacks
- Handles malformed inputs
- Protects treasury funds
- Smart Contract Architecture: Learn how complex governance systems are structured
- Testing Patterns: Understand comprehensive test coverage strategies
- Economic Incentives: See how tokens create aligned incentives
- Community Governance: Experience democratic decision-making at scale
- Best Practices: Production-ready smart contract patterns
- Security Considerations: Real-world attack vectors and defenses
- Gas Optimization: Efficient contract design principles
- User Experience: Building intuitive governance interfaces
- Democratic Processes: Digital democracy implementation
- Participation Incentives: Encouraging community engagement
- Decision Frameworks: Structured proposal and voting systems
- Transparency: Open and auditable governance processes
# Run all 66 tests
npx hardhat test
# Run specific categories
npx hardhat test test/DAO.js # Core governance (42 tests)
npx hardhat test test/DeadlineFeatures.js # Deadline management (12 tests)
npx hardhat test test/CommunityFeatures.js # Community engagement (12 tests)This comprehensive test suite serves to provide examples for specific concepts while contributing to a robust, production-ready DAO system