250 lines
6.8 KiB
Solidity
250 lines
6.8 KiB
Solidity
// SPDX-License-Identifier: MIT
|
|
pragma solidity 0.8.19;
|
|
|
|
import {Test} from "forge-std/Test.sol";
|
|
import {DeployRaffle} from "script/DeployRaffle.s.sol";
|
|
import {Raffle} from "src/Raffle.sol";
|
|
import {CodeConstants, HelperConfig} from "script/HelperConfig.s.sol";
|
|
import {Vm} from "forge-std/Vm.sol";
|
|
import {VRFCoordinatorV2_5Mock} from "@chainlink/contracts/src/v0.8/vrf/mocks/VRFCoordinatorV2_5Mock.sol";
|
|
|
|
contract RaffleTest is CodeConstants, Test {
|
|
Raffle public raffle;
|
|
HelperConfig public helperConfig;
|
|
|
|
uint256 entranceFee;
|
|
uint256 interval;
|
|
address vrfCoordinator;
|
|
bytes32 gasLane;
|
|
uint32 callbackGasLimit;
|
|
uint256 subscriptionId;
|
|
|
|
address public PLAYER = makeAddr("player");
|
|
uint256 public constant STARTING_PLAYER_BALANCE = 10 ether;
|
|
|
|
event RaffleEntered(address indexed player);
|
|
|
|
function setUp() external {
|
|
DeployRaffle deployer = new DeployRaffle();
|
|
(raffle, helperConfig) = deployer.deployContract();
|
|
HelperConfig.NetworkConfig memory config = helperConfig.getConfig();
|
|
entranceFee = config.entranceFee;
|
|
interval = config.interval;
|
|
vrfCoordinator = config.vrfCoordinator;
|
|
gasLane = config.gasLane;
|
|
callbackGasLimit = config.callbackGasLimit;
|
|
subscriptionId = config.subscriptionId;
|
|
|
|
vm.deal(PLAYER, STARTING_PLAYER_BALANCE);
|
|
}
|
|
|
|
function testRaffleInitializedInOpenState() public view {
|
|
assert(raffle.getRaffleState() == Raffle.RaffleState.OPEN);
|
|
}
|
|
|
|
function testRaffleRevertsWhenYouDontPayEnough() public {
|
|
// Arrange
|
|
vm.prank(PLAYER);
|
|
// Act / Asset
|
|
vm.expectRevert(Raffle.Raffle__SendMoreToEnterRaffle.selector);
|
|
// Assert
|
|
raffle.enterRaffle();
|
|
}
|
|
|
|
function testRaffleRecordsPlayersWhenTheyEnter() public {
|
|
// Arrange
|
|
vm.prank(PLAYER);
|
|
// Act
|
|
raffle.enterRaffle{value: entranceFee}();
|
|
// Assert
|
|
address playerRecorded = raffle.getPlayer(0);
|
|
assert(playerRecorded == PLAYER);
|
|
}
|
|
|
|
function testEnteringRaffleEmitsEvent() public {
|
|
// Arrange
|
|
vm.prank(PLAYER);
|
|
// Act
|
|
vm.expectEmit(true, false, false, false, address(raffle));
|
|
emit RaffleEntered(PLAYER);
|
|
// Assert
|
|
raffle.enterRaffle{value: entranceFee}();
|
|
}
|
|
|
|
function testDontAllowPlayersToEnterWhileRaffleIsCalculating() public {
|
|
// Arrange
|
|
vm.prank(PLAYER);
|
|
raffle.enterRaffle{value: entranceFee}();
|
|
vm.warp(block.timestamp + interval + 1);
|
|
vm.roll(block.number + 1);
|
|
raffle.performUpkeep("");
|
|
|
|
// Act
|
|
vm.expectRevert(Raffle.Raffle__RaffleNotOpen.selector);
|
|
vm.prank(PLAYER);
|
|
raffle.enterRaffle{value: entranceFee}();
|
|
|
|
// Assert
|
|
}
|
|
|
|
// Check Upkeep
|
|
function testCheckUpkeepReturnsFalseIfItHasNoBalance() public {
|
|
// Arrange
|
|
vm.warp(block.timestamp + interval + 1);
|
|
vm.roll(block.number + 1);
|
|
|
|
// Act
|
|
(bool upkeepNeeded, ) = raffle.checkUpkeep("");
|
|
|
|
// Assert
|
|
assert(!upkeepNeeded);
|
|
}
|
|
|
|
function testCheckUpkeepReturnsFalseIfRaffleIsntOpen() public {
|
|
// Arrange
|
|
vm.prank(PLAYER);
|
|
raffle.enterRaffle{value: entranceFee}();
|
|
vm.warp(block.timestamp + interval + 1);
|
|
vm.roll(block.number + 1);
|
|
raffle.performUpkeep("");
|
|
|
|
// Act
|
|
(bool upkeepNeeded, ) = raffle.checkUpkeep("");
|
|
|
|
// Assert
|
|
assert(!upkeepNeeded);
|
|
}
|
|
|
|
function testCheckUpkeepReturnsFalseIfEnoughTimeHasPassed() public {
|
|
// Arrange
|
|
vm.prank(PLAYER);
|
|
raffle.enterRaffle{value: entranceFee}();
|
|
vm.warp(block.timestamp + interval + 1);
|
|
vm.roll(block.number + 1);
|
|
raffle.performUpkeep("");
|
|
|
|
vm.warp(block.timestamp + interval + 1);
|
|
vm.roll(block.number + 1);
|
|
|
|
// Act
|
|
(bool upkeepNeeded, ) = raffle.checkUpkeep("");
|
|
|
|
// Assert
|
|
assert(!upkeepNeeded);
|
|
}
|
|
|
|
function testCheckUpkeepReturnsTrueWhenParametersAreGood() public {
|
|
// Arrange
|
|
vm.prank(PLAYER);
|
|
raffle.enterRaffle{value: entranceFee}();
|
|
vm.warp(block.timestamp + interval + 1);
|
|
vm.roll(block.number + 1);
|
|
|
|
// Act
|
|
(bool upkeepNeeded, ) = raffle.checkUpkeep("");
|
|
|
|
// Assert
|
|
assert(upkeepNeeded);
|
|
}
|
|
|
|
// Perform Upkeep
|
|
function testPerformUpkeepCanOnlyRunIfCheckUpkeepIsTrue() public {
|
|
// Arrange
|
|
vm.prank(PLAYER);
|
|
raffle.enterRaffle{value: entranceFee}();
|
|
vm.warp(block.timestamp + interval + 1);
|
|
vm.roll(block.number + 1);
|
|
|
|
// Act / Assert
|
|
raffle.performUpkeep("");
|
|
}
|
|
|
|
function testPerformUpkeepRevertsIfCheckUpkeepIsFalse() public {
|
|
// Arrange
|
|
uint256 currentBalance = 0;
|
|
uint256 numPlayers = 0;
|
|
Raffle.RaffleState rState = raffle.getRaffleState();
|
|
|
|
vm.prank(PLAYER);
|
|
raffle.enterRaffle{value: entranceFee}();
|
|
currentBalance = currentBalance + entranceFee;
|
|
numPlayers = 1;
|
|
|
|
// Act / Assert
|
|
vm.expectRevert(
|
|
abi.encodeWithSelector(Raffle.Raffle__UpkeepNotNeeded.selector, currentBalance, numPlayers, rState)
|
|
);
|
|
raffle.performUpkeep("");
|
|
}
|
|
|
|
modifier raffleEntered() {
|
|
// Arrange
|
|
vm.prank(PLAYER);
|
|
raffle.enterRaffle{value: entranceFee}();
|
|
vm.warp(block.timestamp + interval + 1);
|
|
vm.roll(block.number + 1);
|
|
_;
|
|
}
|
|
|
|
function testPerformUpkeepUpdatesRaffleStateAndEmitsRequested() public raffleEntered {
|
|
// Act
|
|
vm.recordLogs();
|
|
raffle.performUpkeep("");
|
|
Vm.Log[] memory entries = vm.getRecordedLogs();
|
|
bytes32 requestId = entries[1].topics[1];
|
|
|
|
// Assert
|
|
Raffle.RaffleState raffleState = raffle.getRaffleState();
|
|
assert(uint256(requestId) > 0);
|
|
assert(uint256(raffleState) == 1);
|
|
}
|
|
|
|
// Fulfill Random Words
|
|
modifier skipFork() {
|
|
if (block.chainid != LOCAL_CHAIN_ID) {
|
|
return;
|
|
}
|
|
_;
|
|
}
|
|
|
|
function testFulfillRandomWordsCanOnlyBeCalledAfterPerformUpkeep(uint256 randomRequestId) public raffleEntered skipFork {
|
|
// Arrange / Assert
|
|
vm.expectRevert(VRFCoordinatorV2_5Mock.InvalidRequest.selector);
|
|
VRFCoordinatorV2_5Mock(vrfCoordinator).fulfillRandomWords(randomRequestId, address(raffle));
|
|
}
|
|
|
|
function testFulfillRandomWordsPicksAWinnerResetsAndSendsMoney() public raffleEntered skipFork {
|
|
// Arrange
|
|
uint256 additionalEntrants = 3;
|
|
uint256 startingIndex = 1;
|
|
address expectedWinner = address(1);
|
|
|
|
for(uint256 i = startingIndex; i < startingIndex + additionalEntrants; i++) {
|
|
address newPlayer = address(uint160(i)); // address(1)
|
|
hoax(newPlayer, 1 ether);
|
|
raffle.enterRaffle{value: entranceFee}();
|
|
}
|
|
uint256 startingTimeStamp = raffle.getLastTimeStamp();
|
|
uint256 winnerStartingBalance = expectedWinner.balance;
|
|
|
|
// Act
|
|
vm.recordLogs();
|
|
raffle.performUpkeep("");
|
|
Vm.Log[] memory entries = vm.getRecordedLogs();
|
|
bytes32 requestId = entries[1].topics[1];
|
|
VRFCoordinatorV2_5Mock(vrfCoordinator).fulfillRandomWords(uint256(requestId), address(raffle));
|
|
|
|
// Assert
|
|
address recentWinner = raffle.getRecentWinner();
|
|
Raffle.RaffleState raffleState = raffle.getRaffleState();
|
|
uint256 winnerBalance = recentWinner.balance;
|
|
uint256 endingTimeStamp = raffle.getLastTimeStamp();
|
|
uint256 prize = entranceFee * (additionalEntrants + 1);
|
|
|
|
assert(recentWinner == expectedWinner);
|
|
assert(uint256(raffleState) == 0);
|
|
assert(winnerBalance == winnerStartingBalance + prize);
|
|
assert(endingTimeStamp > startingTimeStamp);
|
|
}
|
|
}
|