mirror of https://github.com/nucypher/nucypher.git
170 lines
4.9 KiB
Python
170 lines
4.9 KiB
Python
import datetime
|
|
from unittest.mock import Mock, PropertyMock
|
|
|
|
import pytest
|
|
from web3 import HTTPProvider
|
|
|
|
from nucypher.blockchain.eth.clients import EthereumClient
|
|
from nucypher.blockchain.eth.interfaces import BlockchainInterface
|
|
|
|
DEFAULT_GAS_PRICE = 42
|
|
GAS_PRICE_FROM_STRATEGY = 1234
|
|
CHAIN_ID = 11155111 # pretend to be sepolia
|
|
|
|
|
|
@pytest.mark.parametrize("chain_id_return_value", [hex(CHAIN_ID), CHAIN_ID])
|
|
def test_cached_chain_id(mocker, chain_id_return_value):
|
|
web3_mock = mocker.MagicMock()
|
|
chain_id_property_mock = PropertyMock(return_value=chain_id_return_value)
|
|
type(web3_mock.eth).chain_id = chain_id_property_mock
|
|
|
|
mock_client = EthereumClient(w3=web3_mock)
|
|
|
|
assert mock_client.chain_id == CHAIN_ID
|
|
chain_id_property_mock.assert_called_once()
|
|
|
|
assert mock_client.chain_id == CHAIN_ID
|
|
chain_id_property_mock.assert_called_once(), "not called again since cached"
|
|
|
|
# second instance of client, but uses the same w3 mock
|
|
mock_client_2 = EthereumClient(
|
|
w3=web3_mock,
|
|
)
|
|
assert mock_client_2.chain_id == CHAIN_ID
|
|
assert (
|
|
chain_id_property_mock.call_count == 2
|
|
), "additional call since different client instance"
|
|
|
|
assert mock_client_2.chain_id == CHAIN_ID
|
|
assert chain_id_property_mock.call_count == 2, "not called again since cached"
|
|
|
|
|
|
class MockGethProvider:
|
|
endpoint_uri = "http://192.168.9.0:8545"
|
|
client_version = "Geth/v1.4.11-stable-fed692f6/darwin/go1.7"
|
|
|
|
|
|
class SyncedMockW3Eth:
|
|
|
|
# Support older and newer versions of web3 py in-test
|
|
version = 5
|
|
chain_id = hex(CHAIN_ID)
|
|
block_number = 5
|
|
|
|
def get_block(self, blockNumber):
|
|
return {
|
|
"timestamp": datetime.datetime.timestamp(
|
|
datetime.datetime.now() - datetime.timedelta(seconds=25)
|
|
),
|
|
"number": 123456789,
|
|
}
|
|
|
|
|
|
#
|
|
# Mock Web3
|
|
#
|
|
|
|
class SyncedMockWeb3:
|
|
|
|
net = SyncedMockW3Eth()
|
|
eth = SyncedMockW3Eth()
|
|
|
|
def __init__(self, provider):
|
|
self.provider = provider
|
|
self.middleware_onion = Mock()
|
|
|
|
@property
|
|
def client_version(self):
|
|
return self.provider.client_version
|
|
|
|
@property
|
|
def is_connected(self):
|
|
return lambda: True
|
|
|
|
|
|
#
|
|
# Mock Blockchain
|
|
#
|
|
|
|
class BlockchainInterfaceTestBase(BlockchainInterface):
|
|
|
|
Web3 = SyncedMockWeb3
|
|
|
|
def _configure_registry(self, *args, **kwargs):
|
|
pass
|
|
|
|
def _setup_solidity(self, *args, **kwargs):
|
|
pass
|
|
|
|
def attach_middleware(self):
|
|
pass
|
|
|
|
|
|
class ProviderTypeTestClient(BlockchainInterfaceTestBase):
|
|
def __init__(self,
|
|
expected_provider_class,
|
|
actual_provider_to_attach,
|
|
*args,
|
|
**kwargs):
|
|
super().__init__(*args, **kwargs)
|
|
self.expected_provider_class = expected_provider_class
|
|
self.test_provider_to_attach = actual_provider_to_attach
|
|
|
|
def _attach_blockchain_provider(self, *args, **kwargs) -> None:
|
|
super()._attach_blockchain_provider(*args, **kwargs)
|
|
|
|
# check type
|
|
assert isinstance(self.provider, self.expected_provider_class)
|
|
|
|
super()._attach_blockchain_provider(provider=self.test_provider_to_attach)
|
|
|
|
|
|
class GethClientTestBlockchain(BlockchainInterfaceTestBase):
|
|
|
|
def _attach_blockchain_provider(self, *args, **kwargs) -> None:
|
|
super()._attach_blockchain_provider(provider=MockGethProvider())
|
|
|
|
|
|
def test_client_no_provider():
|
|
with pytest.raises(BlockchainInterface.NoProvider):
|
|
interface = BlockchainInterfaceTestBase()
|
|
interface.connect()
|
|
|
|
|
|
def test_geth_web3_client():
|
|
interface = GethClientTestBlockchain(endpoint="https://my.geth:8545")
|
|
interface.connect()
|
|
assert isinstance(interface.client, EthereumClient)
|
|
assert interface.client.chain_id == CHAIN_ID # Hardcoded above
|
|
|
|
|
|
def test_detect_provider_type_http():
|
|
interface = ProviderTypeTestClient(
|
|
endpoint="http://mynode.com:8445",
|
|
expected_provider_class=HTTPProvider,
|
|
actual_provider_to_attach=MockGethProvider(),
|
|
)
|
|
interface.connect()
|
|
assert isinstance(interface.client, EthereumClient)
|
|
|
|
|
|
def test_detect_provider_type_https():
|
|
interface = ProviderTypeTestClient(
|
|
endpoint="https://public-node.io:8445",
|
|
expected_provider_class=HTTPProvider,
|
|
actual_provider_to_attach=MockGethProvider,
|
|
)
|
|
interface.connect()
|
|
assert isinstance(interface.client, EthereumClient)
|
|
|
|
|
|
def test_gas_prices(mocker, mock_ethereum_client):
|
|
web3_mock = mock_ethereum_client.w3
|
|
|
|
web3_mock.eth.generate_gas_price = mocker.Mock(side_effect=[None, GAS_PRICE_FROM_STRATEGY])
|
|
type(web3_mock.eth).gas_price = PropertyMock(return_value=DEFAULT_GAS_PRICE) # See docs of PropertyMock
|
|
|
|
assert mock_ethereum_client.gas_price == DEFAULT_GAS_PRICE
|
|
assert mock_ethereum_client.gas_price_for_transaction("there's no gas strategy") == DEFAULT_GAS_PRICE
|
|
assert mock_ethereum_client.gas_price_for_transaction("2nd time is the charm") == GAS_PRICE_FROM_STRATEGY
|