// 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); } }