Skip to content

Commit

Permalink
Integration test: Generic runtime and environment (#1583)
Browse files Browse the repository at this point in the history
* Basic compiling draft

* refactor structure

* example test working

* add Blocks enum

* different runtime implementations

* fix altair & centrifuge

* add event support

* simplify weight calculation

* inmutable state method

* API support

* file renaming

* minor changes

* improve macro support

* minor style changes. add failing check_fees test

* fees workings

* remove unused things

* update to use Tokens in treasury configs

* minor rename
  • Loading branch information
lemunozm authored Oct 12, 2023
1 parent 74aeeb1 commit b8effb0
Show file tree
Hide file tree
Showing 12 changed files with 651 additions and 25 deletions.
10 changes: 10 additions & 0 deletions Cargo.lock

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

2 changes: 1 addition & 1 deletion runtime/altair/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -848,7 +848,7 @@ impl pallet_treasury::Config for Runtime {
type Burn = Burn;
// we burn and dont handle the unbalance
type BurnDestination = ();
type Currency = Balances;
type Currency = Tokens;
type MaxApprovals = MaxApprovals;
// slashed amount goes to treasury account
type OnSlash = Treasury;
Expand Down
2 changes: 1 addition & 1 deletion runtime/centrifuge/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1000,7 +1000,7 @@ impl pallet_treasury::Config for Runtime {
type Burn = Burn;
// we burn and dont handle the unbalance
type BurnDestination = ();
type Currency = Balances;
type Currency = Tokens;
type MaxApprovals = MaxApprovals;
// slashed amount goes to treasury account
type OnSlash = Treasury;
Expand Down
12 changes: 12 additions & 0 deletions runtime/integration-tests/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -27,18 +27,25 @@ pallet-democracy = { git = "https://github.com/paritytech//substrate", rev = "bc
pallet-preimage = { git = "https://github.com/paritytech//substrate", rev = "bcff60a227d455d95b4712b6cb356ce56b1ff672" }
pallet-transaction-payment = { git = "https://github.com/paritytech/substrate", default-features = false, branch = "polkadot-v0.9.38" }
pallet-uniques = { git = "https://github.com/paritytech/substrate", default-features = false, branch = "polkadot-v0.9.38" }
pallet-timestamp = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.38" }
pallet-treasury = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.38" }
pallet-authorship = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.38" }


## Substrate-Primitives
sp-api = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.38" }
#sp-authorship = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.38" }
sp-block-builder = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.38" }
sp-consensus-aura = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.38" }
sp-consensus-babe = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.38" }
sp-consensus-slots = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.38" }
sp-core = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.38" }
sp-inherents = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.38" }
sp-timestamp = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.38" }
sp-io = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.38" }
sp-runtime = { git = "https://github.com/paritytech/substrate", default-features = false, branch = "polkadot-v0.9.38" }
sp-tracing = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.38" }
fp-self-contained = { git = "https://github.com/PureStake/frontier", branch = "moonbeam-polkadot-v0.9.38" }

## Substrate-Client
node-primitives = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.38" }
Expand All @@ -61,6 +68,10 @@ xcm = { git = "https://github.com/paritytech/polkadot", branch = "release-v0.9.3

# Cumulus
cumulus-primitives-core = { git = "https://github.com/paritytech/cumulus", branch = "polkadot-v0.9.38" }
cumulus-pallet-parachain-system = { git = "https://github.com/paritytech/cumulus", branch = "polkadot-v0.9.38" }
cumulus-primitives-parachain-inherent = { git = "https://github.com/paritytech/cumulus", branch = "polkadot-v0.9.38" }
cumulus-test-relay-sproof-builder = { git = "https://github.com/paritytech/cumulus", branch = "polkadot-v0.9.38" }

parachain-info = { git = "https://github.com/paritytech/cumulus", branch = "polkadot-v0.9.38" }

# Orml pallets
Expand Down Expand Up @@ -109,6 +120,7 @@ pallet-permissions = { path = "../../pallets/permissions" }
pallet-pool-registry = { path = "../../pallets/pool-registry" }
pallet-pool-system = { path = "../../pallets/pool-system" }
pallet-rewards = { path = "../../pallets/rewards" }
pallet-restricted-tokens = { path = "../../pallets/restricted-tokens" }

pallet-session = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.38" }
pallet-xcm-transactor = { git = "https://github.com/PureStake/moonbeam", default-features = false, rev = "00b3e3d97806e889b02e1bcb4b69e65433dd805d" }
Expand Down
118 changes: 118 additions & 0 deletions runtime/integration-tests/src/generic/cases/example.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
use cfg_primitives::{AuraId, Balance, CFG};
use frame_support::traits::Get;

use crate::{
generic::{
environment::{Blocks, Env},
envs::runtime_env::RuntimeEnv,
runtime::Runtime,
utils::genesis::Genesis,
},
utils::accounts::Keyring,
};

fn transfer_balance<T: Runtime>() {
const TRANSFER: Balance = 1000 * CFG;
const FOR_FEES: Balance = 1 * CFG;

// Set up all GenesisConfig for your initial state
let mut env = RuntimeEnv::<T>::from_storage(
Genesis::default()
.add(pallet_aura::GenesisConfig::<T> {
authorities: vec![AuraId::from(Keyring::Charlie.public())],
})
.add(pallet_balances::GenesisConfig::<T> {
balances: vec![(
Keyring::Alice.to_account_id(),
T::ExistentialDeposit::get() + FOR_FEES + TRANSFER,
)],
})
.storage(),
);

// Call an extrinsic that would be processed immediately
env.submit(
Keyring::Alice,
pallet_balances::Call::transfer {
dest: Keyring::Bob.into(),
value: TRANSFER,
},
)
.unwrap();

// Check for an even occurred in this block
env.check_event(pallet_balances::Event::Transfer {
from: Keyring::Alice.to_account_id(),
to: Keyring::Bob.to_account_id(),
amount: TRANSFER,
})
.unwrap();

// Pass blocks to evolve the system
env.pass(Blocks::ByNumber(1));

// Check the state
env.state(|| {
assert_eq!(
pallet_balances::Pallet::<T>::free_balance(Keyring::Bob.to_account_id()),
TRANSFER
);
});
}

fn call_api<T: Runtime>() {
let env = RuntimeEnv::<T>::from_storage(
Genesis::default()
.add(pallet_aura::GenesisConfig::<T> {
authorities: vec![AuraId::from(Keyring::Charlie.public())],
})
.storage(),
);

env.state(|| {
// Call to Core::version() API.
// It's automatically implemented by the runtime T, so you can easily do:
// T::version()
assert_eq!(T::version(), <T as frame_system::Config>::Version::get());
})
}

fn check_fee<T: Runtime>() {
let mut env = RuntimeEnv::<T>::from_storage(
Genesis::default()
.add(pallet_aura::GenesisConfig::<T> {
authorities: vec![AuraId::from(Keyring::Charlie.public())],
})
.add(pallet_balances::GenesisConfig::<T> {
balances: vec![(Keyring::Alice.to_account_id(), 1 * CFG)],
})
.storage(),
);

env.submit(
Keyring::Alice,
frame_system::Call::remark { remark: vec![] },
)
.unwrap();

// Get the fee of the last submitted extrinsic
let fee = env.last_fee();

env.state(|| {
assert_eq!(
pallet_balances::Pallet::<T>::free_balance(Keyring::Alice.to_account_id()),
1 * CFG - fee
);
});
}

// Generate tests for all runtimes
crate::test_for_runtimes!((development, altair, centrifuge), transfer_balance);
crate::test_for_all_runtimes!(call_api);
crate::test_for_all_runtimes!(check_fee);

// Output: for `cargo test -p runtime-integration-tests transfer_balance`
// running 6 tests
// test generic::cases::example::transfer_balance::altair ... ok
// test generic::cases::example::transfer_balance::development ... ok
// test generic::cases::example::transfer_balance::centrifuge ... ok
85 changes: 85 additions & 0 deletions runtime/integration-tests/src/generic/environment.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
use cfg_primitives::{Balance, BlockNumber, Moment};
use sp_runtime::{DispatchResult, Storage};

use crate::{generic::runtime::Runtime, utils::accounts::Keyring};

/// Used by Env::pass() to determine how many blocks should be passed
#[derive(Clone)]
pub enum Blocks<T: Runtime> {
/// Pass X blocks
ByNumber(BlockNumber),

/// Pass a number of blocks enough to emulate the given passage of time.
/// i.e. choosing 1 sec would pass 1 block to emulate such change in the
/// time.
BySeconds(Moment),

/// Pass a number of block until find an event or reach the limit
UntilEvent {
event: T::RuntimeEventExt,
limit: BlockNumber,
},
}

/// Define an environment behavior
pub trait Env<T: Runtime> {
/// Load the environment from a storage
fn from_storage(storage: Storage) -> Self;

/// Submit an extrinsic mutating the state
fn submit(&mut self, who: Keyring, call: impl Into<T::RuntimeCall>) -> DispatchResult;

/// Pass any number of blocks
fn pass(&mut self, blocks: Blocks<T>);

/// Allows to mutate the storage state through the closure
fn state_mut<R>(&mut self, f: impl FnOnce() -> R) -> R;

/// Allows to read the storage state through the closure
/// If storage is modified, it would not be applied.
fn state<R>(&self, f: impl FnOnce() -> R) -> R;

/// Check for an exact event introduced in the current block.
/// Starting from last event introduced
/// Returns an Option to unwrap it from the tests and have good panic
/// message with the error test line
fn check_event(&self, event: impl Into<T::RuntimeEventExt>) -> Option<()> {
self.state(|| {
let event = event.into();
frame_system::Pallet::<T>::events()
.into_iter()
.rev()
.find(|record| record.event == event)
.map(|_| ())
})
}

/// Find an event introduced in the current block
/// Starting from last event introduced
/// Returns an Option to unwrap it from the tests and have good panic
/// message with the error test line
fn find_event<E, R>(&self, f: impl Fn(E) -> Option<R>) -> Option<R>
where
T::RuntimeEventExt: TryInto<E>,
{
self.state(|| {
frame_system::Pallet::<T>::events()
.into_iter()
.rev()
.map(|record| record.event.try_into().ok())
.find_map(|event| event.map(|e| f(e)))
.flatten()
})
}

/// Retrieve the fees used in the last submit call
fn last_fee(&self) -> Balance {
self.find_event(|e| match e {
pallet_transaction_payment::Event::TransactionFeePaid { actual_fee, .. } => {
Some(actual_fee)
}
_ => None,
})
.expect("Expected transaction")
}
}
1 change: 1 addition & 0 deletions runtime/integration-tests/src/generic/envs/fudge_env.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
// TODO: implement generic::env::Env for an environment using fudge
Loading

0 comments on commit b8effb0

Please sign in to comment.