Skip to content

Commit

Permalink
fix: readme
Browse files Browse the repository at this point in the history
  • Loading branch information
chungquantin committed Oct 16, 2024
1 parent fc7db56 commit 2ba64df
Show file tree
Hide file tree
Showing 2 changed files with 93 additions and 130 deletions.
23 changes: 20 additions & 3 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

200 changes: 73 additions & 127 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,163 +15,125 @@ Forked version of [inkdevhub/drink](https://github.com/inkdevhub/drink) for E2E

About the repository folder structure:

- `crates/drink`
- `drink`: DRink! is a toolbox for ink! developers that allows for a fully functional ink! contract development without any running node.
- `drink-cli`: Simple command line tool to help you play with your local contracts in a convenient way.
- `examples`: A collection of example contracts tested with DRink!
- `crates/ink-sandbox`: In the context of quasi-testing with pop-drink, a sandbox refers to an isolated runtime environment that simulates the behavior of a full node, without requiring an actual node.
- `crates/pop-drink`: Provides utility methods for testing contract methods with DRink! and Pop Network runtimes.
- [pop-drink](/crates/pop-drink): Provides utility methods for testing contract methods with DRink! and Pop Network runtimes.
- [examples](/crates/drink/examples): A collection of example contracts tested with DRink!
- [drink](/crates/drink/drink): DRink! is a toolbox for ink! developers that allows for a fully functional ink! contract development without any running node.
- [ink-sandbox](/crates/ink-sandbox): Sandbox refers to an isolated runtime environment that simulates the behavior of a full node, without requiring an actual node.
- [drink-cli](/crates/drink/drink-cli): Simple command line tool to help you play with your local contracts in a convenient way.

## Getting Started

- Add `pop-drink` crate to your contract `Cargo.toml`:
Add `pop-drink` crate to your contract `Cargo.toml`:

```toml
drink = { version = "1.0.0", package = "pop-drink" }
```

### 1. Setup a testing environment
### Setup a testing environment

- Import required methods and types:
Please see ["Quick start with DRink!"](/crates/drink/examples/quick-start-with-drink/README.md) for a detailed explanation.

Add the below code at the top of your contract test file to setup [Sandbox](TODO) for the [**Pop Network Devnet**](https://github.com/r0gue-io/pop-node/tree/main/runtime/devnet) runtime.

```rs
use drink::{
devnet::{AccounId, Balance},
TestExternalities
};
#[derive(Default)]
struct Sandbox;

// Implement `Sandbox` environment for the Pop Network Devnet runtime.
drink::impl_sandbox!(Sandbox, drink::devnet::Runtime, ALICE);
```

- Add the below code at the top of your contract test file to setup sandbox and contract bundle provider for the **Pop Network Devnet** runtime:
## Writing tests

```rs
// Initialising useful variables for testing the contract.
const UNIT: Balance = 10_000_000_000;
const INIT_AMOUNT: Balance = 100_000_000 * UNIT;
const INIT_VALUE: Balance = 100 * UNIT;
const ALICE: AccountId = AccountId::new([1u8; 32]);
const BOB: AccountId = AccountId::new([2_u8; 32]);
const CHARLIE: AccountId = AccountId::new([3_u8; 32]);

// The contract bundle provider.
//
// Reference: https://github.com/r0gue-io/pop-drink/blob/main/crates/drink/drink/test-macro/src/lib.rs
#[drink::contract_bundle_provider]
enum BundleProvider {}
### Writing tests

// Sandbox environment for Pop Devnet Runtime.
struct Pop {
ext: TestExternalities,
}
Your typical test module will look like (See ["Quick start with DRink!"](/crates/drink/examples/quick-start-with-drink/README.md) example tests):

impl Default for Pop {
fn default() -> Self {
// Initialising genesis state, providing accounts with an initial balance.
let balances: Vec<(AccountId, u128)> =
vec![(ALICE, INIT_AMOUNT), (BOB, INIT_AMOUNT), (CHARLIE, INIT_AMOUNT)];
let ext = BlockBuilder::<Runtime>::new_ext(balances);
Self { ext }
}
}
```rust
#[cfg(test)]
mod tests {
use drink::session::{Session, NO_ARGS, None, NO_SALT};

// Implement `Sandbox` environment for the Pop Network Devnet runtime.
drink::impl_sandbox!(Pop, Runtime, ALICE);
```
#[drink::contract_bundle_provider]
enum BundleProvider {}

### 2. Deploy a new contract
#[drink::test]
fn deploy_and_call_a_contract(mut session: Session) -> Result<(), Box<dyn Error>> {
let result: bool = session
.deploy_bundle_and(BundleProvider::local(), "new", &["true"], NO_SALT, None)?
.call_and("flip", NO_ARGS, None)?
.call_and("flip", NO_ARGS, None)?
.call_and("flip", NO_ARGS, None)?
.call("get", NO_ARGS, None)??;
assert_eq!(result, false);
}
}
```

- Import required methods and types:
So, firstly, you declare a bundle provider like:

```rs
use drink::{assert_ok, deploy};
```rust
#[drink::contract_bundle_provider]
enum BundleProvider {}
```

- Instantiate and deploy a local contract with `new` constructor method:
It will take care of building all contract dependencies in the compilation phase and gather all contract bundles into a single registry.
Then, you will be able to get a contract bundle by calling:

```rs
let local_contract = BundleProvider::local().unwrap();
// Contract address is returned if a deployment succeeds.
assert_ok!(deploy(
&mut session,
local_contract,
"new",
vec![TOKEN.to_string(), MIN_BALANCE.to_string()],
vec![],
None
));
```rust
let bundle = BundleProvider::local()?; // for the contract from the current crate
let bundle = BundleProvider::Flipper.bundle()?; // for the contract from the `flipper` crate
```

### 3. Test contract method that uses [Pop API](https://github.com/r0gue-io/pop-node/tree/main/pop-api)
We mark each testcase with `#[drink::test]` attribute and declare return type as `Result` so that we can use the `?` operator:

This example interacts with a [PSP22](https://github.com/w3f/PSPs/blob/master/PSPs/psp-22.md) example contract that uses [Pop API](https://github.com/r0gue-io/pop-node/tree/main/pop-api). The contract method returns [`PSP22Error`](https://github.com/r0gue-io/pop-node/blob/main/pop-api/src/v0/fungibles/errors.rs#L73C1-L73C22) which is provided by Pop API library.
```rust
#[drink::test]
fn testcase() -> Result<(), Box<dyn Error>> {
// ...
}
```

Then, we can use the `Session` API to interact with both contracts and the whole runtime.
For details, check out testcases in [lib.rs](lib.rs).

Learn more in the [PSP22 example contract](https://github.com/r0gue-io/pop-node/blob/main/pop-api/examples/fungibles/lib.rs).
### Writing tests for methods using [Pop API](https://github.com/r0gue-io/pop-node/tree/main/pop-api)

- Import required methods and types:
Pop DRink! also provides [utilitiy methods](/crates/pop-drink/src/lib.rs) that you can use to test your contracts. This example interacts with a [PSP22 example contract](https://github.com/r0gue-io/pop-node/blob/main/pop-api/examples/fungibles/lib.rs) that uses [Pop API](https://github.com/r0gue-io/pop-node/tree/main/pop-api). The contract method returns [`PSP22Error`](https://github.com/r0gue-io/pop-node/blob/main/pop-api/src/v0/fungibles/errors.rs#L73C1-L73C22) custom error which is provided by Pop API library.

```rs
// Import required methods and types.
use drink::{call, session::Session};
use pop_api::v0::fungibles::PSP22Error;
```

- Example of interacting with the `transfer` method of the `Psp22` contract:

```rs
// Example of a method to interact with contract via pop-drink.
// Interact with `transfer` method of the `Psp22` contract.
fn transfer(session: &mut Session<Pop>, to: AccountId, amount: Balance) -> Result<(), PSP22Error> {
// Initialising a string value of an empty array.
let empty_array = serde_json::to_string::<[u8; 0]>(&[]).unwrap();
// Call to a contract method.
call::<Pop, (), PSP22Error>(
session,
"Psp22::transfer",
vec![to.to_string(), amount.to_string(), empty_array],
None,
)
}
```
let input = vec![to.to_string(), amount.to_string(), empty_array];

#### If the contract throws a non-custom error:

Non-custom errors are fixed variants of the provided [`PSP22Error`](https://github.com/r0gue-io/pop-node/blob/main/pop-api/src/v0/fungibles/errors.rs#L73C1-L73C22).

```rs
// Not enough balance. Failed with `InsufficientBalance`.
assert_eq!(
transfer_from(&mut session, ALICE, contract.clone(), AMOUNT + 1),
Err(PSP22Error::InsufficientBalance)
);
call::<Pop, (), PSP22Error>(session, "Psp22::transfer", input, None)
}
```

#### If the contract throws a custom error:

A custom error is returned if the error doesn't conform to the [`PSP22Error`](https://github.com/r0gue-io/pop-node/blob/main/pop-api/src/v0/fungibles/errors.rs#L73C1-L73C22) standard.
The custom error is represented by a [`StatusCode`](https://github.com/r0gue-io/pop-node/blob/main/pop-api/src/lib.rs#L33), which encapsulates a `u32` value indicating the success or failure of a runtime call via the Pop API.

Pop DRink! provides an error type and a [macro](https://doc.rust-lang.org/book/ch19-06-macros.html) to simplify testing both runtime module errors and API errors.

- `drink::v0::Error`: Runtime error type for testing API V0.
- `assert_err`: Asserts that a `Result` with an error type convertible to `u32` matches the expected `Error` from pop-drink.

Test the contract call if a custom error is the runtime module error:
Asserts the returned error to an [`Error`](TODO) type using [`assert_err!`](TODO) to test errors of a runtime call.

```rs
use drink::{assert_err, v0::Error, Assets, AssetsError::NoAccount};

assert_err!(
transfer(&mut session, ALICE, AMOUNT),
Error::Module(Assets(NoAccount))
);
#[drink::test(sandbox = Pop)]
fn test_transfer_to_no_account() {
// Test the contract call if a custom error is the runtime module error.
assert_err!(
transfer(&mut session, ALICE, AMOUNT),
Error::Module(Assets(NoAccount))
);
}
```

Test the contract call if a custom error is the API error:
We need to specify the sandbox of Pop Network runtime for a testcase if the contract is using Pop API.

```rs
use drink::{assert_err, v0::{Error, Arithmetic, ArithmeticError::Overflow}};

assert_err!(
transfer(&mut session, ALICE, AMOUNT),
Error::Api(Arithmetic(Overflow))
);
#[drink::test(sandbox = Pop)]
```

## Development Guide
Expand All @@ -182,22 +144,6 @@ To run the `examples` contracts for DRink!
cargo test --release
```

## Terminology

- `crates/drink`

- **[Session](https://github.com/r0gue-io/pop-drink/blob/main/crates/drink/drink/src/session.rs)**: Wrapper around [`Sandbox`](https://github.com/r0gue-io/pop-drink/blob/main/crates/ink-sandbox/src/lib.rs) that provides methods to interact with multiple contracts.

- **[Sandbox](https://github.com/r0gue-io/pop-drink/blob/main/crates/ink-sandbox/src/lib.rs)**: In the context of quasi-testing with pop-drink, a sandbox refers to an isolated runtime environment that simulates the behavior of a full node, without requiring an actual node.

- `crates/pop-drink`

- Mentions of `API` in `crates/pop_drink` refer to `pop_api::Error`.

- **Module Errors**: Errors returned by the Substrate runtime modules (or pallets).

- **API Errors**: Errors returned by the Substrate runtime modules (or pallets).

### Support

- Be part of our passionate community of Web3 pioneers. [Join our Telegram](https://t.me/onpopio)!
Expand Down

0 comments on commit 2ba64df

Please sign in to comment.