diff --git a/pallets/distance/src/mock.rs b/pallets/distance/src/mock.rs index 0b6fd464700c45ed5a38aa897e209e6953c7da9a..08ab337c88d4881d2574ee2b950aaf3ffa9b235a 100644 --- a/pallets/distance/src/mock.rs +++ b/pallets/distance/src/mock.rs @@ -27,7 +27,7 @@ use pallet_session::{ShouldEndSession, TestSessionHandler}; use scale_info::{Type, TypeInfo}; use sp_core::{ConstU128, ConstU32, ConstU64, Get, H256}; use sp_runtime::testing::{TestSignature, UintAuthorityId}; -use sp_runtime::traits::{ConvertInto, IdentifyAccount, IsMember, Verify}; +use sp_runtime::traits::{BlockNumberProvider, ConvertInto, IdentifyAccount, IsMember, Verify}; use sp_runtime::{ impl_opaque_keys, testing::Header, @@ -238,18 +238,21 @@ impl pallet_certification::Config for Test { type ValidityPeriod = ValidityPeriod; } -pub struct AuthorGiven; -impl FindAuthor<u64> for AuthorGiven { +type SystemPallet = frame_system::Pallet<Test>; +pub struct AuthorByBlockNumber; +impl FindAuthor<u64> for AuthorByBlockNumber { fn find_author<'a, I>(digests: I) -> Option<u64> where I: 'a + IntoIterator<Item = (ConsensusEngineId, &'a [u8])>, { - Some(0) + // Round-robin like authoring. + // Necessary to test with multiple authors (3 here) for distance results + Some(SystemPallet::current_block_number() % 3) } } impl pallet_authorship::Config for Test { - type FindAuthor = AuthorGiven; + type FindAuthor = AuthorByBlockNumber; type UncleGenerations = (); type FilterUncle = (); type EventHandler = (); @@ -270,7 +273,7 @@ impl pallet_authority_members::Config for Test { type MaxOfflineSessions = ConstU32<2>; type MemberId = u64; type MemberIdOf = ConvertInto; - type OnNewSession = (); + type OnNewSession = Distance; type OnRemovedMember = (); type RemoveMemberOrigin = system::EnsureRoot<u64>; type RuntimeEvent = RuntimeEvent; @@ -342,6 +345,12 @@ pub fn new_test_ext() -> sp_io::TestExternalities { .assimilate_storage(&mut t) .unwrap(); + pallet_balances::GenesisConfig::<Test> { + balances: vec![(0, 1000)], + } + .assimilate_storage(&mut t) + .unwrap(); + sp_io::TestExternalities::new(t) // GenesisConfig { diff --git a/pallets/distance/src/tests.rs b/pallets/distance/src/tests.rs index 93f433d18512305d5733a5cd94a50973308d0dd3..c3e7f6c035f5e2db610b50b1de9f5968ffb84c8c 100644 --- a/pallets/distance/src/tests.rs +++ b/pallets/distance/src/tests.rs @@ -15,30 +15,119 @@ // along with Duniter-v2S. If not, see <https://www.gnu.org/licenses/>. use crate::mock::*; -use crate::{pallet, Error}; -use frame_support::{assert_noop, assert_ok}; +use crate::{pallet, Error, MedianAcc, MedianResult}; +use frame_support::traits::Len; +use frame_support::{assert_err, assert_noop, assert_ok}; use sp_distance::ComputationResult; use sp_runtime::Perbill; use sp_std::collections::btree_map::BTreeMap; // To enable better IDE checks type Distance = pallet::Pallet<Test, ()>; +type Identity = pallet_identity::Pallet<Test>; +type Cert = pallet_certification::Pallet<Test>; + +const AUTHOR0: &u64 = &0; +const AUTHOR1: &u64 = &1; +const AUTHOR2: &u64 = &2; +// SESSION_LENGTH = 5 +const SESSION_0_START: u64 = 0; +const SESSION_1_START: u64 = 5; +const SESSION_2_START: u64 = 10; #[test] -fn test_compilation() { +fn basic_single_distance() { new_test_ext().execute_with(|| { - // Session 1: we ask for evaluation - run_to_block(1); + // Session 0: distance is asked + run_to_block(SESSION_0_START); assert_ok!(Distance::evaluate_distance(RuntimeOrigin::signed(0))); - // Session 2: distance is being computed - run_to_block(6); // session 2 wraps blocks [6, 10] because of SESSION_LENGTH - // Session 3: distance results are supposed to be published - run_to_block(11); // session 3 wraps blocks [11, 15] because of SESSION_LENGTH + // Session 1: distance is being computed + // Session 2: results publication + run_to_block(SESSION_2_START); + assert_ok!(Distance::update_evaluation( + RuntimeOrigin::none(), + ComputationResult { + distances: vec![Perbill::from_percent(100)] + } + )); + let authors_tree = pallet::EvaluationPool2::<Test>::get().1; + assert_eq!(authors_tree.len(), 1); + assert_eq!(authors_tree.get(AUTHOR0), None); + assert_eq!(authors_tree.get(AUTHOR1), Some(AUTHOR1)); + assert_eq!(authors_tree.get(AUTHOR2), None); + + // Other pools have never been used + assert_eq!(pallet::EvaluationPool0::<Test>::get().0.len(), 0); + assert_eq!(pallet::EvaluationPool1::<Test>::get().0.len(), 0); + }); +} + +#[test] +fn multiple_authors() { + new_test_ext().execute_with(|| { + run_to_block(SESSION_0_START); + assert_ok!(Distance::evaluate_distance(RuntimeOrigin::signed(0))); + + // Evaluation #1 + run_to_block(SESSION_2_START); assert_ok!(Distance::update_evaluation( RuntimeOrigin::none(), ComputationResult { distances: vec![Perbill::from_percent(100)] } )); + let authors_tree = pallet::EvaluationPool2::<Test>::get().1; + assert_eq!(authors_tree.len(), 1); + assert_eq!(authors_tree.get(AUTHOR0), None); + assert_eq!(authors_tree.get(AUTHOR1), Some(AUTHOR1)); + assert_eq!(authors_tree.get(AUTHOR2), None); + + // Evaluation #2 + run_to_block(SESSION_2_START + 1); // to change author + assert_ok!(Distance::update_evaluation( + RuntimeOrigin::none(), + ComputationResult { + distances: vec![Perbill::from_percent(60)] + } + )); + let authors_tree = pallet::EvaluationPool2::<Test>::get().1; + assert_eq!(authors_tree.len(), 2); + assert_eq!(authors_tree.get(AUTHOR0), None); + assert_eq!(authors_tree.get(AUTHOR1), Some(AUTHOR1)); + assert_eq!(authors_tree.get(AUTHOR2), Some(AUTHOR2)); + + // Evaluation #3 + run_to_block(SESSION_2_START + 2); // to change author + assert_ok!(Distance::update_evaluation( + RuntimeOrigin::none(), + ComputationResult { + distances: vec![Perbill::from_percent(20)] + } + )); + let authors_tree = pallet::EvaluationPool2::<Test>::get().1; + assert_eq!(authors_tree.len(), 3); + assert_eq!(authors_tree.get(AUTHOR0), Some(AUTHOR0)); + assert_eq!(authors_tree.get(AUTHOR1), Some(AUTHOR1)); + assert_eq!(authors_tree.get(AUTHOR2), Some(AUTHOR2)); + + // Other pools have never been used (because of session number) + assert_eq!(pallet::EvaluationPool0::<Test>::get().0.len(), 0); + assert_eq!(pallet::EvaluationPool1::<Test>::get().0.len(), 0); + + // Evaluation #2 replay i disallowed + assert_err!( + Distance::update_evaluation( + RuntimeOrigin::none(), + ComputationResult { + distances: vec![Perbill::from_percent(100)] + } + ), + pallet::Error::<Test>::ManyEvaluationsByAuthor + ); }); } + +// TODO: +// #[test] +// fn evaluation_for_non_asked() { +// }