diff --git a/Makefile b/Makefile index ebb44f15..ba7b6363 100644 --- a/Makefile +++ b/Makefile @@ -2,7 +2,7 @@ UNITS = "@unit.abijson or @unit.abijson.byname or @unit.algod or @unit.algod.led unit: behave --tags=$(UNITS) tests -f progress2 -INTEGRATIONS = "@abi or @algod or @applications or @applications.verified or @assets or @auction or @c2c or @compile or @dryrun or @dryrun.testing or @indexer or @indexer.231 or @indexer.applications or @kmd or @rekey or @send.keyregtxn or @send or @compile.sourcemap" +INTEGRATIONS = "@abi or @algod or @applications or @applications.verified or @assets or @auction or @c2c or @compile or @dryrun or @dryrun.testing or @indexer or @indexer.231 or @indexer.applications or @kmd or @rekey_v1 or @send.keyregtxn or @send or @compile.sourcemap" integration: behave --tags=$(INTEGRATIONS) tests -f progress2 diff --git a/tests/steps/account_v2_steps.py b/tests/steps/account_v2_steps.py index d2284669..5778f4aa 100644 --- a/tests/steps/account_v2_steps.py +++ b/tests/steps/account_v2_steps.py @@ -1,10 +1,9 @@ -from behave import given, then, when from typing import Union -from algosdk import account, encoding, logic +from algosdk import account, constants, encoding, logic from algosdk.future import transaction - -import tests.steps.other_v2_steps +from behave import given, then, when +import tests.steps.other_v2_steps # Imports MaybeString def fund_account_address( @@ -19,7 +18,7 @@ def fund_account_address( ) signed_payment = context.wallet.sign_transaction(payment) context.app_acl.send_transaction(signed_payment) - transaction.wait_for_confirmation(context.app_acl, payment.get_txid(), 10) + transaction.wait_for_confirmation(context.app_acl, payment.get_txid(), 1) @when( @@ -454,7 +453,7 @@ def create_transient_and_fund(context, transient_fund_amount): ) signed_payment = context.wallet.sign_transaction(payment) context.app_acl.send_transaction(signed_payment) - transaction.wait_for_confirmation(context.app_acl, payment.get_txid(), 10) + transaction.wait_for_confirmation(context.app_acl, payment.get_txid(), 1) @then( diff --git a/tests/steps/application_v2_steps.py b/tests/steps/application_v2_steps.py index a3c09f5d..e480ff1a 100644 --- a/tests/steps/application_v2_steps.py +++ b/tests/steps/application_v2_steps.py @@ -1,20 +1,21 @@ import base64 import json import re +import time -from behave import given, step, then, when import pytest - from algosdk import abi, atomic_transaction_composer, encoding, mnemonic from algosdk.abi.contract import NetworkInfo from algosdk.error import ( ABITypeError, - IndexerHTTPError, AtomicTransactionComposerError, + IndexerHTTPError, ) from algosdk.future import transaction - -from tests.steps.other_v2_steps import read_program +from behave import given, step, then, when +from tests.steps.other_v2_steps import ( + read_program, +) def operation_string_to_enum(operation): @@ -402,20 +403,26 @@ def remember_app_id(context): context.app_ids.append(app_id) +def wait_for_algod_transaction_processing_to_complete(): + """ + wait_for_algod_transaction_processing_to_complete is a Dev mode helper method that's a rough analog to `context.app_acl.status_after_block(last_round + 2)`. + *

+ * Since Dev mode produces blocks on a per transaction basis, it's possible algod generates a block _before_ the corresponding SDK call to wait for a block. Without _any_ wait, it's possible the SDK looks for the transaction before algod completes processing. So, the method performs a local sleep to simulate waiting for a block. + + """ + time.sleep(0.5) + + @step("I wait for the transaction to be confirmed.") def wait_for_app_txn_confirm(context): - sp = context.app_acl.suggested_params() - last_round = sp.first - context.app_acl.status_after_block(last_round + 2) + wait_for_algod_transaction_processing_to_complete() if hasattr(context, "acl"): assert "type" in context.acl.transaction_info( context.transient_pk, context.app_txid ) assert "type" in context.acl.transaction_by_id(context.app_txid) else: - transaction.wait_for_confirmation( - context.app_acl, context.app_txid, 10 - ) + transaction.wait_for_confirmation(context.app_acl, context.app_txid, 1) @given("an application id {app_id}") diff --git a/tests/steps/other_v2_steps.py b/tests/steps/other_v2_steps.py index 0f97b88a..a5367755 100644 --- a/tests/steps/other_v2_steps.py +++ b/tests/steps/other_v2_steps.py @@ -1,36 +1,35 @@ import base64 import json import os -import urllib import unittest +import urllib from datetime import datetime from pathlib import Path from urllib.request import Request, urlopen -from behave import ( - given, - when, - then, - register_type, - step, -) # pylint: disable=no-name-in-module - -from glom import glom import parse - -from algosdk import dryrun_results, encoding, error, mnemonic, source_map +from algosdk import ( + dryrun_results, + encoding, + error, + mnemonic, + source_map, +) from algosdk.error import AlgodHTTPError from algosdk.future import transaction +from algosdk.testing.dryrun import DryrunTestCaseMixin from algosdk.v2client import * from algosdk.v2client.models import ( - DryrunRequest, - DryrunSource, Account, ApplicationLocalState, + DryrunRequest, + DryrunSource, ) -from algosdk.testing.dryrun import DryrunTestCaseMixin - -from tests.steps.steps import algod_port, token as daemon_token +from behave import register_type # pylint: disable=no-name-in-module +from behave import given, step, then, when +from glom import glom +from tests.steps.steps import algod_port +from tests.steps.steps import token as daemon_token @parse.with_pattern(r".*") diff --git a/tests/steps/steps.py b/tests/steps/steps.py index 99e8ea9a..59102280 100644 --- a/tests/steps/steps.py +++ b/tests/steps/steps.py @@ -1,27 +1,73 @@ -from behave import given, when, then import base64 -from algosdk import kmd -from algosdk.future import transaction -from algosdk import encoding -from algosdk import algod -from algosdk import account -from algosdk import mnemonic -from algosdk import wallet -from algosdk import auction -from algosdk import util -from algosdk import constants -from algosdk import logic -from algosdk.future import template import os +import random +import time from datetime import datetime -import hashlib +from algosdk import ( + account, + algod, + auction, + encoding, + kmd, + logic, + mnemonic, + util, + wallet, +) +from algosdk.future import transaction +from behave import given, then, when from nacl.signing import SigningKey token = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" algod_port = 60000 kmd_port = 60001 +DEV_ACCOUNT_INITIAL_MICROALGOS: int = 10_000_000 + + +def wait_for_algod_transaction_processing_to_complete(): + """ + wait_for_algod_transaction_processing_to_complete is a Dev mode helper method that's a rough analog to `context.app_acl.status_after_block(last_round + 2)`. + *

+ * Since Dev mode produces blocks on a per transaction basis, it's possible algod generates a block _before_ the corresponding SDK call to wait for a block. + * Without _any_ wait, it's possible the SDK looks for the transaction before algod completes processing. + * So, the method performs a local sleep to simulate waiting for a block. + """ + time.sleep(0.5) + + +# Initialize a transient account in dev mode to make payment transactions. +def initialize_account(context, account): + payment = transaction.PaymentTxn( + sender=context.accounts[0], + sp=context.acl.suggested_params_as_object(), + receiver=account, + amt=DEV_ACCOUNT_INITIAL_MICROALGOS, + ) + signed_payment = context.wallet.sign_transaction(payment) + context.acl.send_transaction(signed_payment) + # Wait to let transaction get confirmed in dev mode in v1. + wait_for_algod_transaction_processing_to_complete() + + +# Send a self-payment transaction to itself to advance blocks in dev mode. +def self_pay_transactions(context, num_txns=1): + if not hasattr(context, "dev_pk"): + context.dev_sk, context.dev_pk = account.generate_account() + initialize_account(context, context.dev_pk) + for _ in range(num_txns): + payment = transaction.PaymentTxn( + sender=context.dev_pk, + sp=context.acl.suggested_params_as_object(), + receiver=context.dev_pk, + amt=random.randint(1, int(DEV_ACCOUNT_INITIAL_MICROALGOS * 0.01)), + ) + signed_payment = payment.sign(context.dev_sk) + context.acl.send_transaction(signed_payment) + # Wait to let transaction get confirmed in dev mode in v1. + wait_for_algod_transaction_processing_to_complete() + @when("I create a wallet") def create_wallet(context): @@ -225,6 +271,7 @@ def status(context): @when("I get status after this block") def status_block(context): + self_pay_transactions(context) context.status_after = context.acl.status_after_block( context.status["lastRound"] ) @@ -232,6 +279,7 @@ def status_block(context): @then("I can get the block info") def block(context): + self_pay_transactions(context) context.block = context.acl.block_info(context.status["lastRound"] + 1) @@ -274,6 +322,12 @@ def gen_key_kmd(context): context.pk = context.wallet.generate_key() +@when("I generate a key using kmd for rekeying and fund it") +def gen_rekey_kmd(context): + context.rekey = context.wallet.generate_key() + initialize_account(context, context.rekey) + + @then("the key should be in the wallet") def key_in_wallet(context): keys = context.wallet.list_keys() @@ -320,6 +374,7 @@ def algod_client(context): algod_address = "http://localhost:" + str(algod_port) context.acl = algod.AlgodClient(token, algod_address) if context.acl.status()["lastRound"] < 2: + self_pay_transactions(context, 2) context.acl.status_after_block(2) @@ -334,8 +389,7 @@ def wallet_info(context): context.accounts = context.wallet.list_keys() -@given('default transaction with parameters {amt} "{note}"') -def default_txn(context, amt, note): +def default_txn_with_addr(context, amt, note, sender_addr): params = context.acl.suggested_params_as_object() context.last_round = params.first if note == "none": @@ -343,9 +397,19 @@ def default_txn(context, amt, note): else: note = base64.b64decode(note) context.txn = transaction.PaymentTxn( - context.accounts[0], params, context.accounts[1], int(amt), note=note + sender_addr, params, context.accounts[1], int(amt), note=note ) - context.pk = context.accounts[0] + context.pk = sender_addr + + +@given('default transaction with parameters {amt} "{note}"') +def default_txn(context, amt, note): + default_txn_with_addr(context, amt, note, context.accounts[0]) + + +@given('default transaction with parameters {amt} "{note}" and rekeying key') +def default_txn_rekey(context, amt, note): + default_txn_with_addr(context, amt, note, context.rekey) @given('default multisig transaction with parameters {amt} "{note}"') @@ -404,11 +468,10 @@ def send_msig_txn(context): @then("the transaction should go through") def check_txn(context): - last_round = context.acl.status()["lastRound"] + wait_for_algod_transaction_processing_to_complete() assert "type" in context.acl.pending_transaction_info( context.txn.get_txid() ) - context.acl.status_after_block(last_round + 2) assert "type" in context.acl.transaction_info( context.txn.sender, context.txn.get_txid() ) @@ -417,7 +480,7 @@ def check_txn(context): @then("I can get the transaction by ID") def get_txn_by_id(context): - context.acl.status_after_block(context.last_round + 2) + wait_for_algod_transaction_processing_to_complete() assert "type" in context.acl.transaction_by_id(context.txn.get_txid()) @@ -492,8 +555,7 @@ def check_save_txn(context): dir_path = os.path.dirname(os.path.dirname(dir_path)) stx = transaction.retrieve_from_file(dir_path + "/temp/txn.tx")[0] txid = stx.transaction.get_txid() - last_round = context.acl.status()["lastRound"] - context.acl.status_after_block(last_round + 2) + wait_for_algod_transaction_processing_to_complete() assert context.acl.transaction_info(stx.transaction.sender, txid)