-
Notifications
You must be signed in to change notification settings - Fork 3
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add tests for faulted tx conditions - revert or faulted from strategy.
- Loading branch information
1 parent
4bb93a9
commit 88caca6
Showing
2 changed files
with
93 additions
and
36 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,58 +1,115 @@ | ||
import pytest | ||
import pytest_twisted | ||
from twisted.internet import reactor | ||
from twisted.internet.task import deferLater | ||
from web3.exceptions import TransactionNotFound | ||
from web3.types import TxReceipt | ||
|
||
from atxm.exceptions import Fault | ||
from atxm.exceptions import Fault, TransactionFaulted | ||
from atxm.strategies import AsyncTxStrategy | ||
from atxm.tx import FaultedTx | ||
from atxm.utils import _get_receipt_from_txhash | ||
|
||
|
||
@pytest.fixture | ||
def mock_eth_get_transaction(mocker, w3): | ||
return mocker.patch.object( | ||
w3.eth, | ||
"get_transaction", | ||
side_effect=TransactionNotFound | ||
) | ||
|
||
def _broadcast_tx(machine, eip1559_transaction, account, mocker): | ||
fault_hook = mocker.Mock() | ||
|
||
@pytest_twisted.inlineCallbacks | ||
def test_timeout( | ||
machine, clock, eip1559_transaction, account, interval, | ||
mock_wake_sleep, mocker, mock_eth_get_transaction | ||
): | ||
hook = mocker.Mock() | ||
atx = machine.queue_transaction( | ||
params=eip1559_transaction, | ||
signer=account, | ||
on_fault=hook, | ||
on_fault=fault_hook, | ||
) | ||
|
||
machine.start() | ||
while not machine.pending: | ||
yield clock.advance(interval) | ||
machine.stop() | ||
assert not machine.running | ||
# broadcast tx | ||
machine._cycle() | ||
|
||
assert machine.pending == atx | ||
assert atx.final is False | ||
assert atx.fault is None | ||
|
||
atx.created -= 9999999999 | ||
machine.start() | ||
while machine.pending: | ||
yield clock.advance(interval) | ||
machine.stop() | ||
return atx, fault_hook | ||
|
||
|
||
def _verify_faulted_tx(machine, atx, fault_hook, expected_fault: Fault): | ||
assert atx.final is False | ||
assert isinstance(atx.fault, Fault) | ||
assert isinstance(atx, FaultedTx) | ||
assert isinstance(atx.fault, Fault) | ||
assert atx.fault == expected_fault | ||
|
||
# check async tx advanced through the state machine | ||
assert atx not in machine.queued | ||
|
||
assert machine.pending is None | ||
assert atx.final is False | ||
|
||
yield deferLater(reactor, 0.2, lambda: None) | ||
assert hook.call_count == 1 | ||
assert fault_hook.call_count == 1 | ||
fault_hook.assert_called_with(atx) | ||
|
||
|
||
def test_revert( | ||
chain, | ||
w3, | ||
machine, | ||
clock, | ||
eip1559_transaction, | ||
account, | ||
interval, | ||
mock_wake_sleep, | ||
mocker, | ||
): | ||
atx, fault_hook = _broadcast_tx(machine, eip1559_transaction, account, mocker) | ||
|
||
assert machine.pending | ||
|
||
chain.mine(1) | ||
|
||
# force receipt to symbolize a revert of the tx | ||
receipt = _get_receipt_from_txhash(w3, atx.txhash) | ||
revert_receipt = dict(receipt) | ||
revert_receipt["status"] = 0 | ||
|
||
mocker.patch.object( | ||
w3.eth, "get_transaction_receipt", return_value=TxReceipt(revert_receipt) | ||
) | ||
|
||
while fault_hook.call_count == 0: | ||
# ensure tx processed | ||
machine._cycle() | ||
|
||
_verify_faulted_tx(machine, atx, fault_hook, expected_fault=Fault.REVERT) | ||
|
||
|
||
def test_strategy_fault( | ||
w3, machine, clock, eip1559_transaction, account, interval, mock_wake_sleep, mocker | ||
): | ||
faulty_strategy = mocker.Mock(spec=AsyncTxStrategy) | ||
machine._strategies.insert(0, faulty_strategy) # add first | ||
|
||
atx, fault_hook = _broadcast_tx(machine, eip1559_transaction, account, mocker) | ||
|
||
faulty_message = "mocked fault" | ||
faulty_strategy.execute.side_effect = TransactionFaulted( | ||
tx=atx, fault=Fault.ERROR, message=faulty_message | ||
) | ||
|
||
mocker.patch.object( | ||
w3.eth, "get_transaction_receipt", side_effect=TransactionNotFound | ||
) | ||
|
||
while fault_hook.call_count == 0: | ||
# ensure tx processed | ||
machine._cycle() | ||
|
||
_verify_faulted_tx(machine, atx, fault_hook, expected_fault=Fault.ERROR) | ||
assert atx.error == faulty_message | ||
|
||
|
||
def test_timeout_strategy_fault( | ||
w3, machine, clock, eip1559_transaction, account, interval, mock_wake_sleep, mocker | ||
): | ||
atx, fault_hook = _broadcast_tx(machine, eip1559_transaction, account, mocker) | ||
|
||
atx.created -= 9999999999 | ||
mocker.patch.object(w3.eth, "get_transaction", side_effect=TransactionNotFound) | ||
|
||
while fault_hook.call_count == 0: | ||
# ensure tx processed | ||
machine._cycle() | ||
|
||
_verify_faulted_tx(machine, atx, fault_hook, expected_fault=Fault.TIMEOUT) |