Skip to content

Commit

Permalink
test: add CommonUtils
Browse files Browse the repository at this point in the history
test: add CommonConstants
test: rename Baae file
test: improve createUser function
  • Loading branch information
andreivladbrg committed Feb 5, 2025
1 parent b79fd37 commit 1125f3b
Show file tree
Hide file tree
Showing 5 changed files with 105 additions and 44 deletions.
75 changes: 41 additions & 34 deletions tests/Base.t.sol → tests/Base.sol
Original file line number Diff line number Diff line change
Expand Up @@ -4,40 +4,77 @@ pragma solidity >=0.8.22 <0.9.0;
import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import { Test } from "forge-std/src/Test.sol";

import { CommonConstants } from "./utils/Constants.sol";
import { CommonUtils } from "./utils/Utils.sol";
import { ERC20MissingReturn } from "./mocks/erc20/ERC20MissingReturn.sol";
import { ERC20Mock } from "./mocks/erc20/ERC20Mock.sol";
import { ContractWithoutReceive, ContractWithReceive } from "./mocks/Receive.sol";

contract CommonBase is Test {
contract CommonBase is CommonConstants, Test, CommonUtils {
/*//////////////////////////////////////////////////////////////////////////
TEST CONTRACTS
//////////////////////////////////////////////////////////////////////////*/

ContractWithoutReceive internal contractWithoutReceive;
ContractWithReceive internal contractWithReceive;
ERC20Mock internal dai;
address[] internal tokens;
ERC20Mock internal usdc;
ERC20MissingReturn internal usdt;

function setUp() public virtual {
contractWithoutReceive = new ContractWithoutReceive();
contractWithReceive = new ContractWithReceive();

// Deploy the tokens.
dai = new ERC20Mock("Dai stablecoin", "DAI", 18);
usdc = new ERC20Mock("USD Coin", "USDC", 6);
usdt = new ERC20MissingReturn("Tether", "USDT", 6);

// Push in the tokens array.
tokens.push(address(dai));
tokens.push(address(usdc));
tokens.push(address(usdt));

// Label the tokens.
vm.label({ account: address(contractWithoutReceive), newLabel: "Contract without Receive" });
vm.label({ account: address(contractWithReceive), newLabel: "Contract with Receive" });
vm.label(address(dai), "DAI");
vm.label(address(usdc), "USDC");
vm.label(address(usdt), "USDT");
}

/// @dev Creates a new ERC-20 token with `decimals`.
function createToken(uint8 decimals) internal returns (ERC20Mock) {
return createToken("", "", decimals);
}

/// @dev Creates a new ERC-20 token with `name`, `symbol` and `decimals`.
function createToken(string memory name, string memory symbol, uint8 decimals) internal returns (ERC20Mock) {
return new ERC20Mock(name, symbol, decimals);
}

/// @dev Generates a user, labels its address and funds it with test tokens.
function createUser(string memory name) internal returns (address payable) {
function createUser(string memory name, address[] memory spenders) internal returns (address payable) {
address payable user = payable(makeAddr(name));
vm.deal({ account: user, newBalance: 100 ether });
deal({ token: address(dai), to: user, give: 1e28 });
deal({ token: address(usdt), to: user, give: 1e28 });
deal({ token: address(usdc), to: user, give: 1e16 });

for (uint256 i = 0; i < spenders.length; ++i) {
for (uint256 j = 0; j < tokens.length; ++j) {
approveContract(tokens[j], spenders[i], user);
}
}

return user;
}

/// @dev Approve `spender` to spend tokens from `from`.
function approveContract(IERC20 token_, address from, address spender) internal {
function approveContract(address token_, address from, address spender) internal {
resetPrank({ msgSender: from });
(bool success,) = address(token_).call(abi.encodeCall(IERC20.approve, (spender, UINT256_MAX)));
(bool success,) = token_.call(abi.encodeCall(IERC20.approve, (spender, UINT256_MAX)));
success;
}

Expand Down Expand Up @@ -91,34 +128,4 @@ contract CommonBase is Test {
data: abi.encodeCall(IERC20.transferFrom, (from, to, value))
});
}

/*//////////////////////////////////////////////////////////////////////////
UTILS
//////////////////////////////////////////////////////////////////////////*/

/// @dev Bounds a `uint128` number.
function boundUint128(uint128 x, uint128 min, uint128 max) internal pure returns (uint128) {
return uint128(_bound(x, min, max));
}

/// @dev Bounds a `uint40` number.
function boundUint40(uint40 x, uint40 min, uint40 max) internal pure returns (uint40) {
return uint40(_bound(x, min, max));
}

/// @dev Bounds a `uint8` number.
function boundUint8(uint8 x, uint8 min, uint8 max) internal pure returns (uint8) {
return uint8(_bound(x, min, max));
}

/// @dev Retrieves the current block timestamp as an `uint40`.
function getBlockTimestamp() internal view returns (uint40) {
return uint40(block.timestamp);
}

/// @dev Stops the active prank and sets a new one.
function resetPrank(address msgSender) internal {
vm.stopPrank();
vm.startPrank(msgSender);
}
}
16 changes: 7 additions & 9 deletions tests/unit/Unit.t.sol
Original file line number Diff line number Diff line change
@@ -1,23 +1,21 @@
// SPDX-License-Identifier: UNLICENSED
pragma solidity >=0.8.22 <0.9.0;

import { CommonBase } from "../Base.t.sol";
import { ContractWithoutReceive, ContractWithReceive } from "../mocks/Receive.sol";
import { CommonBase } from "../Base.sol";

abstract contract Unit_Test is CommonBase {
address internal admin;
address internal alice;
address internal eve;

ContractWithoutReceive internal contractWithoutReceive;
ContractWithReceive internal contractWithReceive;

function setUp() public virtual override {
CommonBase.setUp();

admin = createUser("admin");
eve = createUser("eve");
contractWithoutReceive = new ContractWithoutReceive();
contractWithReceive = new ContractWithReceive();
address[] memory noSpenders = new address[](0);

admin = createUser("admin", noSpenders);
alice = createUser("alice", noSpenders);
eve = createUser("eve", noSpenders);

resetPrank(admin);
}
Expand Down
1 change: 0 additions & 1 deletion tests/unit/concrete/transfer-admin/transferAdmin.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,6 @@ contract TransferAdmin_Unit_Concrete_Test is Unit_Test {
}

function test_WhenNewAdminNotZeroAddress() external whenCallerAdmin whenNewAdminNotSameAsCurrentAdmin {
address alice = createUser("alice");
// It should emit a {TransferAdmin} event.
vm.expectEmit({ emitter: address(adminableMock) });
emit IAdminable.TransferAdmin({ oldAdmin: admin, newAdmin: alice });
Expand Down
12 changes: 12 additions & 0 deletions tests/utils/Constants.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity >=0.8.22;

abstract contract CommonConstants {
uint256 internal constant FEE = 0.001e18;
uint40 internal constant FEB_1_2025 = 1_732_073_600;
uint128 internal constant MAX_UINT128 = type(uint128).max;
uint256 internal constant MAX_UINT256 = type(uint256).max;
uint40 internal constant MAX_UINT40 = type(uint40).max;
uint64 internal constant MAX_UINT64 = type(uint64).max;
uint40 internal constant MAX_UNIX_TIMESTAMP = 2_147_483_647; // 2^31 - 1
}
45 changes: 45 additions & 0 deletions tests/utils/Utils.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity >=0.8.22;

import { Strings } from "@openzeppelin/contracts/utils/Strings.sol";
import { CommonBase as StdBase } from "forge-std/src/Base.sol";
import { StdUtils } from "forge-std/src/StdUtils.sol";

abstract contract CommonUtils is StdBase, StdUtils {
/// @dev Bounds a `uint128` number.
function boundUint128(uint128 x, uint128 min, uint128 max) internal pure returns (uint128) {
return uint128(_bound(x, min, max));
}

/// @dev Bounds a `uint40` number.
function boundUint40(uint40 x, uint40 min, uint40 max) internal pure returns (uint40) {
return uint40(_bound(x, min, max));
}

/// @dev Bounds a `uint64` number.
function boundUint64(uint64 x, uint64 min, uint64 max) internal pure returns (uint64) {
return uint64(_bound(x, min, max));
}

/// @dev Bounds a `uint8` number.
function boundUint8(uint8 x, uint8 min, uint8 max) internal pure returns (uint8) {
return uint8(_bound(x, min, max));
}

/// @dev Retrieves the current block timestamp as an `uint40`.
function getBlockTimestamp() internal view returns (uint40) {
return uint40(block.timestamp);
}

/// @dev Checks if the Foundry profile is "test-optimized".
function isTestOptimizedProfile() internal view returns (bool) {
string memory profile = vm.envOr({ name: "FOUNDRY_PROFILE", defaultValue: string("default") });
return Strings.equal(profile, "test-optimized");
}

/// @dev Stops the active prank and sets a new one.
function resetPrank(address msgSender) internal {
vm.stopPrank();
vm.startPrank(msgSender);
}
}

0 comments on commit 1125f3b

Please sign in to comment.