refactor: migrate all tests to Bulloak BTT format (#63)#119
Conversation
There was a problem hiding this comment.
Pull request overview
Refactors the Forge test suite to align with Bulloak’s Branching Tree Technique (BTT) by introducing .tree specs and reorganizing/renaming tests to match the intended hierarchical structure.
Changes:
- Added BTT
.treespecification files for existing test contracts. - Renamed and reorganized test functions into BTT-style “when/given/should” naming and sections.
- Split some previously compound tests into more granular cases and repositioned helper functions for readability.
Reviewed changes
Copilot reviewed 26 out of 26 changed files in this pull request and generated 8 comments.
Show a summary per file
| File | Description |
|---|---|
| test/automation/AutomationBase.tree | Adds BTT tree spec for automation tests. |
| test/automation/AutomationBase.t.sol | Splits/renames automation tests to BTT-style cases. |
| test/VotingStreakNFTModule.tree | Adds BTT tree spec for VotingStreakNFTModule tests. |
| test/VotingStreakNFTModule.t.sol | Renames/resections tests to match BTT structure. |
| test/VotingRecipientRegistry.tree | Adds BTT tree spec for VotingRecipientRegistry tests. |
| test/VotingRecipientRegistry.t.sol | Renames tests and reorganizes into BTT-style groupings. |
| test/VotingModuleSimple.tree | Adds BTT tree spec for VotingModuleSimple tests. |
| test/VotingModuleSimple.t.sol | Renames tests and moves vote-digest helper earlier. |
| test/VotingModule.tree | Adds BTT tree spec for VotingModule tests. |
| test/VotingModule.t.sol | Renames tests and moves vote-digest helper earlier. |
| test/TimeWeightedVotingPower.tree | Adds BTT tree spec for TimeWeightedVotingPower tests. |
| test/TimeWeightedVotingPower.t.sol | Renames/resections TWVP tests to align with tree nodes. |
| test/RecipientRegistry.tree | Adds BTT tree spec for RecipientRegistry tests. |
| test/RecipientRegistry.t.sol | Restructures RecipientRegistry tests into BTT “when/given” layout. |
| test/MockDistributionManager.tree | Adds BTT tree spec for MockDistributionManager tests. |
| test/MockDistributionManager.t.sol | Splits/renames tests into BTT-style cases. |
| test/FactoryModuleDeployment.tree | Adds BTT tree spec for factory deployment tests. |
| test/FactoryModuleDeployment.t.sol | Renames/resections factory deployment tests to BTT-style names. |
| test/DistributionCycleIntegration.tree | Adds BTT tree spec for integration test. |
| test/DistributionCycleIntegration.t.sol | Renames integration tests into BTT-style cases. |
| test/CycleModule.tree | Adds BTT tree spec for CycleModule tests. |
| test/CycleModule.t.sol | Renames/resections CycleModule tests to BTT conventions. |
| test/BreadKitTest.tree | Adds BTT tree spec for BreadKit tests. |
| test/BreadKitTest.t.sol | Renames/resections BreadKit tests into BTT-style cases. |
| test/AdminRecipientRegistry.tree | Adds BTT tree spec for AdminRecipientRegistry tests. |
| test/AdminRecipientRegistry.t.sol | Renames/resections AdminRecipientRegistry tests to BTT-style cases. |
Comments suppressed due to low confidence (1)
test/VotingStreakNFTModule.t.sol:233
test_RevertWhen_NonOwnerSetsNFTContractasserts both the non-owner revert case and the owner success path, but the corresponding.treeleaf only describes the revert behavior. For clearer BTT mapping (and easier failure attribution), consider splitting this into two tests or updating the tree to include the owner-success scenario.
function test_RevertWhen_NonOwnerSetsNFTContract() public {
// Arrange
address newNftAddress = address(0x9999);
// Act & Assert - Non-owner should revert
vm.prank(nonOwner);
vm.expectRevert();
harness.setNFTContract(newNftAddress);
// Act & Assert - Owner should succeed
vm.prank(admin);
harness.setNFTContract(newNftAddress);
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| function test_WhenANon_recipientProposes() external { | ||
| // it should revert with NotARecipient | ||
| vm.prank(NON_RECIPIENT); | ||
| vm.expectRevert(VotingRecipientRegistry.NotARecipient.selector); | ||
| registry.proposeAddition(NEW_RECIPIENT); | ||
| } | ||
|
|
||
| function test_RevertOnNonRecipientVote() public { | ||
| function test_WhenANon_recipientVotes() external { | ||
| // it should revert with NotEligibleVoter |
There was a problem hiding this comment.
Function names test_WhenANon_recipientProposes / test_WhenANon_recipientVotes include an underscore inside the word, which is inconsistent with the rest of the BTT-style names and reduces readability/searchability. Rename to use a single token (e.g., NonRecipient) while keeping the same meaning.
| } | ||
|
|
||
| function test_RevertOnRemovingNonExistent() public { | ||
| function test_WhenRemovingNon_existentRecipient() external { |
There was a problem hiding this comment.
test_WhenRemovingNon_existentRecipient uses an underscore inside “Non_existent”, which is inconsistent with other test names and makes grepping/filtering harder. Consider renaming to NonExistentRecipient (or similar) for readability.
| function test_WhenRemovingNon_existentRecipient() external { | |
| function test_WhenRemovingNonExistentRecipient() external { |
| } | ||
|
|
||
| function test_RevertOnRemovingNonExistent() public { | ||
| function test_RevertWhen_RemovingANon_existentRecipient() external { |
There was a problem hiding this comment.
test_RevertWhen_RemovingANon_existentRecipient includes an underscore inside “Non_existent”, which is inconsistent with other test names and reduces readability. Consider renaming to NonExistentRecipient (or similar).
| function test_RevertWhen_RemovingANon_existentRecipient() external { | |
| function test_RevertWhen_RemovingANonExistentRecipient() external { |
| function test_RevertWhen_ANon_adminQueuesAddition() external { | ||
| // it should revert | ||
| vm.prank(address(0xdead)); | ||
| vm.expectRevert(); | ||
| registry.queueRecipientAddition(RECIPIENT_1); | ||
| } | ||
|
|
||
| function test_OnlyAdminCanQueueRemoval() public { | ||
| function test_RevertWhen_ANon_adminQueuesRemoval() external { | ||
| // it should revert |
There was a problem hiding this comment.
test_RevertWhen_ANon_adminQueuesAddition / test_RevertWhen_ANon_adminQueuesRemoval include an underscore inside “Non_admin”. For consistency with the rest of the BTT-style names, consider renaming to NonAdmin... (no underscore inside the token).
| function test_WhenProposingAnAddition() external { | ||
| // it should create proposal with auto-vote from proposer | ||
| // it should snapshot required votes at creation | ||
| vm.prank(RECIPIENT_1); |
There was a problem hiding this comment.
test_WhenProposingAnAddition validates two separate behaviors (auto-vote + requiredVotes snapshot) while test/VotingRecipientRegistry.tree lists them as two distinct leaves. This makes it harder to map test results back to the BTT tree and conflicts with the PR goal of splitting compound tests. Consider splitting this into two tests (or collapsing the tree leaves into one if they must stay together).
| function test_WhenProposingARemoval() external { | ||
| // it should create removal proposal | ||
| // it should require fewer votes than addition | ||
| vm.prank(RECIPIENT_1); |
There was a problem hiding this comment.
test_WhenProposingARemoval covers both “create removal proposal” and “require fewer votes than addition”, but the corresponding .tree file models these as separate leaves. To keep the BTT structure and test output aligned, split into two tests or adjust the tree to a single leaf.
| function test_WhenAProposalExpires() external { | ||
| // it should reject votes on expired proposals | ||
| // it should reject execution of expired proposals |
There was a problem hiding this comment.
test_WhenAProposalExpires asserts both “reject votes” and “reject execution”, but VotingRecipientRegistry.tree lists these as separate leaves. Consider splitting into two tests so failures are attributable to a single leaf, or merge the leaves in the tree.
| // Helper functions | ||
|
|
||
| function _createVoteDigest(address voter, uint256[] memory points, uint256 nonce) internal view returns (bytes32) { | ||
| bytes32 voteTypehash = keccak256("Vote(address voter,bytes32 pointsHash,uint256 nonce)"); | ||
|
|
||
| bytes32 structHash = keccak256(abi.encode(voteTypehash, voter, keccak256(abi.encodePacked(points)), nonce)); | ||
|
|
||
| return keccak256(abi.encodePacked("\x19\x01", votingModule.DOMAIN_SEPARATOR(), structHash)); | ||
| } |
There was a problem hiding this comment.
This file now has two different implementations for the same EIP-712 vote digest/signature flow (createVoteSignature and _createVoteDigest). Keeping both increases maintenance cost and the risk they diverge. Consider implementing one in terms of the other (single source of truth) and using it consistently across the tests.
- Fix NatSpec grammar: 'reached' → 'has reached' - Add MAX_QUEUE_SIZE boundary tests for both addition and removal queues
Add .tree specification files for all 13 test contracts and restructure test functions to follow Branching Tree Technique naming conventions. All 115 tests pass with no logic changes — only function names and organization updated.
8b992ee to
8509224
Compare
…k-tests # Conflicts: # test/RecipientRegistry.t.sol
Summary
.treeBTT specification files for all 13 test contractsCloses #63
Test plan
bulloak scaffoldgenerates matching structure for all.treefilesStack: PR 2 of 10 (0.0.1) — stacked on #118