algorand-python-testing 0.1.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- algopy/__init__.py +42 -0
- algopy/arc4.py +1 -0
- algopy/gtxn.py +1 -0
- algopy/itxn.py +1 -0
- algopy/op.py +1 -0
- algopy/py.typed +0 -0
- algopy_testing/__init__.py +47 -0
- algopy_testing/arc4.py +1222 -0
- algopy_testing/constants.py +17 -0
- algopy_testing/context.py +769 -0
- algopy_testing/decorators/__init__.py +0 -0
- algopy_testing/decorators/abimethod.py +146 -0
- algopy_testing/decorators/subroutine.py +9 -0
- algopy_testing/enums.py +39 -0
- algopy_testing/gtxn.py +239 -0
- algopy_testing/itxn.py +353 -0
- algopy_testing/models/__init__.py +23 -0
- algopy_testing/models/account.py +128 -0
- algopy_testing/models/application.py +72 -0
- algopy_testing/models/asset.py +109 -0
- algopy_testing/models/contract.py +69 -0
- algopy_testing/models/global_values.py +67 -0
- algopy_testing/models/gtxn.py +40 -0
- algopy_testing/models/itxn.py +34 -0
- algopy_testing/models/transactions.py +158 -0
- algopy_testing/models/txn.py +111 -0
- algopy_testing/models/unsigned_builtins.py +15 -0
- algopy_testing/op.py +639 -0
- algopy_testing/primitives/__init__.py +6 -0
- algopy_testing/primitives/biguint.py +147 -0
- algopy_testing/primitives/bytes.py +173 -0
- algopy_testing/primitives/string.py +67 -0
- algopy_testing/primitives/uint64.py +210 -0
- algopy_testing/py.typed +0 -0
- algopy_testing/state/__init__.py +4 -0
- algopy_testing/state/global_state.py +73 -0
- algopy_testing/state/local_state.py +54 -0
- algopy_testing/utils.py +156 -0
- algorand_python_testing-0.1.0.dist-info/METADATA +29 -0
- algorand_python_testing-0.1.0.dist-info/RECORD +41 -0
- algorand_python_testing-0.1.0.dist-info/WHEEL +4 -0
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import typing
|
|
4
|
+
from typing import TYPE_CHECKING
|
|
5
|
+
|
|
6
|
+
if TYPE_CHECKING:
|
|
7
|
+
import algopy
|
|
8
|
+
|
|
9
|
+
_T = typing.TypeVar("_T")
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class LocalState(typing.Generic[_T]):
|
|
13
|
+
def __init__(
|
|
14
|
+
self,
|
|
15
|
+
type_: type[_T],
|
|
16
|
+
/,
|
|
17
|
+
*,
|
|
18
|
+
key: bytes | str = "",
|
|
19
|
+
description: str = "",
|
|
20
|
+
) -> None:
|
|
21
|
+
self.type_ = type_
|
|
22
|
+
self.key = key
|
|
23
|
+
self.description = description
|
|
24
|
+
self._state: dict[object, _T] = {}
|
|
25
|
+
|
|
26
|
+
def _validate_local_state_key(self, key: algopy.Account | algopy.UInt64 | int) -> None:
|
|
27
|
+
from algopy import Account, UInt64
|
|
28
|
+
|
|
29
|
+
if not isinstance(key, Account | UInt64 | int):
|
|
30
|
+
raise TypeError(f"Invalid key type {type(key)} for LocalState")
|
|
31
|
+
|
|
32
|
+
def __setitem__(self, key: algopy.Account | algopy.UInt64 | int, value: _T) -> None:
|
|
33
|
+
self._validate_local_state_key(key)
|
|
34
|
+
self._state[key] = value
|
|
35
|
+
|
|
36
|
+
def __getitem__(self, key: algopy.Account | algopy.UInt64 | int) -> _T:
|
|
37
|
+
self._validate_local_state_key(key)
|
|
38
|
+
return self._state[key]
|
|
39
|
+
|
|
40
|
+
def __delitem__(self, key: algopy.Account | algopy.UInt64 | int) -> None:
|
|
41
|
+
self._validate_local_state_key(key)
|
|
42
|
+
del self._state[key]
|
|
43
|
+
|
|
44
|
+
def __contains__(self, key: algopy.Account | algopy.UInt64 | int) -> bool:
|
|
45
|
+
self._validate_local_state_key(key)
|
|
46
|
+
return key in self._state
|
|
47
|
+
|
|
48
|
+
def get(self, key: algopy.Account | algopy.UInt64 | int, default: _T | None = None) -> _T:
|
|
49
|
+
self._validate_local_state_key(key)
|
|
50
|
+
return self._state.get(key, default if default is not None else self.type_())
|
|
51
|
+
|
|
52
|
+
def maybe(self, key: algopy.Account | algopy.UInt64 | int) -> tuple[object, bool]:
|
|
53
|
+
self._validate_local_state_key(key)
|
|
54
|
+
return self._state.get(key), key in self._state
|
algopy_testing/utils.py
ADDED
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import enum
|
|
4
|
+
import functools
|
|
5
|
+
import secrets
|
|
6
|
+
from typing import TYPE_CHECKING
|
|
7
|
+
|
|
8
|
+
import algosdk
|
|
9
|
+
import algosdk.transaction
|
|
10
|
+
|
|
11
|
+
from algopy_testing import arc4
|
|
12
|
+
from algopy_testing.constants import MAX_BYTES_SIZE, MAX_UINT8, MAX_UINT16, MAX_UINT64, MAX_UINT512
|
|
13
|
+
|
|
14
|
+
if TYPE_CHECKING:
|
|
15
|
+
import algopy
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def as_int(value: object, *, max: int | None) -> int: # noqa: A002
|
|
19
|
+
"""
|
|
20
|
+
Returns the underlying int value for any numeric type up to UInt512
|
|
21
|
+
|
|
22
|
+
Raises:
|
|
23
|
+
TypeError: If `value` is not a numeric type
|
|
24
|
+
ValueError: If not 0 <= `value` <= max
|
|
25
|
+
"""
|
|
26
|
+
|
|
27
|
+
from algopy_testing.primitives.biguint import BigUInt
|
|
28
|
+
from algopy_testing.primitives.uint64 import UInt64
|
|
29
|
+
|
|
30
|
+
match value:
|
|
31
|
+
case int(int_value):
|
|
32
|
+
pass
|
|
33
|
+
case UInt64(value=int_value):
|
|
34
|
+
pass
|
|
35
|
+
case BigUInt(value=int_value):
|
|
36
|
+
pass
|
|
37
|
+
case arc4.UIntN():
|
|
38
|
+
int_value = value.native.value
|
|
39
|
+
case arc4.BigUIntN():
|
|
40
|
+
int_value = value.native.value
|
|
41
|
+
# TODO: add arc4 numerics
|
|
42
|
+
case _:
|
|
43
|
+
raise TypeError(f"value must be a numeric type, not {type(value).__name__!r}")
|
|
44
|
+
if int_value < 0:
|
|
45
|
+
raise ValueError(f"expected positive value, got {int_value}")
|
|
46
|
+
if max is not None and int_value > max:
|
|
47
|
+
raise ValueError(f"expected value <= {max}, got: {int_value}")
|
|
48
|
+
return int_value
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
def as_int8(value: object) -> int:
|
|
52
|
+
return as_int(value, max=MAX_UINT8)
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
def as_int16(value: object) -> int:
|
|
56
|
+
return as_int(value, max=MAX_UINT16)
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
def as_int64(value: object) -> int:
|
|
60
|
+
return as_int(value, max=MAX_UINT64)
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
def as_int512(value: object) -> int:
|
|
64
|
+
return as_int(value, max=MAX_UINT512)
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
def as_bytes(value: object, *, max_size: int = MAX_BYTES_SIZE) -> bytes:
|
|
68
|
+
"""
|
|
69
|
+
Returns the underlying bytes value for bytes or Bytes type up to 4096
|
|
70
|
+
|
|
71
|
+
Raises:
|
|
72
|
+
TypeError: If `value` is not a bytes type
|
|
73
|
+
ValueError: If not 0 <= `len(value)` <= max_size
|
|
74
|
+
"""
|
|
75
|
+
from algopy_testing.primitives.bytes import Bytes
|
|
76
|
+
|
|
77
|
+
match value:
|
|
78
|
+
case bytes(bytes_value):
|
|
79
|
+
pass
|
|
80
|
+
case Bytes(value=bytes_value):
|
|
81
|
+
pass
|
|
82
|
+
case _:
|
|
83
|
+
raise TypeError(f"value must be a bytes or Bytes type, not {type(value).__name__!r}")
|
|
84
|
+
if len(bytes_value) > max_size:
|
|
85
|
+
raise ValueError(f"expected value length <= {max_size}, got: {len(bytes_value)}")
|
|
86
|
+
return bytes_value
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
def as_string(value: object) -> str:
|
|
90
|
+
from algopy_testing.primitives.string import String
|
|
91
|
+
|
|
92
|
+
match value:
|
|
93
|
+
case str(string_value) | String(value=string_value):
|
|
94
|
+
return string_value
|
|
95
|
+
case arc4.String():
|
|
96
|
+
return value.native.value
|
|
97
|
+
case _:
|
|
98
|
+
raise TypeError(f"value must be a string or String type, not {type(value).__name__!r}")
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
def int_to_bytes(x: int, pad_to: int | None = None) -> bytes:
|
|
102
|
+
result = x.to_bytes((x.bit_length() + 7) // 8, "big")
|
|
103
|
+
result = (
|
|
104
|
+
b"\x00" * (pad_to - len(result)) if pad_to is not None and len(result) < pad_to else b""
|
|
105
|
+
) + result
|
|
106
|
+
|
|
107
|
+
return result
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
def dummy_transaction_id() -> bytes:
|
|
111
|
+
private_key, address = algosdk.account.generate_account()
|
|
112
|
+
|
|
113
|
+
suggested_params = algosdk.transaction.SuggestedParams(fee=1000, first=0, last=1, gh="")
|
|
114
|
+
txn = algosdk.transaction.PaymentTxn(
|
|
115
|
+
sender=address,
|
|
116
|
+
receiver=address,
|
|
117
|
+
amt=1000,
|
|
118
|
+
sp=suggested_params,
|
|
119
|
+
note=secrets.token_bytes(8),
|
|
120
|
+
)
|
|
121
|
+
|
|
122
|
+
signed_txn = txn.sign(private_key)
|
|
123
|
+
txn_id = str(signed_txn.transaction.get_txid()).encode("utf-8")
|
|
124
|
+
return txn_id
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
class _TransactionStrType(enum.StrEnum):
|
|
128
|
+
PAYMENT = algosdk.constants.PAYMENT_TXN
|
|
129
|
+
KEYREG = algosdk.constants.KEYREG_TXN
|
|
130
|
+
ASSETCONFIG = algosdk.constants.ASSETCONFIG_TXN
|
|
131
|
+
ASSETTRANSFER = algosdk.constants.ASSETTRANSFER_TXN
|
|
132
|
+
ASSETFREEZE = algosdk.constants.ASSETFREEZE_TXN
|
|
133
|
+
APPCALL = algosdk.constants.APPCALL_TXN
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
@functools.cache
|
|
137
|
+
def txn_type_to_bytes(txn_type: int) -> algopy.Bytes:
|
|
138
|
+
import algopy
|
|
139
|
+
|
|
140
|
+
match txn_type:
|
|
141
|
+
case algopy.TransactionType.Payment:
|
|
142
|
+
result = _TransactionStrType.PAYMENT
|
|
143
|
+
case algopy.TransactionType.KeyRegistration:
|
|
144
|
+
result = _TransactionStrType.KEYREG
|
|
145
|
+
case algopy.TransactionType.AssetConfig:
|
|
146
|
+
result = _TransactionStrType.ASSETCONFIG
|
|
147
|
+
case algopy.TransactionType.AssetTransfer:
|
|
148
|
+
result = _TransactionStrType.ASSETTRANSFER
|
|
149
|
+
case algopy.TransactionType.AssetFreeze:
|
|
150
|
+
result = _TransactionStrType.ASSETFREEZE
|
|
151
|
+
case algopy.TransactionType.ApplicationCall:
|
|
152
|
+
result = _TransactionStrType.APPCALL
|
|
153
|
+
case _:
|
|
154
|
+
raise ValueError(f"invalid transaction type: {txn_type}")
|
|
155
|
+
|
|
156
|
+
return algopy.Bytes(bytes(result, encoding="utf-8"))
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
Metadata-Version: 2.3
|
|
2
|
+
Name: algorand-python-testing
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Algorand Python testing library
|
|
5
|
+
Project-URL: Documentation, https://github.com/algorandfoundation/puya/tree/main/algopy_testing#README.md
|
|
6
|
+
Project-URL: Issues, https://github.com/algorandfoundation/puya/issues
|
|
7
|
+
Project-URL: Source, https://github.com/algorandfoundation/puya/tree/main/algopy_testing
|
|
8
|
+
Author-email: Algorand Foundation <contact@algorand.foundation>
|
|
9
|
+
License-Expression: AGPL-3.0-or-later
|
|
10
|
+
Classifier: Development Status :: 4 - Beta
|
|
11
|
+
Classifier: Intended Audience :: Developers
|
|
12
|
+
Classifier: Programming Language :: Python
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
14
|
+
Classifier: Topic :: Software Development :: Testing
|
|
15
|
+
Requires-Python: >=3.12
|
|
16
|
+
Requires-Dist: coincurve>=19.0.1
|
|
17
|
+
Requires-Dist: ecdsa>=0.17.0
|
|
18
|
+
Requires-Dist: pycryptodomex<4,>=3.6.0
|
|
19
|
+
Requires-Dist: pynacl<2,>=1.4.0
|
|
20
|
+
Requires-Dist: algorand-python>=1,<1.2.0
|
|
21
|
+
Description-Content-Type: text/markdown
|
|
22
|
+
|
|
23
|
+
# Algorand Python Testing framework
|
|
24
|
+
|
|
25
|
+
Test your Algorand Python smart contracts using your favorite python testing tools.
|
|
26
|
+
|
|
27
|
+
## Contributing
|
|
28
|
+
|
|
29
|
+
We welcome contributions to this project! Read the [contributing guide](CONTRIBUTING.md) to get started.
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
algopy/__init__.py,sha256=l4bTRdKBn9a1WnMtTTvpWYdm-FN_YdBZldEUWcIEBL8,792
|
|
2
|
+
algopy/arc4.py,sha256=zE-cwwmeEBA0m22QiHIujeY0S5rdyf3gDqTyGgROAqI,48
|
|
3
|
+
algopy/gtxn.py,sha256=tS2waKIr3g79T1xcmE2iVcvu524xANsR8awXduUVoT0,48
|
|
4
|
+
algopy/itxn.py,sha256=gXida1ee8xMfRufZ24G4xPGGSe26Iwm9nqfHpQCxbpE,48
|
|
5
|
+
algopy/op.py,sha256=JmF0QiVmqF1Oqhj9VypCl7ef-r2r-dHPI0hxIS3LQD8,46
|
|
6
|
+
algopy/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
7
|
+
algopy_testing/__init__.py,sha256=MbIdz1mOyIpo_6smkfnT2BtHSZSm6TlVyLUxONLnyBQ,1018
|
|
8
|
+
algopy_testing/arc4.py,sha256=DJpZkHNkjGk23aReT_jzkd0U-RDlX3zoJ_MDnZOCRMM,41578
|
|
9
|
+
algopy_testing/constants.py,sha256=_LIk53HoJ_jRNMGMpU7hgd5WZEmz-G7OzwnTjGCpdwk,559
|
|
10
|
+
algopy_testing/context.py,sha256=oLZZ9vhWWkoEpeVD8JJb51IRLrCvF7_zYdkY1oXeYxE,24936
|
|
11
|
+
algopy_testing/enums.py,sha256=Vm2XG2-C2NpfMf8Pml908PydGOBWYawlu6P0N4nDX-g,565
|
|
12
|
+
algopy_testing/gtxn.py,sha256=90nZpNaYjeG7hJN0uCPZQ3_XkJRphlP_vXdtNt5VSrs,7119
|
|
13
|
+
algopy_testing/itxn.py,sha256=rDcUGleCiVkttv1k_0Cs03skbGZH_VnFE-1pNPjqgj8,11310
|
|
14
|
+
algopy_testing/op.py,sha256=OuZGLGumh66B3YZGpl6dJByf6WKRp6HFyZ3wsmMEUB8,18317
|
|
15
|
+
algopy_testing/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
16
|
+
algopy_testing/utils.py,sha256=7Ril5VA_kGN3RrnnCjCb3HYeVV8T9bNxLm6pFL8uNMc,4795
|
|
17
|
+
algopy_testing/decorators/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
18
|
+
algopy_testing/decorators/abimethod.py,sha256=G1NzHSOb30LnorgtSdNqf1k7mFiSUGarJuYSOQIUoSE,4995
|
|
19
|
+
algopy_testing/decorators/subroutine.py,sha256=7bQt-RBdqmbzosMTIllBh1KCEDUSyZwNSxXc-1guflw,192
|
|
20
|
+
algopy_testing/models/__init__.py,sha256=dAUnsLdcIAKlsIp7CvIlOx3Xu9IRf6pm4pnTDTjIXX8,655
|
|
21
|
+
algopy_testing/models/account.py,sha256=vR_furAVjNGkT1x8vJ8BQeOuCyjg3Cwc1pIGUkwX-R0,4215
|
|
22
|
+
algopy_testing/models/application.py,sha256=H5WuY0gAfoTQE9glutAhKTfQs-iC4Nm7czmOpv-OqiQ,2205
|
|
23
|
+
algopy_testing/models/asset.py,sha256=qsvC7vGDKTH_PHqybc0SAgzPLAjPgzgwK0aDliUxoLk,3471
|
|
24
|
+
algopy_testing/models/contract.py,sha256=_jnw6FSiTQFsJlpqiqzLiH6_ct77cJk2dQs1MBO1IRw,1955
|
|
25
|
+
algopy_testing/models/global_values.py,sha256=Hz6Vv2-Mc5I_Om5KsUHVP2nQVmBKDNgwZFlNTrDdyJw,2045
|
|
26
|
+
algopy_testing/models/gtxn.py,sha256=nVsqmhnvRA4av2W2KRA7z5CXbrWOfpb2Mq-mifiXJnQ,1418
|
|
27
|
+
algopy_testing/models/itxn.py,sha256=MItNb6vMsXKxHctR-1_vb5m4vv16iflQu8sz-nR__5Y,1159
|
|
28
|
+
algopy_testing/models/transactions.py,sha256=_vMHFX8Nk6JYWOs3A-KkCjLfM8rsWM8BDAXuv_pZV7w,4313
|
|
29
|
+
algopy_testing/models/txn.py,sha256=ZSx_NKMwXaxqu4fUbwjD0r9Bktbnx8T8jLIlEwzg__4,3767
|
|
30
|
+
algopy_testing/models/unsigned_builtins.py,sha256=kXg1ylxwgTjpzzUnOQUK6I_kjlEvQzQ9MB6gWOd1PLo,261
|
|
31
|
+
algopy_testing/primitives/__init__.py,sha256=qwhRtTef1hp9d8OAcwkhKhGc-NuEhaNxUoTlLzJFXk0,260
|
|
32
|
+
algopy_testing/primitives/biguint.py,sha256=Ggnh5XwgCIFIb33Nwh1jvA9Kyn8nGBdk5cbFI41AS8M,5137
|
|
33
|
+
algopy_testing/primitives/bytes.py,sha256=3XEYGmo0fkSDcVOqU2f4E9BxJgYfpVJTynM5XeZzvgs,5633
|
|
34
|
+
algopy_testing/primitives/string.py,sha256=D3_QXXbHyrEbVjXIZ6L7Oo-kfKEbbOCAW461-UPnFuk,2127
|
|
35
|
+
algopy_testing/primitives/uint64.py,sha256=kteVN31IfNXAB-Ny8qgEq0ZaoxzgZ9GzQwQWZCFKwGE,7046
|
|
36
|
+
algopy_testing/state/__init__.py,sha256=uRFuGSn7RMnhjVKWWdHnVtPNhNfTlQIoQaDPbcmkr3U,155
|
|
37
|
+
algopy_testing/state/global_state.py,sha256=1eJRY9SYzOdj_dnNUqF_vghh0t9Q6N1B1MzLX0hKPxU,1662
|
|
38
|
+
algopy_testing/state/local_state.py,sha256=7m98XD5Afbn72yvgw9Q_VLAEhObLBv_5trp11ZkGwuE,1788
|
|
39
|
+
algorand_python_testing-0.1.0.dist-info/METADATA,sha256=I_qIAhhc679Y_E5R2OdVzeOQNjAjldHWYWGWiTxfgDU,1193
|
|
40
|
+
algorand_python_testing-0.1.0.dist-info/WHEEL,sha256=zEMcRr9Kr03x1ozGwg5v9NQBKn3kndp6LSoSlVg-jhU,87
|
|
41
|
+
algorand_python_testing-0.1.0.dist-info/RECORD,,
|