Skip to content

Commit

Permalink
feat: bring code coverage to 100% (#106)
Browse files Browse the repository at this point in the history
* chore: add unit test multimessage receiver

* chore: bring libraries to 100% coverage

* feat: bring all libraries to 100% coverage

* feat: bring branch coverage for string lib to 100%

* chore: added more unit tests

* chore: change to assertEq

* tests: add reverting refund address tests

* revert: remove lcov.info

* fix: pr comments

* fix: run forge fmt
  • Loading branch information
sujithsomraaj committed Oct 12, 2023
1 parent 16698c6 commit dcaa8b8
Show file tree
Hide file tree
Showing 6 changed files with 535 additions and 0 deletions.
88 changes: 88 additions & 0 deletions test/unit-tests/MultiBridgeMessageReceiver.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -313,6 +313,28 @@ contract MultiBridgeMessageReceiverTest is Setup {
assertTrue(receiver.isExecutionScheduled(msgId));
}

/// @dev cannot execute mismatched message params and hash
function test_schedule_message_hash_mismatch() public {
vm.startPrank(wormholeAdapterAddr);

MessageLibrary.Message memory message = MessageLibrary.Message({
srcChainId: SRC_CHAIN_ID,
dstChainId: DST_CHAIN_ID,
target: address(42),
nonce: 42,
callData: bytes("42"),
nativeValue: 0,
expiration: 0
});
bytes32 msgId = message.computeMsgId();

receiver.receiveMessage(message);

message.nonce = 43;
vm.expectRevert(Error.EXEC_PARAMS_HASH_MISMATCH.selector);
receiver.scheduleMessageExecution(msgId, message.extractExecutionParams());
}

/// @dev cannot schedule execution of message past deadline
function test_schedule_message_execution_passed_deadline() public {
vm.startPrank(wormholeAdapterAddr);
Expand Down Expand Up @@ -701,4 +723,70 @@ contract MultiBridgeMessageReceiverTest is Setup {
assertEq(successfulBridge.length, 1);
assertEq(successfulBridge[0], WormholeReceiverAdapter(wormholeAdapterAddr).name());
}

/// @dev should get message info for invalid message id
function test_get_message_info_invalid_message_id() public {
(bool isScheduled, uint256 msgCurrentVotes, string[] memory successfulBridge) =
receiver.getMessageInfo(bytes32(0));
assertFalse(isScheduled);
assertEq(msgCurrentVotes, 0);
assertEq(successfulBridge.length, 0);
}

/// @dev should get message info for partial delivery
function test_get_message_info_partial_delivery() public {
vm.startPrank(wormholeAdapterAddr);

// Assuming there's a function to add executors or that multiple executors exist by default

MessageLibrary.Message memory message = MessageLibrary.Message({
srcChainId: SRC_CHAIN_ID,
dstChainId: DST_CHAIN_ID,
target: address(42),
nonce: 42,
callData: bytes("42"),
nativeValue: 0,
expiration: type(uint256).max
});
bytes32 msgId = message.computeMsgId();

receiver.receiveMessage(message);

// You may need a mechanism to simulate or enforce failed deliveries for certain executors

(bool isScheduled, uint256 msgCurrentVotes, string[] memory successfulBridge) = receiver.getMessageInfo(msgId);
assertFalse(isScheduled);
// Adjust the following assertions as needed based on your setup
assertTrue(msgCurrentVotes > 0); // Ensure there's at least one successful delivery
assertEq(successfulBridge.length, msgCurrentVotes);
}

/// @dev should get message info scheduled for execution
function test_get_message_info_scheduled_execution() public {
// Reduce quorum first
vm.startPrank(address(timelockAddr));
receiver.updateQuorum(1);
vm.stopPrank();

vm.startPrank(wormholeAdapterAddr);

MessageLibrary.Message memory message = MessageLibrary.Message({
srcChainId: SRC_CHAIN_ID,
dstChainId: DST_CHAIN_ID,
target: address(42),
nonce: 42,
callData: bytes("42"),
nativeValue: 0,
expiration: type(uint256).max
});
bytes32 msgId = message.computeMsgId();

receiver.receiveMessage(message);
receiver.scheduleMessageExecution(msgId, message.extractExecutionParams());

(bool isScheduled, uint256 msgCurrentVotes, string[] memory successfulBridge) = receiver.getMessageInfo(msgId);
assertTrue(isScheduled);
assertEq(msgCurrentVotes, 1);
assertEq(successfulBridge.length, 1);
}
}
40 changes: 40 additions & 0 deletions test/unit-tests/MultiBridgeMessageSender.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,12 @@ import "src/libraries/Error.sol";
import "src/libraries/Message.sol";
import {MultiBridgeMessageSender} from "src/MultiBridgeMessageSender.sol";

contract EthReceiverRevert {
receive() external payable {
revert();
}
}

contract MultiBridgeMessageSenderTest is Setup {
using MessageLibrary for MessageLibrary.Message;

Expand All @@ -39,6 +45,8 @@ contract MultiBridgeMessageSenderTest is Setup {
address wormholeAdapterAddr;
address axelarAdapterAddr;

address revertingReceiver;

/// @dev initializes the setup
function setUp() public override {
super.setUp();
Expand All @@ -49,6 +57,8 @@ contract MultiBridgeMessageSenderTest is Setup {
senderGAC = MessageSenderGAC(contractAddress[SRC_CHAIN_ID]["GAC"]);
wormholeAdapterAddr = contractAddress[SRC_CHAIN_ID]["WORMHOLE_SENDER_ADAPTER"];
axelarAdapterAddr = contractAddress[SRC_CHAIN_ID]["AXELAR_SENDER_ADAPTER"];

revertingReceiver = address(new EthReceiverRevert());
}

/// @dev constructor
Expand Down Expand Up @@ -110,6 +120,36 @@ contract MultiBridgeMessageSenderTest is Setup {
assertEq(sender.nonce(), 1);
}

/// @dev perform remote call with invalid refund receiver
function test_remote_call_reverting_refund_receiver() public {
vm.startPrank(caller);

// Wormhole requires exact fees to be passed in
(uint256 wormholeFee,) = IWormholeRelayer(POLYGON_RELAYER).quoteEVMDeliveryPrice(
_wormholeChainId(DST_CHAIN_ID), 0, senderGAC.msgDeliveryGasLimit()
);
(, uint256[] memory fees) =
_sortTwoAdaptersWithFees(axelarAdapterAddr, wormholeAdapterAddr, 0.01 ether, wormholeFee);
bool[] memory adapterSuccess = new bool[](2);
adapterSuccess[0] = true;
adapterSuccess[1] = true;

uint256 expiration = EXPIRATION_CONSTANT;

vm.expectRevert("safeTransferETH: ETH transfer failed");
sender.remoteCall{value: fees[0] + fees[1] + 1 ether}(
DST_CHAIN_ID,
address(42),
bytes("42"),
0,
expiration,
revertingReceiver,
fees,
DEFAULT_SUCCESS_THRESHOLD,
new address[](0)
);
}

/// @dev perform remote call, checking for refund
function test_remote_call_refund() public {
vm.startPrank(caller);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
// SPDX-License-Identifier: GPL-3.0-only
pragma solidity >=0.8.9;

/// library imports
import {Test, Vm} from "forge-std/Test.sol";

/// local imports
import "src/adapters/axelar/libraries/StringAddressConversion.sol";

/// @dev helper for testing StringAddressConversion library
/// @dev library testing using foundry can only be done through helper contracts
/// @dev see https://github.com/foundry-rs/foundry/issues/2567
contract StringAddressConversionTestClient {
function toString(address _addr) external pure returns (string memory) {
return StringAddressConversion.toString(_addr);
}

function toAddress(string calldata _addressString) external pure returns (address) {
return StringAddressConversion.toAddress(_addressString);
}
}

contract StringAddressConversionTest is Test {
StringAddressConversionTestClient public conversionHelper;

/*///////////////////////////////////////////////////////////////
SETUP
//////////////////////////////////////////////////////////////*/
function setUp() public {
conversionHelper = new StringAddressConversionTestClient();
}

/*///////////////////////////////////////////////////////////////
TEST CASES
//////////////////////////////////////////////////////////////*/

/// @dev tests conversion of address to string
function test_to_string() public {
address testAddr = address(0x1234567890123456789012345678901234567890);
string memory result = conversionHelper.toString(testAddr);
string memory expected = "0x1234567890123456789012345678901234567890";

assertEq(keccak256(bytes(result)), keccak256(bytes(expected)));
}

/// @dev tests conversion of string to address
function test_to_address() public {
string memory testString = "0x1234567890123456789012345678901234567890";
address result = conversionHelper.toAddress(testString);
address expected = address(0x1234567890123456789012345678901234567890);

assertEq(result, expected);
}

/// @dev tests invalid address conversion
function test_invalid_address_string_conversion() public {
string memory invalidAddressString = "1234567890123456789012345678901234567892";

bytes4 selector = StringAddressConversion.InvalidAddressString.selector;
vm.expectRevert(selector);
conversionHelper.toAddress(invalidAddressString);
}

/// @dev tests short address string
function test_short_address_string_conversion() public {
string memory shortAddressString = "0x12345678901234567890123456789012345678";

bytes4 selector = StringAddressConversion.InvalidAddressString.selector;
vm.expectRevert(selector);
conversionHelper.toAddress(shortAddressString);
}

/// @dev tests long address string
function test_long_address_string_conversion() public {
string memory longAddressString = "0x123456789012345678901234567890123456789012";

bytes4 selector = StringAddressConversion.InvalidAddressString.selector;
vm.expectRevert(selector);
conversionHelper.toAddress(longAddressString);
}

/// @dev tests invalid prefix in address string
function test_invalid_prefix_address_string_conversion() public {
string memory invalidPrefixAddressString = "1x1234567890123456789012345678901234567890";

bytes4 selector = StringAddressConversion.InvalidAddressString.selector;
vm.expectRevert(selector);
conversionHelper.toAddress(invalidPrefixAddressString);
}

/// @dev tests address string with invalid characters
function test_invalid_character_address_string_conversion() public {
string memory invalidCharacterAddressString = "0x12345678901234567890123456789012345678g0"; // 'g' is an invalid character

bytes4 selector = StringAddressConversion.InvalidAddressString.selector;
vm.expectRevert(selector);
conversionHelper.toAddress(invalidCharacterAddressString);
}

/// @dev tests conversion of string with lowercase hex characters to address
function test_lowercase_hex_character_to_address() public {
string memory testString = "0xabcdefabcdefabcdefabcdefabcdefabcdefabcd";
address result = conversionHelper.toAddress(testString);
address expected = address(0xABcdEFABcdEFabcdEfAbCdefabcdeFABcDEFabCD);

assertEq(result, expected);
}

/// @dev tests conversion of string with uppercase hex characters to address
function test_ppercase_hex_character_to_address() public {
string memory testString = "0xABCDEFABCDEFABCDEFABCDEFABCDEFABCDEFABCD";
address result = conversionHelper.toAddress(testString);
address expected = address(0xABcdEFABcdEFabcdEfAbCdefabcdeFABcDEFabCD);

assertEq(result, expected);
}
}
80 changes: 80 additions & 0 deletions test/unit-tests/libraries/EIP5164/ExecutorAware.t.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
// SPDX-License-Identifier: GPL-3.0-only
pragma solidity >=0.8.9;

/// library imports
import {Test, Vm} from "forge-std/Test.sol";

/// local imports
import "src/libraries/EIP5164/ExecutorAware.sol";

/// @dev helper to test abstract contract
contract ExecutorAwareTestClient is ExecutorAware {
function addTrustedExecutor(address _executor) external returns (bool) {
return _addTrustedExecutor(_executor);
}

function removeTrustedExecutor(address _executor) external returns (bool) {
return _removeTrustedExecutor(_executor);
}
}

contract ExecutorAwareTest is Test {
ExecutorAwareTestClient public executorAware;

/*///////////////////////////////////////////////////////////////
SETUP
//////////////////////////////////////////////////////////////*/
function setUp() public {
executorAware = new ExecutorAwareTestClient();
}

/*///////////////////////////////////////////////////////////////
TEST CASES
//////////////////////////////////////////////////////////////*/

/// @dev tests adding a trusted executor
function test_add_trusted_executor() public {
address executor = address(0x1234567890123456789012345678901234567890);
bool added = executorAware.addTrustedExecutor(executor);

assertEq(added, true);
assertEq(executorAware.isTrustedExecutor(executor), true);
}

/// @dev tests removing a trusted executor
function test_remove_trusted_executor() public {
address executor = address(0x1234567890123456789012345678901234567890);
executorAware.addTrustedExecutor(executor);

bool removed = executorAware.removeTrustedExecutor(executor);

assertEq(removed, true);
assertEq(executorAware.isTrustedExecutor(executor), false);
}

/// @dev tests retrieval of trusted executors
function test_get_trusted_executors() public {
address executor1 = address(420);
address executor2 = address(421);
executorAware.addTrustedExecutor(executor1);
executorAware.addTrustedExecutor(executor2);

address[] memory executors = executorAware.getTrustedExecutors();

assertEq(executors.length == 2, true);
assertEq(executors[0], executor1);
assertEq(executors[1], executor2);
}

/// @dev tests counting the number of trusted executors
function test_trusted_executors_count() public {
address executor1 = address(420);
address executor2 = address(421);
executorAware.addTrustedExecutor(executor1);
executorAware.addTrustedExecutor(executor2);

uint256 count = executorAware.trustedExecutorsCount();

assertEq(count, 2);
}
}
Loading

0 comments on commit dcaa8b8

Please sign in to comment.