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 tests: fudge support for the generic environment #1588

Merged
merged 29 commits into from
Oct 23, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
b6eb261
Add handle and base env structure
lemunozm Oct 10, 2023
ad491e2
evolving and state support for fudge env
lemunozm Oct 10, 2023
6c5f848
Support Client APIs
lemunozm Oct 10, 2023
fa48bea
simplify test cases
lemunozm Oct 10, 2023
5f17c11
Add spetial fudge methos
lemunozm Oct 10, 2023
c45a04f
Add T to FudgeHandle
lemunozm Oct 10, 2023
12beb07
sumbmit extrinsic works
lemunozm Oct 11, 2023
170b4a2
use correct nonce
lemunozm Oct 11, 2023
b6c8075
minor Env trait simplification
lemunozm Oct 11, 2023
3032d09
minor example reorder
lemunozm Oct 11, 2023
084d148
fudge support for all runtimes
lemunozm Oct 11, 2023
c94c1ad
fix nonce and divide api between submit now and later
lemunozm Oct 13, 2023
2fb97d5
minor organization
lemunozm Oct 13, 2023
bf6269a
init tests for loans
lemunozm Oct 13, 2023
21577d0
fix ongoing loans test
lemunozm Oct 14, 2023
f998636
fix compilation
lemunozm Oct 16, 2023
848339d
Merge remote-tracking branch 'origin/main' into integration-tests/fud…
lemunozm Oct 16, 2023
4b159c5
minor renames
lemunozm Oct 16, 2023
5d056ab
restore expect
lemunozm Oct 16, 2023
ef4d158
loan testing in progress
lemunozm Oct 17, 2023
774c241
Merge remote-tracking branch 'origin/main' into integration-tests/fud…
lemunozm Oct 18, 2023
ab7138b
fixed issue with asset_registry
lemunozm Oct 18, 2023
5e79525
add CurrencyInfo genesis utility
lemunozm Oct 18, 2023
93a0dda
extend loan tests, fix block by seconds
lemunozm Oct 20, 2023
6b29b2b
Api from Runtime into an associated type for expliciteness
lemunozm Oct 20, 2023
83bd44c
minor changes
lemunozm Oct 20, 2023
ed92aab
fix and extend loan testing
lemunozm Oct 20, 2023
a496e35
fix and extend loan testing
lemunozm Oct 20, 2023
744f59f
remove unused pallet sudo addition
lemunozm Oct 20, 2023
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
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(),
Comment on lines +55 to +59
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Here

Copy link
Contributor Author

Choose a reason for hiding this comment

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

The above initializes a currency in orml_asset_registry called Usd6 (6 decimals) with a metadata whose ED correctly matches the orml_tokens. No more balance than ED is given to all users (ALICE, BOB...) for that currency.

);

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
Loading