Skip to content

Commit

Permalink
Integration tests: fudge support for the generic environment (#1588)
Browse files Browse the repository at this point in the history
* Add handle and base env structure

* evolving and state support for fudge env

* Support Client APIs

* simplify test cases

* Add spetial fudge methos

* Add T to FudgeHandle

* sumbmit extrinsic works

* use correct nonce

* minor Env trait simplification

* minor example reorder

* fudge support for all runtimes

* fix nonce and divide api between submit now and later

* minor organization

* init tests for loans

* fix ongoing loans test

* fix compilation

* minor renames

* restore expect

* loan testing in progress

* fixed issue with asset_registry

* add CurrencyInfo genesis utility

* extend loan tests, fix block by seconds

* Api from Runtime into an associated type for expliciteness

* minor changes

* fix and extend loan testing

* fix and extend loan testing

* remove unused pallet sudo addition
  • Loading branch information
lemunozm authored Oct 23, 2023
1 parent 06f3bc8 commit 8e32e01
Show file tree
Hide file tree
Showing 13 changed files with 1,368 additions and 201 deletions.
2 changes: 2 additions & 0 deletions Cargo.lock

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

3 changes: 2 additions & 1 deletion runtime/integration-tests/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -31,11 +31,11 @@ pallet-timestamp = { git = "https://github.com/paritytech/substrate", branch = "
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-transaction-pool = { 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" }
Expand All @@ -50,6 +50,7 @@ fp-self-contained = { git = "https://github.com/PureStake/frontier", branch = "m
## Substrate-Client
node-primitives = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.38" }
sc-client-api = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.38" }
sc-block-builder = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.38" }
#sc-consensus-uncles = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.38" }
sc-executor = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.38" }
sc-service = { git = "https://github.com/paritytech/substrate", features = ["rocksdb", "test-helpers"], branch = "polkadot-v0.9.38" }
Expand Down
148 changes: 96 additions & 52 deletions runtime/integration-tests/src/generic/cases/example.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,14 @@
use cfg_primitives::{AuraId, Balance, CFG};
use cfg_primitives::{Balance, CFG};
use frame_support::traits::Get;
use sp_api::runtime_decl_for_Core::CoreV4;

use crate::{
generic::{
environment::{Blocks, Env},
envs::runtime_env::RuntimeEnv,
envs::{
fudge_env::{FudgeEnv, FudgeSupport},
runtime_env::RuntimeEnv,
},
runtime::Runtime,
utils::genesis::Genesis,
},
Expand All @@ -16,11 +20,10 @@ fn transfer_balance<T: Runtime>() {
const FOR_FEES: Balance = 1 * CFG;

// Set up all GenesisConfig for your initial state
// You can choose `RuntimeEnv` by `FudgeEnv` to make it working with fudge
// environment.
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(),
Expand All @@ -31,14 +34,15 @@ fn transfer_balance<T: Runtime>() {
);

// Call an extrinsic that would be processed immediately
env.submit(
Keyring::Alice,
pallet_balances::Call::transfer {
dest: Keyring::Bob.into(),
value: TRANSFER,
},
)
.unwrap();
let fee = env
.submit_now(
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 {
Expand All @@ -48,71 +52,111 @@ fn transfer_balance<T: Runtime>() {
})
.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::Alice.to_account_id()),
T::ExistentialDeposit::get() + FOR_FEES - fee,
);
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());
})
// Pass blocks to evolve the system
env.pass(Blocks::ByNumber(1));
}

fn check_fee<T: Runtime>() {
let mut env = RuntimeEnv::<T>::from_storage(
// Identical to `transfer_balance()` test but using fudge.
fn fudge_transfer_balance<T: Runtime + FudgeSupport>() {
const TRANSFER: Balance = 1000 * CFG;
const FOR_FEES: Balance = 1 * CFG;

let mut env = FudgeEnv::<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)],
balances: vec![(
Keyring::Alice.to_account_id(),
T::ExistentialDeposit::get() + FOR_FEES + TRANSFER,
)],
})
.storage(),
);

env.submit(
env.submit_later(
Keyring::Alice,
frame_system::Call::remark { remark: vec![] },
pallet_balances::Call::transfer {
dest: Keyring::Bob.into(),
value: TRANSFER,
},
)
.unwrap();

// Get the fee of the last submitted extrinsic
let fee = env.last_fee();
// submit-later will only take effect if a block has passed
env.pass(Blocks::ByNumber(1));

// 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();

// Look for the fee for the last transaction
let fee = env
.find_event(|e| match e {
pallet_transaction_payment::Event::TransactionFeePaid { actual_fee, .. } => {
Some(actual_fee)
}
_ => None,
})
.unwrap();

// Check the state
env.state(|| {
assert_eq!(
pallet_balances::Pallet::<T>::free_balance(Keyring::Alice.to_account_id()),
1 * CFG - fee
T::ExistentialDeposit::get() + FOR_FEES - fee,
);
assert_eq!(
pallet_balances::Pallet::<T>::free_balance(Keyring::Bob.to_account_id()),
TRANSFER
);
});
}

// 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);
fn call_api<T: Runtime>() {
let env = RuntimeEnv::<T>::from_storage(Default::default());

env.state(|| {
// If imported the trait: sp_api::runtime_decl_for_Core::CoreV4,
// you can easily do: T::Api::version()
assert_eq!(
T::Api::version(),
<T as frame_system::Config>::Version::get()
);
})
}

fn fudge_call_api<T: Runtime + FudgeSupport>() {
let env = FudgeEnv::<T>::from_storage(Default::default());

// Exclusive from fudge environment.
// It uses a client to access the runtime api.
env.with_api(|api, latest| {
// We include the API we want to use
use sp_api::Core;

let result = api.version(&latest).unwrap();

assert_eq!(result, T::Api::version());
assert_eq!(result, <T as frame_system::Config>::Version::get());
})
}

// 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
crate::test_for_runtimes!([development, altair, centrifuge], transfer_balance);
crate::test_for_runtimes!(all, call_api);
crate::test_for_runtimes!(all, fudge_transfer_balance);
crate::test_for_runtimes!(all, fudge_call_api);
153 changes: 153 additions & 0 deletions runtime/integration-tests/src/generic/cases/loans.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
use cfg_primitives::{Balance, CollectionId, ItemId, PoolId, SECONDS_PER_YEAR};
use cfg_traits::{
interest::{CompoundingSchedule, InterestRate},
Seconds, TimeAsSecs,
};
use cfg_types::permissions::PoolRole;
use frame_support::traits::Get;
use pallet_loans::{
entities::{
input::PrincipalInput,
loans::LoanInfo,
pricing::{
internal::{InternalPricing, MaxBorrowAmount as IntMaxBorrowAmount},
Pricing,
},
},
types::{
valuation::ValuationMethod, BorrowRestrictions, InterestPayments, LoanRestrictions,
Maturity, PayDownSchedule, RepayRestrictions, RepaymentSchedule,
},
};
use runtime_common::apis::runtime_decl_for_PoolsApi::PoolsApiV1;

use crate::{
generic::{
environment::{Blocks, Env},
envs::runtime_env::RuntimeEnv,
runtime::Runtime,
utils::{
self,
genesis::{
self,
currency::{cfg, usd6, CurrencyInfo, Usd6},
Genesis,
},
POOL_MIN_EPOCH_TIME,
},
},
utils::{accounts::Keyring, tokens::rate_from_percent},
};

const POOL_ADMIN: Keyring = Keyring::Admin;
const INVESTOR: Keyring = Keyring::Alice;
const BORROWER: Keyring = Keyring::Bob;

const POOL_A: PoolId = 23;
const NFT_A: (CollectionId, ItemId) = (1, ItemId(10));

const FOR_FEES: Balance = cfg(1);
const EXPECTED_POOL_BALANCE: Balance = usd6(1_000_000);
const COLLATERAL_VALUE: Balance = usd6(100_000);

fn initialize_state_for_loans<Environment: Env<T>, T: Runtime>() -> Environment {
let mut env = Environment::from_storage(
Genesis::<T>::default()
.add(genesis::balances(T::ExistentialDeposit::get() + FOR_FEES))
.add(genesis::assets(vec![Usd6::ID]))
.add(genesis::tokens(vec![(Usd6::ID, Usd6::ED)]))
.storage(),
);

env.state_mut(|| {
// Creating a pool
utils::give_balance::<T>(POOL_ADMIN.id(), T::PoolDeposit::get());
utils::create_empty_pool::<T>(POOL_ADMIN.id(), POOL_A, Usd6::ID);

// Funding a pool
let tranche_id = T::Api::tranche_id(POOL_A, 0).unwrap();
let tranche_investor = PoolRole::TrancheInvestor(tranche_id, Seconds::MAX);
utils::give_pool_role::<T>(INVESTOR.id(), POOL_A, tranche_investor);
utils::give_tokens::<T>(INVESTOR.id(), Usd6::ID, EXPECTED_POOL_BALANCE);
utils::invest::<T>(INVESTOR.id(), POOL_A, tranche_id, EXPECTED_POOL_BALANCE);
});

env.pass(Blocks::BySeconds(POOL_MIN_EPOCH_TIME));

env.state_mut(|| {
// New epoch with the investor funds available
utils::close_pool_epoch::<T>(POOL_ADMIN.id(), POOL_A);

// Preparing borrower
utils::give_pool_role::<T>(BORROWER.id(), POOL_A, PoolRole::Borrower);
utils::give_nft::<T>(BORROWER.id(), NFT_A);
});

env
}

fn internal_priced_loan<T: Runtime>(now: Seconds) -> LoanInfo<T> {
LoanInfo {
schedule: RepaymentSchedule {
maturity: Maturity::Fixed {
date: now + SECONDS_PER_YEAR,
extension: SECONDS_PER_YEAR / 2,
},
interest_payments: InterestPayments::None,
pay_down_schedule: PayDownSchedule::None,
},
interest_rate: InterestRate::Fixed {
rate_per_year: rate_from_percent(20),
compounding: CompoundingSchedule::Secondly,
},
collateral: NFT_A,
pricing: Pricing::Internal(InternalPricing {
collateral_value: COLLATERAL_VALUE,
max_borrow_amount: IntMaxBorrowAmount::UpToTotalBorrowed {
advance_rate: rate_from_percent(100),
},
valuation_method: ValuationMethod::OutstandingDebt,
}),
restrictions: LoanRestrictions {
borrows: BorrowRestrictions::NotWrittenOff,
repayments: RepayRestrictions::None,
},
}
}

fn borrow<T: Runtime>() {
let mut env = initialize_state_for_loans::<RuntimeEnv<T>, T>();

let info = env.state(|| {
let now = <pallet_timestamp::Pallet<T> as TimeAsSecs>::now();
internal_priced_loan::<T>(now)
});

env.submit_now(
BORROWER,
pallet_loans::Call::create {
pool_id: POOL_A,
info,
},
)
.unwrap();

let loan_id = env
.find_event(|e| match e {
pallet_loans::Event::<T>::Created { loan_id, .. } => Some(loan_id),
_ => None,
})
.unwrap();

env.submit_now(
BORROWER,
pallet_loans::Call::borrow {
pool_id: POOL_A,
loan_id,
amount: PrincipalInput::Internal(COLLATERAL_VALUE / 2),
},
)
.unwrap();
}

crate::test_for_runtimes!(all, borrow);
Loading

0 comments on commit 8e32e01

Please sign in to comment.