Browse Source

runtime: storage-v2: Add tests for create_storage_bucket().

Shamil Gadelshin 3 years ago
parent
commit
49505a7d20

+ 1 - 0
Cargo.lock

@@ -4280,6 +4280,7 @@ dependencies = [
  "pallet-balances",
  "pallet-common",
  "pallet-membership",
+ "pallet-timestamp",
  "parity-scale-codec",
  "serde",
  "sp-arithmetic",

+ 4 - 2
runtime-modules/storage-v2/Cargo.toml

@@ -12,9 +12,10 @@ frame-support = { package = 'frame-support', default-features = false, git = 'ht
 frame-system = { package = 'frame-system', default-features = false, git = 'https://github.com/paritytech/substrate.git', rev = '2cd20966cc09b059817c3ebe12fc130cdd850d62'}
 sp-arithmetic = { package = 'sp-arithmetic', default-features = false, git = 'https://github.com/paritytech/substrate.git', rev = '2cd20966cc09b059817c3ebe12fc130cdd850d62'}
 balances = { package = 'pallet-balances', default-features = false, git = 'https://github.com/paritytech/substrate.git', rev = '2cd20966cc09b059817c3ebe12fc130cdd850d62'}
+pallet-timestamp = { package = 'pallet-timestamp', default-features = false, git = 'https://github.com/paritytech/substrate.git', rev = '2cd20966cc09b059817c3ebe12fc130cdd850d62'}
+sp-runtime = { package = 'sp-runtime', default-features = false, git = 'https://github.com/paritytech/substrate.git', rev = '2cd20966cc09b059817c3ebe12fc130cdd850d62'}
 common = { package = 'pallet-common', default-features = false, path = '../common'}
 membership = { package = 'pallet-membership', default-features = false, path = '../membership'}
-sp-runtime = { package = 'sp-runtime', default-features = false, git = 'https://github.com/paritytech/substrate.git', rev = '2cd20966cc09b059817c3ebe12fc130cdd850d62'}
 
 [dev-dependencies]
 sp-io = { package = 'sp-io', default-features = false, git = 'https://github.com/paritytech/substrate.git', rev = '2cd20966cc09b059817c3ebe12fc130cdd850d62'}
@@ -29,8 +30,9 @@ std = [
     'frame-support/std',
     'frame-system/std',
     'sp-arithmetic/std',
-    'sp-runtime/std',
     'balances/std',
+    'pallet-timestamp/std',
+    'sp-runtime/std',
     'common/std',
     'membership/std',
 ]

+ 71 - 21
runtime-modules/storage-v2/src/lib.rs

@@ -9,28 +9,29 @@
 // TODO: add benchmarks
 // TODO: add constants:
 // Max size of blacklist.
-// Max number of storage buckets.
 // Max number of distribution bucket families
 // Max number of distribution buckets per family.
 // Max number of pending invitations per distribution bucket.
 // Max number of data objects per bag.
 
-
 #[cfg(test)]
 mod tests;
 
 use codec::{Codec, Decode, Encode};
 use frame_support::dispatch::DispatchResult;
+use frame_support::traits::Get;
 use frame_support::{decl_error, decl_event, decl_module, decl_storage, Parameter};
+use frame_system::ensure_signed;
 #[cfg(feature = "std")]
 use serde::{Deserialize, Serialize};
+use sp_arithmetic::traits::BaseArithmetic;
+use sp_arithmetic::traits::One;
+use sp_runtime::traits::{MaybeSerialize, Member};
 use sp_std::collections::btree_map::BTreeMap;
 use sp_std::collections::btree_set::BTreeSet;
-use sp_arithmetic::traits::{BaseArithmetic};
-use sp_runtime::traits::{MaybeSerialize, Member};
 
 /// Storage trait.
-pub trait Trait: frame_system::Trait + balances::Trait + membership::Trait{
+pub trait Trait: frame_system::Trait + balances::Trait + membership::Trait {
     /// Storage event type.
     type Event: From<Event<Self>> + Into<<Self as frame_system::Trait>::Event>;
 
@@ -53,6 +54,9 @@ pub trait Trait: frame_system::Trait + balances::Trait + membership::Trait{
         + Copy
         + MaybeSerialize
         + PartialEq;
+
+    /// Defines max allowed storage bucket number.
+    type MaxStorageBucketNumber: Get<u64>;
 }
 
 // /// Member identifier in membership::member module
@@ -99,7 +103,7 @@ pub struct DataObject<StorageBucketId, Balance> {
 pub struct StaticBag<DataObjectId: Ord, StorageBucketId: Ord, Balance> {
     pub objects: BTreeMap<DataObjectId, DataObject<StorageBucketId, Balance>>,
     pub stored_by: BTreeSet<StorageBucketId>,
-//TODO: implement -    pub distributed_by: BTreeSet<DistributionBucketId>,
+    //TODO: implement -    pub distributed_by: BTreeSet<DistributionBucketId>,
 }
 
 #[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
@@ -127,7 +131,7 @@ impl Default for BagId {
 #[derive(Encode, Decode, Clone, PartialEq, Eq, Debug, PartialOrd, Ord)]
 pub enum StaticBagId {
     Council,
-//TODO: implement -    WorkingGroup(WorkingGroup),
+    //TODO: implement -    WorkingGroup(WorkingGroup),
 }
 
 impl Default for StaticBagId {
@@ -205,13 +209,13 @@ pub struct BaggedDataObject<DataObjectId> {
 #[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
 #[derive(Encode, Decode, Default, Clone, PartialEq, Eq, Debug)]
 pub struct UpdateStorageBucketForStaticBagsParams<StorageBucketId: Ord> {
-    pub bags: BTreeMap<BagId, BTreeSet<StorageBucketId>> //TODO: change to StaticBagId
+    pub bags: BTreeMap<BagId, BTreeSet<StorageBucketId>>, //TODO: change to StaticBagId
 }
 
 #[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
 #[derive(Encode, Decode, Default, Clone, PartialEq, Eq, Debug)]
 pub struct AcceptPendingDataObjectsParams<DataObjectId: Ord> {
-    pub bagged_data_objects: BTreeSet<BaggedDataObject<DataObjectId>>
+    pub bagged_data_objects: BTreeSet<BaggedDataObject<DataObjectId>>,
 }
 
 decl_storage! {
@@ -221,10 +225,13 @@ decl_storage! {
         /// Council bag.
         pub CouncilBag get(fn council_bag): StaticBag<T::DataObjectId, T::StorageBucketId, BalanceOf<T>>;
 
-        // TODO change the comment
-        /// Storage bucket (flat) map
-        pub StorageBucketById get (fn storage_bucket_by_id)
-            : BTreeMap<T::StorageBucketId, StorageBucket<WorkerId<T>>>;
+        /// Storage bucket id counter. Starts at zero.
+        pub NextStorageBucketId get(fn next_storage_bucket_id) : T::StorageBucketId;
+
+        // TODO: rework back to "Storage bucket (flat) map" - BTreemap
+        /// Storage buckets.
+        pub StorageBucketById get (fn storage_bucket_by_id): map hasher(blake2_128_concat)
+            T::StorageBucketId => StorageBucket<WorkerId<T>>;
     }
 }
 
@@ -232,10 +239,16 @@ decl_event! {
     /// Storage events
  pub enum Event<T>
     where
-        <T as frame_system::Trait>::AccountId
+        <T as Trait>::StorageBucketId,
+        WorkerId = WorkerId<T>,
     {
-        /// Emits on adding of the content.
-        ContentAdded(AccountId),
+        /// Emits on creating the storage bucket.
+        /// Params
+        /// - storage bucket ID
+        /// - invited worker
+        /// - flag "accepting_new_data_objects"
+        /// - voucher struct
+        StorageBucketCreated(StorageBucketId, Option<WorkerId>, bool, Voucher),
     }
 }
 
@@ -256,6 +269,9 @@ decl_module! {
         /// Predefined errors.
         type Error = Error<T>;
 
+        /// Exports const -  max allowed storage bucket number.
+        const MaxStorageBucketNumber: u64 = T::MaxStorageBucketNumber::get();
+
         /// Upload new objects, and does so atomically if there is more than one provided.
         /// TODO:
         /// - Must return rich information about bags & data objects created.
@@ -282,12 +298,46 @@ decl_module! {
         /// Create storage bucket.
         #[weight = 10_000_000] // TODO: adjust weight
         pub fn create_storage_bucket(
-            _origin,
-            _invite_worker: Option<WorkerId<T>>,
-            _accepting_new_data_objects: bool,
-            _voucher: Voucher
+            origin,
+            invite_worker: Option<WorkerId<T>>,
+            accepting_new_data_objects: bool,
+            voucher: Voucher
         ) {
-            //TODO implement
+            ensure_signed(origin)?; // TODO: change to the WG lead verification
+
+            //TODO: check max bucket number
+
+            let operator_status = invite_worker
+                .map(StorageBucketOperatorStatus::InvitedStorageWorker)
+                .unwrap_or(StorageBucketOperatorStatus::Missing);
+
+            //TODO: validate voucher?
+
+            let storage_bucket = StorageBucket {
+                 operator_status,
+                 accepting_new_bags: accepting_new_data_objects, //TODO: correct?
+                 number_of_pending_data_objects: 0,
+                 voucher: voucher.clone(),
+            };
+
+            let storage_bucket_id = Self::next_storage_bucket_id();
+
+            //
+            // == MUTATION SAFE ==
+            //
+
+            <NextStorageBucketId<T>>::put(storage_bucket_id + One::one());
+
+            <StorageBucketById<T>>::insert(storage_bucket_id, storage_bucket);
+
+            Self::deposit_event(
+                RawEvent::StorageBucketCreated(
+                    storage_bucket_id,
+                    invite_worker,
+                    accepting_new_data_objects,
+                    voucher,
+                )
+            );
         }
 
 

+ 132 - 0
runtime-modules/storage-v2/src/tests/fixtures.rs

@@ -0,0 +1,132 @@
+use frame_support::dispatch::DispatchResult;
+use frame_support::storage::StorageMap;
+use frame_support::traits::{Currency, OnFinalize, OnInitialize};
+use frame_system::{EventRecord, Phase, RawOrigin};
+
+use super::mock::{Balances, Storage, System, Test, TestEvent};
+
+use crate::{RawEvent, StorageBucket, Voucher};
+
+// Recommendation from Parity on testing on_finalize
+// https://substrate.dev/docs/en/next/development/module/tests
+pub fn run_to_block(n: u64) {
+    while System::block_number() < n {
+        <System as OnFinalize<u64>>::on_finalize(System::block_number());
+        <Storage as OnFinalize<u64>>::on_finalize(System::block_number());
+        System::set_block_number(System::block_number() + 1);
+        <System as OnInitialize<u64>>::on_initialize(System::block_number());
+        <Storage as OnInitialize<u64>>::on_initialize(System::block_number());
+    }
+}
+
+pub fn increase_account_balance(account_id: &u64, balance: u64) {
+    let _ = Balances::deposit_creating(&account_id, balance);
+}
+
+pub struct EventFixture;
+impl EventFixture {
+    pub fn assert_last_crate_event(expected_raw_event: RawEvent<u64, u64>) {
+        let converted_event = TestEvent::storage(expected_raw_event);
+
+        Self::assert_last_global_event(converted_event)
+    }
+
+    pub fn contains_crate_event(expected_raw_event: RawEvent<u64, u64>) {
+        let converted_event = TestEvent::storage(expected_raw_event);
+
+        Self::contains_global_event(converted_event)
+    }
+
+    pub fn assert_last_global_event(expected_event: TestEvent) {
+        let expected_event = EventRecord {
+            phase: Phase::Initialization,
+            event: expected_event,
+            topics: vec![],
+        };
+
+        assert_eq!(System::events().pop().unwrap(), expected_event);
+    }
+
+    fn contains_global_event(expected_event: TestEvent) {
+        let expected_event = EventRecord {
+            phase: Phase::Initialization,
+            event: expected_event,
+            topics: vec![],
+        };
+
+        assert!(System::events().iter().any(|ev| *ev == expected_event));
+    }
+}
+
+const DEFAULT_ACCOUNT_ID: u64 = 1;
+
+pub struct CreateStorageBucketFixture {
+    origin: RawOrigin<u64>,
+    invite_worker: Option<u64>,
+    accepting_new_data_objects: bool,
+    voucher: Voucher,
+}
+
+impl CreateStorageBucketFixture {
+    pub fn default() -> Self {
+        Self {
+            origin: RawOrigin::Signed(DEFAULT_ACCOUNT_ID),
+            invite_worker: None,
+            accepting_new_data_objects: false,
+            voucher: Default::default(),
+        }
+    }
+
+    pub fn with_origin(self, origin: RawOrigin<u64>) -> Self {
+        Self { origin, ..self }
+    }
+
+    pub fn with_invite_worker(self, invite_worker: Option<u64>) -> Self {
+        Self {
+            invite_worker,
+            ..self
+        }
+    }
+
+    pub fn with_accepting_new_data_objects(self, accepting_new_data_objects: bool) -> Self {
+        Self {
+            accepting_new_data_objects,
+            ..self
+        }
+    }
+
+    pub fn with_voucher(self, voucher: Voucher) -> Self {
+        Self { voucher, ..self }
+    }
+
+    pub fn call_and_assert(&self, expected_result: DispatchResult) -> Option<u64> {
+        let next_storage_bucket_id = Storage::next_storage_bucket_id();
+        let actual_result = Storage::create_storage_bucket(
+            self.origin.clone().into(),
+            self.invite_worker,
+            self.accepting_new_data_objects,
+            self.voucher.clone(),
+        );
+
+        assert_eq!(actual_result, expected_result);
+
+        if actual_result.is_ok() {
+            assert_eq!(
+                next_storage_bucket_id + 1,
+                Storage::next_storage_bucket_id()
+            );
+            assert!(<crate::StorageBucketById<Test>>::contains_key(
+                next_storage_bucket_id
+            ));
+
+            Some(next_storage_bucket_id)
+        } else {
+            assert_eq!(next_storage_bucket_id, Storage::next_storage_bucket_id());
+            assert!(!<crate::StorageBucketById<Test>>::contains_key(
+                next_storage_bucket_id
+            ));
+
+            None
+        }
+    }
+}

+ 31 - 0
runtime-modules/storage-v2/src/tests/mock.rs

@@ -21,11 +21,16 @@ mod storage {
     pub use crate::Event;
 }
 
+mod membership_mod {
+    pub use membership::Event;
+}
+
 impl_outer_event! {
     pub enum TestEvent for Test {
         balances<T>,
         storage<T>,
         frame_system<T>,
+        membership_mod<T>,
     }
 }
 
@@ -43,8 +48,34 @@ impl balances::Trait for Test {
     type MaxLocks = ();
 }
 
+parameter_types! {
+    pub const MaxStorageBucketNumber: u64 = 2;
+}
+
 impl crate::Trait for Test {
     type Event = TestEvent;
+    type DataObjectId = u64;
+    type StorageBucketId = u64;
+    type MaxStorageBucketNumber = MaxStorageBucketNumber;
+}
+
+impl membership::Trait for Test {
+    type Event = TestEvent;
+    type MemberId = u64;
+    type PaidTermId = u64;
+    type SubscriptionId = u64;
+    type ActorId = u64;
+}
+
+impl pallet_timestamp::Trait for Test {
+    type Moment = u64;
+    type OnTimestampSet = ();
+    type MinimumPeriod = MinimumPeriod;
+    type WeightInfo = ();
+}
+
+impl common::currency::GovernanceCurrency for Test {
+    type Currency = balances::Module<Self>;
 }
 
 parameter_types! {

+ 68 - 2
runtime-modules/storage-v2/src/tests/mod.rs

@@ -1,12 +1,78 @@
 #![cfg(test)]
 
+mod fixtures;
 mod mock;
 
+use frame_support::dispatch::DispatchError;
+use frame_system::RawOrigin;
+
+use fixtures::{run_to_block, CreateStorageBucketFixture, EventFixture};
 use mock::{initial_test_ext, Storage};
 
+use crate::{RawEvent, StorageBucketOperatorStatus, Voucher};
+
+#[test]
+fn create_storage_bucket_succeeded() {
+    initial_test_ext().execute_with(|| {
+        let starting_block = 1;
+        run_to_block(starting_block);
+
+        let accepting_new_data_objects = true;
+        let voucher = Voucher::default();
+        let invite_worker = None;
+
+        let bucket_id = CreateStorageBucketFixture::default()
+            .with_accepting_new_data_objects(accepting_new_data_objects)
+            .with_invite_worker(invite_worker)
+            .with_voucher(voucher.clone())
+            .call_and_assert(Ok(()))
+            .unwrap();
+
+        let storage_bucket = Storage::storage_bucket_by_id(bucket_id);
+
+        assert_eq!(
+            storage_bucket.operator_status,
+            StorageBucketOperatorStatus::Missing
+        );
+
+        EventFixture::assert_last_crate_event(RawEvent::StorageBucketCreated(
+            bucket_id,
+            invite_worker,
+            accepting_new_data_objects,
+            voucher,
+        ));
+    });
+}
+
+#[test]
+fn create_storage_bucket_succeeded_with_invited_member() {
+    initial_test_ext().execute_with(|| {
+        let invited_worker_id = 10;
+        let accepting_new_data_objects = true;
+        let voucher = Voucher::default();
+        let invite_worker = Some(invited_worker_id);
+
+        let bucket_id = CreateStorageBucketFixture::default()
+            .with_accepting_new_data_objects(accepting_new_data_objects)
+            .with_invite_worker(invite_worker)
+            .with_voucher(voucher.clone())
+            .call_and_assert(Ok(()))
+            .unwrap();
+
+        let storage_bucket = Storage::storage_bucket_by_id(bucket_id);
+
+        assert_eq!(
+            storage_bucket.operator_status,
+            StorageBucketOperatorStatus::InvitedStorageWorker(invited_worker_id)
+        );
+    });
+}
+
 #[test]
-fn test() {
+fn create_storage_bucket_fails_with_invalid_origin() {
     initial_test_ext().execute_with(|| {
-        assert_eq!(Storage::council_bag(), Default::default());
+        CreateStorageBucketFixture::default()
+            .with_origin(RawOrigin::None)
+            .call_and_assert(Err(DispatchError::BadOrigin));
     });
 }