Skip to content

Commit

Permalink
Get MagicSpend test coverage to 100% (#14)
Browse files Browse the repository at this point in the history
* Add OwnerWithdraw test cases

* Add test file for GetHash

* Add test file for IsValidWithdrawalSignature

* Add test for InsufficientBalance revert case

* Rename getHash.t.sol to GetHash.t.sol

* Remove commented lines

* Format

* Added coverage and coveralls to CI

* Remove unecessary filter to lcov
  • Loading branch information
stevieraykatz authored Mar 21, 2024
1 parent 0f214c9 commit 2662df1
Show file tree
Hide file tree
Showing 5 changed files with 139 additions and 6 deletions.
56 changes: 50 additions & 6 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
name: test
name: Forge CI

on:
pull_request:
Expand All @@ -9,11 +9,8 @@ env:
FOUNDRY_PROFILE: ci

jobs:
check:
strategy:
fail-fast: true

name: Foundry project
forge-test:
name: Run Forge Tests and Checks
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
Expand All @@ -40,3 +37,50 @@ jobs:
run: |
forge fmt --check
id: fmt

forge-coverage:
name: Run Coverage Reporting
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
with:
submodules: recursive

- name: Install Foundry
uses: foundry-rs/foundry-toolchain@v1
with:
version: nightly

- name: Install forge dependencies
run: forge install

- name: Install lcov
run: |
sudo apt-get install lcov
id: lcov

- name: Run coverage
run: |
forge coverage --report summary --report lcov
- name: Prune coverage
run: |
lcov --remove ./lcov.info -o ./lcov-filtered.info 'test/*' 'script/*'
- name: Submit coverage to Coveralls
uses: coverallsapp/github-action@master
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
path-to-lcov: ./lcov-filtered.info
flag-name: foundry
parallel: true

finish:
needs: forge-coverage
if: ${{ always() }}
runs-on: ubuntu-latest
steps:
- name: Coveralls Finished
uses: coverallsapp/github-action@v2
with:
parallel-finished: true
23 changes: 23 additions & 0 deletions test/GetHash.t.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.21;

import {MagicSpendTest} from "./MagicSpend.t.sol";
import {MagicSpend} from "../src/MagicSpend.sol";
import {MockERC20} from "solady/test/utils/mocks/MockERC20.sol";
import {SignatureCheckerLib} from "solady/src/utils/SignatureCheckerLib.sol";

contract GetHashTest is MagicSpendTest {
MockERC20 token = new MockERC20("test", "TEST", 18);

function test_returnsValidHash() public {
asset = address(token);
MagicSpend.WithdrawRequest memory request = _getRequest();
bytes32 expectedHash = SignatureCheckerLib.toEthSignedMessageHash(
abi.encode(
address(magic), withdrawer, block.chainid, address(token), request.amount, request.nonce, request.expiry
)
);
bytes32 testHash = magic.getHash(withdrawer, request);
assertEq(testHash, expectedHash);
}
}
24 changes: 24 additions & 0 deletions test/IsValidWithdrawSignature.t.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.21;

import "./MagicSpend.t.sol";
import {MockERC20} from "solady/test/utils/mocks/MockERC20.sol";

contract IsValidWithdrawalSignature is MagicSpendTest {
MockERC20 token = new MockERC20("test", "TEST", 18);

function test_returnsTrueWithValidSignature() public {
asset = address(token);
MagicSpend.WithdrawRequest memory request = _getRequest();
bool success = magic.isValidWithdrawSignature(withdrawer, request);
assert(success);
}

function test_returnsFalseWithInvalidSignature() public {
asset = address(token);
address invalidSender = address(0xdead);
MagicSpend.WithdrawRequest memory request = _getRequest();
bool success = magic.isValidWithdrawSignature(invalidSender, request);
assertFalse(success);
}
}
36 changes: 36 additions & 0 deletions test/OwnerWithdraw.t.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.21;

import {Ownable} from "./MagicSpend.t.sol";
import {MagicSpendTest} from "./Validate.t.sol";
import {MockERC20} from "solady/test/utils/mocks/MockERC20.sol";

contract OwnerWithdrawTest is MagicSpendTest {
MockERC20 token = new MockERC20("test", "TEST", 18);

function test_revertsIfNotOwner() public {
vm.startPrank(withdrawer);
vm.expectRevert(Ownable.Unauthorized.selector);
magic.ownerWithdraw(address(token), withdrawer, 1);
}

function test_transfersERC20Successfully(uint256 amount_) public {
vm.startPrank(owner);
amount = amount_;
token.mint(address(magic), amount);
asset = address(token);
assertEq(token.balanceOf(owner), 0);
magic.ownerWithdraw(asset, owner, amount);
assertEq(token.balanceOf(owner), amount);
}

function test_transfersETHSuccessfully(uint256 amount_) public {
vm.deal(address(magic), amount_);
vm.startPrank(owner);
amount = amount_;
asset = address(0);
assertEq(owner.balance, 0);
magic.ownerWithdraw(asset, owner, amount);
assertEq(owner.balance, amount);
}
}
6 changes: 6 additions & 0 deletions test/ValidatePaymasterUserOp.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,12 @@ contract ValidatePaymasterUserOpTest is PaymasterMagicSpendBaseTest, ValidateTes
magic.validatePaymasterUserOp(_getUserOp(), userOpHash, maxCost);
}

function test_revertsIfWithdrawalExceedsBalance() public {
vm.deal(address(magic), 0);
vm.expectRevert(abi.encodeWithSelector(MagicSpend.InsufficientBalance.selector, amount, 0));
magic.validatePaymasterUserOp(_getUserOp(), userOpHash, maxCost);
}

function test_returnsCorrectly() public {
(bytes memory context, uint256 validationData) =
magic.validatePaymasterUserOp(_getUserOp(), userOpHash, maxCost);
Expand Down

0 comments on commit 2662df1

Please sign in to comment.