// This file is part of Substrate.

// Copyright (C) 2019-2020 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.

//! Tests for election module.

#![cfg(test)]

use crate::mock::*;
use crate::*;

use frame_support::{assert_ok, assert_err, assert_noop};

#[test]
fn params_should_work() {
	ExtBuilder::default().build().execute_with(|| {
		assert_eq!(Elections::next_vote_from(1), 4);
		assert_eq!(Elections::next_vote_from(4), 4);
		assert_eq!(Elections::next_vote_from(5), 8);
		assert_eq!(Elections::vote_index(), 0);
		assert_eq!(Elections::presentation_duration(), 2);
		assert_eq!(Elections::term_duration(), 5);
		assert_eq!(Elections::desired_seats(), 2);

		assert_eq!(Elections::members(), vec![]);
		assert_eq!(Elections::next_tally(), Some(4));
		assert_eq!(Elections::presentation_active(), false);
		assert_eq!(Elections::next_finalize(), None);

		assert_eq!(Elections::candidates(), Vec::<u64>::new());
		assert_eq!(Elections::is_a_candidate(&1), false);
		assert_eq!(Elections::candidate_reg_info(1), None);

		assert_eq!(Elections::voters(0), Vec::<Option<u64>>::new());
		assert_eq!(Elections::voter_info(1), None);
		assert!(Elections::all_approvals_of(&1).is_empty());
	});
}

#[test]
fn chunking_bool_to_flag_should_work() {
	ExtBuilder::default().build().execute_with(|| {
		assert!(Elections::bool_to_flag(vec![]).is_empty());
		assert_eq!(Elections::bool_to_flag(vec![false]), vec![0]);
		assert_eq!(Elections::bool_to_flag(vec![true]), vec![1]);
		assert_eq!(Elections::bool_to_flag(vec![true, true, true, true]), vec![15]);
		assert_eq!(Elections::bool_to_flag(vec![true, true, true, true, true]), vec![15 + 16]);

		let set_1 = vec![
				true, false, false, false, // 0x1
				false, true, true, true, // 0xE
		];
		assert_eq!(
			Elections::bool_to_flag(set_1.clone()),
			vec![0x00_00_00_E1_u32]
		);
		assert_eq!(
			Elections::flag_to_bool(vec![0x00_00_00_E1_u32]),
			set_1
		);

		let set_2 = vec![
				false, false, false, true, // 0x8
				false, true, false, true, // 0xA
		];
		assert_eq!(
			Elections::bool_to_flag(set_2.clone()),
			vec![0x00_00_00_A8_u32]
		);
		assert_eq!(
			Elections::flag_to_bool(vec![0x00_00_00_A8_u32]),
			set_2
		);

		let mut rhs = (0..100/APPROVAL_FLAG_LEN).map(|_| 0xFFFFFFFF_u32).collect::<Vec<u32>>();
		// NOTE: this might be need change based on `APPROVAL_FLAG_LEN`.
		rhs.extend(vec![0x00_00_00_0F]);
		assert_eq!(
			Elections::bool_to_flag((0..100).map(|_| true).collect()),
			rhs
		)
	})
}

#[test]
fn chunking_voter_set_growth_should_work() {
	ExtBuilder::default().build().execute_with(|| {
		assert_ok!(Elections::submit_candidacy(Origin::signed(2), 0));

		// create 65. 64 (set0) + 1 (set1)
		(1..=63).for_each(|i| vote(i, 0));
		assert_eq!(Elections::next_nonfull_voter_set(), 0);
		vote(64, 0);
		assert_eq!(Elections::next_nonfull_voter_set(), 1);
		vote(65, 0);

		let set1 = Elections::voters(0);
		let set2 = Elections::voters(1);

		assert_eq!(set1.len(), 64);
		assert_eq!(set2.len(), 1);

		assert_eq!(set1[0], Some(1));
		assert_eq!(set1[10], Some(11));
		assert_eq!(set2[0], Some(65));
	})
}

#[test]
fn chunking_voter_set_reclaim_should_work() {
	ExtBuilder::default().build().execute_with(|| {
		assert_ok!(Elections::submit_candidacy(Origin::signed(2), 0));

		(1..=129).for_each(|i| vote(i, 0));
		assert_eq!(Elections::next_nonfull_voter_set(), 2);

		assert_ok!(Elections::retract_voter(Origin::signed(11), 10));

		assert_ok!(Elections::retract_voter(Origin::signed(66), 65));
		assert_ok!(Elections::retract_voter(Origin::signed(67), 66));

		// length does not show it but holes do exist.
		assert_eq!(Elections::voters(0).len(), 64);
		assert_eq!(Elections::voters(1).len(), 64);
		assert_eq!(Elections::voters(2).len(), 1);

		assert_eq!(Elections::voters(0)[10], None);
		assert_eq!(Elections::voters(1)[1], None);
		assert_eq!(Elections::voters(1)[2], None);
		// Next set with capacity is 2.
		assert_eq!(Elections::next_nonfull_voter_set(), 2);

		// But we can fill a hole.
		vote_at(130, 0, 10);

		// Nothing added to set 2. A hole was filled.
		assert_eq!(Elections::voters(0).len(), 64);
		assert_eq!(Elections::voters(1).len(), 64);
		assert_eq!(Elections::voters(2).len(), 1);

		// and the next two (scheduled) to the second set.
		assert_eq!(Elections::next_nonfull_voter_set(), 2);
	})
}

#[test]
fn chunking_approvals_set_growth_should_work() {
	ExtBuilder::default().build().execute_with(|| {
		// create candidates and voters.
		(1..=250).for_each(|i| create_candidate(i, (i-1) as u32));
		(1..=250).for_each(|i| vote(i, i as usize));

		// all approvals of should return the exact expected vector.
		assert_eq!(
			Elections::all_approvals_of(&180),
			(0..180).map(|_| true).collect::<Vec<bool>>()
		);
		assert_eq!(
			Elections::all_approvals_of(&32),
			(0..32).map(|_| true).collect::<Vec<bool>>()
		);
		assert_eq!(
			Elections::all_approvals_of(&8),
			(0..8).map(|_| true).collect::<Vec<bool>>()
		);
		assert_eq!(
			Elections::all_approvals_of(&64),
			(0..64).map(|_| true).collect::<Vec<bool>>()
		);
		assert_eq!(
			Elections::all_approvals_of(&65),
			(0..65).map(|_| true).collect::<Vec<bool>>()
		);
		assert_eq!(
			Elections::all_approvals_of(&63),
			(0..63).map(|_| true).collect::<Vec<bool>>()
		);

		// NOTE: assuming that APPROVAL_SET_SIZE is more or less small-ish. Might fail otherwise.
		let full_sets = (180 / APPROVAL_FLAG_LEN) / APPROVAL_SET_SIZE;
		let left_over = (180 / APPROVAL_FLAG_LEN) / APPROVAL_SET_SIZE;
		let rem = 180 % APPROVAL_FLAG_LEN;

		// grab and check the last full set, if it exists.
		if full_sets > 0 {
			assert_eq!(
				Elections::approvals_of((180, (full_sets-1) as SetIndex )),
				Elections::bool_to_flag(
					(0..APPROVAL_SET_SIZE * APPROVAL_FLAG_LEN)
					.map(|_| true).collect::<Vec<bool>>()
				)
			);
		}

		// grab and check the last, half-empty, set.
		if left_over > 0 {
			assert_eq!(
				Elections::approvals_of((180, full_sets as SetIndex)),
				Elections::bool_to_flag(
					(0..left_over * APPROVAL_FLAG_LEN + rem)
					.map(|_| true).collect::<Vec<bool>>()
				)
			);
		}
	})
}

#[test]
fn chunking_cell_status_works() {
	ExtBuilder::default().build().execute_with(|| {
		assert_ok!(Elections::submit_candidacy(Origin::signed(2), 0));

		(1..=63).for_each(|i| vote(i, 0));

		assert_ok!(Elections::retract_voter(Origin::signed(11), 10));
		assert_ok!(Elections::retract_voter(Origin::signed(21), 20));

		assert_eq!(Elections::cell_status(0, 10), CellStatus::Hole);
		assert_eq!(Elections::cell_status(0, 0), CellStatus::Occupied);
		assert_eq!(Elections::cell_status(0, 20), CellStatus::Hole);
		assert_eq!(Elections::cell_status(0, 63), CellStatus::Head);
		assert_eq!(Elections::cell_status(1, 0), CellStatus::Head);
		assert_eq!(Elections::cell_status(1, 10), CellStatus::Head);
	})
}

#[test]
fn chunking_voter_index_does_not_take_holes_into_account() {
	ExtBuilder::default().build().execute_with(|| {
		assert_ok!(Elections::submit_candidacy(Origin::signed(2), 0));

		// create 65. 64 (set0) + 1 (set1)
		(1..=65).for_each(|i| vote(i, 0));

		// account 65 has global index 65.
		assert_eq!(Elections::voter_at(64).unwrap(), 65);

		assert_ok!(Elections::retract_voter(Origin::signed(1), 0));
		assert_ok!(Elections::retract_voter(Origin::signed(2), 1));

		// still the same. These holes are in some other set.
		assert_eq!(Elections::voter_at(64).unwrap(), 65);
		// proof: can submit a new approval with the old index.
		assert_noop!(
			Elections::set_approvals(Origin::signed(65), vec![], 0, 64 - 2, 10),
			Error::<Test>::InvalidVoterIndex,
		);
		assert_ok!(Elections::set_approvals(Origin::signed(65), vec![], 0, 64, 10));
	})
}

#[test]
fn chunking_approval_storage_should_work() {
	ExtBuilder::default().build().execute_with(|| {
		assert_ok!(Elections::submit_candidacy(Origin::signed(2), 0));
		assert_ok!(Elections::submit_candidacy(Origin::signed(3), 1));

		assert_ok!(Elections::set_approvals(Origin::signed(2), vec![true, false], 0, 0, 20));
		assert_ok!(Elections::set_approvals(Origin::signed(3), vec![false, false], 0, 0, 30));
		assert_ok!(Elections::set_approvals(Origin::signed(4), vec![], 0, 0, 40));

		assert_eq!(Elections::all_approvals_of(&2), vec![true]);
		// NOTE: these two are stored in mem differently though.
		assert!(Elections::all_approvals_of(&3).is_empty());
		assert!(Elections::all_approvals_of(&4).is_empty());

		assert_eq!(Elections::approvals_of((3, 0)), vec![0]);
		assert!(Elections::approvals_of((4, 0)).is_empty());
	});
}

#[test]
fn voting_initial_set_approvals_ignores_voter_index() {
	ExtBuilder::default().build().execute_with(|| {
		assert_ok!(Elections::submit_candidacy(Origin::signed(2), 0));

		// Last argument is essentially irrelevant. You might get or miss a tip.
		assert_ok!(Elections::set_approvals(Origin::signed(3), vec![], 0, 0, 30));
		assert_ok!(Elections::set_approvals(Origin::signed(4), vec![], 0, 5, 40));
		assert_ok!(Elections::set_approvals(Origin::signed(5), vec![], 0, 100, 50));

		// indices are more or less ignored. all is pushed.
		assert_eq!(voter_ids(), vec![3, 4, 5]);
	})
}
#[test]
fn voting_bad_approval_index_slashes_voters_and_bond_reduces_stake() {
	ExtBuilder::default().voting_fee(5).voter_bond(2).build().execute_with(|| {
		assert_ok!(Elections::submit_candidacy(Origin::signed(2), 0));

		(1..=63).for_each(|i| vote(i, 0));
		assert_eq!(balances(&1), (13, 2));
		assert_eq!(balances(&10), (18, 2));
		assert_eq!(balances(&60), (18, 2));

		// still no fee
		vote(64, 0);
		assert_eq!(balances(&64), (18, 2));
		assert_eq!(
			Elections::voter_info(&64).unwrap(),
			VoterInfo { last_win: 0, last_active: 0, stake: 20, pot:0 }
		);

		assert_eq!(Elections::next_nonfull_voter_set(), 1);

		// now we charge the next voter.
		vote(65, 0);
		assert_eq!(balances(&65), (13, 2));
		assert_eq!(
			Elections::voter_info(&65).unwrap(),
			VoterInfo { last_win: 0, last_active: 0, stake: 15, pot:0 }
		);
	});
}

#[test]
fn voting_subsequent_set_approvals_checks_voter_index() {
	ExtBuilder::default().build().execute_with(|| {
		assert_ok!(Elections::submit_candidacy(Origin::signed(2), 0));

		assert_ok!(Elections::set_approvals(Origin::signed(3), vec![], 0, 0, 30));
		assert_ok!(Elections::set_approvals(Origin::signed(4), vec![], 0, 5, 40));
		assert_ok!(Elections::set_approvals(Origin::signed(5), vec![], 0, 100, 50));

		// invalid index
		assert_noop!(
			Elections::set_approvals(Origin::signed(4), vec![true], 0, 5, 40),
			Error::<Test>::InvalidVoterIndex,
		);
		// wrong index
		assert_noop!(
			Elections::set_approvals(Origin::signed(4), vec![true], 0, 0, 40),
			Error::<Test>::InvalidVoterIndex,
		);
		// correct
		assert_ok!(Elections::set_approvals(Origin::signed(4), vec![true], 0, 1, 40));
	})
}

#[test]
fn voting_cannot_lock_less_than_limit() {
	ExtBuilder::default().build().execute_with(|| {
		assert_ok!(Elections::submit_candidacy(Origin::signed(2), 0));

		assert_noop!(
			Elections::set_approvals(Origin::signed(3), vec![], 0, 0, 4),
			Error::<Test>::InsufficientLockedValue,
		);
		assert_ok!(Elections::set_approvals(Origin::signed(3), vec![], 0, 0, 5));
	});
}

#[test]
fn voting_locking_more_than_total_balance_is_moot() {
	ExtBuilder::default().voter_bond(2).build().execute_with(|| {
		assert_ok!(Elections::submit_candidacy(Origin::signed(2), 0));

		assert_eq!(balances(&3), (30, 0));
		assert_ok!(Elections::set_approvals(Origin::signed(3), vec![], 0, 0, 35));

		assert_eq!(balances(&3), (28, 2));
		assert_eq!(
			Elections::voter_info(&3).unwrap(),
			VoterInfo { last_win: 0, last_active: 0, stake: 30, pot:0 }
		);
	});
}

#[test]
fn voting_locking_stake_and_reserving_bond_works() {
	ExtBuilder::default().voter_bond(2).build().execute_with(|| {
		assert_ok!(Elections::submit_candidacy(Origin::signed(5), 0));

		assert_eq!(balances(&2), (20, 0));
		assert!(locks(&2).is_empty());
		assert_ok!(Elections::set_approvals(Origin::signed(2), vec![], 0, 0, 15));
		assert_eq!(balances(&2), (18, 2));
		assert_eq!(locks(&2), vec![15]);

		// deposit a bit more.
		let _ = Balances::make_free_balance_be(&2, 100);

		// change vote
		assert_ok!(Elections::set_approvals(Origin::signed(2), vec![true], 0, 0, 70));
		assert_eq!(balances(&2), (100, 2));
		assert_eq!(locks(&2), vec![70]);

		assert_ok!(Elections::retract_voter(Origin::signed(2), 0));

		assert_eq!(balances(&2), (102, 0));
		assert!(locks(&2).is_empty());
	});
}

#[test]
fn voting_without_any_candidate_count_should_not_work() {
	ExtBuilder::default().build().execute_with(|| {
		assert_eq!(Elections::candidates().len(), 0);

		assert_noop!(
			Elections::set_approvals(Origin::signed(4), vec![], 0, 0, 40),
			Error::<Test>::ZeroCandidates,
		);
	});
}

#[test]
fn voting_setting_an_approval_vote_count_more_than_candidate_count_should_not_work() {
	ExtBuilder::default().build().execute_with(|| {
		assert_ok!(Elections::submit_candidacy(Origin::signed(5), 0));
		assert_eq!(Elections::candidates().len(), 1);

		assert_noop!(
			Elections::set_approvals(Origin::signed(4),vec![true, true], 0, 0, 40),
			Error::<Test>::TooManyVotes,
		);
	});
}

#[test]
fn voting_resubmitting_approvals_should_work() {
	ExtBuilder::default().build().execute_with(|| {
		assert_ok!(Elections::submit_candidacy(Origin::signed(5), 0));
		assert_ok!(Elections::set_approvals(Origin::signed(4), vec![true], 0, 0, 40));

		assert_eq!(Elections::all_approvals_of(&4), vec![true]);

		assert_ok!(Elections::submit_candidacy(Origin::signed(2), 1));
		assert_ok!(Elections::submit_candidacy(Origin::signed(3), 2));
		assert_eq!(Elections::candidates().len(), 3);
		assert_ok!(Elections::set_approvals(Origin::signed(4), vec![true, false, true], 0, 0, 40));

		assert_eq!(Elections::all_approvals_of(&4), vec![true, false, true]);
	});
}

#[test]
fn voting_retracting_voter_should_work() {
	ExtBuilder::default().build().execute_with(|| {
		assert_ok!(Elections::submit_candidacy(Origin::signed(5), 0));
		assert_eq!(Elections::candidates().len(), 1);

		assert_ok!(Elections::set_approvals(Origin::signed(1), vec![true], 0, 0, 10));
		assert_ok!(Elections::set_approvals(Origin::signed(2), vec![true], 0, 1, 20));
		assert_ok!(Elections::set_approvals(Origin::signed(3), vec![true], 0, 2, 30));
		assert_ok!(Elections::set_approvals(Origin::signed(4), vec![true], 0, 3, 40));

		assert_eq!(voter_ids(), vec![1, 2, 3, 4]);
		assert_eq!(Elections::all_approvals_of(&1), vec![true]);
		assert_eq!(Elections::all_approvals_of(&2), vec![true]);
		assert_eq!(Elections::all_approvals_of(&3), vec![true]);
		assert_eq!(Elections::all_approvals_of(&4), vec![true]);

		assert_ok!(Elections::retract_voter(Origin::signed(1), 0));

		assert_eq!(voter_ids(), vec![0, 2, 3, 4]);
		assert_eq!(Elections::all_approvals_of(&1), Vec::<bool>::new());
		assert_eq!(Elections::all_approvals_of(&2), vec![true]);
		assert_eq!(Elections::all_approvals_of(&3), vec![true]);
		assert_eq!(Elections::all_approvals_of(&4), vec![true]);

		assert_ok!(Elections::retract_voter(Origin::signed(2), 1));

		assert_eq!(voter_ids(), vec![0, 0, 3, 4]);
		assert_eq!(Elections::all_approvals_of(&1), Vec::<bool>::new());
		assert_eq!(Elections::all_approvals_of(&2), Vec::<bool>::new());
		assert_eq!(Elections::all_approvals_of(&3), vec![true]);
		assert_eq!(Elections::all_approvals_of(&4), vec![true]);

		assert_ok!(Elections::retract_voter(Origin::signed(3), 2));

		assert_eq!(voter_ids(), vec![0, 0, 0, 4]);
		assert_eq!(Elections::all_approvals_of(&1), Vec::<bool>::new());
		assert_eq!(Elections::all_approvals_of(&2), Vec::<bool>::new());
		assert_eq!(Elections::all_approvals_of(&3), Vec::<bool>::new());
		assert_eq!(Elections::all_approvals_of(&4), vec![true]);
	});
}

#[test]
fn voting_invalid_retraction_index_should_not_work() {
	ExtBuilder::default().build().execute_with(|| {
		assert_ok!(Elections::submit_candidacy(Origin::signed(3), 0));

		assert_ok!(Elections::set_approvals(Origin::signed(1), vec![true], 0, 0, 10));
		assert_ok!(Elections::set_approvals(Origin::signed(2), vec![true], 0, 0, 20));
		assert_eq!(voter_ids(), vec![1, 2]);
		assert_noop!(Elections::retract_voter(Origin::signed(1), 1), Error::<Test>::InvalidRetractionIndex);
	});
}

#[test]
fn voting_overflow_retraction_index_should_not_work() {
	ExtBuilder::default().build().execute_with(|| {
		assert_ok!(Elections::submit_candidacy(Origin::signed(3), 0));

		assert_ok!(Elections::set_approvals(Origin::signed(1), vec![true], 0, 0, 10));
		assert_noop!(Elections::retract_voter(Origin::signed(1), 1), Error::<Test>::InvalidRetractionIndex);
	});
}

#[test]
fn voting_non_voter_retraction_should_not_work() {
	ExtBuilder::default().build().execute_with(|| {
		assert_ok!(Elections::submit_candidacy(Origin::signed(3), 0));

		assert_ok!(Elections::set_approvals(Origin::signed(1), vec![true], 0, 0, 10));
		assert_noop!(Elections::retract_voter(Origin::signed(2), 0), Error::<Test>::RetractNonVoter);
	});
}

#[test]
fn retracting_inactive_voter_should_work() {
	ExtBuilder::default().build().execute_with(|| {
		System::set_block_number(4);
		assert_ok!(Elections::submit_candidacy(Origin::signed(2), 0));
		assert_ok!(Elections::set_approvals(Origin::signed(2), vec![true], 0, 0, 20));
		assert_ok!(Elections::end_block(System::block_number()));

		System::set_block_number(6);
		assert_ok!(Elections::present_winner(Origin::signed(4), 2, 20, 0));
		assert_ok!(Elections::end_block(System::block_number()));

		System::set_block_number(8);
		assert_ok!(Elections::submit_candidacy(Origin::signed(5), 0));
		assert_ok!(Elections::set_approvals(Origin::signed(5), vec![true], 1, 0, 50));
		assert_ok!(Elections::end_block(System::block_number()));

		System::set_block_number(10);
		assert_ok!(Elections::present_winner(Origin::signed(4), 5, 50, 1));
		assert_ok!(Elections::end_block(System::block_number()));

		assert_ok!(Elections::reap_inactive_voter(Origin::signed(5),
			(voter_ids().iter().position(|&i| i == 5).unwrap() as u32).into(),
			2, (voter_ids().iter().position(|&i| i == 2).unwrap() as u32).into(),
			2
		));

		assert_eq!(voter_ids(), vec![0, 5]);
		assert_eq!(Elections::all_approvals_of(&2).len(), 0);
		assert_eq!(Balances::total_balance(&2), 20);
		assert_eq!(Balances::total_balance(&5), 50);
	});
}

#[test]
fn retracting_inactive_voter_with_other_candidates_in_slots_should_work() {
	ExtBuilder::default().voter_bond(2).build().execute_with(|| {
		System::set_block_number(4);
		assert_ok!(Elections::submit_candidacy(Origin::signed(2), 0));
		assert_ok!(Elections::set_approvals(Origin::signed(2), vec![true], 0, 0, 20));
		assert_ok!(Elections::end_block(System::block_number()));

		System::set_block_number(6);
		assert_ok!(Elections::present_winner(Origin::signed(4), 2, 20, 0));
		assert_ok!(Elections::end_block(System::block_number()));

		System::set_block_number(8);
		assert_ok!(Elections::submit_candidacy(Origin::signed(5), 0));
		assert_ok!(Elections::set_approvals(Origin::signed(5), vec![true], 1, 0, 50));
		assert_ok!(Elections::end_block(System::block_number()));

		System::set_block_number(10);
		assert_ok!(Elections::present_winner(Origin::signed(4), 5, 50, 1));
		assert_ok!(Elections::end_block(System::block_number()));

		System::set_block_number(11);
		assert_ok!(Elections::submit_candidacy(Origin::signed(1), 0));

		assert_ok!(Elections::reap_inactive_voter(Origin::signed(5),
			(voter_ids().iter().position(|&i| i == 5).unwrap() as u32).into(),
			2, (voter_ids().iter().position(|&i| i == 2).unwrap() as u32).into(),
			2
		));

		assert_eq!(voter_ids(), vec![0, 5]);
		assert_eq!(Elections::all_approvals_of(&2).len(), 0);
	});
}

#[test]
fn retracting_inactive_voter_with_bad_reporter_index_should_not_work() {
	ExtBuilder::default().build().execute_with(|| {
		System::set_block_number(4);
		assert_ok!(Elections::submit_candidacy(Origin::signed(2), 0));
		assert_ok!(Elections::set_approvals(Origin::signed(2), vec![true], 0, 0, 20));
		assert_ok!(Elections::end_block(System::block_number()));

		System::set_block_number(6);
		assert_ok!(Elections::present_winner(Origin::signed(4), 2, 20, 0));
		assert_ok!(Elections::end_block(System::block_number()));

		System::set_block_number(8);
		assert_ok!(Elections::submit_candidacy(Origin::signed(5), 0));
		assert_ok!(Elections::set_approvals(Origin::signed(5), vec![true], 1, 0, 50));
		assert_ok!(Elections::end_block(System::block_number()));

		System::set_block_number(10);
		assert_ok!(Elections::present_winner(Origin::signed(4), 5, 50, 1));
		assert_ok!(Elections::end_block(System::block_number()));

		assert_noop!(Elections::reap_inactive_voter(Origin::signed(2),
			42,
			2, (voter_ids().iter().position(|&i| i == 2).unwrap() as u32).into(),
			2
		), Error::<Test>::InvalidReporterIndex);
	});
}

#[test]
fn retracting_inactive_voter_with_bad_target_index_should_not_work() {
	ExtBuilder::default().build().execute_with(|| {
		System::set_block_number(4);
		assert_ok!(Elections::submit_candidacy(Origin::signed(2), 0));
		assert_ok!(Elections::set_approvals(Origin::signed(2), vec![true], 0, 0, 20));
		assert_ok!(Elections::end_block(System::block_number()));

		System::set_block_number(6);
		assert_ok!(Elections::present_winner(Origin::signed(4), 2, 20, 0));
		assert_ok!(Elections::end_block(System::block_number()));

		System::set_block_number(8);
		assert_ok!(Elections::submit_candidacy(Origin::signed(5), 0));
		assert_ok!(Elections::set_approvals(Origin::signed(5), vec![true], 1, 0, 50));
		assert_ok!(Elections::end_block(System::block_number()));

		System::set_block_number(10);
		assert_ok!(Elections::present_winner(Origin::signed(4), 5, 50, 1));
		assert_ok!(Elections::end_block(System::block_number()));

		assert_noop!(Elections::reap_inactive_voter(Origin::signed(2),
			(voter_ids().iter().position(|&i| i == 2).unwrap() as u32).into(),
			2, 42,
			2
		), Error::<Test>::InvalidTargetIndex);
	});
}

#[test]
fn retracting_active_voter_should_slash_reporter() {
	ExtBuilder::default().build().execute_with(|| {
		System::set_block_number(4);
		assert_ok!(Elections::submit_candidacy(Origin::signed(2), 0));
		assert_ok!(Elections::submit_candidacy(Origin::signed(3), 1));
		assert_ok!(Elections::submit_candidacy(Origin::signed(4), 2));
		assert_ok!(Elections::submit_candidacy(Origin::signed(5), 3));
		assert_ok!(Elections::set_approvals(Origin::signed(2), vec![true, false, false, false], 0, 0, 20));
		assert_ok!(Elections::set_approvals(Origin::signed(3), vec![false, true, false, false], 0, 0, 30));
		assert_ok!(Elections::set_approvals(Origin::signed(4), vec![false, false, true, false], 0, 0, 40));
		assert_ok!(Elections::set_approvals(Origin::signed(5), vec![false, false, false, true], 0, 0, 50));
		assert_ok!(Elections::end_block(System::block_number()));

		System::set_block_number(6);
		assert_ok!(Elections::present_winner(Origin::signed(4), 2, 20, 0));
		assert_ok!(Elections::present_winner(Origin::signed(4), 3, 30, 0));
		assert_ok!(Elections::present_winner(Origin::signed(4), 4, 40, 0));
		assert_ok!(Elections::present_winner(Origin::signed(4), 5, 50, 0));
		assert_ok!(Elections::end_block(System::block_number()));

		System::set_block_number(8);
		assert_ok!(Elections::set_desired_seats(Origin::root(), 3));
		assert_ok!(Elections::end_block(System::block_number()));

		System::set_block_number(10);
		assert_ok!(Elections::present_winner(Origin::signed(4), 2, 20 + Elections::get_offset(20, 1), 1));
		assert_ok!(Elections::present_winner(Origin::signed(4), 3, 30 + Elections::get_offset(30, 1), 1));
		assert_ok!(Elections::end_block(System::block_number()));

		assert_eq!(Elections::vote_index(), 2);
		assert_eq!(<Test as Trait>::InactiveGracePeriod::get(), 1);
		assert_eq!(<Test as Trait>::VotingPeriod::get(), 4);
		assert_eq!(Elections::voter_info(4), Some(VoterInfo { last_win: 1, last_active: 0, stake: 40, pot: 0 }));

		assert_ok!(Elections::reap_inactive_voter(Origin::signed(4),
			(voter_ids().iter().position(|&i| i == 4).unwrap() as u32).into(),
			2,
			(voter_ids().iter().position(|&i| i == 2).unwrap() as u32).into(),
			2
		));

		assert_eq!(voter_ids(), vec![2, 3, 0, 5]);
		assert_eq!(Elections::all_approvals_of(&4).len(), 0);
		assert_eq!(Balances::total_balance(&4), 40);
	});
}

#[test]
fn retracting_inactive_voter_by_nonvoter_should_not_work() {
	ExtBuilder::default().build().execute_with(|| {
		System::set_block_number(4);
		assert_ok!(Elections::submit_candidacy(Origin::signed(2), 0));
		assert_ok!(Elections::set_approvals(Origin::signed(2), vec![true], 0, 0, 20));
		assert_ok!(Elections::end_block(System::block_number()));

		System::set_block_number(6);
		assert_ok!(Elections::present_winner(Origin::signed(4), 2, 20, 0));
		assert_ok!(Elections::end_block(System::block_number()));

		System::set_block_number(8);
		assert_ok!(Elections::submit_candidacy(Origin::signed(5), 0));
		assert_ok!(Elections::set_approvals(Origin::signed(5), vec![true], 1, 0, 50));
		assert_ok!(Elections::end_block(System::block_number()));

		System::set_block_number(10);
		assert_ok!(Elections::present_winner(Origin::signed(4), 5, 50, 1));
		assert_ok!(Elections::end_block(System::block_number()));

		assert_noop!(Elections::reap_inactive_voter(Origin::signed(4),
			0,
			2, (voter_ids().iter().position(|&i| i == 2).unwrap() as u32).into(),
			2
		), Error::<Test>::NotVoter);
	});
}

#[test]
fn candidacy_simple_candidate_submission_should_work() {
	ExtBuilder::default().build().execute_with(|| {
		assert_eq!(Elections::candidates(), Vec::<u64>::new());
		assert_eq!(Elections::candidate_reg_info(1), None);
		assert_eq!(Elections::candidate_reg_info(2), None);
		assert_eq!(Elections::is_a_candidate(&1), false);
		assert_eq!(Elections::is_a_candidate(&2), false);

		assert_ok!(Elections::submit_candidacy(Origin::signed(1), 0));
		assert_eq!(Elections::candidates(), vec![1]);
		assert_eq!(Elections::candidate_reg_info(1), Some((0, 0)));
		assert_eq!(Elections::candidate_reg_info(2), None);
		assert_eq!(Elections::is_a_candidate(&1), true);
		assert_eq!(Elections::is_a_candidate(&2), false);

		assert_ok!(Elections::submit_candidacy(Origin::signed(2), 1));
		assert_eq!(Elections::candidates(), vec![1, 2]);
		assert_eq!(Elections::candidate_reg_info(1), Some((0, 0)));
		assert_eq!(Elections::candidate_reg_info(2), Some((0, 1)));
		assert_eq!(Elections::is_a_candidate(&1), true);
		assert_eq!(Elections::is_a_candidate(&2), true);
	});
}

#[test]
fn candidacy_submission_using_free_slot_should_work() {
	let mut t = new_test_ext_with_candidate_holes();

	t.execute_with(|| {
		assert_eq!(Elections::candidates(), vec![0, 0, 1]);

		assert_ok!(Elections::submit_candidacy(Origin::signed(2), 1));
		assert_eq!(Elections::candidates(), vec![0, 2, 1]);

		assert_ok!(Elections::submit_candidacy(Origin::signed(3), 0));
		assert_eq!(Elections::candidates(), vec![3, 2, 1]);
	});
}

#[test]
fn candidacy_submission_using_alternative_free_slot_should_work() {
	let mut t = new_test_ext_with_candidate_holes();

	t.execute_with(|| {
		assert_eq!(Elections::candidates(), vec![0, 0, 1]);

		assert_ok!(Elections::submit_candidacy(Origin::signed(2), 0));
		assert_eq!(Elections::candidates(), vec![2, 0, 1]);

		assert_ok!(Elections::submit_candidacy(Origin::signed(3), 1));
		assert_eq!(Elections::candidates(), vec![2, 3, 1]);
	});
}

#[test]
fn candidacy_submission_not_using_free_slot_should_not_work() {
	let mut t = new_test_ext_with_candidate_holes();

	t.execute_with(|| {
		assert_noop!(
			Elections::submit_candidacy(Origin::signed(4), 3),
			Error::<Test>::InvalidCandidateSlot
		);
	});
}

#[test]
fn candidacy_bad_candidate_slot_submission_should_not_work() {
	ExtBuilder::default().build().execute_with(|| {
		assert_eq!(Elections::candidates(), Vec::<u64>::new());
		assert_noop!(
			Elections::submit_candidacy(Origin::signed(1), 1),
			Error::<Test>::InvalidCandidateSlot
		);
	});
}

#[test]
fn candidacy_non_free_candidate_slot_submission_should_not_work() {
	ExtBuilder::default().build().execute_with(|| {
		assert_eq!(Elections::candidates(), Vec::<u64>::new());
		assert_ok!(Elections::submit_candidacy(Origin::signed(1), 0));
		assert_eq!(Elections::candidates(), vec![1]);
		assert_noop!(
			Elections::submit_candidacy(Origin::signed(2), 0),
			Error::<Test>::InvalidCandidateSlot
		);
	});
}

#[test]
fn candidacy_dupe_candidate_submission_should_not_work() {
	ExtBuilder::default().build().execute_with(|| {
		assert_eq!(Elections::candidates(), Vec::<u64>::new());
		assert_ok!(Elections::submit_candidacy(Origin::signed(1), 0));
		assert_eq!(Elections::candidates(), vec![1]);
		assert_noop!(
			Elections::submit_candidacy(Origin::signed(1), 1),
			Error::<Test>::DuplicatedCandidate,
		);
	});
}

#[test]
fn candidacy_poor_candidate_submission_should_not_work() {
	ExtBuilder::default().build().execute_with(|| {
		assert_eq!(Elections::candidates(), Vec::<u64>::new());
		assert_noop!(
			Elections::submit_candidacy(Origin::signed(7), 0),
			Error::<Test>::InsufficientCandidateFunds,
		);
	});
}

#[test]
fn election_voting_should_work() {
	ExtBuilder::default().build().execute_with(|| {
		assert_ok!(Elections::submit_candidacy(Origin::signed(5), 0));

		assert_ok!(Elections::set_approvals(Origin::signed(1), vec![true], 0, 0, 10));
		assert_ok!(Elections::set_approvals(Origin::signed(4), vec![true], 0, 1, 40));

		assert_eq!(Elections::all_approvals_of(&1), vec![true]);
		assert_eq!(Elections::all_approvals_of(&4), vec![true]);
		assert_eq!(voter_ids(), vec![1, 4]);

		assert_ok!(Elections::submit_candidacy(Origin::signed(2), 1));
		assert_ok!(Elections::submit_candidacy(Origin::signed(3), 2));

		assert_ok!(Elections::set_approvals(Origin::signed(2), vec![false, true, true], 0, 2, 20));
		assert_ok!(Elections::set_approvals(Origin::signed(3), vec![false, true, true], 0, 3, 30));

		assert_eq!(Elections::all_approvals_of(&1), vec![true]);
		assert_eq!(Elections::all_approvals_of(&4), vec![true]);
		assert_eq!(Elections::all_approvals_of(&2), vec![false, true, true]);
		assert_eq!(Elections::all_approvals_of(&3), vec![false, true, true]);

		assert_eq!(voter_ids(), vec![1, 4, 2, 3]);
	});
}

#[test]
fn election_simple_tally_should_work() {
	ExtBuilder::default().build().execute_with(|| {
		System::set_block_number(4);
		assert!(!Elections::presentation_active());

		assert_ok!(Elections::submit_candidacy(Origin::signed(2), 0));
		assert_ok!(Elections::submit_candidacy(Origin::signed(5), 1));
		assert_ok!(Elections::set_approvals(Origin::signed(2), vec![true], 0, 0, 20));
		assert_ok!(Elections::set_approvals(Origin::signed(5), vec![false, true], 0, 0, 50));
		assert_eq!(voter_ids(), vec![2, 5]);
		assert_eq!(Elections::all_approvals_of(&2), vec![true]);
		assert_eq!(Elections::all_approvals_of(&5), vec![false, true]);
		assert_ok!(Elections::end_block(System::block_number()));

		System::set_block_number(6);
		assert!(Elections::presentation_active());
		assert_eq!(Elections::present_winner(Origin::signed(4), 2, 20, 0), Ok(()));
		assert_eq!(Elections::present_winner(Origin::signed(4), 5, 50, 0), Ok(()));
		assert_eq!(Elections::leaderboard(), Some(vec![(0, 0), (0, 0), (20, 2), (50, 5)]));
		assert_ok!(Elections::end_block(System::block_number()));

		assert!(!Elections::presentation_active());
		assert_eq!(Elections::members(), vec![(5, 11), (2, 11)]);

		assert!(!Elections::is_a_candidate(&2));
		assert!(!Elections::is_a_candidate(&5));
		assert_eq!(Elections::vote_index(), 1);
		assert_eq!(
			Elections::voter_info(2),
			Some(VoterInfo { last_win: 1, last_active: 0, stake: 20, pot: 0 })
		);
		assert_eq!(
			Elections::voter_info(5),
			Some(VoterInfo { last_win: 1, last_active: 0, stake: 50, pot: 0 })
		);
	});
}

#[test]
fn election_seats_should_be_released() {
	ExtBuilder::default().build().execute_with(|| {
		System::set_block_number(4);
		assert_ok!(Elections::submit_candidacy(Origin::signed(2), 0));
		assert_ok!(Elections::submit_candidacy(Origin::signed(5), 1));
		assert_ok!(Elections::set_approvals(Origin::signed(2), vec![true, false], 0, 0, 20));
		assert_ok!(Elections::set_approvals(Origin::signed(5), vec![false, true], 0, 0, 50));
		assert_ok!(Elections::end_block(System::block_number()));

		System::set_block_number(6);
		assert!(Elections::presentation_active());
		assert_eq!(Elections::present_winner(Origin::signed(4), 2, 20, 0), Ok(()));
		assert_eq!(Elections::present_winner(Origin::signed(4), 5, 50, 0), Ok(()));
		assert_eq!(Elections::leaderboard(), Some(vec![(0, 0), (0, 0), (20, 2), (50, 5)]));
		assert_ok!(Elections::end_block(System::block_number()));

		assert_eq!(Elections::members(), vec![(5, 11), (2, 11)]);
		let mut current = System::block_number();
		let free_block;
		loop {
			current += 1;
			System::set_block_number(current);
			assert_ok!(Elections::end_block(System::block_number()));
			if Elections::members().len() == 0 {
				free_block = current;
				break;
			}
		}
		// 11 + 2 which is the next voting period.
		assert_eq!(free_block, 14);
	});
}

#[test]
fn election_presentations_with_zero_staked_deposit_should_not_work() {
	ExtBuilder::default().build().execute_with(|| {
		System::set_block_number(4);
		assert_ok!(Elections::submit_candidacy(Origin::signed(2), 0));
		assert_ok!(Elections::set_approvals(Origin::signed(2), vec![true], 0, 0, 20));
		assert_ok!(Elections::end_block(System::block_number()));

		System::set_block_number(6);
		assert_noop!(
			Elections::present_winner(Origin::signed(4), 2, 0, 0),
			Error::<Test>::ZeroDeposit,
		);
	});
}

#[test]
fn election_double_presentations_should_be_punished() {
	ExtBuilder::default().build().execute_with(|| {
		assert!(Balances::can_slash(&4, 10));

		System::set_block_number(4);
		assert_ok!(Elections::submit_candidacy(Origin::signed(2), 0));
		assert_ok!(Elections::submit_candidacy(Origin::signed(5), 1));
		assert_ok!(Elections::set_approvals(Origin::signed(2), vec![true, false], 0, 0, 20));
		assert_ok!(Elections::set_approvals(Origin::signed(5), vec![false, true], 0, 0, 50));
		assert_ok!(Elections::end_block(System::block_number()));

		System::set_block_number(6);
		assert_ok!(Elections::present_winner(Origin::signed(4), 2, 20, 0));
		assert_ok!(Elections::present_winner(Origin::signed(4), 5, 50, 0));
		assert_eq!(
			Elections::present_winner(Origin::signed(4), 5, 50, 0),
			Err(Error::<Test>::DuplicatedPresentation.into()),
		);
		assert_ok!(Elections::end_block(System::block_number()));

		assert_eq!(Elections::members(), vec![(5, 11), (2, 11)]);
		assert_eq!(Balances::total_balance(&4), 38);
	});
}

#[test]
fn election_presenting_for_double_election_should_not_work() {
	ExtBuilder::default().build().execute_with(|| {
		System::set_block_number(4);
		assert_eq!(Elections::submit_candidacy(Origin::signed(2), 0), Ok(()));
		assert_ok!(Elections::set_approvals(Origin::signed(2), vec![true], 0, 0, 20));
		assert_ok!(Elections::end_block(System::block_number()));

		System::set_block_number(6);
		assert_ok!(Elections::present_winner(Origin::signed(4), 2, 20, 0));
		assert_ok!(Elections::end_block(System::block_number()));

		System::set_block_number(8);
		// NOTE: This is now mandatory to disable the lock
		assert_ok!(Elections::retract_voter(Origin::signed(2), 0));
		assert_eq!(Elections::submit_candidacy(Origin::signed(2), 0), Ok(()));
		assert_ok!(Elections::set_approvals(Origin::signed(2), vec![true], 1, 0, 20));
		assert_ok!(Elections::end_block(System::block_number()));

		System::set_block_number(10);
		assert_noop!(
			Elections::present_winner(Origin::signed(4), 2, 20, 1),
			Error::<Test>::DuplicatedCandidate,
		);
	});
}

#[test]
fn election_presenting_loser_should_not_work() {
	ExtBuilder::default().build().execute_with(|| {
		System::set_block_number(4);
		assert_ok!(Elections::submit_candidacy(Origin::signed(1), 0));
		assert_ok!(Elections::set_approvals(Origin::signed(6), vec![true], 0, 0, 60));
		assert_ok!(Elections::submit_candidacy(Origin::signed(2), 1));
		assert_ok!(Elections::set_approvals(Origin::signed(2), vec![false, true], 0, 0, 20));
		assert_ok!(Elections::submit_candidacy(Origin::signed(3), 2));
		assert_ok!(Elections::set_approvals(Origin::signed(3), vec![false, false, true], 0, 0, 30));
		assert_ok!(Elections::submit_candidacy(Origin::signed(4), 3));
		assert_ok!(Elections::set_approvals(Origin::signed(4), vec![false, false, false, true], 0, 0, 40));
		assert_ok!(Elections::submit_candidacy(Origin::signed(5), 4));
		assert_ok!(Elections::set_approvals(Origin::signed(5), vec![false, false, false, false, true], 0, 0, 50));
		assert_ok!(Elections::end_block(System::block_number()));

		System::set_block_number(6);
		assert_ok!(Elections::present_winner(Origin::signed(4), 1, 60, 0));
		assert_ok!(Elections::present_winner(Origin::signed(4), 3, 30, 0));
		assert_ok!(Elections::present_winner(Origin::signed(4), 4, 40, 0));
		assert_ok!(Elections::present_winner(Origin::signed(4), 5, 50, 0));

		assert_eq!(Elections::leaderboard(), Some(vec![
			(30, 3),
			(40, 4),
			(50, 5),
			(60, 1)
		]));

		assert_noop!(Elections::present_winner(Origin::signed(4), 2, 20, 0), Error::<Test>::UnworthyCandidate);
	});
}

#[test]
fn election_presenting_loser_first_should_not_matter() {
	ExtBuilder::default().build().execute_with(|| {
		System::set_block_number(4);
		assert_ok!(Elections::submit_candidacy(Origin::signed(1), 0));
		assert_ok!(Elections::set_approvals(Origin::signed(6), vec![true], 0, 0, 60));
		assert_ok!(Elections::submit_candidacy(Origin::signed(2), 1));
		assert_ok!(Elections::set_approvals(Origin::signed(2), vec![false, true], 0, 0, 20));
		assert_ok!(Elections::submit_candidacy(Origin::signed(3), 2));
		assert_ok!(Elections::set_approvals(Origin::signed(3), vec![false, false, true], 0, 0, 30));
		assert_ok!(Elections::submit_candidacy(Origin::signed(4), 3));
		assert_ok!(Elections::set_approvals(Origin::signed(4), vec![false, false, false, true], 0, 0, 40));
		assert_ok!(Elections::submit_candidacy(Origin::signed(5), 4));
		assert_ok!(Elections::set_approvals(Origin::signed(5), vec![false, false, false, false, true], 0, 0, 50));
		assert_ok!(Elections::end_block(System::block_number()));

		System::set_block_number(6);
		assert_ok!(Elections::present_winner(Origin::signed(4), 2, 20, 0));
		assert_ok!(Elections::present_winner(Origin::signed(4), 1, 60, 0));
		assert_ok!(Elections::present_winner(Origin::signed(4), 3, 30, 0));
		assert_ok!(Elections::present_winner(Origin::signed(4), 4, 40, 0));
		assert_ok!(Elections::present_winner(Origin::signed(4), 5, 50, 0));

		assert_eq!(Elections::leaderboard(), Some(vec![
			(30, 3),
			(40, 4),
			(50, 5),
			(60, 1)
		]));
	});
}

#[test]
fn election_present_outside_of_presentation_period_should_not_work() {
	ExtBuilder::default().build().execute_with(|| {
		System::set_block_number(4);
		assert!(!Elections::presentation_active());
		assert_noop!(
			Elections::present_winner(Origin::signed(5), 5, 1, 0),
			Error::<Test>::NotPresentationPeriod,
		);
	});
}

#[test]
fn election_present_with_invalid_vote_index_should_not_work() {
	ExtBuilder::default().build().execute_with(|| {
		System::set_block_number(4);
		assert_ok!(Elections::submit_candidacy(Origin::signed(2), 0));
		assert_ok!(Elections::submit_candidacy(Origin::signed(5), 1));
		assert_ok!(Elections::set_approvals(Origin::signed(2), vec![true, false], 0, 0, 20));
		assert_ok!(Elections::set_approvals(Origin::signed(5), vec![false, true], 0, 0, 50));
		assert_ok!(Elections::end_block(System::block_number()));

		System::set_block_number(6);
		assert_noop!(Elections::present_winner(Origin::signed(4), 2, 20, 1), Error::<Test>::InvalidVoteIndex);
	});
}

#[test]
fn election_present_when_presenter_is_poor_should_not_work() {
	let test_present = |p| {
		ExtBuilder::default()
			.voting_fee(5)
			.voter_bond(2)
			.bad_presentation_punishment(p)
			.build()
			.execute_with(|| {
				System::set_block_number(4);
				let _ = Balances::make_free_balance_be(&1, 15);
				assert!(!Elections::presentation_active());

 				// -3
				assert_ok!(Elections::submit_candidacy(Origin::signed(1), 0));
				assert_eq!(Balances::free_balance(1), 12);
 				// -2 -5
				assert_ok!(Elections::set_approvals(Origin::signed(1), vec![true], 0, 0, 15));
				assert_ok!(Elections::end_block(System::block_number()));

				System::set_block_number(6);
				assert_eq!(Balances::free_balance(1), 5);
				assert_eq!(Balances::reserved_balance(1), 5);
				if p > 5 {
					assert_noop!(Elections::present_winner(
						Origin::signed(1), 1, 10, 0),
						Error::<Test>::InsufficientPresenterFunds,
					);
				} else {
					assert_ok!(Elections::present_winner(Origin::signed(1), 1, 10, 0));
				}
			});
	};
	test_present(4);
	test_present(6);
}

#[test]
fn election_invalid_present_tally_should_slash() {
	ExtBuilder::default().build().execute_with(|| {
		System::set_block_number(4);
		assert!(!Elections::presentation_active());
		assert_eq!(Balances::total_balance(&4), 40);

		assert_ok!(Elections::submit_candidacy(Origin::signed(2), 0));
		assert_ok!(Elections::submit_candidacy(Origin::signed(5), 1));
		assert_ok!(Elections::set_approvals(Origin::signed(2), vec![true, false], 0, 0, 20));
		assert_ok!(Elections::set_approvals(Origin::signed(5), vec![false, true], 0, 0, 50));
		assert_ok!(Elections::end_block(System::block_number()));

		System::set_block_number(6);
		assert_err!(Elections::present_winner(Origin::signed(4), 2, 80, 0), Error::<Test>::IncorrectTotal);

		assert_eq!(Balances::total_balance(&4), 38);
	});
}

#[test]
fn election_runners_up_should_be_kept() {
	ExtBuilder::default().build().execute_with(|| {
		System::set_block_number(4);
		assert!(!Elections::presentation_active());

		assert_ok!(Elections::submit_candidacy(Origin::signed(1), 0));
		assert_ok!(Elections::set_approvals(Origin::signed(6), vec![true], 0, 0, 60));
		assert_ok!(Elections::submit_candidacy(Origin::signed(2), 1));
		assert_ok!(Elections::set_approvals(Origin::signed(2), vec![false, true], 0, 0, 20));
		assert_ok!(Elections::submit_candidacy(Origin::signed(3), 2));
		assert_ok!(Elections::set_approvals(Origin::signed(3), vec![false, false, true], 0, 0, 30));
		assert_ok!(Elections::submit_candidacy(Origin::signed(4), 3));
		assert_ok!(Elections::set_approvals(Origin::signed(4), vec![false, false, false, true], 0, 0, 40));
		assert_ok!(Elections::submit_candidacy(Origin::signed(5), 4));
		assert_ok!(Elections::set_approvals(Origin::signed(5), vec![false, false, false, false, true], 0, 0, 50));

		assert_ok!(Elections::end_block(System::block_number()));

		System::set_block_number(6);
		assert!(Elections::presentation_active());
		assert_ok!(Elections::present_winner(Origin::signed(4), 1, 60, 0));
		// leaderboard length is the empty seats plus the carry count (i.e. 5 + 2), where those
		// to be carried are the lowest and stored in lowest indices
		assert_eq!(Elections::leaderboard(), Some(vec![
			(0, 0),
			(0, 0),
			(0, 0),
			(60, 1)
		]));
		assert_ok!(Elections::present_winner(Origin::signed(4), 3, 30, 0));
		assert_ok!(Elections::present_winner(Origin::signed(4), 4, 40, 0));
		assert_ok!(Elections::present_winner(Origin::signed(4), 5, 50, 0));
		assert_eq!(Elections::leaderboard(), Some(vec![
			(30, 3),
			(40, 4),
			(50, 5),
			(60, 1)
		]));

		assert_ok!(Elections::end_block(System::block_number()));

		assert!(!Elections::presentation_active());
		assert_eq!(Elections::members(), vec![(1, 11), (5, 11)]);

		assert!(!Elections::is_a_candidate(&1));
		assert!(!Elections::is_a_candidate(&5));
		assert!(!Elections::is_a_candidate(&2));
		assert!(Elections::is_a_candidate(&3));
		assert!(Elections::is_a_candidate(&4));
		assert_eq!(Elections::vote_index(), 1);
		assert_eq!(Elections::voter_info(2), Some(VoterInfo { last_win: 0, last_active: 0, stake: 20, pot: 0 }));
		assert_eq!(Elections::voter_info(3), Some(VoterInfo { last_win: 0, last_active: 0, stake: 30, pot: 0 }));
		assert_eq!(Elections::voter_info(4), Some(VoterInfo { last_win: 0, last_active: 0, stake: 40, pot: 0 }));
		assert_eq!(Elections::voter_info(5), Some(VoterInfo { last_win: 1, last_active: 0, stake: 50, pot: 0 }));
		assert_eq!(Elections::voter_info(6), Some(VoterInfo { last_win: 1, last_active: 0, stake: 60, pot: 0 }));
		assert_eq!(Elections::candidate_reg_info(3), Some((0, 2)));
		assert_eq!(Elections::candidate_reg_info(4), Some((0, 3)));
	});
}

#[test]
fn election_second_tally_should_use_runners_up() {
	ExtBuilder::default().build().execute_with(|| {
		System::set_block_number(4);
		assert_ok!(Elections::submit_candidacy(Origin::signed(1), 0));
		assert_ok!(Elections::set_approvals(Origin::signed(6), vec![true], 0, 0, 60));
		assert_ok!(Elections::submit_candidacy(Origin::signed(2), 1));
		assert_ok!(Elections::set_approvals(Origin::signed(2), vec![false, true], 0, 0, 20));
		assert_ok!(Elections::submit_candidacy(Origin::signed(3), 2));
		assert_ok!(Elections::set_approvals(Origin::signed(3), vec![false, false, true], 0, 0, 30));
		assert_ok!(Elections::submit_candidacy(Origin::signed(4), 3));
		assert_ok!(Elections::set_approvals(Origin::signed(4), vec![false, false, false, true], 0, 0, 40));
		assert_ok!(Elections::submit_candidacy(Origin::signed(5), 4));
		assert_ok!(Elections::set_approvals(Origin::signed(5), vec![false, false, false, false, true], 0, 0, 50));
		assert_ok!(Elections::end_block(System::block_number()));

		System::set_block_number(6);
		assert_ok!(Elections::present_winner(Origin::signed(4), 1, 60, 0));
		assert_ok!(Elections::present_winner(Origin::signed(4), 3, 30, 0));
		assert_ok!(Elections::present_winner(Origin::signed(4), 4, 40, 0));
		assert_ok!(Elections::present_winner(Origin::signed(4), 5, 50, 0));
		assert_ok!(Elections::end_block(System::block_number()));

		System::set_block_number(8);
		assert_ok!(Elections::set_approvals(Origin::signed(6), vec![false, false, true, false], 1, 0, 60));
		assert_ok!(Elections::set_desired_seats(Origin::root(), 3));
		assert_ok!(Elections::end_block(System::block_number()));

		System::set_block_number(10);
		assert_ok!(Elections::present_winner(Origin::signed(4), 3, 30 + Elections::get_offset(30, 1) + 60, 1));
		assert_ok!(Elections::present_winner(Origin::signed(4), 4, 40 + Elections::get_offset(40, 1), 1));
		assert_ok!(Elections::end_block(System::block_number()));

		assert!(!Elections::presentation_active());
		assert_eq!(Elections::members(), vec![(1, 11), (5, 11), (3, 15)]);

		assert!(!Elections::is_a_candidate(&1));
		assert!(!Elections::is_a_candidate(&2));
		assert!(!Elections::is_a_candidate(&3));
		assert!(!Elections::is_a_candidate(&5));
		assert!(Elections::is_a_candidate(&4));
		assert_eq!(Elections::vote_index(), 2);
		assert_eq!(Elections::voter_info(2), Some( VoterInfo { last_win: 0, last_active: 0, stake: 20, pot: 0}));
		assert_eq!(Elections::voter_info(3), Some( VoterInfo { last_win: 2, last_active: 0, stake: 30, pot: 0}));
		assert_eq!(Elections::voter_info(4), Some( VoterInfo { last_win: 0, last_active: 0, stake: 40, pot: 0}));
		assert_eq!(Elections::voter_info(5), Some( VoterInfo { last_win: 1, last_active: 0, stake: 50, pot: 0}));
		assert_eq!(
			Elections::voter_info(6),
			Some(VoterInfo { last_win: 2, last_active: 1, stake: 60, pot: 0})
		);

		assert_eq!(Elections::candidate_reg_info(4), Some((0, 3)));
	});
}

#[test]
fn election_loser_candidates_bond_gets_slashed() {
	ExtBuilder::default().desired_seats(1).build().execute_with(|| {
		System::set_block_number(4);
		assert!(!Elections::presentation_active());

		assert_ok!(Elections::submit_candidacy(Origin::signed(1), 0));
		assert_ok!(Elections::submit_candidacy(Origin::signed(2), 1));
		assert_ok!(Elections::submit_candidacy(Origin::signed(3), 2));
		assert_ok!(Elections::submit_candidacy(Origin::signed(4), 3));

		assert_eq!(balances(&2), (17, 3));

		assert_ok!(Elections::set_approvals(Origin::signed(5), vec![true], 0, 0, 50));
		assert_ok!(
			Elections::set_approvals(Origin::signed(1), vec![false, true, true, true], 0, 0, 10)
		);

		assert_ok!(Elections::end_block(System::block_number()));

		System::set_block_number(6);
		assert!(Elections::presentation_active());
		assert_eq!(Elections::present_winner(Origin::signed(4), 4, 10, 0), Ok(()));
		assert_eq!(Elections::present_winner(Origin::signed(3), 3, 10, 0), Ok(()));
		assert_eq!(Elections::present_winner(Origin::signed(2), 2, 10, 0), Ok(()));
		assert_eq!(Elections::present_winner(Origin::signed(1), 1, 50, 0), Ok(()));


		// winner + carry
		assert_eq!(Elections::leaderboard(), Some(vec![(10, 3), (10, 4), (50, 1)]));
		assert_ok!(Elections::end_block(System::block_number()));
		assert!(!Elections::presentation_active());
		assert_eq!(Elections::members(), vec![(1, 11)]);

		// account 2 is not a runner up or in leaderboard.
		assert_eq!(balances(&2), (17, 0));
	});
}

#[test]
fn pot_accumulating_weight_and_decaying_should_work() {
	ExtBuilder::default().balance_factor(10).build().execute_with(|| {
		System::set_block_number(4);
		assert!(!Elections::presentation_active());

		assert_ok!(Elections::submit_candidacy(Origin::signed(6), 0));
		assert_ok!(Elections::submit_candidacy(Origin::signed(5), 1));
		assert_ok!(Elections::submit_candidacy(Origin::signed(1), 2));

		assert_ok!(
			Elections::set_approvals(Origin::signed(6), vec![true, false, false], 0, 0, 600)
		);
		assert_ok!(
			Elections::set_approvals(Origin::signed(5), vec![false, true, false], 0, 0, 500)
		);
		assert_ok!(
			Elections::set_approvals(Origin::signed(1), vec![false, false, true], 0, 0, 100)
		);

		assert_ok!(Elections::end_block(System::block_number()));

		System::set_block_number(6);
		assert!(Elections::presentation_active());

		assert_eq!(Elections::present_winner(Origin::signed(6), 6, 600, 0), Ok(()));
		assert_eq!(Elections::present_winner(Origin::signed(5), 5, 500, 0), Ok(()));
		assert_eq!(Elections::present_winner(Origin::signed(1), 1, 100, 0), Ok(()));
		assert_eq!(Elections::leaderboard(), Some(vec![(0, 0), (100, 1), (500, 5), (600, 6)]));
		assert_ok!(Elections::end_block(System::block_number()));

		assert_eq!(Elections::members(), vec![(6, 11), (5, 11)]);
		assert_eq!(
			Elections::voter_info(6).unwrap(),
			VoterInfo { last_win: 1, last_active: 0, stake: 600, pot: 0},
		);
		assert_eq!(
			Elections::voter_info(5).unwrap(),
			VoterInfo { last_win: 1, last_active: 0, stake: 500, pot: 0},
		);
		assert_eq!(
			Elections::voter_info(1).unwrap(),
			VoterInfo { last_win: 0, last_active: 0, stake: 100, pot: 0},
		);

		System::set_block_number(12);
		// retract needed to unlock approval funds => submit candidacy again.
		assert_ok!(Elections::retract_voter(Origin::signed(6), 0));
		assert_ok!(Elections::retract_voter(Origin::signed(5), 1));
		assert_ok!(Elections::submit_candidacy(Origin::signed(6), 0));
		assert_ok!(Elections::submit_candidacy(Origin::signed(5), 1));
		assert_ok!(
			Elections::set_approvals(Origin::signed(6), vec![true, false, false], 1, 0, 600)
		);
		assert_ok!(
			Elections::set_approvals(Origin::signed(5), vec![false, true, false], 1, 1, 500)
		);
		assert_ok!(Elections::end_block(System::block_number()));

		System::set_block_number(14);
		assert!(Elections::presentation_active());
		assert_eq!(Elections::present_winner(Origin::signed(6), 6, 600, 1), Ok(()));
		assert_eq!(Elections::present_winner(Origin::signed(5), 5, 500, 1), Ok(()));
		assert_eq!(Elections::present_winner(Origin::signed(1), 1, 100 + Elections::get_offset(100, 1), 1), Ok(()));
		assert_eq!(Elections::leaderboard(), Some(vec![(0, 0), (100 + 96, 1), (500, 5), (600, 6)]));
		assert_ok!(Elections::end_block(System::block_number()));

		assert_eq!(Elections::members(), vec![(6, 19), (5, 19)]);
		assert_eq!(
			Elections::voter_info(6).unwrap(),
			VoterInfo { last_win: 2, last_active: 1, stake: 600, pot:0 }
		);
		assert_eq!(Elections::voter_info(5).unwrap(), VoterInfo { last_win: 2, last_active: 1, stake: 500, pot:0 });
		assert_eq!(Elections::voter_info(1).unwrap(), VoterInfo { last_win: 0, last_active: 0, stake: 100, pot:0 });

		System::set_block_number(20);
		assert_ok!(Elections::retract_voter(Origin::signed(6), 0));
		assert_ok!(Elections::retract_voter(Origin::signed(5), 1));
		assert_ok!(Elections::submit_candidacy(Origin::signed(6), 0));
		assert_ok!(Elections::submit_candidacy(Origin::signed(5), 1));
		assert_ok!(Elections::set_approvals(Origin::signed(6), vec![true, false, false], 2, 0, 600));
		assert_ok!(Elections::set_approvals(Origin::signed(5), vec![false, true, false], 2, 1, 500));
		assert_ok!(Elections::end_block(System::block_number()));

		System::set_block_number(22);
		assert!(Elections::presentation_active());
		assert_eq!(Elections::present_winner(Origin::signed(6), 6, 600, 2), Ok(()));
		assert_eq!(Elections::present_winner(Origin::signed(5), 5, 500, 2), Ok(()));
		assert_eq!(Elections::present_winner(Origin::signed(1), 1, 100 + Elections::get_offset(100, 2), 2), Ok(()));
		assert_eq!(Elections::leaderboard(), Some(vec![(0, 0), (100 + 96 + 93, 1), (500, 5), (600, 6)]));
		assert_ok!(Elections::end_block(System::block_number()));

		assert_eq!(Elections::members(), vec![(6, 27), (5, 27)]);
		assert_eq!(
			Elections::voter_info(6).unwrap(),
			VoterInfo { last_win: 3, last_active: 2, stake: 600, pot: 0}
		);
		assert_eq!(Elections::voter_info(5).unwrap(), VoterInfo { last_win: 3, last_active: 2, stake: 500, pot: 0});
		assert_eq!(Elections::voter_info(1).unwrap(), VoterInfo { last_win: 0, last_active: 0, stake: 100, pot: 0});


		System::set_block_number(28);
		assert_ok!(Elections::retract_voter(Origin::signed(6), 0));
		assert_ok!(Elections::retract_voter(Origin::signed(5), 1));
		assert_ok!(Elections::submit_candidacy(Origin::signed(6), 0));
		assert_ok!(Elections::submit_candidacy(Origin::signed(5), 1));
		assert_ok!(Elections::set_approvals(Origin::signed(6), vec![true, false, false], 3, 0, 600));
		assert_ok!(Elections::set_approvals(Origin::signed(5), vec![false, true, false], 3, 1, 500));
		assert_ok!(Elections::end_block(System::block_number()));

		System::set_block_number(30);
		assert!(Elections::presentation_active());
		assert_eq!(Elections::present_winner(Origin::signed(6), 6, 600, 3), Ok(()));
		assert_eq!(Elections::present_winner(Origin::signed(5), 5, 500, 3), Ok(()));
		assert_eq!(Elections::present_winner(Origin::signed(1), 1, 100 + Elections::get_offset(100, 3), 3), Ok(()));
		assert_eq!(Elections::leaderboard(), Some(vec![(0, 0), (100 + 96 + 93 + 90, 1), (500, 5), (600, 6)]));
		assert_ok!(Elections::end_block(System::block_number()));

		assert_eq!(Elections::members(), vec![(6, 35), (5, 35)]);
		assert_eq!(
			Elections::voter_info(6).unwrap(),
			VoterInfo { last_win: 4, last_active: 3, stake: 600, pot: 0}
		);
		assert_eq!(Elections::voter_info(5).unwrap(), VoterInfo { last_win: 4, last_active: 3, stake: 500, pot: 0});
		assert_eq!(Elections::voter_info(1).unwrap(), VoterInfo { last_win: 0, last_active: 0, stake: 100, pot: 0});
	})
}

#[test]
fn pot_winning_resets_accumulated_pot() {
	ExtBuilder::default().balance_factor(10).build().execute_with(|| {
		System::set_block_number(4);
		assert!(!Elections::presentation_active());

		assert_ok!(Elections::submit_candidacy(Origin::signed(6), 0));
		assert_ok!(Elections::submit_candidacy(Origin::signed(4), 1));
		assert_ok!(Elections::submit_candidacy(Origin::signed(3), 2));
		assert_ok!(Elections::submit_candidacy(Origin::signed(2), 3));

		assert_ok!(Elections::set_approvals(Origin::signed(6), vec![true, false, false, false], 0, 0, 600));
		assert_ok!(Elections::set_approvals(Origin::signed(4), vec![false, true, false, false], 0, 1, 400));
		assert_ok!(Elections::set_approvals(Origin::signed(3), vec![false, false, true, true], 0, 2, 300));
		assert_ok!(Elections::end_block(System::block_number()));

		System::set_block_number(6);
		assert!(Elections::presentation_active());
		assert_eq!(Elections::present_winner(Origin::signed(6), 6, 600, 0), Ok(()));
		assert_eq!(Elections::present_winner(Origin::signed(4), 4, 400, 0), Ok(()));
		assert_eq!(Elections::present_winner(Origin::signed(3), 3, 300, 0), Ok(()));
		assert_eq!(Elections::present_winner(Origin::signed(2), 2, 300, 0), Ok(()));
		assert_eq!(Elections::leaderboard(), Some(vec![(300, 2), (300, 3), (400, 4), (600, 6)]));
		assert_ok!(Elections::end_block(System::block_number()));

		assert_eq!(Elections::members(), vec![(6, 11), (4, 11)]);

		System::set_block_number(12);
		assert_ok!(Elections::retract_voter(Origin::signed(6), 0));
		assert_ok!(Elections::retract_voter(Origin::signed(4), 1));
		assert_ok!(Elections::submit_candidacy(Origin::signed(6), 0));
		assert_ok!(Elections::submit_candidacy(Origin::signed(4), 1));
		assert_ok!(Elections::set_approvals(Origin::signed(6), vec![true, false, false, false], 1, 0, 600));
		assert_ok!(Elections::set_approvals(Origin::signed(4), vec![false, true, false, false], 1, 1, 400));
		assert_ok!(Elections::end_block(System::block_number()));

		System::set_block_number(14);
		assert!(Elections::presentation_active());
		assert_eq!(Elections::present_winner(Origin::signed(6), 6, 600, 1), Ok(()));
		assert_eq!(Elections::present_winner(Origin::signed(4), 4, 400, 1), Ok(()));
		assert_eq!(Elections::present_winner(Origin::signed(3), 3, 300 + Elections::get_offset(300, 1), 1), Ok(()));
		assert_eq!(Elections::present_winner(Origin::signed(2), 2, 300 + Elections::get_offset(300, 1), 1), Ok(()));
		assert_eq!(Elections::leaderboard(), Some(vec![(400, 4), (588, 2), (588, 3), (600, 6)]));
		assert_ok!(Elections::end_block(System::block_number()));

		assert_eq!(Elections::members(), vec![(6, 19), (3, 19)]);

		System::set_block_number(20);
		assert_ok!(Elections::end_block(System::block_number()));

		System::set_block_number(22);
		// 2 will not get re-elected with 300 + 288, instead just 300.
		// because one of 3's candidates (3) won in previous round
		// 4 on the other hand will get extra weight since it was unlucky.
		assert_eq!(Elections::present_winner(Origin::signed(3), 2, 300, 2), Ok(()));
		assert_eq!(Elections::present_winner(Origin::signed(4), 4, 400 + Elections::get_offset(400, 1), 2), Ok(()));
		assert_ok!(Elections::end_block(System::block_number()));

		assert_eq!(Elections::members(), vec![(4, 27), (2, 27)]);
	})
}

#[test]
fn pot_resubmitting_approvals_stores_pot() {
	ExtBuilder::default()
		.voter_bond(0)
		.voting_fee(0)
		.balance_factor(10)
		.build()
		.execute_with(|| {
			System::set_block_number(4);
			assert!(!Elections::presentation_active());

			assert_ok!(Elections::submit_candidacy(Origin::signed(6), 0));
			assert_ok!(Elections::submit_candidacy(Origin::signed(5), 1));
			assert_ok!(Elections::submit_candidacy(Origin::signed(1), 2));

			assert_ok!(
				Elections::set_approvals(Origin::signed(6), vec![true, false, false], 0, 0, 600),
			);
			assert_ok!(
				Elections::set_approvals(Origin::signed(5), vec![false, true, false], 0, 1, 500),
			);
			assert_ok!(
				Elections::set_approvals(Origin::signed(1), vec![false, false, true], 0, 2, 100),
			);

			assert_ok!(Elections::end_block(System::block_number()));

			System::set_block_number(6);
			assert!(Elections::presentation_active());

			assert_eq!(Elections::present_winner(Origin::signed(6), 6, 600, 0), Ok(()));
			assert_eq!(Elections::present_winner(Origin::signed(5), 5, 500, 0), Ok(()));
			assert_eq!(Elections::present_winner(Origin::signed(1), 1, 100, 0), Ok(()));
			assert_eq!(Elections::leaderboard(), Some(vec![(0, 0), (100, 1), (500, 5), (600, 6)]));
			assert_ok!(Elections::end_block(System::block_number()));

			assert_eq!(Elections::members(), vec![(6, 11), (5, 11)]);

			System::set_block_number(12);
			assert_ok!(Elections::retract_voter(Origin::signed(6), 0));
			assert_ok!(Elections::retract_voter(Origin::signed(5), 1));
			assert_ok!(Elections::submit_candidacy(Origin::signed(6), 0));
			assert_ok!(Elections::submit_candidacy(Origin::signed(5), 1));
			assert_ok!(
				Elections::set_approvals(Origin::signed(6), vec![true, false, false], 1, 0, 600),
			);
			assert_ok!(
				Elections::set_approvals(Origin::signed(5), vec![false, true, false], 1, 1, 500),
			);
			// give 1 some new high balance
			let _ = Balances::make_free_balance_be(&1, 997);
			assert_ok!(
				Elections::set_approvals(Origin::signed(1), vec![false, false, true], 1, 2, 1000),
			);
			assert_eq!(Elections::voter_info(1).unwrap(),
				VoterInfo {
					stake: 1000, // 997 + 3 which is candidacy bond.
					pot: Elections::get_offset(100, 1),
					last_active: 1,
					last_win: 1,
				}
			);
			assert_ok!(Elections::end_block(System::block_number()));

			assert_eq!(Elections::members(), vec![(6, 11), (5, 11)]);

			System::set_block_number(14);
			assert!(Elections::presentation_active());
			assert_eq!(Elections::present_winner(Origin::signed(6), 6, 600, 1), Ok(()));
			assert_eq!(Elections::present_winner(Origin::signed(5), 5, 500, 1), Ok(()));
			assert_eq!(
				Elections::present_winner(Origin::signed(1), 1, 1000 + 96 /* pot */, 1),
				Ok(()),
			);
			assert_eq!(Elections::leaderboard(), Some(vec![(0, 0), (500, 5), (600, 6), (1096, 1)]));
			assert_ok!(Elections::end_block(System::block_number()));

			assert_eq!(Elections::members(), vec![(1, 19), (6, 19)]);
		})
}

#[test]
fn pot_get_offset_should_work() {
	ExtBuilder::default().build().execute_with(|| {
		assert_eq!(Elections::get_offset(100, 0), 0);
		assert_eq!(Elections::get_offset(100, 1), 96);
		assert_eq!(Elections::get_offset(100, 2), 96 + 93);
		assert_eq!(Elections::get_offset(100, 3), 96 + 93 + 90);
		assert_eq!(Elections::get_offset(100, 4), 96 + 93 + 90 + 87);
		// limit
		assert_eq!(Elections::get_offset(100, 1000), 100 * 24);

		assert_eq!(Elections::get_offset(50_000_000_000, 0), 0);
		assert_eq!(Elections::get_offset(50_000_000_000, 1), 48_000_000_000);
		assert_eq!(Elections::get_offset(50_000_000_000, 2), 48_000_000_000 + 46_080_000_000);
		assert_eq!(Elections::get_offset(50_000_000_000, 3), 48_000_000_000 + 46_080_000_000 + 44_236_800_000);
		assert_eq!(
			Elections::get_offset(50_000_000_000, 4),
			48_000_000_000 + 46_080_000_000 + 44_236_800_000 + 42_467_328_000
		);
		// limit
		assert_eq!(Elections::get_offset(50_000_000_000, 1000), 50_000_000_000 * 24);
	})
}

#[test]
fn pot_get_offset_with_zero_decay() {
	ExtBuilder::default().decay_ratio(0).build().execute_with(|| {
		assert_eq!(Elections::get_offset(100, 0), 0);
		assert_eq!(Elections::get_offset(100, 1), 0);
		assert_eq!(Elections::get_offset(100, 2), 0);
		assert_eq!(Elections::get_offset(100, 3), 0);
		// limit
		assert_eq!(Elections::get_offset(100, 1000), 0);
	})
}
