Skip to content

Commit

Permalink
async more filter tests (#2732)
Browse files Browse the repository at this point in the history
* async existing filter test

* async test_filter_against_transaction_logs

* async test_filter_against_pending_transactions

* async test_contract_get_logs

* async test_contract_topic_filters

* async test_contract_past_event_filtering

* async test_contract_on_event_filtering

* async test_contract_data_filters
  • Loading branch information
pacrob authored and pacrob committed Dec 2, 2022
1 parent e1abe7e commit 8c454d6
Show file tree
Hide file tree
Showing 14 changed files with 1,061 additions and 209 deletions.
16 changes: 12 additions & 4 deletions tests/core/filtering/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -149,7 +149,7 @@ def emitter_log_topics():
return LogTopics


def return_filter(contract=None, args=[]):
def return_filter(contract, args):
event_name = args[0]
kwargs = apply_key_map({"filter": "argument_filters"}, args[1])
if "fromBlock" not in kwargs:
Expand Down Expand Up @@ -196,6 +196,14 @@ async def async_emitter(
)


@pytest.fixture(scope="module")
def async_create_filter(request):
return async_partial(return_filter)
async def async_return_filter(contract, args):
event_name = args[0]
kwargs = apply_key_map({"filter": "argument_filters"}, args[1])
if "fromBlock" not in kwargs:
kwargs["fromBlock"] = "latest"
return await contract.events[event_name].create_filter(**kwargs)


@pytest_asyncio.fixture(scope="module")
async def async_create_filter(request):
return async_partial(async_return_filter)
10 changes: 3 additions & 7 deletions tests/core/filtering/test_basic_filter_tests.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,5 @@
import pytest

from tests.core.filtering.utils import (
async_range,
)


def test_filtering_sequential_blocks_with_bounded_range(
w3, emitter, Emitter, wait_for_transaction
Expand Down Expand Up @@ -51,7 +47,7 @@ async def test_async_filtering_sequential_blocks_with_bounded_range(
initial_block_number = await async_w3.eth.block_number
builder.toBlock = initial_block_number + 100
filter_ = await builder.deploy(async_w3)
async for i in async_range(100):
for i in range(100):
await async_emitter.functions.logNoArgs(which=1).transact()
eth_block_number = await async_w3.eth.block_number
assert eth_block_number == initial_block_number + 100
Expand All @@ -61,12 +57,12 @@ async def test_async_filtering_sequential_blocks_with_bounded_range(

@pytest.mark.asyncio
async def test_async_filtering_starting_block_range(async_w3, async_emitter):
async for i in async_range(10):
for i in range(10):
await async_emitter.functions.logNoArgs(which=1).transact()
builder = async_emitter.events.LogNoArguments.build_filter()
filter_ = await builder.deploy(async_w3)
initial_block_number = await async_w3.eth.block_number
async for i in async_range(10):
for i in range(10):
await async_emitter.functions.logNoArgs(which=1).transact()
eth_block_number = await async_w3.eth.block_number
assert eth_block_number == initial_block_number + 10
Expand Down
292 changes: 252 additions & 40 deletions tests/core/filtering/test_contract_data_filters.py
Original file line number Diff line number Diff line change
@@ -1,37 +1,28 @@
import asyncio
import pytest

from hypothesis import (
given,
settings,
strategies as st,
)
import pytest_asyncio

from web3 import Web3
from tests.core.filtering.utils import (
_async_emitter_fixture_logic,
_async_w3_fixture_logic,
_emitter_fixture_logic,
_w3_fixture_logic,
)
from tests.utils import (
_async_wait_for_block_fixture_logic,
_async_wait_for_transaction_fixture_logic,
)
from web3._utils.module_testing.emitter_contract import (
CONTRACT_EMITTER_ABI,
CONTRACT_EMITTER_CODE,
CONTRACT_EMITTER_RUNTIME,
)
from web3.middleware import (
local_filter_middleware,
)
from web3.providers.eth_tester import (
EthereumTesterProvider,
)


@pytest.fixture(
scope="module",
params=[True, False],
ids=["local_filter_middleware", "node_based_filter"],
)
def w3(request):
use_filter_middleware = request.param
provider = EthereumTesterProvider()
w3 = Web3(provider)
if use_filter_middleware:
w3.middleware_onion.add(local_filter_middleware)
return w3


@pytest.fixture(scope="module")
Expand All @@ -58,25 +49,6 @@ def EMITTER(EMITTER_CODE, EMITTER_RUNTIME, EMITTER_ABI):
}


@pytest.fixture(scope="module")
def Emitter(w3, EMITTER):
return w3.eth.contract(**EMITTER)


@pytest.fixture(scope="module")
def emitter(w3, Emitter, wait_for_transaction, wait_for_block, address_conversion_func):
wait_for_block(w3)
deploy_txn_hash = Emitter.constructor().transact({"gas": 10000000})
deploy_receipt = wait_for_transaction(w3, deploy_txn_hash)
contract_address = address_conversion_func(deploy_receipt["contractAddress"])

bytecode = w3.eth.get_code(contract_address)
assert bytecode == Emitter.bytecode_runtime
_emitter = Emitter(address=contract_address)
assert _emitter.address == contract_address
return _emitter


def not_empty_string(x):
return x != ""

Expand Down Expand Up @@ -130,6 +102,30 @@ def array_values(draw):
return (matching, non_matching)


# --- sync --- #


@pytest.fixture(
scope="module",
params=[True, False],
ids=["local_filter_middleware", "node_based_filter"],
)
def w3(request):
return _w3_fixture_logic(request)


@pytest.fixture(scope="module")
def Emitter(w3, EMITTER):
return w3.eth.contract(**EMITTER)


@pytest.fixture(scope="module")
def emitter(w3, Emitter, wait_for_transaction, wait_for_block, address_conversion_func):
return _emitter_fixture_logic(
w3, Emitter, wait_for_transaction, wait_for_block, address_conversion_func
)


@pytest.mark.parametrize("api_style", ("v4", "build_filter"))
@given(vals=dynamic_values())
@settings(max_examples=5, deadline=None)
Expand Down Expand Up @@ -284,3 +280,219 @@ def test_data_filters_with_list_arguments(
else:
with pytest.raises(TypeError):
create_filter(emitter, ["LogListArgs", {"filter": {"arg1": matching}}])


# --- async --- #


@pytest_asyncio.fixture(scope="module")
async def async_wait_for_block():
return _async_wait_for_block_fixture_logic


@pytest_asyncio.fixture(scope="module")
async def async_wait_for_transaction():
return _async_wait_for_transaction_fixture_logic


@pytest.fixture(scope="module")
def event_loop():
policy = asyncio.get_event_loop_policy()
loop = policy.new_event_loop()
yield loop
loop.close()


@pytest.fixture(
scope="module",
params=[True, False],
ids=["local_filter_middleware", "node_based_filter"],
)
def async_w3(request):
return _async_w3_fixture_logic(request)


@pytest.fixture(scope="module")
def AsyncEmitter(async_w3, EMITTER):
return async_w3.eth.contract(**EMITTER)


@pytest_asyncio.fixture(scope="module")
async def async_emitter(
async_w3,
AsyncEmitter,
async_wait_for_transaction,
async_wait_for_block,
address_conversion_func,
):
return await _async_emitter_fixture_logic(
async_w3,
AsyncEmitter,
async_wait_for_transaction,
async_wait_for_block,
address_conversion_func,
)


@pytest.mark.asyncio
@pytest.mark.parametrize("api_style", ("v4", "build_filter"))
@given(vals=dynamic_values())
@settings(max_examples=5, deadline=None)
async def test_async_data_filters_with_dynamic_arguments(
async_w3,
async_wait_for_transaction,
async_create_filter,
async_emitter,
api_style,
vals,
):
if api_style == "build_filter":
filter_builder = async_emitter.events.LogDynamicArgs.build_filter()
filter_builder.args["arg1"].match_single(vals["matching"])
event_filter = await filter_builder.deploy(async_w3)
else:
event_filter = await async_create_filter(
async_emitter, ["LogDynamicArgs", {"filter": {"arg1": vals["matching"]}}]
)

txn_hashes = [
await async_emitter.functions.logDynamicArgs(
arg0=vals["matching"], arg1=vals["matching"]
).transact(
{"maxFeePerGas": 10**9, "maxPriorityFeePerGas": 10**9, "gas": 400000}
),
await async_emitter.functions.logDynamicArgs(
arg0=vals["non_matching"][0], arg1=vals["non_matching"][0]
).transact(
{"maxFeePerGas": 10**9, "maxPriorityFeePerGas": 10**9, "gas": 400000}
),
]

for txn_hash in txn_hashes:
await async_wait_for_transaction(async_w3, txn_hash)

log_entries = await event_filter.get_new_entries()
assert len(log_entries) == 1
assert log_entries[0]["transactionHash"] == txn_hashes[0]


@pytest.mark.asyncio
@pytest.mark.parametrize("api_style", ("v4", "build_filter"))
@given(vals=fixed_values())
@settings(max_examples=5, deadline=None)
async def test_async_data_filters_with_fixed_arguments(
async_w3,
async_emitter,
async_wait_for_transaction,
async_create_filter,
api_style,
vals,
):
if api_style == "build_filter":
filter_builder = async_emitter.events.LogQuadrupleArg.build_filter()
filter_builder.args["arg0"].match_single(vals["matching"][0])
filter_builder.args["arg1"].match_single(vals["matching"][1])
filter_builder.args["arg2"].match_single(vals["matching"][2])
filter_builder.args["arg3"].match_single(vals["matching"][3])
event_filter = await filter_builder.deploy(async_w3)
else:
event_filter = await async_create_filter(
async_emitter,
[
"LogQuadrupleArg",
{
"filter": {
"arg0": vals["matching"][0],
"arg1": vals["matching"][1],
"arg2": vals["matching"][2],
"arg3": vals["matching"][3],
}
},
],
)

txn_hashes = []
txn_hashes.append(
await async_emitter.functions.logQuadruple(
which=5,
arg0=vals["matching"][0],
arg1=vals["matching"][1],
arg2=vals["matching"][2],
arg3=vals["matching"][3],
).transact(
{"maxFeePerGas": 10**9, "maxPriorityFeePerGas": 10**9, "gas": 100000}
)
)
txn_hashes.append(
await async_emitter.functions.logQuadruple(
which=5,
arg0=vals["non_matching"][0],
arg1=vals["non_matching"][1],
arg2=vals["non_matching"][2],
arg3=vals["non_matching"][3],
).transact(
{"maxFeePerGas": 10**9, "maxPriorityFeePerGas": 10**9, "gas": 100000}
)
)

for txn_hash in txn_hashes:
await async_wait_for_transaction(async_w3, txn_hash)

log_entries = await event_filter.get_new_entries()
assert len(log_entries) == 1
assert log_entries[0]["transactionHash"] == txn_hashes[0]


@pytest.mark.asyncio
@pytest.mark.parametrize("api_style", ("v4", "build_filter"))
@given(vals=array_values())
@settings(max_examples=5, deadline=None)
async def test_async_data_filters_with_list_arguments(
async_w3,
async_emitter,
async_wait_for_transaction,
async_create_filter,
api_style,
vals,
):
matching, non_matching = vals

if api_style == "build_filter":
filter_builder = async_emitter.events.LogListArgs.build_filter()
filter_builder.args["arg1"].match_single(matching)
event_filter = await filter_builder.deploy(async_w3)

txn_hashes = []
txn_hashes.append(
await async_emitter.functions.logListArgs(
arg0=matching, arg1=matching
).transact({"maxFeePerGas": 10**9, "maxPriorityFeePerGas": 10**9})
)
txn_hashes.append(
await async_emitter.functions.logListArgs(
arg0=non_matching, arg1=non_matching
).transact({"maxFeePerGas": 10**9, "maxPriorityFeePerGas": 10**9})
)
txn_hashes.append(
await async_emitter.functions.logListArgs(
arg0=non_matching, arg1=matching
).transact({"maxFeePerGas": 10**9, "maxPriorityFeePerGas": 10**9})
)
txn_hashes.append(
await async_emitter.functions.logListArgs(
arg0=matching, arg1=non_matching
).transact({"maxFeePerGas": 10**9, "maxPriorityFeePerGas": 10**9})
)

for txn_hash in txn_hashes:
await async_wait_for_transaction(async_w3, txn_hash)

log_entries = await event_filter.get_new_entries()
assert len(log_entries) == 2
assert log_entries[0]["transactionHash"] == txn_hashes[0]
assert log_entries[1]["transactionHash"] == txn_hashes[2]
else:
with pytest.raises(TypeError):
await async_create_filter(
async_emitter, ["LogListArgs", {"filter": {"arg1": matching}}]
)
Loading

0 comments on commit 8c454d6

Please sign in to comment.