Skip to content

Commit

Permalink
Add test for unrecoverable errors encoutered when trying to retry a tx.
Browse files Browse the repository at this point in the history
  • Loading branch information
derekpierre committed Mar 7, 2024
1 parent 4905e07 commit d695078
Showing 1 changed file with 86 additions and 4 deletions.
90 changes: 86 additions & 4 deletions tests/test_machine.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
Web3Exception,
)

from atxm.exceptions import Fault
from atxm.strategies import AsyncTxStrategy, TimeoutStrategy
from atxm.tx import FutureTx, PendingTx
from atxm.utils import _is_recoverable_send_tx_error
Expand Down Expand Up @@ -774,10 +775,8 @@ def test_use_strategies_that_dont_make_updates(
# params remained unchanged since strategies don't make updates
assert atx.params == original_params, "params remain unchanged"

s1_call_count = strategy_1.execute.call_count
assert s1_call_count > 0, "strategy #1 was called"
s2_call_count = strategy_2.execute.call_count
assert s2_call_count > 0, "strategy #2 was called"
assert strategy_1.execute.call_count > 0, "strategy #1 was called"
assert strategy_2.execute.call_count > 0, "strategy #2 was called"

assert atx.params == original_params, "params remain unchanged"
assert update_spy.call_count == 0, "update never called because no retry"
Expand All @@ -804,6 +803,89 @@ def test_use_strategies_that_dont_make_updates(
machine.stop()


@pytest_twisted.inlineCallbacks
@pytest.mark.usefixtures("disable_auto_mining")
@pytest.mark.parametrize(
"non_recoverable_error", [ValidationError, ValueError, Web3Exception]
)
def test_retry_non_recoverable(
non_recoverable_error,
ethereum_tester,
w3,
machine,
state_observer,
clock,
eip1559_transaction,
account,
mocker,
mock_wake_sleep,
):
# TODO consider whether this should just be provided to constructor - #23
machine._strategies.clear()

# strategies that don't make updates
strategy_1 = mocker.Mock(spec=AsyncTxStrategy)
strategy_1.name = "mock_strategy"
# return non-None so retry is attempted
strategy_1.execute.return_value = dict(eip1559_transaction)

machine._strategies = [strategy_1]

update_spy = mocker.spy(machine._tx_tracker, "update_after_retry")

machine.start()
assert machine.current_state == machine._IDLE

fault_hook = mocker.Mock()
atx = machine.queue_transaction(
params=eip1559_transaction, signer=account, on_fault=fault_hook
)

# advance to broadcast the transaction
while machine.pending is None:
yield clock.advance(1)

# ensure that hook is called
assert machine.current_state == machine._BUSY

# make firing of transaction fail with non-recoverable error
error = non_recoverable_error("non-recoverable error")
assert not _is_recoverable_send_tx_error(error)
mocker.patch.object(w3.eth, "send_raw_transaction", side_effect=error)

# run a cycle while tx unmined for strategies to kick in
yield clock.advance(1)

assert strategy_1.execute.call_count > 0, "strategy #1 was called"

# however retry failed, so params shouldn't have been updated
assert update_spy.call_count == 0, "update never called because no retry"

# ensure that hook is called
yield deferLater(reactor, 0.2, lambda: None)
assert fault_hook.call_count == 1
fault_hook.assert_called_with(atx)
assert atx.fault == Fault.ERROR
assert atx.error == "non-recoverable error"

# ensure switch back to IDLE
yield clock.advance(1)

assert len(machine.queued) == 0
assert machine.pending is None

assert not machine.busy
assert not atx.final

assert machine.current_state == machine._IDLE

assert len(state_observer.transitions) == 2
assert state_observer.transitions[0] == (machine._IDLE, machine._BUSY)
assert state_observer.transitions[1] == (machine._BUSY, machine._IDLE)

machine.stop()


@pytest_twisted.inlineCallbacks
def test_pause_when_idle(clock, machine, mocker):
machine.start()
Expand Down

0 comments on commit d695078

Please sign in to comment.