From 49fb66d1bae9483c47fa9b854b7ec51591ad986d Mon Sep 17 00:00:00 2001 From: lemunozm Date: Tue, 24 Oct 2023 14:17:38 +0200 Subject: [PATCH 1/4] Add example for mock-builder proposal --- Cargo.lock | 24 ++ substrate/frame/examples/Cargo.toml | 3 + .../frame/examples/mock-builder/Cargo.toml | 57 +++ .../examples/mock-builder/src/benchmarking.rs | 0 .../frame/examples/mock-builder/src/lib.rs | 96 +++++ .../frame/examples/mock-builder/src/mock.rs | 198 +++++++++ .../mock-builder/src/mock/mock_pallets.rs | 384 ++++++++++++++++++ .../frame/examples/mock-builder/src/tests.rs | 0 substrate/frame/examples/src/lib.rs | 3 + 9 files changed, 765 insertions(+) create mode 100644 substrate/frame/examples/mock-builder/Cargo.toml create mode 100644 substrate/frame/examples/mock-builder/src/benchmarking.rs create mode 100644 substrate/frame/examples/mock-builder/src/lib.rs create mode 100644 substrate/frame/examples/mock-builder/src/mock.rs create mode 100644 substrate/frame/examples/mock-builder/src/mock/mock_pallets.rs create mode 100644 substrate/frame/examples/mock-builder/src/tests.rs diff --git a/Cargo.lock b/Cargo.lock index 4a45d5c602e8..40755d26157c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -8402,6 +8402,11 @@ dependencies = [ "sp-runtime", ] +[[package]] +name = "mock-builder" +version = "0.2.0" +source = "git+https://github.com/foss3/runtime-pallet-library?branch=polkadot-v1.2.0#f1f74a76ee7a66fa90e9762bc9034b73e1c4f1b3" + [[package]] name = "mockall" version = "0.11.4" @@ -9974,6 +9979,24 @@ dependencies = [ "sp-std 8.0.0", ] +[[package]] +name = "pallet-example-mock-builder" +version = "4.0.0-dev" +dependencies = [ + "frame-benchmarking", + "frame-support", + "frame-system", + "mock-builder", + "parity-scale-codec", + "polkadot-primitives", + "polkadot-runtime-common", + "scale-info", + "sp-core", + "sp-io", + "sp-runtime", + "sp-std", +] + [[package]] name = "pallet-example-offchain-worker" version = "4.0.0-dev" @@ -10015,6 +10038,7 @@ dependencies = [ "pallet-example-basic", "pallet-example-frame-crate", "pallet-example-kitchensink", + "pallet-example-mock-builder", "pallet-example-offchain-worker", "pallet-example-split", ] diff --git a/substrate/frame/examples/Cargo.toml b/substrate/frame/examples/Cargo.toml index 1b2150227154..3125c64d8f37 100644 --- a/substrate/frame/examples/Cargo.toml +++ b/substrate/frame/examples/Cargo.toml @@ -20,6 +20,7 @@ pallet-example-frame-crate = { path = "frame-crate", default-features = false } pallet-example-kitchensink = { path = "kitchensink", default-features = false} pallet-example-offchain-worker = { path = "offchain-worker", default-features = false} pallet-example-split = { path = "split", default-features = false} +pallet-example-mock-builder = { path = "mock-builder", default-features = false} [features] default = [ "std" ] @@ -31,6 +32,7 @@ std = [ "pallet-example-kitchensink/std", "pallet-example-offchain-worker/std", "pallet-example-split/std", + "pallet-example-mock-builder/std", ] try-runtime = [ "pallet-default-config-example/try-runtime", @@ -39,4 +41,5 @@ try-runtime = [ "pallet-example-kitchensink/try-runtime", "pallet-example-offchain-worker/try-runtime", "pallet-example-split/try-runtime", + "pallet-example-mock-builder/try-runtime", ] diff --git a/substrate/frame/examples/mock-builder/Cargo.toml b/substrate/frame/examples/mock-builder/Cargo.toml new file mode 100644 index 000000000000..938898536980 --- /dev/null +++ b/substrate/frame/examples/mock-builder/Cargo.toml @@ -0,0 +1,57 @@ +[package] +name = "pallet-example-mock-builder" +version = "4.0.0-dev" +authors.workspace = true +edition.workspace = true +license = "MIT-0" +homepage = "https://substrate.io" +repository.workspace = true +description = "FRAME pallet tested with mock-builder" +readme = "README.md" + +[package.metadata.docs.rs] +targets = ["x86_64-unknown-linux-gnu"] + +[dependencies] +codec = { package = "parity-scale-codec", version = "3.6.1", default-features = false } +scale-info = { version = "2.10.0", default-features = false, features = ["derive"] } +frame-benchmarking = { path = "../../benchmarking", default-features = false, optional = true} +frame-support = { path = "../../support", default-features = false} +frame-system = { path = "../../system", default-features = false} +sp-io = { path = "../../../primitives/io", default-features = false} +sp-runtime = { path = "../../../primitives/runtime", default-features = false} +sp-std = { path = "../../../primitives/std", default-features = false} +polkadot-runtime-common = { path = "../../../../polkadot/runtime/common", default-features = false} + +[dev-dependencies] +sp-core = { path = "../../../primitives/core", default-features = false} +mock-builder = { git = "https://github.com/foss3/runtime-pallet-library", branch = "polkadot-v1.2.0" } +polkadot-primitives = { path = "../../../../polkadot/primitives", default-features = false} + +[features] +default = [ "std" ] +std = [ + "codec/std", + "frame-benchmarking?/std", + "frame-support/std", + "frame-system/std", + "scale-info/std", + "sp-core/std", + "sp-io/std", + "sp-runtime/std", + "sp-std/std", + "polkadot-runtime-common/std", +] +runtime-benchmarks = [ + "frame-benchmarking/runtime-benchmarks", + "frame-support/runtime-benchmarks", + "frame-system/runtime-benchmarks", + "sp-runtime/runtime-benchmarks", + "polkadot-runtime-common/runtime-benchmarks", +] +try-runtime = [ + "frame-support/try-runtime", + "frame-system/try-runtime", + "sp-runtime/try-runtime", + "polkadot-runtime-common/try-runtime", +] diff --git a/substrate/frame/examples/mock-builder/src/benchmarking.rs b/substrate/frame/examples/mock-builder/src/benchmarking.rs new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/substrate/frame/examples/mock-builder/src/lib.rs b/substrate/frame/examples/mock-builder/src/lib.rs new file mode 100644 index 000000000000..560c67a03a97 --- /dev/null +++ b/substrate/frame/examples/mock-builder/src/lib.rs @@ -0,0 +1,96 @@ +//! Silly pallet to show how to test it with mock-builder +//! +//! The pallet allows to create auctions once a certain deposit is reached and some time has been +//! passed from the last deposit. + +#![cfg_attr(not(feature = "std"), no_std)] + +pub use pallet::*; + +#[cfg(test)] +mod mock; + +#[cfg(test)] +mod tests; + +#[cfg(feature = "runtime-benchmarks")] +mod benchmarking; + +#[frame_support::pallet] +pub mod pallet { + use frame_support::{ + pallet_prelude::*, + traits::{Currency, ReservableCurrency, Time}, + }; + use frame_system::pallet_prelude::*; + use polkadot_runtime_common::traits::Auctioneer; + + type MomentOf = <::Time as Time>::Moment; + type BalanceOf = + <::Currency as Currency<::AccountId>>::Balance; + + #[pallet::pallet] + pub struct Pallet(_); + + #[pallet::config] + pub trait Config: frame_system::Config { + type Time: Time; + type Currency: ReservableCurrency; + type Auction: Auctioneer, LeasePeriod = BlockNumberFor>; + + #[pallet::constant] + type ExpectedAmount: Get>; + + #[pallet::constant] + type WaitingTime: Get>; + + #[pallet::constant] + type Period: Get>; + } + + #[pallet::storage] + pub type LastDeposit = StorageMap<_, Blake2_128Concat, T::AccountId, MomentOf>; + + #[pallet::error] + pub enum Error { + NotEnoughDeposit, + NotEnoughWaiting, + } + + #[pallet::call] + impl Pallet { + #[pallet::call_index(0)] + #[pallet::weight(Weight::default())] + pub fn make_reserve(origin: OriginFor, amount: BalanceOf) -> DispatchResult { + let who = ensure_signed(origin)?; + + T::Currency::reserve(&who, amount)?; + LastDeposit::::insert(who, T::Time::now()); + + Ok(()) + } + + /// To create an auction we need to fullfull the following non-sense conditions: + /// - origin has T::ExpectedAmount reserved. + /// - T::WaitingTime has passed from the last make_deposit call. + #[pallet::call_index(1)] + #[pallet::weight(Weight::default())] + pub fn create_auction(origin: OriginFor) -> DispatchResult { + let who = ensure_signed(origin)?; + + // Check reserve + let current = T::Currency::reserved_balance(&who); + ensure!(current >= T::ExpectedAmount::get(), Error::::NotEnoughDeposit); + + // Check time + let now = T::Time::now(); + let ready_at = T::WaitingTime::get() + LastDeposit::::get(who).unwrap_or(now); + ensure!(now >= ready_at, Error::::NotEnoughWaiting); + + let block = frame_system::Pallet::::block_number(); + T::Auction::new_auction(block, T::Period::get())?; + + Ok(()) + } + } +} diff --git a/substrate/frame/examples/mock-builder/src/mock.rs b/substrate/frame/examples/mock-builder/src/mock.rs new file mode 100644 index 000000000000..c1d449253c5b --- /dev/null +++ b/substrate/frame/examples/mock-builder/src/mock.rs @@ -0,0 +1,198 @@ +mod mock_pallets; +use mock_pallets::{mock_pallet_auctioneer, mock_pallet_currency, mock_pallet_time}; + +use super::{pallet, Error, LastDeposit}; +use frame_support::{ + assert_noop, assert_ok, derive_impl, + pallet_prelude::DispatchError, + traits::{ConstU128, ConstU64, ReservableCurrency}, +}; +use frame_system::{pallet_prelude::BlockNumberFor, RawOrigin}; + +const DAY: u64 = 24 * 3600 * 1000; // ms + +const INITIAL_TIME: u64 = 10 * DAY; +const EXPECTED_AMOUNT: u128 = 100; +const WAITING_TIME: u64 = DAY; +const PERIOD: u64 = 50; + +frame_support::construct_runtime!( + pub struct Runtime { + System: frame_system, + MockTime: mock_pallet_time, + MockCurrency: mock_pallet_currency, + MockAuctioneer: mock_pallet_auctioneer, + MyPallet: pallet, + } +); + +#[derive_impl(frame_system::config_preludes::TestDefaultConfig as frame_system::DefaultConfig)] +impl frame_system::Config for Runtime { + type Block = frame_system::mocking::MockBlock; +} + +impl mock_pallet_time::Config for Runtime { + type Moment = u64; +} + +impl mock_pallet_currency::Config for Runtime { + type Balance = u128; + type PositiveImbalance = (); + type NegativeImbalance = (); +} + +impl mock_pallet_auctioneer::Config for Runtime { + type LeasePeriod = BlockNumberFor; + type Currency = mock_pallet_currency::Pallet; +} + +impl pallet::Config for Runtime { + type Time = mock_pallet_time::Pallet; + type Currency = mock_pallet_currency::Pallet; + type Auction = mock_pallet_auctioneer::Pallet; + type ExpectedAmount = ConstU128; + type WaitingTime = ConstU64; + type Period = ConstU64; +} + +pub fn new_test_ext() -> sp_io::TestExternalities { + let mut ext = sp_io::TestExternalities::new(Default::default()); + + ext.execute_with(|| { + // Initial time for all test cases + MockTime::mock_now(|| INITIAL_TIME); + + // Initial reserved balances for all test cases + MockCurrency::mock_reserved_balance(|_account| 0); + + // Mock calls can be later overwriten in the test cases + }); + + ext +} + +mod tests { + use super::*; + + const ALICE: u64 = 2; + + #[test] + fn reserve() { + new_test_ext().execute_with(|| { + // Mock the internal call done to T::Currency::reserve() inside of + // MyPaller:make_reserve() checking the expected inputs and returining a successfull + // value. + MockCurrency::mock_reserve(|account_id, amount| { + assert_eq!(account_id, &ALICE); + assert_eq!(amount, EXPECTED_AMOUNT); + Ok(()) + }); + + assert_ok!(MyPallet::make_reserve(RawOrigin::Signed(ALICE).into(), EXPECTED_AMOUNT)); + + assert_eq!(LastDeposit::::get(ALICE), Some(INITIAL_TIME)) + }) + } + + #[test] + fn reserve_error() { + new_test_ext().execute_with(|| { + // Mock the internal call to T::Currency::reserve() to emulate an error + MockCurrency::mock_reserve(|_, _| Err(DispatchError::Other("Err"))); + + assert_noop!( + MyPallet::make_reserve(RawOrigin::Signed(ALICE).into(), EXPECTED_AMOUNT), + DispatchError::Other("Err") + ); + }) + } + + /// Utility to amalgamate the pallet call with the required mocks to make them work successfull + fn do_alice_reserve(amount: u128) { + MockCurrency::mock_reserve(|account_id, amount| { + let previous_reserve = MockCurrency::reserved_balance(account_id); + + // Mocks can be nested. + // Mocking a reserve implies to create a new mock for the updated reserved_balance value + // In order to fetch later the correct updated value + MockCurrency::mock_reserved_balance(move |_| previous_reserve + amount); + + Ok(()) + }); + + MyPallet::make_reserve(RawOrigin::Signed(ALICE).into(), amount).unwrap(); + } + + #[test] + fn create_auction() { + new_test_ext().execute_with(|| { + do_alice_reserve(EXPECTED_AMOUNT); + + // Emulate advance in time to fulfill the auction conditions + MockTime::mock_now(|| INITIAL_TIME + DAY); + + // Successfull internal call to new auction + MockAuctioneer::mock_new_auction(|block, period| { + assert_eq!(block, frame_system::Pallet::::block_number()); + assert_eq!(period, PERIOD); + Ok(()) + }); + + assert_ok!(MyPallet::create_auction(RawOrigin::Signed(ALICE).into())); + }) + } + + #[test] + fn create_auction_with_several_deposits() { + new_test_ext().execute_with(|| { + do_alice_reserve(EXPECTED_AMOUNT / 2); + do_alice_reserve(EXPECTED_AMOUNT / 2); + + MockTime::mock_now(|| INITIAL_TIME + DAY); + MockAuctioneer::mock_new_auction(|_, _| Ok(())); + + assert_ok!(MyPallet::create_auction(RawOrigin::Signed(ALICE).into())); + }) + } + + #[test] + fn not_enough_deposit_error() { + new_test_ext().execute_with(|| { + do_alice_reserve(EXPECTED_AMOUNT / 2); + + assert_noop!( + MyPallet::create_auction(RawOrigin::Signed(ALICE).into()), + Error::::NotEnoughDeposit + ); + }); + } + + #[test] + fn not_enough_waiting_error() { + new_test_ext().execute_with(|| { + do_alice_reserve(EXPECTED_AMOUNT); + + assert_noop!( + MyPallet::create_auction(RawOrigin::Signed(ALICE).into()), + Error::::NotEnoughWaiting + ); + }); + } + + #[test] + fn auction_error() { + new_test_ext().execute_with(|| { + do_alice_reserve(EXPECTED_AMOUNT); + + MockTime::mock_now(|| INITIAL_TIME + DAY); + + // We emulate an error in the new_auction() dependency call. + MockAuctioneer::mock_new_auction(|_, _| Err(DispatchError::Other("Err"))); + + assert_noop!( + MyPallet::create_auction(RawOrigin::Signed(ALICE).into()), + DispatchError::Other("Err") + ); + }); + } +} diff --git a/substrate/frame/examples/mock-builder/src/mock/mock_pallets.rs b/substrate/frame/examples/mock-builder/src/mock/mock_pallets.rs new file mode 100644 index 000000000000..442694798411 --- /dev/null +++ b/substrate/frame/examples/mock-builder/src/mock/mock_pallets.rs @@ -0,0 +1,384 @@ +//! DO NOT GET SCARED about this file. +//! It's expected that almost every line of this file can be automatically generated +//! once mock-builder support procedural macros to build the mocked pallets. + +/// With procedural support, it will be autogenerated as follows: +/// +/// ```ignore +/// #[mock_builder::pallet] +/// mod mock_pallet_time { +/// trait Time { +/// type Moment: AtLeast32Bit + Parameter + Default + Copy + MaxEncodedLen; +/// fn now() -> Self::Moment; +/// } +/// } +/// ``` +/// +/// It's expected that if you own the trait, you can simply put one procedural attribute on +/// top of it to autogerate the mocked pallet: +/// +/// ```ignore +/// #[mock_builder::pallet] +/// trait MyOwnTrait { .. } +/// ``` +#[frame_support::pallet(dev_mode)] +pub mod mock_pallet_time { + use frame_support::{pallet_prelude::*, traits::Time}; + use mock_builder::{execute_call, register_call}; + use sp_runtime::traits::AtLeast32Bit; + + #[pallet::config] + pub trait Config: frame_system::Config { + type Moment: AtLeast32Bit + Parameter + Default + Copy + MaxEncodedLen; + } + + #[pallet::pallet] + pub struct Pallet(_); + + #[pallet::storage] + type CallIds = StorageMap<_, _, String, mock_builder::CallId>; + + impl Pallet { + pub fn mock_now(f: impl Fn() -> T::Moment + 'static) { + register_call!(move |()| f()); + } + } + + impl Time for Pallet { + type Moment = T::Moment; + + fn now() -> Self::Moment { + execute_call!(()) + } + } +} + +/// With procedural support, it will be autogenerated as follows, +/// implementing all mock_* functions. +/// +/// Be aware that this show a very complex use case with several traits with a bunch of functions +/// +/// NOTE: It's expected that if you own the trait, you can simply put one procedural attribute on +/// top of it to autogerate the mocked pallet. +/// ```ignore +/// #[mock_builder::pallet] +/// mod mock_pallet_time { +/// trait Currency { +/// type Balance: Balance + MaybeSerializeDeserialize; +/// type PositiveImbalance: Imbalance; +/// type NegativeImbalance: Imbalance; +/// fn total_balance(a: &AccountId) -> Self::Balance; +/// fn can_slash(a: &T::AccountId, b: Self::Balance); +/// fn total_issuance() -> Self::Balance; +/// fn minimum_balance() -> Self::Balance; +/// fn burn(a: Self::Balance) -> Self::PositiveImbalance; +/// fn issue(a: Self::Balance) -> Self::NegativeImbalance; +/// fn free_balance(a: &T::AccountId) -> Self::Balance; +/// fn ensure_can_withdraw( +/// a: &T::AccountId, +/// b: Self::Balance, +/// c: WithdrawReasons, +/// d: Self::Balance, +/// ) -> DispatchResult; +/// fn transfer( +/// a: &T::AccountId, +/// b: &T::AccountId, +/// c: Self::Balance, +/// d: ExistenceRequirement, +/// ) -> DispatchResult; +/// fn slash(a: &T::AccountId, b: Self::Balance) -> (Self::NegativeImbalance, Self::Balance); +/// fn deposit_into_existing( +/// a: &T::AccountId, +/// b: Self::Balance, +/// ) -> Result; +/// fn deposit_creating(a: &T::AccountId, b: Self::Balance) -> Self::PositiveImbalance; +/// fn withdraw( +/// a: &T::AccountId, +/// b: Self::Balance, +/// c: WithdrawReasons, +/// d: ExistenceRequirement, +/// ) -> Result; +/// fn make_free_balance_be( +/// a: &T::AccountId, +/// b: Self::Balance, +/// ) -> SignedImbalance +/// } +/// trait ReservableCurrency { +/// fn can_reserve(a: &T::AccountId, b: Self::Balance) -> bool; +/// fn slash_reserved( +/// a: &T::AccountId, +/// b: Self::Balance, +/// ) -> (Self::NegativeImbalance, Self::Balance); +/// fn reserved_balance(a: &T::AccountId) -> Self::Balance; +/// fn reserve(a: &T::AccountId, b: Self::Balance) -> DispatchResult; +/// fn unreserve(a: &T::AccountId, b: Self::Balance) -> Self::Balance; +/// fn repatriate_reserved( +/// a: &T::AccountId, +/// b: &T::AccountId, +/// c: Self::Balance, +/// d: BalanceStatus, +/// ) -> Result; +/// } +/// } +/// ``` +#[frame_support::pallet(dev_mode)] +pub mod mock_pallet_currency { + use frame_support::{ + pallet_prelude::*, + traits::{ + tokens::{ + imbalance::{Imbalance, SignedImbalance}, + Balance, BalanceStatus, ExistenceRequirement, WithdrawReasons, + }, + Currency, ReservableCurrency, + }, + }; + use mock_builder::{execute_call, register_call}; + + #[pallet::config] + pub trait Config: frame_system::Config { + type Balance: Balance + MaybeSerializeDeserialize; + type PositiveImbalance: Imbalance; + type NegativeImbalance: Imbalance; + } + + #[pallet::pallet] + pub struct Pallet(_); + + #[pallet::storage] + type CallIds = StorageMap<_, _, String, mock_builder::CallId>; + + impl Pallet { + pub fn mock_reserved_balance(f: impl Fn(&T::AccountId) -> T::Balance + 'static) { + register_call!(f) + } + + pub fn mock_reserve(f: impl Fn(&T::AccountId, T::Balance) -> DispatchResult + 'static) { + register_call!(move |(a, b)| f(a, b)) + } + + // Not mandatory to mock all functions from the traits + // Any `mock_` function can be used to mock the a function. + // + // With procedural macros, every mock method will be generated + } + + impl Currency for Pallet { + type Balance = T::Balance; + type PositiveImbalance = T::PositiveImbalance; + type NegativeImbalance = T::NegativeImbalance; + + fn total_balance(a: &T::AccountId) -> Self::Balance { + execute_call!(a) + } + + fn can_slash(a: &T::AccountId, b: Self::Balance) -> bool { + execute_call!((a, b)) + } + + fn total_issuance() -> Self::Balance { + execute_call!(()) + } + + fn minimum_balance() -> Self::Balance { + execute_call!(()) + } + + fn burn(a: Self::Balance) -> Self::PositiveImbalance { + execute_call!(a) + } + + fn issue(a: Self::Balance) -> Self::NegativeImbalance { + execute_call!(a) + } + + fn free_balance(a: &T::AccountId) -> Self::Balance { + execute_call!(a) + } + + fn ensure_can_withdraw( + a: &T::AccountId, + b: Self::Balance, + c: WithdrawReasons, + d: Self::Balance, + ) -> DispatchResult { + execute_call!((a, b, c, d)) + } + + fn transfer( + a: &T::AccountId, + b: &T::AccountId, + c: Self::Balance, + d: ExistenceRequirement, + ) -> DispatchResult { + execute_call!((a, b, c, d)) + } + + fn slash(a: &T::AccountId, b: Self::Balance) -> (Self::NegativeImbalance, Self::Balance) { + execute_call!((a, b)) + } + + fn deposit_into_existing( + a: &T::AccountId, + b: Self::Balance, + ) -> Result { + execute_call!((a, b)) + } + + fn deposit_creating(a: &T::AccountId, b: Self::Balance) -> Self::PositiveImbalance { + execute_call!((a, b)) + } + + fn withdraw( + a: &T::AccountId, + b: Self::Balance, + c: WithdrawReasons, + d: ExistenceRequirement, + ) -> Result { + execute_call!((a, b, c, d)) + } + + fn make_free_balance_be( + a: &T::AccountId, + b: Self::Balance, + ) -> SignedImbalance { + execute_call!((a, b)) + } + } + + impl ReservableCurrency for Pallet { + fn can_reserve(a: &T::AccountId, b: Self::Balance) -> bool { + execute_call!((a, b)) + } + + fn slash_reserved( + a: &T::AccountId, + b: Self::Balance, + ) -> (Self::NegativeImbalance, Self::Balance) { + execute_call!((a, b)) + } + + fn reserved_balance(a: &T::AccountId) -> Self::Balance { + execute_call!(a) + } + + fn reserve(a: &T::AccountId, b: Self::Balance) -> DispatchResult { + execute_call!((a, b)) + } + + fn unreserve(a: &T::AccountId, b: Self::Balance) -> Self::Balance { + execute_call!((a, b)) + } + + fn repatriate_reserved( + a: &T::AccountId, + b: &T::AccountId, + c: Self::Balance, + d: BalanceStatus, + ) -> Result { + execute_call!((a, b, c, d)) + } + } +} + +/// With procedural support, it will be autogenerated as follows, +/// implementing all mock_* functions. +/// +/// NOTE: It's expected that if you own the trait, you can simply put one procedural attribute on +/// top of it to autogerate the mocked pallet. +/// ```ignore +/// #[mock_builder::pallet] +/// mod mock_pallet_time { +/// trait Auctioneer { +/// type AccountId = AccountId; /// Initialized with a value +/// type LeasePeriod: T::LeasePeriod; +/// type Currency: T::Currency; +/// fn new_auction(a: BlockNumberFor, b: Self::LeasePeriod) -> DispatchResult; +/// fn auction_status(a: BlockNumberFor) -> AuctionStatus>; +/// fn place_bid( +/// a: Self::AccountId, +/// b: Id, +/// c: Self::LeasePeriod, +/// d: Self::LeasePeriod, +/// e: >::Balance, +/// ) -> DispatchResult; +/// #[cfg(feature = "runtime-benchmarks")] +/// fn lease_period_length() -> (BlockNumberFor, BlockNumberFor); +/// fn lease_period_index(a: BlockNumberFor) -> Option<(Self::LeasePeriod, bool)>; +/// fn has_won_an_auction(a: Id, b: &Self::AccountId) -> bool; +/// } +/// } +/// ``` +#[frame_support::pallet(dev_mode)] +pub mod mock_pallet_auctioneer { + use frame_support::{ + pallet_prelude::*, + traits::{Currency, ReservableCurrency}, + }; + use frame_system::pallet_prelude::BlockNumberFor; + use mock_builder::{execute_call, register_call}; + use polkadot_primitives::Id; + use polkadot_runtime_common::traits::{AuctionStatus, Auctioneer}; + + #[pallet::config] + pub trait Config: frame_system::Config { + type LeasePeriod; + type Currency: ReservableCurrency; + } + + #[pallet::pallet] + pub struct Pallet(_); + + #[pallet::storage] + type CallIds = StorageMap<_, _, String, mock_builder::CallId>; + + impl Pallet { + pub fn mock_new_auction( + f: impl Fn(BlockNumberFor, T::LeasePeriod) -> DispatchResult + 'static, + ) { + register_call!(move |(a, b)| f(a, b)) + } + + // Not mandatory to mock all functions from the traits + // Any `mock_` function can be used to mock the a function. + // + // With procedural macros, every mock method will be generated + } + + impl Auctioneer> for Pallet { + type AccountId = T::AccountId; + type LeasePeriod = T::LeasePeriod; + type Currency = T::Currency; + + fn new_auction(a: BlockNumberFor, b: Self::LeasePeriod) -> DispatchResult { + execute_call!((a, b)) + } + + fn auction_status(a: BlockNumberFor) -> AuctionStatus> { + execute_call!(a) + } + + fn place_bid( + a: Self::AccountId, + b: Id, + c: Self::LeasePeriod, + d: Self::LeasePeriod, + e: >::Balance, + ) -> DispatchResult { + execute_call!((a, b, c, d, e)) + } + + #[cfg(feature = "runtime-benchmarks")] + fn lease_period_length() -> (BlockNumberFor, BlockNumberFor) { + execute_call!(()) + } + + fn lease_period_index(a: BlockNumberFor) -> Option<(Self::LeasePeriod, bool)> { + execute_call!(a) + } + + fn has_won_an_auction(a: Id, b: &Self::AccountId) -> bool { + execute_call!((a, b)) + } + } +} diff --git a/substrate/frame/examples/mock-builder/src/tests.rs b/substrate/frame/examples/mock-builder/src/tests.rs new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/substrate/frame/examples/src/lib.rs b/substrate/frame/examples/src/lib.rs index 8d65639f8352..f8d9ed28eccc 100644 --- a/substrate/frame/examples/src/lib.rs +++ b/substrate/frame/examples/src/lib.rs @@ -43,4 +43,7 @@ //! - [`pallet_example_frame_crate`]: Example pallet showcasing how one can be //! built using only the `frame` umbrella crate. //! +//! - [`pallet_example_mock_builder`]: A simple pallet demonstrating how to make testing with the +//! mock-builder utility. +//! //! **Tip**: Use `cargo doc --package --open` to view each pallet's documentation. From 381b0e977cc7db2f1e6dd10ce61ae067894f7bd1 Mon Sep 17 00:00:00 2001 From: lemunozm Date: Tue, 24 Oct 2023 14:38:57 +0200 Subject: [PATCH 2/4] split files --- .../frame/examples/mock-builder/Cargo.toml | 20 +-- .../frame/examples/mock-builder/src/lib.rs | 2 +- .../frame/examples/mock-builder/src/mock.rs | 145 ++---------------- .../frame/examples/mock-builder/src/tests.rs | 133 ++++++++++++++++ 4 files changed, 153 insertions(+), 147 deletions(-) diff --git a/substrate/frame/examples/mock-builder/Cargo.toml b/substrate/frame/examples/mock-builder/Cargo.toml index 938898536980..a7392eedf06b 100644 --- a/substrate/frame/examples/mock-builder/Cargo.toml +++ b/substrate/frame/examples/mock-builder/Cargo.toml @@ -15,31 +15,31 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] codec = { package = "parity-scale-codec", version = "3.6.1", default-features = false } scale-info = { version = "2.10.0", default-features = false, features = ["derive"] } + frame-benchmarking = { path = "../../benchmarking", default-features = false, optional = true} frame-support = { path = "../../support", default-features = false} frame-system = { path = "../../system", default-features = false} -sp-io = { path = "../../../primitives/io", default-features = false} -sp-runtime = { path = "../../../primitives/runtime", default-features = false} -sp-std = { path = "../../../primitives/std", default-features = false} + polkadot-runtime-common = { path = "../../../../polkadot/runtime/common", default-features = false} [dev-dependencies] -sp-core = { path = "../../../primitives/core", default-features = false} -mock-builder = { git = "https://github.com/foss3/runtime-pallet-library", branch = "polkadot-v1.2.0" } +sp-io = { path = "../../../primitives/io"} +sp-core = { path = "../../../primitives/core"} +sp-runtime = { path = "../../../primitives/runtime"} +sp-std = { path = "../../../primitives/std"} + polkadot-primitives = { path = "../../../../polkadot/primitives", default-features = false} +mock-builder = { git = "https://github.com/foss3/runtime-pallet-library", branch = "polkadot-v1.2.0" } + [features] default = [ "std" ] std = [ "codec/std", + "scale-info/std", "frame-benchmarking?/std", "frame-support/std", "frame-system/std", - "scale-info/std", - "sp-core/std", - "sp-io/std", - "sp-runtime/std", - "sp-std/std", "polkadot-runtime-common/std", ] runtime-benchmarks = [ diff --git a/substrate/frame/examples/mock-builder/src/lib.rs b/substrate/frame/examples/mock-builder/src/lib.rs index 560c67a03a97..0755f929e633 100644 --- a/substrate/frame/examples/mock-builder/src/lib.rs +++ b/substrate/frame/examples/mock-builder/src/lib.rs @@ -1,4 +1,4 @@ -//! Silly pallet to show how to test it with mock-builder +//! Silly pallet to show how to testing with mock-builder works //! //! The pallet allows to create auctions once a certain deposit is reached and some time has been //! passed from the last deposit. diff --git a/substrate/frame/examples/mock-builder/src/mock.rs b/substrate/frame/examples/mock-builder/src/mock.rs index c1d449253c5b..2f34226adae8 100644 --- a/substrate/frame/examples/mock-builder/src/mock.rs +++ b/substrate/frame/examples/mock-builder/src/mock.rs @@ -1,20 +1,19 @@ mod mock_pallets; use mock_pallets::{mock_pallet_auctioneer, mock_pallet_currency, mock_pallet_time}; -use super::{pallet, Error, LastDeposit}; +use super::pallet; use frame_support::{ - assert_noop, assert_ok, derive_impl, - pallet_prelude::DispatchError, - traits::{ConstU128, ConstU64, ReservableCurrency}, + derive_impl, + traits::{ConstU128, ConstU64}, }; -use frame_system::{pallet_prelude::BlockNumberFor, RawOrigin}; +use frame_system::pallet_prelude::BlockNumberFor; -const DAY: u64 = 24 * 3600 * 1000; // ms +pub const DAY: u64 = 24 * 3600 * 1000; // ms -const INITIAL_TIME: u64 = 10 * DAY; -const EXPECTED_AMOUNT: u128 = 100; -const WAITING_TIME: u64 = DAY; -const PERIOD: u64 = 50; +pub const INITIAL_TIME: u64 = 10 * DAY; +pub const EXPECTED_AMOUNT: u128 = 100; +pub const WAITING_TIME: u64 = DAY; +pub const PERIOD: u64 = 50; frame_support::construct_runtime!( pub struct Runtime { @@ -70,129 +69,3 @@ pub fn new_test_ext() -> sp_io::TestExternalities { ext } - -mod tests { - use super::*; - - const ALICE: u64 = 2; - - #[test] - fn reserve() { - new_test_ext().execute_with(|| { - // Mock the internal call done to T::Currency::reserve() inside of - // MyPaller:make_reserve() checking the expected inputs and returining a successfull - // value. - MockCurrency::mock_reserve(|account_id, amount| { - assert_eq!(account_id, &ALICE); - assert_eq!(amount, EXPECTED_AMOUNT); - Ok(()) - }); - - assert_ok!(MyPallet::make_reserve(RawOrigin::Signed(ALICE).into(), EXPECTED_AMOUNT)); - - assert_eq!(LastDeposit::::get(ALICE), Some(INITIAL_TIME)) - }) - } - - #[test] - fn reserve_error() { - new_test_ext().execute_with(|| { - // Mock the internal call to T::Currency::reserve() to emulate an error - MockCurrency::mock_reserve(|_, _| Err(DispatchError::Other("Err"))); - - assert_noop!( - MyPallet::make_reserve(RawOrigin::Signed(ALICE).into(), EXPECTED_AMOUNT), - DispatchError::Other("Err") - ); - }) - } - - /// Utility to amalgamate the pallet call with the required mocks to make them work successfull - fn do_alice_reserve(amount: u128) { - MockCurrency::mock_reserve(|account_id, amount| { - let previous_reserve = MockCurrency::reserved_balance(account_id); - - // Mocks can be nested. - // Mocking a reserve implies to create a new mock for the updated reserved_balance value - // In order to fetch later the correct updated value - MockCurrency::mock_reserved_balance(move |_| previous_reserve + amount); - - Ok(()) - }); - - MyPallet::make_reserve(RawOrigin::Signed(ALICE).into(), amount).unwrap(); - } - - #[test] - fn create_auction() { - new_test_ext().execute_with(|| { - do_alice_reserve(EXPECTED_AMOUNT); - - // Emulate advance in time to fulfill the auction conditions - MockTime::mock_now(|| INITIAL_TIME + DAY); - - // Successfull internal call to new auction - MockAuctioneer::mock_new_auction(|block, period| { - assert_eq!(block, frame_system::Pallet::::block_number()); - assert_eq!(period, PERIOD); - Ok(()) - }); - - assert_ok!(MyPallet::create_auction(RawOrigin::Signed(ALICE).into())); - }) - } - - #[test] - fn create_auction_with_several_deposits() { - new_test_ext().execute_with(|| { - do_alice_reserve(EXPECTED_AMOUNT / 2); - do_alice_reserve(EXPECTED_AMOUNT / 2); - - MockTime::mock_now(|| INITIAL_TIME + DAY); - MockAuctioneer::mock_new_auction(|_, _| Ok(())); - - assert_ok!(MyPallet::create_auction(RawOrigin::Signed(ALICE).into())); - }) - } - - #[test] - fn not_enough_deposit_error() { - new_test_ext().execute_with(|| { - do_alice_reserve(EXPECTED_AMOUNT / 2); - - assert_noop!( - MyPallet::create_auction(RawOrigin::Signed(ALICE).into()), - Error::::NotEnoughDeposit - ); - }); - } - - #[test] - fn not_enough_waiting_error() { - new_test_ext().execute_with(|| { - do_alice_reserve(EXPECTED_AMOUNT); - - assert_noop!( - MyPallet::create_auction(RawOrigin::Signed(ALICE).into()), - Error::::NotEnoughWaiting - ); - }); - } - - #[test] - fn auction_error() { - new_test_ext().execute_with(|| { - do_alice_reserve(EXPECTED_AMOUNT); - - MockTime::mock_now(|| INITIAL_TIME + DAY); - - // We emulate an error in the new_auction() dependency call. - MockAuctioneer::mock_new_auction(|_, _| Err(DispatchError::Other("Err"))); - - assert_noop!( - MyPallet::create_auction(RawOrigin::Signed(ALICE).into()), - DispatchError::Other("Err") - ); - }); - } -} diff --git a/substrate/frame/examples/mock-builder/src/tests.rs b/substrate/frame/examples/mock-builder/src/tests.rs index e69de29bb2d1..17a21d4fa48e 100644 --- a/substrate/frame/examples/mock-builder/src/tests.rs +++ b/substrate/frame/examples/mock-builder/src/tests.rs @@ -0,0 +1,133 @@ +use super::{ + mock::{ + new_test_ext, MockAuctioneer, MockCurrency, MockTime, MyPallet, Runtime, DAY, + EXPECTED_AMOUNT, INITIAL_TIME, PERIOD, + }, + Error, LastDeposit, +}; +use frame_support::{ + assert_noop, assert_ok, pallet_prelude::DispatchError, traits::ReservableCurrency, +}; +use frame_system::RawOrigin; + +const ALICE: u64 = 2; + +#[test] +fn reserve() { + new_test_ext().execute_with(|| { + // Mock the internal call done to T::Currency::reserve() inside of + // MyPaller:make_reserve() checking the expected inputs and returining a successfull + // value. + MockCurrency::mock_reserve(|account_id, amount| { + assert_eq!(account_id, &ALICE); + assert_eq!(amount, EXPECTED_AMOUNT); + Ok(()) + }); + + assert_ok!(MyPallet::make_reserve(RawOrigin::Signed(ALICE).into(), EXPECTED_AMOUNT)); + + assert_eq!(LastDeposit::::get(ALICE), Some(INITIAL_TIME)) + }) +} + +#[test] +fn reserve_error() { + new_test_ext().execute_with(|| { + // Mock the internal call to T::Currency::reserve() to emulate an error + MockCurrency::mock_reserve(|_, _| Err(DispatchError::Other("Err"))); + + assert_noop!( + MyPallet::make_reserve(RawOrigin::Signed(ALICE).into(), EXPECTED_AMOUNT), + DispatchError::Other("Err") + ); + }) +} + +/// Utility to amalgamate the pallet call with the required mocks to make them work successfull +fn do_alice_reserve(amount: u128) { + MockCurrency::mock_reserve(|account_id, amount| { + let previous_reserve = MockCurrency::reserved_balance(account_id); + + // Mocks can be nested. + // Mocking a reserve implies to create a new mock for the updated reserved_balance value + // In order to fetch later the correct updated value + MockCurrency::mock_reserved_balance(move |_| previous_reserve + amount); + + Ok(()) + }); + + MyPallet::make_reserve(RawOrigin::Signed(ALICE).into(), amount).unwrap(); +} + +#[test] +fn create_auction() { + new_test_ext().execute_with(|| { + do_alice_reserve(EXPECTED_AMOUNT); + + // Emulate advance in time to fulfill the auction conditions + MockTime::mock_now(|| INITIAL_TIME + DAY); + + // Successfull internal call to new auction + MockAuctioneer::mock_new_auction(|block, period| { + assert_eq!(block, frame_system::Pallet::::block_number()); + assert_eq!(period, PERIOD); + Ok(()) + }); + + assert_ok!(MyPallet::create_auction(RawOrigin::Signed(ALICE).into())); + }) +} + +#[test] +fn create_auction_with_several_deposits() { + new_test_ext().execute_with(|| { + do_alice_reserve(EXPECTED_AMOUNT / 2); + do_alice_reserve(EXPECTED_AMOUNT / 2); + + MockTime::mock_now(|| INITIAL_TIME + DAY); + MockAuctioneer::mock_new_auction(|_, _| Ok(())); + + assert_ok!(MyPallet::create_auction(RawOrigin::Signed(ALICE).into())); + }) +} + +#[test] +fn not_enough_deposit_error() { + new_test_ext().execute_with(|| { + do_alice_reserve(EXPECTED_AMOUNT / 2); + + assert_noop!( + MyPallet::create_auction(RawOrigin::Signed(ALICE).into()), + Error::::NotEnoughDeposit + ); + }); +} + +#[test] +fn not_enough_waiting_error() { + new_test_ext().execute_with(|| { + do_alice_reserve(EXPECTED_AMOUNT); + + assert_noop!( + MyPallet::create_auction(RawOrigin::Signed(ALICE).into()), + Error::::NotEnoughWaiting + ); + }); +} + +#[test] +fn auction_error() { + new_test_ext().execute_with(|| { + do_alice_reserve(EXPECTED_AMOUNT); + + MockTime::mock_now(|| INITIAL_TIME + DAY); + + // We emulate an error in the new_auction() dependency call. + MockAuctioneer::mock_new_auction(|_, _| Err(DispatchError::Other("Err"))); + + assert_noop!( + MyPallet::create_auction(RawOrigin::Signed(ALICE).into()), + DispatchError::Other("Err") + ); + }); +} From 891d959bc9c4bd00afa40c381ed17c9fa4980768 Mon Sep 17 00:00:00 2001 From: lemunozm Date: Tue, 24 Oct 2023 14:54:50 +0200 Subject: [PATCH 3/4] remove benchmarking --- Cargo.lock | 1 - substrate/frame/examples/mock-builder/Cargo.toml | 3 --- substrate/frame/examples/mock-builder/src/benchmarking.rs | 0 substrate/frame/examples/mock-builder/src/lib.rs | 3 --- 4 files changed, 7 deletions(-) delete mode 100644 substrate/frame/examples/mock-builder/src/benchmarking.rs diff --git a/Cargo.lock b/Cargo.lock index 40755d26157c..ac1546ed5c3d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -9983,7 +9983,6 @@ dependencies = [ name = "pallet-example-mock-builder" version = "4.0.0-dev" dependencies = [ - "frame-benchmarking", "frame-support", "frame-system", "mock-builder", diff --git a/substrate/frame/examples/mock-builder/Cargo.toml b/substrate/frame/examples/mock-builder/Cargo.toml index a7392eedf06b..694078cbf9b5 100644 --- a/substrate/frame/examples/mock-builder/Cargo.toml +++ b/substrate/frame/examples/mock-builder/Cargo.toml @@ -16,7 +16,6 @@ targets = ["x86_64-unknown-linux-gnu"] codec = { package = "parity-scale-codec", version = "3.6.1", default-features = false } scale-info = { version = "2.10.0", default-features = false, features = ["derive"] } -frame-benchmarking = { path = "../../benchmarking", default-features = false, optional = true} frame-support = { path = "../../support", default-features = false} frame-system = { path = "../../system", default-features = false} @@ -37,13 +36,11 @@ default = [ "std" ] std = [ "codec/std", "scale-info/std", - "frame-benchmarking?/std", "frame-support/std", "frame-system/std", "polkadot-runtime-common/std", ] runtime-benchmarks = [ - "frame-benchmarking/runtime-benchmarks", "frame-support/runtime-benchmarks", "frame-system/runtime-benchmarks", "sp-runtime/runtime-benchmarks", diff --git a/substrate/frame/examples/mock-builder/src/benchmarking.rs b/substrate/frame/examples/mock-builder/src/benchmarking.rs deleted file mode 100644 index e69de29bb2d1..000000000000 diff --git a/substrate/frame/examples/mock-builder/src/lib.rs b/substrate/frame/examples/mock-builder/src/lib.rs index 0755f929e633..935b87ef33b6 100644 --- a/substrate/frame/examples/mock-builder/src/lib.rs +++ b/substrate/frame/examples/mock-builder/src/lib.rs @@ -13,9 +13,6 @@ mod mock; #[cfg(test)] mod tests; -#[cfg(feature = "runtime-benchmarks")] -mod benchmarking; - #[frame_support::pallet] pub mod pallet { use frame_support::{ From eb0c31d7af8d0a7585c7c89a4faf6280e6662b8c Mon Sep 17 00:00:00 2001 From: lemunozm Date: Tue, 14 Nov 2023 11:43:20 +0100 Subject: [PATCH 4/4] minor corrections --- substrate/frame/examples/mock-builder/Cargo.toml | 4 +++- substrate/frame/examples/mock-builder/src/lib.rs | 4 +++- .../examples/mock-builder/src/mock/mock_pallets.rs | 14 +++++++++++--- substrate/frame/examples/mock-builder/src/tests.rs | 2 +- 4 files changed, 18 insertions(+), 6 deletions(-) diff --git a/substrate/frame/examples/mock-builder/Cargo.toml b/substrate/frame/examples/mock-builder/Cargo.toml index 694078cbf9b5..c99bb691e256 100644 --- a/substrate/frame/examples/mock-builder/Cargo.toml +++ b/substrate/frame/examples/mock-builder/Cargo.toml @@ -19,12 +19,13 @@ scale-info = { version = "2.10.0", default-features = false, features = ["derive frame-support = { path = "../../support", default-features = false} frame-system = { path = "../../system", default-features = false} +sp-runtime = { path = "../../../primitives/runtime", default-features = false} + polkadot-runtime-common = { path = "../../../../polkadot/runtime/common", default-features = false} [dev-dependencies] sp-io = { path = "../../../primitives/io"} sp-core = { path = "../../../primitives/core"} -sp-runtime = { path = "../../../primitives/runtime"} sp-std = { path = "../../../primitives/std"} polkadot-primitives = { path = "../../../../polkadot/primitives", default-features = false} @@ -38,6 +39,7 @@ std = [ "scale-info/std", "frame-support/std", "frame-system/std", + "sp-runtime/std", "polkadot-runtime-common/std", ] runtime-benchmarks = [ diff --git a/substrate/frame/examples/mock-builder/src/lib.rs b/substrate/frame/examples/mock-builder/src/lib.rs index 935b87ef33b6..fe0497bb3cec 100644 --- a/substrate/frame/examples/mock-builder/src/lib.rs +++ b/substrate/frame/examples/mock-builder/src/lib.rs @@ -21,6 +21,7 @@ pub mod pallet { }; use frame_system::pallet_prelude::*; use polkadot_runtime_common::traits::Auctioneer; + use sp_runtime::traits::Saturating; type MomentOf = <::Time as Time>::Moment; type BalanceOf = @@ -81,7 +82,8 @@ pub mod pallet { // Check time let now = T::Time::now(); - let ready_at = T::WaitingTime::get() + LastDeposit::::get(who).unwrap_or(now); + let last = LastDeposit::::get(who).unwrap_or(now); + let ready_at = T::WaitingTime::get().saturating_add(last); ensure!(now >= ready_at, Error::::NotEnoughWaiting); let block = frame_system::Pallet::::block_number(); diff --git a/substrate/frame/examples/mock-builder/src/mock/mock_pallets.rs b/substrate/frame/examples/mock-builder/src/mock/mock_pallets.rs index 442694798411..ddc2d49d9590 100644 --- a/substrate/frame/examples/mock-builder/src/mock/mock_pallets.rs +++ b/substrate/frame/examples/mock-builder/src/mock/mock_pallets.rs @@ -56,10 +56,8 @@ pub mod mock_pallet_time { /// With procedural support, it will be autogenerated as follows, /// implementing all mock_* functions. /// -/// Be aware that this show a very complex use case with several traits with a bunch of functions +/// Be aware that this shows a very complex use case with several traits with a bunch of functions /// -/// NOTE: It's expected that if you own the trait, you can simply put one procedural attribute on -/// top of it to autogerate the mocked pallet. /// ```ignore /// #[mock_builder::pallet] /// mod mock_pallet_time { @@ -121,6 +119,16 @@ pub mod mock_pallet_time { /// } /// } /// ``` +/// +/// NOTE: It's expected that if you own the trait, you can simply put ONE procedural attribute on +/// top of it to autogerate the mocked pallet. +/// +/// ```ignore +/// #[mock_builder::pallet] // <- this +/// trait MyCustomTrait { +/// // A lot of methods +/// } +/// ``` #[frame_support::pallet(dev_mode)] pub mod mock_pallet_currency { use frame_support::{ diff --git a/substrate/frame/examples/mock-builder/src/tests.rs b/substrate/frame/examples/mock-builder/src/tests.rs index 17a21d4fa48e..cc82f1d511cd 100644 --- a/substrate/frame/examples/mock-builder/src/tests.rs +++ b/substrate/frame/examples/mock-builder/src/tests.rs @@ -16,7 +16,7 @@ const ALICE: u64 = 2; fn reserve() { new_test_ext().execute_with(|| { // Mock the internal call done to T::Currency::reserve() inside of - // MyPaller:make_reserve() checking the expected inputs and returining a successfull + // MyPallet:make_reserve() checking the expected inputs and returning a successfull // value. MockCurrency::mock_reserve(|account_id, amount| { assert_eq!(account_id, &ALICE);