Skip to content

Commit 77e8a14

Browse files
committed
refactor: expand accepted types for account value generator
1 parent e4f9457 commit 77e8a14

File tree

6 files changed

+48
-37
lines changed

6 files changed

+48
-37
lines changed

docs/testing-guide/avm-types.md

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -103,7 +103,7 @@ asset = context.ledger.get_asset(asset_id=random_asset.id)
103103
104104
# Update an asset
105105
context.ledger.update_asset(
106-
asset=random_asset,
106+
random_asset,
107107
name=..., # Optional: New asset name
108108
total=..., # Optional: New total supply
109109
decimals=..., # Optional: Number of decimals
@@ -130,7 +130,7 @@ account = algopy.Account(raw_address) # zero address by default
130130
# Generate a random account
131131
random_account = context.any.account(
132132
address=str(raw_address), # Optional: Specify a custom address, defaults to a random address
133-
opted_asset_balances={}, # Optional: Specify opted asset balances as dict of algopy.UInt64 as key and algopy.UInt64 as value
133+
opted_asset_balances={}, # Optional: Specify opted asset balances as dict of assets to balance
134134
opted_apps=[], # Optional: Specify opted apps as sequence of algopy.Application objects
135135
balance=..., # Optional: Specify an initial balance
136136
min_balance=..., # Optional: Specify a minimum balance
@@ -145,15 +145,15 @@ random_account = context.any.account(
145145
# Generate a random account that is opted into a specific asset
146146
mock_asset = context.any.asset()
147147
mock_account = context.any.account(
148-
opted_asset_balances={mock_asset.id: algopy.UInt64(123)}
148+
opted_asset_balances={mock_asset: 123}
149149
)
150150
151151
# Get an account by address
152152
account = context.ledger.get_account(str(mock_account))
153153
154154
# Update an account
155155
context.ledger.update_account(
156-
str(mock_account),
156+
mock_account,
157157
balance=..., # Optional: New balance
158158
min_balance=..., # Optional: New minimum balance
159159
auth_address=context.any.account(), # Optional: New auth address
@@ -196,7 +196,7 @@ app = context.ledger.get_app(app_id=random_app.id)
196196
197197
# Update an application
198198
context.ledger.update_app(
199-
app=random_app,
199+
random_app,
200200
approval_program=..., # Optional: New approval program
201201
clear_state_program=..., # Optional: New clear state program
202202
global_num_uint=..., # Optional: New number of global uint values

src/_algopy_testing/context_helpers/ledger_context.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -107,10 +107,11 @@ def update_asset_holdings(
107107

108108
address = _get_address(account)
109109
account_data = self._account_data[address]
110-
asset = self.get_asset(_get_asset_id(asset))
110+
asset_id = _get_asset_id(asset)
111+
asset = self.get_asset(asset_id)
111112

112113
holdings = account_data.opted_assets.setdefault(
113-
asset.id,
114+
asset_id,
114115
AssetHolding(balance=UInt64(), frozen=asset.default_frozen),
115116
)
116117
if balance is not None:

src/_algopy_testing/models/account.py

Lines changed: 14 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -33,22 +33,21 @@ class AccountFields(typing.TypedDict, total=False):
3333

3434

3535
def get_empty_account() -> AccountContextData:
36-
zero = UInt64()
3736
return AccountContextData(
3837
fields={
39-
"balance": zero,
38+
"balance": UInt64(),
4039
"min_balance": UInt64(DEFAULT_ACCOUNT_MIN_BALANCE),
4140
"auth_address": Account(),
42-
"total_num_uint": zero,
43-
"total_num_byte_slice": zero,
44-
"total_extra_app_pages": zero,
45-
"total_apps_created": zero,
46-
"total_apps_opted_in": zero,
47-
"total_assets_created": zero,
48-
"total_assets": zero,
49-
"total_boxes": zero,
50-
"total_box_bytes": zero,
51-
}
41+
"total_num_uint": UInt64(),
42+
"total_num_byte_slice": UInt64(),
43+
"total_extra_app_pages": UInt64(),
44+
"total_apps_created": UInt64(),
45+
"total_apps_opted_in": UInt64(),
46+
"total_assets_created": UInt64(),
47+
"total_assets": UInt64(),
48+
"total_boxes": UInt64(),
49+
"total_box_bytes": UInt64(),
50+
},
5251
)
5352

5453

@@ -64,12 +63,12 @@ class AccountContextData:
6463
6564
Attributes:
6665
opted_assets (dict[int, AssetHolding]): Mapping of asset IDs to holdings.
67-
opted_apps (dict[int, algopy.UInt64]): Mapping of application IDs to instances.
66+
opted_apps (dict[int, algopy.Application]): Mapping of application IDs to instances.
6867
fields (AccountFields): Additional account fields.
6968
"""
7069

71-
opted_assets: dict[algopy.UInt64, AssetHolding] = dataclasses.field(default_factory=dict)
72-
opted_apps: dict[algopy.UInt64, algopy.Application] = dataclasses.field(default_factory=dict)
70+
opted_assets: dict[int, AssetHolding] = dataclasses.field(default_factory=dict)
71+
opted_apps: dict[int, algopy.Application] = dataclasses.field(default_factory=dict)
7372
fields: AccountFields = dataclasses.field(default_factory=AccountFields) # type: ignore[arg-type]
7473

7574

src/_algopy_testing/models/asset.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -45,14 +45,14 @@ def balance(self, account: algopy.Account) -> algopy.UInt64:
4545

4646
account_data = lazy_context.get_account_data(account.public_key)
4747

48-
if int(self.id) not in account_data.opted_assets:
48+
if self.int_ not in account_data.opted_assets:
4949
raise ValueError(
5050
"The asset is not opted into the account! "
5151
"Use `ctx.any.account(opted_asset_balances={{ASSET_ID: VALUE}})` "
5252
"to set emulated opted asset into the account."
5353
)
5454

55-
return account_data.opted_assets[self.id].balance
55+
return account_data.opted_assets[self.int_].balance
5656

5757
def frozen(self, account: algopy.Account) -> bool:
5858
from _algopy_testing.context_helpers import lazy_context
@@ -64,7 +64,7 @@ def frozen(self, account: algopy.Account) -> bool:
6464
"Use `ctx.any.account(opted_asset_balances={{ASSET_ID: VALUE}})` "
6565
"to set emulated opted asset into the account."
6666
)
67-
return account_data.opted_assets[self.id].frozen
67+
return account_data.opted_assets[self.int_].frozen
6868

6969
@property
7070
def fields(self) -> AssetFields:

src/_algopy_testing/op/misc.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -171,8 +171,9 @@ def _get_asset_holding(
171171
return UInt64(0), False
172172

173173
account_data = lazy_context.get_account_data(account.public_key)
174-
holding = account_data.opted_assets.get(asset.id)
175-
if holding is None:
174+
try:
175+
holding = account_data.opted_assets[asset.id.value]
176+
except KeyError:
176177
return UInt64(0), False
177178

178179
if field == "balance":

src/_algopy_testing/value_generators/avm.py

Lines changed: 20 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
MAX_UINT512,
1616
)
1717
from _algopy_testing.context_helpers import lazy_context
18-
from _algopy_testing.models.account import AccountFields, AssetHolding
18+
from _algopy_testing.models.account import AccountFields
1919
from _algopy_testing.models.application import ApplicationContextData, ApplicationFields
2020
from _algopy_testing.models.asset import AssetFields
2121
from _algopy_testing.utils import generate_random_int
@@ -70,10 +70,13 @@ def string(self, length: int = MAX_BYTES_SIZE) -> algopy.String:
7070
def account(
7171
self,
7272
address: str | None = None,
73-
opted_asset_balances: dict[algopy.UInt64, algopy.UInt64] | None = None,
74-
opted_apps: typing.Sequence[algopy.Application] = (),
73+
opted_asset_balances: (
74+
dict[algopy.Asset | algopy.UInt64 | int, algopy.UInt64 | int] | None
75+
) = None,
76+
opted_apps: typing.Sequence[algopy.Application | algopy.UInt64 | int] = (),
7577
**account_fields: typing.Unpack[AccountFields],
7678
) -> algopy.Account:
79+
"""Initialize a new account with specified fields and balances."""
7780
import algopy
7881

7982
if address is not None and address in lazy_context.ledger._account_data:
@@ -86,26 +89,26 @@ def account(
8689
if key not in AccountFields.__annotations__:
8790
raise AttributeError(f"Invalid field '{key}' for Account")
8891

92+
ledger = lazy_context.ledger
8993
new_account_address = address or algosdk.account.generate_account()[1]
9094
new_account = algopy.Account(new_account_address)
9195
# defaultdict of account_data ensures we get a new initialized account
9296
account_data = lazy_context.get_account_data(new_account_address)
9397
# update so defaults are preserved
9498
account_data.fields.update(account_fields)
95-
# can set these since it is a new account
96-
account_data.opted_assets = {}
9799
for asset_id, balance in (opted_asset_balances or {}).items():
98-
asset = lazy_context.get_asset_data(asset_id)
99-
account_data.opted_assets[asset_id] = AssetHolding(
100-
balance=balance, frozen=asset["default_frozen"]
100+
ledger.update_asset_holdings(
101+
new_account_address,
102+
asset_id,
103+
balance=balance,
101104
)
102-
account_data.opted_apps = {app.id: app for app in opted_apps}
105+
account_data.opted_apps = {_get_app_id(app): ledger.get_app(app) for app in opted_apps}
103106
return new_account
104107

105108
def asset(
106109
self, asset_id: int | None = None, **asset_fields: typing.Unpack[AssetFields]
107110
) -> algopy.Asset:
108-
r"""Generate and add a new asset with a unique ID."""
111+
"""Generate and add a new asset with a unique ID."""
109112
import algopy
110113

111114
if asset_id and asset_id in lazy_context.ledger._asset_data:
@@ -193,3 +196,10 @@ def bytes(self, length: int | None = None) -> algopy.Bytes:
193196
"""
194197
length = length or MAX_BYTES_SIZE
195198
return _algopy_testing.Bytes(secrets.token_bytes(length))
199+
200+
201+
def _get_app_id(app: algopy.Application | algopy.UInt64 | int) -> int:
202+
from _algopy_testing.models import Application
203+
204+
app_id = app.id if isinstance(app, Application) else app
205+
return int(app_id)

0 commit comments

Comments
 (0)