Skip to content

Commit

Permalink
Document Sn Foundry test utils (#1085)
Browse files Browse the repository at this point in the history
* Minor structure and code improvements

* fix: openzeppelin_utils snforge dependency

* feat: update CHANGELOG

* fix: linter issues

* Update the documentation

* Add and improve the in-code documentation

* Run linter

* Support changes

* Move test utils doc to a separate page

* Fix success values in mocks

* Update packages/token/src/tests/erc20/test_erc20.cairo

Co-authored-by: Andrew Fleming <[email protected]>

* Update packages/token/src/tests/erc721/test_erc721.cairo

Co-authored-by: Andrew Fleming <[email protected]>

* Update packages/access/src/tests/test_ownable_twostep.cairo

Co-authored-by: Andrew Fleming <[email protected]>

* Update packages/account/src/tests/test_account.cairo

Co-authored-by: Andrew Fleming <[email protected]>

* Update packages/account/src/tests/test_signature.cairo

Co-authored-by: Andrew Fleming <[email protected]>

* Update packages/token/src/tests/erc1155/test_dual1155.cairo

Co-authored-by: Andrew Fleming <[email protected]>

* Update packages/token/src/tests/erc1155/test_erc1155.cairo

Co-authored-by: Andrew Fleming <[email protected]>

* feat: apply review updates

* refactor: format files

* fix: tests

* Update test utils module name in doc

* Fix review issues

* Improve structure of testing doc page

* Minor doc fixes

* Add hint on how to add openzeppelin_testing dependency

* Change references to SnFoundry to snforge

* Change variable names in the example to more generic ones

* Change back openzeppelin::utils module name in doc

* Change module reference in doc from testutils to testing

---------

Co-authored-by: Eric Nordelo <[email protected]>
Co-authored-by: Andrew Fleming <[email protected]>
  • Loading branch information
3 people authored Aug 8, 2024
1 parent a13bae3 commit c2c1b41
Show file tree
Hide file tree
Showing 16 changed files with 367 additions and 189 deletions.
1 change: 1 addition & 0 deletions docs/modules/ROOT/nav.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@
*** xref:/api/upgrades.adoc[API Reference]

** xref:/api/utilities.adoc[Utilities]
** xref:/api/testing.adoc[Test Utilities]

* xref:backwards-compatibility.adoc[Backwards Compatibility]
* xref:contracts::index.adoc[Contracts for Solidity]
298 changes: 298 additions & 0 deletions docs/modules/ROOT/pages/api/testing.adoc
Original file line number Diff line number Diff line change
@@ -0,0 +1,298 @@
= Testing

:stark: https://docs.starknet.io/architecture-and-concepts/cryptography/stark-curve/[Stark]
:secp256k1: https://github.com/starkware-libs/cairo/blob/main/corelib/src/starknet/secp256k1.cairo[Secp256k1]

This module provides various helper functions for declaring, deploying,
and testing Smart Contracts using `snforge` toolchain from Starknet Foundry.

```cairo
use openzeppelin_testing;
```

The module isn't part of the `openzeppelin` package and to be accessible has to
be added as a separate dependency in `Scarb.toml`:

```
[dev-dependencies]
openzeppelin_testing = { git = "https://github.com/OpenZeppelin/cairo-contracts.git", tag = "v0.15.0-rc.0" }
```

== Test Utilities

[.contract]
[[testing-common]]
=== `++common++`

A module providing common test helpers.

```cairo
use openzeppelin_testing::common;
```

[.contract-index]
.Members
--
.Functions
* xref:#testing-common-panic_data_to_byte_array[`++panic_data_to_byte_array(panic_data)++`]
* xref:#testing-common-to_base_16_string[`++to_base_16_string(value)++`]
* xref:#testing-common-assert_entrypoint_not_found_error[`++assert_entrypoint_not_found_error(result, selector, contract_address)++`]

.Traits
* xref:#testing-common-IntoBase16StringTrait[`++IntoBase16StringTrait++`]
--

[#testing-common-Functions]
==== Functions

[.contract-item]
[[testing-common-panic_data_to_byte_array]]
===== `[.contract-item-name]#++panic_data_to_byte_array++#++(panic_data: Array<felt252>) → ByteArray++` [.item-kind]#function#

Converts panic data into a string (`ByteArray`).

`panic_data` is expected to be a valid serialized `ByteArray` with an extra `felt252` at the beginning, which is the BYTE_ARRAY_MAGIC.

[.contract-item]
[[testing-common-to_base_16_string]]
===== `[.contract-item-name]#++to_base_16_string++#++(value: felt252) → ByteArray++` [.item-kind]#function#

Converts a `felt252` to a `base16` string padded to 66 characters (including the `0x` prefix).

[.contract-item]
[[testing-common-assert_entrypoint_not_found_error]]
==== `[.contract-item-name]#++assert_entrypoint_not_found_error++#<T, +Drop<T>>(result: SyscallResult<T>, selector: felt252, contract_address: ContractAddress)` [.item-kind]#function#

Asserts that the syscall result of a call failed with an "Entrypoint not found" error,
following the Starknet Foundry emitted error format.

[#testing-common-Traits]
==== Traits

[.contract-item]
[[testing-common-IntoBase16StringTrait]]
==== `[.contract-item-name]#++IntoBase16StringTrait++#` [.item-kind]#trait#

A helper trait that enables a value to be represented as a `base16` string padded to 66 characters
(including the `0x` prefix). The type of the value must implement `Into<T, felt252>` to be
convertible to `felt252`.

Usage example:

```cairo
use openzeppelin_testing::common::IntoBase16String;

let expected_panic_message = format!(
"Entry point selector {} not found in contract {}",
selector.into_base_16_string(),
contract_address.into_base_16_string()
);
```

[.contract]
[[testing-deployment]]
=== `++deployment++`

```cairo
use openzeppelin_testing::deployment;
```

A module containing utilities that simplify declaring and deploying contracts using the `snforge` toolchain.

[.contract-index]
.Members
--
.Functions
* xref:#testing-deployment-declare_class[`++declare_class(contract_name)++`]
* xref:#testing-deployment-deploy[`++deploy(contract_class, calldata)++`]
* xref:#testing-deployment-deploy_at[`++deploy_at(contract_class, contract_address, calldata)++`]
* xref:#testing-deployment-deploy_another_at[`++deploy_another_at(existing, target_address, calldata)++`]
* xref:#testing-deployment-declare_and_deploy[`++declare_and_deploy(contract_name, calldata)++`]
* xref:#testing-deployment-declare_and_deploy_at[`++declare_and_deploy_at(contract_name, target_address, calldata)++`]
--

[#testing-deployment-Functions]
==== Functions

[.contract-item]
[[testing-deployment-declare_class]]
==== `[.contract-item-name]#++declare_class++#++(contract_name: ByteArray) → ContractClass++` [.item-kind]#function#

Declares a contract with a `snforge` `declare` call and unwraps the result.

[.contract-item]
[[testing-deployment-deploy]]
==== `[.contract-item-name]#++deploy++#++(contract_class: ContractClass, calldata: Array<felt252>) → ContractAddress++` [.item-kind]#function#

Deploys an instance of a contract and unwraps the result.

[.contract-item]
[[testing-deployment-deploy_at]]
==== `[.contract-item-name]#++deploy_at++#++(contract_class: ContractClass, target_address: ContractAddress, calldata: Array<felt252>)++` [.item-kind]#function#

Deploys an instance of a contract at a given address.

[.contract-item]
[[testing-deployment-deploy_another_at]]
==== `[.contract-item-name]#++deploy_another_at++#++(existing: ContractAddress, target_address: ContractAddress, calldata: Array<felt252>)++` [.item-kind]#function#

Deploys a contract using the class hash from another already-deployed contract.

Note that currently, `snforge` does not support redeclaring a contract class. Consequently,
there is no direct method to deploy a second instance of a contract if neither its `ContractClass`
nor its `class_hash` is available in the context. This helper function provides a solution by retrieving
the class hash from an existing contract and using it to facilitate the deployment.

```cairo
use openzeppelin_testing::deploy_another_at;

let alice_address = setup_account(array!['ALICE_PUBKEY']);
let bob_address = contract_address_const::<'BOB'>();
deploy_another_at(alice_address, bob_address, array!['BOB_PUBKEY']);
```

[.contract-item]
[[testing-deployment-declare_and_deploy]]
==== `[.contract-item-name]#++declare_and_deploy++#++(contract_name: ByteArray, calldata: Array<felt252>) → ContractAddress++` [.item-kind]#function#

Combines the declaration of a class and the deployment of a contract into one function call.

[.contract-item]
[[testing-deployment-declare_and_deploy_at]]
==== `[.contract-item-name]#++declare_and_deploy_at++#++(contract_name: ByteArray, target_address: ContractAddress, calldata: Array<felt252>)++` [.item-kind]#function#

Combines the declaration of a class and the deployment of a contract at the given address into one function call.

[.contract]
[[testing-events]]
=== `++events++`

```cairo
use openzeppelin_testing::events;
use openzeppelin_testing::events::EventSpyExt;
```

A module offering an extended set of functions for handling emitted events, enhancing the default
event utilities provided by `snforge`. These functions are accessible via the `EventSpyExt`
trait implemented on the `EventSpy` struct.

[.contract-index]
.Members
--
.Functions
* xref:#testing-events-assert_only_event[`++assert_only_event(self, from_address, event)++`]
* xref:#testing-events-assert_emitted_single[`++assert_emitted_single(self, from_address, expected_event)++`]
* xref:#testing-events-drop_event[`++drop_event(self)++`]
* xref:#testing-events-drop_n_events[`++drop_n_events(self, number_to_drop)++`]
* xref:#testing-events-drop_all_events[`++drop_all_events(self)++`]
* xref:#testing-events-assert_no_events_left[`++assert_no_events_left(self)++`]
* xref:#testing-events-assert_no_events_left_from[`++assert_no_events_left_from(self, from_address)++`]
* xref:#testing-events-count_events_from[`++count_events_from(self, from_address)++`]
--

[#testing-events-Functions]
==== Functions

[.contract-item]
[[testing-events-assert_only_event]]
==== `[.contract-item-name]#++assert_only_event++#++<T, +starknet::Event<T>, +Drop<T>>(ref self: EventSpy, from_address: ContractAddress, expected_event: T)++` [.item-kind]#function#

Ensures that `from_address` has emitted only the `expected_event` and no additional events.

[.contract-item]
[[testing-events-assert_emitted_single]]
==== `[.contract-item-name]#++assert_emitted_single++#++<T, +starknet::Event<T>, +Drop<T>>(ref self: EventSpy, from_address: ContractAddress, expected_event: T)++` [.item-kind]#function#

Ensures that `from_address` has emitted the `expected_event`.

[.contract-item]
[[testing-events-drop_event]]
==== `[.contract-item-name]#++drop_event++#++(ref self: EventSpy)++` [.item-kind]#function#

Removes a single event from the queue. If the queue is empty, the function will panic.

[.contract-item]
[[testing-events-drop_n_events]]
==== `[.contract-item-name]#++drop_n_events++#++(ref self: EventSpy, number_to_drop: u32)++` [.item-kind]#function#

Removes `number_to_drop` events from the queue. If the queue is empty, the function will panic.

[.contract-item]
[[testing-events-drop_all_events]]
==== `[.contract-item-name]#++drop_all_events++#++(ref self: EventSpy)++` [.item-kind]#function#

Removes all events remaining on the queue. If the queue is empty already, the function will do nothing.

[.contract-item]
[[testing-events-assert_no_events_left]]
==== `[.contract-item-name]#++assert_no_events_left++#++(ref self: EventSpy)++` [.item-kind]#function#

Ensures that there are no events remaining on the queue.

[.contract-item]
[[testing-events-assert_no_events_left_from]]
==== `[.contract-item-name]#++assert_no_events_left_from++#++(ref self: EventSpy, from_address: ContractAddress)++` [.item-kind]#function#

Ensures that there are no events emitted from the given address remaining on the queue.

[.contract-item]
[[testing-events-count_events_from]]
==== `[.contract-item-name]#++count_events_from++#++(ref self: EventSpy, from_address: ContractAddress) → u32++` [.item-kind]#function#

Counts the number of remaining events emitted from the given address.

[.contract]
[[testing-signing]]
=== `++signing++`

```cairo
use openzeppelin_testing::signing;
```

A module offering utility functions for easier management of key pairs and signatures.

[.contract-index]
.Members
--
.Functions
* xref:#testing-signing-get_stark_keys_from[`++get_stark_keys_from(private_key)++`]
* xref:#testing-signing-get_secp256k1_keys_from[`++get_secp256k1_keys_from(private_key)++`]

.Traits
* xref:#testing-signing-SerializedSigning[`++SerializedSigning++`]
--

[#testing-signing-Functions]
==== Functions

[.contract-item]
[[testing-signing-get_stark_keys_from]]
==== `[.contract-item-name]#++get_stark_keys_from++#++(private_key: felt252) → StarkKeyPair++` [.item-kind]#function#

Builds a {stark} key pair from a private key represented by a `felt252` value.

[.contract-item]
[[testing-signing-get_secp256k1_keys_from]]
==== `[.contract-item-name]#++get_secp256k1_keys_from++#++(private_key: u256) → Secp256k1KeyPair++` [.item-kind]#function#

Builds a {secp256k1} key pair from a private key represented by a `u256` value.

[#testing-signing-Traits]
==== Traits

[.contract-item]
[[testing-signing-SerializedSigning]]
==== `[.contract-item-name]#++SerializedSigning++#` [.item-kind]#trait#

A helper trait that facilitates signing and converting the result signature into a serialized format.

Usage example:

```cairo
use openzeppelin_testing::signing::{
StarkKeyPair, get_stark_keys_from, StarkSerializedSigning
};

let key_pair = get_stark_keys_from('SECRET_KEY');
let serialized_signature = key_pair.serialized_sign('TX_HASH');
```
Loading

0 comments on commit c2c1b41

Please sign in to comment.