diff --git a/tests/conftest.py b/tests/conftest.py index 0ec0cfd..d300f0d 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -85,6 +85,16 @@ def machine(w3, strategies): _machine.stop() +@pytest.fixture +def broadcast_failure_hook(mocker): + return mocker.Mock() + + +@pytest.fixture +def fault_hook(mocker): + return mocker.Mock() + + @pytest.fixture def clock(machine): return machine._task.clock diff --git a/tests/test_api.py b/tests/test_api.py index f0489ac..726d331 100644 --- a/tests/test_api.py +++ b/tests/test_api.py @@ -12,12 +12,16 @@ def test_machine( machine, clock, mock_wake_sleep, + broadcast_failure_hook, + fault_hook, ): assert not machine.busy async_txs = machine.queue_transactions( params=[legacy_transaction, eip1559_transaction], signer=account, info={"message": "something wonderful is happening..."}, + on_broadcast_failure=broadcast_failure_hook, + on_fault=fault_hook, ) assert len(async_txs) == 2 diff --git a/tests/test_faults.py b/tests/test_faults.py index 3fe125b..b31f0e4 100644 --- a/tests/test_faults.py +++ b/tests/test_faults.py @@ -10,12 +10,13 @@ from atxm.utils import _get_receipt_from_txhash -def _broadcast_tx(machine, eip1559_transaction, account, mocker): - fault_hook = mocker.Mock() - +def _broadcast_tx( + machine, eip1559_transaction, account, broadcast_failure_hook, fault_hook +): atx = machine.queue_transaction( params=eip1559_transaction, signer=account, + on_broadcast_failure=broadcast_failure_hook, on_fault=fault_hook, ) @@ -26,7 +27,7 @@ def _broadcast_tx(machine, eip1559_transaction, account, mocker): assert atx.final is False assert atx.fault is None - return atx, fault_hook + return atx @pytest_twisted.inlineCallbacks @@ -58,9 +59,13 @@ def test_revert( account, interval, mock_wake_sleep, + broadcast_failure_hook, + fault_hook, mocker, ): - atx, fault_hook = _broadcast_tx(machine, eip1559_transaction, account, mocker) + atx = _broadcast_tx( + machine, eip1559_transaction, account, broadcast_failure_hook, fault_hook + ) assert machine.pending @@ -78,14 +83,25 @@ def test_revert( @pytest.mark.usefixtures("disable_auto_mining") def test_strategy_fault( - w3, machine, clock, eip1559_transaction, account, interval, mock_wake_sleep, mocker + w3, + machine, + clock, + eip1559_transaction, + account, + interval, + mock_wake_sleep, + broadcast_failure_hook, + fault_hook, + mocker, ): faulty_strategy = mocker.Mock(spec=AsyncTxStrategy) # TODO: consider whether strategies should just be overridden through the constructor machine._strategies.insert(0, faulty_strategy) # add first - atx, fault_hook = _broadcast_tx(machine, eip1559_transaction, account, mocker) + atx = _broadcast_tx( + machine, eip1559_transaction, account, broadcast_failure_hook, fault_hook + ) faulty_message = "mocked fault" faulty_strategy.execute.side_effect = TransactionFaulted( @@ -99,9 +115,19 @@ def test_strategy_fault( @pytest.mark.usefixtures("disable_auto_mining") def test_timeout_strategy_fault( - w3, machine, clock, eip1559_transaction, account, interval, mock_wake_sleep, mocker + w3, + machine, + clock, + eip1559_transaction, + account, + interval, + mock_wake_sleep, + broadcast_failure_hook, + fault_hook, ): - atx, fault_hook = _broadcast_tx(machine, eip1559_transaction, account, mocker) + atx = _broadcast_tx( + machine, eip1559_transaction, account, broadcast_failure_hook, fault_hook + ) atx.created -= 9999999999 diff --git a/tests/test_machine.py b/tests/test_machine.py index 3889050..f745815 100644 --- a/tests/test_machine.py +++ b/tests/test_machine.py @@ -55,6 +55,8 @@ def test_queue_from_parameter_handling( account, eip1559_transaction, mock_wake_sleep, + broadcast_failure_hook, + fault_hook, ): # 1. "from" parameter does not match account with pytest.raises(ValueError): @@ -70,6 +72,8 @@ def test_queue_from_parameter_handling( _ = machine.queue_transaction( params=tx_params, signer=account, + on_broadcast_failure=broadcast_failure_hook, + on_fault=fault_hook, ) # 2. no "from" parameter @@ -80,6 +84,8 @@ def test_queue_from_parameter_handling( atx = machine.queue_transaction( params=tx_params, signer=account, + on_broadcast_failure=broadcast_failure_hook, + on_fault=fault_hook, ) assert atx.params["from"] == account.address, "same as signer account" @@ -89,6 +95,8 @@ def test_queue_from_parameter_handling( atx = machine.queue_transaction( params=tx_params, signer=account, + on_broadcast_failure=broadcast_failure_hook, + on_fault=fault_hook, ) assert atx.params["from"] == account.address @@ -100,6 +108,8 @@ def test_queue( account, eip1559_transaction, mock_wake_sleep, + broadcast_failure_hook, + fault_hook, ): wake, _ = mock_wake_sleep @@ -112,6 +122,8 @@ def test_queue( atx = machine.queue_transaction( params=eip1559_transaction, signer=account, + on_broadcast_failure=broadcast_failure_hook, + on_fault=fault_hook, ) assert isinstance(atx, FutureTx) @@ -152,6 +164,8 @@ def test_wake_after_queuing_when_idle_and_not_already_running( params=eip1559_transaction, signer=account, info={"message": "something wonderful is happening..."}, + on_broadcast_failure=mocker.Mock(), + on_fault=mocker.Mock(), ) assert stop_spy.call_count == 0, "no task to stop" @@ -184,6 +198,8 @@ def test_wake_after_queuing_when_idle_and_already_running( params=eip1559_transaction, signer=account, info={"message": "something wonderful is happening..."}, + on_broadcast_failure=mocker.Mock(), + on_fault=mocker.Mock(), ) assert stop_spy.call_count == 1, "task stopped" @@ -200,6 +216,8 @@ def test_wake_no_call_after_queuing_when_already_busy( eip1559_transaction, account, mock_wake_sleep, + broadcast_failure_hook, + fault_hook, ): wake, _ = mock_wake_sleep @@ -210,6 +228,8 @@ def test_wake_no_call_after_queuing_when_already_busy( params=eip1559_transaction, signer=account, info={"message": "something wonderful is happening..."}, + on_broadcast_failure=broadcast_failure_hook, + on_fault=fault_hook, ) assert wake.call_count == 1 @@ -222,6 +242,8 @@ def test_wake_no_call_after_queuing_when_already_busy( params=eip1559_transaction, signer=account, info={"message": "something wonderful is happening..."}, + on_broadcast_failure=broadcast_failure_hook, + on_fault=fault_hook, ) assert wake.call_count == 1 # remains unchanged @@ -232,6 +254,8 @@ def test_wake_no_call_after_queuing_when_already_paused( eip1559_transaction, account, mock_wake_sleep, + broadcast_failure_hook, + fault_hook, ): wake, sleep = mock_wake_sleep @@ -248,6 +272,8 @@ def test_wake_no_call_after_queuing_when_already_paused( params=eip1559_transaction, signer=account, info={"message": "something wonderful is happening..."}, + on_broadcast_failure=broadcast_failure_hook, + on_fault=fault_hook, ) assert wake.call_count == 0 @@ -263,6 +289,8 @@ def test_broadcast( account, mocker, mock_wake_sleep, + broadcast_failure_hook, + fault_hook, ): wake, _ = mock_wake_sleep @@ -270,11 +298,13 @@ def test_broadcast( assert not machine.busy # Queue a transaction - hook = mocker.Mock() + broadcast_hook = mocker.Mock() atx = machine.queue_transaction( params=eip1559_transaction, signer=account, - on_broadcast=hook, + on_broadcast=broadcast_hook, + on_broadcast_failure=broadcast_failure_hook, + on_fault=fault_hook, info={"message": "something wonderful is happening..."}, ) @@ -301,7 +331,7 @@ def test_broadcast( # wait for the hook to be called yield deferLater(reactor, 0.2, lambda: None) - assert hook.call_count == 1 + assert broadcast_hook.call_count == 1 assert atx.retries == 0 @@ -327,6 +357,8 @@ def test_broadcast_non_recoverable_error( state_observer, eip1559_transaction, account, + broadcast_failure_hook, + fault_hook, mocker, mock_wake_sleep, ): @@ -336,13 +368,13 @@ def test_broadcast_non_recoverable_error( assert not machine.busy # Queue a transaction - broadcast_failure_hook = mocker.Mock() broadcast_hook = mocker.Mock() atx = machine.queue_transaction( params=eip1559_transaction, signer=account, on_broadcast=broadcast_hook, on_broadcast_failure=broadcast_failure_hook, + on_fault=fault_hook, info={"message": "something wonderful is happening..."}, ) @@ -357,15 +389,14 @@ def test_broadcast_non_recoverable_error( mocker.patch.object(w3.eth, "send_raw_transaction", side_effect=error) machine.start(now=True) - yield clock.advance(1) # wait for the hook to be called yield deferLater(reactor, 0.2, lambda: None) assert broadcast_failure_hook.call_count == 1 broadcast_failure_hook.assert_called_with(atx, error) - # The transaction failed and is not requeued - assert len(machine.queued) == 0 + # tx remains in queue + assert len(machine.queued) == 1 # run a few cycles for i in range(2): @@ -373,14 +404,13 @@ def test_broadcast_non_recoverable_error( assert broadcast_hook.call_count == 0 - assert atx.requeues == 0 + assert atx.retries == 0 # tx failed and not requeued - assert machine.current_state == machine._IDLE + assert machine.current_state == machine._BUSY - assert len(state_observer.transitions) == 2 + assert len(state_observer.transitions) == 1 assert state_observer.transitions[0] == (machine._IDLE, machine._BUSY) - assert state_observer.transitions[1] == (machine._BUSY, machine._IDLE) machine.stop() @@ -400,9 +430,11 @@ def test_broadcast_recoverable_error( account, mocker, mock_wake_sleep, + broadcast_failure_hook, + fault_hook, ): # need more freedom with redo attempts for test - mocker.patch.object(machine, "_MAX_REDO_ATTEMPTS", 10) + mocker.patch.object(machine, "_MAX_RETRY_ATTEMPTS", 10) wake, _ = mock_wake_sleep @@ -417,6 +449,7 @@ def test_broadcast_recoverable_error( signer=account, on_broadcast=broadcast_hook, on_broadcast_failure=broadcast_failure_hook, + on_fault=fault_hook, info={"message": "something wonderful is happening..."}, ) @@ -436,7 +469,7 @@ def test_broadcast_recoverable_error( for i in range(5): yield clock.advance(1) assert len(machine.queued) == 1 # remains in queue and not broadcasted - assert atx.requeues >= i + assert atx.retries >= i # call real method from now on mocker.patch.object(w3.eth, "send_raw_transaction", side_effect=real_method) @@ -474,7 +507,7 @@ def test_broadcast_recoverable_error( @pytest.mark.parametrize( "recoverable_error", [TooManyRequests, ProviderConnectionError, TimeExhausted] ) -def test_broadcast_recoverable_error_requeues_exceeded( +def test_broadcast_recoverable_error_retries_exceeded( recoverable_error, clock, w3, @@ -484,6 +517,8 @@ def test_broadcast_recoverable_error_requeues_exceeded( account, mocker, mock_wake_sleep, + broadcast_failure_hook, + fault_hook, ): wake, _ = mock_wake_sleep @@ -491,13 +526,13 @@ def test_broadcast_recoverable_error_requeues_exceeded( assert not machine.busy # Queue a transaction - broadcast_failure_hook = mocker.Mock() broadcast_hook = mocker.Mock() atx = machine.queue_transaction( params=eip1559_transaction, signer=account, on_broadcast=broadcast_hook, on_broadcast_failure=broadcast_failure_hook, + on_fault=fault_hook, info={"message": "something wonderful is happening..."}, ) @@ -511,13 +546,13 @@ def test_broadcast_recoverable_error_requeues_exceeded( assert _is_recoverable_send_tx_error(error) mocker.patch.object(w3.eth, "send_raw_transaction", side_effect=error) - # repeat some cycles; tx fails then gets requeued since error is "recoverable" + # repeat some cycles; tx fails then gets retried since error is "recoverable" machine.start(now=True) # one less than max attempts - for i in range(machine._MAX_REDO_ATTEMPTS - 1): + for i in range(machine._MAX_RETRY_ATTEMPTS - 1): assert len(machine.queued) == 1 # remains in queue and not broadcasted yield clock.advance(1) - assert atx.requeues >= i + assert atx.retries >= i # push over the retry limit yield clock.advance(1) @@ -527,23 +562,20 @@ def test_broadcast_recoverable_error_requeues_exceeded( assert broadcast_failure_hook.call_count == 1 broadcast_failure_hook.assert_called_with(atx, error) - assert atx.requeues == machine._MAX_REDO_ATTEMPTS + # The transaction failed but remains in the queue, unless the user does something + assert len(machine.queued) == 1 - # The transaction failed and is not requeued - assert len(machine.queued) == 0 + # retries are reset + assert atx.retries == 0 # run a few cycles - for i in range(2): + for i in range(machine._MAX_RETRY_ATTEMPTS - 1): yield clock.advance(1) assert broadcast_hook.call_count == 0 - # tx failed and not requeued - assert machine.current_state == machine._IDLE - - assert len(state_observer.transitions) == 2 + assert len(state_observer.transitions) == 1 assert state_observer.transitions[0] == (machine._IDLE, machine._BUSY) - assert state_observer.transitions[1] == (machine._BUSY, machine._IDLE) machine.stop() @@ -563,11 +595,13 @@ def test_finalize( assert machine.current_state == machine._IDLE # Queue a transaction - hook = mocker.Mock() + on_finalized_hook = mocker.Mock() atx = machine.queue_transaction( params=eip1559_transaction, signer=account, - on_finalized=hook, + on_finalized=on_finalized_hook, + on_broadcast_failure=mocker.Mock(), + on_fault=mocker.Mock(), ) # There is one queued transaction @@ -602,7 +636,7 @@ def test_finalize( # wait for the hook to be called yield deferLater(reactor, 0.2, lambda: None) - assert hook.call_count == 1 + assert on_finalized_hook.call_count == 1 yield clock.advance(1) @@ -625,6 +659,8 @@ def test_follow( eip1559_transaction, account, mock_wake_sleep, + broadcast_failure_hook, + fault_hook, ): machine.start() assert machine.current_state == machine._IDLE @@ -632,6 +668,8 @@ def test_follow( atx = machine.queue_transaction( params=eip1559_transaction, signer=account, + on_broadcast_failure=broadcast_failure_hook, + on_fault=fault_hook, ) # advance to broadcast the transaction @@ -688,6 +726,8 @@ def test_use_strategies_speedup_used( params=eip1559_transaction, signer=account, on_broadcast=broadcast_hook, + on_broadcast_failure=mocker.Mock(), + on_fault=mocker.Mock(), ) update_spy = mocker.spy(machine._tx_tracker, "update_after_retry") @@ -760,6 +800,8 @@ def test_use_strategies_timeout_used( account, mocker, mock_wake_sleep, + broadcast_failure_hook, + fault_hook, ): fault_hook = mocker.Mock() @@ -767,7 +809,10 @@ def test_use_strategies_timeout_used( assert machine.current_state == machine._IDLE atx = machine.queue_transaction( - params=eip1559_transaction, signer=account, on_fault=fault_hook + params=eip1559_transaction, + signer=account, + on_broadcast_failure=broadcast_failure_hook, + on_fault=fault_hook, ) # advance to broadcast the transaction @@ -837,7 +882,11 @@ def test_use_strategies_that_dont_make_updates( broadcast_hook = mocker.Mock() atx = machine.queue_transaction( - params=eip1559_transaction, signer=account, on_broadcast=broadcast_hook + params=eip1559_transaction, + signer=account, + on_broadcast=broadcast_hook, + on_broadcast_failure=mocker.Mock(), + on_fault=mocker.Mock(), ) # advance to broadcast the transaction @@ -912,9 +961,11 @@ def test_retry_with_errors_but_recovers( account, mocker, mock_wake_sleep, + broadcast_failure_hook, + fault_hook, ): # need more freedom with redo attempts for test - mocker.patch.object(machine, "_MAX_REDO_ATTEMPTS", 10) + mocker.patch.object(machine, "_MAX_RETRY_ATTEMPTS", 10) # strategies that don't make updates strategy_1 = mocker.Mock(spec=AsyncTxStrategy) @@ -930,12 +981,12 @@ def test_retry_with_errors_but_recovers( assert machine.current_state == machine._IDLE broadcast_hook = mocker.Mock() - fault_hook = mocker.Mock() atx = machine.queue_transaction( params=eip1559_transaction, signer=account, on_fault=fault_hook, on_broadcast=broadcast_hook, + on_broadcast_failure=broadcast_failure_hook, ) # advance to broadcast the transaction @@ -1023,6 +1074,8 @@ def test_retry_with_errors_retries_exceeded( account, mocker, mock_wake_sleep, + broadcast_failure_hook, + fault_hook, ): # strategies that don't make updates strategy_1 = mocker.Mock(spec=AsyncTxStrategy) @@ -1038,10 +1091,10 @@ def test_retry_with_errors_retries_exceeded( assert machine.current_state == machine._IDLE broadcast_hook = mocker.Mock() - fault_hook = mocker.Mock() atx = machine.queue_transaction( params=eip1559_transaction, signer=account, + on_broadcast_failure=broadcast_failure_hook, on_fault=fault_hook, on_broadcast=broadcast_hook, ) @@ -1061,7 +1114,7 @@ def test_retry_with_errors_retries_exceeded( mocker.patch.object(w3.eth, "send_raw_transaction", side_effect=error) # retry max attempts - for i in range(machine._MAX_REDO_ATTEMPTS): + for i in range(machine._MAX_RETRY_ATTEMPTS): assert machine.pending is not None yield clock.advance(1) assert atx.retries >= i @@ -1078,7 +1131,7 @@ def test_retry_with_errors_retries_exceeded( assert fault_hook.call_count == 1 fault_hook.assert_called_with(atx) - assert atx.retries == machine._MAX_REDO_ATTEMPTS + assert atx.retries == machine._MAX_RETRY_ATTEMPTS assert len(machine.queued) == 0 assert atx.final is False @@ -1139,6 +1192,8 @@ def test_pause_when_busy(clock, machine, eip1559_transaction, account, mocker): _ = machine.queue_transaction( params=eip1559_transaction, signer=account, + on_broadcast_failure=mocker.Mock(), + on_fault=mocker.Mock(), ) # advance to broadcast the transaction @@ -1172,7 +1227,13 @@ def test_pause_when_busy(clock, machine, eip1559_transaction, account, mocker): @pytest.mark.usefixtures("disable_auto_mining") def test_simple_state_transitions( - ethereum_tester, machine, eip1559_transaction, account, mock_wake_sleep + ethereum_tester, + machine, + eip1559_transaction, + account, + mock_wake_sleep, + broadcast_failure_hook, + fault_hook, ): assert machine.current_state == machine._IDLE @@ -1201,6 +1262,8 @@ def test_simple_state_transitions( atx = machine.queue_transaction( params=eip1559_transaction, signer=account, + on_broadcast_failure=broadcast_failure_hook, + on_fault=fault_hook, ) # broadcast tx diff --git a/tests/test_tracker.py b/tests/test_tracker.py index 1454d74..175d4ff 100644 --- a/tests/test_tracker.py +++ b/tests/test_tracker.py @@ -16,7 +16,13 @@ def test_queue(eip1559_transaction, legacy_transaction, mocker): assert tx_tracker.pending is None assert len(tx_tracker.finalized) == 0 - tx = tx_tracker.queue_tx(params=eip1559_transaction) + broadcast_failure_hook_1 = mocker.Mock() + fault_hook_1 = mocker.Mock() + tx = tx_tracker.queue_tx( + params=eip1559_transaction, + on_broadcast_failure=broadcast_failure_hook_1, + on_fault=fault_hook_1, + ) assert len(tx_tracker.queue) == 1 assert isinstance(tx, FutureTx) assert tx_tracker.queue[0] == tx @@ -31,7 +37,14 @@ def test_queue(eip1559_transaction, legacy_transaction, mocker): assert len(tx_tracker.finalized) == 0 tx_2_info = {"description": "it's me!", "message": "me who?"} - tx_2 = tx_tracker.queue_tx(params=legacy_transaction, info=tx_2_info) + broadcast_failure_hook_2 = mocker.Mock() + fault_hook_2 = mocker.Mock() + tx_2 = tx_tracker.queue_tx( + params=legacy_transaction, + info=tx_2_info, + on_broadcast_failure=broadcast_failure_hook_2, + on_fault=fault_hook_2, + ) assert len(tx_tracker.queue) == 2 assert isinstance(tx_2, FutureTx) assert tx_tracker.queue[1] == tx_2 @@ -47,19 +60,19 @@ def test_queue(eip1559_transaction, legacy_transaction, mocker): # check hooks assert tx.on_broadcast is None - assert tx.on_broadcast_failure is None - assert tx.on_fault is None + assert tx.on_broadcast_failure == broadcast_failure_hook_1 + assert tx.on_fault == fault_hook_1 assert tx.on_finalized is None + broadcast_failure_hook_3 = mocker.Mock() + fault_hook_3 = mocker.Mock() broadcast_hook = mocker.Mock() - broadcast_failure_hook = mocker.Mock() - fault_hook = mocker.Mock() finalized_hook = mocker.Mock() tx_3 = tx_tracker.queue_tx( params=eip1559_transaction, on_broadcast=broadcast_hook, - on_broadcast_failure=broadcast_failure_hook, - on_fault=fault_hook, + on_broadcast_failure=broadcast_failure_hook_3, + on_fault=fault_hook_3, on_finalized=finalized_hook, ) assert tx_3.params == eip1559_transaction @@ -68,8 +81,8 @@ def test_queue(eip1559_transaction, legacy_transaction, mocker): assert tx_3.fault is None assert tx_3.id == 2 assert tx_3.on_broadcast == broadcast_hook - assert tx_3.on_broadcast_failure == broadcast_failure_hook - assert tx_3.on_fault == fault_hook + assert tx_3.on_broadcast_failure == broadcast_failure_hook_3 + assert tx_3.on_fault == fault_hook_3 assert tx_3.on_finalized == finalized_hook assert len(tx_tracker.queue) == 3 @@ -80,64 +93,11 @@ def test_queue(eip1559_transaction, legacy_transaction, mocker): assert len(tx_tracker.finalized) == 0 -def test_pop(eip1559_transaction, legacy_transaction): - tx_tracker = _TxTracker(disk_cache=False) - tx_1 = tx_tracker.queue_tx(params=eip1559_transaction) - tx_2 = tx_tracker.queue_tx(params=legacy_transaction) - tx_3 = tx_tracker.queue_tx(params=eip1559_transaction) - - assert len(tx_tracker.queue) == 3 - - for tx in [tx_1, tx_2, tx_3]: - popped_tx = tx_tracker.pop() - assert popped_tx is tx - - with pytest.raises(IndexError): - tx_tracker.pop() - - -def test_requeue(eip1559_transaction, legacy_transaction): - tx_tracker = _TxTracker(disk_cache=False) - tx_1 = tx_tracker.queue_tx(params=eip1559_transaction) - assert tx_1.requeues == 0 - tx_2 = tx_tracker.queue_tx(params=legacy_transaction) - assert tx_2.requeues == 0 - tx_3 = tx_tracker.queue_tx(params=eip1559_transaction) - assert tx_3.requeues == 0 - - assert len(tx_tracker.queue) == 3 - - base_num_requeues = 4 - for i in range(1, base_num_requeues + 1): - prior_pop = None - for _ in tx_tracker.queue: - popped_tx = tx_tracker.pop() - assert popped_tx is not prior_pop, "requeue was an append, not a prepend" - - tx_tracker.requeue(popped_tx) - prior_pop = popped_tx - assert popped_tx.requeues == i - - assert len(tx_tracker.queue) == 3, "remains the same length" - - assert tx_1.requeues == base_num_requeues - assert tx_2.requeues == base_num_requeues - assert tx_3.requeues == base_num_requeues - - _ = tx_tracker.pop() # remove tx_1 - _ = tx_tracker.pop() # remove tx_2 - - tx_tracker.requeue(tx_2) - assert tx_2.requeues == base_num_requeues + 1 - assert tx_1.requeues == base_num_requeues - assert tx_3.requeues == base_num_requeues - - -def test_morph(eip1559_transaction, legacy_transaction, mocker): +def test_morph( + eip1559_transaction, legacy_transaction, broadcast_failure_hook, fault_hook, mocker +): tx_tracker = _TxTracker(disk_cache=False) broadcast_hook = mocker.Mock() - broadcast_failure_hook = mocker.Mock() - fault_hook = mocker.Mock() finalized_hook = mocker.Mock() tx_1 = tx_tracker.queue_tx( params=eip1559_transaction, @@ -146,11 +106,14 @@ def test_morph(eip1559_transaction, legacy_transaction, mocker): on_fault=fault_hook, on_finalized=finalized_hook, ) - tx_2 = tx_tracker.queue_tx(params=legacy_transaction) + tx_2 = tx_tracker.queue_tx( + params=legacy_transaction, + on_broadcast_failure=mocker.Mock(), + on_fault=mocker.Mock(), + ) assert tx_1.id != tx_2.id tx_hash = TxHash("0xdeadbeef") - assert tx_tracker.pop() == tx_1 pending_tx = tx_tracker.morph(tx_1, tx_hash) assert isinstance(pending_tx, PendingTx) @@ -166,12 +129,13 @@ def test_morph(eip1559_transaction, legacy_transaction, mocker): assert tx_1.on_broadcast_failure == broadcast_failure_hook assert tx_1.on_fault == fault_hook assert tx_1.on_finalized == finalized_hook + assert tx_1 not in tx_tracker.queue + assert len(tx_tracker.queue) == 1 assert isinstance(tx_2, FutureTx), "unaffected by the morph" assert tx_tracker.pending is not tx_2 tx_2_hash = TxHash("0xdeadbeef2") - assert tx_tracker.pop() == tx_2 pending_tx_2 = tx_tracker.morph(tx_2, tx_2_hash) assert isinstance(pending_tx_2, PendingTx) assert pending_tx_2 is tx_2, "same underlying object" @@ -183,13 +147,16 @@ def test_morph(eip1559_transaction, legacy_transaction, mocker): tx_tracker.pending is not tx_tracker.pending ), "copy of object always returned" + assert tx_2 not in tx_tracker.queue + assert len(tx_tracker.queue) == 0 + @pytest_twisted.inlineCallbacks -def test_fault(eip1559_transaction, legacy_transaction, mocker): +def test_fault( + eip1559_transaction, legacy_transaction, broadcast_failure_hook, fault_hook, mocker +): tx_tracker = _TxTracker(disk_cache=False) broadcast_hook = mocker.Mock() - broadcast_failure_hook = mocker.Mock() - fault_hook = mocker.Mock() finalized_hook = mocker.Mock() tx = tx_tracker.queue_tx( params=eip1559_transaction, @@ -198,7 +165,11 @@ def test_fault(eip1559_transaction, legacy_transaction, mocker): on_fault=fault_hook, on_finalized=finalized_hook, ) - tx_2 = tx_tracker.queue_tx(params=legacy_transaction) + tx_2 = tx_tracker.queue_tx( + params=legacy_transaction, + on_broadcast_failure=mocker.Mock(), + on_fault=mocker.Mock(), + ) assert len(tx_tracker.queue) == 2 @@ -211,7 +182,6 @@ def test_fault(eip1559_transaction, legacy_transaction, mocker): tx_tracker.fault(fault_error) tx_hash = TxHash("0xdeadbeef") - assert tx_tracker.pop() == tx pending_tx = tx_tracker.morph(tx, tx_hash) assert tx_tracker.pending.params == tx.params @@ -255,7 +225,6 @@ def test_fault(eip1559_transaction, legacy_transaction, mocker): # repeat with no hook tx_hash_2 = TxHash("0xdeadbeef2") - assert tx_tracker.pop() == tx_2 pending_tx_2 = tx_tracker.morph(tx_2, tx_hash_2) assert tx_tracker.pending.params == tx_2.params @@ -284,16 +253,19 @@ def test_fault(eip1559_transaction, legacy_transaction, mocker): def test_update_after_retry(eip1559_transaction, legacy_transaction, mocker): tx_tracker = _TxTracker(disk_cache=False) - tx = tx_tracker.queue_tx(params=eip1559_transaction) + tx = tx_tracker.queue_tx( + params=eip1559_transaction, + on_broadcast_failure=mocker.Mock(), + on_fault=mocker.Mock(), + ) assert tx_tracker.pending is None with pytest.raises(RuntimeError, match="No active transaction"): # there is no active tx - tx_tracker.update_after_retry(mocker.Mock(spec=PendingTx)) + tx_tracker.update_active_after_retry(mocker.Mock(spec=PendingTx)) tx_hash = TxHash("0xdeadbeef") - assert tx_tracker.pop() == tx tx_tracker.morph(tx, tx_hash) assert isinstance(tx, PendingTx) assert tx_tracker.pending.params == tx.params @@ -301,7 +273,7 @@ def test_update_after_retry(eip1559_transaction, legacy_transaction, mocker): with pytest.raises(RuntimeError, match="Mismatch between active tx"): mocked_tx = mocker.Mock(spec=PendingTx) mocked_tx.id = 20 - tx_tracker.update_after_retry(mocked_tx) + tx_tracker.update_active_after_retry(mocked_tx) # first update new_params = legacy_transaction @@ -310,7 +282,7 @@ def test_update_after_retry(eip1559_transaction, legacy_transaction, mocker): pending_tx = tx_tracker.pending # obtain fresh copy pending_tx.params = new_params pending_tx.txhash = new_tx_hash - tx_tracker.update_after_retry(pending_tx) + tx_tracker.update_active_after_retry(pending_tx) assert tx.params == new_params assert tx.txhash == new_tx_hash @@ -321,22 +293,25 @@ def test_update_after_retry(eip1559_transaction, legacy_transaction, mocker): pending_tx = tx_tracker.pending # obtain fresh copy pending_tx.params = new_params pending_tx.txhash = new_tx_hash - tx_tracker.update_after_retry(pending_tx) + tx_tracker.update_active_after_retry(pending_tx) assert tx.params == new_params assert tx.txhash == new_tx_hash def test_update_failed_retry_attempt(eip1559_transaction, legacy_transaction, mocker): tx_tracker = _TxTracker(disk_cache=False) - tx = tx_tracker.queue_tx(params=eip1559_transaction) + tx = tx_tracker.queue_tx( + params=eip1559_transaction, + on_broadcast_failure=mocker.Mock(), + on_fault=mocker.Mock(), + ) assert tx_tracker.pending is None with pytest.raises(RuntimeError, match="No active transaction"): # there is no active tx - tx_tracker.update_failed_retry_attempt(mocker.Mock(spec=PendingTx)) + tx_tracker.update_active_after_failed_retry_attempt(mocker.Mock(spec=PendingTx)) tx_hash = TxHash("0xdeadbeef") - assert tx_tracker.pop() == tx tx_tracker.morph(tx, tx_hash) assert isinstance(tx, PendingTx) pending_tx = tx_tracker.pending @@ -346,18 +321,20 @@ def test_update_failed_retry_attempt(eip1559_transaction, legacy_transaction, mo with pytest.raises(RuntimeError, match="Mismatch between active tx"): mocked_tx = mocker.Mock(spec=PendingTx) mocked_tx.id = 20 - tx_tracker.update_failed_retry_attempt(mocked_tx) + tx_tracker.update_active_after_failed_retry_attempt(mocked_tx) assert tx.retries == 0 for i in range(1, 5): - tx_tracker.update_failed_retry_attempt(tx_tracker.pending) + tx_tracker.update_active_after_failed_retry_attempt(tx_tracker.pending) assert tx.retries == i assert tx_tracker.pending.retries == i @pytest_twisted.inlineCallbacks -def test_finalize_active_tx(eip1559_transaction, mocker, tx_receipt): +def test_finalize_active_tx( + eip1559_transaction, tx_receipt, broadcast_failure_hook, fault_hook, mocker +): tx_tracker = _TxTracker(disk_cache=False) with pytest.raises(RuntimeError, match="No pending transaction to finalize"): @@ -365,8 +342,6 @@ def test_finalize_active_tx(eip1559_transaction, mocker, tx_receipt): tx_tracker.finalize_active_tx(mocker.Mock()) broadcast_hook = mocker.Mock() - broadcast_failure_hook = mocker.Mock() - fault_hook = mocker.Mock() finalized_hook = mocker.Mock() tx = tx_tracker.queue_tx( params=eip1559_transaction, @@ -377,7 +352,6 @@ def test_finalize_active_tx(eip1559_transaction, mocker, tx_receipt): ) tx_hash = TxHash("0xdeadbeef") - assert tx_tracker.pop() == tx tx_tracker.morph(tx, tx_hash) assert isinstance(tx, PendingTx) pending_tx = tx_tracker.pending @@ -410,7 +384,7 @@ def test_finalize_active_tx(eip1559_transaction, mocker, tx_receipt): def test_commit_restore( - eip1559_transaction, legacy_transaction, tx_receipt, tempfile_path + eip1559_transaction, legacy_transaction, tx_receipt, tempfile_path, mocker ): tx_tracker = _TxTracker(disk_cache=True, filepath=tempfile_path) @@ -419,16 +393,38 @@ def test_commit_restore( restored_tracker = _TxTracker(disk_cache=True, filepath=tempfile_path) _compare_trackers(tx_tracker, restored_tracker) - tx_1 = tx_tracker.queue_tx(params=eip1559_transaction, info={"name": "tx_1"}) - tx_2 = tx_tracker.queue_tx(params=legacy_transaction) - tx_3 = tx_tracker.queue_tx(params=eip1559_transaction, info={"name": "tx_3"}) - tx_4 = tx_tracker.queue_tx(params=legacy_transaction) - tx_5 = tx_tracker.queue_tx(params=eip1559_transaction, info={"name": "tx_5"}) - tx_6 = tx_tracker.queue_tx(params=legacy_transaction) + hook = mocker.Mock() + + tx_1 = tx_tracker.queue_tx( + params=eip1559_transaction, + info={"name": "tx_1"}, + on_broadcast_failure=hook, + on_fault=hook, + ) + tx_2 = tx_tracker.queue_tx( + params=legacy_transaction, on_broadcast_failure=hook, on_fault=hook + ) + tx_3 = tx_tracker.queue_tx( + params=eip1559_transaction, + info={"name": "tx_3"}, + on_broadcast_failure=hook, + on_fault=hook, + ) + tx_4 = tx_tracker.queue_tx( + params=legacy_transaction, on_broadcast_failure=hook, on_fault=hook + ) + tx_5 = tx_tracker.queue_tx( + params=eip1559_transaction, + info={"name": "tx_5"}, + on_broadcast_failure=hook, + on_fault=hook, + ) + tx_6 = tx_tracker.queue_tx( + params=legacy_transaction, on_broadcast_failure=hook, on_fault=hook + ) # max tx_1 finalized tx_hash = TxHash("0xdeadbeef") - assert tx_tracker.pop() == tx_1 tx_tracker.morph(tx_1, tx_hash) tx_tracker.finalize_active_tx(tx_receipt) @@ -441,7 +437,6 @@ def test_commit_restore( # make tx_2 finalized tx_hash_2 = TxHash("0xdeadbeef2") - assert tx_tracker.pop() == tx_2 tx_tracker.morph(tx_2, tx_hash_2) tx_tracker.finalize_active_tx(tx_receipt) @@ -456,7 +451,6 @@ def test_commit_restore( # make tx_3 active tx_hash_3 = TxHash("0xdeadbeef3") - assert tx_tracker.pop() == tx_3 tx_tracker.morph(tx_3, tx_hash_3) assert tx_tracker.pending == tx_3