Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/reserve-protocol/reserve-index-dtf/llms.txt

Use this file to discover all available pages before exploring further.

Overview

Reserve Folio uses Foundry as its primary testing framework. The test suite includes unit tests, integration tests, and extreme condition tests to ensure protocol security and correctness.

Running Tests

Basic Test Suite

Run all tests except extreme tests:
yarn test
This executes forge test --no-match-test extreme and runs the standard test suite.

Extreme Tests

Extreme tests verify the protocol behavior under edge cases and boundary conditions:
yarn test:extreme
This runs tests with the extreme keyword in their name, testing scenarios like:
  • Maximum token supplies (1e36)
  • Extreme price ranges
  • Boundary basket weights
  • Large-scale rebalancing operations

All Tests

Run the complete test suite including extreme tests:
yarn test:all
Running all tests may take several minutes and requires significant computational resources due to the extreme test cases.

Specific Test Files

Run tests from a specific file:
forge test --match-path test/Folio.t.sol

Specific Test Functions

Run a specific test function:
forge test --match-test testMintAndRedeem

Verbose Output

Get detailed output including console logs:
forge test -vvv
Verbosity levels:
  • -v: Show test results
  • -vv: Show test results and logs for failed tests
  • -vvv: Show test results and logs for all tests
  • -vvvv: Show test results, logs, and traces
  • -vvvvv: Show test results, logs, traces, and setup traces

Test Coverage

Generate Coverage Report

Generate an LCOV coverage report:
yarn coverage
This creates a lcov.info file that can be viewed with coverage tools.

Coverage Summary

View a quick coverage summary in the terminal:
yarn coverage:summary
Example output:
| File                          | % Lines       | % Statements  | % Branches    | % Funcs       |
|-------------------------------|---------------|---------------|---------------|---------------|
| contracts/Folio.sol           | 98.50%        | 98.75%        | 95.00%        | 100.00%       |
| contracts/StakingVault.sol    | 97.25%        | 97.50%        | 93.75%        | 100.00%       |
| Total                         | 97.80%        | 98.00%        | 94.25%        | 99.50%        |

Test Structure

Directory Organization

The test suite is organized as follows:
test/
├── base/
│   ├── BaseTest.sol              # Base test contract with common setup
│   └── BaseExtremeTest.sol       # Base for extreme condition tests
├── utils/
│   ├── MockERC20.sol             # Mock ERC20 token for testing
│   ├── MockBidder.sol            # Mock auction bidder
│   ├── MockRoleRegistry.sol      # Mock role registry
│   └── upgrades/
│       ├── FolioV2.sol           # Mock upgrade version
│       └── FolioDeployerV2.sol   # Mock deployer upgrade
├── Folio.t.sol                   # Core Folio contract tests
├── FolioDeployer.t.sol           # Deployer tests
├── FolioDAOFeeRegistry.t.sol     # Fee registry tests
├── FolioVersionRegistry.t.sol    # Version registry tests
├── StakingVault.t.sol            # Staking vault tests
├── Governance.t.sol              # Governance tests
├── GovernanceDeployer.t.sol      # Governance deployer tests
├── Allowlist.t.sol               # Allowlist functionality tests
└── Extreme.t.sol                 # Extreme condition tests

Base Test Contract

All tests inherit from BaseTest.sol, which provides:
abstract contract BaseTest is Script, Test {
    // Common test addresses
    address auctionLauncher = 0x00000000000000000000000000000000000000cc;
    address dao = 0xDA00000000000000000000000000000000000000;
    address owner = 0xCc00000000000000000000000000000000000000;
    address user1 = 0xfF00000000000000000000000000000000000000;
    address user2 = 0xbb00000000000000000000000000000000000000;
    
    // Test tokens
    IERC20 USDC;
    IERC20 DAI;
    IERC20 MEME;
    
    // Core contracts
    Folio folio;
    FolioDeployer folioDeployer;
    FolioDAOFeeRegistry daoFeeRegistry;
    
    function setUp() public virtual {
        // Setup logic
    }
}

Writing Tests

Test Function Naming

Follow these conventions:
function test_DescriptiveTestName() public {
    // Test succeeds if it doesn't revert
}

function testFail_ExpectedFailure() public {
    // Test succeeds if it reverts
}

function test_RevertWhen_Condition() public {
    // Test expects a revert with specific condition
    vm.expectRevert(ErrorSelector);
    // Action that should revert
}

function test_extreme_BoundaryCondition() public {
    // Extreme test case
}

Example Test

Folio.t.sol
function test_MintAndRedeem() public {
    // Arrange
    uint256 mintAmount = 1000e18;
    deal(address(USDC), user1, mintAmount);
    
    vm.startPrank(user1);
    USDC.approve(address(folio), mintAmount);
    
    // Act
    uint256 sharesBefore = folio.balanceOf(user1);
    folio.mint(mintAmount, user1);
    uint256 sharesAfter = folio.balanceOf(user1);
    
    // Assert
    assertGt(sharesAfter, sharesBefore);
    assertEq(USDC.balanceOf(address(folio)), mintAmount);
    
    // Act - Redeem
    uint256 sharesReceived = sharesAfter - sharesBefore;
    folio.redeem(sharesReceived, user1, user1);
    
    // Assert
    assertEq(folio.balanceOf(user1), sharesBefore);
    vm.stopPrank();
}

Testing Reverts

function test_RevertWhen_UnauthorizedAccess() public {
    vm.prank(user1);
    vm.expectRevert(
        abi.encodeWithSelector(
            IAccessControl.AccessControlUnauthorizedAccount.selector,
            user1,
            folio.REBALANCE_MANAGER()
        )
    );
    folio.startRebalance(/* params */);
}

Using Cheatcodes

Foundry provides powerful testing cheatcodes:
// Manipulate time
vm.warp(block.timestamp + 1 days);

// Set msg.sender
vm.prank(user1);
folio.deposit(amount);

// Set msg.sender for multiple calls
vm.startPrank(user1);
token.approve(address(folio), amount);
folio.deposit(amount);
vm.stopPrank();

// Give tokens to address
deal(address(token), user1, 1000e18);

// Mock external calls
vm.mockCall(
    address(oracle),
    abi.encodeWithSelector(IOracle.getPrice.selector),
    abi.encode(1e27)
);

// Expect events
vm.expectEmit(true, true, true, true);
emit Deposit(user1, amount, shares);

Testing Best Practices

Structure tests with clear sections:
  1. Arrange: Set up test conditions
  2. Act: Execute the function being tested
  3. Assert: Verify the expected outcomes
Each test should verify a single behavior or requirement. This makes tests easier to understand and debug.
Test names should clearly describe what is being tested and under what conditions.Good: test_RevertWhen_MintingAboveSupplyCap Bad: test_Mint2
Always test boundary conditions:
  • Zero values
  • Maximum values
  • Empty arrays
  • Invalid inputs
Each test should be independent and not rely on state from other tests.

Gas Reporting

Generate gas usage reports:
forge test --gas-report
Optionally filter by contract:
forge test --gas-report --match-contract Folio

Debugging Failed Tests

Interactive Debugging

Use Forge’s debugger:
forge test --match-test testName --debug

Trace Execution

Show detailed execution traces:
forge test --match-test testName -vvvvv

Isolate Failures

Run only failed tests:
forge test --failed

Continuous Integration

Tests run automatically on:
  • Every pull request
  • Commits to main branch
  • Before deployments
Ensure all tests pass before submitting a pull request:
yarn test:all && yarn coverage:summary

Next Steps

Deployment

Deploy contracts to testnets and mainnet

Contributing

Learn how to contribute to the protocol

Build docs developers (and LLMs) love