Skip to content
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
8 changes: 7 additions & 1 deletion docs/coverage.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,11 @@
See which `algorand-python` stubs are implemented by the `algorand-python-testing` library. See the [Concepts](testing-guide/concepts.md#types-of-algopy-stub-implementations) section for more details on the implementation categories. Refer to the [`algorand-python` stubs API](api.md) for the full list of stubs for which the `algorand-python-testing` library provides implementations referenced in the table below.

| Name | Implementation type |
| ------------------------------------------- | ------------------- |
|---------------------------------------------|---------------------|
| algopy.Account | Emulated |
| algopy.Application | Emulated |
| algopy.Asset | Emulated |
| algopy.Array | Native |
| algopy.BigUInt | Native |
| algopy.Box | Emulated |
| algopy.BoxMap | Emulated |
Expand All @@ -16,14 +17,19 @@ See which `algorand-python` stubs are implemented by the `algorand-python-testin
| algopy.CompiledContract | Mockable |
| algopy.CompiledLogicSig | Mockable |
| algopy.Contract | Emulated |
| algopy.FixedArray | Native |
| algopy.Global | Emulated |
| algopy.GlobalState | Emulated |
| algopy.ImmutableArray | Native |
| algopy.ImmutableFixedArray | Native |
| algopy.LocalState | Emulated |
| algopy.LogicSig | Emulated |
| algopy.OnCompleteAction | Native |
| algopy.OpUpFeeSource | Native |
| algopy.ReferenceArray | Native |
| algopy.StateTotals | Emulated |
| algopy.String | Native |
| algopy.Struct | Native |
| algopy.TemplateVar | Emulated |
| algopy.TransactionType | Native |
| algopy.Txn | Emulated |
Expand Down
5 changes: 5 additions & 0 deletions docs/testing-guide/concepts.md
Original file line number Diff line number Diff line change
Expand Up @@ -58,3 +58,8 @@ As explained in the [introduction](index.md), `algorand-python-testing` _injects
3. **Mockable**: Not implemented, but can be mocked or patched. For example, `algopy.abi_call` can be mocked to return specific values or behaviours; otherwise, it raises a `NotImplementedError`. This category covers cases where native or emulated implementation in a unit test context is impractical or overly complex.

For a full list of all public `algopy` types and their corresponding implementation category, refer to the [Coverage](../coverage.md) section.

## Data Validation

Algorand Python and the puya compiler have functionality to perform validation of transaction inputs via the `--validate-abi-args`, `--validate-abi-return` CLI arguments, `arc4.abimethod(validate_encoding=...)` decorator and `.validate()` methods.
The Algorand Python Testing library does *NOT* implement this validation behaviour, as you should test invalid inputs using an integrated test against a real Algorand network.
4 changes: 4 additions & 0 deletions scripts/refresh_test_artifacts.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,10 @@ def compile_contract(folder: Path) -> None:
str(contract_path),
"--out-dir",
"data",
# testing library does not perform ABI validation
# so ensure compiled contracts also do not, so behaviour matches
"--no-validate-abi-args",
"--no-validate-abi-return",
]
subprocess.run(
compile_cmd,
Expand Down
4 changes: 3 additions & 1 deletion scripts/validate_examples.py
Original file line number Diff line number Diff line change
Expand Up @@ -70,9 +70,11 @@ def generate_client(folder: Path) -> None:
text=True,
)
except subprocess.CalledProcessError as e:
if "No app specs" in e.stderr:
output = e.stdout + e.stderr
if "No app specs" in output:
logger.warning(f"No app spec found for: {folder}, skipping...")
else:
logger.exception(output)
raise


Expand Down
3 changes: 3 additions & 0 deletions src/_algopy_testing/arc4.py
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,9 @@ def bytes(self) -> algopy.Bytes:

return algopy.Bytes(self._value)

def validate(self) -> None:
pass

def __eq__(self, other: object) -> bool:
if isinstance(other, _ABIEncoded):
return self._type_info == other._type_info and self.bytes == other.bytes
Expand Down
1 change: 1 addition & 0 deletions src/_algopy_testing/decorators/arc4.py
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,7 @@ def abimethod( # noqa: PLR0913
resource_encoding: _ResourceEncoding = "value",
readonly: bool = False,
default_args: Mapping[str, str | object] | None = None,
validate_encoding: typing.Literal["unsafe_disabled", "args"] = "args", # noqa: ARG001
) -> Callable[[Callable[_P, _R]], Callable[_P, _R]] | Callable[_P, _R]:
from _algopy_testing.utilities.log import log

Expand Down
3 changes: 3 additions & 0 deletions src/_algopy_testing/models/account.py
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,9 @@ def bytes(self) -> Bytes:
def public_key(self) -> str:
return algosdk.encoding.encode_address(self._public_key) # type: ignore[no-any-return]

def validate(self) -> None:
pass

def __getattr__(self, name: str) -> typing.Any:
try:
return self.data.fields[name] # type: ignore[literal-required]
Expand Down
15 changes: 15 additions & 0 deletions src/_algopy_testing/primitives/array.py
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,9 @@ def serialize(self) -> bytes:
def from_bytes(cls, value: bytes, /) -> typing.Self:
return deserialize_from_bytes(cls, value)

def validate(self) -> None:
pass


class _FixedArrayMeta(type, typing.Generic[_TArrayItem, _TArrayLength]):
__concrete__: typing.ClassVar[dict[tuple[type, type], type]] = {}
Expand Down Expand Up @@ -255,6 +258,9 @@ def serialize(self) -> bytes:
def from_bytes(cls, value: bytes, /) -> typing.Self:
return deserialize_from_bytes(cls, value)

def validate(self) -> None:
pass


class _ImmutableArrayMeta(type):
__concrete__: typing.ClassVar[dict[type, type]] = {}
Expand Down Expand Up @@ -362,6 +368,9 @@ def serialize(self) -> bytes:
def from_bytes(cls, value: bytes, /) -> typing.Self:
return deserialize_from_bytes(cls, value)

def validate(self) -> None:
pass


class ReferenceArray(Reversible[_TArrayItem]):
def __init__(self, values: Iterable[_TArrayItem] = ()):
Expand Down Expand Up @@ -510,6 +519,9 @@ def serialize(self) -> bytes:
def from_bytes(cls, value: bytes, /) -> typing.Self:
return deserialize_from_bytes(cls, value)

def validate(self) -> None:
pass


@typing.dataclass_transform()
class Struct(Serializable, MutableBytes):
Expand Down Expand Up @@ -558,6 +570,9 @@ def from_bytes(cls, value: bytes, /) -> typing.Self:
def _update_backing_value(self) -> None:
self._value = serialize_to_bytes(self)

def validate(self) -> None:
pass


def zero_bytes(typ: type[_T]) -> _T:
# Get the static size of the type
Expand Down
28 changes: 15 additions & 13 deletions tests/artifacts/AVM12/data/Contract.approval.teal
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

// algopy.arc4.ARC4Contract.approval_program() -> uint64:
main:
intcblock 6 0 1
intcblock 1 6 0
bytecblock base64(DIEBQw==)
// tests/artifacts/AVM12/contract.py:4
// class Contract(ARC4Contract, avm_version=12):
Expand All @@ -25,7 +25,9 @@ main___algopy_default_create@9:
txn ApplicationID
!
&&
return // on error: OnCompletion must be NoOp && can only call when creating
assert // OnCompletion must be NoOp && can only call when creating
intc_0 // 1
return


// tests.artifacts.AVM12.contract.Contract.test_falcon_verify[routing]() -> void:
Expand All @@ -51,11 +53,11 @@ test_reject_version:
itxn_begin
bytec_0 // base64(DIEBQw==)
itxn_field ClearStateProgramPages
pushbytes base64(DDEbQQAagASg6BhyNhoAjgEAAQAxGYEEEjEYEERCAAgxGRQxGBQQQzFEgQESQw==)
pushbytes base64(DDEbQQAagASg6BhyNhoAjgEAAQAxGYEEEjEYEERCAAsxGRQxGBQQRIEBQzFEgQESRIEBQw==)
itxn_field ApprovalProgramPages
intc_0 // appl
intc_1 // appl
itxn_field TypeEnum
intc_1 // 0
intc_2 // 0
itxn_field Fee
itxn_submit
itxn CreatedApplicationID
Expand All @@ -75,7 +77,7 @@ test_reject_version:
// ContractV0.update, app_id=app, reject_version=1, compiled=compile_contract(ContractV1)
bytec_0 // base64(DIEBQw==)
itxn_field ClearStateProgramPages
pushbytes base64(DDEbQQAagAQkN408NhoAjgEAAQAxGYEFEjEYEERCAAgxGRQxGBQQQzFEgQISQw==)
pushbytes base64(DDEbQQAagAQkN408NhoAjgEAAQAxGYEFEjEYEERCAAsxGRQxGBQQRIEBQzFEgQISRIEBQw==)
itxn_field ApprovalProgramPages
// tests/artifacts/AVM12/contract.py:15-17
// arc4.arc4_update(
Expand All @@ -85,7 +87,7 @@ test_reject_version:
itxn_field OnCompletion
// tests/artifacts/AVM12/contract.py:16
// ContractV0.update, app_id=app, reject_version=1, compiled=compile_contract(ContractV1)
intc_2 // 1
intc_0 // 1
itxn_field RejectVersion
dup
itxn_field ApplicationID
Expand All @@ -95,17 +97,17 @@ test_reject_version:
// )
pushbytes 0xa0e81872 // method "update()void"
itxn_field ApplicationArgs
intc_0 // appl
intc_1 // appl
itxn_field TypeEnum
intc_1 // 0
intc_2 // 0
itxn_field Fee
itxn_submit
// tests/artifacts/AVM12/contract.py:18
// assert app.version == 1, "should be version 1"
dup
app_params_get AppVersion
assert // application exists
intc_2 // 1
intc_0 // 1
==
assert // should be version 1
// tests/artifacts/AVM12/contract.py:20-29
Expand Down Expand Up @@ -137,9 +139,9 @@ test_reject_version:
itxn_field ApplicationArgs
// tests/artifacts/AVM12/contract.py:20
// itxn.ApplicationCall(
intc_0 // appl
intc_1 // appl
itxn_field TypeEnum
intc_1 // 0
intc_2 // 0
itxn_field Fee
// tests/artifacts/AVM12/contract.py:20-29
// itxn.ApplicationCall(
Expand All @@ -155,5 +157,5 @@ test_reject_version:
itxn_submit
// tests/artifacts/AVM12/contract.py:9
// @arc4.abimethod
intc_2 // 1
intc_0 // 1
return
Loading