Files
pezkuwi-subxt/substrate/srml/scored-pool/src/tests.rs
T
Michael Müller 017752df41 Introduce srml/scored-pool (#3381)
* Introduce srml/scored-pool

* Bump impl_version

* Apply suggestions from code review

Co-Authored-By: Bastian Köcher <bkchr@users.noreply.github.com>

* Remove unnecessary pub use

* Remove unnecessary import

* Adapt to InitializeMembers

* Bump impl_version

* Implement remarks (shortens code)

* Improve complexity of score()

Search and remove and search again for the
new spot and insert then => O(2LogN).

* Get rid of a clone()

* Reduce complexity of issue_candidacy()

* Add CandidateScored event + Improve comments

* Fix naming

* Use Lookup instead of AccountId as param

* Use set_members_sorted instead of computing diff

* Remove function which is only used during genesis

* Get rid of rev() by changing sort order of Pool

* Rename issue_candidacy to submit_candidacy

* Shorten code

* Remove find_in_pool() and have transactor submit index

* Remove unnecessary dependency

* Improve error messages

* Improve naming

* Improve comments

* Make code clearer wrt which receiver to invoke

* Adapt to new system trait

* Refactor to request CandidateDeposit only once

* Refactor to request Pool only once

* Improve structure and comments
2019-08-19 09:26:21 +02:00

284 lines
8.4 KiB
Rust

// Copyright 2019 Parity Technologies (UK) Ltd.
// This file is part of Substrate.
// Substrate is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Substrate is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Substrate. If not, see <http://www.gnu.org/licenses/>.
//! Tests for the module.
use super::*;
use mock::*;
use srml_support::{assert_ok, assert_noop};
use sr_io::with_externalities;
use sr_primitives::traits::OnInitialize;
type ScoredPool = Module<Test>;
type System = system::Module<Test>;
type Balances = balances::Module<Test>;
const OOB_ERR: &str = "index out of bounds";
const INDEX_ERR: &str = "index does not match requested account";
#[test]
fn query_membership_works() {
with_externalities(&mut new_test_ext(), || {
assert_eq!(ScoredPool::members(), vec![20, 40]);
assert_eq!(Balances::reserved_balance(&31), CandidateDeposit::get());
assert_eq!(Balances::reserved_balance(&40), CandidateDeposit::get());
assert_eq!(MEMBERS.with(|m| m.borrow().clone()), vec![20, 40]);
});
}
#[test]
fn submit_candidacy_must_not_work() {
with_externalities(&mut new_test_ext(), || {
assert_noop!(
ScoredPool::submit_candidacy(Origin::signed(99)),
"balance too low to submit candidacy"
);
assert_noop!(
ScoredPool::submit_candidacy(Origin::signed(40)),
"already a member"
);
});
}
#[test]
fn submit_candidacy_works() {
with_externalities(&mut new_test_ext(), || {
// given
let who = 15;
// when
assert_ok!(ScoredPool::submit_candidacy(Origin::signed(who)));
assert_eq!(fetch_from_pool(15), Some((who, None)));
// then
assert_eq!(Balances::reserved_balance(&who), CandidateDeposit::get());
});
}
#[test]
fn scoring_works() {
with_externalities(&mut new_test_ext(), || {
// given
let who = 15;
let score = 99;
assert_ok!(ScoredPool::submit_candidacy(Origin::signed(who)));
// when
let index = find_in_pool(who).expect("entity must be in pool") as u32;
assert_ok!(ScoredPool::score(Origin::signed(ScoreOrigin::get()), who, index, score));
// then
assert_eq!(fetch_from_pool(who), Some((who, Some(score))));
assert_eq!(find_in_pool(who), Some(0)); // must be first element, since highest scored
});
}
#[test]
fn scoring_same_element_with_same_score_works() {
with_externalities(&mut new_test_ext(), || {
// given
let who = 31;
let index = find_in_pool(who).expect("entity must be in pool") as u32;
let score = 2;
// when
assert_ok!(ScoredPool::score(Origin::signed(ScoreOrigin::get()), who, index, score));
// then
assert_eq!(fetch_from_pool(who), Some((who, Some(score))));
// must have been inserted right before the `20` element which is
// of the same score as `31`. so sort order is maintained.
assert_eq!(find_in_pool(who), Some(1));
});
}
#[test]
fn kicking_works_only_for_authorized() {
with_externalities(&mut new_test_ext(), || {
let who = 40;
let index = find_in_pool(who).expect("entity must be in pool") as u32;
assert_noop!(ScoredPool::kick(Origin::signed(99), who, index), "bad origin");
});
}
#[test]
fn kicking_works() {
with_externalities(&mut new_test_ext(), || {
// given
let who = 40;
assert_eq!(Balances::reserved_balance(&who), CandidateDeposit::get());
assert_eq!(find_in_pool(who), Some(0));
// when
let index = find_in_pool(who).expect("entity must be in pool") as u32;
assert_ok!(ScoredPool::kick(Origin::signed(KickOrigin::get()), who, index));
// then
assert_eq!(find_in_pool(who), None);
assert_eq!(ScoredPool::members(), vec![20, 31]);
assert_eq!(MEMBERS.with(|m| m.borrow().clone()), ScoredPool::members());
assert_eq!(Balances::reserved_balance(&who), 0); // deposit must have been returned
});
}
#[test]
fn unscored_entities_must_not_be_used_for_filling_members() {
with_externalities(&mut new_test_ext(), || {
// given
// we submit a candidacy, score will be `None`
assert_ok!(ScoredPool::submit_candidacy(Origin::signed(15)));
// when
// we remove every scored member
ScoredPool::pool()
.into_iter()
.for_each(|(who, score)| {
if let Some(_) = score {
let index = find_in_pool(who).expect("entity must be in pool") as u32;
assert_ok!(ScoredPool::kick(Origin::signed(KickOrigin::get()), who, index));
}
});
// then
// the `None` candidates should not have been filled in
assert_eq!(ScoredPool::members(), vec![]);
assert_eq!(MEMBERS.with(|m| m.borrow().clone()), ScoredPool::members());
});
}
#[test]
fn refreshing_works() {
with_externalities(&mut new_test_ext(), || {
// given
let who = 15;
assert_ok!(ScoredPool::submit_candidacy(Origin::signed(who)));
let index = find_in_pool(who).expect("entity must be in pool") as u32;
assert_ok!(ScoredPool::score(Origin::signed(ScoreOrigin::get()), who, index, 99));
// when
ScoredPool::refresh_members(ScoredPool::pool(), ChangeReceiver::MembershipChanged);
// then
assert_eq!(ScoredPool::members(), vec![15, 40]);
assert_eq!(MEMBERS.with(|m| m.borrow().clone()), ScoredPool::members());
});
}
#[test]
fn refreshing_happens_every_period() {
with_externalities(&mut new_test_ext(), || {
// given
System::set_block_number(1);
assert_ok!(ScoredPool::submit_candidacy(Origin::signed(15)));
let index = find_in_pool(15).expect("entity must be in pool") as u32;
assert_ok!(ScoredPool::score(Origin::signed(ScoreOrigin::get()), 15, index, 99));
assert_eq!(ScoredPool::members(), vec![20, 40]);
// when
System::set_block_number(4);
ScoredPool::on_initialize(4);
// then
assert_eq!(ScoredPool::members(), vec![15, 40]);
assert_eq!(MEMBERS.with(|m| m.borrow().clone()), ScoredPool::members());
});
}
#[test]
fn withdraw_candidacy_must_only_work_for_members() {
with_externalities(&mut new_test_ext(), || {
let who = 77;
let index = 0;
assert_noop!( ScoredPool::withdraw_candidacy(Origin::signed(who), index), INDEX_ERR);
});
}
#[test]
fn oob_index_should_abort() {
with_externalities(&mut new_test_ext(), || {
let who = 40;
let oob_index = ScoredPool::pool().len() as u32;
assert_noop!(ScoredPool::withdraw_candidacy(Origin::signed(who), oob_index), OOB_ERR);
assert_noop!(ScoredPool::score(Origin::signed(ScoreOrigin::get()), who, oob_index, 99), OOB_ERR);
assert_noop!(ScoredPool::kick(Origin::signed(KickOrigin::get()), who, oob_index), OOB_ERR);
});
}
#[test]
fn index_mismatches_should_abort() {
with_externalities(&mut new_test_ext(), || {
let who = 40;
let index = 3;
assert_noop!(ScoredPool::withdraw_candidacy(Origin::signed(who), index), INDEX_ERR);
assert_noop!(ScoredPool::score(Origin::signed(ScoreOrigin::get()), who, index, 99), INDEX_ERR);
assert_noop!(ScoredPool::kick(Origin::signed(KickOrigin::get()), who, index), INDEX_ERR);
});
}
#[test]
fn withdraw_unscored_candidacy_must_work() {
with_externalities(&mut new_test_ext(), || {
// given
let who = 5;
// when
let index = find_in_pool(who).expect("entity must be in pool") as u32;
assert_ok!(ScoredPool::withdraw_candidacy(Origin::signed(who), index));
// then
assert_eq!(fetch_from_pool(5), None);
});
}
#[test]
fn withdraw_scored_candidacy_must_work() {
with_externalities(&mut new_test_ext(), || {
// given
let who = 40;
assert_eq!(Balances::reserved_balance(&who), CandidateDeposit::get());
// when
let index = find_in_pool(who).expect("entity must be in pool") as u32;
assert_ok!(ScoredPool::withdraw_candidacy(Origin::signed(who), index));
// then
assert_eq!(fetch_from_pool(who), None);
assert_eq!(ScoredPool::members(), vec![20, 31]);
assert_eq!(Balances::reserved_balance(&who), 0);
});
}
#[test]
fn candidacy_resubmitting_works() {
with_externalities(&mut new_test_ext(), || {
// given
let who = 15;
// when
assert_ok!(ScoredPool::submit_candidacy(Origin::signed(who)));
assert_eq!(ScoredPool::candidate_exists(who), true);
let index = find_in_pool(who).expect("entity must be in pool") as u32;
assert_ok!(ScoredPool::withdraw_candidacy(Origin::signed(who), index));
assert_eq!(ScoredPool::candidate_exists(who), false);
assert_ok!(ScoredPool::submit_candidacy(Origin::signed(who)));
// then
assert_eq!(ScoredPool::candidate_exists(who), true);
});
}