diff --git a/pallets/bidding/src/benchmarking.rs b/pallets/bidding/src/benchmarking.rs index 575fd419..fbd83d10 100644 --- a/pallets/bidding/src/benchmarking.rs +++ b/pallets/bidding/src/benchmarking.rs @@ -6,15 +6,3 @@ use super::*; use crate::Pallet as Bidding; use frame_benchmarking::{benchmarks, whitelisted_caller}; use frame_system::RawOrigin; - -benchmarks! { - do_something { - let s in 0 .. 100; - let caller: T::AccountId = whitelisted_caller(); - }: _(RawOrigin::Signed(caller), s) - verify { - assert_eq!(Something::::get(), Some(s)); - } - - impl_benchmark_test_suite!(Bidding, crate::mock::new_test_ext(), crate::mock::Test); -} diff --git a/pallets/bidding/src/lib.rs b/pallets/bidding/src/lib.rs index 737962a2..beccc755 100644 --- a/pallets/bidding/src/lib.rs +++ b/pallets/bidding/src/lib.rs @@ -73,9 +73,6 @@ pub mod pallet { #[pallet::event] #[pallet::generate_deposit(pub(super) fn deposit_event)] pub enum Event { - /// Event documentation should end with an array that provides descriptive names for event - /// parameters. [something, who] - SomethingStored(u32, T::AccountId), // The house is already being processed HouseAlreadyInBiddingProcess(T::NftCollectionId, T::NftItemId, Housing_Fund::BalanceOf, BlockNumberOf), // No enough fund for the house @@ -91,52 +88,14 @@ pub mod pallet { // Errors inform users that something went wrong. #[pallet::error] pub enum Error { - /// Error names should be descriptive. - NoneValue, - /// Errors should have helpful documentation associated with them. - StorageOverflow, /// No new onboarded houses found NoNewHousesFound, } #[pallet::call] impl Pallet { - /// An example dispatchable that takes a singles value as a parameter, writes the value to - /// storage and emits an event. This function must be dispatched by a signed extrinsic. - //#[pallet::weight(10_000 + T::DbWeight::get().writes(1))] - #[pallet::weight(::WeightInfo::do_something(100))] - pub fn do_something(origin: OriginFor, something: u32) -> DispatchResult { - // Check that the extrinsic was signed and get the signer. - // This function will return an error if the extrinsic is not signed. - let who = ensure_signed(origin)?; - - // Update storage. - >::put(something); - - // Emit an event. - Self::deposit_event(Event::SomethingStored(something, who)); - // Return a successful DispatchResultWithPostInfo - Ok(()) - } - - /// An example dispatchable that may throw a custom error. - #[pallet::weight(10_000 + T::DbWeight::get().reads_writes(1,1))] - pub fn cause_error(origin: OriginFor) -> DispatchResult { - let _who = ensure_signed(origin)?; - - // Read a value from storage. - match >::get() { - // Return an error if the value has not been set. - None => return Err(Error::::NoneValue.into()), - Some(old) => { - // Increment the value read from storage; will error in the event of overflow. - let new = old.checked_add(1).ok_or(Error::::StorageOverflow)?; - // Update the value in storage with the incremented result. - >::put(new); - Ok(()) - }, - } - } + + } } @@ -176,8 +135,7 @@ impl Pallet { // Checki that the investor list creation was successful if investors_shares.len() == 0 { - let block = > - ::block_number(); + let block = >::block_number(); Self::deposit_event(Event::FailedToAssembleInvestor( item.0.clone(), item.1.clone(), amount.clone(), block, )); @@ -209,7 +167,7 @@ impl Pallet { } /// Create the list of investor and their contribution for a given asset's price - /// I follows the rules: + /// It follows the rules: /// - the oldest contribution comes first /// - no more than T::MaximumSharePerInvestor share per investor /// - no less than T::MinimumSharePerInvestor share per investor diff --git a/pallets/bidding/src/mock.rs b/pallets/bidding/src/mock.rs index a9e686f8..d7e3dfa4 100644 --- a/pallets/bidding/src/mock.rs +++ b/pallets/bidding/src/mock.rs @@ -23,6 +23,7 @@ type Block = frame_system::mocking::MockBlock; pub(crate) type Balance = u128; pub type CollectionId = u32; pub type ItemId = u32; +pub type NftCollection = pallet_nft::PossibleCollections; type AccountId = u64; pub type MaxProposals = u32; pub type BlockNumber = u64; @@ -309,6 +310,8 @@ pub const BOB: u64 = 2; pub const CHARLIE: u64 = 3; pub const DAVE: u64 = 4; pub const EVE: u64 = 5; +pub const AMANI: u64 = 6; +pub const KEZIA: u64 = 7; // Build genesis storage according to the mock runtime. pub fn new_test_ext() -> sp_io::TestExternalities { @@ -321,6 +324,8 @@ pub fn new_test_ext() -> sp_io::TestExternalities { (CHARLIE, 200_000_000), (DAVE, 150_000), (EVE, 150_000), + (AMANI, 150_000), + (KEZIA, 150_000), ], } .assimilate_storage(&mut storage) diff --git a/pallets/bidding/src/tests.rs b/pallets/bidding/src/tests.rs index 05c2ca53..09faa1e3 100644 --- a/pallets/bidding/src/tests.rs +++ b/pallets/bidding/src/tests.rs @@ -1,22 +1,429 @@ +use super::*; use crate::{mock::*, Error}; -use frame_support::{assert_noop, assert_ok}; +use frame_support::{assert_noop, assert_ok, BoundedVec}; +use std::any::type_name; + +fn type_of(_: T) -> &'static str { + type_name::() +} + +#[test] +fn convert_u64_to_balance_option_should_succeed() { + new_test_ext().execute_with(|| { + let amount: u64 = 100; + let converted_amount: crate::Housing_Fund::BalanceOf = 100; + + assert_eq!( + type_of(BiddingModule::u64_to_balance_option(amount)), + type_of(Some(converted_amount)) + ); + + assert_eq!( + BiddingModule::u64_to_balance_option(amount), + Some(converted_amount) + ); + }); +} + +#[test] +fn convert_balance_should_succeed() { + new_test_ext().execute_with(|| { + let amount: crate::Onboarding::BalanceOf = 100; + let converted_amount: crate::Housing_Fund::BalanceOf = 100; + + assert_eq!( + type_of(BiddingModule::convert_balance(amount)), + type_of(Some(converted_amount)) + ); + + assert_eq!( + BiddingModule::convert_balance(amount), + Some(converted_amount) + ); + }); +} + +#[test] +fn get_amount_percentage_should_succeed() { + new_test_ext().execute_with(|| { + let amount: crate::Housing_Fund::BalanceOf = 1000; + + assert_eq!( + BiddingModule::get_amount_percentage(amount, 20), + 200 + ); + + assert_eq!( + BiddingModule::get_amount_percentage(amount, 36), + 360 + ); + }); +} + + +#[test] +fn get_investor_share_should_succeed() { + new_test_ext().execute_with(|| { + let amount: crate::Housing_Fund::BalanceOf = 100; + + let mut contribution: crate::Housing_Fund::Contribution = crate::Housing_Fund::Contribution { + account_id: 1, + available_balance: HousingFund::u64_to_balance_option(25).unwrap(), + reserved_balance: HousingFund::u64_to_balance_option(0).unwrap(), + contributed_balance: HousingFund::u64_to_balance_option(0).unwrap(), + has_withdrawn: false, + block_number: 1, + contributions: vec![crate::Housing_Fund::ContributionLog { + amount: HousingFund::u64_to_balance_option(25).unwrap(), + block_number: 1 + }], + withdraws: Vec::new() + }; + + assert_eq!( + BiddingModule::get_investor_share(amount.clone(), contribution.clone()), + 20 + ); + + contribution.reserve_amount(10); + + assert_eq!( + BiddingModule::get_investor_share(amount, contribution), + 15 + ); + }); +} + +#[test] +fn get_oldest_contribution_should_succeed() { + new_test_ext().execute_with(|| { + let ordered_list = Vec::new(); + + let contribution: crate::Housing_Fund::Contribution = crate::Housing_Fund::Contribution { + account_id: 1, + available_balance: HousingFund::u64_to_balance_option(25).unwrap(), + reserved_balance: HousingFund::u64_to_balance_option(0).unwrap(), + contributed_balance: HousingFund::u64_to_balance_option(0).unwrap(), + has_withdrawn: false, + block_number: 1, + contributions: vec![crate::Housing_Fund::ContributionLog { + amount: HousingFund::u64_to_balance_option(25).unwrap(), + block_number: 1 + }], + withdraws: Vec::new() + }; + + let contributions = vec![ + (1, contribution.clone()), + (2, crate::Housing_Fund::Contribution { + account_id: 1, + available_balance: HousingFund::u64_to_balance_option(30).unwrap(), + reserved_balance: HousingFund::u64_to_balance_option(0).unwrap(), + contributed_balance: HousingFund::u64_to_balance_option(0).unwrap(), + has_withdrawn: false, + block_number: 2, + contributions: vec![crate::Housing_Fund::ContributionLog { + amount: HousingFund::u64_to_balance_option(25).unwrap(), + block_number: 1 + }], + withdraws: Vec::new() + }) + ]; + + assert_eq!( + BiddingModule::get_oldest_contribution(ordered_list, contributions), + (1, contribution) + ); + }); +} + +#[test] +fn create_investor_list_should_succeed() { + new_test_ext().execute_with(|| { + + let mut block_number = System::block_number(); + let mut amount = 20; + + for account_id in 1..7 { + assert_ok!(RoleModule::set_role( + Origin::signed(account_id.clone()), + account_id, + crate::Onboarding::HousingFund::ROLES::Accounts::INVESTOR + )); + + if account_id > 4 { + amount = 10; + } + // test contribute with sufficient contribution and free balance + assert_ok!(HousingFund::contribute_to_fund(Origin::signed(account_id), amount)); + + let contribution = HousingFund::contributions(account_id).unwrap(); + + assert_eq!(contribution.block_number, block_number); + + block_number = block_number.saturating_add(1); + System::set_block_number(block_number.clone()); + } + + let investor_list = BiddingModule::create_investor_list(100); + + assert_eq!(investor_list.contains(&(1, 20)), true); + assert_eq!(investor_list.contains(&(2, 20)), true); + assert_eq!(investor_list.contains(&(3, 20)), true); + assert_eq!(investor_list.contains(&(4, 20)), true); + assert_eq!(investor_list.contains(&(5, 10)), true); + assert_eq!(investor_list.contains(&(6, 10)), true); + }); +} + +#[test] +fn create_investor_list_should_fail() { + new_test_ext().execute_with(|| { + + let mut block_number = System::block_number(); + let mut amount = 20; + + for account_id in 1..7 { + assert_ok!(RoleModule::set_role( + Origin::signed(account_id.clone()), + account_id, + crate::Onboarding::HousingFund::ROLES::Accounts::INVESTOR + )); + + if account_id > 2 { + if account_id == 6 { + amount = 30; + } + else { + amount = 10; + } + } + + // test contribute with sufficient contribution and free balance + assert_ok!(HousingFund::contribute_to_fund(Origin::signed(account_id), amount)); + + let contribution = HousingFund::contributions(account_id).unwrap(); + + assert_eq!(contribution.block_number, block_number); + + block_number = block_number.saturating_add(1); + System::set_block_number(block_number.clone()); + } + + let investor_list = BiddingModule::create_investor_list(100); + + assert_eq!(investor_list.len(), 0); + }); +} #[test] -fn it_works_for_default_value() { +fn process_asset_not_enough_fund_should_fail() { new_test_ext().execute_with(|| { - // Dispatch a signed extrinsic. - assert_ok!(BiddingModule::do_something(Origin::signed(1), 42)); - // Read pallet storage and assert an expected result. - assert_eq!(BiddingModule::something(), Some(42)); + + let mut block_number = System::block_number(); + let amount = 20; + + for account_id in 1..6 { + assert_ok!(RoleModule::set_role( + Origin::signed(account_id.clone()), + account_id, + crate::Onboarding::HousingFund::ROLES::Accounts::INVESTOR + )); + + // test contribute with sufficient contribution and free balance + assert_ok!(HousingFund::contribute_to_fund(Origin::signed(account_id), amount)); + + let contribution = HousingFund::contributions(account_id).unwrap(); + + assert_eq!(contribution.block_number, block_number); + + block_number = block_number.saturating_add(1); + System::set_block_number(block_number.clone()); + } + + assert_ok!(RoleModule::set_role(Origin::signed(KEZIA).clone(), KEZIA, crate::Onboarding::HousingFund::ROLES::Accounts::SERVICER)); + assert_ok!(RoleModule::account_approval(Origin::signed(ALICE), KEZIA)); + assert_ok!(RoleModule::set_role(Origin::signed(AMANI).clone(), AMANI, crate::Onboarding::HousingFund::ROLES::Accounts::SELLER)); + assert_ok!(RoleModule::account_approval(Origin::signed(ALICE), AMANI)); + + let metadata: BoundedVec::StringLimit> = b"metadata0".to_vec().try_into().unwrap(); + + assert_ok!(NftModule::create_collection( + Origin::signed(KEZIA), + NftCollection::OFFICESTEST, + metadata.clone() + )); + + assert_ok!(OnboardingModule::create_and_submit_proposal( + Origin::signed(AMANI), + NftCollection::OFFICESTEST, + Some(100), + metadata, + false + )); + + let collection_id = NftCollection::OFFICESTEST.value(); + let item_id = pallet_nft::ItemsCount::::get()[collection_id as usize] - 1; + + assert_ok!(OnboardingModule::change_status( + Origin::signed(AMANI), + NftCollection::OFFICESTEST,item_id.clone(), + crate::Onboarding::AssetStatus::ONBOARDED) + ); + + assert_ok!(BiddingModule::process_asset()); + + let event = >::events() + .pop() + .expect("Expected at least one EventRecord to be found") + .event; + + // check that the event has been raised + assert_eq!( + event, + mock::Event::BiddingModule(crate::Event::HousingFundNotEnough(collection_id, item_id, 100, block_number)) + ); }); } #[test] -fn correct_error_for_none_value() { +fn process_asset_cannot_assemble_investor_should_fail() { new_test_ext().execute_with(|| { - // Ensure the expected error is thrown when no value is present. - assert_noop!(BiddingModule::cause_error(Origin::signed(1)), Error::::NoneValue); + + let mut block_number = System::block_number(); + let amount = 100; + + for account_id in 1..6 { + assert_ok!(RoleModule::set_role( + Origin::signed(account_id.clone()), + account_id, + crate::Onboarding::HousingFund::ROLES::Accounts::INVESTOR + )); + + // test contribute with sufficient contribution and free balance + assert_ok!(HousingFund::contribute_to_fund(Origin::signed(account_id), amount)); + + let contribution = HousingFund::contributions(account_id).unwrap(); + + assert_eq!(contribution.block_number, block_number); + + block_number = block_number.saturating_add(1); + System::set_block_number(block_number.clone()); + } + + assert_ok!(HousingFund::withdraw_fund(Origin::signed(EVE), 90)); + + assert_ok!(RoleModule::set_role(Origin::signed(KEZIA).clone(), KEZIA, crate::Onboarding::HousingFund::ROLES::Accounts::SERVICER)); + assert_ok!(RoleModule::account_approval(Origin::signed(ALICE), KEZIA)); + assert_ok!(RoleModule::set_role(Origin::signed(AMANI).clone(), AMANI, crate::Onboarding::HousingFund::ROLES::Accounts::SELLER)); + assert_ok!(RoleModule::account_approval(Origin::signed(ALICE), AMANI)); + + let metadata: BoundedVec::StringLimit> = b"metadata0".to_vec().try_into().unwrap(); + + assert_ok!(NftModule::create_collection( + Origin::signed(KEZIA), + NftCollection::OFFICESTEST, + metadata.clone() + )); + + assert_ok!(OnboardingModule::create_and_submit_proposal( + Origin::signed(AMANI), + NftCollection::OFFICESTEST, + Some(100), + metadata, + false + )); + + let collection_id = NftCollection::OFFICESTEST.value(); + let item_id = pallet_nft::ItemsCount::::get()[collection_id as usize] - 1; + + assert_ok!(OnboardingModule::change_status( + Origin::signed(AMANI), + NftCollection::OFFICESTEST,item_id.clone(), + crate::Onboarding::AssetStatus::ONBOARDED) + ); + + assert_ok!(BiddingModule::process_asset()); + + let event = >::events() + .pop() + .expect("Expected at least one EventRecord to be found") + .event; + + // check that the event has been raised + assert_eq!( + event, + mock::Event::BiddingModule(crate::Event::FailedToAssembleInvestor(collection_id, item_id, 100, block_number)) + ); }); } +#[test] +fn process_asset_should_succeed() { + new_test_ext().execute_with(|| { + + let mut block_number = System::block_number(); + let amount = 100; + + for account_id in 1..6 { + assert_ok!(RoleModule::set_role( + Origin::signed(account_id.clone()), + account_id, + crate::Onboarding::HousingFund::ROLES::Accounts::INVESTOR + )); + + // test contribute with sufficient contribution and free balance + assert_ok!(HousingFund::contribute_to_fund(Origin::signed(account_id), amount)); + let contribution = HousingFund::contributions(account_id).unwrap(); + + assert_eq!(contribution.block_number, block_number); + + block_number = block_number.saturating_add(1); + System::set_block_number(block_number.clone()); + } + + assert_ok!(RoleModule::set_role(Origin::signed(KEZIA).clone(), KEZIA, crate::Onboarding::HousingFund::ROLES::Accounts::SERVICER)); + assert_ok!(RoleModule::account_approval(Origin::signed(ALICE), KEZIA)); + assert_ok!(RoleModule::set_role(Origin::signed(AMANI).clone(), AMANI, crate::Onboarding::HousingFund::ROLES::Accounts::SELLER)); + assert_ok!(RoleModule::account_approval(Origin::signed(ALICE), AMANI)); + + let metadata: BoundedVec::StringLimit> = b"metadata0".to_vec().try_into().unwrap(); + + assert_ok!(NftModule::create_collection( + Origin::signed(KEZIA), + NftCollection::OFFICESTEST, + metadata.clone() + )); + + assert_ok!(OnboardingModule::create_and_submit_proposal( + Origin::signed(AMANI), + NftCollection::OFFICESTEST, + Some(100), + metadata, + false + )); + + let collection_id = NftCollection::OFFICESTEST.value(); + let item_id = pallet_nft::ItemsCount::::get()[collection_id as usize] - 1; + + assert_ok!(OnboardingModule::change_status( + Origin::signed(AMANI), + NftCollection::OFFICESTEST,item_id.clone(), + crate::Onboarding::AssetStatus::ONBOARDED) + ); + + assert_ok!(BiddingModule::process_asset()); + + let event = >::events() + .pop() + .expect("Expected at least one EventRecord to be found") + .event; + + // check that the event has been raised + assert_eq!( + event, + mock::Event::BiddingModule(crate::Event::HouseBiddingSucceeded(collection_id, item_id, 100, block_number)) + ); + }); +} diff --git a/runtime/src/lib.rs b/runtime/src/lib.rs index 92629913..4caab214 100644 --- a/runtime/src/lib.rs +++ b/runtime/src/lib.rs @@ -1175,7 +1175,6 @@ mod benches { [pallet_im_online, ImOnline] [pallet_utility, Utility] //[pallet_voting, VotingModule] - [pallet_bidding, BiddingModule] // flag add pallet bench_macro ); } @@ -1415,7 +1414,6 @@ impl_runtime_apis! { add_benchmark!(params, batches, pallet_share_distributor, ShareDistributor); add_benchmark!(params, batches, pallet_utility, Utility); //add_benchmark!(params, batches, pallet_voting, VotingModule); - add_benchmark!(params, batches, pallet_bidding, BiddingModule); // flag add pallet benchmark Ok(batches)