Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Integration test: Generic runtime and environment #1583

Merged
merged 20 commits into from
Oct 12, 2023
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
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())],
})
Comment on lines +83 to +85
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not a blocker: The authority pattern is and will be heavily used. IMO, we should add setter functions for commonly executed calls such as this one, i.e. with_authorities(vec![AuraId::from(Keyring::Charlie.public())].

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah! The idea is to add this kind of thing. By now, in #1588 such genesis are moved to the environment because it's mandatory needed to make aura works.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It still need a lot of utilities for common stuff as creating assets, etc

.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")
}
Comment on lines +75 to +84
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This seems pretty detailed for the trait itself...

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, it is, and I've removed it in #1588

Now, submit_now() returns the fee it has cost. In order to later check in tests the exact balance required for some actions

Thanks for your post-morten review!

}
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
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we still want to do this in this PR?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, this will be a future PR not to block this. That file is just a placeholder.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I should create an issue for this

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Loading
Loading