From 7ab587c0c326c8fcb629167ca0585bd98626fd87 Mon Sep 17 00:00:00 2001 From: Zeph Grunschlag Date: Tue, 14 Jun 2022 13:01:51 -0500 Subject: [PATCH 1/6] Imlement new step "the most recently added method call has an exception..." --- run_integration.sh | 2 +- test/steps/application_v2_steps.py | 115 ++++++++++++++++++++--------- test/steps/other_v2_steps.py | 23 +----- 3 files changed, 82 insertions(+), 58 deletions(-) diff --git a/run_integration.sh b/run_integration.sh index 76b293e8..7ae630c8 100755 --- a/run_integration.sh +++ b/run_integration.sh @@ -7,7 +7,7 @@ pushd $rootdir # Reset test harness rm -rf test-harness -git clone --single-branch --branch master https://github.com/algorand/algorand-sdk-testing.git test-harness +git clone --single-branch --branch abi-too-many-args https://github.com/algorand/algorand-sdk-testing.git test-harness ## Copy feature files into the project resources mkdir -p test/features diff --git a/test/steps/application_v2_steps.py b/test/steps/application_v2_steps.py index e7a1f9c7..46df401b 100644 --- a/test/steps/application_v2_steps.py +++ b/test/steps/application_v2_steps.py @@ -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 @@ -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}"' ) @@ -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) ) @@ -512,35 +537,37 @@ def add_transaction_to_composer(context): ) -def process_abi_args(context, method, arg_tokens): +def process_abi_args(context, method, arg_tokens, erase_empty_tokens=True): + if erase_empty_tokens: + arg_tokens = [tok for tok in arg_tokens if tok] 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 @@ -621,6 +648,7 @@ 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 = ( @@ -628,21 +656,38 @@ def int_if_given(given): + 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, - ) + context.most_recent_added_method_exception = None + 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: + context.most_recent_added_method_exception = atce + + +@then( + 'the most recently added method call has an exception which satisfies "{regex}".' +) +def most_recently_added_method_exception_satisfies(context, regex): + most_recent_exception = context.most_recent_added_method_exception + if regex == "none": + assert most_recent_exception is None + return + assert re.search( + regex, str(most_recent_exception) + ), f"{most_recent_exception} did not satisfy {regex}. FYI: method_args={context.method_args}; app_args={context.app_args}" @step( @@ -827,7 +872,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, diff --git a/test/steps/other_v2_steps.py b/test/steps/other_v2_steps.py index b2673798..4f526f4b 100644 --- a/test/steps/other_v2_steps.py +++ b/test/steps/other_v2_steps.py @@ -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, @@ -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) From c5f282ba9fb5f47aeee3895c0d45ecac5ccee4e6 Mon Sep 17 00:00:00 2001 From: Zeph Grunschlag Date: Tue, 14 Jun 2022 14:49:16 -0500 Subject: [PATCH 2/6] Per CR suggestion, make another variant with exception handler of "I add a method call..." --- test/steps/application_v2_steps.py | 41 +++++++++++++++++------------- 1 file changed, 24 insertions(+), 17 deletions(-) diff --git a/test/steps/application_v2_steps.py b/test/steps/application_v2_steps.py index 46df401b..5051329b 100644 --- a/test/steps/application_v2_steps.py +++ b/test/steps/application_v2_steps.py @@ -610,6 +610,7 @@ def abi_method_adder( local_ints=None, extra_pages=None, force_unique_transactions=False, + exception_regex="none", ): if account_type == "transient": sender = context.transient_pk @@ -656,7 +657,6 @@ def int_if_given(given): + context.nonce.encode() ) - context.most_recent_added_method_exception = None try: context.atomic_transaction_composer.add_method_call( app_id=app_id, @@ -674,31 +674,26 @@ def int_if_given(given): note=note, ) except AtomicTransactionComposerError as atce: - context.most_recent_added_method_exception = atce + assert ( + exception_regex != "none" + ), f"cucumber step asserted that no exception resulted, but the following exception actually occurred: {atce}" - -@then( - 'the most recently added method call has an exception which satisfies "{regex}".' -) -def most_recently_added_method_exception_satisfies(context, regex): - most_recent_exception = context.most_recent_added_method_exception - if regex == "none": - assert most_recent_exception is None - return - assert re.search( - regex, str(most_recent_exception) - ), f"{most_recent_exception} did not satisfy {regex}. FYI: method_args={context.method_args}; app_args={context.app_args}" + assert re.search( + exception_regex, str(atce) + ), f"{atce} did not satisfy the expected regular expression {exception_regex}" @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 satisfies the regex "{exception_regex}".' ) -def add_abi_method_call_nonced(context, account_type, operation): +def add_abi_method_call_with_exception( + context, account_type, operation, exception_regex +): abi_method_adder( context, account_type, operation, - force_unique_transactions=True, + exception_regex=exception_regex, ) @@ -763,6 +758,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}".' ) From 9bf3149c19185c61e02f5b8c3269e0006e5a0e7e Mon Sep 17 00:00:00 2001 From: Zeph Grunschlag Date: Tue, 14 Jun 2022 15:43:04 -0500 Subject: [PATCH 3/6] forgot to include the case when there was no exception but there should have been --- test/steps/application_v2_steps.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/test/steps/application_v2_steps.py b/test/steps/application_v2_steps.py index 5051329b..45ab6262 100644 --- a/test/steps/application_v2_steps.py +++ b/test/steps/application_v2_steps.py @@ -681,6 +681,11 @@ def int_if_given(given): assert re.search( exception_regex, str(atce) ), f"{atce} did not satisfy the expected regular expression {exception_regex}" + return + + assert ( + exception_regex == "none" + ), f"should have encountered an AtomicTransactionComposerError satisfying the regex pattern {exception_regex}, but no such exception has been detected" @step( From aaa7f7789f57cd246e88592fe4239580aff0ce68 Mon Sep 17 00:00:00 2001 From: Zeph Grunschlag Date: Thu, 16 Jun 2022 14:26:08 -0500 Subject: [PATCH 4/6] per cr suggestions --- test/steps/application_v2_steps.py | 30 ++++++++++++++++++++---------- 1 file changed, 20 insertions(+), 10 deletions(-) diff --git a/test/steps/application_v2_steps.py b/test/steps/application_v2_steps.py index 45ab6262..a07cd6c5 100644 --- a/test/steps/application_v2_steps.py +++ b/test/steps/application_v2_steps.py @@ -610,7 +610,7 @@ def abi_method_adder( local_ints=None, extra_pages=None, force_unique_transactions=False, - exception_regex="none", + exception_key="none", ): if account_type == "transient": sender = context.transient_pk @@ -675,30 +675,40 @@ def int_if_given(given): ) except AtomicTransactionComposerError as atce: assert ( - exception_regex != "none" + exception_key != "none" ), f"cucumber step asserted that no exception resulted, but the following exception actually occurred: {atce}" - assert re.search( - exception_regex, str(atce) - ), f"{atce} did not satisfy the expected regular expression {exception_regex}" + 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_regex == "none" - ), f"should have encountered an AtomicTransactionComposerError satisfying the regex pattern {exception_regex}, but no such exception has been detected" + exception_key == "none" + ), f"should have encountered an AtomicTransactionComposerError keyed by '{exception_key}', but no such exception has been detected" @step( - '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 satisfies the regex "{exception_regex}".' + '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_with_exception( - context, account_type, operation, exception_regex + context, account_type, operation, exception_key ): abi_method_adder( context, account_type, operation, - exception_regex=exception_regex, + exception_key=exception_key, ) From 9ad1a763890fa5a13276b0793843a9261782f698 Mon Sep 17 00:00:00 2001 From: Zeph Grunschlag Date: Fri, 17 Jun 2022 14:42:24 -0500 Subject: [PATCH 5/6] modify `append_app_args...` to interpret the empty string as empty array and stop erasing args --- Makefile | 2 +- test/steps/application_v2_steps.py | 6 ++---- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/Makefile b/Makefile index 8f066457..d5e994b3 100644 --- a/Makefile +++ b/Makefile @@ -1,4 +1,4 @@ -UNITS = "@unit.abijson 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.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 diff --git a/test/steps/application_v2_steps.py b/test/steps/application_v2_steps.py index a07cd6c5..41a7b29f 100644 --- a/test/steps/application_v2_steps.py +++ b/test/steps/application_v2_steps.py @@ -537,9 +537,7 @@ def add_transaction_to_composer(context): ) -def process_abi_args(context, method, arg_tokens, erase_empty_tokens=True): - if erase_empty_tokens: - arg_tokens = [tok for tok in arg_tokens if tok] +def process_abi_args(context, method, arg_tokens): method_args = [] for arg_index, arg_token in enumerate(arg_tokens): if arg_index >= len(method.args): @@ -588,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 From 7d79134554257a0cabf69510900406f0dd65d6cb Mon Sep 17 00:00:00 2001 From: Zeph Grunschlag Date: Fri, 17 Jun 2022 15:53:53 -0500 Subject: [PATCH 6/6] Update run_integration.sh --- run_integration.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/run_integration.sh b/run_integration.sh index 7ae630c8..76b293e8 100755 --- a/run_integration.sh +++ b/run_integration.sh @@ -7,7 +7,7 @@ pushd $rootdir # Reset test harness rm -rf test-harness -git clone --single-branch --branch abi-too-many-args https://github.com/algorand/algorand-sdk-testing.git test-harness +git clone --single-branch --branch master https://github.com/algorand/algorand-sdk-testing.git test-harness ## Copy feature files into the project resources mkdir -p test/features