173 lines
5.1 KiB
Solidity
173 lines
5.1 KiB
Solidity
// SPDX-License-Identifier: MIT
|
||
pragma solidity ^0.8.20;
|
||
|
||
import "forge-std/Test.sol";
|
||
import "../src/Governance.sol";
|
||
|
||
contract GovernanceTest is Test {
|
||
Governance governance;
|
||
|
||
address admin = address(1);
|
||
address member1 = address(10);
|
||
address member2 = address(11);
|
||
address member3 = address(12);
|
||
address member4 = address(13);
|
||
address member5 = address(14);
|
||
address target = address(99);
|
||
|
||
function setUp() public {
|
||
vm.startPrank(admin);
|
||
|
||
address[] memory members = new address[](5);
|
||
members[0] = member1;
|
||
members[1] = member2;
|
||
members[2] = member3;
|
||
members[3] = member4;
|
||
members[4] = member5;
|
||
|
||
governance = new Governance();
|
||
governance.initialize(members, admin);
|
||
|
||
vm.stopPrank();
|
||
}
|
||
|
||
function test_ProposeCreatesProposal() public {
|
||
vm.prank(member1);
|
||
uint256 proposalId = governance.propose(target, "0x1234", "Test proposal");
|
||
|
||
(
|
||
address pTarget,
|
||
uint256 executeAfter,
|
||
uint256 approvalCount,
|
||
bool executed,
|
||
bool emergency,
|
||
address proposer,
|
||
string memory description
|
||
) = governance.getProposal(proposalId);
|
||
|
||
assertEq(pTarget, target);
|
||
assertEq(approvalCount, 1); // 提案者自动审批
|
||
assertFalse(executed);
|
||
assertFalse(emergency);
|
||
assertEq(proposer, member1);
|
||
assertEq(description, "Test proposal");
|
||
assertEq(executeAfter, block.timestamp + 48 hours);
|
||
}
|
||
|
||
function test_EmergencyProposalShortTimelock() public {
|
||
vm.prank(member1);
|
||
uint256 proposalId = governance.proposeEmergency(target, "0x1234", "Emergency");
|
||
|
||
(, uint256 executeAfter,,, bool emergency,,) = governance.getProposal(proposalId);
|
||
assertTrue(emergency);
|
||
assertEq(executeAfter, block.timestamp + 4 hours);
|
||
}
|
||
|
||
function test_ApproveAndExecute() public {
|
||
// 提案
|
||
vm.prank(member1);
|
||
uint256 proposalId = governance.propose(target, "", "Test");
|
||
|
||
// member2 和 member3 审批 (共 3 个)
|
||
vm.prank(member2);
|
||
governance.approve(proposalId);
|
||
vm.prank(member3);
|
||
governance.approve(proposalId);
|
||
|
||
(,, uint256 approvalCount,,,,) = governance.getProposal(proposalId);
|
||
assertEq(approvalCount, 3);
|
||
|
||
// 快进 48h
|
||
vm.warp(block.timestamp + 48 hours);
|
||
|
||
// 构造一个可执行的target
|
||
// 由于 target 没有代码,call 会成功
|
||
vm.prank(member1);
|
||
governance.execute(proposalId);
|
||
|
||
(,,, bool executed,,,) = governance.getProposal(proposalId);
|
||
assertTrue(executed);
|
||
}
|
||
|
||
function test_ExecuteNotEnoughApprovalsReverts() public {
|
||
vm.prank(member1);
|
||
uint256 proposalId = governance.propose(target, "", "Test");
|
||
|
||
// 只有 1 个审批(提案者自己)
|
||
vm.warp(block.timestamp + 48 hours);
|
||
|
||
vm.prank(member1);
|
||
vm.expectRevert("Governance: not enough approvals");
|
||
governance.execute(proposalId);
|
||
}
|
||
|
||
function test_ExecuteTimelockNotExpiredReverts() public {
|
||
vm.prank(member1);
|
||
uint256 proposalId = governance.propose(target, "", "Test");
|
||
|
||
vm.prank(member2);
|
||
governance.approve(proposalId);
|
||
vm.prank(member3);
|
||
governance.approve(proposalId);
|
||
|
||
// 不快进时间
|
||
vm.prank(member1);
|
||
vm.expectRevert("Governance: timelock not expired");
|
||
governance.execute(proposalId);
|
||
}
|
||
|
||
function test_DoubleApproveReverts() public {
|
||
vm.prank(member1);
|
||
uint256 proposalId = governance.propose(target, "", "Test");
|
||
|
||
vm.prank(member1);
|
||
vm.expectRevert("Governance: already approved");
|
||
governance.approve(proposalId);
|
||
}
|
||
|
||
function test_CancelByProposer() public {
|
||
vm.prank(member1);
|
||
uint256 proposalId = governance.propose(target, "", "Test");
|
||
|
||
vm.prank(member1);
|
||
governance.cancel(proposalId);
|
||
|
||
(,,, bool executed,,,) = governance.getProposal(proposalId);
|
||
assertTrue(executed); // 标记为已执行以阻止未来执行
|
||
}
|
||
|
||
function test_CancelByNonProposerReverts() public {
|
||
vm.prank(member1);
|
||
uint256 proposalId = governance.propose(target, "", "Test");
|
||
|
||
vm.prank(member2);
|
||
vm.expectRevert("Governance: not authorized");
|
||
governance.cancel(proposalId);
|
||
}
|
||
|
||
function test_HasApproved() public {
|
||
vm.prank(member1);
|
||
uint256 proposalId = governance.propose(target, "", "Test");
|
||
|
||
assertTrue(governance.hasApproved(proposalId, member1));
|
||
assertFalse(governance.hasApproved(proposalId, member2));
|
||
}
|
||
|
||
function test_OnlyMultisigCanPropose() public {
|
||
address random = address(888);
|
||
vm.prank(random);
|
||
vm.expectRevert();
|
||
governance.propose(target, "", "Test");
|
||
}
|
||
|
||
function test_RecordPreviousImplementation() public {
|
||
address proxy = address(100);
|
||
address impl = address(101);
|
||
|
||
vm.prank(member1);
|
||
governance.recordPreviousImplementation(proxy, impl);
|
||
|
||
assertEq(governance.previousImplementations(proxy), impl);
|
||
}
|
||
}
|