Skip to content

Commit

Permalink
Merge pull request #527 from lidofinance/increase-converage-middleware
Browse files Browse the repository at this point in the history
Increase middleware coverage
  • Loading branch information
F4ever authored Oct 3, 2024
2 parents 4e1e221 + 884b9de commit ec708b9
Show file tree
Hide file tree
Showing 2 changed files with 138 additions and 4 deletions.
4 changes: 2 additions & 2 deletions src/web3py/middleware.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ def metrics_collector(
with open(os.path.join(abi_dir, filename), 'r') as f:
try:
contracts.append(w3.eth.contract(abi=json.load(f)))
except json.JSONDecodeError:
except json.JSONDecodeError: # pragma: no cover
pass

def middleware(method: RPCEndpoint, params: Any) -> RPCResponse:
Expand All @@ -49,7 +49,7 @@ def middleware(method: RPCEndpoint, params: Any) -> RPCResponse:
for contract in contracts:
try:
call_method = contract.get_function_by_selector(args['data']).fn_name
except ValueError:
except ValueError: # pragma: no cover
pass
if call_method:
break
Expand Down
138 changes: 136 additions & 2 deletions tests/web3_extentions/test_middleware.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
from unittest.mock import call, Mock, patch, mock_open, MagicMock, ANY

import pytest
from requests import HTTPError
from web3 import Web3, HTTPProvider
from web3.exceptions import MethodUnavailable
from web3_multi_provider import NoActiveProviderError

from src.metrics.prometheus.basic import EL_REQUESTS_DURATION
from src.variables import EXECUTION_CLIENT_URI
Expand Down Expand Up @@ -35,7 +38,7 @@ def _get_requests_labels():
return labels


def test_success(provider, web3):
def test_success(web3):
web3.eth.get_block_number()
labels = _get_requests_labels()
assert labels == {
Expand All @@ -61,11 +64,142 @@ def test_fail_with_status_code(provider, web3):
}


def test_fail_with_body_error(provider, web3):
def test_fail_with_body_error(web3):
with pytest.raises((MethodUnavailable, ValueError)):
web3.eth.coinbase
labels = _get_requests_labels()
assert labels in [
{'call_method': '', 'call_to': '', 'code': '-32601', 'endpoint': 'eth_coinbase', 'le': '0.01'},
{'call_method': '', 'call_to': '', 'code': '-32000', 'endpoint': 'eth_coinbase', 'le': '0.01'},
]


class TestMetricsCollectorUnit:

@pytest.fixture
def mock_web3(self):
"""Mock Web3 instance."""
mock_w3 = MagicMock(spec=Web3)
mock_w3.provider = MagicMock()
mock_w3.eth = Mock()
mock_w3.provider.endpoint_uri = 'http://localhost:8545'
return mock_w3

@pytest.fixture
def mock_make_request(self):
"""Mock for make_request callable."""
return Mock()

@pytest.fixture
def mock_metrics_duration(self):
"""Mock for EL_REQUESTS_DURATION.histogram."""
with patch('src.web3py.middleware.EL_REQUESTS_DURATION') as mock_histogram:
mock_histogram.time.return_value.__enter__ = Mock(return_value=mock_histogram)
mock_histogram.time.return_value.__exit__ = Mock(return_value=False)
yield mock_histogram

@pytest.fixture
def load_abi_files(self):
"""Helper function to simulate loading ABI files from the assets directory."""
assets_dir = './assets/'
test_abi = '{"abi": [{"type": "function", "name": "testFunction", "inputs": []}]}'

with patch('os.listdir') as mock_listdir, patch('builtins.open', mock_open(read_data=test_abi)):
mock_listdir.return_value = ['test_contract.json']
yield

def test_metrics_collector_eth_call(self, mock_web3, mock_make_request, mock_metrics_duration, load_abi_files):
"""
Test the metrics collector for an `eth_call` method.
"""
middleware = metrics_collector(mock_make_request, mock_web3)

method = 'eth_call'
params = [{'to': '0x1234567890abcdef', 'data': '0xabcdef'}]
mock_make_request.return_value = {'result': 'success'}

response = middleware(method, params)

assert response == {'result': 'success'}
mock_make_request.assert_called_once_with(method, params)
assert mock_metrics_duration.time.called
assert mock_metrics_duration.labels.called
assert mock_metrics_duration.labels.call_args == call(
endpoint=method,
call_method=ANY,
call_to='0x1234567890abcdef',
code=0,
domain='localhost:8545',
)

def test_metrics_collector_eth_getBalance(
self, mock_web3, mock_make_request, mock_metrics_duration, load_abi_files
):
"""
Test the metrics collector for an `eth_getBalance` method.
"""
middleware = metrics_collector(mock_make_request, mock_web3)

method = 'eth_getBalance'
params = ['0x1234567890abcdef', 'latest']
mock_make_request.return_value = {'result': '1000'}

response = middleware(method, params)

assert response == {'result': '1000'}
mock_make_request.assert_called_once_with(method, params)
assert mock_metrics_duration.time.called
assert mock_metrics_duration.labels.called
assert mock_metrics_duration.labels.call_args == call(
endpoint=method,
call_method=ANY,
call_to='0x1234567890abcdef',
code=0,
domain='localhost:8545',
)

def test_metrics_collector_handle_no_provider(self, mock_web3, mock_make_request, mock_metrics_duration):
"""
Test that the metrics collector handles the NoActiveProviderError.
"""
middleware = metrics_collector(mock_make_request, mock_web3)

method = 'eth_call'
params = [{'to': '0x1234567890abcdef', 'data': '0xabcdef'}]
mock_make_request.side_effect = NoActiveProviderError

with pytest.raises(NoActiveProviderError):
middleware(method, params)

assert mock_metrics_duration.labels.called
assert mock_metrics_duration.labels.call_args == call(
endpoint=method,
call_method=ANY,
call_to='0x1234567890abcdef',
code=None,
domain='localhost:8545',
)

def test_metrics_collector_handle_http_error(self, mock_web3, mock_make_request, mock_metrics_duration):
"""
Test that the metrics collector handles HTTPError correctly.
"""
middleware = metrics_collector(mock_make_request, mock_web3)

method = 'eth_call'
params = [{'to': '0x1234567890abcdef', 'data': '0xabcdef'}]
mock_response = Mock(status_code=500)
mock_http_error = HTTPError(response=mock_response)
mock_make_request.side_effect = mock_http_error

with pytest.raises(HTTPError):
middleware(method, params)

assert mock_metrics_duration.labels.called
assert mock_metrics_duration.labels.call_args == call(
endpoint=method,
call_method=ANY,
call_to='0x1234567890abcdef',
code=500,
domain='localhost:8545',
)

0 comments on commit ec708b9

Please sign in to comment.