mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-06-17 04:21:01 +00:00
adb9f6d71a
* make BagsList::put_in_fron_of be permissionless * Update frame/bags-list/src/lib.rs Co-authored-by: Liam Aharon <liam.aharon@hotmail.com> * improve docs as well * update lock * Update frame/bags-list/Cargo.toml Co-authored-by: Sam Johnson <sam@durosoft.com> * fix * Update frame/bags-list/src/lib.rs Co-authored-by: Michael Assaf <94772640+snowmead@users.noreply.github.com> --------- Co-authored-by: Liam Aharon <liam.aharon@hotmail.com> Co-authored-by: Sam Johnson <sam@durosoft.com> Co-authored-by: Michael Assaf <94772640+snowmead@users.noreply.github.com>
739 lines
22 KiB
Rust
739 lines
22 KiB
Rust
// This file is part of Substrate.
|
|
|
|
// Copyright (C) Parity Technologies (UK) Ltd.
|
|
// SPDX-License-Identifier: Apache-2.0
|
|
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
|
|
use frame_support::{assert_noop, assert_ok, assert_storage_noop, traits::IntegrityTest};
|
|
|
|
use super::*;
|
|
use frame_election_provider_support::{SortedListProvider, VoteWeight};
|
|
use list::Bag;
|
|
use mock::{test_utils::*, *};
|
|
|
|
#[docify::export]
|
|
#[test]
|
|
fn examples_work() {
|
|
ExtBuilder::default()
|
|
.skip_genesis_ids()
|
|
// initially set the score of 11 for 22 to push it next to 12
|
|
.add_ids(vec![(25, 25), (21, 21), (12, 12), (22, 11), (5, 5), (7, 7), (3, 3)])
|
|
.build_and_execute(|| {
|
|
// initial bags
|
|
assert_eq!(
|
|
List::<Runtime>::get_bags(),
|
|
vec![
|
|
// bag 0 -> 10
|
|
(10, vec![5, 7, 3]),
|
|
// bag 10 -> 20
|
|
(20, vec![12, 22]),
|
|
// bag 20 -> 30
|
|
(30, vec![25, 21])
|
|
]
|
|
);
|
|
|
|
// set score of 22 to 22
|
|
StakingMock::set_score_of(&22, 22);
|
|
|
|
// now we rebag 22 to the first bag
|
|
assert_ok!(BagsList::rebag(RuntimeOrigin::signed(42), 22));
|
|
|
|
assert_eq!(
|
|
List::<Runtime>::get_bags(),
|
|
vec![
|
|
// bag 0 -> 10
|
|
(10, vec![5, 7, 3]),
|
|
// bag 10 -> 20
|
|
(20, vec![12]),
|
|
// bag 20 -> 30
|
|
(30, vec![25, 21, 22])
|
|
]
|
|
);
|
|
|
|
// now we put 7 at the front of bag 0
|
|
assert_ok!(BagsList::put_in_front_of(RuntimeOrigin::signed(7), 5));
|
|
|
|
assert_eq!(
|
|
List::<Runtime>::get_bags(),
|
|
vec![
|
|
// bag 0 -> 10
|
|
(10, vec![7, 5, 3]),
|
|
// bag 10 -> 20
|
|
(20, vec![12]),
|
|
// bag 20 -> 30
|
|
(30, vec![25, 21, 22])
|
|
]
|
|
);
|
|
})
|
|
}
|
|
|
|
mod pallet {
|
|
use super::*;
|
|
|
|
#[test]
|
|
fn rebag_works() {
|
|
ExtBuilder::default().add_ids(vec![(42, 20)]).build_and_execute(|| {
|
|
// given
|
|
assert_eq!(
|
|
List::<Runtime>::get_bags(),
|
|
vec![(10, vec![1]), (20, vec![42]), (1_000, vec![2, 3, 4])]
|
|
);
|
|
|
|
// when increasing score to the level of non-existent bag
|
|
assert_eq!(List::<Runtime>::get_score(&42).unwrap(), 20);
|
|
StakingMock::set_score_of(&42, 2_000);
|
|
assert_ok!(BagsList::rebag(RuntimeOrigin::signed(0), 42));
|
|
assert_eq!(List::<Runtime>::get_score(&42).unwrap(), 2_000);
|
|
|
|
// then a new bag is created and the id moves into it
|
|
assert_eq!(
|
|
List::<Runtime>::get_bags(),
|
|
vec![(10, vec![1]), (1_000, vec![2, 3, 4]), (2_000, vec![42])]
|
|
);
|
|
|
|
// when decreasing score within the range of the current bag
|
|
StakingMock::set_score_of(&42, 1_001);
|
|
assert_ok!(BagsList::rebag(RuntimeOrigin::signed(0), 42));
|
|
|
|
// then the id does not move
|
|
assert_eq!(
|
|
List::<Runtime>::get_bags(),
|
|
vec![(10, vec![1]), (1_000, vec![2, 3, 4]), (2_000, vec![42])]
|
|
);
|
|
// but the score is updated
|
|
assert_eq!(List::<Runtime>::get_score(&42).unwrap(), 1_001);
|
|
|
|
// when reducing score to the level of a non-existent bag
|
|
StakingMock::set_score_of(&42, 30);
|
|
assert_ok!(BagsList::rebag(RuntimeOrigin::signed(0), 42));
|
|
|
|
// then a new bag is created and the id moves into it
|
|
assert_eq!(
|
|
List::<Runtime>::get_bags(),
|
|
vec![(10, vec![1]), (30, vec![42]), (1_000, vec![2, 3, 4])]
|
|
);
|
|
assert_eq!(List::<Runtime>::get_score(&42).unwrap(), 30);
|
|
|
|
// when increasing score to the level of a pre-existing bag
|
|
StakingMock::set_score_of(&42, 500);
|
|
assert_ok!(BagsList::rebag(RuntimeOrigin::signed(0), 42));
|
|
|
|
// then the id moves into that bag
|
|
assert_eq!(
|
|
List::<Runtime>::get_bags(),
|
|
vec![(10, vec![1]), (1_000, vec![2, 3, 4, 42])]
|
|
);
|
|
assert_eq!(List::<Runtime>::get_score(&42).unwrap(), 500);
|
|
});
|
|
}
|
|
|
|
// Rebagging the tail of a bag results in the old bag having a new tail and an overall correct
|
|
// state.
|
|
#[test]
|
|
fn rebag_tail_works() {
|
|
ExtBuilder::default().build_and_execute(|| {
|
|
// given
|
|
assert_eq!(List::<Runtime>::get_bags(), vec![(10, vec![1]), (1_000, vec![2, 3, 4])]);
|
|
|
|
// when
|
|
StakingMock::set_score_of(&4, 10);
|
|
assert_ok!(BagsList::rebag(RuntimeOrigin::signed(0), 4));
|
|
|
|
// then
|
|
assert_eq!(List::<Runtime>::get_bags(), vec![(10, vec![1, 4]), (1_000, vec![2, 3])]);
|
|
assert_eq!(Bag::<Runtime>::get(1_000).unwrap(), Bag::new(Some(2), Some(3), 1_000));
|
|
|
|
// when
|
|
StakingMock::set_score_of(&3, 10);
|
|
assert_ok!(BagsList::rebag(RuntimeOrigin::signed(0), 3));
|
|
|
|
// then
|
|
assert_eq!(List::<Runtime>::get_bags(), vec![(10, vec![1, 4, 3]), (1_000, vec![2])]);
|
|
|
|
assert_eq!(Bag::<Runtime>::get(10).unwrap(), Bag::new(Some(1), Some(3), 10));
|
|
assert_eq!(Bag::<Runtime>::get(1_000).unwrap(), Bag::new(Some(2), Some(2), 1_000));
|
|
assert_eq!(get_list_as_ids(), vec![2u32, 1, 4, 3]);
|
|
|
|
// when
|
|
StakingMock::set_score_of(&2, 10);
|
|
assert_ok!(BagsList::rebag(RuntimeOrigin::signed(0), 2));
|
|
|
|
// then
|
|
assert_eq!(List::<Runtime>::get_bags(), vec![(10, vec![1, 4, 3, 2])]);
|
|
assert_eq!(Bag::<Runtime>::get(1_000), None);
|
|
});
|
|
}
|
|
|
|
// Rebagging the head of a bag results in the old bag having a new head and an overall correct
|
|
// state.
|
|
#[test]
|
|
fn rebag_head_works() {
|
|
ExtBuilder::default().build_and_execute(|| {
|
|
// when
|
|
StakingMock::set_score_of(&2, 10);
|
|
assert_ok!(BagsList::rebag(RuntimeOrigin::signed(0), 2));
|
|
|
|
// then
|
|
assert_eq!(List::<Runtime>::get_bags(), vec![(10, vec![1, 2]), (1_000, vec![3, 4])]);
|
|
assert_eq!(Bag::<Runtime>::get(1_000).unwrap(), Bag::new(Some(3), Some(4), 1_000));
|
|
|
|
// when
|
|
StakingMock::set_score_of(&3, 10);
|
|
assert_ok!(BagsList::rebag(RuntimeOrigin::signed(0), 3));
|
|
|
|
// then
|
|
assert_eq!(List::<Runtime>::get_bags(), vec![(10, vec![1, 2, 3]), (1_000, vec![4])]);
|
|
assert_eq!(Bag::<Runtime>::get(1_000).unwrap(), Bag::new(Some(4), Some(4), 1_000));
|
|
|
|
// when
|
|
StakingMock::set_score_of(&4, 10);
|
|
assert_ok!(BagsList::rebag(RuntimeOrigin::signed(0), 4));
|
|
|
|
// then
|
|
assert_eq!(List::<Runtime>::get_bags(), vec![(10, vec![1, 2, 3, 4])]);
|
|
assert_eq!(Bag::<Runtime>::get(1_000), None);
|
|
});
|
|
}
|
|
|
|
#[test]
|
|
fn wrong_rebag_errs() {
|
|
ExtBuilder::default().build_and_execute(|| {
|
|
let node_3 = list::Node::<Runtime>::get(&3).unwrap();
|
|
// when account 3 is _not_ misplaced with score 500
|
|
NextVoteWeight::set(500);
|
|
assert!(!node_3.is_misplaced(500));
|
|
|
|
// then calling rebag on account 3 with score 500 is a noop
|
|
assert_storage_noop!(assert_eq!(BagsList::rebag(RuntimeOrigin::signed(0), 3), Ok(())));
|
|
|
|
// when account 42 is not in the list
|
|
assert!(!BagsList::contains(&42));
|
|
// then rebag-ing account 42 is an error
|
|
assert_storage_noop!(assert!(matches!(
|
|
BagsList::rebag(RuntimeOrigin::signed(0), 42),
|
|
Err(_)
|
|
)));
|
|
});
|
|
}
|
|
|
|
#[test]
|
|
#[should_panic = "thresholds must strictly increase, and have no duplicates"]
|
|
fn duplicate_in_bags_threshold_panics() {
|
|
const DUPE_THRESH: &[VoteWeight; 4] = &[10, 20, 30, 30];
|
|
BagThresholds::set(DUPE_THRESH);
|
|
BagsList::integrity_test();
|
|
}
|
|
|
|
#[test]
|
|
#[should_panic = "thresholds must strictly increase, and have no duplicates"]
|
|
fn decreasing_in_bags_threshold_panics() {
|
|
const DECREASING_THRESH: &[VoteWeight; 4] = &[10, 30, 20, 40];
|
|
BagThresholds::set(DECREASING_THRESH);
|
|
BagsList::integrity_test();
|
|
}
|
|
|
|
#[test]
|
|
fn empty_threshold_works() {
|
|
BagThresholds::set(Default::default()); // which is the same as passing `()` to `Get<_>`.
|
|
ExtBuilder::default().build_and_execute(|| {
|
|
// everyone in the same bag.
|
|
assert_eq!(List::<Runtime>::get_bags(), vec![(VoteWeight::MAX, vec![1, 2, 3, 4])]);
|
|
|
|
// any insertion goes there as well.
|
|
assert_ok!(List::<Runtime>::insert(5, 999));
|
|
assert_ok!(List::<Runtime>::insert(6, 0));
|
|
assert_eq!(
|
|
List::<Runtime>::get_bags(),
|
|
vec![(VoteWeight::MAX, vec![1, 2, 3, 4, 5, 6])]
|
|
);
|
|
|
|
// any rebag is noop.
|
|
assert_storage_noop!(assert_eq!(BagsList::rebag(RuntimeOrigin::signed(0), 1), Ok(())));
|
|
})
|
|
}
|
|
|
|
#[test]
|
|
fn put_in_front_of_other_can_be_permissionless() {
|
|
ExtBuilder::default()
|
|
.skip_genesis_ids()
|
|
.add_ids(vec![(10, 15), (11, 16), (12, 19)])
|
|
.build_and_execute(|| {
|
|
// given
|
|
assert_eq!(List::<Runtime>::get_bags(), vec![(20, vec![10, 11, 12])]);
|
|
// 11 now has more weight than 10 and can be moved before it.
|
|
StakingMock::set_score_of(&11u32, 17);
|
|
|
|
// when
|
|
assert_ok!(BagsList::put_in_front_of_other(RuntimeOrigin::signed(42), 11u32, 10));
|
|
|
|
// then
|
|
assert_eq!(List::<Runtime>::get_bags(), vec![(20, vec![11, 10, 12])]);
|
|
});
|
|
}
|
|
|
|
#[test]
|
|
fn put_in_front_of_two_node_bag_heavier_is_tail() {
|
|
ExtBuilder::default()
|
|
.skip_genesis_ids()
|
|
.add_ids(vec![(10, 15), (11, 16)])
|
|
.build_and_execute(|| {
|
|
// given
|
|
assert_eq!(List::<Runtime>::get_bags(), vec![(20, vec![10, 11])]);
|
|
|
|
// when
|
|
assert_ok!(BagsList::put_in_front_of(RuntimeOrigin::signed(11), 10));
|
|
|
|
// then
|
|
assert_eq!(List::<Runtime>::get_bags(), vec![(20, vec![11, 10])]);
|
|
});
|
|
}
|
|
|
|
#[test]
|
|
fn put_in_front_of_two_node_bag_heavier_is_head() {
|
|
ExtBuilder::default()
|
|
.skip_genesis_ids()
|
|
.add_ids(vec![(11, 16), (10, 15)])
|
|
.build_and_execute(|| {
|
|
// given
|
|
assert_eq!(List::<Runtime>::get_bags(), vec![(20, vec![11, 10])]);
|
|
|
|
// when
|
|
assert_ok!(BagsList::put_in_front_of(RuntimeOrigin::signed(11), 10));
|
|
|
|
// then
|
|
assert_eq!(List::<Runtime>::get_bags(), vec![(20, vec![11, 10])]);
|
|
});
|
|
}
|
|
|
|
#[test]
|
|
fn put_in_front_of_non_terminal_nodes_heavier_behind() {
|
|
ExtBuilder::default().add_ids(vec![(5, 1_000)]).build_and_execute(|| {
|
|
// given
|
|
assert_eq!(List::<Runtime>::get_bags(), vec![(10, vec![1]), (1_000, vec![2, 3, 4, 5])]);
|
|
|
|
StakingMock::set_score_of(&3, 999);
|
|
|
|
// when
|
|
assert_ok!(BagsList::put_in_front_of(RuntimeOrigin::signed(4), 3));
|
|
|
|
// then
|
|
assert_eq!(List::<Runtime>::get_bags(), vec![(10, vec![1]), (1_000, vec![2, 4, 3, 5])]);
|
|
});
|
|
}
|
|
|
|
#[test]
|
|
fn put_in_front_of_non_terminal_nodes_heavier_in_front() {
|
|
ExtBuilder::default()
|
|
.add_ids(vec![(5, 1_000), (6, 1_000)])
|
|
.build_and_execute(|| {
|
|
// given
|
|
assert_eq!(
|
|
List::<Runtime>::get_bags(),
|
|
vec![(10, vec![1]), (1_000, vec![2, 3, 4, 5, 6])]
|
|
);
|
|
|
|
StakingMock::set_score_of(&5, 999);
|
|
|
|
// when
|
|
assert_ok!(BagsList::put_in_front_of(RuntimeOrigin::signed(3), 5));
|
|
|
|
// then
|
|
assert_eq!(
|
|
List::<Runtime>::get_bags(),
|
|
vec![(10, vec![1]), (1_000, vec![2, 4, 3, 5, 6])]
|
|
);
|
|
});
|
|
}
|
|
|
|
#[test]
|
|
fn put_in_front_of_lighter_is_head_heavier_is_non_terminal() {
|
|
ExtBuilder::default().build_and_execute(|| {
|
|
// given
|
|
assert_eq!(List::<Runtime>::get_bags(), vec![(10, vec![1]), (1_000, vec![2, 3, 4])]);
|
|
|
|
StakingMock::set_score_of(&2, 999);
|
|
|
|
// when
|
|
assert_ok!(BagsList::put_in_front_of(RuntimeOrigin::signed(3), 2));
|
|
|
|
// then
|
|
assert_eq!(List::<Runtime>::get_bags(), vec![(10, vec![1]), (1_000, vec![3, 2, 4])]);
|
|
});
|
|
}
|
|
|
|
#[test]
|
|
fn put_in_front_of_heavier_is_tail_lighter_is_non_terminal() {
|
|
ExtBuilder::default().build_and_execute(|| {
|
|
// given
|
|
assert_eq!(List::<Runtime>::get_bags(), vec![(10, vec![1]), (1_000, vec![2, 3, 4])]);
|
|
|
|
StakingMock::set_score_of(&3, 999);
|
|
|
|
// when
|
|
assert_ok!(BagsList::put_in_front_of(RuntimeOrigin::signed(4), 3));
|
|
|
|
// then
|
|
assert_eq!(List::<Runtime>::get_bags(), vec![(10, vec![1]), (1_000, vec![2, 4, 3])]);
|
|
});
|
|
}
|
|
|
|
#[test]
|
|
fn put_in_front_of_heavier_is_tail_lighter_is_head() {
|
|
ExtBuilder::default().add_ids(vec![(5, 1_000)]).build_and_execute(|| {
|
|
// given
|
|
assert_eq!(List::<Runtime>::get_bags(), vec![(10, vec![1]), (1_000, vec![2, 3, 4, 5])]);
|
|
|
|
StakingMock::set_score_of(&2, 999);
|
|
|
|
// when
|
|
assert_ok!(BagsList::put_in_front_of(RuntimeOrigin::signed(5), 2));
|
|
|
|
// then
|
|
assert_eq!(List::<Runtime>::get_bags(), vec![(10, vec![1]), (1_000, vec![5, 2, 3, 4])]);
|
|
});
|
|
}
|
|
|
|
#[test]
|
|
fn put_in_front_of_heavier_is_head_lighter_is_not_terminal() {
|
|
ExtBuilder::default().add_ids(vec![(5, 1_000)]).build_and_execute(|| {
|
|
// given
|
|
assert_eq!(List::<Runtime>::get_bags(), vec![(10, vec![1]), (1_000, vec![2, 3, 4, 5])]);
|
|
|
|
StakingMock::set_score_of(&4, 999);
|
|
|
|
// when
|
|
BagsList::put_in_front_of(RuntimeOrigin::signed(2), 4).unwrap();
|
|
|
|
// then
|
|
assert_eq!(List::<Runtime>::get_bags(), vec![(10, vec![1]), (1_000, vec![3, 2, 4, 5])]);
|
|
});
|
|
}
|
|
|
|
#[test]
|
|
fn put_in_front_of_lighter_is_tail_heavier_is_not_terminal() {
|
|
ExtBuilder::default().add_ids(vec![(5, 900)]).build_and_execute(|| {
|
|
// given
|
|
assert_eq!(List::<Runtime>::get_bags(), vec![(10, vec![1]), (1_000, vec![2, 3, 4, 5])]);
|
|
|
|
// when
|
|
BagsList::put_in_front_of(RuntimeOrigin::signed(3), 5).unwrap();
|
|
|
|
// then
|
|
assert_eq!(List::<Runtime>::get_bags(), vec![(10, vec![1]), (1_000, vec![2, 4, 3, 5])]);
|
|
});
|
|
}
|
|
|
|
#[test]
|
|
fn put_in_front_of_lighter_is_tail_heavier_is_head() {
|
|
ExtBuilder::default().build_and_execute(|| {
|
|
// given
|
|
assert_eq!(List::<Runtime>::get_bags(), vec![(10, vec![1]), (1_000, vec![2, 3, 4])]);
|
|
|
|
StakingMock::set_score_of(&4, 999);
|
|
|
|
// when
|
|
assert_ok!(BagsList::put_in_front_of(RuntimeOrigin::signed(2), 4));
|
|
|
|
// then
|
|
assert_eq!(List::<Runtime>::get_bags(), vec![(10, vec![1]), (1_000, vec![3, 2, 4])]);
|
|
});
|
|
}
|
|
|
|
#[test]
|
|
fn put_in_front_of_errors_if_heavier_is_less_than_lighter() {
|
|
ExtBuilder::default().build_and_execute(|| {
|
|
// given
|
|
assert_eq!(List::<Runtime>::get_bags(), vec![(10, vec![1]), (1_000, vec![2, 3, 4])]);
|
|
|
|
StakingMock::set_score_of(&3, 999);
|
|
|
|
// then
|
|
assert_noop!(
|
|
BagsList::put_in_front_of(RuntimeOrigin::signed(3), 2),
|
|
crate::pallet::Error::<Runtime>::List(ListError::NotHeavier)
|
|
);
|
|
});
|
|
}
|
|
|
|
#[test]
|
|
fn put_in_front_of_errors_if_heavier_is_equal_weight_to_lighter() {
|
|
ExtBuilder::default().build_and_execute(|| {
|
|
// given
|
|
assert_eq!(List::<Runtime>::get_bags(), vec![(10, vec![1]), (1_000, vec![2, 3, 4])]);
|
|
|
|
// then
|
|
assert_noop!(
|
|
BagsList::put_in_front_of(RuntimeOrigin::signed(3), 4),
|
|
crate::pallet::Error::<Runtime>::List(ListError::NotHeavier)
|
|
);
|
|
});
|
|
}
|
|
|
|
#[test]
|
|
fn put_in_front_of_errors_if_nodes_not_found() {
|
|
// `heavier` not found
|
|
ExtBuilder::default().build_and_execute(|| {
|
|
// given
|
|
assert_eq!(List::<Runtime>::get_bags(), vec![(10, vec![1]), (1_000, vec![2, 3, 4])]);
|
|
|
|
assert!(!ListNodes::<Runtime>::contains_key(5));
|
|
|
|
// then
|
|
assert_noop!(
|
|
BagsList::put_in_front_of(RuntimeOrigin::signed(5), 4),
|
|
crate::pallet::Error::<Runtime>::List(ListError::NodeNotFound)
|
|
);
|
|
});
|
|
|
|
// `lighter` not found
|
|
ExtBuilder::default().build_and_execute(|| {
|
|
// given
|
|
assert_eq!(List::<Runtime>::get_bags(), vec![(10, vec![1]), (1_000, vec![2, 3, 4])]);
|
|
|
|
assert!(!ListNodes::<Runtime>::contains_key(5));
|
|
|
|
// then
|
|
assert_noop!(
|
|
BagsList::put_in_front_of(RuntimeOrigin::signed(4), 5),
|
|
crate::pallet::Error::<Runtime>::List(ListError::NodeNotFound)
|
|
);
|
|
});
|
|
}
|
|
|
|
#[test]
|
|
fn put_in_front_of_errors_if_nodes_not_in_same_bag() {
|
|
ExtBuilder::default().build_and_execute(|| {
|
|
// given
|
|
assert_eq!(List::<Runtime>::get_bags(), vec![(10, vec![1]), (1_000, vec![2, 3, 4])]);
|
|
|
|
// then
|
|
assert_noop!(
|
|
BagsList::put_in_front_of(RuntimeOrigin::signed(4), 1),
|
|
crate::pallet::Error::<Runtime>::List(ListError::NotInSameBag)
|
|
);
|
|
});
|
|
}
|
|
}
|
|
|
|
mod sorted_list_provider {
|
|
use super::*;
|
|
|
|
#[test]
|
|
fn iter_works() {
|
|
ExtBuilder::default().build_and_execute(|| {
|
|
let expected = vec![2, 3, 4, 1];
|
|
for (i, id) in BagsList::iter().enumerate() {
|
|
assert_eq!(id, expected[i])
|
|
}
|
|
});
|
|
}
|
|
|
|
#[test]
|
|
fn iter_from_works() {
|
|
ExtBuilder::default().add_ids(vec![(5, 5), (6, 15)]).build_and_execute(|| {
|
|
// given
|
|
assert_eq!(
|
|
List::<Runtime>::get_bags(),
|
|
vec![(10, vec![1, 5]), (20, vec![6]), (1000, vec![2, 3, 4])]
|
|
);
|
|
|
|
assert_eq!(BagsList::iter_from(&2).unwrap().collect::<Vec<_>>(), vec![3, 4, 6, 1, 5]);
|
|
assert_eq!(BagsList::iter_from(&3).unwrap().collect::<Vec<_>>(), vec![4, 6, 1, 5]);
|
|
assert_eq!(BagsList::iter_from(&4).unwrap().collect::<Vec<_>>(), vec![6, 1, 5]);
|
|
assert_eq!(BagsList::iter_from(&6).unwrap().collect::<Vec<_>>(), vec![1, 5]);
|
|
assert_eq!(BagsList::iter_from(&1).unwrap().collect::<Vec<_>>(), vec![5]);
|
|
assert!(BagsList::iter_from(&5).unwrap().collect::<Vec<_>>().is_empty());
|
|
assert!(BagsList::iter_from(&7).is_err());
|
|
|
|
assert_storage_noop!(assert!(BagsList::iter_from(&8).is_err()));
|
|
});
|
|
}
|
|
|
|
#[test]
|
|
fn count_works() {
|
|
ExtBuilder::default().build_and_execute(|| {
|
|
// given
|
|
assert_eq!(BagsList::count(), 4);
|
|
|
|
// when inserting
|
|
assert_ok!(BagsList::on_insert(201, 0));
|
|
// then the count goes up
|
|
assert_eq!(BagsList::count(), 5);
|
|
|
|
// when removing
|
|
BagsList::on_remove(&201).unwrap();
|
|
// then the count goes down
|
|
assert_eq!(BagsList::count(), 4);
|
|
|
|
// when updating
|
|
assert_noop!(BagsList::on_update(&201, VoteWeight::MAX), ListError::NodeNotFound);
|
|
// then the count stays the same
|
|
assert_eq!(BagsList::count(), 4);
|
|
});
|
|
}
|
|
|
|
#[test]
|
|
fn on_insert_works() {
|
|
ExtBuilder::default().build_and_execute(|| {
|
|
// when
|
|
assert_ok!(BagsList::on_insert(6, 1_000));
|
|
|
|
// then the bags
|
|
assert_eq!(List::<Runtime>::get_bags(), vec![(10, vec![1]), (1_000, vec![2, 3, 4, 6])]);
|
|
// and list correctly include the new id,
|
|
assert_eq!(BagsList::iter().collect::<Vec<_>>(), vec![2, 3, 4, 6, 1]);
|
|
// and the count is incremented.
|
|
assert_eq!(BagsList::count(), 5);
|
|
|
|
// when
|
|
assert_ok!(BagsList::on_insert(7, 1_001));
|
|
|
|
// then the bags
|
|
assert_eq!(
|
|
List::<Runtime>::get_bags(),
|
|
vec![(10, vec![1]), (1_000, vec![2, 3, 4, 6]), (2_000, vec![7])]
|
|
);
|
|
// and list correctly include the new id,
|
|
assert_eq!(BagsList::iter().collect::<Vec<_>>(), vec![7, 2, 3, 4, 6, 1]);
|
|
// and the count is incremented.
|
|
assert_eq!(BagsList::count(), 6);
|
|
})
|
|
}
|
|
|
|
#[test]
|
|
fn on_insert_errors_with_duplicate_id() {
|
|
ExtBuilder::default().build_and_execute(|| {
|
|
// given
|
|
assert!(get_list_as_ids().contains(&3));
|
|
|
|
// then
|
|
assert_storage_noop!(assert_eq!(
|
|
BagsList::on_insert(3, 20).unwrap_err(),
|
|
ListError::Duplicate
|
|
));
|
|
});
|
|
}
|
|
|
|
#[test]
|
|
fn on_update_works() {
|
|
ExtBuilder::default().add_ids(vec![(42, 20)]).build_and_execute(|| {
|
|
// given
|
|
assert_eq!(
|
|
List::<Runtime>::get_bags(),
|
|
vec![(10, vec![1]), (20, vec![42]), (1_000, vec![2, 3, 4])]
|
|
);
|
|
assert_eq!(BagsList::count(), 5);
|
|
|
|
// when increasing score to the level of non-existent bag
|
|
BagsList::on_update(&42, 2_000).unwrap();
|
|
|
|
// then the bag is created with the id in it,
|
|
assert_eq!(
|
|
List::<Runtime>::get_bags(),
|
|
vec![(10, vec![1]), (1_000, vec![2, 3, 4]), (2000, vec![42])]
|
|
);
|
|
// and the id position is updated in the list.
|
|
assert_eq!(BagsList::iter().collect::<Vec<_>>(), vec![42, 2, 3, 4, 1]);
|
|
|
|
// when decreasing score within the range of the current bag
|
|
BagsList::on_update(&42, 1_001).unwrap();
|
|
|
|
// then the id does not change bags,
|
|
assert_eq!(
|
|
List::<Runtime>::get_bags(),
|
|
vec![(10, vec![1]), (1_000, vec![2, 3, 4]), (2000, vec![42])]
|
|
);
|
|
// or change position in the list.
|
|
assert_eq!(BagsList::iter().collect::<Vec<_>>(), vec![42, 2, 3, 4, 1]);
|
|
|
|
// when increasing score to the level of a non-existent bag with the max threshold
|
|
BagsList::on_update(&42, VoteWeight::MAX).unwrap();
|
|
|
|
// the the new bag is created with the id in it,
|
|
assert_eq!(
|
|
List::<Runtime>::get_bags(),
|
|
vec![(10, vec![1]), (1_000, vec![2, 3, 4]), (VoteWeight::MAX, vec![42])]
|
|
);
|
|
// and the id position is updated in the list.
|
|
assert_eq!(BagsList::iter().collect::<Vec<_>>(), vec![42, 2, 3, 4, 1]);
|
|
|
|
// when decreasing the score to a pre-existing bag
|
|
BagsList::on_update(&42, 1_000).unwrap();
|
|
|
|
// then id is moved to the correct bag (as the last member),
|
|
assert_eq!(
|
|
List::<Runtime>::get_bags(),
|
|
vec![(10, vec![1]), (1_000, vec![2, 3, 4, 42])]
|
|
);
|
|
// and the id position is updated in the list.
|
|
assert_eq!(BagsList::iter().collect::<Vec<_>>(), vec![2, 3, 4, 42, 1]);
|
|
|
|
// since we have only called on_update, the `count` has not changed.
|
|
assert_eq!(BagsList::count(), 5);
|
|
});
|
|
}
|
|
|
|
#[test]
|
|
fn on_remove_works() {
|
|
let ensure_left = |id, counter| {
|
|
assert!(!ListNodes::<Runtime>::contains_key(id));
|
|
assert_eq!(BagsList::count(), counter);
|
|
assert_eq!(ListNodes::<Runtime>::count(), counter);
|
|
assert_eq!(ListNodes::<Runtime>::iter().count() as u32, counter);
|
|
};
|
|
|
|
ExtBuilder::default().build_and_execute(|| {
|
|
// it is a noop removing a non-existent id
|
|
assert!(!ListNodes::<Runtime>::contains_key(42));
|
|
assert_noop!(BagsList::on_remove(&42), ListError::NodeNotFound);
|
|
|
|
// when removing a node from a bag with multiple nodes
|
|
BagsList::on_remove(&2).unwrap();
|
|
|
|
// then
|
|
assert_eq!(get_list_as_ids(), vec![3, 4, 1]);
|
|
assert_eq!(List::<Runtime>::get_bags(), vec![(10, vec![1]), (1_000, vec![3, 4])]);
|
|
ensure_left(2, 3);
|
|
|
|
// when removing a node from a bag with only one node
|
|
BagsList::on_remove(&1).unwrap();
|
|
|
|
// then
|
|
assert_eq!(get_list_as_ids(), vec![3, 4]);
|
|
assert_eq!(List::<Runtime>::get_bags(), vec![(1_000, vec![3, 4])]);
|
|
ensure_left(1, 2);
|
|
|
|
// when removing all remaining ids
|
|
BagsList::on_remove(&4).unwrap();
|
|
assert_eq!(get_list_as_ids(), vec![3]);
|
|
ensure_left(4, 1);
|
|
BagsList::on_remove(&3).unwrap();
|
|
|
|
// then the storage is completely cleaned up
|
|
assert_eq!(get_list_as_ids(), Vec::<AccountId>::new());
|
|
ensure_left(3, 0);
|
|
});
|
|
}
|
|
|
|
#[test]
|
|
fn contains_works() {
|
|
ExtBuilder::default().build_and_execute(|| {
|
|
assert!(GENESIS_IDS.iter().all(|(id, _)| BagsList::contains(id)));
|
|
|
|
let non_existent_ids = vec![&42, &666, &13];
|
|
assert!(non_existent_ids.iter().all(|id| !BagsList::contains(id)));
|
|
})
|
|
}
|
|
}
|