From 6f8a25f4a0de05f661a05237e7b42dad4ad6854d Mon Sep 17 00:00:00 2001 From: Andrew Date: Sat, 6 Mar 2021 19:21:10 -0500 Subject: [PATCH 01/10] Refactored BaseContract and Utils. Added artifacts_dir test --- src/BaseContract/base_contract.py | 82 +++++-- src/BaseContract/utils.py | 58 +++-- tests/BaseContract/test_base_contract.py | 267 +++++++++++++++++++++++ 3 files changed, 366 insertions(+), 41 deletions(-) create mode 100644 tests/BaseContract/test_base_contract.py diff --git a/src/BaseContract/base_contract.py b/src/BaseContract/base_contract.py index fde613e6..16c40a77 100644 --- a/src/BaseContract/base_contract.py +++ b/src/BaseContract/base_contract.py @@ -1,24 +1,70 @@ -import utils from web3 import Web3 +from src.index import Artifacts +from utils import Utils + class BaseContract: - def __init__(self, artifact_name, network_id, network_provider, coordinator, address, web3): - self.name = artifact_name + provider: any + web3: any + contract: any + network_id: int + coordinator: any + artifact: any + name: str + address: str = None + + def __init__(self, + artifact_name: str, + web3: any = None, + network_id: int = 1, + network_provider: any = None, + artifacts_dir: str = None, + coordinator: str = None, + contract: any = None, + address: str = None + ): try: - self.artifact = artifact_name - self.network_id = network_id or '1' - coor_artifact_abi = utils.load_abi('ZapCoordinator') - coor_artifact_address = utils.load_address('ZapCoordinator', self.network_id) - self.provider = web3 or Web3(network_provider) or Web3(Web3.HTTPProvider('https://cloudflare-eth.com')) - self.coordinator = self.provider.eth.Contract(coor_artifact_abi, coordinator or coor_artifact_address) - self.contract = None - if address: - self.address = address + if artifacts_dir is None: + self.artifact = Artifacts[artifact_name] + self.coor_artifact = Artifacts['ZAPCOORDINATOR'] else: - self.address = self.artifact.networks[self.network_id].address - if coordinator: - self.coordinator.getContract() - else: - self.coordinator = self.provider.eth.Contract(self.artifact.abi, self.address) + artifacts: any = Utils.get_artifacts(artifacts_dir) + self.artifact = Utils.open_artifact_in_dir(artifacts[artifact_name]) + self.coor_artifact = Utils.open_artifact_in_dir(artifacts['ZAPCOORDINATOR']) + + self.name = artifact_name + self.provider = web3 or Web3(network_provider or Web3.HTTPProvider("https://cloudflare-eth.com")) + self.w3 = web3 or Web3(network_provider or Web3.HTTPProvider("https://cloudflare-eth.com")) + self.network_id = network_id or 1 + self.address = address or self.artifact['networks'][str(self.network_id)]['address'] + + if coordinator is not None: + self.coordinator = self.w3.eth.contract(address=self.w3.toChecksumAddress(coordinator), + abi=self.coor_artifact['abi']) + self.get_contract() + else: + self.coor_address = self.coor_artifact['networks'][str(self.network_id)]['address'] + self.coordinator = self.w3.eth.contract(address=self.w3.toChecksumAddress(self.coor_address), + abi=self.coor_artifact['abi']) + self.contract = self.w3.eth.contract(address=self.w3.toChecksumAddress(self.address), + abi=self.artifact['abi']) except Exception as e: - raise e \ No newline at end of file + raise e + + def get_contract(self) -> str: + """ + This function fetches the contract address from coordinator and assigns the 'self.contract' contract object. + :return: the contract address of the coordinator. + """ + contract_address = self.coordinator.functions.getContract(self.name.upper()).call() + self.contract = self.w3.eth.contract(address=self.w3.toChecksumAddress(contract_address), + abi=self.artifact['abi']) + return contract_address + + def get_contract_owner(self) -> str: + """ + This function fetches the owner of the contract instance. + :return: the contract owner's address. + """ + contract_owner = self.contract.functions.owner().call() + return contract_owner diff --git a/src/BaseContract/utils.py b/src/BaseContract/utils.py index 6244e404..45ef1ec7 100644 --- a/src/BaseContract/utils.py +++ b/src/BaseContract/utils.py @@ -1,29 +1,41 @@ -import json import os +import json -# These functions replace the monorepo's artifactDir and negates the need for each -# artifact's location to be hardcoded in a seperate module. -# -# Helper function for load_abi and load_address. -def load_json(name: str) -> str: - # Adjust path below to fetch Artifacts/.json. - path = "./Artifacts/" - with open(os.path.abspath(path + f"{name}.json")) as f: - json_file = json.load(f) - return json_file +class Utils: + """ + # @ignore + # @params {string} buildDir + # @returns {any} + """ -def load_abi(name: str) -> dict: - obj = load_json(name) - abi = obj['abi'] - return abi + @staticmethod + def get_artifacts(build_dir: str) -> dict: + artifacts = { + 'ARBITER': os.path.join(build_dir, 'Arbiter.json'), + 'BONDAGE': os.path.join(build_dir, 'Bondage.json'), + 'DISPATCH': os.path.join(build_dir, 'Dispatch.json'), + 'REGISTRY': os.path.join(build_dir, 'Registry.json'), + 'CurrentCost': os.path.join(build_dir, 'CurrentCost.json'), + 'PiecewiseLogic': os.path.join(build_dir, 'PiecewiseLogic.json'), + 'ZAP_TOKEN': os.path.join(build_dir, 'ZapToken.json'), + 'Client1': os.path.join(build_dir, 'Client1.json'), + 'Client2': os.path.join(build_dir, 'Client2.json'), + 'Client3': os.path.join(build_dir, 'Client3.json'), + 'Client4': os.path.join(build_dir, 'Client4.json'), + 'ZAPCOORDINATOR': os.path.join(build_dir, 'ZapCoordinator.json'), + 'TOKENDOTFACTORY': os.path.join(build_dir, 'TokenDotFactory.json'), + } + return artifacts -def load_address(name: str, netId: int or str) -> dict: - try: - a_dict = load_json(name) - addr = a_dict['networks'] - network_address = addr[netId]['address'] - return network_address - except Exception as e: - print('Error with: ' + str(e)) + """ + # @ignore + # @params {string} artifact + # @returns {dict} + """ + @staticmethod + def open_artifact_in_dir(artifact: str) -> dict: + with open(artifact) as f: + abi = json.load(f) + return abi diff --git a/tests/BaseContract/test_base_contract.py b/tests/BaseContract/test_base_contract.py new file mode 100644 index 00000000..4b47a663 --- /dev/null +++ b/tests/BaseContract/test_base_contract.py @@ -0,0 +1,267 @@ +from unittest.mock import MagicMock, patch +import pytest +import base_contract + +mock_abi = { + 'TEST_ARTIFACT': {'abi': [], 'networks': + {'1': {'address': '0xmainnet'}, + '42': {'address': '0xkovan'}, + '31337': {'address': '0xdevnet'}}}, + 'ZAPCOORDINATOR': {'abi': [], 'networks': + {'1': {'address': '0xcoormainnet'}, + '42': {'address': '0xcoorkovan'}, + '31337': {'address': '0xcoordevnet'}}} +} + + + +@patch.dict('base_contract.Artifacts', mock_abi) +@patch('base_contract.Web3', autospec=True) +class TestInit: + + def test_instance_name(self, mock_Web3): + """ + Sanity check to ensure the instance runs without errors while mocking web3 + + :param mock_Web3: patched web3. + """ + mock_Web3.return_value = MagicMock() + w3 = mock_Web3() + w3.eth.contract.return_value = MagicMock() + + instance = base_contract.BaseContract(artifact_name='TEST_ARTIFACT') + + assert type(instance.name) == str + assert instance.name == 'TEST_ARTIFACT' + + with pytest.raises(AssertionError): + assert type(instance.name) != str + assert instance.name != 'TEST_ARTIFACT' + + def test_name_without_arg(self, mock_Web3): + """ + Testing that the contract fails if the artifact_name kwarg is an empty string. + """ + mock_Web3.return_value = MagicMock() + w3 = mock_Web3() + w3.eth.contract.return_value = MagicMock() + + with pytest.raises(KeyError): + instance = base_contract.BaseContract(artifact_name='') + + + def test_network_id_default(self, mock_Web3): + """ + Testing that the default network (1) is assigned to the contract instance. + + :param mock_Web3: patched web3. + """ + mock_Web3.return_value = MagicMock() + w3 = mock_Web3() + w3.eth.contract.return_value = MagicMock() + + instance = base_contract.BaseContract(artifact_name='TEST_ARTIFACT') + + assert type(instance.network_id) == int + assert instance.network_id == 1 + + with pytest.raises(AssertionError): + assert type(instance.network_id) != int + assert instance.network_id != 1 + + @pytest.mark.parametrize('input', [1, 42, 31337]) + def test_assigned_network_ids(self, mock_Web3, input): + """ + Testing that the network kwarg gets assigned to the contract network instance. + + :param mock_Web3: patched web3. + :param input: the relevant networks' corresponding integer. + """ + mock_Web3.return_value = MagicMock() + w3 = mock_Web3() + w3.eth.contract.return_value = MagicMock() + instance = base_contract.BaseContract(artifact_name='TEST_ARTIFACT', network_id=input) + + assert instance.network_id == input + + with pytest.raises(AssertionError): + assert instance.network_id != input + + @pytest.mark.parametrize('wrong_net_id', [11, 16, 47, 118893]) + def test_network_id_should_fail_if_given_unknown_id(self, mock_Web3, wrong_net_id): + """ + Testing that the contract should fail if given an unknown network id. + """ + mock_Web3.return_value = MagicMock() + w3 = mock_Web3() + w3.eth.contract.return_value = MagicMock() + + with pytest.raises(KeyError): + instance = base_contract.BaseContract(artifact_name='TEST_ARTIFACT', network_id=wrong_net_id) + + def test_network_id_of_zero(self, mock_Web3): + """ + Testing that the network_id of zero actually uses the default network of '1.' + """ + mock_Web3.return_value = MagicMock() + w3 = mock_Web3() + w3.eth.contract.return_value = MagicMock() + + instance = base_contract.BaseContract(artifact_name='TEST_ARTIFACT', network_id=0) + + assert instance.network_id != 0 + assert instance.network_id == 1 + + +@patch.dict('base_contract.Artifacts', mock_abi) +@patch('base_contract.Web3', autospec=True) +class TestAddress: + + def test_address_argument(self, mock_Web3): + """ + Testing that the address argument is assigned as the instance address. + + :param mock_Web3: patched web3. + """ + mock_Web3.return_value = MagicMock() + w3 = mock_Web3() + w3.eth.contract.return_value = MagicMock() + + instance = base_contract.BaseContract(artifact_name='TEST_ARTIFACT', address='0x_some_address') + + assert type(instance.address) == str + assert instance.address == '0x_some_address' + + with pytest.raises(AssertionError): + assert instance.address == '0xsomeotheraddress' + + @pytest.mark.parametrize('network_input, address_output', [(1, '0xmainnet'), (42, '0xkovan'), (31337, '0xdevnet')]) + def test_address_with_no_argument(self, mock_Web3, network_input, address_output): + """ + Testing that the address is correctly fetched from the abi when no address arg is given. + + :param mock_Web3: patched web3. + :param network_input: relevant networks including mainnet, kovan, and devnet. + :param address_output: the associated address within the mock_abi dictionary. + """ + mock_Web3.return_value = MagicMock() + w3 = mock_Web3() + w3.eth.contract.return_value = MagicMock() + + instance = base_contract.BaseContract(artifact_name='TEST_ARTIFACT', network_id=network_input) + + assert type(instance.address) == str + assert instance.address == address_output + + with pytest.raises(AssertionError): + assert instance.address == '0xwrongaddress' + + +@patch.dict('base_contract.Artifacts', mock_abi) +@patch('base_contract.Web3', autospec=True) +class TestArtifacts: + + mock_contract = MagicMock(address="0x000000000000000000", abi=['abi'], name="MOCKCONTRACT") + + def test_artifact_and_coor_artifact_assignments_from_dictionary(self, mock_Web3): + """ + Testing that the artifact_name kwarg triggers the Artifacts dictionary and populates the artifact + attribute with the controlled mock abi. + + :param mock_Web3: patched web3. + """ + mock_Web3.return_value = MagicMock() + w3 = mock_Web3() + w3.eth.contract.return_value = MagicMock() + + instance = base_contract.BaseContract(artifact_name='TEST_ARTIFACT') + + """Asserting the artifact attribute is equal to the mock dictionary""" + + assert type(instance.artifact) == dict + assert instance.artifact['networks']['1']['address'] == '0xmainnet' + + assert type(instance.coor_artifact) == dict + assert instance.coor_artifact['networks']['1']['address'] == '0xcoormainnet' + + """Ensuring this includes no false positives""" + + with pytest.raises(AssertionError): + assert type(instance.artifact) != dict + assert instance.artifact['networks']['1']['address'] == '0x123' + + assert type(instance.coor_artifact) != dict + assert instance.coor_artifact['networks']['1']['address'] == '0x123' + + +@patch('base_contract.Web3', autospec=True) +class TestArtifactsDirectory: + mock_test_abi = {'abi': [], 'networks': {'1': {'address': '0xmainnet'}, '42': {'address': '0xkovan'}, + '31337': {'address': '0xdevnet'}}} + + mock_coor_abi = {'abi': [], 'networks': {'1': {'address': '0xcoormainnet'}, '42': {'address': '0xcoorkovan'}, + '31337': {'address': '0xcoordevnet'}}} + + mock_dict_dir = {'TEST_ARTIFACT': 'Artifacts/contracts/TestArtifact.json', + 'ZAPCOORDINATOR': 'Artifacts/contracts/ZapCoordinator.json'} + + @patch('base_contract.Utils.get_artifacts') + @patch('base_contract.Utils.open_artifact_in_dir') + @pytest.mark.parametrize( + 'art_input, net_id_input, address_output, coor_address_output', [ + ('TEST_ARTIFACT', 1, '0xmainnet', '0xcoormainnet'), ('TEST_ARTIFACT', 42, '0xkovan', '0xcoorkovan'), + ('TEST_ARTIFACT', 31337, '0xdevnet', '0xcoordevnet') + ]) + def test_artifact_instance_from_artifacts_directory_arg(self, mock_utils_abi, mock_artifact_dir, mock_Web3, + art_input, net_id_input, address_output, + coor_address_output): + """ + Testing that the artifacts_dir kwarg passes through both get_artifacts and open_artifact_in_dir functions + respectively. Thereafter, the BaseContract instance attribute 'artifact' should be assigned as an object + (abi). The get_artifacts and open_artifact_in_dir functions are patched and return expected values. + + :param mock_utils_abi: a mocked abi that mimicks what open_artifacts_in_dir function will return. + :param mock_artifact_dir: a small mocked directory that replicates the return of get_artifacts function. + :param mock_Web3: patched web3. + """ + + """Mock web3""" + mock_Web3.return_value = MagicMock() + w3 = mock_Web3() + w3.eth.contract.return_value = MagicMock() + + """Mock the relevant function returns""" + mock_artifact_dir.return_value = self.mock_dict_dir + mock_utils_abi.side_effect = [self.mock_test_abi, self.mock_coor_abi] + + instance = base_contract.BaseContract(artifact_name=art_input, artifacts_dir='some/path/', + network_id=net_id_input) + + assert type(instance.artifact) == dict + assert type(instance.artifact['abi']) == list + assert instance.artifact['networks'][str(net_id_input)]['address'] == address_output + + """Checking coordinator instance object and coordinator instance address are also set""" + + assert type(instance.coor_address) == str + assert type(instance.coor_artifact['abi']) == list + assert instance.coor_address == coor_address_output + + """Assertions regarding the artifact instance that should fail""" + + with pytest.raises(AssertionError): + assert type(instance.artifact) == list + assert type(instance.artifact['abi']) == dict + assert instance.artifact['network'][str(net_id_input)]['address'] == '0xfailingstring' + assert instance.artifact['abi']['network'] + + """Assertions regarding the coordinator artifact instance that should fail""" + + assert type(instance.coor_address) != str + assert type(instance.coor_artifact['abi']) != dict + assert instance.coor_address == '0xfailingstring' + assert instance.coor_artifact['abi']['network'] + + + + From fdd91bf9cd0ba67c4f696d565eae4019dfbc18d5 Mon Sep 17 00:00:00 2001 From: Andrew Date: Mon, 8 Mar 2021 04:43:56 -0500 Subject: [PATCH 02/10] Implemented asyncio in base_contract, still need to test --- src/BaseContract/base_contract.py | 18 ++++-- tests/BaseContract/test_base_contract.py | 72 +++++++++++++++++++----- 2 files changed, 72 insertions(+), 18 deletions(-) diff --git a/src/BaseContract/base_contract.py b/src/BaseContract/base_contract.py index 16c40a77..435d6c50 100644 --- a/src/BaseContract/base_contract.py +++ b/src/BaseContract/base_contract.py @@ -1,6 +1,7 @@ from web3 import Web3 from src.index import Artifacts from utils import Utils +import asyncio class BaseContract: @@ -41,30 +42,39 @@ def __init__(self, if coordinator is not None: self.coordinator = self.w3.eth.contract(address=self.w3.toChecksumAddress(coordinator), abi=self.coor_artifact['abi']) - self.get_contract() + call_get_contract = self.get_contract() + asyncio.run(call_get_contract) else: self.coor_address = self.coor_artifact['networks'][str(self.network_id)]['address'] self.coordinator = self.w3.eth.contract(address=self.w3.toChecksumAddress(self.coor_address), abi=self.coor_artifact['abi']) self.contract = self.w3.eth.contract(address=self.w3.toChecksumAddress(self.address), abi=self.artifact['abi']) + + call_contract_owner = self.get_contract_owner() + asyncio.run(call_contract_owner) + except Exception as e: raise e - def get_contract(self) -> str: + async def get_contract(self) -> str: """ This function fetches the contract address from coordinator and assigns the 'self.contract' contract object. :return: the contract address of the coordinator. """ - contract_address = self.coordinator.functions.getContract(self.name.upper()).call() + await asyncio.sleep(1) + contract_address = self.coordinator.functions.getContract.address + print('address is:' + contract_address) #DELETE: For demo purposes!!! self.contract = self.w3.eth.contract(address=self.w3.toChecksumAddress(contract_address), abi=self.artifact['abi']) return contract_address - def get_contract_owner(self) -> str: + async def get_contract_owner(self) -> str: """ This function fetches the owner of the contract instance. :return: the contract owner's address. """ + await asyncio.sleep(1) contract_owner = self.contract.functions.owner().call() + print('owner is:' + contract_owner) #DELETE: For demo purposes!!! return contract_owner diff --git a/tests/BaseContract/test_base_contract.py b/tests/BaseContract/test_base_contract.py index 4b47a663..b5396595 100644 --- a/tests/BaseContract/test_base_contract.py +++ b/tests/BaseContract/test_base_contract.py @@ -14,12 +14,11 @@ } - @patch.dict('base_contract.Artifacts', mock_abi) @patch('base_contract.Web3', autospec=True) class TestInit: - def test_instance_name(self, mock_Web3): + def test_instance_name_and_coordinator(self, mock_Web3): """ Sanity check to ensure the instance runs without errors while mocking web3 @@ -44,10 +43,11 @@ def test_name_without_arg(self, mock_Web3): """ mock_Web3.return_value = MagicMock() w3 = mock_Web3() - w3.eth.contract.return_value = MagicMock() + w3.eth.contract.return_value = w3._utils.datatypes.Contract with pytest.raises(KeyError): instance = base_contract.BaseContract(artifact_name='') + assert instance def test_network_id_default(self, mock_Web3): @@ -58,7 +58,7 @@ def test_network_id_default(self, mock_Web3): """ mock_Web3.return_value = MagicMock() w3 = mock_Web3() - w3.eth.contract.return_value = MagicMock() + w3.eth.contract.return_value = w3._utils.datatypes.Contract instance = base_contract.BaseContract(artifact_name='TEST_ARTIFACT') @@ -79,7 +79,7 @@ def test_assigned_network_ids(self, mock_Web3, input): """ mock_Web3.return_value = MagicMock() w3 = mock_Web3() - w3.eth.contract.return_value = MagicMock() + w3.eth.contract.return_value = w3._utils.datatypes.Contract instance = base_contract.BaseContract(artifact_name='TEST_ARTIFACT', network_id=input) assert instance.network_id == input @@ -94,10 +94,11 @@ def test_network_id_should_fail_if_given_unknown_id(self, mock_Web3, wrong_net_i """ mock_Web3.return_value = MagicMock() w3 = mock_Web3() - w3.eth.contract.return_value = MagicMock() + w3.eth.contract.return_value = w3._utils.datatypes.Contract with pytest.raises(KeyError): instance = base_contract.BaseContract(artifact_name='TEST_ARTIFACT', network_id=wrong_net_id) + assert instance def test_network_id_of_zero(self, mock_Web3): """ @@ -105,7 +106,7 @@ def test_network_id_of_zero(self, mock_Web3): """ mock_Web3.return_value = MagicMock() w3 = mock_Web3() - w3.eth.contract.return_value = MagicMock() + w3.eth.contract.return_value = w3._utils.datatypes.Contract instance = base_contract.BaseContract(artifact_name='TEST_ARTIFACT', network_id=0) @@ -125,7 +126,7 @@ def test_address_argument(self, mock_Web3): """ mock_Web3.return_value = MagicMock() w3 = mock_Web3() - w3.eth.contract.return_value = MagicMock() + w3.eth.contract.return_value = w3._utils.datatypes.Contract instance = base_contract.BaseContract(artifact_name='TEST_ARTIFACT', address='0x_some_address') @@ -146,7 +147,7 @@ def test_address_with_no_argument(self, mock_Web3, network_input, address_output """ mock_Web3.return_value = MagicMock() w3 = mock_Web3() - w3.eth.contract.return_value = MagicMock() + w3.eth.contract.return_value = w3._utils.datatypes.Contract instance = base_contract.BaseContract(artifact_name='TEST_ARTIFACT', network_id=network_input) @@ -161,8 +162,6 @@ def test_address_with_no_argument(self, mock_Web3, network_input, address_output @patch('base_contract.Web3', autospec=True) class TestArtifacts: - mock_contract = MagicMock(address="0x000000000000000000", abi=['abi'], name="MOCKCONTRACT") - def test_artifact_and_coor_artifact_assignments_from_dictionary(self, mock_Web3): """ Testing that the artifact_name kwarg triggers the Artifacts dictionary and populates the artifact @@ -172,7 +171,7 @@ def test_artifact_and_coor_artifact_assignments_from_dictionary(self, mock_Web3) """ mock_Web3.return_value = MagicMock() w3 = mock_Web3() - w3.eth.contract.return_value = MagicMock() + w3.eth.contract.return_value = w3._utils.datatypes.Contract instance = base_contract.BaseContract(artifact_name='TEST_ARTIFACT') @@ -220,7 +219,7 @@ def test_artifact_instance_from_artifacts_directory_arg(self, mock_utils_abi, mo respectively. Thereafter, the BaseContract instance attribute 'artifact' should be assigned as an object (abi). The get_artifacts and open_artifact_in_dir functions are patched and return expected values. - :param mock_utils_abi: a mocked abi that mimicks what open_artifacts_in_dir function will return. + :param mock_utils_abi: a mocked abi that mimics what open_artifacts_in_dir function will return. :param mock_artifact_dir: a small mocked directory that replicates the return of get_artifacts function. :param mock_Web3: patched web3. """ @@ -228,7 +227,7 @@ def test_artifact_instance_from_artifacts_directory_arg(self, mock_utils_abi, mo """Mock web3""" mock_Web3.return_value = MagicMock() w3 = mock_Web3() - w3.eth.contract.return_value = MagicMock() + w3.eth.contract.return_value = w3._utils.datatypes.Contract """Mock the relevant function returns""" mock_artifact_dir.return_value = self.mock_dict_dir @@ -263,5 +262,50 @@ def test_artifact_instance_from_artifacts_directory_arg(self, mock_utils_abi, mo assert instance.coor_artifact['abi']['network'] +@patch.dict('base_contract.Artifacts', mock_abi) +@patch('base_contract.Web3', autospec=True) +class TestContracts: + + def test_coordinator_contract_if_no_coor_provided(self, mock_Web3): + """ + Testing that the coordinator contract object is assigned. + """ + mock_Web3.return_value = MagicMock() + w3 = mock_Web3() + w3.eth.contract.return_value = w3._utils.datatypes.Contract + + instance = base_contract.BaseContract(artifact_name='TEST_ARTIFACT') + assert instance.coordinator + + def test_coordinator_contract_with_coordinator_address(self, mock_Web3): + """ + Testing that the coordinator contract object is assigned. + """ + + mock_Web3.return_value = MagicMock() + w3 = mock_Web3() + w3.eth.contract.return_value = w3._utils.datatypes.Contract + + instance = base_contract.BaseContract(artifact_name='TEST_ARTIFACT', coordinator='0x0102030405') + + """Testing that the coordinator instance was assigned""" + + assert instance.coordinator + + + + def test_self_contract_if_no_coor_provided(self, mock_Web3): + """ + Testing that the contract instance object is assigned. + """ + mock_Web3.return_value = MagicMock() + w3 = mock_Web3() + w3.eth.contract.return_value = w3._utils.datatypes.Contract + + instance = base_contract.BaseContract(artifact_name='TEST_ARTIFACT') + assert instance.contract + + + From b81383b2587612f64f399ddcdba3f86d449a82e5 Mon Sep 17 00:00:00 2001 From: Andrew Date: Wed, 10 Mar 2021 02:36:23 -0500 Subject: [PATCH 03/10] Adjusted async in base_contract, added arg capture side effect in tests --- .../contracts/Arbiter.json | 0 .../contracts/Bondage.json | 0 .../contracts/Client1.json | 0 .../contracts/Client2.json | 0 .../contracts/Client3.json | 0 .../contracts/Client4.json | 0 .../contracts/ClientBytes32Array.json | 0 .../contracts/ClientIntArray.json | 0 .../contracts/CurrentCost.json | 0 .../contracts/Dispatch.json | 0 .../contracts/Piecewise.json | 0 .../contracts/Registry.json | 0 .../contracts/RegistryInterface.json | 0 .../contracts/TokenDotFactory.json | 0 .../contracts/TokenFactory.json | 0 .../contracts/ZapCoordinator.json | 0 .../contracts/ZapCoordinatorInterface.json | 0 .../contracts/ZapToken.json | 0 src/{Artifacts => artifacts}/src/index.py | 0 .../src/tests/__init__.py | 0 .../src/tests/test_artifacts.py | 0 .../base_contract.py | 62 ++++++++++--- .../test/__init__.py | 0 .../test/test_base_contract.py | 0 src/{BaseContract => base_contract}/utils.py | 0 .../test_base_contract.py | 89 ++++++++++++++++--- 26 files changed, 124 insertions(+), 27 deletions(-) rename src/{Artifacts => artifacts}/contracts/Arbiter.json (100%) rename src/{Artifacts => artifacts}/contracts/Bondage.json (100%) rename src/{Artifacts => artifacts}/contracts/Client1.json (100%) rename src/{Artifacts => artifacts}/contracts/Client2.json (100%) rename src/{Artifacts => artifacts}/contracts/Client3.json (100%) rename src/{Artifacts => artifacts}/contracts/Client4.json (100%) rename src/{Artifacts => artifacts}/contracts/ClientBytes32Array.json (100%) rename src/{Artifacts => artifacts}/contracts/ClientIntArray.json (100%) rename src/{Artifacts => artifacts}/contracts/CurrentCost.json (100%) rename src/{Artifacts => artifacts}/contracts/Dispatch.json (100%) rename src/{Artifacts => artifacts}/contracts/Piecewise.json (100%) rename src/{Artifacts => artifacts}/contracts/Registry.json (100%) rename src/{Artifacts => artifacts}/contracts/RegistryInterface.json (100%) rename src/{Artifacts => artifacts}/contracts/TokenDotFactory.json (100%) rename src/{Artifacts => artifacts}/contracts/TokenFactory.json (100%) rename src/{Artifacts => artifacts}/contracts/ZapCoordinator.json (100%) rename src/{Artifacts => artifacts}/contracts/ZapCoordinatorInterface.json (100%) rename src/{Artifacts => artifacts}/contracts/ZapToken.json (100%) rename src/{Artifacts => artifacts}/src/index.py (100%) rename src/{Artifacts => artifacts}/src/tests/__init__.py (100%) rename src/{Artifacts => artifacts}/src/tests/test_artifacts.py (100%) rename src/{BaseContract => base_contract}/base_contract.py (58%) rename src/{BaseContract => base_contract}/test/__init__.py (100%) rename src/{BaseContract => base_contract}/test/test_base_contract.py (100%) rename src/{BaseContract => base_contract}/utils.py (100%) rename tests/{BaseContract => base_contract}/test_base_contract.py (83%) diff --git a/src/Artifacts/contracts/Arbiter.json b/src/artifacts/contracts/Arbiter.json similarity index 100% rename from src/Artifacts/contracts/Arbiter.json rename to src/artifacts/contracts/Arbiter.json diff --git a/src/Artifacts/contracts/Bondage.json b/src/artifacts/contracts/Bondage.json similarity index 100% rename from src/Artifacts/contracts/Bondage.json rename to src/artifacts/contracts/Bondage.json diff --git a/src/Artifacts/contracts/Client1.json b/src/artifacts/contracts/Client1.json similarity index 100% rename from src/Artifacts/contracts/Client1.json rename to src/artifacts/contracts/Client1.json diff --git a/src/Artifacts/contracts/Client2.json b/src/artifacts/contracts/Client2.json similarity index 100% rename from src/Artifacts/contracts/Client2.json rename to src/artifacts/contracts/Client2.json diff --git a/src/Artifacts/contracts/Client3.json b/src/artifacts/contracts/Client3.json similarity index 100% rename from src/Artifacts/contracts/Client3.json rename to src/artifacts/contracts/Client3.json diff --git a/src/Artifacts/contracts/Client4.json b/src/artifacts/contracts/Client4.json similarity index 100% rename from src/Artifacts/contracts/Client4.json rename to src/artifacts/contracts/Client4.json diff --git a/src/Artifacts/contracts/ClientBytes32Array.json b/src/artifacts/contracts/ClientBytes32Array.json similarity index 100% rename from src/Artifacts/contracts/ClientBytes32Array.json rename to src/artifacts/contracts/ClientBytes32Array.json diff --git a/src/Artifacts/contracts/ClientIntArray.json b/src/artifacts/contracts/ClientIntArray.json similarity index 100% rename from src/Artifacts/contracts/ClientIntArray.json rename to src/artifacts/contracts/ClientIntArray.json diff --git a/src/Artifacts/contracts/CurrentCost.json b/src/artifacts/contracts/CurrentCost.json similarity index 100% rename from src/Artifacts/contracts/CurrentCost.json rename to src/artifacts/contracts/CurrentCost.json diff --git a/src/Artifacts/contracts/Dispatch.json b/src/artifacts/contracts/Dispatch.json similarity index 100% rename from src/Artifacts/contracts/Dispatch.json rename to src/artifacts/contracts/Dispatch.json diff --git a/src/Artifacts/contracts/Piecewise.json b/src/artifacts/contracts/Piecewise.json similarity index 100% rename from src/Artifacts/contracts/Piecewise.json rename to src/artifacts/contracts/Piecewise.json diff --git a/src/Artifacts/contracts/Registry.json b/src/artifacts/contracts/Registry.json similarity index 100% rename from src/Artifacts/contracts/Registry.json rename to src/artifacts/contracts/Registry.json diff --git a/src/Artifacts/contracts/RegistryInterface.json b/src/artifacts/contracts/RegistryInterface.json similarity index 100% rename from src/Artifacts/contracts/RegistryInterface.json rename to src/artifacts/contracts/RegistryInterface.json diff --git a/src/Artifacts/contracts/TokenDotFactory.json b/src/artifacts/contracts/TokenDotFactory.json similarity index 100% rename from src/Artifacts/contracts/TokenDotFactory.json rename to src/artifacts/contracts/TokenDotFactory.json diff --git a/src/Artifacts/contracts/TokenFactory.json b/src/artifacts/contracts/TokenFactory.json similarity index 100% rename from src/Artifacts/contracts/TokenFactory.json rename to src/artifacts/contracts/TokenFactory.json diff --git a/src/Artifacts/contracts/ZapCoordinator.json b/src/artifacts/contracts/ZapCoordinator.json similarity index 100% rename from src/Artifacts/contracts/ZapCoordinator.json rename to src/artifacts/contracts/ZapCoordinator.json diff --git a/src/Artifacts/contracts/ZapCoordinatorInterface.json b/src/artifacts/contracts/ZapCoordinatorInterface.json similarity index 100% rename from src/Artifacts/contracts/ZapCoordinatorInterface.json rename to src/artifacts/contracts/ZapCoordinatorInterface.json diff --git a/src/Artifacts/contracts/ZapToken.json b/src/artifacts/contracts/ZapToken.json similarity index 100% rename from src/Artifacts/contracts/ZapToken.json rename to src/artifacts/contracts/ZapToken.json diff --git a/src/Artifacts/src/index.py b/src/artifacts/src/index.py similarity index 100% rename from src/Artifacts/src/index.py rename to src/artifacts/src/index.py diff --git a/src/Artifacts/src/tests/__init__.py b/src/artifacts/src/tests/__init__.py similarity index 100% rename from src/Artifacts/src/tests/__init__.py rename to src/artifacts/src/tests/__init__.py diff --git a/src/Artifacts/src/tests/test_artifacts.py b/src/artifacts/src/tests/test_artifacts.py similarity index 100% rename from src/Artifacts/src/tests/test_artifacts.py rename to src/artifacts/src/tests/test_artifacts.py diff --git a/src/BaseContract/base_contract.py b/src/base_contract/base_contract.py similarity index 58% rename from src/BaseContract/base_contract.py rename to src/base_contract/base_contract.py index 435d6c50..265f5745 100644 --- a/src/BaseContract/base_contract.py +++ b/src/base_contract/base_contract.py @@ -40,41 +40,75 @@ def __init__(self, self.address = address or self.artifact['networks'][str(self.network_id)]['address'] if coordinator is not None: - self.coordinator = self.w3.eth.contract(address=self.w3.toChecksumAddress(coordinator), + self.coor_address = self.w3.toChecksumAddress(coordinator) + self.coordinator = self.w3.eth.contract(address=self.w3.toChecksumAddress(self.coor_address), abi=self.coor_artifact['abi']) - call_get_contract = self.get_contract() - asyncio.run(call_get_contract) + + contract_address = self.get_contract() + + self.contract = self.w3.eth.contract(address=self.w3.toChecksumAddress(contract_address), + abi=self.artifact['abi']) + else: self.coor_address = self.coor_artifact['networks'][str(self.network_id)]['address'] self.coordinator = self.w3.eth.contract(address=self.w3.toChecksumAddress(self.coor_address), abi=self.coor_artifact['abi']) + self.contract = self.w3.eth.contract(address=self.w3.toChecksumAddress(self.address), abi=self.artifact['abi']) - call_contract_owner = self.get_contract_owner() - asyncio.run(call_contract_owner) - except Exception as e: raise e - async def get_contract(self) -> str: + async def _get_contract(self) -> str: """ - This function fetches the contract address from coordinator and assigns the 'self.contract' contract object. + This async function fetches the contract address from the coordinator and assigns the contract object instance + to the coordinator (within the context of the conditional statement of where it's located). Further, :return: the contract address of the coordinator. """ await asyncio.sleep(1) contract_address = self.coordinator.functions.getContract.address - print('address is:' + contract_address) #DELETE: For demo purposes!!! - self.contract = self.w3.eth.contract(address=self.w3.toChecksumAddress(contract_address), - abi=self.artifact['abi']) return contract_address - async def get_contract_owner(self) -> str: + async def _get_contract_owner(self) -> str: """ - This function fetches the owner of the contract instance. + This async function fetches the owner of the contract instance. + :return: the contract owner's address. """ await asyncio.sleep(1) contract_owner = self.contract.functions.owner().call() - print('owner is:' + contract_owner) #DELETE: For demo purposes!!! return contract_owner + + def get_contract(self) -> str: + """ + A synchronous function that wraps the asynchronous _get_contract method. This provides flexibility. The + async function is used in the constructor to assign the coordinator address to the contract object; while in a + different context, a user can fetch the contract object's address. + + :return: the contract address. + """ + task = self._get_contract() + contract_address = asyncio.run(task) + return contract_address + + def get_contract_owner(self) -> str: + """ + A synchronous function that wraps the asynchronous _get_contract_owner method. This function returns the + contract owner's address. + + :return: the contract owner's address. + """ + task = self._get_contract_owner() + owner = asyncio.run(task) + return owner + + +w3 = Web3(Web3.HTTPProvider('http://127.0.0.1:8545')) +y = BaseContract(artifact_name='ARBITER', web3=w3, network_id=31337, + coordinator='0x9fE46736679d2D9a65F0992F2272dE9f3c7fa6e0') +print(y.get_contract_owner()) + + + + diff --git a/src/BaseContract/test/__init__.py b/src/base_contract/test/__init__.py similarity index 100% rename from src/BaseContract/test/__init__.py rename to src/base_contract/test/__init__.py diff --git a/src/BaseContract/test/test_base_contract.py b/src/base_contract/test/test_base_contract.py similarity index 100% rename from src/BaseContract/test/test_base_contract.py rename to src/base_contract/test/test_base_contract.py diff --git a/src/BaseContract/utils.py b/src/base_contract/utils.py similarity index 100% rename from src/BaseContract/utils.py rename to src/base_contract/utils.py diff --git a/tests/BaseContract/test_base_contract.py b/tests/base_contract/test_base_contract.py similarity index 83% rename from tests/BaseContract/test_base_contract.py rename to tests/base_contract/test_base_contract.py index b5396595..81a2e8a0 100644 --- a/tests/BaseContract/test_base_contract.py +++ b/tests/base_contract/test_base_contract.py @@ -1,6 +1,8 @@ -from unittest.mock import MagicMock, patch +from unittest.mock import MagicMock, patch, AsyncMock, PropertyMock import pytest +import unittest import base_contract +import re mock_abi = { 'TEST_ARTIFACT': {'abi': [], 'networks': @@ -18,7 +20,7 @@ @patch('base_contract.Web3', autospec=True) class TestInit: - def test_instance_name_and_coordinator(self, mock_Web3): + def test_instance_name(self, mock_Web3): """ Sanity check to ensure the instance runs without errors while mocking web3 @@ -134,7 +136,7 @@ def test_address_argument(self, mock_Web3): assert instance.address == '0x_some_address' with pytest.raises(AssertionError): - assert instance.address == '0xsomeotheraddress' + assert instance.address == '0x_some_other_address' @pytest.mark.parametrize('network_input, address_output', [(1, '0xmainnet'), (42, '0xkovan'), (31337, '0xdevnet')]) def test_address_with_no_argument(self, mock_Web3, network_input, address_output): @@ -155,7 +157,7 @@ def test_address_with_no_argument(self, mock_Web3, network_input, address_output assert instance.address == address_output with pytest.raises(AssertionError): - assert instance.address == '0xwrongaddress' + assert instance.address == '0x_wrong_address' @patch.dict('base_contract.Artifacts', mock_abi) @@ -164,7 +166,7 @@ class TestArtifacts: def test_artifact_and_coor_artifact_assignments_from_dictionary(self, mock_Web3): """ - Testing that the artifact_name kwarg triggers the Artifacts dictionary and populates the artifact + Testing that the artifact_name kwarg triggers the artifacts dictionary and populates the artifact attribute with the controlled mock abi. :param mock_Web3: patched web3. @@ -201,8 +203,8 @@ class TestArtifactsDirectory: mock_coor_abi = {'abi': [], 'networks': {'1': {'address': '0xcoormainnet'}, '42': {'address': '0xcoorkovan'}, '31337': {'address': '0xcoordevnet'}}} - mock_dict_dir = {'TEST_ARTIFACT': 'Artifacts/contracts/TestArtifact.json', - 'ZAPCOORDINATOR': 'Artifacts/contracts/ZapCoordinator.json'} + mock_dict_dir = {'TEST_ARTIFACT': 'artifacts/contracts/TestArtifact.json', + 'ZAPCOORDINATOR': 'artifacts/contracts/ZapCoordinator.json'} @patch('base_contract.Utils.get_artifacts') @patch('base_contract.Utils.open_artifact_in_dir') @@ -216,7 +218,7 @@ def test_artifact_instance_from_artifacts_directory_arg(self, mock_utils_abi, mo coor_address_output): """ Testing that the artifacts_dir kwarg passes through both get_artifacts and open_artifact_in_dir functions - respectively. Thereafter, the BaseContract instance attribute 'artifact' should be assigned as an object + respectively. Thereafter, the base_contract instance attribute 'artifact' should be assigned as an object (abi). The get_artifacts and open_artifact_in_dir functions are patched and return expected values. :param mock_utils_abi: a mocked abi that mimics what open_artifacts_in_dir function will return. @@ -266,16 +268,33 @@ def test_artifact_instance_from_artifacts_directory_arg(self, mock_utils_abi, mo @patch('base_contract.Web3', autospec=True) class TestContracts: - def test_coordinator_contract_if_no_coor_provided(self, mock_Web3): + """Side effect function for checking args and kwargs passed""" + def capture_args(self, *args, **kwargs) -> any: + return args, kwargs + + @patch('base_contract.BaseContract.get_contract') + def test_coordinator_contract_if_no_coor_provided(self, mock_get_contract, mock_Web3): """ Testing that the coordinator contract object is assigned. """ mock_Web3.return_value = MagicMock() w3 = mock_Web3() - w3.eth.contract.return_value = w3._utils.datatypes.Contract + w3.toChecksumAddress.side_effect = self.capture_args + w3.eth.contract.side_effect = self.capture_args + + mock_get_contract = MagicMock() + + instance = base_contract.BaseContract(artifact_name='TEST_ARTIFACT', coordinator='0x_some_address') + + assert type(instance.coordinator) == tuple + assert instance.coordinator[1]['abi'] == [] + + expected_address = '0x_some_address' + + """Iterate through the returned tuple to find the passed address""" + res = re.search(expected_address, str(instance.coordinator[1]['address'])) + assert res is not None - instance = base_contract.BaseContract(artifact_name='TEST_ARTIFACT') - assert instance.coordinator def test_coordinator_contract_with_coordinator_address(self, mock_Web3): """ @@ -294,7 +313,7 @@ def test_coordinator_contract_with_coordinator_address(self, mock_Web3): - def test_self_contract_if_no_coor_provided(self, mock_Web3): + def test_contract_instance_if_no_coor_provided(self, mock_Web3): """ Testing that the contract instance object is assigned. """ @@ -306,6 +325,50 @@ def test_self_contract_if_no_coor_provided(self, mock_Web3): assert instance.contract +@patch.dict('base_contract.Artifacts', mock_abi) +@patch('base_contract.Web3', autospec=True) +class TestMethods: + + + def mock_get_contract(self): + return '0xcoormainnet' + + @patch('base_contract.asyncio') + @patch('base_contract.BaseContract._get_contract') + def test_get_contract(self, mock_async_get, mock_asyncio, mock_Web3): + """ + Testing that the get_contract function returns the proper values. + """ + mock_Web3.return_value = MagicMock() + w3 = mock_Web3() + w3.eth.contract.return_value = w3._utils.datatypes.Contract + + """Mock the async function call""" + mock_async_get.return_value = AsyncMock() + + """Mock asyncio""" + mock_asyncio.return_value = MagicMock() + m_asyncio = mock_asyncio() + + instance = base_contract.BaseContract(artifact_name='TEST_ARTIFACT', coordinator='0xcoormainnet') + + + # FINISH THIS TEST!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + + + + + + + + + + + + + + + From c6cf62304c8cddf5ca331f3d5efdeba5ad07e512 Mon Sep 17 00:00:00 2001 From: Andrew Date: Thu, 11 Mar 2021 21:28:45 -0500 Subject: [PATCH 04/10] Working on imports --- src/base_contract/base_contract.py | 34 +-- tests/base_contract/test_base_contract.py | 284 +++++++++++++++++----- 2 files changed, 239 insertions(+), 79 deletions(-) diff --git a/src/base_contract/base_contract.py b/src/base_contract/base_contract.py index 265f5745..4c57e406 100644 --- a/src/base_contract/base_contract.py +++ b/src/base_contract/base_contract.py @@ -1,6 +1,10 @@ from web3 import Web3 -from src.index import Artifacts -from utils import Utils +import sys +#from os.path import join, realpath +#sys.path.insert(0, realpath(join(__file__, "../../../src/"))) +from artifacts.src.index import Artifacts +#from src import index +import utils import asyncio @@ -29,9 +33,9 @@ def __init__(self, self.artifact = Artifacts[artifact_name] self.coor_artifact = Artifacts['ZAPCOORDINATOR'] else: - artifacts: any = Utils.get_artifacts(artifacts_dir) - self.artifact = Utils.open_artifact_in_dir(artifacts[artifact_name]) - self.coor_artifact = Utils.open_artifact_in_dir(artifacts['ZAPCOORDINATOR']) + artifacts: any = utils.Utils.get_artifacts(artifacts_dir) + self.artifact = utils.Utils.open_artifact_in_dir(artifacts[artifact_name]) + self.coor_artifact = utils.Utils.open_artifact_in_dir(artifacts['ZAPCOORDINATOR']) self.name = artifact_name self.provider = web3 or Web3(network_provider or Web3.HTTPProvider("https://cloudflare-eth.com")) @@ -40,12 +44,10 @@ def __init__(self, self.address = address or self.artifact['networks'][str(self.network_id)]['address'] if coordinator is not None: - self.coor_address = self.w3.toChecksumAddress(coordinator) - self.coordinator = self.w3.eth.contract(address=self.w3.toChecksumAddress(self.coor_address), + self.coordinator = self.w3.eth.contract(address=self.w3.toChecksumAddress(coordinator), abi=self.coor_artifact['abi']) contract_address = self.get_contract() - self.contract = self.w3.eth.contract(address=self.w3.toChecksumAddress(contract_address), abi=self.artifact['abi']) @@ -66,8 +68,8 @@ async def _get_contract(self) -> str: to the coordinator (within the context of the conditional statement of where it's located). Further, :return: the contract address of the coordinator. """ - await asyncio.sleep(1) - contract_address = self.coordinator.functions.getContract.address + await asyncio.sleep(.5) + contract_address = self.coordinator.functions.getContract(self.name).call() return contract_address async def _get_contract_owner(self) -> str: @@ -76,7 +78,7 @@ async def _get_contract_owner(self) -> str: :return: the contract owner's address. """ - await asyncio.sleep(1) + await asyncio.sleep(.5) contract_owner = self.contract.functions.owner().call() return contract_owner @@ -102,13 +104,3 @@ def get_contract_owner(self) -> str: task = self._get_contract_owner() owner = asyncio.run(task) return owner - - -w3 = Web3(Web3.HTTPProvider('http://127.0.0.1:8545')) -y = BaseContract(artifact_name='ARBITER', web3=w3, network_id=31337, - coordinator='0x9fE46736679d2D9a65F0992F2272dE9f3c7fa6e0') -print(y.get_contract_owner()) - - - - diff --git a/tests/base_contract/test_base_contract.py b/tests/base_contract/test_base_contract.py index 81a2e8a0..c3c7225c 100644 --- a/tests/base_contract/test_base_contract.py +++ b/tests/base_contract/test_base_contract.py @@ -1,8 +1,20 @@ from unittest.mock import MagicMock, patch, AsyncMock, PropertyMock import pytest import unittest -import base_contract +from unittest import mock +from anyio import run +import anyio +import sys import re +import asyncio +from os.path import join, realpath +sys.path.insert(0, realpath(join(__file__, "../../../src/"))) + +from base_contract import BaseContract, Artifacts, Web3 + + + +print(sys.path[0]) mock_abi = { 'TEST_ARTIFACT': {'abi': [], 'networks': @@ -15,22 +27,31 @@ '31337': {'address': '0xcoordevnet'}}} } +@pytest.fixture +@patch('Web3', autospec=True) +def mock_web3(mock_Web3): + mock_Web3.return_value = MagicMock() + w3 = mock_Web3() + w3.eth.contract.return_value = MagicMock() + + -@patch.dict('base_contract.Artifacts', mock_abi) -@patch('base_contract.Web3', autospec=True) + +@patch.dict('Artifacts', mock_abi) +@patch('Web3', autospec=True) class TestInit: - def test_instance_name(self, mock_Web3): + def test_instance_name(self, mock_web3): """ Sanity check to ensure the instance runs without errors while mocking web3 :param mock_Web3: patched web3. """ - mock_Web3.return_value = MagicMock() - w3 = mock_Web3() - w3.eth.contract.return_value = MagicMock() + #mock_Web3.return_value = MagicMock() + #w3 = mock_Web3() + #w3.eth.contract.return_value = MagicMock() - instance = base_contract.BaseContract(artifact_name='TEST_ARTIFACT') + instance = BaseContract(artifact_name='TEST_ARTIFACT') assert type(instance.name) == str assert instance.name == 'TEST_ARTIFACT' @@ -48,21 +69,20 @@ def test_name_without_arg(self, mock_Web3): w3.eth.contract.return_value = w3._utils.datatypes.Contract with pytest.raises(KeyError): - instance = base_contract.BaseContract(artifact_name='') + instance = BaseContract(artifact_name='') assert instance def test_network_id_default(self, mock_Web3): """ Testing that the default network (1) is assigned to the contract instance. - :param mock_Web3: patched web3. """ mock_Web3.return_value = MagicMock() w3 = mock_Web3() w3.eth.contract.return_value = w3._utils.datatypes.Contract - instance = base_contract.BaseContract(artifact_name='TEST_ARTIFACT') + instance = BaseContract(artifact_name='TEST_ARTIFACT') assert type(instance.network_id) == int assert instance.network_id == 1 @@ -82,7 +102,7 @@ def test_assigned_network_ids(self, mock_Web3, input): mock_Web3.return_value = MagicMock() w3 = mock_Web3() w3.eth.contract.return_value = w3._utils.datatypes.Contract - instance = base_contract.BaseContract(artifact_name='TEST_ARTIFACT', network_id=input) + instance = BaseContract(artifact_name='TEST_ARTIFACT', network_id=input) assert instance.network_id == input @@ -99,7 +119,7 @@ def test_network_id_should_fail_if_given_unknown_id(self, mock_Web3, wrong_net_i w3.eth.contract.return_value = w3._utils.datatypes.Contract with pytest.raises(KeyError): - instance = base_contract.BaseContract(artifact_name='TEST_ARTIFACT', network_id=wrong_net_id) + instance = BaseContract(artifact_name='TEST_ARTIFACT', network_id=wrong_net_id) assert instance def test_network_id_of_zero(self, mock_Web3): @@ -110,14 +130,14 @@ def test_network_id_of_zero(self, mock_Web3): w3 = mock_Web3() w3.eth.contract.return_value = w3._utils.datatypes.Contract - instance = base_contract.BaseContract(artifact_name='TEST_ARTIFACT', network_id=0) + instance = BaseContract(artifact_name='TEST_ARTIFACT', network_id=0) assert instance.network_id != 0 assert instance.network_id == 1 -@patch.dict('base_contract.Artifacts', mock_abi) -@patch('base_contract.Web3', autospec=True) +@patch.dict('Artifacts', mock_abi) +@patch('Web3', autospec=True) class TestAddress: def test_address_argument(self, mock_Web3): @@ -130,7 +150,7 @@ def test_address_argument(self, mock_Web3): w3 = mock_Web3() w3.eth.contract.return_value = w3._utils.datatypes.Contract - instance = base_contract.BaseContract(artifact_name='TEST_ARTIFACT', address='0x_some_address') + instance = BaseContract(artifact_name='TEST_ARTIFACT', address='0x_some_address') assert type(instance.address) == str assert instance.address == '0x_some_address' @@ -151,7 +171,7 @@ def test_address_with_no_argument(self, mock_Web3, network_input, address_output w3 = mock_Web3() w3.eth.contract.return_value = w3._utils.datatypes.Contract - instance = base_contract.BaseContract(artifact_name='TEST_ARTIFACT', network_id=network_input) + instance = BaseContract(artifact_name='TEST_ARTIFACT', network_id=network_input) assert type(instance.address) == str assert instance.address == address_output @@ -160,8 +180,8 @@ def test_address_with_no_argument(self, mock_Web3, network_input, address_output assert instance.address == '0x_wrong_address' -@patch.dict('base_contract.Artifacts', mock_abi) -@patch('base_contract.Web3', autospec=True) +@patch.dict('Artifacts', mock_abi) +@patch('Web3', autospec=True) class TestArtifacts: def test_artifact_and_coor_artifact_assignments_from_dictionary(self, mock_Web3): @@ -175,7 +195,7 @@ def test_artifact_and_coor_artifact_assignments_from_dictionary(self, mock_Web3) w3 = mock_Web3() w3.eth.contract.return_value = w3._utils.datatypes.Contract - instance = base_contract.BaseContract(artifact_name='TEST_ARTIFACT') + instance = BaseContract(artifact_name='TEST_ARTIFACT') """Asserting the artifact attribute is equal to the mock dictionary""" @@ -195,7 +215,7 @@ def test_artifact_and_coor_artifact_assignments_from_dictionary(self, mock_Web3) assert instance.coor_artifact['networks']['1']['address'] == '0x123' -@patch('base_contract.Web3', autospec=True) +@patch('Web3', autospec=True) class TestArtifactsDirectory: mock_test_abi = {'abi': [], 'networks': {'1': {'address': '0xmainnet'}, '42': {'address': '0xkovan'}, '31337': {'address': '0xdevnet'}}} @@ -206,8 +226,8 @@ class TestArtifactsDirectory: mock_dict_dir = {'TEST_ARTIFACT': 'artifacts/contracts/TestArtifact.json', 'ZAPCOORDINATOR': 'artifacts/contracts/ZapCoordinator.json'} - @patch('base_contract.Utils.get_artifacts') - @patch('base_contract.Utils.open_artifact_in_dir') + @patch('Utils.get_artifacts') + @patch('Utils.open_artifact_in_dir') @pytest.mark.parametrize( 'art_input, net_id_input, address_output, coor_address_output', [ ('TEST_ARTIFACT', 1, '0xmainnet', '0xcoormainnet'), ('TEST_ARTIFACT', 42, '0xkovan', '0xcoorkovan'), @@ -235,7 +255,7 @@ def test_artifact_instance_from_artifacts_directory_arg(self, mock_utils_abi, mo mock_artifact_dir.return_value = self.mock_dict_dir mock_utils_abi.side_effect = [self.mock_test_abi, self.mock_coor_abi] - instance = base_contract.BaseContract(artifact_name=art_input, artifacts_dir='some/path/', + instance = BaseContract(artifact_name=art_input, artifacts_dir='some/path/', network_id=net_id_input) assert type(instance.artifact) == dict @@ -264,98 +284,246 @@ def test_artifact_instance_from_artifacts_directory_arg(self, mock_utils_abi, mo assert instance.coor_artifact['abi']['network'] -@patch.dict('base_contract.Artifacts', mock_abi) -@patch('base_contract.Web3', autospec=True) +@patch.dict('Artifacts', mock_abi) +@patch('Web3', autospec=True) class TestContracts: """Side effect function for checking args and kwargs passed""" def capture_args(self, *args, **kwargs) -> any: return args, kwargs - @patch('base_contract.BaseContract.get_contract') - def test_coordinator_contract_if_no_coor_provided(self, mock_get_contract, mock_Web3): + @patch('BaseContract.get_contract') + @pytest.mark.parametrize('net_id', [1, 42, 31337]) + def test_coordinator_contract_args_with_coordinator_address_provided(self, mock_get_contract, mock_Web3, net_id): """ - Testing that the coordinator contract object is assigned. + Testing that the coordinator contract object is assigned with the correct args. """ mock_Web3.return_value = MagicMock() w3 = mock_Web3() w3.toChecksumAddress.side_effect = self.capture_args w3.eth.contract.side_effect = self.capture_args + """Mocking the get_contract function so the contract instance runs without error""" mock_get_contract = MagicMock() - instance = base_contract.BaseContract(artifact_name='TEST_ARTIFACT', coordinator='0x_some_address') + instance = BaseContract(artifact_name='TEST_ARTIFACT', coordinator='0x_some_address', + network_id=net_id) assert type(instance.coordinator) == tuple assert instance.coordinator[1]['abi'] == [] + """ + Iterate through the returned tuple to find the passed address. Because of all the mocks, the returned + kwargs include a lot of unnecessary parenthesis hence the utilization of regexp. + """ + expected_address = '0x_some_address' - """Iterate through the returned tuple to find the passed address""" res = re.search(expected_address, str(instance.coordinator[1]['address'])) assert res is not None + """Testing for false positive""" + + with pytest.raises(AssertionError): + res = re.search('this_should_fail', str(instance.coordinator[1]['address'])) + assert res is not None - def test_coordinator_contract_with_coordinator_address(self, mock_Web3): + @pytest.mark.parametrize('input_id, expected_output', [(1, '0xcoormainnet'), (42, '0xcoorkovan'), + (31337, '0xcoordevnet')]) + def test_coordinator_contract_without_coordinator_address_provided(self, mock_Web3, input_id, expected_output): """ - Testing that the coordinator contract object is assigned. + Testing that the coordinator contract object is assigned with the correct args. """ mock_Web3.return_value = MagicMock() w3 = mock_Web3() - w3.eth.contract.return_value = w3._utils.datatypes.Contract + w3.toChecksumAddress.side_effect = self.capture_args + w3.eth.contract.side_effect = self.capture_args - instance = base_contract.BaseContract(artifact_name='TEST_ARTIFACT', coordinator='0x0102030405') + instance = BaseContract(artifact_name='TEST_ARTIFACT', network_id=input_id) """Testing that the coordinator instance was assigned""" - assert instance.coordinator + assert type(instance.coordinator) == tuple + assert instance.coordinator[1]['abi'] == [] + + res = re.search(expected_output, str(instance.coordinator[1]['address'])) + assert res is not None + with pytest.raises(AssertionError): + res = re.search('this_should_fail', str(instance.coordinator[1]['address'])) + assert res is not None + @patch('BaseContract.get_contract') + def test_contract_instance_with_coor_through_get_contract_method(self, mock_get_contract, mock_Web3): - def test_contract_instance_if_no_coor_provided(self, mock_Web3): """ - Testing that the contract instance object is assigned. + Testing that the return value from get_contract passes through to the contract instance object. """ + + test_address = '0x_some_address' + mock_Web3.return_value = MagicMock() w3 = mock_Web3() - w3.eth.contract.return_value = w3._utils.datatypes.Contract + w3.toChecksumAddress.side_effect = self.capture_args + w3.eth.contract.side_effect = self.capture_args + + """Mocking the get_contract function to return the expected value""" + + mock_get_contract.return_value = test_address - instance = base_contract.BaseContract(artifact_name='TEST_ARTIFACT') - assert instance.contract + instance = BaseContract(artifact_name='TEST_ARTIFACT', coordinator=test_address) + """Actual test that iterates through the returned arguments from the web3 side effect""" -@patch.dict('base_contract.Artifacts', mock_abi) -@patch('base_contract.Web3', autospec=True) + res = re.search(test_address, str(instance.contract[1]['address'])) + assert res is not None + + @pytest.mark.parametrize('input_id, expected_address', [(1, '0xmainnet'), (42, '0xkovan'), (31337, '0xdevnet')]) + def test_contract_instance_if_no_coor_provided(self, mock_Web3, input_id, expected_address): + """ + Testing that the contract instance object is assigned with the correct args using the mock_abi with different + networks. + """ + mock_Web3.return_value = MagicMock() + w3 = mock_Web3() + w3.toChecksumAddress.side_effect = self.capture_args + w3.eth.contract.side_effect = self.capture_args + + instance = BaseContract(artifact_name='TEST_ARTIFACT', network_id=input_id) + + assert type(instance.contract) == tuple + assert instance.contract[1]['abi'] == [] + + expected_address = expected_address + res = re.search(expected_address, str(instance.contract[1]['address'])) + assert res is not None + + with pytest.raises(AssertionError): + res = re.search('this_will_fail', str(instance.contract[1]['address'])) + assert res is not None + + +@patch.dict('Artifacts', mock_abi) +@patch('Web3', autospec=True) class TestMethods: + """Side effect function for checking args and kwargs passed""" - def mock_get_contract(self): - return '0xcoormainnet' + def capture_args(self, *args, **kwargs) -> any: + return args, kwargs - @patch('base_contract.asyncio') - @patch('base_contract.BaseContract._get_contract') - def test_get_contract(self, mock_async_get, mock_asyncio, mock_Web3): + @patch('BaseContract._get_contract') + def test_sync_get_contract(self, mock_async_get_contract, mock_Web3): """ - Testing that the get_contract function returns the proper values. + Testing that the get_contract function returns the proper values from the _get_contract async function. """ mock_Web3.return_value = MagicMock() w3 = mock_Web3() - w3.eth.contract.return_value = w3._utils.datatypes.Contract + w3.eth.contract.side_effect = self.capture_args + w3.toChecksumAddress.side_effect = self.capture_args + + """ + Mock the async _get_contract function and give it a return value to check that the get_contract + wrapper returns the proper value. + """ + mock_async_get_contract.return_value = 'test_get_contract_address' + + instance = BaseContract(artifact_name='ARBITER', coordinator='some_address') + + assert type(instance.get_contract()) == str + assert instance.get_contract() == 'test_get_contract_address' + + """Test false positive""" + + with pytest.raises(AssertionError): + assert instance.get_contract() == 'this_should_fail' + + + @patch('BaseContract._get_contract_owner') + def test_sync_get_contract_owner(self, mock_async_owner, mock_Web3): + """ + Testing that the get_contract_owner returns the proper values from the async _get_contract_owner function. + """ + mock_Web3.return_value = MagicMock() + + """ + Mock the async _get_contract_owner function and give it a return value to check that the get_contract_owner + wrapper returns the proper value. + """ + mock_async_owner.return_value = 'test_owner_address' + + instance = BaseContract(artifact_name='TEST_ARTIFACT') + + assert type(instance.get_contract_owner()) == str + assert instance.get_contract_owner() == 'test_owner_address' - """Mock the async function call""" - mock_async_get.return_value = AsyncMock() +@pytest.fixture +def anyio_backend(): + """ Ensures anyio uses the default, pytest-asyncio plugin + for running async tests + """ + return 'asyncio' +@patch.dict('Artifacts', mock_abi) - """Mock asyncio""" - mock_asyncio.return_value = MagicMock() - m_asyncio = mock_asyncio() +@patch('Web3', autospec=True) +class TestAsyncs: - instance = base_contract.BaseContract(artifact_name='TEST_ARTIFACT', coordinator='0xcoormainnet') + def capture_args(self, *args, **kwargs) -> any: + """Side effect function for checking args and kwargs passed""" + return args, kwargs + + + def mock_owner_abi(self, *args, **kwargs): + """ + A mocked abi for the async _get_contract_owner to fetch from without being read by web3--hence it being + a class and not a dictionary. + """ + return_val = 'some_string' + + class Irrelevant: + class functions: + class owner: + def call(self): + return return_val + return Irrelevant + + def test_get_contract_owner_type(self, mock_Web3): + mock_Web3.return_value = MagicMock() + w3 = mock_Web3() + w3.eth.contract.side_effect = self.capture_args + w3.toChecksumAddress.side_effect = self.capture_args - # FINISH THIS TEST!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + """Create instance to allow the coordinator attribute to be visible""" + instance = BaseContract(artifact_name='TEST_ARTIFACT') + async_owner = instance._get_contract_owner() + isinstance(async_owner, type(object)) + assert asyncio.iscoroutine(async_owner) is True + + @pytest.mark.anyio + async def test_get_contract_type(self, mock_Web3): + mock_Web3.return_value = MagicMock() + + instance = BaseContract(artifact_name='TEST_ARTIFACT') + async_contract = await instance._get_contract() + + isinstance(async_contract, type(object)) + assert asyncio.iscoroutine(async_contract) is True + + + + + + def test_async_get_contract_owner(self, mock_Web3): + mock_Web3.return_value = MagicMock() + w3 = mock_Web3() + w3.eth.contract.side_effect = self.mock_owner_abi + w3.toChecksumAddress.side_effect = self.mock_owner_abi + instance = BaseContract(artifact_name='TEST_ARTIFACT') + assert instance.get_contract_owner() == 'some_string' From 944912d08373ee0c404a6a81dc311c86a21bd33e Mon Sep 17 00:00:00 2001 From: Andrew Date: Fri, 12 Mar 2021 23:46:51 -0500 Subject: [PATCH 05/10] Refactored imports, pytest runs from root --- src/artifacts/src/{tests => }/__init__.py | 0 src/artifacts/src/tests/test_artifacts.py | 34 --- src/base_contract/__init__.py | 2 + src/base_contract/base_contract.py | 12 +- src/base_contract/test/test_base_contract.py | 9 - src/portedFiles/default_tx_test.py | 9 - src/portedFiles/filter_test.py | 13 -- .../test => tests/base_contract}/__init__.py | 0 tests/base_contract/test_base_contract.py | 198 ++++++++---------- 9 files changed, 97 insertions(+), 180 deletions(-) rename src/artifacts/src/{tests => }/__init__.py (100%) delete mode 100644 src/artifacts/src/tests/test_artifacts.py create mode 100644 src/base_contract/__init__.py delete mode 100644 src/base_contract/test/test_base_contract.py delete mode 100644 src/portedFiles/default_tx_test.py delete mode 100644 src/portedFiles/filter_test.py rename {src/base_contract/test => tests/base_contract}/__init__.py (100%) diff --git a/src/artifacts/src/tests/__init__.py b/src/artifacts/src/__init__.py similarity index 100% rename from src/artifacts/src/tests/__init__.py rename to src/artifacts/src/__init__.py diff --git a/src/artifacts/src/tests/test_artifacts.py b/src/artifacts/src/tests/test_artifacts.py deleted file mode 100644 index c71d1c87..00000000 --- a/src/artifacts/src/tests/test_artifacts.py +++ /dev/null @@ -1,34 +0,0 @@ -import pytest -import index - -class TestIndex: - - @pytest.fixture - def artifacts_iterator(self): - for i in index.Artifacts: - artifacts = index.Artifacts[i] - return artifacts - - def test_fetch_artifacts_by_key(self, artifacts_iterator): - assert type(artifacts_iterator) == dict - - with pytest.raises(AssertionError): - assert type(artifacts_iterator) != dict - - - def test_fetch_arbiter_address(self): - arbiter_addr = index.Artifacts['ARBITER']['networks']['1']['address'] - assert arbiter_addr == '0x131e22ae3e90f0eeb1fb739eaa62ea0290c3fbe1' - - def test_fetch_zap_coor_kovan_address(self): - zap_coor_kovan_addr = index.Artifacts['ZAPCOORDINATOR']['networks']['42']['address'] - assert zap_coor_kovan_addr == '0xdbcac7c8bcca78fb05e96d0d2c68efb1c5922539' - - def test_fetch_registry_devnet_address(self): - registry_devnet_addr = index.Artifacts['REGISTRY']['networks']['31337']['address'] - assert registry_devnet_addr == '0xa513E6E4b8f2a923D98304ec87F64353C4D5C853' - - - def test_zap_coor_abi(self): - zap_get_contract = index.Artifacts['ZAPCOORDINATOR']['abi'] - assert type(zap_get_contract) == list \ No newline at end of file diff --git a/src/base_contract/__init__.py b/src/base_contract/__init__.py new file mode 100644 index 00000000..c54d3f94 --- /dev/null +++ b/src/base_contract/__init__.py @@ -0,0 +1,2 @@ +from web3 import Web3 +from src.artifacts.src.index import Artifacts \ No newline at end of file diff --git a/src/base_contract/base_contract.py b/src/base_contract/base_contract.py index 4c57e406..3c9eb0e8 100644 --- a/src/base_contract/base_contract.py +++ b/src/base_contract/base_contract.py @@ -1,10 +1,6 @@ from web3 import Web3 -import sys -#from os.path import join, realpath -#sys.path.insert(0, realpath(join(__file__, "../../../src/"))) -from artifacts.src.index import Artifacts -#from src import index -import utils +import src.artifacts.src.index as index +import src.base_contract.utils as utils import asyncio @@ -30,8 +26,8 @@ def __init__(self, ): try: if artifacts_dir is None: - self.artifact = Artifacts[artifact_name] - self.coor_artifact = Artifacts['ZAPCOORDINATOR'] + self.artifact = index.Artifacts[artifact_name] + self.coor_artifact = index.Artifacts['ZAPCOORDINATOR'] else: artifacts: any = utils.Utils.get_artifacts(artifacts_dir) self.artifact = utils.Utils.open_artifact_in_dir(artifacts[artifact_name]) diff --git a/src/base_contract/test/test_base_contract.py b/src/base_contract/test/test_base_contract.py deleted file mode 100644 index e32a0fbe..00000000 --- a/src/base_contract/test/test_base_contract.py +++ /dev/null @@ -1,9 +0,0 @@ -# from unittest import TestCase -# from unittest.mock import Mock, MagicMock, patch -# import base_contract -# import utils - - -# class test_base_contract(TestCase): -# pass - diff --git a/src/portedFiles/default_tx_test.py b/src/portedFiles/default_tx_test.py deleted file mode 100644 index 0aa58140..00000000 --- a/src/portedFiles/default_tx_test.py +++ /dev/null @@ -1,9 +0,0 @@ -from default_tx import DefaultTx -import unittest - -class DefaultTxTest(unittest.TestCase): - def test_instance(self): - defaultTx = DefaultTx("5555555555", 5, 51416566) - self.assertEqual(defaultTx.address, "5555555555") - self.assertEqual(defaultTx.gas, 5) - self.assertEqual(defaultTx.gasPrice, 51416566) diff --git a/src/portedFiles/filter_test.py b/src/portedFiles/filter_test.py deleted file mode 100644 index abdd15a6..00000000 --- a/src/portedFiles/filter_test.py +++ /dev/null @@ -1,13 +0,0 @@ -from filter import Filter -import unittest - -class TestFilter(unittest.TestCase): - def test_instance(self): - filterTest = Filter(11, 22, "123123123", "321321321", "221221221", "11111", 123) - self.assertEqual(filterTest.fromBlock, 11) - self.assertEqual(filterTest.toBlock, 22) - self.assertEqual(filterTest.provider, "123123123") - self.assertEqual(filterTest.subscriber, "321321321") - self.assertEqual(filterTest.terminator, "221221221") - self.assertEqual(filterTest.endpoint, "11111") - self.assertEqual(filterTest.id, 123) diff --git a/src/base_contract/test/__init__.py b/tests/base_contract/__init__.py similarity index 100% rename from src/base_contract/test/__init__.py rename to tests/base_contract/__init__.py diff --git a/tests/base_contract/test_base_contract.py b/tests/base_contract/test_base_contract.py index c3c7225c..2ac38a89 100644 --- a/tests/base_contract/test_base_contract.py +++ b/tests/base_contract/test_base_contract.py @@ -1,5 +1,6 @@ from unittest.mock import MagicMock, patch, AsyncMock, PropertyMock import pytest +import pytest_mock import unittest from unittest import mock from anyio import run @@ -7,14 +8,7 @@ import sys import re import asyncio -from os.path import join, realpath -sys.path.insert(0, realpath(join(__file__, "../../../src/"))) - -from base_contract import BaseContract, Artifacts, Web3 - - - -print(sys.path[0]) +import src.base_contract.base_contract as base_contract mock_abi = { 'TEST_ARTIFACT': {'abi': [], 'networks': @@ -27,31 +21,26 @@ '31337': {'address': '0xcoordevnet'}}} } -@pytest.fixture -@patch('Web3', autospec=True) -def mock_web3(mock_Web3): - mock_Web3.return_value = MagicMock() - w3 = mock_Web3() - w3.eth.contract.return_value = MagicMock() +artifacts_dict = base_contract.index.Artifacts +mocked_web3 = 'src.base_contract.Web3' - -@patch.dict('Artifacts', mock_abi) -@patch('Web3', autospec=True) +@patch.dict(artifacts_dict, mock_abi, clear=True) +@patch(mocked_web3, autospec=True) class TestInit: - def test_instance_name(self, mock_web3): + def test_instance_name(self, mock_Web3): """ Sanity check to ensure the instance runs without errors while mocking web3 :param mock_Web3: patched web3. """ - #mock_Web3.return_value = MagicMock() - #w3 = mock_Web3() - #w3.eth.contract.return_value = MagicMock() + mock_Web3.return_value = MagicMock() + w3 = mock_Web3() + w3.eth.contract.side_effect = MagicMock() - instance = BaseContract(artifact_name='TEST_ARTIFACT') + instance = base_contract.BaseContract(artifact_name='TEST_ARTIFACT', web3=w3) assert type(instance.name) == str assert instance.name == 'TEST_ARTIFACT' @@ -69,10 +58,9 @@ def test_name_without_arg(self, mock_Web3): w3.eth.contract.return_value = w3._utils.datatypes.Contract with pytest.raises(KeyError): - instance = BaseContract(artifact_name='') + instance = base_contract.BaseContract(artifact_name='', web3=w3) assert instance - def test_network_id_default(self, mock_Web3): """ Testing that the default network (1) is assigned to the contract instance. @@ -82,7 +70,7 @@ def test_network_id_default(self, mock_Web3): w3 = mock_Web3() w3.eth.contract.return_value = w3._utils.datatypes.Contract - instance = BaseContract(artifact_name='TEST_ARTIFACT') + instance = base_contract.BaseContract(artifact_name='TEST_ARTIFACT', web3=w3) assert type(instance.network_id) == int assert instance.network_id == 1 @@ -102,7 +90,7 @@ def test_assigned_network_ids(self, mock_Web3, input): mock_Web3.return_value = MagicMock() w3 = mock_Web3() w3.eth.contract.return_value = w3._utils.datatypes.Contract - instance = BaseContract(artifact_name='TEST_ARTIFACT', network_id=input) + instance = base_contract.BaseContract(artifact_name='TEST_ARTIFACT', network_id=input, web3=w3) assert instance.network_id == input @@ -119,7 +107,7 @@ def test_network_id_should_fail_if_given_unknown_id(self, mock_Web3, wrong_net_i w3.eth.contract.return_value = w3._utils.datatypes.Contract with pytest.raises(KeyError): - instance = BaseContract(artifact_name='TEST_ARTIFACT', network_id=wrong_net_id) + instance = base_contract.BaseContract(artifact_name='TEST_ARTIFACT', network_id=wrong_net_id, web3=w3) assert instance def test_network_id_of_zero(self, mock_Web3): @@ -130,14 +118,14 @@ def test_network_id_of_zero(self, mock_Web3): w3 = mock_Web3() w3.eth.contract.return_value = w3._utils.datatypes.Contract - instance = BaseContract(artifact_name='TEST_ARTIFACT', network_id=0) + instance = base_contract.BaseContract(artifact_name='TEST_ARTIFACT', network_id=0, web3=w3) assert instance.network_id != 0 assert instance.network_id == 1 -@patch.dict('Artifacts', mock_abi) -@patch('Web3', autospec=True) +@patch.dict(artifacts_dict, mock_abi, clear=True) +@patch(mocked_web3, autospec=False) class TestAddress: def test_address_argument(self, mock_Web3): @@ -148,9 +136,9 @@ def test_address_argument(self, mock_Web3): """ mock_Web3.return_value = MagicMock() w3 = mock_Web3() - w3.eth.contract.return_value = w3._utils.datatypes.Contract + w3.eth.contract.return_value = MagicMock() - instance = BaseContract(artifact_name='TEST_ARTIFACT', address='0x_some_address') + instance = base_contract.BaseContract(artifact_name='TEST_ARTIFACT', address='0x_some_address', web3=w3) assert type(instance.address) == str assert instance.address == '0x_some_address' @@ -169,9 +157,9 @@ def test_address_with_no_argument(self, mock_Web3, network_input, address_output """ mock_Web3.return_value = MagicMock() w3 = mock_Web3() - w3.eth.contract.return_value = w3._utils.datatypes.Contract + w3.eth.contract.side_effect = MagicMock() - instance = BaseContract(artifact_name='TEST_ARTIFACT', network_id=network_input) + instance = base_contract.BaseContract(artifact_name='TEST_ARTIFACT', network_id=network_input, web3=w3) assert type(instance.address) == str assert instance.address == address_output @@ -180,8 +168,8 @@ def test_address_with_no_argument(self, mock_Web3, network_input, address_output assert instance.address == '0x_wrong_address' -@patch.dict('Artifacts', mock_abi) -@patch('Web3', autospec=True) +@patch.dict(artifacts_dict, mock_abi, clear=True) +@patch(mocked_web3, autospec=False) class TestArtifacts: def test_artifact_and_coor_artifact_assignments_from_dictionary(self, mock_Web3): @@ -195,7 +183,7 @@ def test_artifact_and_coor_artifact_assignments_from_dictionary(self, mock_Web3) w3 = mock_Web3() w3.eth.contract.return_value = w3._utils.datatypes.Contract - instance = BaseContract(artifact_name='TEST_ARTIFACT') + instance = base_contract.BaseContract(artifact_name='TEST_ARTIFACT', web3=w3) """Asserting the artifact attribute is equal to the mock dictionary""" @@ -215,7 +203,7 @@ def test_artifact_and_coor_artifact_assignments_from_dictionary(self, mock_Web3) assert instance.coor_artifact['networks']['1']['address'] == '0x123' -@patch('Web3', autospec=True) +@patch(mocked_web3, autospec=False) class TestArtifactsDirectory: mock_test_abi = {'abi': [], 'networks': {'1': {'address': '0xmainnet'}, '42': {'address': '0xkovan'}, '31337': {'address': '0xdevnet'}}} @@ -226,8 +214,8 @@ class TestArtifactsDirectory: mock_dict_dir = {'TEST_ARTIFACT': 'artifacts/contracts/TestArtifact.json', 'ZAPCOORDINATOR': 'artifacts/contracts/ZapCoordinator.json'} - @patch('Utils.get_artifacts') - @patch('Utils.open_artifact_in_dir') + @patch('src.base_contract.utils.Utils.get_artifacts') + @patch('src.base_contract.utils.Utils.open_artifact_in_dir') @pytest.mark.parametrize( 'art_input, net_id_input, address_output, coor_address_output', [ ('TEST_ARTIFACT', 1, '0xmainnet', '0xcoormainnet'), ('TEST_ARTIFACT', 42, '0xkovan', '0xcoorkovan'), @@ -255,8 +243,8 @@ def test_artifact_instance_from_artifacts_directory_arg(self, mock_utils_abi, mo mock_artifact_dir.return_value = self.mock_dict_dir mock_utils_abi.side_effect = [self.mock_test_abi, self.mock_coor_abi] - instance = BaseContract(artifact_name=art_input, artifacts_dir='some/path/', - network_id=net_id_input) + instance = base_contract.BaseContract(artifact_name=art_input, artifacts_dir='some/path/', + network_id=net_id_input, web3=w3) assert type(instance.artifact) == dict assert type(instance.artifact['abi']) == list @@ -284,15 +272,15 @@ def test_artifact_instance_from_artifacts_directory_arg(self, mock_utils_abi, mo assert instance.coor_artifact['abi']['network'] -@patch.dict('Artifacts', mock_abi) -@patch('Web3', autospec=True) +@patch.dict(artifacts_dict, mock_abi, clear=True) +@patch(mocked_web3, autospec=False) class TestContracts: """Side effect function for checking args and kwargs passed""" def capture_args(self, *args, **kwargs) -> any: return args, kwargs - @patch('BaseContract.get_contract') + @patch('src.base_contract.base_contract.BaseContract.get_contract') @pytest.mark.parametrize('net_id', [1, 42, 31337]) def test_coordinator_contract_args_with_coordinator_address_provided(self, mock_get_contract, mock_Web3, net_id): """ @@ -306,8 +294,8 @@ def test_coordinator_contract_args_with_coordinator_address_provided(self, mock_ """Mocking the get_contract function so the contract instance runs without error""" mock_get_contract = MagicMock() - instance = BaseContract(artifact_name='TEST_ARTIFACT', coordinator='0x_some_address', - network_id=net_id) + instance = base_contract.BaseContract(artifact_name='TEST_ARTIFACT', coordinator='0x_some_address', + network_id=net_id, web3=w3) assert type(instance.coordinator) == tuple assert instance.coordinator[1]['abi'] == [] @@ -340,7 +328,7 @@ def test_coordinator_contract_without_coordinator_address_provided(self, mock_We w3.toChecksumAddress.side_effect = self.capture_args w3.eth.contract.side_effect = self.capture_args - instance = BaseContract(artifact_name='TEST_ARTIFACT', network_id=input_id) + instance = base_contract.BaseContract(artifact_name='TEST_ARTIFACT', network_id=input_id, web3=w3) """Testing that the coordinator instance was assigned""" @@ -354,7 +342,7 @@ def test_coordinator_contract_without_coordinator_address_provided(self, mock_We res = re.search('this_should_fail', str(instance.coordinator[1]['address'])) assert res is not None - @patch('BaseContract.get_contract') + @patch('src.base_contract.base_contract.BaseContract.get_contract') def test_contract_instance_with_coor_through_get_contract_method(self, mock_get_contract, mock_Web3): """ @@ -372,7 +360,7 @@ def test_contract_instance_with_coor_through_get_contract_method(self, mock_get_ mock_get_contract.return_value = test_address - instance = BaseContract(artifact_name='TEST_ARTIFACT', coordinator=test_address) + instance = base_contract.BaseContract(artifact_name='TEST_ARTIFACT', coordinator=test_address, web3=w3) """Actual test that iterates through the returned arguments from the web3 side effect""" @@ -390,7 +378,7 @@ def test_contract_instance_if_no_coor_provided(self, mock_Web3, input_id, expect w3.toChecksumAddress.side_effect = self.capture_args w3.eth.contract.side_effect = self.capture_args - instance = BaseContract(artifact_name='TEST_ARTIFACT', network_id=input_id) + instance = base_contract.BaseContract(artifact_name='TEST_ARTIFACT', network_id=input_id, web3=w3) assert type(instance.contract) == tuple assert instance.contract[1]['abi'] == [] @@ -404,8 +392,8 @@ def test_contract_instance_if_no_coor_provided(self, mock_Web3, input_id, expect assert res is not None -@patch.dict('Artifacts', mock_abi) -@patch('Web3', autospec=True) +@patch.dict(artifacts_dict, mock_abi, clear=True) +@patch(mocked_web3, autospec=False) class TestMethods: """Side effect function for checking args and kwargs passed""" @@ -413,7 +401,7 @@ class TestMethods: def capture_args(self, *args, **kwargs) -> any: return args, kwargs - @patch('BaseContract._get_contract') + @patch('src.base_contract.base_contract.BaseContract._get_contract') def test_sync_get_contract(self, mock_async_get_contract, mock_Web3): """ Testing that the get_contract function returns the proper values from the _get_contract async function. @@ -429,7 +417,7 @@ def test_sync_get_contract(self, mock_async_get_contract, mock_Web3): """ mock_async_get_contract.return_value = 'test_get_contract_address' - instance = BaseContract(artifact_name='ARBITER', coordinator='some_address') + instance = base_contract.BaseContract(artifact_name='TEST_ARTIFACT', coordinator='some_address', web3=w3) assert type(instance.get_contract()) == str assert instance.get_contract() == 'test_get_contract_address' @@ -440,12 +428,15 @@ def test_sync_get_contract(self, mock_async_get_contract, mock_Web3): assert instance.get_contract() == 'this_should_fail' - @patch('BaseContract._get_contract_owner') + @patch('src.base_contract.base_contract.BaseContract._get_contract_owner') def test_sync_get_contract_owner(self, mock_async_owner, mock_Web3): """ Testing that the get_contract_owner returns the proper values from the async _get_contract_owner function. """ mock_Web3.return_value = MagicMock() + w3 = mock_Web3() + w3.eth.contract.side_effect = self.capture_args + w3.toChecksumAddress.side_effect = self.capture_args """ Mock the async _get_contract_owner function and give it a return value to check that the get_contract_owner @@ -453,90 +444,83 @@ def test_sync_get_contract_owner(self, mock_async_owner, mock_Web3): """ mock_async_owner.return_value = 'test_owner_address' - instance = BaseContract(artifact_name='TEST_ARTIFACT') + instance = base_contract.BaseContract(artifact_name='TEST_ARTIFACT', web3=w3) assert type(instance.get_contract_owner()) == str assert instance.get_contract_owner() == 'test_owner_address' -@pytest.fixture -def anyio_backend(): - """ Ensures anyio uses the default, pytest-asyncio plugin - for running async tests - """ - return 'asyncio' -@patch.dict('Artifacts', mock_abi) -@patch('Web3', autospec=True) + +@patch.dict(artifacts_dict, mock_abi, clear=True) +@patch(mocked_web3, autospec=False) class TestAsyncs: + """Side effect function for checking args and kwargs passed""" + def capture_args(self, *args, **kwargs) -> any: - """Side effect function for checking args and kwargs passed""" return args, kwargs - - def mock_owner_abi(self, *args, **kwargs): - """ - A mocked abi for the async _get_contract_owner to fetch from without being read by web3--hence it being - a class and not a dictionary. - """ - return_val = 'some_string' - - class Irrelevant: - class functions: - class owner: - def call(self): - return return_val - return Irrelevant - - def test_get_contract_owner_type(self, mock_Web3): + async def get_contract_owner(self, mock_Web3): mock_Web3.return_value = MagicMock() w3 = mock_Web3() w3.eth.contract.side_effect = self.capture_args w3.toChecksumAddress.side_effect = self.capture_args - """Create instance to allow the coordinator attribute to be visible""" - instance = BaseContract(artifact_name='TEST_ARTIFACT') - async_owner = instance._get_contract_owner() - isinstance(async_owner, type(object)) - assert asyncio.iscoroutine(async_owner) is True - - @pytest.mark.anyio - async def test_get_contract_type(self, mock_Web3): - mock_Web3.return_value = MagicMock() + instance = base_contract.BaseContract(artifact_name='TEST_ARTIFACT', web3=w3) + res = await instance._get_contract_owner() + return res - instance = BaseContract(artifact_name='TEST_ARTIFACT') - async_contract = await instance._get_contract() - isinstance(async_contract, type(object)) - assert asyncio.iscoroutine(async_contract) is True - - - - - - def test_async_get_contract_owner(self, mock_Web3): - mock_Web3.return_value = MagicMock() - w3 = mock_Web3() - w3.eth.contract.side_effect = self.mock_owner_abi - w3.toChecksumAddress.side_effect = self.mock_owner_abi - instance = BaseContract(artifact_name='TEST_ARTIFACT') - assert instance.get_contract_owner() == 'some_string' +#class TestAsyncs: +# def capture_args(self, *args, **kwargs) -> any: +# """Side effect function for checking args and kwargs passed""" +# return args, kwargs +# def mock_owner_abi(self, *args, **kwargs): +# """ +# A mocked abi for the async _get_contract_owner to fetch from without being read by web3--hence it being +# a class and not a dictionary. +# """ +# return_val = 'some_string' +# class Irrelevant: +# class functions: +# class owner: +# def call(self): +# return return_val +# return Irrelevant +# @pytest.mark.anyio +# async def test_get_contract_owner_type(self, mock_Web3): +# mock_Web3.return_value = MagicMock() +# w3 = mock_Web3() +# w3.eth.contract.side_effect = self.capture_args +# w3.toChecksumAddress.side_effect = self.capture_args +# """Create instance to allow the coordinator attribute to be visible""" +# instance = base_contract.BaseContract(artifact_name='TEST_ARTIFACT', web3=w3) +# async_owner = instance._get_contract_owner() +# isinstance(async_owner, type(object)) +# assert asyncio.iscoroutine(async_owner) is True +# @pytest.mark.anyio +# async def test_get_contract_type(self, mock_Web3): +# mock_Web3.return_value = MagicMock() +# instance = base_contract.BaseContract(artifact_name='TEST_ARTIFACT', web3=w3) +# async_contract = await instance._get_contract() +# isinstance(async_contract, type(object)) +# assert asyncio.iscoroutine(async_contract) is True From d48573b8a5fe8e97fab4e358de062dba13379894 Mon Sep 17 00:00:00 2001 From: Andrew Date: Sun, 14 Mar 2021 01:51:19 -0500 Subject: [PATCH 06/10] Fixed imports, finished testing base contract --- src/base_contract/base_contract.py | 14 +- tests/base_contract/test_base_contract.py | 376 ++++++++++------------ 2 files changed, 178 insertions(+), 212 deletions(-) diff --git a/src/base_contract/base_contract.py b/src/base_contract/base_contract.py index 3c9eb0e8..ef073a44 100644 --- a/src/base_contract/base_contract.py +++ b/src/base_contract/base_contract.py @@ -1,7 +1,8 @@ from web3 import Web3 +import asyncio + import src.artifacts.src.index as index import src.base_contract.utils as utils -import asyncio class BaseContract: @@ -21,7 +22,6 @@ def __init__(self, network_provider: any = None, artifacts_dir: str = None, coordinator: str = None, - contract: any = None, address: str = None ): try: @@ -60,8 +60,9 @@ def __init__(self, async def _get_contract(self) -> str: """ - This async function fetches the contract address from the coordinator and assigns the contract object instance - to the coordinator (within the context of the conditional statement of where it's located). Further, + This async function fetches the contract address from the coordinator abi. Thereafter, the function returns the + address where it is assigned to the address kwarg in the contract instance. + :return: the contract address of the coordinator. """ await asyncio.sleep(.5) @@ -80,9 +81,8 @@ async def _get_contract_owner(self) -> str: def get_contract(self) -> str: """ - A synchronous function that wraps the asynchronous _get_contract method. This provides flexibility. The - async function is used in the constructor to assign the coordinator address to the contract object; while in a - different context, a user can fetch the contract object's address. + A synchronous function that wraps the asynchronous _get_contract method. This function returns the contract + address fetched from the asynchronous _get_contract function. :return: the contract address. """ diff --git a/tests/base_contract/test_base_contract.py b/tests/base_contract/test_base_contract.py index 2ac38a89..f743badf 100644 --- a/tests/base_contract/test_base_contract.py +++ b/tests/base_contract/test_base_contract.py @@ -1,16 +1,20 @@ -from unittest.mock import MagicMock, patch, AsyncMock, PropertyMock +""" + This module tests the different possible branches of execution within the Zap base contract. + + The following unit tests require minimal dependencies. Because the base contract embodies the constructor role for + the entire Python interface of Zap (Zappy), the focus here lies in testing the instantiated dynamic attributes + of the base contract class. +""" + +from unittest.mock import MagicMock, patch import pytest -import pytest_mock -import unittest -from unittest import mock -from anyio import run -import anyio -import sys import re -import asyncio import src.base_contract.base_contract as base_contract -mock_abi = { + +"""Default setup""" + +MOCK_ABI = { 'TEST_ARTIFACT': {'abi': [], 'networks': {'1': {'address': '0xmainnet'}, '42': {'address': '0xkovan'}, @@ -21,26 +25,31 @@ '31337': {'address': '0xcoordevnet'}}} } +ARTIFACTS_DICT = base_contract.index.Artifacts +WEB3 = 'src.base_contract.Web3' -artifacts_dict = base_contract.index.Artifacts -mocked_web3 = 'src.base_contract.Web3' + +def capture_args(*args, **kwargs) -> any: + """Side effect function for checking args and kwargs passed""" + return args, kwargs -@patch.dict(artifacts_dict, mock_abi, clear=True) -@patch(mocked_web3, autospec=True) +@patch.dict(ARTIFACTS_DICT, MOCK_ABI, clear=True) +@patch(WEB3, autospec=True) class TestInit: - def test_instance_name(self, mock_Web3): - """ - Sanity check to ensure the instance runs without errors while mocking web3 + """Setup""" - :param mock_Web3: patched web3. - """ + with patch(WEB3) as mock_Web3: mock_Web3.return_value = MagicMock() w3 = mock_Web3() w3.eth.contract.side_effect = MagicMock() - instance = base_contract.BaseContract(artifact_name='TEST_ARTIFACT', web3=w3) + def test_instance_name(self, mock_Web3): + """ + Sanity check to ensure the instance runs without errors while mocking web3. + """ + instance = base_contract.BaseContract(artifact_name='TEST_ARTIFACT', web3=self.w3) assert type(instance.name) == str assert instance.name == 'TEST_ARTIFACT' @@ -53,24 +62,17 @@ def test_name_without_arg(self, mock_Web3): """ Testing that the contract fails if the artifact_name kwarg is an empty string. """ - mock_Web3.return_value = MagicMock() - w3 = mock_Web3() - w3.eth.contract.return_value = w3._utils.datatypes.Contract with pytest.raises(KeyError): - instance = base_contract.BaseContract(artifact_name='', web3=w3) + instance = base_contract.BaseContract(artifact_name='', web3=self.w3) assert instance def test_network_id_default(self, mock_Web3): """ Testing that the default network (1) is assigned to the contract instance. - :param mock_Web3: patched web3. """ - mock_Web3.return_value = MagicMock() - w3 = mock_Web3() - w3.eth.contract.return_value = w3._utils.datatypes.Contract - instance = base_contract.BaseContract(artifact_name='TEST_ARTIFACT', web3=w3) + instance = base_contract.BaseContract(artifact_name='TEST_ARTIFACT', web3=self.w3) assert type(instance.network_id) == int assert instance.network_id == 1 @@ -87,58 +89,52 @@ def test_assigned_network_ids(self, mock_Web3, input): :param mock_Web3: patched web3. :param input: the relevant networks' corresponding integer. """ - mock_Web3.return_value = MagicMock() - w3 = mock_Web3() - w3.eth.contract.return_value = w3._utils.datatypes.Contract - instance = base_contract.BaseContract(artifact_name='TEST_ARTIFACT', network_id=input, web3=w3) + + instance = base_contract.BaseContract(artifact_name='TEST_ARTIFACT', network_id=input, web3=self.w3) assert instance.network_id == input with pytest.raises(AssertionError): assert instance.network_id != input - @pytest.mark.parametrize('wrong_net_id', [11, 16, 47, 118893]) + @pytest.mark.parametrize('wrong_net_id', [11, 16, 47, 118893, 'string']) def test_network_id_should_fail_if_given_unknown_id(self, mock_Web3, wrong_net_id): """ - Testing that the contract should fail if given an unknown network id. + Testing that the contract fails if given an unknown network id. """ - mock_Web3.return_value = MagicMock() - w3 = mock_Web3() - w3.eth.contract.return_value = w3._utils.datatypes.Contract with pytest.raises(KeyError): - instance = base_contract.BaseContract(artifact_name='TEST_ARTIFACT', network_id=wrong_net_id, web3=w3) + instance = base_contract.BaseContract(artifact_name='TEST_ARTIFACT', network_id=wrong_net_id, web3=self.w3) assert instance def test_network_id_of_zero(self, mock_Web3): """ Testing that the network_id of zero actually uses the default network of '1.' """ - mock_Web3.return_value = MagicMock() - w3 = mock_Web3() - w3.eth.contract.return_value = w3._utils.datatypes.Contract - instance = base_contract.BaseContract(artifact_name='TEST_ARTIFACT', network_id=0, web3=w3) + instance = base_contract.BaseContract(artifact_name='TEST_ARTIFACT', network_id=0, web3=self.w3) assert instance.network_id != 0 assert instance.network_id == 1 -@patch.dict(artifacts_dict, mock_abi, clear=True) -@patch(mocked_web3, autospec=False) +@patch.dict(ARTIFACTS_DICT, MOCK_ABI, clear=True) +@patch(WEB3, autospec=False) class TestAddress: + """Setup""" + + with patch(WEB3) as mock_Web3: + mock_Web3.return_value = MagicMock() + w3 = mock_Web3() + w3.eth.contract.side_effect = MagicMock() + def test_address_argument(self, mock_Web3): """ Testing that the address argument is assigned as the instance address. - - :param mock_Web3: patched web3. """ - mock_Web3.return_value = MagicMock() - w3 = mock_Web3() - w3.eth.contract.return_value = MagicMock() - instance = base_contract.BaseContract(artifact_name='TEST_ARTIFACT', address='0x_some_address', web3=w3) + instance = base_contract.BaseContract(artifact_name='TEST_ARTIFACT', address='0x_some_address', web3=self.w3) assert type(instance.address) == str assert instance.address == '0x_some_address' @@ -153,13 +149,10 @@ def test_address_with_no_argument(self, mock_Web3, network_input, address_output :param mock_Web3: patched web3. :param network_input: relevant networks including mainnet, kovan, and devnet. - :param address_output: the associated address within the mock_abi dictionary. + :param address_output: the associated address within the MOCK_ABI dictionary. """ - mock_Web3.return_value = MagicMock() - w3 = mock_Web3() - w3.eth.contract.side_effect = MagicMock() - instance = base_contract.BaseContract(artifact_name='TEST_ARTIFACT', network_id=network_input, web3=w3) + instance = base_contract.BaseContract(artifact_name='TEST_ARTIFACT', network_id=network_input, web3=self.w3) assert type(instance.address) == str assert instance.address == address_output @@ -168,10 +161,17 @@ def test_address_with_no_argument(self, mock_Web3, network_input, address_output assert instance.address == '0x_wrong_address' -@patch.dict(artifacts_dict, mock_abi, clear=True) -@patch(mocked_web3, autospec=False) +@patch.dict(ARTIFACTS_DICT, MOCK_ABI, clear=True) +@patch(WEB3, autospec=True) class TestArtifacts: + """Setup""" + + with patch(WEB3) as mock_Web3: + mock_Web3.return_value = MagicMock() + w3 = mock_Web3() + w3.eth.contract.side_effect = MagicMock() + def test_artifact_and_coor_artifact_assignments_from_dictionary(self, mock_Web3): """ Testing that the artifact_name kwarg triggers the artifacts dictionary and populates the artifact @@ -179,13 +179,7 @@ def test_artifact_and_coor_artifact_assignments_from_dictionary(self, mock_Web3) :param mock_Web3: patched web3. """ - mock_Web3.return_value = MagicMock() - w3 = mock_Web3() - w3.eth.contract.return_value = w3._utils.datatypes.Contract - - instance = base_contract.BaseContract(artifact_name='TEST_ARTIFACT', web3=w3) - - """Asserting the artifact attribute is equal to the mock dictionary""" + instance = base_contract.BaseContract(artifact_name='TEST_ARTIFACT', web3=self.w3) assert type(instance.artifact) == dict assert instance.artifact['networks']['1']['address'] == '0xmainnet' @@ -193,8 +187,6 @@ def test_artifact_and_coor_artifact_assignments_from_dictionary(self, mock_Web3) assert type(instance.coor_artifact) == dict assert instance.coor_artifact['networks']['1']['address'] == '0xcoormainnet' - """Ensuring this includes no false positives""" - with pytest.raises(AssertionError): assert type(instance.artifact) != dict assert instance.artifact['networks']['1']['address'] == '0x123' @@ -203,8 +195,16 @@ def test_artifact_and_coor_artifact_assignments_from_dictionary(self, mock_Web3) assert instance.coor_artifact['networks']['1']['address'] == '0x123' -@patch(mocked_web3, autospec=False) +@patch(WEB3, autospec=False) class TestArtifactsDirectory: + + """Setup""" + + with patch(WEB3) as mock_Web3: + mock_Web3.return_value = MagicMock() + w3 = mock_Web3() + w3.eth.contract.side_effect = MagicMock() + mock_test_abi = {'abi': [], 'networks': {'1': {'address': '0xmainnet'}, '42': {'address': '0xkovan'}, '31337': {'address': '0xdevnet'}}} @@ -234,17 +234,12 @@ def test_artifact_instance_from_artifacts_directory_arg(self, mock_utils_abi, mo :param mock_Web3: patched web3. """ - """Mock web3""" - mock_Web3.return_value = MagicMock() - w3 = mock_Web3() - w3.eth.contract.return_value = w3._utils.datatypes.Contract - """Mock the relevant function returns""" mock_artifact_dir.return_value = self.mock_dict_dir mock_utils_abi.side_effect = [self.mock_test_abi, self.mock_coor_abi] instance = base_contract.BaseContract(artifact_name=art_input, artifacts_dir='some/path/', - network_id=net_id_input, web3=w3) + network_id=net_id_input, web3=self.w3) assert type(instance.artifact) == dict assert type(instance.artifact['abi']) == list @@ -272,37 +267,36 @@ def test_artifact_instance_from_artifacts_directory_arg(self, mock_utils_abi, mo assert instance.coor_artifact['abi']['network'] -@patch.dict(artifacts_dict, mock_abi, clear=True) -@patch(mocked_web3, autospec=False) +@patch.dict(ARTIFACTS_DICT, MOCK_ABI, clear=True) +@patch(WEB3, autospec=False) class TestContracts: - """Side effect function for checking args and kwargs passed""" - def capture_args(self, *args, **kwargs) -> any: - return args, kwargs + """Setup""" + + with patch(WEB3) as mock_Web3: + mock_Web3.return_value = MagicMock() + w3 = mock_Web3() + w3.toChecksumAddress.side_effect = capture_args + w3.eth.contract.side_effect = capture_args @patch('src.base_contract.base_contract.BaseContract.get_contract') @pytest.mark.parametrize('net_id', [1, 42, 31337]) def test_coordinator_contract_args_with_coordinator_address_provided(self, mock_get_contract, mock_Web3, net_id): """ - Testing that the coordinator contract object is assigned with the correct args. + Testing that the coordinator contract object is assigned with the correct args. This test first mocks the + get_contract function so the contract instance runs without errors. """ - mock_Web3.return_value = MagicMock() - w3 = mock_Web3() - w3.toChecksumAddress.side_effect = self.capture_args - w3.eth.contract.side_effect = self.capture_args - - """Mocking the get_contract function so the contract instance runs without error""" mock_get_contract = MagicMock() instance = base_contract.BaseContract(artifact_name='TEST_ARTIFACT', coordinator='0x_some_address', - network_id=net_id, web3=w3) + network_id=net_id, web3=self.w3) assert type(instance.coordinator) == tuple assert instance.coordinator[1]['abi'] == [] """ Iterate through the returned tuple to find the passed address. Because of all the mocks, the returned - kwargs include a lot of unnecessary parenthesis hence the utilization of regexp. + kwargs include a lot of unnecessary punctuation--hence the use of regexp. """ expected_address = '0x_some_address' @@ -310,8 +304,6 @@ def test_coordinator_contract_args_with_coordinator_address_provided(self, mock_ res = re.search(expected_address, str(instance.coordinator[1]['address'])) assert res is not None - """Testing for false positive""" - with pytest.raises(AssertionError): res = re.search('this_should_fail', str(instance.coordinator[1]['address'])) assert res is not None @@ -320,17 +312,10 @@ def test_coordinator_contract_args_with_coordinator_address_provided(self, mock_ (31337, '0xcoordevnet')]) def test_coordinator_contract_without_coordinator_address_provided(self, mock_Web3, input_id, expected_output): """ - Testing that the coordinator contract object is assigned with the correct args. + Testing that the coordinator contract object is assigned with the correct args. The first assertions test that + the coordinator instance was correctly assigned. """ - - mock_Web3.return_value = MagicMock() - w3 = mock_Web3() - w3.toChecksumAddress.side_effect = self.capture_args - w3.eth.contract.side_effect = self.capture_args - - instance = base_contract.BaseContract(artifact_name='TEST_ARTIFACT', network_id=input_id, web3=w3) - - """Testing that the coordinator instance was assigned""" + instance = base_contract.BaseContract(artifact_name='TEST_ARTIFACT', network_id=input_id, web3=self.w3) assert type(instance.coordinator) == tuple assert instance.coordinator[1]['abi'] == [] @@ -344,25 +329,14 @@ def test_coordinator_contract_without_coordinator_address_provided(self, mock_We @patch('src.base_contract.base_contract.BaseContract.get_contract') def test_contract_instance_with_coor_through_get_contract_method(self, mock_get_contract, mock_Web3): - """ - Testing that the return value from get_contract passes through to the contract instance object. + Testing that the return value from get_contract passes through to the contract instance object. This test + mocks the get_contract function to return the expected value. """ - test_address = '0x_some_address' - - mock_Web3.return_value = MagicMock() - w3 = mock_Web3() - w3.toChecksumAddress.side_effect = self.capture_args - w3.eth.contract.side_effect = self.capture_args - - """Mocking the get_contract function to return the expected value""" - mock_get_contract.return_value = test_address - instance = base_contract.BaseContract(artifact_name='TEST_ARTIFACT', coordinator=test_address, web3=w3) - - """Actual test that iterates through the returned arguments from the web3 side effect""" + instance = base_contract.BaseContract(artifact_name='TEST_ARTIFACT', coordinator=test_address, web3=self.w3) res = re.search(test_address, str(instance.contract[1]['address'])) assert res is not None @@ -370,15 +344,10 @@ def test_contract_instance_with_coor_through_get_contract_method(self, mock_get_ @pytest.mark.parametrize('input_id, expected_address', [(1, '0xmainnet'), (42, '0xkovan'), (31337, '0xdevnet')]) def test_contract_instance_if_no_coor_provided(self, mock_Web3, input_id, expected_address): """ - Testing that the contract instance object is assigned with the correct args using the mock_abi with different - networks. + Testing that the contract instance object is assigned with the correct args using the MOCK_ABI with + different networks. """ - mock_Web3.return_value = MagicMock() - w3 = mock_Web3() - w3.toChecksumAddress.side_effect = self.capture_args - w3.eth.contract.side_effect = self.capture_args - - instance = base_contract.BaseContract(artifact_name='TEST_ARTIFACT', network_id=input_id, web3=w3) + instance = base_contract.BaseContract(artifact_name='TEST_ARTIFACT', network_id=input_id, web3=self.w3) assert type(instance.contract) == tuple assert instance.contract[1]['abi'] == [] @@ -392,32 +361,28 @@ def test_contract_instance_if_no_coor_provided(self, mock_Web3, input_id, expect assert res is not None -@patch.dict(artifacts_dict, mock_abi, clear=True) -@patch(mocked_web3, autospec=False) -class TestMethods: +@patch.dict(ARTIFACTS_DICT, MOCK_ABI, clear=True) +@patch(WEB3, autospec=False) +class TestWrapperMethods: - """Side effect function for checking args and kwargs passed""" + """Setup""" - def capture_args(self, *args, **kwargs) -> any: - return args, kwargs - - @patch('src.base_contract.base_contract.BaseContract._get_contract') - def test_sync_get_contract(self, mock_async_get_contract, mock_Web3): - """ - Testing that the get_contract function returns the proper values from the _get_contract async function. - """ + with patch(WEB3) as mock_Web3: mock_Web3.return_value = MagicMock() w3 = mock_Web3() - w3.eth.contract.side_effect = self.capture_args - w3.toChecksumAddress.side_effect = self.capture_args + w3.toChecksumAddress.side_effect = capture_args + w3.eth.contract.side_effect = capture_args + @patch('src.base_contract.base_contract.BaseContract._get_contract') + def test_sync_get_contract(self, mock_async_get_contract, mock_Web3): """ - Mock the async _get_contract function and give it a return value to check that the get_contract + Testing that the get_contract function returns the proper values from the _get_contract async function. This + test mocks the async _get_contract function and gives it a return value to check that the get_contract wrapper returns the proper value. """ mock_async_get_contract.return_value = 'test_get_contract_address' - instance = base_contract.BaseContract(artifact_name='TEST_ARTIFACT', coordinator='some_address', web3=w3) + instance = base_contract.BaseContract(artifact_name='TEST_ARTIFACT', coordinator='some_address', web3=self.w3) assert type(instance.get_contract()) == str assert instance.get_contract() == 'test_get_contract_address' @@ -432,95 +397,96 @@ def test_sync_get_contract(self, mock_async_get_contract, mock_Web3): def test_sync_get_contract_owner(self, mock_async_owner, mock_Web3): """ Testing that the get_contract_owner returns the proper values from the async _get_contract_owner function. - """ - mock_Web3.return_value = MagicMock() - w3 = mock_Web3() - w3.eth.contract.side_effect = self.capture_args - w3.toChecksumAddress.side_effect = self.capture_args - - """ - Mock the async _get_contract_owner function and give it a return value to check that the get_contract_owner - wrapper returns the proper value. + The test mocks the async _get_contract_owner function and gives it a return value to check that the + get_contract_owner wrapper returns the proper value. """ mock_async_owner.return_value = 'test_owner_address' - instance = base_contract.BaseContract(artifact_name='TEST_ARTIFACT', web3=w3) + instance = base_contract.BaseContract(artifact_name='TEST_ARTIFACT', web3=self.w3) assert type(instance.get_contract_owner()) == str assert instance.get_contract_owner() == 'test_owner_address' + with pytest.raises(AssertionError): + assert instance.get_contract_owner() == 'this_should_fail' -@patch.dict(artifacts_dict, mock_abi, clear=True) -@patch(mocked_web3, autospec=False) -class TestAsyncs: - - - """Side effect function for checking args and kwargs passed""" - - def capture_args(self, *args, **kwargs) -> any: - return args, kwargs - - async def get_contract_owner(self, mock_Web3): - mock_Web3.return_value = MagicMock() - w3 = mock_Web3() - w3.eth.contract.side_effect = self.capture_args - w3.toChecksumAddress.side_effect = self.capture_args - - instance = base_contract.BaseContract(artifact_name='TEST_ARTIFACT', web3=w3) - res = await instance._get_contract_owner() - return res - - - - - +class TestAsyncMethods: + """ + The following asynchronous functions should not be called directly; rather, the wrapper function should be + called instead. The following unit tests require the somewhat hacky functions mock_owner_abi and mock_coor_abi + in order to ensure the async functions were called. The mock abi functions are returned as a side effect from + the web3.eth.contract call. While patching both Web3 and the artifacts dictionary, these functions proved + difficult to test (hence, the creative workaround). The Pythonic syntax of these side effects are, nevertheless, + exactly the same: + _get_contract_owner() = .functions.owner().call() + _get_contract() = .functions.getContract(self.name).call() + """ + """Setup""" + def mock_owner_abi(self, *args, **kwargs): + """Function mimicking abi""" + return_val = '0x_owner_address' + class Irrelevant: + class functions: + class owner: + def call(self): + return return_val + return Irrelevant -#class TestAsyncs: + def mock_coor_abi(self, *args, **kwargs): + """Function mimicking abi""" + return_val = '0x_artifact_address' -# def capture_args(self, *args, **kwargs) -> any: -# """Side effect function for checking args and kwargs passed""" -# return args, kwargs + class mimicked_coordinator: + class functions: + class getContract: + def __init__(self, name): + self.name = name + def call(self): + return return_val + return mimicked_coordinator -# def mock_owner_abi(self, *args, **kwargs): -# """ -# A mocked abi for the async _get_contract_owner to fetch from without being read by web3--hence it being -# a class and not a dictionary. -# """ -# return_val = 'some_string' + @pytest.mark.asyncio + async def test_async_get_contract_owner(self): + """ + Testing the asynchronous _get_contract_owner function. + """ + with patch(WEB3) as mock_Web3: + mock_Web3.return_value = MagicMock() + w3 = mock_Web3() + w3.eth.contract.side_effect = self.mock_owner_abi + w3.toChecksumAddress.side_effect = MagicMock() -# class Irrelevant: -# class functions: -# class owner: -# def call(self): -# return return_val -# return Irrelevant + with patch.dict(ARTIFACTS_DICT, MOCK_ABI, clear=True): -# @pytest.mark.anyio -# async def test_get_contract_owner_type(self, mock_Web3): -# mock_Web3.return_value = MagicMock() -# w3 = mock_Web3() -# w3.eth.contract.side_effect = self.capture_args -# w3.toChecksumAddress.side_effect = self.capture_args + instance = base_contract.BaseContract(artifact_name='TEST_ARTIFACT', web3=w3) + res = await instance._get_contract_owner() + assert res == '0x_owner_address' -# """Create instance to allow the coordinator attribute to be visible""" -# instance = base_contract.BaseContract(artifact_name='TEST_ARTIFACT', web3=w3) -# async_owner = instance._get_contract_owner() -# isinstance(async_owner, type(object)) -# assert asyncio.iscoroutine(async_owner) is True + with pytest.raises(AssertionError): + assert res == 'this_should_fail' -# @pytest.mark.anyio -# async def test_get_contract_type(self, mock_Web3): -# mock_Web3.return_value = MagicMock() + @pytest.mark.asyncio + async def test_async_get_contract(self): + """ + Testing the asynchronous _get_contract function. + """ + with patch(WEB3) as mock_Web3: + mock_Web3.return_value = MagicMock() + w3 = mock_Web3() + w3.eth.contract.side_effect = self.mock_coor_abi + w3.toChecksumAddress.side_effect = MagicMock() -# instance = base_contract.BaseContract(artifact_name='TEST_ARTIFACT', web3=w3) -# async_contract = await instance._get_contract() + with patch.dict(ARTIFACTS_DICT, MOCK_ABI, clear=True): -# isinstance(async_contract, type(object)) -# assert asyncio.iscoroutine(async_contract) is True + instance = base_contract.BaseContract(artifact_name='TEST_ARTIFACT', web3=w3) + res = await instance._get_contract() + assert res == '0x_artifact_address' + with pytest.raises(AssertionError): + assert res == 'this_should_fail' From 88627431e5ebecca0fa5f383317512b03d3387c1 Mon Sep 17 00:00:00 2001 From: Andrew Date: Sun, 14 Mar 2021 13:49:25 -0400 Subject: [PATCH 07/10] Refactored async decorators in test to use anyio --- tests/base_contract/test_base_contract.py | 52 ++++++++++++----------- 1 file changed, 28 insertions(+), 24 deletions(-) diff --git a/tests/base_contract/test_base_contract.py b/tests/base_contract/test_base_contract.py index f743badf..f1b42526 100644 --- a/tests/base_contract/test_base_contract.py +++ b/tests/base_contract/test_base_contract.py @@ -34,6 +34,26 @@ def capture_args(*args, **kwargs) -> any: return args, kwargs +@pytest.fixture +def anyio_backend(): + """ + Ensures anyio uses the default, pytest-asyncio plugin + for running async tests. + """ + return 'asyncio' + + +@pytest.fixture +@patch.dict(ARTIFACTS_DICT, MOCK_ABI, clear=True) +@patch(WEB3, autospec=True) +def instance(mock_Web3): + with patch(WEB3) as mock_Web3: + mock_Web3.return_value = MagicMock() + w3 = mock_Web3() + w3.eth.contract.side_effect = MagicMock() + return base_contract.BaseContract(artifact_name='TEST_ARTIFACT', web3=w3) + + @patch.dict(ARTIFACTS_DICT, MOCK_ABI, clear=True) @patch(WEB3, autospec=True) class TestInit: @@ -45,12 +65,10 @@ class TestInit: w3 = mock_Web3() w3.eth.contract.side_effect = MagicMock() - def test_instance_name(self, mock_Web3): + def test_instance_name(self, mock_Web3, instance): """ Sanity check to ensure the instance runs without errors while mocking web3. """ - instance = base_contract.BaseContract(artifact_name='TEST_ARTIFACT', web3=self.w3) - assert type(instance.name) == str assert instance.name == 'TEST_ARTIFACT' @@ -62,18 +80,14 @@ def test_name_without_arg(self, mock_Web3): """ Testing that the contract fails if the artifact_name kwarg is an empty string. """ - with pytest.raises(KeyError): instance = base_contract.BaseContract(artifact_name='', web3=self.w3) assert instance - def test_network_id_default(self, mock_Web3): + def test_network_id_default(self, mock_Web3, instance): """ Testing that the default network (1) is assigned to the contract instance. """ - - instance = base_contract.BaseContract(artifact_name='TEST_ARTIFACT', web3=self.w3) - assert type(instance.network_id) == int assert instance.network_id == 1 @@ -89,7 +103,6 @@ def test_assigned_network_ids(self, mock_Web3, input): :param mock_Web3: patched web3. :param input: the relevant networks' corresponding integer. """ - instance = base_contract.BaseContract(artifact_name='TEST_ARTIFACT', network_id=input, web3=self.w3) assert instance.network_id == input @@ -102,7 +115,6 @@ def test_network_id_should_fail_if_given_unknown_id(self, mock_Web3, wrong_net_i """ Testing that the contract fails if given an unknown network id. """ - with pytest.raises(KeyError): instance = base_contract.BaseContract(artifact_name='TEST_ARTIFACT', network_id=wrong_net_id, web3=self.w3) assert instance @@ -111,7 +123,6 @@ def test_network_id_of_zero(self, mock_Web3): """ Testing that the network_id of zero actually uses the default network of '1.' """ - instance = base_contract.BaseContract(artifact_name='TEST_ARTIFACT', network_id=0, web3=self.w3) assert instance.network_id != 0 @@ -133,7 +144,6 @@ def test_address_argument(self, mock_Web3): """ Testing that the address argument is assigned as the instance address. """ - instance = base_contract.BaseContract(artifact_name='TEST_ARTIFACT', address='0x_some_address', web3=self.w3) assert type(instance.address) == str @@ -151,7 +161,6 @@ def test_address_with_no_argument(self, mock_Web3, network_input, address_output :param network_input: relevant networks including mainnet, kovan, and devnet. :param address_output: the associated address within the MOCK_ABI dictionary. """ - instance = base_contract.BaseContract(artifact_name='TEST_ARTIFACT', network_id=network_input, web3=self.w3) assert type(instance.address) == str @@ -172,15 +181,13 @@ class TestArtifacts: w3 = mock_Web3() w3.eth.contract.side_effect = MagicMock() - def test_artifact_and_coor_artifact_assignments_from_dictionary(self, mock_Web3): + def test_artifact_and_coor_artifact_assignments_from_dictionary(self, mock_Web3, instance): """ Testing that the artifact_name kwarg triggers the artifacts dictionary and populates the artifact attribute with the controlled mock abi. :param mock_Web3: patched web3. """ - instance = base_contract.BaseContract(artifact_name='TEST_ARTIFACT', web3=self.w3) - assert type(instance.artifact) == dict assert instance.artifact['networks']['1']['address'] == '0xmainnet' @@ -394,7 +401,7 @@ def test_sync_get_contract(self, mock_async_get_contract, mock_Web3): @patch('src.base_contract.base_contract.BaseContract._get_contract_owner') - def test_sync_get_contract_owner(self, mock_async_owner, mock_Web3): + def test_sync_get_contract_owner(self, mock_async_owner, mock_Web3, instance): """ Testing that the get_contract_owner returns the proper values from the async _get_contract_owner function. The test mocks the async _get_contract_owner function and gives it a return value to check that the @@ -402,8 +409,6 @@ def test_sync_get_contract_owner(self, mock_async_owner, mock_Web3): """ mock_async_owner.return_value = 'test_owner_address' - instance = base_contract.BaseContract(artifact_name='TEST_ARTIFACT', web3=self.w3) - assert type(instance.get_contract_owner()) == str assert instance.get_contract_owner() == 'test_owner_address' @@ -429,12 +434,12 @@ class TestAsyncMethods: def mock_owner_abi(self, *args, **kwargs): """Function mimicking abi""" return_val = '0x_owner_address' - class Irrelevant: + class mimicked_abi: class functions: class owner: def call(self): return return_val - return Irrelevant + return mimicked_abi def mock_coor_abi(self, *args, **kwargs): @@ -450,8 +455,7 @@ def call(self): return return_val return mimicked_coordinator - - @pytest.mark.asyncio + @pytest.mark.anyio async def test_async_get_contract_owner(self): """ Testing the asynchronous _get_contract_owner function. @@ -471,7 +475,7 @@ async def test_async_get_contract_owner(self): with pytest.raises(AssertionError): assert res == 'this_should_fail' - @pytest.mark.asyncio + @pytest.mark.anyio async def test_async_get_contract(self): """ Testing the asynchronous _get_contract function. From 386c1dd64be72b39a15b98336bc4c9bfc7194830 Mon Sep 17 00:00:00 2001 From: Andrew Date: Sun, 14 Mar 2021 15:12:44 -0400 Subject: [PATCH 08/10] Added conftest --- tests/base_contract/conftest.py | 37 ++++++++++++ tests/base_contract/test_base_contract.py | 68 ++++++++--------------- 2 files changed, 60 insertions(+), 45 deletions(-) create mode 100644 tests/base_contract/conftest.py diff --git a/tests/base_contract/conftest.py b/tests/base_contract/conftest.py new file mode 100644 index 00000000..486720fd --- /dev/null +++ b/tests/base_contract/conftest.py @@ -0,0 +1,37 @@ +from unittest.mock import MagicMock, patch +import pytest +import re +import src.base_contract.base_contract as base_contract + +"""Default setup""" + +MOCK_ABI = { + 'TEST_ARTIFACT': {'abi': [], 'networks': + {'1': {'address': '0xmainnet'}, + '42': {'address': '0xkovan'}, + '31337': {'address': '0xdevnet'}}}, + 'ZAPCOORDINATOR': {'abi': [], 'networks': + {'1': {'address': '0xcoormainnet'}, + '42': {'address': '0xcoorkovan'}, + '31337': {'address': '0xcoordevnet'}}} +} + + +@pytest.fixture +def anyio_backend(): + """ + Ensures anyio uses the default, pytest-asyncio plugin + for running async tests. + """ + return 'asyncio' + + +@pytest.fixture +@patch.dict(ARTIFACTS_DICT, MOCK_ABI, clear=True) +@patch(WEB3, autospec=True) +def instance(mock_Web3): + with patch(WEB3) as mock_Web3: + mock_Web3.return_value = MagicMock() + w3 = mock_Web3() + w3.eth.contract.side_effect = MagicMock() + return base_contract.BaseContract(artifact_name='TEST_ARTIFACT', web3=w3) diff --git a/tests/base_contract/test_base_contract.py b/tests/base_contract/test_base_contract.py index f1b42526..8a93adcf 100644 --- a/tests/base_contract/test_base_contract.py +++ b/tests/base_contract/test_base_contract.py @@ -14,6 +14,7 @@ """Default setup""" + MOCK_ABI = { 'TEST_ARTIFACT': {'abi': [], 'networks': {'1': {'address': '0xmainnet'}, @@ -25,42 +26,18 @@ '31337': {'address': '0xcoordevnet'}}} } -ARTIFACTS_DICT = base_contract.index.Artifacts -WEB3 = 'src.base_contract.Web3' - - def capture_args(*args, **kwargs) -> any: """Side effect function for checking args and kwargs passed""" return args, kwargs -@pytest.fixture -def anyio_backend(): - """ - Ensures anyio uses the default, pytest-asyncio plugin - for running async tests. - """ - return 'asyncio' - - -@pytest.fixture -@patch.dict(ARTIFACTS_DICT, MOCK_ABI, clear=True) -@patch(WEB3, autospec=True) -def instance(mock_Web3): - with patch(WEB3) as mock_Web3: - mock_Web3.return_value = MagicMock() - w3 = mock_Web3() - w3.eth.contract.side_effect = MagicMock() - return base_contract.BaseContract(artifact_name='TEST_ARTIFACT', web3=w3) - - -@patch.dict(ARTIFACTS_DICT, MOCK_ABI, clear=True) -@patch(WEB3, autospec=True) +@patch.dict(base_contract.index.Artifacts, MOCK_ABI, clear=True) +@patch('src.base_contract.Web3', autospec=True) class TestInit: """Setup""" - with patch(WEB3) as mock_Web3: + with patch('src.base_contract.Web3') as mock_Web3: mock_Web3.return_value = MagicMock() w3 = mock_Web3() w3.eth.contract.side_effect = MagicMock() @@ -129,13 +106,13 @@ def test_network_id_of_zero(self, mock_Web3): assert instance.network_id == 1 -@patch.dict(ARTIFACTS_DICT, MOCK_ABI, clear=True) -@patch(WEB3, autospec=False) +@patch.dict(base_contract.index.Artifacts, MOCK_ABI, clear=True) +@patch('src.base_contract.Web3', autospec=False) class TestAddress: """Setup""" - with patch(WEB3) as mock_Web3: + with patch('src.base_contract.Web3') as mock_Web3: mock_Web3.return_value = MagicMock() w3 = mock_Web3() w3.eth.contract.side_effect = MagicMock() @@ -170,13 +147,13 @@ def test_address_with_no_argument(self, mock_Web3, network_input, address_output assert instance.address == '0x_wrong_address' -@patch.dict(ARTIFACTS_DICT, MOCK_ABI, clear=True) -@patch(WEB3, autospec=True) +@patch.dict(base_contract.index.Artifacts, MOCK_ABI, clear=True) +@patch('src.base_contract.Web3', autospec=True) class TestArtifacts: """Setup""" - with patch(WEB3) as mock_Web3: + with patch('src.base_contract.Web3') as mock_Web3: mock_Web3.return_value = MagicMock() w3 = mock_Web3() w3.eth.contract.side_effect = MagicMock() @@ -202,12 +179,12 @@ def test_artifact_and_coor_artifact_assignments_from_dictionary(self, mock_Web3, assert instance.coor_artifact['networks']['1']['address'] == '0x123' -@patch(WEB3, autospec=False) +@patch('src.base_contract.Web3', autospec=False) class TestArtifactsDirectory: """Setup""" - with patch(WEB3) as mock_Web3: + with patch('src.base_contract.Web3') as mock_Web3: mock_Web3.return_value = MagicMock() w3 = mock_Web3() w3.eth.contract.side_effect = MagicMock() @@ -274,13 +251,13 @@ def test_artifact_instance_from_artifacts_directory_arg(self, mock_utils_abi, mo assert instance.coor_artifact['abi']['network'] -@patch.dict(ARTIFACTS_DICT, MOCK_ABI, clear=True) -@patch(WEB3, autospec=False) +@patch.dict(base_contract.index.Artifacts, MOCK_ABI, clear=True) +@patch('src.base_contract.Web3', autospec=False) class TestContracts: """Setup""" - with patch(WEB3) as mock_Web3: + with patch('src.base_contract.Web3') as mock_Web3: mock_Web3.return_value = MagicMock() w3 = mock_Web3() w3.toChecksumAddress.side_effect = capture_args @@ -368,13 +345,13 @@ def test_contract_instance_if_no_coor_provided(self, mock_Web3, input_id, expect assert res is not None -@patch.dict(ARTIFACTS_DICT, MOCK_ABI, clear=True) -@patch(WEB3, autospec=False) +@patch.dict(base_contract.index.Artifacts, MOCK_ABI, clear=True) +@patch('src.base_contract.Web3', autospec=False) class TestWrapperMethods: """Setup""" - with patch(WEB3) as mock_Web3: + with patch('src.base_contract.Web3') as mock_Web3: mock_Web3.return_value = MagicMock() w3 = mock_Web3() w3.toChecksumAddress.side_effect = capture_args @@ -431,6 +408,7 @@ class TestAsyncMethods: """Setup""" + def mock_owner_abi(self, *args, **kwargs): """Function mimicking abi""" return_val = '0x_owner_address' @@ -460,13 +438,13 @@ async def test_async_get_contract_owner(self): """ Testing the asynchronous _get_contract_owner function. """ - with patch(WEB3) as mock_Web3: + with patch('src.base_contract.Web3') as mock_Web3: mock_Web3.return_value = MagicMock() w3 = mock_Web3() w3.eth.contract.side_effect = self.mock_owner_abi w3.toChecksumAddress.side_effect = MagicMock() - with patch.dict(ARTIFACTS_DICT, MOCK_ABI, clear=True): + with patch.dict(base_contract.index.Artifacts, MOCK_ABI, clear=True): instance = base_contract.BaseContract(artifact_name='TEST_ARTIFACT', web3=w3) res = await instance._get_contract_owner() @@ -480,13 +458,13 @@ async def test_async_get_contract(self): """ Testing the asynchronous _get_contract function. """ - with patch(WEB3) as mock_Web3: + with patch('src.base_contract.Web3') as mock_Web3: mock_Web3.return_value = MagicMock() w3 = mock_Web3() w3.eth.contract.side_effect = self.mock_coor_abi w3.toChecksumAddress.side_effect = MagicMock() - with patch.dict(ARTIFACTS_DICT, MOCK_ABI, clear=True): + with patch.dict(base_contract.index.Artifacts, MOCK_ABI, clear=True): instance = base_contract.BaseContract(artifact_name='TEST_ARTIFACT', web3=w3) res = await instance._get_contract() From e201456140f241ac0ab0caba81ba124115c77550 Mon Sep 17 00:00:00 2001 From: Andrew Date: Sun, 14 Mar 2021 19:51:46 -0400 Subject: [PATCH 09/10] Changed how web3 was patched, conftest also reflects these changes --- src/base_contract/base_contract.py | 2 +- tests/base_contract/conftest.py | 6 +++--- tests/base_contract/test_base_contract.py | 1 + 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/src/base_contract/base_contract.py b/src/base_contract/base_contract.py index ef073a44..07dbb55a 100644 --- a/src/base_contract/base_contract.py +++ b/src/base_contract/base_contract.py @@ -13,7 +13,7 @@ class BaseContract: coordinator: any artifact: any name: str - address: str = None + address: str or None def __init__(self, artifact_name: str, diff --git a/tests/base_contract/conftest.py b/tests/base_contract/conftest.py index 486720fd..2a0adcc5 100644 --- a/tests/base_contract/conftest.py +++ b/tests/base_contract/conftest.py @@ -27,10 +27,10 @@ def anyio_backend(): @pytest.fixture -@patch.dict(ARTIFACTS_DICT, MOCK_ABI, clear=True) -@patch(WEB3, autospec=True) +@patch.dict(base_contract.index.Artifacts, MOCK_ABI, clear=True) +@patch('src.base_contract.Web3', autospec=True) def instance(mock_Web3): - with patch(WEB3) as mock_Web3: + with patch('src.base_contract.Web3') as mock_Web3: mock_Web3.return_value = MagicMock() w3 = mock_Web3() w3.eth.contract.side_effect = MagicMock() diff --git a/tests/base_contract/test_base_contract.py b/tests/base_contract/test_base_contract.py index 8a93adcf..88c23e2d 100644 --- a/tests/base_contract/test_base_contract.py +++ b/tests/base_contract/test_base_contract.py @@ -26,6 +26,7 @@ '31337': {'address': '0xcoordevnet'}}} } + def capture_args(*args, **kwargs) -> any: """Side effect function for checking args and kwargs passed""" return args, kwargs From e06b76232b85c780869aba518e0e0451cb146569 Mon Sep 17 00:00:00 2001 From: Andrew Date: Tue, 16 Mar 2021 03:12:25 -0400 Subject: [PATCH 10/10] Updated comments in test --- tests/base_contract/test_base_contract.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/tests/base_contract/test_base_contract.py b/tests/base_contract/test_base_contract.py index 88c23e2d..9712f451 100644 --- a/tests/base_contract/test_base_contract.py +++ b/tests/base_contract/test_base_contract.py @@ -1,9 +1,11 @@ """ - This module tests the different possible branches of execution within the Zap base contract. + This module tests the different possible branches of execution within the Zap Base Contract. The following unit + tests require minimal dependencies. - The following unit tests require minimal dependencies. Because the base contract embodies the constructor role for - the entire Python interface of Zap (Zappy), the focus here lies in testing the instantiated dynamic attributes - of the base contract class. + The Base Contract is the parent class to the Dispatch, Bondage, Arbiter, Token, and Registry classes; further, it + provides access to both the contract instance as well as the Web3 provider instance. Because the majority of this + contract is a constructor, the focus here lies in testing the instantiated dynamic attributes of the base contract + class. """ from unittest.mock import MagicMock, patch