Skip to content

chore: utils-v3 support #31

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Feb 18, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ dependencies = [
"pytest-mock>=3.10.0",
"pytest-xdist[psutil]>=3.3",
"py-algorand-sdk>=2.4.0",
"algokit-utils>=2.2.1",
"algokit-utils>=3.0.0",
"pytest-cov>=4.1.0",
"prettytable>=3.9.0",
"mypy==1.10",
Expand Down Expand Up @@ -171,7 +171,7 @@ dependencies = [
"pytest-mock>=3.10.0",
"pytest-xdist[psutil]>=3.3",
"py-algorand-sdk>=2.4.0",
"algokit-utils>=2.2.1",
"algokit-utils>=3.0.0",
"pytest-cov>=4.1.0",
"mypy==1.10",
]
Expand Down
12 changes: 6 additions & 6 deletions src/_algopy_testing/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
__all__ = [
"ARC4Contract",
"ARC4ValueGenerator",
"AVMValueGenerator",
"Account",
"AlgopyTestContext",
"Application",
Expand All @@ -39,20 +40,19 @@
"Bytes",
"Contract",
"GlobalState",
"ITxnGroupLoader",
"ITxnLoader",
"LedgerContext",
"LocalState",
"LogicSig",
"ITxnLoader",
"TxnValueGenerator",
"ITxnGroupLoader",
"OnCompleteAction",
"StateTotals",
"String",
"TemplateVar",
"LedgerContext",
"TransactionContext",
"AVMValueGenerator",
"TxnValueGenerator",
"TransactionType",
"TxnValueGenerator",
"TxnValueGenerator",
"UInt64",
"algopy_testing_context",
"arc4",
Expand Down
2 changes: 1 addition & 1 deletion src/_algopy_testing/models/txn_fields.py
Original file line number Diff line number Diff line change
Expand Up @@ -508,10 +508,10 @@ def wrapper(index: algopy.UInt64 | int) -> _T:
"AssetConfigFields",
"AssetFreezeFields",
"AssetTransferFields",
"TransactionFieldsGetter",
"KeyRegistrationFields",
"PaymentFields",
"TransactionFields",
"TransactionFieldsGetter",
"get_txn_defaults",
"narrow_field_type",
]
8 changes: 4 additions & 4 deletions src/algopy/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,14 @@
from . import arc4, gtxn, itxn, op

__all__ = [
"ARC4Contract",
"Account",
"Application",
"ARC4Contract",
"Asset",
"BigUInt",
"Box",
"BoxMap",
"BoxRef",
"Bytes",
"BytesBacked",
"CompiledContract",
Expand Down Expand Up @@ -56,7 +59,4 @@
"subroutine",
"uenumerate",
"urange",
"Box",
"BoxRef",
"BoxMap",
]
6 changes: 3 additions & 3 deletions tests/arc4/conftest.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
from pathlib import Path

import pytest
from algosdk.v2client.algod import AlgodClient
from algokit_utils import AlgorandClient

from tests.common import AVMInvoker, create_avm_invoker

Expand All @@ -10,5 +10,5 @@


@pytest.fixture(scope="module")
def get_avm_result(algod_client: AlgodClient) -> AVMInvoker:
return create_avm_invoker(APP_SPEC, algod_client)
def get_avm_result(algorand: AlgorandClient) -> AVMInvoker:
return create_avm_invoker(APP_SPEC, algorand)
52 changes: 25 additions & 27 deletions tests/arc4/test_arc4_method_signature.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,9 @@
import algopy
import algosdk
import pytest
from algokit_utils.beta.algorand_client import AlgorandClient, AssetCreateParams, PayParams
from algokit_utils import AlgoAmount, AlgorandClient, AssetCreateParams, PaymentParams
from algopy import arc4
from algosdk.atomic_transaction_composer import TransactionWithSigner
from algosdk.v2client.algod import AlgodClient

from tests.artifacts.Arc4ABIMethod.contract import (
AnotherStruct,
Expand All @@ -25,8 +24,8 @@


@pytest.fixture()
def get_avm_result(algod_client: AlgodClient) -> AVMInvoker:
return create_avm_invoker(APP_SPEC, algod_client)
def get_avm_result(algorand: AlgorandClient) -> AVMInvoker:
return create_avm_invoker(APP_SPEC, algorand)


@pytest.fixture()
Expand All @@ -36,25 +35,22 @@ def context() -> Generator[_algopy_testing.AlgopyTestContext, None, None]:


@pytest.fixture()
def funded_account(algod_client: AlgodClient, context: _algopy_testing.AlgopyTestContext) -> str:
pk, address = algosdk.account.generate_account()
assert isinstance(address, str)
algokit_utils.ensure_funded(
algod_client,
algokit_utils.EnsureBalanceParameters(
account_to_fund=address,
min_spending_balance_micro_algos=_FUNDED_ACCOUNT_SPENDING,
),
def funded_account(algorand: AlgorandClient, context: _algopy_testing.AlgopyTestContext) -> str:
account = algorand.account.random()
algorand.account.ensure_funded_from_environment(
account_to_fund=account,
min_spending_balance=algokit_utils.AlgoAmount(micro_algo=_FUNDED_ACCOUNT_SPENDING),
)

# ensure context has the same account with matching balance
context.any.account(address, balance=algopy.Global.min_balance + _FUNDED_ACCOUNT_SPENDING)
return address
context.any.account(
account.address, balance=algopy.Global.min_balance + _FUNDED_ACCOUNT_SPENDING
)
return account.address


@pytest.fixture()
def other_app_id(algod_client: AlgodClient, context: _algopy_testing.AlgopyTestContext) -> int:
second_invoker = create_avm_invoker(APP_SPEC, algod_client)
def other_app_id(algorand: AlgorandClient, context: _algopy_testing.AlgopyTestContext) -> int:
second_invoker = create_avm_invoker(APP_SPEC, algorand)
client = second_invoker.client
app_id = client.app_id

Expand Down Expand Up @@ -127,14 +123,14 @@ def test_app_args_is_correct_with_txn(
"with_txn",
value="hello",
pay=TransactionWithSigner(
txn=algorand.transactions.payment(
PayParams(
txn=algorand.create_transaction.payment(
PaymentParams(
sender=localnet_creator_address,
receiver=localnet_creator_address,
amount=123,
amount=AlgoAmount(micro_algo=123),
)
),
signer=algorand.account.get_signer("default"),
signer=algorand.account.get_signer(localnet_creator_address),
),
arr=[1, 2],
)
Expand Down Expand Up @@ -169,7 +165,9 @@ def test_app_args_is_correct_with_asset(
sender=localnet_creator_address,
total=123,
)
)["confirmation"]["asset-index"]
).confirmation[
"asset-index"
] # type: ignore[call-overload]

# act
get_avm_result("with_asset", value="hello", asset=asa_id, arr=[1, 2])
Expand Down Expand Up @@ -333,14 +331,14 @@ def test_prepare_txns_with_complex(
"complex_sig",
struct1=((1, "2"), (1, "2"), 3, 4),
txn=TransactionWithSigner(
txn=algorand.transactions.payment(
PayParams(
txn=algorand.create_transaction.payment(
PaymentParams(
sender=localnet_creator_address,
receiver=localnet_creator_address,
amount=123,
amount=AlgoAmount(micro_algo=123),
)
),
signer=algorand.account.get_signer("default"),
signer=algorand.account.get_signer(localnet_creator_address),
),
acc=funded_account,
five=[5],
Expand Down
99 changes: 46 additions & 53 deletions tests/common.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,14 @@

import algosdk
from algokit_utils import (
Account,
ApplicationClient,
EnsureBalanceParameters,
ensure_funded,
get_localnet_default_account,
AlgorandClient,
AppClient,
AppClientMethodCallParams,
AppFactory,
AppFactoryCreateMethodCallParams,
AppFactoryCreateParams,
AppFactoryParams,
SigningAccount,
)
from algosdk.v2client.algod import AlgodClient

Expand All @@ -21,65 +24,70 @@ class AVMInvoker:
"""Protocol used in global test fixtures to simplify invocation of AVM methods via an Algokit
typed client."""

def __init__(self, client: ApplicationClient):
def __init__(self, client: AppClient, factory: AppFactory):
self.client = client
self.factory = factory

def __call__(
self,
method: str,
on_complete: algosdk.transaction.OnComplete = algosdk.transaction.OnComplete.NoOpOC,
**kwargs: typing.Any,
) -> object:
response = self.client.call(
method,
transaction_parameters={
# random note avoids duplicate txn if tests are running concurrently
"note": _random_note(),
"accounts": kwargs.pop("accounts", None),
"foreign_apps": kwargs.pop("foreign_apps", None),
"foreign_assets": kwargs.pop("foreign_assets", None),
"suggested_params": kwargs.pop("suggested_params", None),
"on_complete": on_complete,
},
**kwargs,
response = self.client.send.call(
AppClientMethodCallParams(
method=method,
note=_random_note(),
account_references=kwargs.pop("accounts", None),
app_references=kwargs.pop("foreign_apps", None),
asset_references=kwargs.pop("foreign_assets", None),
on_complete=on_complete,
static_fee=kwargs.pop("static_fee", None),
args=[item[1] for item in kwargs.items()],
),
)
if response.decode_error:
raise ValueError(response.decode_error)
result = response.return_value
if result is None:
return response.tx_info.get("logs", None)
if response.returns and len(response.returns) > 0 and response.returns[0].decode_error:
raise ValueError(response.returns[0].decode_error)
result = response.abi_return
if result is None and response.returns and len(response.returns) > 0:
assert response.returns[0].tx_info
return response.returns[0].tx_info.get("logs", None)
if isinstance(result, list) and all(
isinstance(i, int) and i >= 0 and i <= 255 for i in result
):
return bytes(result)
return bytes(result) # type: ignore[arg-type]
return result


def _random_note() -> bytes:
return secrets.token_bytes(8)


def create_avm_invoker(app_spec: Path, algod_client: AlgodClient) -> AVMInvoker:
client = ApplicationClient(
algod_client,
app_spec,
signer=get_localnet_default_account(algod_client),
)

client.create(
transaction_parameters={
# random note avoids duplicate txn if tests are running concurrently
"note": _random_note(),
}
def create_avm_invoker(app_spec: Path, algorand: AlgorandClient) -> AVMInvoker:
dispenser = algorand.account.localnet_dispenser()
factory = AppFactory(
AppFactoryParams(
algorand=algorand,
app_spec=app_spec.read_text(),
default_sender=dispenser.address,
),
)
try:
client, _ = factory.send.bare.create(
AppFactoryCreateParams(note=_random_note()),
)
except Exception as __:
client, _ = factory.send.create(
AppFactoryCreateMethodCallParams(method="create", note=_random_note()),
)

return AVMInvoker(client)
return AVMInvoker(client, factory)


def generate_test_asset( # noqa: PLR0913
*,
algod_client: AlgodClient,
sender: Account,
sender: SigningAccount,
total: int | None = None,
decimals: int = 0,
default_frozen: bool = False,
Expand Down Expand Up @@ -134,18 +142,3 @@ def generate_test_asset( # noqa: PLR0913
return ptx["asset-index"]
else:
raise ValueError("Unexpected response from pending_transaction_info")


def generate_test_account(algod_client: AlgodClient) -> Account:
raw_account = algosdk.account.generate_account()
account = Account(private_key=raw_account[0], address=raw_account[1])

ensure_funded(
algod_client,
EnsureBalanceParameters(
account_to_fund=account,
min_spending_balance_micro_algos=INITIAL_BALANCE_MICRO_ALGOS,
),
)

return account
Loading