Skip to content

Commit

Permalink
Implement new step asserting that AtomicTransactionComposer's attempt…
Browse files Browse the repository at this point in the history
… to add a method can fail with a particular error (#347)

* Implement new step
  • Loading branch information
tzaffi authored Jun 17, 2022
1 parent d6774b6 commit 989893a
Show file tree
Hide file tree
Showing 3 changed files with 105 additions and 61 deletions.
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
UNITS = "@unit.abijson or @unit.abijson.byname or @unit.algod or @unit.algod.ledger_refactoring or @unit.applications or @unit.atomic_transaction_composer or @unit.dryrun or @unit.dryrun.trace.application or @unit.feetest or @unit.indexer or @unit.indexer.ledger_refactoring or @unit.indexer.logs or @unit.offline or @unit.rekey or @unit.transactions.keyreg or @unit.responses or @unit.responses.231 or @unit.tealsign or @unit.transactions or @unit.transactions.payment or @unit.responses.unlimited_assets"
UNITS = "@unit.abijson or @unit.abijson.byname or @unit.algod or @unit.algod.ledger_refactoring or @unit.applications or @unit.atc_method_args or @unit.atomic_transaction_composer or @unit.dryrun or @unit.dryrun.trace.application or @unit.feetest or @unit.indexer or @unit.indexer.ledger_refactoring or @unit.indexer.logs or @unit.offline or @unit.rekey or @unit.transactions.keyreg or @unit.responses or @unit.responses.231 or @unit.tealsign or @unit.transactions or @unit.transactions.payment or @unit.responses.unlimited_assets"
unit:
behave --tags=$(UNITS) test -f progress2

Expand Down
141 changes: 103 additions & 38 deletions test/steps/application_v2_steps.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,11 @@

from algosdk import abi, atomic_transaction_composer, encoding, mnemonic
from algosdk.abi.contract import NetworkInfo
from algosdk.error import ABITypeError, IndexerHTTPError
from algosdk.error import (
ABITypeError,
IndexerHTTPError,
AtomicTransactionComposerError,
)
from algosdk.future import transaction

from test.steps.other_v2_steps import read_program
Expand Down Expand Up @@ -85,16 +89,32 @@ def s512_256_uint64(witness):
return int.from_bytes(encoding.checksum(witness)[:8], "big")


@step(
'I sign and submit the transaction, saving the txid. If there is an error it is "{error_string:MaybeString}".'
)
def sign_submit_save_txid_with_error(context, error_string):
try:
signed_app_transaction = context.app_transaction.sign(
context.transient_sk
)
context.app_txid = context.app_acl.send_transaction(
signed_app_transaction
)
except Exception as e:
if not error_string or error_string not in str(e):
raise RuntimeError(
"error string "
+ error_string
+ " not in actual error "
+ str(e)
)


@when("we make a GetApplicationByID call for applicationID {app_id}")
def application_info(context, app_id):
context.response = context.acl.application_info(int(app_id))


@when("we make a LookupApplications call with applicationID {app_id}")
def lookup_application(context, app_id):
context.response = context.icl.applications(int(app_id))


@when(
'we make a LookupApplicationLogsByID call with applicationID {app_id} limit {limit} minRound {min_round} maxRound {max_round} nextToken "{next_token:MaybeString}" sender "{sender:MaybeString}" and txID "{txid:MaybeString}"'
)
Expand Down Expand Up @@ -159,8 +179,13 @@ def lookup_application_include_all2(
context.response = json.loads(str(e))


@when("we make a LookupApplications call with applicationID {app_id}")
def lookup_application(context, app_id):
context.response = context.icl.applications(int(app_id))


@when("I use {indexer} to lookup application with {application_id}")
def lookup_application(context, indexer, application_id):
def lookup_application2(context, indexer, application_id):
context.response = context.icls[indexer].applications(
application_id=int(application_id)
)
Expand Down Expand Up @@ -514,33 +539,33 @@ def add_transaction_to_composer(context):

def process_abi_args(context, method, arg_tokens):
method_args = []
for arg_index, arg in enumerate(method.args):
# Skip arg if it does not have a type
for arg_index, arg_token in enumerate(arg_tokens):
if arg_index >= len(method.args):
method_args.append(arg_token)
continue

arg = method.args[arg_index]
if isinstance(arg.type, abi.ABIType):
method_arg = arg.type.decode(
base64.b64decode(arg_tokens[arg_index])
)
method_arg = arg.type.decode(base64.b64decode(arg_token))
method_args.append(method_arg)
elif arg.type == abi.ABIReferenceType.ACCOUNT:
method_arg = abi.AddressType().decode(
base64.b64decode(arg_tokens[arg_index])
)
method_arg = abi.AddressType().decode(base64.b64decode(arg_token))
method_args.append(method_arg)
elif (
arg.type == abi.ABIReferenceType.APPLICATION
or arg.type == abi.ABIReferenceType.ASSET
):
parts = arg_tokens[arg_index].split(":")
parts = arg_token.split(":")
if len(parts) == 2 and parts[0] == "ctxAppIdx":
method_arg = context.app_ids[int(parts[1])]
else:
method_arg = abi.UintType(64).decode(
base64.b64decode(arg_tokens[arg_index])
base64.b64decode(arg_token)
)
method_args.append(method_arg)
else:
# Append the transaction signer as is
method_args.append(arg_tokens[arg_index])
method_args.append(arg_token)
return method_args


Expand All @@ -561,7 +586,7 @@ def append_txn_to_method_args(context):
)
def append_app_args_to_method_args(context, method_args):
# Returns a list of ABI method arguments
app_args = method_args.split(",")
app_args = method_args.split(",") if method_args else []
context.method_args += app_args


Expand All @@ -583,6 +608,7 @@ def abi_method_adder(
local_ints=None,
extra_pages=None,
force_unique_transactions=False,
exception_key="none",
):
if account_type == "transient":
sender = context.transient_pk
Expand Down Expand Up @@ -621,39 +647,66 @@ def int_if_given(given):
app_args = process_abi_args(
context, context.abi_method, context.method_args
)
context.app_args = app_args
note = None
if force_unique_transactions:
note = (
b"I should be unique thanks to this nonce: "
+ context.nonce.encode()
)

context.atomic_transaction_composer.add_method_call(
app_id=app_id,
method=context.abi_method,
sender=sender,
sp=context.suggested_params,
signer=context.transaction_signer,
method_args=app_args,
on_complete=operation_string_to_enum(operation),
local_schema=local_schema,
global_schema=global_schema,
approval_program=approval_program,
clear_program=clear_program,
extra_pages=extra_pages,
note=note,
)
try:
context.atomic_transaction_composer.add_method_call(
app_id=app_id,
method=context.abi_method,
sender=sender,
sp=context.suggested_params,
signer=context.transaction_signer,
method_args=app_args,
on_complete=operation_string_to_enum(operation),
local_schema=local_schema,
global_schema=global_schema,
approval_program=approval_program,
clear_program=clear_program,
extra_pages=extra_pages,
note=note,
)
except AtomicTransactionComposerError as atce:
assert (
exception_key != "none"
), f"cucumber step asserted that no exception resulted, but the following exception actually occurred: {atce}"

arglen_exception = "argument_count_mismatch"
known_exception_keys = [arglen_exception]
assert (
exception_key in known_exception_keys
), f"encountered exception key '{exception_key}' which is not in known set: {known_exception_keys}"

if exception_key == arglen_exception:
exception_msg = (
"number of method arguments do not match the method signature"
)
assert exception_msg in str(
atce
), f"expected argument count mismatch error such as '{exception_msg}' but got the following instead: {atce}"
return

assert (
exception_key == "none"
), f"should have encountered an AtomicTransactionComposerError keyed by '{exception_key}', but no such exception has been detected"


@step(
'I add a nonced method call with the {account_type} account, the current application, suggested params, on complete "{operation}", current transaction signer, current method arguments.'
'I add a method call with the {account_type} account, the current application, suggested params, on complete "{operation}", current transaction signer, current method arguments; any resulting exception has key "{exception_key}".'
)
def add_abi_method_call_nonced(context, account_type, operation):
def add_abi_method_call_with_exception(
context, account_type, operation, exception_key
):
abi_method_adder(
context,
account_type,
operation,
force_unique_transactions=True,
exception_key=exception_key,
)


Expand Down Expand Up @@ -718,6 +771,18 @@ def add_abi_method_call_creation(
)


@step(
'I add a nonced method call with the {account_type} account, the current application, suggested params, on complete "{operation}", current transaction signer, current method arguments.'
)
def add_abi_method_call_nonced(context, account_type, operation):
abi_method_adder(
context,
account_type,
operation,
force_unique_transactions=True,
)


@step(
'I build the transaction group with the composer. If there is an error it is "{error_string:MaybeString}".'
)
Expand Down Expand Up @@ -827,7 +892,7 @@ def serialize_method_to_json(context):
@when(
'I create the Method object with name "{method_name}" method description "{method_desc}" first argument type "{first_arg_type}" first argument description "{first_arg_desc}" second argument type "{second_arg_type}" second argument description "{second_arg_desc}" and return type "{return_arg_type}"'
)
def create_method_from_test_with_arg_name(
def create_method_from_test_with_arg_name_and_desc(
context,
method_name,
method_desc,
Expand Down
23 changes: 1 addition & 22 deletions test/steps/other_v2_steps.py
Original file line number Diff line number Diff line change
Expand Up @@ -604,7 +604,7 @@ def txns_by_addr(
@when(
'we make a Lookup Account Transactions call against account "{account:MaybeString}" with NotePrefix "{notePrefixB64:MaybeString}" TxType "{txType:MaybeString}" SigType "{sigType:MaybeString}" txid "{txid:MaybeString}" round {block} minRound {minRound} maxRound {maxRound} limit {limit} beforeTime "{beforeTime:MaybeString}" afterTime "{afterTime:MaybeString}" currencyGreaterThan {currencyGreaterThan} currencyLessThan {currencyLessThan} assetIndex {index}'
)
def txns_by_addr(
def txns_by_addr2(
context,
account,
notePrefixB64,
Expand Down Expand Up @@ -1406,27 +1406,6 @@ def algod_v2_client(context):
context.app_acl = algod.AlgodClient(daemon_token, algod_address)


@step(
'I sign and submit the transaction, saving the txid. If there is an error it is "{error_string:MaybeString}".'
)
def sign_submit_save_txid_with_error(context, error_string):
try:
signed_app_transaction = context.app_transaction.sign(
context.transient_sk
)
context.app_txid = context.app_acl.send_transaction(
signed_app_transaction
)
except Exception as e:
if not error_string or error_string not in str(e):
raise RuntimeError(
"error string "
+ error_string
+ " not in actual error "
+ str(e)
)


@when('I compile a teal program "{program}"')
def compile_step(context, program):
data = load_resource(program)
Expand Down

0 comments on commit 989893a

Please sign in to comment.