Skip to content

Commit

Permalink
Foundry tests for HeartbeatRequester.sol (#9617)
Browse files Browse the repository at this point in the history
* HeartbeatRequester.t.sol, errors with getAggregatorRequestHeartbeat tests

* HeartbeatRequester.t.sol fixed

* HearbeatRequester.t.sol updated

* Revert pnpm-lock.yaml to match develop branch

* HeartbeatRequester refactored

* HeartBeatRequester.t.sol refactor

* AutomationForwarder comment added

* fix the solc compile path

* update foundry toml

---------

Co-authored-by: FelixFan1992 <fankejin@gmail.com>
  • Loading branch information
JonWong203 and FelixFan1992 committed Jul 14, 2023
1 parent 948c780 commit 665c631
Show file tree
Hide file tree
Showing 11 changed files with 188 additions and 44 deletions.
6 changes: 6 additions & 0 deletions contracts/foundry.toml
Original file line number Diff line number Diff line change
Expand Up @@ -27,4 +27,10 @@ src = 'src/v0.8/vrf'
test = 'src/v0.8/vrf' # skips tests for no VRF foundry tests
solc_version = '0.8.6'

[profile.automation]
optimizer_runs = 10000
src = 'src/v0.8/automation'
test = 'src/v0.8/automation/test'
solc_version = '0.8.6'

# See more config options https://github.com/foundry-rs/foundry/tree/master/config
2 changes: 1 addition & 1 deletion contracts/scripts/native_solc_compile_all
Original file line number Diff line number Diff line change
Expand Up @@ -64,11 +64,11 @@ $SCRIPTPATH/native_solc8_6_compile tests/VerifiableLoadMercuryUpkeep.sol
$SCRIPTPATH/native_solc8_15_compile tests/MercuryUpkeep.sol
$SCRIPTPATH/native_solc8_15_compile dev/automation/2_1/interfaces/FeedLookupCompatibleInterface.sol
$SCRIPTPATH/native_solc8_16_compile tests/AutomationConsumerBenchmark.sol
$SCRIPTPATH/native_solc8_6_compile automation/mocks/MockAggregatorProxy.sol

# Aggregators
$SCRIPTPATH/native_solc8_6_compile interfaces/AggregatorV2V3Interface.sol

$SCRIPTPATH/native_solc8_6_compile mocks/MockAggregatorProxy.sol
$SCRIPTPATH/native_solc8_6_compile Chainlink.sol
$SCRIPTPATH/native_solc8_6_compile ChainlinkClient.sol

Expand Down
22 changes: 22 additions & 0 deletions contracts/src/v0.8/automation/mocks/MockAggregator.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
// SPDX-License-Identifier: MIT
pragma solidity 0.8.6;

import {IOffchainAggregator} from "../HeartbeatRequester.sol";

contract MockAggregator is IOffchainAggregator {
int256 public s_answer;
bool public newRoundCalled;

function setLatestAnswer(int256 answer) public {
s_answer = answer;
}

function latestAnswer() public view returns (int256) {
return s_answer;
}

function requestNewRound() external override returns (uint80) {
newRoundCalled = true;
return 1;
}
}
20 changes: 20 additions & 0 deletions contracts/src/v0.8/automation/mocks/MockAggregatorProxy.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
// SPDX-License-Identifier: MIT
pragma solidity 0.8.6;

import {IAggregatorProxy} from "../HeartbeatRequester.sol";

contract MockAggregatorProxy is IAggregatorProxy {
address internal s_aggregator;

constructor(address _aggregator) {
s_aggregator = _aggregator;
}

function updateAggregator(address _aggregator) external {
s_aggregator = _aggregator;
}

function aggregator() external view override returns (address) {
return s_aggregator;
}
}
14 changes: 14 additions & 0 deletions contracts/src/v0.8/automation/test/BaseTest.t.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
// SPDX-License-Identifier: MIT
pragma solidity 0.8.6;

import "forge-std/Test.sol";

contract BaseTest is Test {
address internal OWNER = 0x00007e64E1fB0C487F25dd6D3601ff6aF8d32e4e;
address internal constant STRANGER = address(999);

function setUp() public virtual {
vm.startPrank(OWNER);
deal(OWNER, 1e20);
}
}
112 changes: 112 additions & 0 deletions contracts/src/v0.8/automation/test/HeartbeatRequester.t.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
// SPDX-License-Identifier: MIT
pragma solidity 0.8.6;

import {HeartbeatRequester, IAggregatorProxy} from "../HeartbeatRequester.sol";
import {MockAggregator} from "../mocks/MockAggregator.sol";
import {MockAggregatorProxy} from "../mocks/MockAggregatorProxy.sol";
import {BaseTest} from "./BaseTest.t.sol";

// from contracts directory,
// forge test --match-path src/v0.8/automation/test/HeartbeatRequester.t.sol

contract HeartbeatRequesterSetUp is BaseTest {
HeartbeatRequester internal heartbeatRequester;
MockAggregator internal aggregator;
IAggregatorProxy internal aggregatorProxy;
MockAggregator internal aggregator2;
IAggregatorProxy internal aggregatorProxy2;

event HeartbeatPermitted(address indexed permittedCaller, address newProxy, address oldProxy);
event HeartbeatRemoved(address indexed permittedCaller, address removedProxy);
error HeartbeatNotPermitted();

function setUp() public override {
BaseTest.setUp();
heartbeatRequester = new HeartbeatRequester();
aggregator = new MockAggregator();
aggregatorProxy = IAggregatorProxy(new MockAggregatorProxy(address(aggregator)));
aggregator2 = new MockAggregator();
aggregatorProxy2 = IAggregatorProxy(new MockAggregatorProxy(address(aggregator2)));
}
}

contract HeartbeatRequester_permitHeartbeat is HeartbeatRequesterSetUp {
function testBasicSuccess() public {
vm.expectEmit();
emit HeartbeatPermitted(STRANGER, address(aggregatorProxy), address(0));
heartbeatRequester.permitHeartbeat(STRANGER, aggregatorProxy);

vm.expectEmit();
emit HeartbeatPermitted(STRANGER, address(aggregatorProxy2), address(aggregatorProxy));
heartbeatRequester.permitHeartbeat(STRANGER, aggregatorProxy2);
}

function testBasicDeployerSuccess() public {
vm.expectEmit();
emit HeartbeatPermitted(OWNER, address(aggregatorProxy), address(0));
heartbeatRequester.permitHeartbeat(OWNER, aggregatorProxy);

vm.expectEmit();
emit HeartbeatPermitted(OWNER, address(aggregatorProxy2), address(aggregatorProxy));
heartbeatRequester.permitHeartbeat(OWNER, aggregatorProxy2);
}

function testOnlyCallableByOwnerReverts() public {
vm.expectRevert(bytes("Only callable by owner"));
changePrank(STRANGER);
heartbeatRequester.permitHeartbeat(STRANGER, aggregatorProxy);
}
}

contract HeartbeatRequester_removeHeartbeat is HeartbeatRequesterSetUp {
function testBasicSuccess() public {
vm.expectEmit();
emit HeartbeatPermitted(STRANGER, address(aggregatorProxy), address(0));
heartbeatRequester.permitHeartbeat(STRANGER, aggregatorProxy);

vm.expectEmit();
emit HeartbeatRemoved(STRANGER, address(aggregatorProxy));
heartbeatRequester.removeHeartbeat(STRANGER);
}

function testRemoveNoPermitsSuccess() public {
vm.expectEmit();
emit HeartbeatRemoved(STRANGER, address(0));
heartbeatRequester.removeHeartbeat(STRANGER);
}

function testOnlyCallableByOwnerReverts() public {
vm.expectRevert(bytes("Only callable by owner"));
changePrank(STRANGER);
heartbeatRequester.removeHeartbeat(address(this));
}
}

contract HeartbeatRequester_getAggregatorRequestHeartbeat is HeartbeatRequesterSetUp {
function testBasicSuccess() public {
vm.expectEmit();
emit HeartbeatPermitted(OWNER, address(aggregatorProxy), address(0));
heartbeatRequester.permitHeartbeat(OWNER, aggregatorProxy);
heartbeatRequester.getAggregatorAndRequestHeartbeat(address(aggregatorProxy));
// getter for newRoundCalled value
bool val = aggregator.newRoundCalled();
assertEq(val, true);
}

function testHeartbeatNotPermittedReverts() public {
bytes32 hashedReason = keccak256(abi.encodePacked("HeartbeatNotPermitted()"));
bytes memory revertMessage = bytes32ToBytes(hashedReason);
vm.expectRevert(revertMessage);
heartbeatRequester.getAggregatorAndRequestHeartbeat(address(aggregatorProxy));
bool val = aggregator.newRoundCalled();
assertFalse(val);
}

function bytes32ToBytes(bytes32 _bytes32) public pure returns (bytes memory) {
bytes memory bytesArray = new bytes(4);
for (uint256 i; i < 4; ++i) {
bytesArray[i] = _bytes32[i];
}
return bytesArray;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,12 @@ contract AutomationForwarderSetUp is BaseTest {
uint256 constant GAS = 1e18;

function setUp() public override {
// BaseTest.setUp() not called since we want calls to iniatiate from default_registry, not from some predefined owner
default_registry = IAutomationRegistryConsumer(new MockKeeperRegistry2_1());
default_target = new UpkeepCounter(10000, 1);
vm.startPrank(address(default_registry));
forwarder = new AutomationForwarder(1, address(default_target), address(default_registry));
// OWNER not necessary?
OWNER = address(default_registry);
}

Expand Down
14 changes: 0 additions & 14 deletions contracts/src/v0.8/mocks/MockAggregator.sol

This file was deleted.

18 changes: 0 additions & 18 deletions contracts/src/v0.8/mocks/MockAggregatorProxy.sol

This file was deleted.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ mercury_exposed_verifier: ../../contracts/solc/v0.8.16/ExposedVerifier.abi ../..
mercury_upkeep_wrapper: ../../contracts/solc/v0.8.15/MercuryUpkeep.abi ../../contracts/solc/v0.8.15/MercuryUpkeep.bin c4627433ed9ebe7f8cf6594f577b4a1826ce6f2209271895a885fd4b3017320e
mercury_verifier: ../../contracts/solc/v0.8.16/Verifier.abi ../../contracts/solc/v0.8.16/Verifier.bin 24b2db0d5db8e0198bb7eae2ecde65f2596df4bcc7b17d604d25300546c1694e
mercury_verifier_proxy: ../../contracts/solc/v0.8.16/VerifierProxy.abi ../../contracts/solc/v0.8.16/VerifierProxy.bin 3b69ffe9c694e8551b5375c02b9e960adc985e2390566740e7fea70c89e436f1
mock_aggregator_proxy: ../../contracts/solc/v0.8.6/MockAggregatorProxy.abi ../../contracts/solc/v0.8.6/MockAggregatorProxy.bin 22ea93fc25a620183fc2a03664c1fbb631566d4667d1e6237846979f938bf29b
mock_aggregator_proxy: ../../contracts/solc/v0.8.6/MockAggregatorProxy.abi ../../contracts/solc/v0.8.6/MockAggregatorProxy.bin b16c108f3dd384c342ddff5e94da7c0a8d39d1be5e3d8f2cf61ecc7f0e50ff42
mock_ethlink_aggregator_wrapper: ../../contracts/solc/v0.6/MockETHLINKAggregator.abi ../../contracts/solc/v0.6/MockETHLINKAggregator.bin 1c52c24f797b8482aa12b8251dcea1c072827bd5b3426b822621261944b99ca0
mock_gas_aggregator_wrapper: ../../contracts/solc/v0.6/MockGASAggregator.abi ../../contracts/solc/v0.6/MockGASAggregator.bin bacbb1ea4dc6beac0db8a13ca5c75e2fd61b903d70feea9b3b1c8b10fe8df4f3
multiwordconsumer_wrapper: ../../contracts/solc/v0.7/MultiWordConsumer.abi ../../contracts/solc/v0.7/MultiWordConsumer.bin 6e68abdf614e3ed0f5066c1b5f9d7c1199f1e7c5c5251fe8a471344a59afc6ba
Expand Down

0 comments on commit 665c631

Please sign in to comment.