Skip to main content

Testing framework

The testing framework includes functions and operations to simplify dapps testing using the ft4 library. You can import the testing framework as follows:

import test: lib.ft4.test.core; // for testing functions
import test_op: lib.ft4.test.operations; // for test operations

For a complete API reference of the library, please refer to the API docs.

Registering accounts

When testing with ft4, you will often need to create test accounts. You can easily register basic test accounts using the built-in functions:

test.register_alice();
test.register_bob();
test.register_eve();
test.register_trudy();

These functions utilize keypairs from rell.test.keypairs for account registration using ft signing. For registering accounts using EVM signatures, there are corresponding functions with the _evm suffix, for example, test.register_alice_evm().

The functions return an instance of ft_account_data struct (or evm_account_data for the EVM variants), which includes information about the newly created account, such as a reference to the account, the ID of the auth descriptor that can access it, and the keypair used for signing.

struct ft_account_data {
ft_auth: (rell.test.op) -> list<rell.test.op>; // or evm_auth for the evm variant
account: account;
auth_descriptor_id: byte_array;
rell.test.keypair;
}

The auth operations can also authorize an operation against the account using the auth descriptor. For example:

// Register some accounts
val alice = test.register_alice();

// Authenticate the `authenticated_operation` as alice.
rell.test.tx()
.op(alice.ft_auth(test_op.authenticated_operation()))
.sign(alice.keypair)
.run();

If you need more than four accounts in your test, there are functions available to create new accounts using any auth descriptor:

test.register_account_with_auth_descriptor(single_sig_auth_descriptor, keypair);
test.register_account_with_multisig_auth_descriptor(multi_sig_auth_descriptor, keypair);

There are also convenience functions that will let you easily create auth descriptors to pass to the account registration functions, such as:

// For single sig auth descriptors
val single_sig = test.create_auth_descriptor(pubkey);
val admin_single_sig = test.create_auth_descriptor(pubkey, ["A", "T"]);
val single_sig_with_rules = test.create_auth_descriptor(
pubkey,
["A", "T"],
rule_expression(rule_operator.tt, rule_variable.block_height, 1)
);

// And similarly for multi sig auth descriptors
val multi_sig = test.create_multisig_auth_descriptor(2, [pubkey1, pubkey2]);
// ... etc.

Working with assets

When writing tests, it's often necessary to acquire and move assets. Acquiring assets can be a bit complicated in a test because of the security functions surrounding the operation that may not be needed. For this purpose, the testing framework provides functions to bypass the usual restrictions.

For example, to register a new asset, you can use the function:

val test_asset = test.create_asset(
name,
symbol,
decimals,
);

However, this will only register the asset. To use it, you would have to mint some of it to an account, which can be done with another built-in function:

test.mint(recipient_account, asset_to_mint, amount_to_mint);

Authentication

In addition to the functions for authenticating an account, the test framework also includes functionality for generic authentication. For example, there are functions for authenticating operations against any account using any auth descriptor:

rell.test.tx().op(test.ft_auth(account_id, auth_descriptor_id, <operation to authorize>)).sign(keypair).run();
rell.test.tx().op(test.evm_auth(keypair, account_id, auth_descriptor_id, <operation to authorize>)).run();

There is also a convenience method that will create the message that needs to be signed by an EVM signature to call an operation that EVM protects auth, e.g.:

val op = my_module.my_operation();
val message = test.create_evm_auth_message(op);

// If the message contains account or auth descriptor placeholders, you will
// also have to provide an `evm_account_data` instance
val message_with_account_data = test.create_evm_auth_message(op);

This produced auth message can then be signed by passing it to the evm_sign function:

val signature = test.evm_sign(message, rell.test.keypairs.alice.priv); // Or any other private key

Here, we have covered some everyday use cases made easy by the testing framework. There are more features in the testing framework than can fit on this page. For example, multiple operations wrap some of these functions to be called in a transaction. Please see the Rell API docs for the ft4 library to get a complete list of available features.