From 36d8d28808e818e229b09e8a7afc4d6d7c0351f3 Mon Sep 17 00:00:00 2001 From: Erik Dubovyk Date: Mon, 29 Jul 2024 04:40:13 +0300 Subject: [PATCH] Restructuring tests: added constructor test --- test/concrete/Flow/Flow.construction.t.sol | 45 +++++++++++++++++++ test/util/abstract/FlowMockRealTest.sol | 52 ++++++++++++++++++++++ test/util/lib/LibTestConstants.sol | 19 ++++++++ 3 files changed, 116 insertions(+) create mode 100644 test/concrete/Flow/Flow.construction.t.sol create mode 100644 test/util/abstract/FlowMockRealTest.sol create mode 100644 test/util/lib/LibTestConstants.sol diff --git a/test/concrete/Flow/Flow.construction.t.sol b/test/concrete/Flow/Flow.construction.t.sol new file mode 100644 index 00000000..4da8071d --- /dev/null +++ b/test/concrete/Flow/Flow.construction.t.sol @@ -0,0 +1,45 @@ +// SPDX-License-Identifier: CAL +pragma solidity ^0.8.18; + +import {Test, Vm, console2} from "forge-std/Test.sol"; + +import {FlowMockRealTest} from "test/util/abstract/FlowMockRealTest.sol"; +import {IFlowV5} from "src/interface/unstable/IFlowV5.sol"; +import {EvaluableConfigV3} from "rain.interpreter.interface/interface/IInterpreterCallerV2.sol"; +import {IExpressionDeployerV3} from "rain.interpreter.interface/interface/IExpressionDeployerV3.sol"; +import {STUB_EXPRESSION_BYTECODE} from "test/util/lib/LibTestConstants.sol"; +import {Flow} from "src/concrete/basic/Flow.sol"; + +contract FlowConstructionTest is FlowMockRealTest { + IFlowV5 internal flowImplementation; + + function testInitializeOnTheGoodPath() external { + vm.mockCall( + address(iDeployer), + abi.encodeWithSelector(IExpressionDeployerV3.deployExpression2.selector), + abi.encode(iInterpreter, iStore, address(0), hex"0007") // 1 in, 1 out + ); + + bytes memory bytecode = STUB_EXPRESSION_BYTECODE; + uint256[] memory constants = new uint256[](1); + constants[0] = 2; + + EvaluableConfigV3[] memory flowConfig = new EvaluableConfigV3[](1); + flowConfig[0] = EvaluableConfigV3(iDeployer, bytecode, constants); + + flowImplementation = new Flow(); + + vm.recordLogs(); + iCloneableFactoryV2.clone(address(flowImplementation), abi.encode(flowConfig)); + + Vm.Log[] memory logs = vm.getRecordedLogs(); + bytes32 eventSignature = keccak256("Initialize(address,(address,bytes,uint256[])[])"); + + Vm.Log memory concreteEvent = findEvent(logs, eventSignature); + (address sender, EvaluableConfigV3[] memory config) = + abi.decode(concreteEvent.data, (address, EvaluableConfigV3[])); + + assertEq(sender, address(iCloneableFactoryV2), "wrong sender in Initialize event"); + assertEq(keccak256(abi.encode(flowConfig)), keccak256(abi.encode(config)), "wrong sender in Initialize event"); + } +} diff --git a/test/util/abstract/FlowMockRealTest.sol b/test/util/abstract/FlowMockRealTest.sol new file mode 100644 index 00000000..5ae88a86 --- /dev/null +++ b/test/util/abstract/FlowMockRealTest.sol @@ -0,0 +1,52 @@ +// SPDX-License-Identifier: CAL +pragma solidity ^0.8.18; + +import {Test, Vm, console2} from "forge-std/Test.sol"; +import {REVERTING_MOCK_BYTECODE} from "test/util/lib/LibTestConstants.sol"; +import {IInterpreterStoreV2} from "rain.interpreter.interface/interface/IInterpreterStoreV2.sol"; +import {IInterpreterV2} from "rain.interpreter.interface/interface/IInterpreterV2.sol"; +import {IExpressionDeployerV3} from "rain.interpreter.interface/interface/IExpressionDeployerV3.sol"; +import {IERC1820Registry} from "openzeppelin-contracts/contracts/utils/introspection/IERC1820Registry.sol"; +import {IERC1820_REGISTRY} from "test/util/lib/LibTestConstants.sol"; +import {CloneFactory, ICloneableFactoryV2} from "rain.factory/src/concrete/CloneFactory.sol"; +import {EvaluableConfigV3} from "rain.interpreter.interface/interface/IInterpreterCallerV2.sol"; + +abstract contract FlowMockRealTest is Test { + IInterpreterV2 internal immutable iInterpreter; + IInterpreterStoreV2 internal immutable iStore; + IExpressionDeployerV3 internal immutable iDeployer; + ICloneableFactoryV2 internal immutable iCloneableFactoryV2; + + constructor() { + iInterpreter = IInterpreterV2(address(uint160(uint256(keccak256("interpreter.rain.test"))))); + vm.etch(address(iInterpreter), REVERTING_MOCK_BYTECODE); + + iStore = IInterpreterStoreV2(address(uint160(uint256(keccak256("store.rain.test"))))); + vm.etch(address(iStore), REVERTING_MOCK_BYTECODE); + + iDeployer = IExpressionDeployerV3(address(uint160(uint256(keccak256("deployer.rain.test"))))); + vm.etch(address(iDeployer), REVERTING_MOCK_BYTECODE); + + vm.etch(address(IERC1820_REGISTRY), REVERTING_MOCK_BYTECODE); + vm.mockCall( + address(IERC1820_REGISTRY), + abi.encodeWithSelector(IERC1820Registry.interfaceHash.selector), + abi.encode(bytes32(uint256(5))) + ); + + vm.mockCall( + address(IERC1820_REGISTRY), abi.encodeWithSelector(IERC1820Registry.setInterfaceImplementer.selector), "" + ); + + iCloneableFactoryV2 = new CloneFactory(); + } + + function findEvent(Vm.Log[] memory logs, bytes32 eventSignature) internal pure returns (Vm.Log memory) { + for (uint256 i = 0; i < logs.length; i++) { + if (logs[i].topics[0] == eventSignature) { + return logs[i]; + } + } + revert("Event not found!"); + } +} diff --git a/test/util/lib/LibTestConstants.sol b/test/util/lib/LibTestConstants.sol new file mode 100644 index 00000000..0f6f428a --- /dev/null +++ b/test/util/lib/LibTestConstants.sol @@ -0,0 +1,19 @@ +// SPDX-License-Identifier: CAL +pragma solidity ^0.8.18; + +import "openzeppelin-contracts/contracts/utils/introspection/IERC1820Registry.sol"; + +/// @dev https://eips.ethereum.org/EIPS/eip-1820#single-use-registry-deployment-account +IERC1820Registry constant IERC1820_REGISTRY = IERC1820Registry(0x1820a4B7618BdE71Dce8cdc73aAB6C95905faD24); + +/// @dev Mocks need to be etched with some bytecode or they cannot even be +/// called. This is because Solidity first checks the bytecode size before +/// calling, so it never even gets to the point that mocking logic can intercept +/// the call. We want all non-mocked calls to revert, so all mocks should be +/// etched with a revert opcode. +bytes constant REVERTING_MOCK_BYTECODE = hex"FD"; + +/// @dev Stub expression bytecode used for testing purposes. +/// This is a simple bytecode stub that can be used as a placeholder for +/// expressions with mock initialization in tests. The bytecode is arbitrary +bytes constant STUB_EXPRESSION_BYTECODE = hex"010000";