diff --git a/substrate/frame/referenda-tracks/README.md b/substrate/frame/referenda-tracks/README.md index 5224f1b288207..5227ed32637d6 100644 --- a/substrate/frame/referenda-tracks/README.md +++ b/substrate/frame/referenda-tracks/README.md @@ -1,6 +1,18 @@ -# Remark Storage Pallet +# Referenda Tracks Pallet -Allows storing arbitrary data off chain. +- [`Config`][Config] +- [`Call`][Call] +## Overview + +Manage referenda voting tracks. + +## Interface + +### Dispatchable Functions + +- `insert` - Insert a new referenda Track. +- `update` - Update the configuration of an existing referenda Track. +- `remove` - Remove an existing track License: Apache-2.0 diff --git a/substrate/frame/referenda-tracks/src/mock.rs b/substrate/frame/referenda-tracks/src/mock.rs index 5e3a70808daec..8dae217235f35 100644 --- a/substrate/frame/referenda-tracks/src/mock.rs +++ b/substrate/frame/referenda-tracks/src/mock.rs @@ -25,8 +25,10 @@ use frame_support::{ weights::Weight, }; use frame_system::{EnsureRoot, EnsureSignedBy}; +use pallet_referenda::{PalletsOriginOf, TrackIdOf, TrackInfoOf}; use scale_info::TypeInfo; use sp_core::H256; +use sp_io::TestExternalities; use sp_runtime::{ traits::{BlakeTwo256, IdentityLookup}, BuildStorage, Perbill, @@ -118,7 +120,8 @@ impl pallet_referenda_tracks::Config for Test { type TrackId = u8; type RuntimeEvent = RuntimeEvent; type MaxTracks = MaxTracks; - // type WeightInfo = (); + type UpdateOrigin = EnsureRoot; + type WeightInfo = (); } parameter_types! { @@ -177,13 +180,32 @@ impl VoteTally for Tally { } } -pub fn new_test_ext() -> sp_io::TestExternalities { +pub fn new_test_ext( + maybe_tracks: Option, TrackInfoOf, PalletsOriginOf)>>, +) -> sp_io::TestExternalities { let balances = vec![(1, 100), (2, 100), (3, 100), (4, 100), (5, 100), (6, 100)]; + let t = RuntimeGenesisConfig { system: Default::default(), balances: pallet_balances::GenesisConfig:: { balances }, } .build_storage() .unwrap(); - t.into() + + let mut ext = TestExternalities::new(t); + ext.execute_with(|| { + System::set_block_number(1); + + if let Some(tracks) = maybe_tracks { + for (id, info, pallet_origin) in tracks { + crate::Pallet::::insert(RuntimeOrigin::root(), id, info, pallet_origin) + .expect("can insert track"); + } + + System::reset_events(); + } else { + } + }); + + ext } diff --git a/substrate/frame/referenda-tracks/src/tests.rs b/substrate/frame/referenda-tracks/src/tests.rs index fa9c8194eb034..1d85210df573d 100644 --- a/substrate/frame/referenda-tracks/src/tests.rs +++ b/substrate/frame/referenda-tracks/src/tests.rs @@ -17,28 +17,219 @@ //! Tests for referenda tracks pallet. -use super::{Error, Event, Pallet as Tracks}; +use super::{Error, Event, Pallet as ReferendaTracks, Tracks}; use crate::mock::*; use frame_support::{assert_noop, assert_ok}; -use frame_system::RawOrigin; - -#[test] -fn generates_event() { - new_test_ext().execute_with(|| { - // let caller = 1; - // let data = vec![0u8; 100]; - // System::set_block_number(System::block_number() + 1); //otherwise event won't be - // registered. assert_ok!(Remark::::store(RawOrigin::Signed(caller).into(), - // data.clone(),)); let events = System::events(); - // // this one we create as we expect it - // let system_event: ::RuntimeEvent = Event::Stored { - // content_hash: sp_io::hashing::blake2_256(&data).into(), - // sender: caller, - // } - // .into(); - // // this one we actually go into the system pallet and get the last event - // // because we know its there from block +1 - // let frame_system::EventRecord { event, .. } = &events[events.len() - 1]; - assert_eq!(true, true); - }); +use frame_system::{EventRecord, Phase, RawOrigin}; +use pallet_referenda::TrackInfo; +use sp_runtime::{str_array as s, traits::BadOrigin, Perbill}; + +const TRACK: pallet_referenda::TrackInfoOf = TrackInfo { + name: s("Test Track"), + max_deciding: 1, + decision_deposit: 0, + prepare_period: 10, + decision_period: 100, + confirm_period: 10, + min_enactment_period: 2, + min_approval: pallet_referenda::Curve::LinearDecreasing { + length: Perbill::from_percent(100), + floor: Perbill::from_percent(50), + ceil: Perbill::from_percent(100), + }, + min_support: pallet_referenda::Curve::LinearDecreasing { + length: Perbill::from_percent(100), + floor: Perbill::from_percent(0), + ceil: Perbill::from_percent(50), + }, +}; + +const ORIGIN_SIGNED_1: OriginCaller = OriginCaller::system(RawOrigin::Signed(1)); +const ORIGIN_SIGNED_2: OriginCaller = OriginCaller::system(RawOrigin::Signed(2)); +const ORIGIN_SIGNED_3: OriginCaller = OriginCaller::system(RawOrigin::Signed(3)); + +mod insert { + use super::*; + + #[test] + fn fails_if_incorrect_origin() { + new_test_ext(None).execute_with(|| { + assert_noop!( + ReferendaTracks::::insert( + RuntimeOrigin::signed(1), + 1, + TRACK, + ORIGIN_SIGNED_1 + ), + BadOrigin + ); + }); + } + + #[test] + fn it_works() { + new_test_ext(None).execute_with(|| { + System::set_block_number(1); + + assert_ok!(ReferendaTracks::::insert( + RuntimeOrigin::root(), + 1, + TRACK, + ORIGIN_SIGNED_1 + )); + + assert_eq!( + System::events(), + vec![EventRecord { + phase: Phase::Initialization, + event: RuntimeEvent::Tracks(Event::Created { id: 1 }), + topics: vec![], + }], + ); + + assert_eq!(Tracks::::get(1), Some(TRACK)); + }); + } + + #[test] + fn it_fails_if_inserting_an_already_existing_track() { + new_test_ext(None).execute_with(|| { + assert_ok!(ReferendaTracks::::insert( + RuntimeOrigin::root(), + 1, + TRACK, + ORIGIN_SIGNED_1 + )); + + assert_noop!( + ReferendaTracks::::insert( + RuntimeOrigin::root(), + 1, + TRACK, + ORIGIN_SIGNED_2 + ), + Error::::TrackIdAlreadyExisting + ); + }); + } + + #[test] + fn fails_if_exceeds_max_tracks() { + new_test_ext(None).execute_with(|| { + assert_ok!(ReferendaTracks::::insert( + RuntimeOrigin::root(), + 1, + TRACK, + ORIGIN_SIGNED_1 + )); + + assert_ok!(ReferendaTracks::::insert( + RuntimeOrigin::root(), + 2, + TRACK, + ORIGIN_SIGNED_2 + )); + + assert_noop!( + ReferendaTracks::::insert( + RuntimeOrigin::root(), + 3, + TRACK, + ORIGIN_SIGNED_3 + ), + Error::::MaxTracksExceeded + ); + }); + } +} + +mod update { + use super::*; + + #[test] + fn fails_if_incorrect_origin() { + new_test_ext(None).execute_with(|| { + assert_noop!( + ReferendaTracks::::update(RuntimeOrigin::signed(1), 1, TRACK), + BadOrigin + ); + }); + } + + #[test] + fn fails_if_non_existing() { + new_test_ext(None).execute_with(|| { + assert_noop!( + ReferendaTracks::::update(RuntimeOrigin::root(), 1, TRACK), + Error::::TrackIdNotFound, + ); + }); + } + + #[test] + fn it_works() { + new_test_ext(Some(vec![(1, TRACK, ORIGIN_SIGNED_1)])).execute_with(|| { + let mut track = TRACK.clone(); + track.max_deciding = 2; + + assert_ok!(ReferendaTracks::::update( + RuntimeOrigin::root(), + 1, + track.clone() + )); + + assert_eq!( + System::events(), + vec![EventRecord { + phase: Phase::Initialization, + event: RuntimeEvent::Tracks(Event::Updated { id: 1, info: track.clone() }), + topics: vec![], + }], + ); + + assert_eq!(Tracks::::get(1), Some(track)); + }); + } +} + +mod remove { + use super::*; + + #[test] + fn fails_if_incorrect_origin() { + new_test_ext(None).execute_with(|| { + assert_noop!( + ReferendaTracks::::remove(RuntimeOrigin::signed(1), 1), + BadOrigin + ); + }); + } + + #[test] + fn fails_if_non_existing() { + new_test_ext(None).execute_with(|| { + assert_noop!( + ReferendaTracks::::remove(RuntimeOrigin::root(), 1), + Error::::TrackIdNotFound, + ); + }); + } + + #[test] + fn it_works() { + new_test_ext(Some(vec![(1, TRACK, ORIGIN_SIGNED_1)])).execute_with(|| { + assert_ok!(ReferendaTracks::::remove(RuntimeOrigin::root(), 1)); + + assert_eq!( + System::events(), + vec![EventRecord { + phase: Phase::Initialization, + event: RuntimeEvent::Tracks(Event::Removed { id: 1 }), + topics: vec![], + }], + ); + + assert_eq!(Tracks::::get(1), None); + }); + } }