Browse Source

Merge remote-tracking branch 'origin/proposal_codex_upgrade' into content_directory_schema_refactoring

iorveth 4 years ago
parent
commit
d0842cb95b

+ 24 - 2
Cargo.lock

@@ -2117,6 +2117,7 @@ dependencies = [
  "sp-std",
  "sp-transaction-pool",
  "sp-version",
+ "strum 0.19.2",
  "substrate-wasm-builder-runner",
 ]
 
@@ -3301,6 +3302,8 @@ dependencies = [
  "parity-scale-codec",
  "serde",
  "sp-runtime",
+ "strum 0.19.2",
+ "strum_macros 0.19.2",
 ]
 
 [[package]]
@@ -3549,6 +3552,7 @@ dependencies = [
  "sp-runtime",
  "sp-staking",
  "sp-std",
+ "strum 0.19.2",
 ]
 
 [[package]]
@@ -6456,7 +6460,7 @@ dependencies = [
  "lazy_static",
  "sp-core",
  "sp-runtime",
- "strum",
+ "strum 0.16.0",
 ]
 
 [[package]]
@@ -6799,9 +6803,15 @@ version = "0.16.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "6138f8f88a16d90134763314e3fc76fa3ed6a7db4725d6acf9a3ef95a3188d22"
 dependencies = [
- "strum_macros",
+ "strum_macros 0.16.0",
 ]
 
+[[package]]
+name = "strum"
+version = "0.19.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3924a58d165da3b7b2922c667ab0673c7b5fd52b5c19ea3442747bcb3cd15abe"
+
 [[package]]
 name = "strum_macros"
 version = "0.16.0"
@@ -6814,6 +6824,18 @@ dependencies = [
  "syn 1.0.17",
 ]
 
+[[package]]
+name = "strum_macros"
+version = "0.19.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2d2ab682ecdcae7f5f45ae85cd7c1e6c8e68ea42c8a612d47fedf831c037146a"
+dependencies = [
+ "heck",
+ "proc-macro2",
+ "quote 1.0.7",
+ "syn 1.0.17",
+]
+
 [[package]]
 name = "substrate-bip39"
 version = "0.4.1"

+ 4 - 0
runtime-modules/common/Cargo.toml

@@ -6,6 +6,8 @@ edition = '2018'
 
 [dependencies]
 serde = { version = "1.0.101", optional = true, features = ["derive"] }
+strum = {version = "0.19", optional = true}
+strum_macros = {version = "0.19", optional = true}
 codec = { package = 'parity-scale-codec', version = '1.3.1', default-features = false, features = ['derive'] }
 sp-runtime = { package = 'sp-runtime', default-features = false, git = 'https://github.com/paritytech/substrate.git', rev = '00768a1f21a579c478fe5d4f51e1fa71f7db9fd4'}
 frame-support = { package = 'frame-support', default-features = false, git = 'https://github.com/paritytech/substrate.git', rev = '00768a1f21a579c478fe5d4f51e1fa71f7db9fd4'}
@@ -16,6 +18,8 @@ pallet-timestamp = { package = 'pallet-timestamp', default-features = false, git
 default = ['std']
 std = [
 	'serde',
+	'strum',
+	'strum_macros',
 	'codec/std',
 	'sp-runtime/std',
 	'frame-support/std',

+ 3 - 1
runtime-modules/common/src/working_group.rs

@@ -1,9 +1,11 @@
 use codec::{Decode, Encode};
 #[cfg(feature = "std")]
 use serde::{Deserialize, Serialize};
+#[cfg(feature = "std")]
+use strum_macros::EnumIter;
 
 /// Defines well-known working groups.
-#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
+#[cfg_attr(feature = "std", derive(Serialize, Deserialize, EnumIter))]
 #[derive(Encode, Decode, Clone, PartialEq, Eq, Copy, Debug)]
 pub enum WorkingGroup {
     /* Reserved

+ 1 - 1
runtime-modules/governance/src/mock.rs

@@ -71,7 +71,7 @@ impl election::Trait for Test {
 }
 impl membership::Trait for Test {
     type Event = ();
-    type MemberId = u32;
+    type MemberId = u64;
     type SubscriptionId = u32;
     type PaidTermId = u32;
     type ActorId = u32;

+ 1 - 1
runtime-modules/membership/src/mock.rs

@@ -80,7 +80,7 @@ impl GovernanceCurrency for Test {
 
 impl Trait for Test {
     type Event = ();
-    type MemberId = u32;
+    type MemberId = u64;
     type PaidTermId = u32;
     type SubscriptionId = u32;
     type ActorId = u32;

+ 1 - 1
runtime-modules/membership/src/tests.rs

@@ -5,7 +5,7 @@ use super::mock::*;
 
 use frame_support::*;
 
-fn get_membership_by_id(member_id: u32) -> crate::Membership<Test> {
+fn get_membership_by_id(member_id: u64) -> crate::Membership<Test> {
     if <crate::MembershipById<Test>>::contains_key(member_id) {
         Members::membership(member_id)
     } else {

+ 1 - 0
runtime-modules/proposals/codex/Cargo.toml

@@ -31,6 +31,7 @@ sp-core = { package = 'sp-core', default-features = false, git = 'https://github
 sp-staking = { package = 'sp-staking', default-features = false, git = 'https://github.com/paritytech/substrate.git', rev = '00768a1f21a579c478fe5d4f51e1fa71f7db9fd4'}
 pallet-staking-reward-curve = { package = 'pallet-staking-reward-curve', default-features = false, git = 'https://github.com/paritytech/substrate.git', rev = '00768a1f21a579c478fe5d4f51e1fa71f7db9fd4'}
 recurring-rewards = { package = 'pallet-recurring-reward', default-features = false, path = '../../recurring-reward'}
+strum = {version = "0.19", default-features = false}
 
 [features]
 default = ['std']

+ 8 - 0
runtime-modules/proposals/codex/src/tests/mock.rs

@@ -157,6 +157,9 @@ impl governance::election::Trait for Test {
 // The content directory working group instance alias.
 pub type ContentDirectoryWorkingGroupInstance = working_group::Instance3;
 
+// The storage working group instance alias.
+pub type StorageWorkingGroupInstance = working_group::Instance2;
+
 parameter_types! {
     pub const MaxWorkerNumberLimit: u32 = 100;
 }
@@ -166,6 +169,11 @@ impl working_group::Trait<ContentDirectoryWorkingGroupInstance> for Test {
     type MaxWorkerNumberLimit = MaxWorkerNumberLimit;
 }
 
+impl working_group::Trait<StorageWorkingGroupInstance> for Test {
+    type Event = ();
+    type MaxWorkerNumberLimit = MaxWorkerNumberLimit;
+}
+
 impl recurring_rewards::Trait for Test {
     type PayoutStatusHandler = ();
     type RecipientId = u64;

+ 130 - 38
runtime-modules/proposals/codex/src/tests/mod.rs

@@ -16,6 +16,8 @@ use crate::*;
 use crate::{BalanceOf, Error, ProposalDetails};
 pub use mock::*;
 
+use strum::IntoEnumIterator;
+
 pub(crate) fn increase_total_balance_issuance(balance: u64) {
     increase_total_balance_issuance_using_account_id(999, balance);
 }
@@ -764,12 +766,21 @@ fn set_default_proposal_parameters_succeeded() {
 
 #[test]
 fn create_add_working_group_leader_opening_proposal_common_checks_succeed() {
+    // This uses strum crate for enum iteration
+    for group in WorkingGroup::iter() {
+        run_create_add_working_group_leader_opening_proposal_common_checks_succeed(group);
+    }
+}
+
+fn run_create_add_working_group_leader_opening_proposal_common_checks_succeed(
+    working_group: WorkingGroup,
+) {
     initial_test_ext().execute_with(|| {
         let add_opening_parameters = AddOpeningParameters {
             activate_at: ActivateOpeningAt::CurrentBlock,
             commitment: OpeningPolicyCommitment::default(),
             human_readable_text: b"some text".to_vec(),
-            working_group: WorkingGroup::Content,
+            working_group,
         };
 
         increase_total_balance_issuance_using_account_id(1, 500000);
@@ -826,6 +837,17 @@ fn create_add_working_group_leader_opening_proposal_common_checks_succeed() {
 
 #[test]
 fn create_begin_review_working_group_leader_applications_proposal_common_checks_succeed() {
+    // This uses strum crate for enum iteration
+    for group in WorkingGroup::iter() {
+        run_create_begin_review_working_group_leader_applications_proposal_common_checks_succeed(
+            group,
+        );
+    }
+}
+
+fn run_create_begin_review_working_group_leader_applications_proposal_common_checks_succeed(
+    working_group: WorkingGroup,
+) {
     initial_test_ext().execute_with(|| {
         let opening_id = 1; // random opening id.
 
@@ -840,7 +862,7 @@ fn create_begin_review_working_group_leader_applications_proposal_common_checks_
                     b"body".to_vec(),
                     None,
                     opening_id,
-                    WorkingGroup::Content
+                    working_group
                 )
             },
             empty_stake_call: || {
@@ -851,7 +873,7 @@ fn create_begin_review_working_group_leader_applications_proposal_common_checks_
                     b"body".to_vec(),
                     None,
                     opening_id,
-                    WorkingGroup::Content
+                    working_group
                 )
             },
             invalid_stake_call: || {
@@ -862,7 +884,7 @@ fn create_begin_review_working_group_leader_applications_proposal_common_checks_
                     b"body".to_vec(),
                     Some(<BalanceOf<Test>>::from(5000u32)),
                     opening_id,
-                    WorkingGroup::Content
+                    working_group
                 )
             },
             successful_call: || {
@@ -873,14 +895,14 @@ fn create_begin_review_working_group_leader_applications_proposal_common_checks_
                     b"body".to_vec(),
                     Some(<BalanceOf<Test>>::from(25000u32)),
                     opening_id,
-                    WorkingGroup::Content
+                    working_group
                 )
             },
             proposal_parameters: crate::proposal_types::parameters::begin_review_working_group_leader_applications_proposal::<
                 Test,
             >(),
             proposal_details: ProposalDetails::BeginReviewWorkingGroupLeaderApplications(opening_id,
-                WorkingGroup::Content),
+                working_group),
         };
         proposal_fixture.check_all();
     });
@@ -888,6 +910,15 @@ fn create_begin_review_working_group_leader_applications_proposal_common_checks_
 
 #[test]
 fn create_fill_working_group_leader_opening_proposal_common_checks_succeed() {
+    // This uses strum crate for enum iteration
+    for group in WorkingGroup::iter() {
+        run_create_fill_working_group_leader_opening_proposal_common_checks_succeed(group);
+    }
+}
+
+fn run_create_fill_working_group_leader_opening_proposal_common_checks_succeed(
+    working_group: WorkingGroup,
+) {
     initial_test_ext().execute_with(|| {
         let opening_id = 1; // random opening id.
 
@@ -895,7 +926,7 @@ fn create_fill_working_group_leader_opening_proposal_common_checks_succeed() {
             opening_id,
             successful_application_id: 1,
             reward_policy: None,
-            working_group: WorkingGroup::Content,
+            working_group,
         };
 
         increase_total_balance_issuance_using_account_id(1, 500000);
@@ -952,6 +983,15 @@ fn create_fill_working_group_leader_opening_proposal_common_checks_succeed() {
 
 #[test]
 fn create_working_group_mint_capacity_proposal_fails_with_invalid_parameters() {
+    // This uses strum crate for enum iteration
+    for group in WorkingGroup::iter() {
+        run_create_working_group_mint_capacity_proposal_fails_with_invalid_parameters(group);
+    }
+}
+
+fn run_create_working_group_mint_capacity_proposal_fails_with_invalid_parameters(
+    working_group: WorkingGroup,
+) {
     initial_test_ext().execute_with(|| {
         increase_total_balance_issuance_using_account_id(1, 500000);
 
@@ -963,7 +1003,7 @@ fn create_working_group_mint_capacity_proposal_fails_with_invalid_parameters() {
                 b"body".to_vec(),
                 Some(<BalanceOf<Test>>::from(50000u32)),
                 (crate::WORKING_GROUP_MINT_CAPACITY_MAX_VALUE + 1) as u64,
-                WorkingGroup::Content,
+                working_group,
             ),
             Err(Error::<Test>::InvalidWorkingGroupMintCapacity.into())
         );
@@ -972,6 +1012,15 @@ fn create_working_group_mint_capacity_proposal_fails_with_invalid_parameters() {
 
 #[test]
 fn create_set_working_group_mint_capacity_proposal_common_checks_succeed() {
+    // This uses strum crate for enum iteration
+    for group in WorkingGroup::iter() {
+        run_create_set_working_group_mint_capacity_proposal_common_checks_succeed(group);
+    }
+}
+
+fn run_create_set_working_group_mint_capacity_proposal_common_checks_succeed(
+    working_group: WorkingGroup,
+) {
     initial_test_ext().execute_with(|| {
         increase_total_balance_issuance(500000);
 
@@ -984,7 +1033,7 @@ fn create_set_working_group_mint_capacity_proposal_common_checks_succeed() {
                     b"body".to_vec(),
                     None,
                     0,
-                    WorkingGroup::Content,
+                    working_group,
                 )
             },
             empty_stake_call: || {
@@ -995,7 +1044,7 @@ fn create_set_working_group_mint_capacity_proposal_common_checks_succeed() {
                     b"body".to_vec(),
                     None,
                     0,
-                    WorkingGroup::Content,
+                    working_group,
                 )
             },
             invalid_stake_call: || {
@@ -1006,7 +1055,7 @@ fn create_set_working_group_mint_capacity_proposal_common_checks_succeed() {
                     b"body".to_vec(),
                     Some(<BalanceOf<Test>>::from(5000u32)),
                     0,
-                    WorkingGroup::Content,
+                    working_group,
                 )
             },
             successful_call: || {
@@ -1017,16 +1066,13 @@ fn create_set_working_group_mint_capacity_proposal_common_checks_succeed() {
                     b"body".to_vec(),
                     Some(<BalanceOf<Test>>::from(50000u32)),
                     10,
-                    WorkingGroup::Content,
+                    working_group,
                 )
             },
             proposal_parameters:
                 crate::proposal_types::parameters::set_working_group_mint_capacity_proposal::<Test>(
                 ),
-            proposal_details: ProposalDetails::SetWorkingGroupMintCapacity(
-                10,
-                WorkingGroup::Content,
-            ),
+            proposal_details: ProposalDetails::SetWorkingGroupMintCapacity(10, working_group),
         };
         proposal_fixture.check_all();
     });
@@ -1034,6 +1080,15 @@ fn create_set_working_group_mint_capacity_proposal_common_checks_succeed() {
 
 #[test]
 fn create_decrease_working_group_leader_stake_proposal_common_checks_succeed() {
+    // This uses strum crate for enum iteration
+    for group in WorkingGroup::iter() {
+        run_create_decrease_working_group_leader_stake_proposal_common_checks_succeed(group);
+    }
+}
+
+fn run_create_decrease_working_group_leader_stake_proposal_common_checks_succeed(
+    working_group: WorkingGroup,
+) {
     initial_test_ext().execute_with(|| {
         increase_total_balance_issuance(500000);
 
@@ -1047,7 +1102,7 @@ fn create_decrease_working_group_leader_stake_proposal_common_checks_succeed() {
                     None,
                     0,
                     10,
-                    WorkingGroup::Content,
+                    working_group,
                 )
             },
             empty_stake_call: || {
@@ -1059,7 +1114,7 @@ fn create_decrease_working_group_leader_stake_proposal_common_checks_succeed() {
                     None,
                     0,
                     10,
-                    WorkingGroup::Content,
+                    working_group,
                 )
             },
             invalid_stake_call: || {
@@ -1071,7 +1126,7 @@ fn create_decrease_working_group_leader_stake_proposal_common_checks_succeed() {
                     Some(<BalanceOf<Test>>::from(5000u32)),
                     0,
                     10,
-                    WorkingGroup::Content,
+                    working_group,
                 )
             },
             successful_call: || {
@@ -1083,7 +1138,7 @@ fn create_decrease_working_group_leader_stake_proposal_common_checks_succeed() {
                     Some(<BalanceOf<Test>>::from(50000u32)),
                     10,
                     10,
-                    WorkingGroup::Content,
+                    working_group,
                 )
             },
             proposal_parameters:
@@ -1093,7 +1148,7 @@ fn create_decrease_working_group_leader_stake_proposal_common_checks_succeed() {
             proposal_details: ProposalDetails::DecreaseWorkingGroupLeaderStake(
                 10,
                 10,
-                WorkingGroup::Content,
+                working_group,
             ),
         };
         proposal_fixture.check_all();
@@ -1102,6 +1157,15 @@ fn create_decrease_working_group_leader_stake_proposal_common_checks_succeed() {
 
 #[test]
 fn create_slash_working_group_leader_stake_proposal_common_checks_succeed() {
+    // This uses strum crate for enum iteration
+    for group in WorkingGroup::iter() {
+        run_create_slash_working_group_leader_stake_proposal_common_checks_succeed(group);
+    }
+}
+
+fn run_create_slash_working_group_leader_stake_proposal_common_checks_succeed(
+    working_group: WorkingGroup,
+) {
     initial_test_ext().execute_with(|| {
         increase_total_balance_issuance(500000);
 
@@ -1115,7 +1179,7 @@ fn create_slash_working_group_leader_stake_proposal_common_checks_succeed() {
                     None,
                     0,
                     10,
-                    WorkingGroup::Content,
+                    working_group,
                 )
             },
             empty_stake_call: || {
@@ -1127,7 +1191,7 @@ fn create_slash_working_group_leader_stake_proposal_common_checks_succeed() {
                     None,
                     0,
                     10,
-                    WorkingGroup::Content,
+                    working_group,
                 )
             },
             invalid_stake_call: || {
@@ -1139,7 +1203,7 @@ fn create_slash_working_group_leader_stake_proposal_common_checks_succeed() {
                     Some(<BalanceOf<Test>>::from(5000u32)),
                     0,
                     10,
-                    WorkingGroup::Content,
+                    working_group,
                 )
             },
             successful_call: || {
@@ -1151,7 +1215,7 @@ fn create_slash_working_group_leader_stake_proposal_common_checks_succeed() {
                     Some(<BalanceOf<Test>>::from(50000u32)),
                     10,
                     10,
-                    WorkingGroup::Content,
+                    working_group,
                 )
             },
             proposal_parameters:
@@ -1161,7 +1225,7 @@ fn create_slash_working_group_leader_stake_proposal_common_checks_succeed() {
             proposal_details: ProposalDetails::SlashWorkingGroupLeaderStake(
                 10,
                 10,
-                WorkingGroup::Content,
+                working_group,
             ),
         };
         proposal_fixture.check_all();
@@ -1170,6 +1234,13 @@ fn create_slash_working_group_leader_stake_proposal_common_checks_succeed() {
 
 #[test]
 fn slash_stake_with_zero_staking_balance_fails() {
+    // This uses strum crate for enum iteration
+    for group in WorkingGroup::iter() {
+        run_slash_stake_with_zero_staking_balance_fails(group);
+    }
+}
+
+fn run_slash_stake_with_zero_staking_balance_fails(working_group: WorkingGroup) {
     initial_test_ext().execute_with(|| {
         increase_total_balance_issuance_using_account_id(1, 500000);
 
@@ -1189,7 +1260,7 @@ fn slash_stake_with_zero_staking_balance_fails() {
                 Some(<BalanceOf<Test>>::from(50000u32)),
                 10,
                 0,
-                WorkingGroup::Content,
+                working_group,
             ),
             Err(Error::<Test>::SlashingStakeIsZero.into())
         );
@@ -1198,6 +1269,13 @@ fn slash_stake_with_zero_staking_balance_fails() {
 
 #[test]
 fn decrease_stake_with_zero_staking_balance_fails() {
+    // This uses strum crate for enum iteration
+    for group in WorkingGroup::iter() {
+        run_decrease_stake_with_zero_staking_balance_fails(group);
+    }
+}
+
+fn run_decrease_stake_with_zero_staking_balance_fails(working_group: WorkingGroup) {
     initial_test_ext().execute_with(|| {
         increase_total_balance_issuance_using_account_id(1, 500000);
 
@@ -1217,7 +1295,7 @@ fn decrease_stake_with_zero_staking_balance_fails() {
                 Some(<BalanceOf<Test>>::from(50000u32)),
                 10,
                 0,
-                WorkingGroup::Content,
+                working_group,
             ),
             Err(Error::<Test>::DecreasingStakeIsZero.into())
         );
@@ -1226,6 +1304,15 @@ fn decrease_stake_with_zero_staking_balance_fails() {
 
 #[test]
 fn create_set_working_group_leader_reward_proposal_common_checks_succeed() {
+    // This uses strum crate for enum iteration
+    for group in WorkingGroup::iter() {
+        run_create_set_working_group_leader_reward_proposal_common_checks_succeed(group);
+    }
+}
+
+fn run_create_set_working_group_leader_reward_proposal_common_checks_succeed(
+    working_group: WorkingGroup,
+) {
     initial_test_ext().execute_with(|| {
         let proposal_fixture = ProposalTestFixture {
             insufficient_rights_call: || {
@@ -1237,7 +1324,7 @@ fn create_set_working_group_leader_reward_proposal_common_checks_succeed() {
                     None,
                     0,
                     10,
-                    WorkingGroup::Content,
+                    working_group,
                 )
             },
             empty_stake_call: || {
@@ -1249,7 +1336,7 @@ fn create_set_working_group_leader_reward_proposal_common_checks_succeed() {
                     None,
                     0,
                     10,
-                    WorkingGroup::Content,
+                    working_group,
                 )
             },
             invalid_stake_call: || {
@@ -1261,7 +1348,7 @@ fn create_set_working_group_leader_reward_proposal_common_checks_succeed() {
                     Some(<BalanceOf<Test>>::from(5000u32)),
                     0,
                     10,
-                    WorkingGroup::Content,
+                    working_group,
                 )
             },
             successful_call: || {
@@ -1273,17 +1360,13 @@ fn create_set_working_group_leader_reward_proposal_common_checks_succeed() {
                     Some(<BalanceOf<Test>>::from(50000u32)),
                     10,
                     10,
-                    WorkingGroup::Content,
+                    working_group,
                 )
             },
             proposal_parameters:
                 crate::proposal_types::parameters::set_working_group_leader_reward_proposal::<Test>(
                 ),
-            proposal_details: ProposalDetails::SetWorkingGroupLeaderReward(
-                10,
-                10,
-                WorkingGroup::Content,
-            ),
+            proposal_details: ProposalDetails::SetWorkingGroupLeaderReward(10, 10, working_group),
         };
         proposal_fixture.check_all();
     });
@@ -1291,6 +1374,15 @@ fn create_set_working_group_leader_reward_proposal_common_checks_succeed() {
 
 #[test]
 fn create_terminate_working_group_leader_role_proposal_common_checks_succeed() {
+    // This uses strum crate for enum iteration
+    for group in WorkingGroup::iter() {
+        run_create_terminate_working_group_leader_role_proposal_common_checks_succeed(group);
+    }
+}
+
+fn run_create_terminate_working_group_leader_role_proposal_common_checks_succeed(
+    working_group: WorkingGroup,
+) {
     initial_test_ext().execute_with(|| {
         increase_total_balance_issuance(500000);
 
@@ -1298,7 +1390,7 @@ fn create_terminate_working_group_leader_role_proposal_common_checks_succeed() {
             worker_id: 10,
             rationale: Vec::new(),
             slash: false,
-            working_group: WorkingGroup::Content,
+            working_group,
         };
 
         let proposal_fixture = ProposalTestFixture {

+ 1 - 1
runtime/Cargo.toml

@@ -82,7 +82,7 @@ content-directory = { package = 'pallet-content-directory', default-features = f
 
 [dev-dependencies]
 sp-io = { package = 'sp-io', default-features = false, git = 'https://github.com/paritytech/substrate.git', rev = '00768a1f21a579c478fe5d4f51e1fa71f7db9fd4'}
-
+strum = {version = "0.19", default-features = false}
 [build-dependencies]
 wasm-builder-runner = { package = "substrate-wasm-builder-runner", git = 'https://github.com/paritytech/substrate.git', rev = '00768a1f21a579c478fe5d4f51e1fa71f7db9fd4' }
 

+ 1 - 0
runtime/src/integration/content_working_group.rs

@@ -73,6 +73,7 @@ impl versioned_store_permissions::CredentialChecker<Runtime> for ContentWorkingG
     }
 }
 
+#[allow(dead_code)]
 pub struct ContentWorkingGroupStakingEventHandler {}
 impl stake::StakingEventsHandler<Runtime> for ContentWorkingGroupStakingEventHandler {
     fn unstaked(

+ 47 - 3
runtime/src/integration/working_group.rs

@@ -1,15 +1,15 @@
 use frame_support::StorageMap;
 use sp_std::marker::PhantomData;
 
-use crate::ContentDirectoryWorkingGroupInstance;
+use crate::{ContentDirectoryWorkingGroupInstance, StorageWorkingGroupInstance};
 use stake::{BalanceOf, NegativeImbalance};
 
-pub struct StakingEventsHandler<T> {
+pub struct ContentDirectoryWGStakingEventsHandler<T> {
     pub marker: PhantomData<T>,
 }
 
 impl<T: stake::Trait + working_group::Trait<ContentDirectoryWorkingGroupInstance>>
-    stake::StakingEventsHandler<T> for StakingEventsHandler<T>
+    stake::StakingEventsHandler<T> for ContentDirectoryWGStakingEventsHandler<T>
 {
     /// Unstake remaining sum back to the source_account_id
     fn unstaked(
@@ -47,3 +47,47 @@ impl<T: stake::Trait + working_group::Trait<ContentDirectoryWorkingGroupInstance
         remaining_imbalance
     }
 }
+
+pub struct StorageWgStakingEventsHandler<T> {
+    pub marker: PhantomData<T>,
+}
+
+impl<T: stake::Trait + working_group::Trait<StorageWorkingGroupInstance>>
+    stake::StakingEventsHandler<T> for StorageWgStakingEventsHandler<T>
+{
+    /// Unstake remaining sum back to the source_account_id
+    fn unstaked(
+        stake_id: &<T as stake::Trait>::StakeId,
+        _unstaked_amount: BalanceOf<T>,
+        remaining_imbalance: NegativeImbalance<T>,
+    ) -> NegativeImbalance<T> {
+        // Stake not related to a staked role managed by the hiring module.
+        if !hiring::ApplicationIdByStakingId::<T>::contains_key(*stake_id) {
+            return remaining_imbalance;
+        }
+
+        let hiring_application_id = hiring::ApplicationIdByStakingId::<T>::get(*stake_id);
+
+        if working_group::MemberIdByHiringApplicationId::<T, StorageWorkingGroupInstance>::contains_key(
+            hiring_application_id,
+        ) {
+            return <working_group::Module<T, StorageWorkingGroupInstance>>::refund_working_group_stake(
+				*stake_id,
+				remaining_imbalance,
+			);
+        }
+
+        remaining_imbalance
+    }
+
+    /// Empty handler for the slashing.
+    fn slashed(
+        _: &<T as stake::Trait>::StakeId,
+        _: Option<<T as stake::Trait>::SlashId>,
+        _: BalanceOf<T>,
+        _: BalanceOf<T>,
+        remaining_imbalance: NegativeImbalance<T>,
+    ) -> NegativeImbalance<T> {
+        remaining_imbalance
+    }
+}

+ 3 - 5
runtime/src/lib.rs

@@ -453,10 +453,10 @@ impl stake::Trait for Runtime {
     type Currency = <Self as common::currency::GovernanceCurrency>::Currency;
     type StakePoolId = StakePoolId;
     type StakingEventsHandler = (
-        crate::integration::content_working_group::ContentWorkingGroupStakingEventHandler,
+        crate::integration::proposals::StakingEventsHandler<Self>,
         (
-            crate::integration::proposals::StakingEventsHandler<Self>,
-            crate::integration::working_group::StakingEventsHandler<Self>,
+            crate::integration::working_group::ContentDirectoryWGStakingEventsHandler<Self>,
+            crate::integration::working_group::StorageWgStakingEventsHandler<Self>,
         ),
     );
     type StakeId = u64;
@@ -509,8 +509,6 @@ impl storage::data_object_storage_registry::Trait for Runtime {
     type ContentIdExists = DataDirectory;
 }
 
-pub type MemberId = u64;
-
 impl membership::Trait for Runtime {
     type Event = Event;
     type MemberId = MemberId;

+ 3 - 0
runtime/src/primitives.rs

@@ -63,6 +63,9 @@ pub type PostId = u64;
 /// Represent an actor in membership group, which is the same in the working groups.
 pub type ActorId = u64;
 
+/// Represent an member in membership group, which is the same in the working groups.
+pub type MemberId = u64;
+
 /// App-specific crypto used for reporting equivocation/misbehavior in BABE and
 /// GRANDPA. Any rewards for misbehavior reporting will be paid out to this
 /// account.

+ 1 - 0
runtime/src/tests/mod.rs

@@ -1,6 +1,7 @@
 //! The Joystream Substrate Node runtime integration tests.
 
 #![cfg(test)]
+#[macro_use]
 
 mod proposals_integration;
 mod storage_integration;

+ 881 - 522
runtime/src/tests/proposals_integration/working_group_proposals.rs

@@ -1,3 +1,6 @@
+#![allow(unnameable_test_items)]
+#![allow(dead_code)]
+
 use super::*;
 
 use system::RawOrigin;
@@ -9,31 +12,51 @@ use working_group::{OpeningPolicyCommitment, RewardPolicy};
 
 use crate::{
     Balance, BlockNumber, ContentDirectoryWorkingGroup, ContentDirectoryWorkingGroupInstance,
+    StorageWorkingGroup, StorageWorkingGroupInstance,
 };
 use sp_std::collections::btree_set::BTreeSet;
 
+use crate::primitives::{ActorId, MemberId};
+use frame_support::traits;
+use strum::IntoEnumIterator;
+
+type WorkingGroupInstance<T, I> = working_group::Module<T, I>;
+
 type Hiring = hiring::Module<Runtime>;
 
 fn add_opening(
-    member_id: u8,
+    member_id: MemberId,
     account_id: [u8; 32],
     activate_at: hiring::ActivateOpeningAt<BlockNumber>,
     opening_policy_commitment: Option<OpeningPolicyCommitment<BlockNumber, u128>>,
     sequence_number: u32, // action sequence number to align with other actions
+    working_group: WorkingGroup,
 ) -> u64 {
     let expected_proposal_id = sequence_number;
     let run_to_block = sequence_number * 2;
 
-    let opening_id = ContentDirectoryWorkingGroup::next_opening_id();
-
-    assert!(!<working_group::OpeningById<
-        Runtime,
-        ContentDirectoryWorkingGroupInstance,
-    >>::contains_key(opening_id));
+    let opening_id = match working_group {
+        WorkingGroup::Content => {
+            let opening_id = ContentDirectoryWorkingGroup::next_opening_id();
+            assert!(!<working_group::OpeningById<
+                Runtime,
+                ContentDirectoryWorkingGroupInstance,
+            >>::contains_key(opening_id));
+            opening_id
+        }
+        WorkingGroup::Storage => {
+            let opening_id = StorageWorkingGroup::next_opening_id();
+            assert!(!<working_group::OpeningById<
+                Runtime,
+                StorageWorkingGroupInstance,
+            >>::contains_key(opening_id));
+            opening_id
+        }
+    };
 
     let codex_extrinsic_test_fixture = CodexProposalTestFixture::default_for_call(|| {
         ProposalCodex::create_add_working_group_leader_opening_proposal(
-            RawOrigin::Signed(account_id.clone().into()).into(),
+            RawOrigin::Signed(account_id.into()).into(),
             member_id as u64,
             b"title".to_vec(),
             b"body".to_vec(),
@@ -44,7 +67,7 @@ fn add_opening(
                     .clone()
                     .unwrap_or(OpeningPolicyCommitment::default()),
                 human_readable_text: Vec::new(),
-                working_group: WorkingGroup::Content,
+                working_group,
             },
         )
     })
@@ -57,23 +80,24 @@ fn add_opening(
 }
 
 fn begin_review_applications(
-    member_id: u8,
+    member_id: MemberId,
     account_id: [u8; 32],
     opening_id: u64,
     sequence_number: u32, // action sequence number to align with other actions
+    working_group: WorkingGroup,
 ) {
     let expected_proposal_id = sequence_number;
     let run_to_block = sequence_number * 2;
 
     let codex_extrinsic_test_fixture = CodexProposalTestFixture::default_for_call(|| {
         ProposalCodex::create_begin_review_working_group_leader_applications_proposal(
-            RawOrigin::Signed(account_id.clone().into()).into(),
-            member_id as u64,
+            RawOrigin::Signed(account_id.into()).into(),
+            member_id,
             b"title".to_vec(),
             b"body".to_vec(),
             Some(<BalanceOf<Runtime>>::from(25_000_u32)),
             opening_id,
-            WorkingGroup::Content,
+            working_group,
         )
     })
     .disable_setup_enviroment()
@@ -84,20 +108,21 @@ fn begin_review_applications(
 }
 
 fn fill_opening(
-    member_id: u8,
+    member_id: MemberId,
     account_id: [u8; 32],
     opening_id: u64,
     successful_application_id: u64,
     reward_policy: Option<RewardPolicy<Balance, BlockNumber>>,
     sequence_number: u32, // action sequence number to align with other actions
+    working_group: WorkingGroup,
 ) {
     let expected_proposal_id = sequence_number;
     let run_to_block = sequence_number * 2;
 
     let codex_extrinsic_test_fixture = CodexProposalTestFixture::default_for_call(|| {
         ProposalCodex::create_fill_working_group_leader_opening_proposal(
-            RawOrigin::Signed(account_id.clone().into()).into(),
-            member_id as u64,
+            RawOrigin::Signed(account_id.into()).into(),
+            member_id,
             b"title".to_vec(),
             b"body".to_vec(),
             Some(<BalanceOf<Runtime>>::from(50_000_u32)),
@@ -105,7 +130,7 @@ fn fill_opening(
                 opening_id,
                 successful_application_id,
                 reward_policy: reward_policy.clone(),
-                working_group: WorkingGroup::Content,
+                working_group,
             },
         )
     })
@@ -125,25 +150,26 @@ fn get_stake_balance(stake: stake::Stake<BlockNumber, Balance, u64>) -> Balance
 }
 
 fn decrease_stake(
-    member_id: u8,
+    member_id: u64,
     account_id: [u8; 32],
     leader_worker_id: u64,
     stake_amount: Balance,
     sequence_number: u32, // action sequence number to align with other actions
+    working_group: WorkingGroup,
 ) {
     let expected_proposal_id = sequence_number;
     let run_to_block = sequence_number * 2;
 
     let codex_extrinsic_test_fixture = CodexProposalTestFixture::default_for_call(|| {
         ProposalCodex::create_decrease_working_group_leader_stake_proposal(
-            RawOrigin::Signed(account_id.clone().into()).into(),
-            member_id as u64,
+            RawOrigin::Signed(account_id.into()).into(),
+            member_id,
             b"title".to_vec(),
             b"body".to_vec(),
             Some(<BalanceOf<Runtime>>::from(50_000_u32)),
             leader_worker_id,
             stake_amount,
-            WorkingGroup::Content,
+            working_group,
         )
     })
     .disable_setup_enviroment()
@@ -154,25 +180,26 @@ fn decrease_stake(
 }
 
 fn slash_stake(
-    member_id: u8,
+    member_id: MemberId,
     account_id: [u8; 32],
-    leader_worker_id: u64,
+    leader_worker_id: ActorId,
     stake_amount: Balance,
     sequence_number: u32, // action sequence number to align with other actions
+    working_group: WorkingGroup,
 ) {
     let expected_proposal_id = sequence_number;
     let run_to_block = sequence_number * 2;
 
     let codex_extrinsic_test_fixture = CodexProposalTestFixture::default_for_call(|| {
         ProposalCodex::create_slash_working_group_leader_stake_proposal(
-            RawOrigin::Signed(account_id.clone().into()).into(),
-            member_id as u64,
+            RawOrigin::Signed(account_id.into()).into(),
+            member_id,
             b"title".to_vec(),
             b"body".to_vec(),
             Some(<BalanceOf<Runtime>>::from(50_000_u32)),
             leader_worker_id,
             stake_amount,
-            WorkingGroup::Content,
+            working_group,
         )
     })
     .disable_setup_enviroment()
@@ -183,25 +210,26 @@ fn slash_stake(
 }
 
 fn set_reward(
-    member_id: u8,
+    member_id: MemberId,
     account_id: [u8; 32],
     leader_worker_id: u64,
     reward_amount: Balance,
     sequence_number: u32, // action sequence number to align with other actions
+    working_group: WorkingGroup,
 ) {
     let expected_proposal_id = sequence_number;
     let run_to_block = sequence_number * 2;
 
     let codex_extrinsic_test_fixture = CodexProposalTestFixture::default_for_call(|| {
         ProposalCodex::create_set_working_group_leader_reward_proposal(
-            RawOrigin::Signed(account_id.clone().into()).into(),
+            RawOrigin::Signed(account_id.into()).into(),
             member_id as u64,
             b"title".to_vec(),
             b"body".to_vec(),
             Some(<BalanceOf<Runtime>>::from(50_000_u32)),
             leader_worker_id,
             reward_amount,
-            WorkingGroup::Content,
+            working_group,
         )
     })
     .disable_setup_enviroment()
@@ -211,31 +239,38 @@ fn set_reward(
     codex_extrinsic_test_fixture.call_extrinsic_and_assert();
 }
 
-fn set_mint_capacity(
-    member_id: u8,
+fn set_mint_capacity<
+    T: working_group::Trait<I> + system::Trait + minting::Trait,
+    I: working_group::Instance,
+>(
+    member_id: MemberId,
     account_id: [u8; 32],
     mint_capacity: Balance,
     sequence_number: u32, // action sequence number to align with other actions
     setup_environment: bool,
-) {
+    working_group: WorkingGroup,
+) where
+    <T as minting::Trait>::MintId: From<u64>,
+{
     let expected_proposal_id = sequence_number;
     let run_to_block = sequence_number * 2;
 
     let mint_id_result = <minting::Module<Runtime>>::add_mint(0, None);
 
     if let Ok(mint_id) = mint_id_result {
-        <working_group::Mint<Runtime, ContentDirectoryWorkingGroupInstance>>::put(mint_id);
+        let mint_id: <T as minting::Trait>::MintId = mint_id.into();
+        <working_group::Mint<T, I>>::put(mint_id);
     }
 
     let codex_extrinsic_test_fixture = CodexProposalTestFixture::default_for_call(|| {
         ProposalCodex::create_set_working_group_mint_capacity_proposal(
-            RawOrigin::Signed(account_id.clone().into()).into(),
-            member_id as u64,
+            RawOrigin::Signed(account_id.into()).into(),
+            member_id,
             b"title".to_vec(),
             b"body".to_vec(),
             Some(<BalanceOf<Runtime>>::from(50_000_u32)),
             mint_capacity,
-            WorkingGroup::Content,
+            working_group,
         )
     })
     .with_setup_enviroment(setup_environment)
@@ -246,19 +281,20 @@ fn set_mint_capacity(
 }
 
 fn terminate_role(
-    member_id: u8,
+    member_id: MemberId,
     account_id: [u8; 32],
     leader_worker_id: u64,
     slash: bool,
     sequence_number: u32, // action sequence number to align with other actions
+    working_group: WorkingGroup,
 ) {
     let expected_proposal_id = sequence_number;
     let run_to_block = sequence_number * 2;
 
     let codex_extrinsic_test_fixture = CodexProposalTestFixture::default_for_call(|| {
         ProposalCodex::create_terminate_working_group_leader_role_proposal(
-            RawOrigin::Signed(account_id.clone().into()).into(),
-            member_id as u64,
+            RawOrigin::Signed(account_id.into()).into(),
+            member_id,
             b"title".to_vec(),
             b"body".to_vec(),
             Some(<BalanceOf<Runtime>>::from(100_000_u32)),
@@ -266,7 +302,7 @@ fn terminate_role(
                 worker_id: leader_worker_id,
                 rationale: Vec::new(),
                 slash,
-                working_group: WorkingGroup::Content,
+                working_group,
             },
         )
     })
@@ -279,53 +315,110 @@ fn terminate_role(
 
 #[test]
 fn create_add_working_group_leader_opening_proposal_execution_succeeds() {
+    // This uses strum crate for enum iteration
+    for group in WorkingGroup::iter() {
+        match group {
+            WorkingGroup::Content => {
+                run_create_add_working_group_leader_opening_proposal_execution_succeeds::<
+                    Runtime,
+                    ContentDirectoryWorkingGroupInstance,
+                >(group);
+            }
+            WorkingGroup::Storage => {
+                run_create_add_working_group_leader_opening_proposal_execution_succeeds::<
+                    Runtime,
+                    StorageWorkingGroupInstance,
+                >(group);
+            }
+        }
+    }
+}
+
+fn run_create_add_working_group_leader_opening_proposal_execution_succeeds<
+    T: working_group::Trait<I> + system::Trait + stake::Trait,
+    I: working_group::Instance,
+>(
+    working_group: WorkingGroup,
+) where
+    <T as membership::Trait>::MemberId: From<u64>,
+    <T as hiring::Trait>::OpeningId: From<u64>,
+{
     initial_test_ext().execute_with(|| {
-        let member_id = 1;
-        let account_id: [u8; 32] = [member_id; 32];
+        let member_id: MemberId = 1;
+        let account_id: [u8; 32] = [member_id as u8; 32];
 
-        let next_opening_id = ContentDirectoryWorkingGroup::next_opening_id();
+        let next_opening_id = WorkingGroupInstance::<T, I>::next_opening_id();
 
-        assert!(!<working_group::OpeningById<
-            Runtime,
-            ContentDirectoryWorkingGroupInstance,
-        >>::contains_key(next_opening_id));
+        assert!(!<working_group::OpeningById<T, I>>::contains_key(
+            next_opening_id
+        ));
 
-        let opening_id = add_opening(
+        let opening_id: <T as hiring::Trait>::OpeningId = add_opening(
             member_id,
             account_id,
             ActivateOpeningAt::CurrentBlock,
             None,
             1,
-        );
+            working_group,
+        )
+        .into();
 
         // Check for expected opening id.
         assert_eq!(opening_id, next_opening_id);
 
         // Check for the new opening creation.
-        assert!(<working_group::OpeningById<
-            Runtime,
-            ContentDirectoryWorkingGroupInstance,
-        >>::contains_key(opening_id));
+        assert!(<working_group::OpeningById<T, I>>::contains_key(opening_id));
     });
 }
 
 #[test]
 fn create_begin_review_working_group_leader_applications_proposal_execution_succeeds() {
+    // This uses strum crate for enum iteration
+    for group in WorkingGroup::iter() {
+        match group {
+            WorkingGroup::Content => {
+                run_create_begin_review_working_group_leader_applications_proposal_execution_succeeds::<
+                Runtime,
+                ContentDirectoryWorkingGroupInstance,
+            >(group);
+            }
+            WorkingGroup::Storage => {
+                run_create_begin_review_working_group_leader_applications_proposal_execution_succeeds::<
+                Runtime,
+                StorageWorkingGroupInstance,
+            >(group);
+            }
+        }
+    }
+}
+
+fn run_create_begin_review_working_group_leader_applications_proposal_execution_succeeds<
+    T: working_group::Trait<I> + system::Trait + stake::Trait,
+    I: working_group::Instance,
+>(
+    working_group: WorkingGroup,
+) where
+    <T as hiring::Trait>::OpeningId: From<u64> + Into<u64>,
+{
     initial_test_ext().execute_with(|| {
-        let member_id = 1;
-        let account_id: [u8; 32] = [member_id; 32];
+        let member_id: MemberId = 1;
+        let account_id: [u8; 32] = [member_id as u8; 32];
 
         let opening_id = add_opening(
             member_id,
-            account_id.clone(),
+            account_id,
             ActivateOpeningAt::CurrentBlock,
             None,
             1,
+            working_group,
         );
 
-        let opening = ContentDirectoryWorkingGroup::opening_by_id(opening_id);
+        let opening = WorkingGroupInstance::<T, I>::opening_by_id(
+            <T as hiring::Trait>::OpeningId::from(opening_id),
+        );
 
-        let hiring_opening = Hiring::opening_by_id(opening.hiring_opening_id);
+        let hiring_opening_id: u64 = opening.hiring_opening_id.into();
+        let hiring_opening = Hiring::opening_by_id(hiring_opening_id);
         assert_eq!(
             hiring_opening.stage,
             hiring::OpeningStage::Active {
@@ -339,9 +432,9 @@ fn create_begin_review_working_group_leader_applications_proposal_execution_succ
             }
         );
 
-        begin_review_applications(member_id, account_id, opening_id, 2);
+        begin_review_applications(member_id, account_id, opening_id, 2, working_group);
 
-        let hiring_opening = Hiring::opening_by_id(opening.hiring_opening_id);
+        let hiring_opening = Hiring::opening_by_id(hiring_opening_id);
         assert_eq!(
             hiring_opening.stage,
             hiring::OpeningStage::Active {
@@ -360,475 +453,741 @@ fn create_begin_review_working_group_leader_applications_proposal_execution_succ
 
 #[test]
 fn create_fill_working_group_leader_opening_proposal_execution_succeeds() {
-    initial_test_ext().execute_with(|| {
-        let member_id = 1;
-        let account_id: [u8; 32] = [member_id; 32];
-
-        let opening_id = add_opening(
-            member_id,
-            account_id.clone(),
-            ActivateOpeningAt::CurrentBlock,
-            None,
-            1,
-        );
-
-        let apply_result = ContentDirectoryWorkingGroup::apply_on_opening(
-            RawOrigin::Signed(account_id.clone().into()).into(),
-            member_id as u64,
-            opening_id,
-            account_id.clone().into(),
-            None,
-            None,
-            Vec::new(),
-        );
-
-        assert_eq!(apply_result, Ok(()));
-
-        let expected_application_id = 0;
-
-        begin_review_applications(member_id, account_id, opening_id, 2);
-
-        let lead = ContentDirectoryWorkingGroup::current_lead();
-        assert!(lead.is_none());
-
-        fill_opening(
-            member_id,
-            account_id,
-            opening_id,
-            expected_application_id,
-            None,
-            3,
-        );
-
-        let lead = ContentDirectoryWorkingGroup::current_lead();
-        assert!(lead.is_some());
-    });
-}
-
-#[test]
-fn create_decrease_group_leader_stake_proposal_execution_succeeds() {
-    initial_test_ext().execute_with(|| {
-        let member_id = 1;
-        let account_id: [u8; 32] = [member_id; 32];
-        let stake_amount = 100;
-
-        let opening_policy_commitment = OpeningPolicyCommitment {
-            role_staking_policy: Some(hiring::StakingPolicy {
-                amount: 100,
-                amount_mode: hiring::StakingAmountLimitMode::AtLeast,
-                crowded_out_unstaking_period_length: None,
-                review_period_expired_unstaking_period_length: None,
-            }),
-            ..OpeningPolicyCommitment::default()
-        };
-
-        let opening_id = add_opening(
-            member_id,
-            account_id.clone(),
-            ActivateOpeningAt::CurrentBlock,
-            Some(opening_policy_commitment),
-            1,
-        );
-
-        let apply_result = ContentDirectoryWorkingGroup::apply_on_opening(
-            RawOrigin::Signed(account_id.clone().into()).into(),
-            member_id as u64,
-            opening_id,
-            account_id.clone().into(),
-            Some(stake_amount),
-            None,
-            Vec::new(),
-        );
-
-        assert_eq!(apply_result, Ok(()));
-
-        let expected_application_id = 0;
-
-        begin_review_applications(member_id, account_id, opening_id, 2);
-
-        let lead = ContentDirectoryWorkingGroup::current_lead();
-        assert!(lead.is_none());
-
-        fill_opening(
-            member_id,
-            account_id,
-            opening_id,
-            expected_application_id,
-            None,
-            3,
-        );
-
-        let leader_worker_id = ContentDirectoryWorkingGroup::current_lead().unwrap();
-
-        let stake_id = 1;
-        let old_balance = Balances::free_balance(&account_id.into());
-        let old_stake = <stake::Module<Runtime>>::stakes(stake_id);
-
-        assert_eq!(get_stake_balance(old_stake), stake_amount);
-
-        let decreasing_stake_amount = 30;
-        decrease_stake(
-            member_id,
-            account_id,
-            leader_worker_id,
-            decreasing_stake_amount,
-            4,
-        );
-
-        let new_balance = Balances::free_balance(&account_id.into());
-        let new_stake = <stake::Module<Runtime>>::stakes(stake_id);
-
-        assert_eq!(
-            get_stake_balance(new_stake),
-            stake_amount - decreasing_stake_amount
-        );
-        assert_eq!(new_balance, old_balance + decreasing_stake_amount);
-    });
-}
-
-#[test]
-fn create_slash_group_leader_stake_proposal_execution_succeeds() {
-    initial_test_ext().execute_with(|| {
-        let member_id = 1;
-        let account_id: [u8; 32] = [member_id; 32];
-        let stake_amount = 100;
-
-        let opening_policy_commitment = OpeningPolicyCommitment {
-            role_staking_policy: Some(hiring::StakingPolicy {
-                amount: 100,
-                amount_mode: hiring::StakingAmountLimitMode::AtLeast,
-                crowded_out_unstaking_period_length: None,
-                review_period_expired_unstaking_period_length: None,
-            }),
-            ..OpeningPolicyCommitment::default()
-        };
-
-        let opening_id = add_opening(
-            member_id,
-            account_id.clone(),
-            ActivateOpeningAt::CurrentBlock,
-            Some(opening_policy_commitment),
-            1,
-        );
-
-        let apply_result = ContentDirectoryWorkingGroup::apply_on_opening(
-            RawOrigin::Signed(account_id.clone().into()).into(),
-            member_id as u64,
-            opening_id,
-            account_id.clone().into(),
-            Some(stake_amount),
-            None,
-            Vec::new(),
-        );
-
-        assert_eq!(apply_result, Ok(()));
-
-        let expected_application_id = 0;
-
-        begin_review_applications(member_id, account_id, opening_id, 2);
-
-        let lead = ContentDirectoryWorkingGroup::current_lead();
-        assert!(lead.is_none());
-
-        fill_opening(
-            member_id,
-            account_id,
-            opening_id,
-            expected_application_id,
-            None,
-            3,
-        );
-
-        let leader_worker_id = ContentDirectoryWorkingGroup::current_lead().unwrap();
-
-        let stake_id = 1;
-        let old_balance = Balances::free_balance(&account_id.into());
-        let old_stake = <stake::Module<Runtime>>::stakes(stake_id);
-
-        assert_eq!(get_stake_balance(old_stake), stake_amount);
-
-        let slashing_stake_amount = 30;
-        slash_stake(
-            member_id,
-            account_id,
-            leader_worker_id,
-            slashing_stake_amount,
-            4,
-        );
-
-        let new_balance = Balances::free_balance(&account_id.into());
-        let new_stake = <stake::Module<Runtime>>::stakes(stake_id);
-
-        assert_eq!(
-            get_stake_balance(new_stake),
-            stake_amount - slashing_stake_amount
-        );
-        assert_eq!(new_balance, old_balance);
-    });
-}
-
-#[test]
-fn create_set_working_group_mint_capacity_proposal_execution_succeeds() {
-    initial_test_ext().execute_with(|| {
-        let member_id = 1;
-        let account_id: [u8; 32] = [member_id; 32];
-
-        assert_eq!(ContentDirectoryWorkingGroup::mint(), 0);
-
-        let mint_capacity = 999999;
-        set_mint_capacity(member_id, account_id, mint_capacity, 1, true);
-
-        let mint_id = ContentDirectoryWorkingGroup::mint();
-        let mint = <minting::Module<Runtime>>::mints(mint_id);
-
-        assert_eq!(mint.capacity(), mint_capacity);
-    });
-}
-
-#[test]
-fn create_set_group_leader_reward_proposal_execution_succeeds() {
-    initial_test_ext().execute_with(|| {
-        let member_id = 1;
-        let account_id: [u8; 32] = [member_id; 32];
-        let stake_amount = 100;
-
-        let opening_policy_commitment = OpeningPolicyCommitment {
-            role_staking_policy: Some(hiring::StakingPolicy {
-                amount: 100,
-                amount_mode: hiring::StakingAmountLimitMode::AtLeast,
-                crowded_out_unstaking_period_length: None,
-                review_period_expired_unstaking_period_length: None,
-            }),
-            ..OpeningPolicyCommitment::default()
-        };
-
-        let opening_id = add_opening(
-            member_id,
-            account_id.clone(),
-            ActivateOpeningAt::CurrentBlock,
-            Some(opening_policy_commitment),
-            1,
-        );
-
-        let apply_result = ContentDirectoryWorkingGroup::apply_on_opening(
-            RawOrigin::Signed(account_id.clone().into()).into(),
-            member_id as u64,
-            opening_id,
-            account_id.clone().into(),
-            Some(stake_amount),
-            None,
-            Vec::new(),
-        );
-
-        assert_eq!(apply_result, Ok(()));
-
-        let expected_application_id = 0;
-
-        begin_review_applications(member_id, account_id, opening_id, 2);
-
-        let lead = ContentDirectoryWorkingGroup::current_lead();
-        assert!(lead.is_none());
+    // This uses strum crate for enum iteration
+    for group in WorkingGroup::iter() {
+        match group {
+            WorkingGroup::Content => {
+                run_create_fill_working_group_leader_opening_proposal_execution_succeeds::<
+                    Runtime,
+                    ContentDirectoryWorkingGroupInstance,
+                >(group);
+            }
+            WorkingGroup::Storage => {
+                run_create_fill_working_group_leader_opening_proposal_execution_succeeds::<
+                    Runtime,
+                    StorageWorkingGroupInstance,
+                >(group);
+            }
+        }
+    }
 
-        let old_reward_amount = 100;
-        let reward_policy = Some(RewardPolicy {
-            amount_per_payout: old_reward_amount,
-            next_payment_at_block: 9999,
-            payout_interval: None,
+    fn run_create_fill_working_group_leader_opening_proposal_execution_succeeds<
+        T: working_group::Trait<I> + system::Trait + stake::Trait,
+        I: working_group::Instance,
+    >(
+        working_group: WorkingGroup,
+    ) where
+        <T as system::Trait>::AccountId: From<[u8; 32]>,
+        <T as membership::Trait>::MemberId: From<u64>,
+        <T as hiring::Trait>::OpeningId: From<u64>,
+    {
+        initial_test_ext().execute_with(|| {
+            let member_id: MemberId = 1;
+            let account_id: [u8; 32] = [member_id as u8; 32];
+
+            let opening_id = add_opening(
+                member_id,
+                account_id,
+                ActivateOpeningAt::CurrentBlock,
+                None,
+                1,
+                working_group,
+            );
+
+            let apply_result = WorkingGroupInstance::<T, I>::apply_on_opening(
+                RawOrigin::Signed(account_id.into()).into(),
+                member_id.into(),
+                opening_id.into(),
+                account_id.into(),
+                None,
+                None,
+                Vec::new(),
+            );
+
+            assert_eq!(apply_result, Ok(()));
+
+            let expected_application_id = 0;
+
+            begin_review_applications(member_id, account_id, opening_id, 2, working_group);
+
+            let lead = WorkingGroupInstance::<T, I>::current_lead();
+            assert!(lead.is_none());
+
+            fill_opening(
+                member_id,
+                account_id,
+                opening_id,
+                expected_application_id,
+                None,
+                3,
+                working_group,
+            );
+
+            let lead = WorkingGroupInstance::<T, I>::current_lead();
+            assert!(lead.is_some());
         });
+    }
 
-        set_mint_capacity(member_id, account_id, 999999, 3, false);
-
-        fill_opening(
-            member_id,
-            account_id,
-            opening_id,
-            expected_application_id,
-            reward_policy,
-            4,
-        );
-
-        let leader_worker_id = ContentDirectoryWorkingGroup::current_lead().unwrap();
-
-        let worker = ContentDirectoryWorkingGroup::worker_by_id(leader_worker_id);
-        let relationship_id = worker.reward_relationship.unwrap();
-
-        let relationship = recurring_rewards::RewardRelationships::<Runtime>::get(relationship_id);
-        assert_eq!(relationship.amount_per_payout, old_reward_amount);
-
-        let new_reward_amount = 999;
-        set_reward(
-            member_id,
-            account_id,
-            leader_worker_id,
-            new_reward_amount,
-            5,
-        );
-
-        let relationship = recurring_rewards::RewardRelationships::<Runtime>::get(relationship_id);
-        assert_eq!(relationship.amount_per_payout, new_reward_amount);
-    });
-}
-
-#[test]
-fn create_terminate_group_leader_role_proposal_execution_succeeds() {
-    initial_test_ext().execute_with(|| {
-        let member_id = 1;
-        let account_id: [u8; 32] = [member_id; 32];
-        let stake_amount = 100;
-
-        let opening_policy_commitment = OpeningPolicyCommitment {
-            role_staking_policy: Some(hiring::StakingPolicy {
-                amount: 100,
-                amount_mode: hiring::StakingAmountLimitMode::AtLeast,
-                crowded_out_unstaking_period_length: None,
-                review_period_expired_unstaking_period_length: None,
-            }),
-            ..OpeningPolicyCommitment::default()
-        };
-
-        let opening_id = add_opening(
-            member_id,
-            account_id.clone(),
-            ActivateOpeningAt::CurrentBlock,
-            Some(opening_policy_commitment),
-            1,
-        );
-
-        let apply_result = ContentDirectoryWorkingGroup::apply_on_opening(
-            RawOrigin::Signed(account_id.clone().into()).into(),
-            member_id as u64,
-            opening_id,
-            account_id.clone().into(),
-            Some(stake_amount),
-            None,
-            Vec::new(),
-        );
-
-        assert_eq!(apply_result, Ok(()));
-
-        let expected_application_id = 0;
-
-        begin_review_applications(member_id, account_id, opening_id, 2);
-
-        let lead = ContentDirectoryWorkingGroup::current_lead();
-        assert!(lead.is_none());
+    #[test]
+    fn create_decrease_group_leader_stake_proposal_execution_succeeds() {
+        // This uses strum crate for enum iteration
+        for group in WorkingGroup::iter() {
+            match group {
+                WorkingGroup::Content => {
+                    run_create_decrease_group_leader_stake_proposal_execution_succeeds::<
+                        Runtime,
+                        ContentDirectoryWorkingGroupInstance,
+                    >(group);
+                }
+                WorkingGroup::Storage => {
+                    run_create_decrease_group_leader_stake_proposal_execution_succeeds::<
+                        Runtime,
+                        StorageWorkingGroupInstance,
+                    >(group);
+                }
+            }
+        }
+    }
 
-        let old_reward_amount = 100;
-        let reward_policy = Some(RewardPolicy {
-            amount_per_payout: old_reward_amount,
-            next_payment_at_block: 9999,
-            payout_interval: None,
+fn run_create_decrease_group_leader_stake_proposal_execution_succeeds<
+    T: working_group::Trait<I> + system::Trait + stake::Trait,
+    I: working_group::Instance,
+>(
+    working_group: WorkingGroup,
+) where
+    <T as system::Trait>::AccountId: From<[u8; 32]>,
+    <T as hiring::Trait>::OpeningId: From<u64>,
+    <T as membership::Trait>::MemberId: From<u64>,
+    <T as membership::Trait>::ActorId: Into<u64>,
+    <<T as stake::Trait>::Currency as traits::Currency<<T as system::Trait>::AccountId>>::Balance:
+        From<u128>,
+{
+        initial_test_ext().execute_with(|| {
+            let member_id: MemberId = 1;
+            let account_id: [u8; 32] = [member_id as u8; 32];
+            let stake_amount: Balance = 100;
+
+            let opening_policy_commitment = OpeningPolicyCommitment {
+                role_staking_policy: Some(hiring::StakingPolicy {
+                    amount: 100,
+                    amount_mode: hiring::StakingAmountLimitMode::AtLeast,
+                    crowded_out_unstaking_period_length: None,
+                    review_period_expired_unstaking_period_length: None,
+                }),
+                ..OpeningPolicyCommitment::default()
+            };
+
+            let opening_id = add_opening(
+                member_id,
+                account_id,
+                ActivateOpeningAt::CurrentBlock,
+                Some(opening_policy_commitment),
+                1,
+                working_group,
+            );
+
+            let apply_result = WorkingGroupInstance::<T, I>::apply_on_opening(
+                RawOrigin::Signed(account_id.into()).into(),
+                member_id.into(),
+                opening_id.into(),
+                account_id.into(),
+                Some(stake_amount.into()),
+                None,
+                Vec::new(),
+            );
+
+            assert_eq!(apply_result, Ok(()));
+
+            let expected_application_id = 0;
+
+            begin_review_applications(member_id, account_id, opening_id, 2, working_group);
+
+            let lead = WorkingGroupInstance::<T, I>::current_lead();
+            assert!(lead.is_none());
+
+            fill_opening(
+                member_id,
+                account_id,
+                opening_id,
+                expected_application_id,
+                None,
+                3,
+                working_group,
+            );
+
+            let leader_worker_id = WorkingGroupInstance::<T, I>::current_lead().unwrap();
+
+            let stake_id = 1;
+            let old_balance = Balances::free_balance(&account_id.into());
+            let old_stake = <stake::Module<Runtime>>::stakes(stake_id);
+
+            assert_eq!(get_stake_balance(old_stake), stake_amount);
+
+            let decreasing_stake_amount = 30;
+            decrease_stake(
+                member_id,
+                account_id,
+                leader_worker_id.into(),
+                decreasing_stake_amount,
+                4,
+                working_group,
+            );
+
+            let new_balance = Balances::free_balance(&account_id.into());
+            let new_stake = <stake::Module<Runtime>>::stakes(stake_id);
+
+            assert_eq!(
+                get_stake_balance(new_stake),
+                stake_amount - decreasing_stake_amount
+            );
+            assert_eq!(new_balance, old_balance + decreasing_stake_amount);
         });
+    }
 
-        set_mint_capacity(member_id, account_id, 999999, 3, false);
-
-        fill_opening(
-            member_id,
-            account_id,
-            opening_id,
-            expected_application_id,
-            reward_policy,
-            4,
-        );
-
-        let leader_worker_id = ContentDirectoryWorkingGroup::current_lead().unwrap();
-
-        let stake_id = 1;
-        let old_balance = Balances::free_balance(&account_id.into());
-        let old_stake = <stake::Module<Runtime>>::stakes(stake_id);
-
-        assert_eq!(get_stake_balance(old_stake), stake_amount);
-
-        terminate_role(member_id, account_id, leader_worker_id, false, 5);
-
-        assert!(ContentDirectoryWorkingGroup::current_lead().is_none());
-
-        let new_balance = Balances::free_balance(&account_id.into());
-        let new_stake = <stake::Module<Runtime>>::stakes(stake_id);
-
-        assert_eq!(new_stake.staking_status, stake::StakingStatus::NotStaked);
-        assert_eq!(new_balance, old_balance + stake_amount);
-    });
-}
-
-#[test]
-fn create_terminate_group_leader_role_proposal_with_slashing_execution_succeeds() {
-    initial_test_ext().execute_with(|| {
-        let member_id = 1;
-        let account_id: [u8; 32] = [member_id; 32];
-        let stake_amount = 100;
-
-        let opening_policy_commitment = OpeningPolicyCommitment {
-            role_staking_policy: Some(hiring::StakingPolicy {
-                amount: 100,
-                amount_mode: hiring::StakingAmountLimitMode::AtLeast,
-                crowded_out_unstaking_period_length: None,
-                review_period_expired_unstaking_period_length: None,
-            }),
-            ..OpeningPolicyCommitment::default()
-        };
-
-        let opening_id = add_opening(
-            member_id,
-            account_id.clone(),
-            ActivateOpeningAt::CurrentBlock,
-            Some(opening_policy_commitment),
-            1,
-        );
-
-        let apply_result = ContentDirectoryWorkingGroup::apply_on_opening(
-            RawOrigin::Signed(account_id.clone().into()).into(),
-            member_id as u64,
-            opening_id,
-            account_id.clone().into(),
-            Some(stake_amount),
-            None,
-            Vec::new(),
-        );
-
-        assert_eq!(apply_result, Ok(()));
-
-        let expected_application_id = 0;
-
-        begin_review_applications(member_id, account_id, opening_id, 2);
-
-        let lead = ContentDirectoryWorkingGroup::current_lead();
-        assert!(lead.is_none());
+    #[test]
+    fn create_slash_group_leader_stake_proposal_execution_succeeds() {
+        // This uses strum crate for enum iteration
+        for group in WorkingGroup::iter() {
+            match group {
+                WorkingGroup::Content => {
+                    run_create_slash_group_leader_stake_proposal_execution_succeeds::<
+                        Runtime,
+                        ContentDirectoryWorkingGroupInstance,
+                    >(group)
+                }
+                WorkingGroup::Storage => {
+                    run_create_slash_group_leader_stake_proposal_execution_succeeds::<
+                        Runtime,
+                        StorageWorkingGroupInstance,
+                    >(group)
+                }
+            }
+        }
+    }
 
-        let old_reward_amount = 100;
-        let reward_policy = Some(RewardPolicy {
-            amount_per_payout: old_reward_amount,
-            next_payment_at_block: 9999,
-            payout_interval: None,
+fn run_create_slash_group_leader_stake_proposal_execution_succeeds<
+    T: working_group::Trait<I> + system::Trait + stake::Trait,
+    I: working_group::Instance,
+>(
+    working_group: WorkingGroup,
+) where
+    <T as system::Trait>::AccountId: From<[u8; 32]>,
+    <T as hiring::Trait>::OpeningId: From<u64>,
+    <T as membership::Trait>::MemberId: From<u64>,
+    <T as membership::Trait>::ActorId: Into<u64>,
+    <<T as stake::Trait>::Currency as traits::Currency<<T as system::Trait>::AccountId>>::Balance:
+        From<u128>,
+{
+        initial_test_ext().execute_with(|| {
+            let member_id: MemberId = 1;
+            let account_id: [u8; 32] = [member_id as u8; 32];
+            let stake_amount: Balance = 100;
+
+            let opening_policy_commitment = OpeningPolicyCommitment {
+                role_staking_policy: Some(hiring::StakingPolicy {
+                    amount: 100,
+                    amount_mode: hiring::StakingAmountLimitMode::AtLeast,
+                    crowded_out_unstaking_period_length: None,
+                    review_period_expired_unstaking_period_length: None,
+                }),
+                ..OpeningPolicyCommitment::default()
+            };
+
+            let opening_id = add_opening(
+                member_id,
+                account_id,
+                ActivateOpeningAt::CurrentBlock,
+                Some(opening_policy_commitment),
+                1,
+                working_group,
+            );
+
+            let apply_result = WorkingGroupInstance::<T, I>::apply_on_opening(
+                RawOrigin::Signed(account_id.into()).into(),
+                member_id.into(),
+                opening_id.into(),
+                account_id.into(),
+                Some(stake_amount.into()),
+                None,
+                Vec::new(),
+            );
+
+            assert_eq!(apply_result, Ok(()));
+
+            let expected_application_id = 0;
+
+            begin_review_applications(member_id, account_id, opening_id, 2, working_group);
+
+            let lead = WorkingGroupInstance::<T, I>::current_lead();
+
+            assert!(lead.is_none());
+
+            fill_opening(
+                member_id,
+                account_id,
+                opening_id,
+                expected_application_id,
+                None,
+                3,
+                working_group,
+            );
+
+            let leader_worker_id = WorkingGroupInstance::<T, I>::current_lead().unwrap();
+
+            let stake_id = 1;
+            let old_balance = Balances::free_balance(&account_id.into());
+            let old_stake = <stake::Module<Runtime>>::stakes(stake_id);
+
+            assert_eq!(get_stake_balance(old_stake), stake_amount);
+
+            let slashing_stake_amount = 30;
+            slash_stake(
+                member_id,
+                account_id,
+                leader_worker_id.into(),
+                slashing_stake_amount,
+                4,
+                working_group,
+            );
+
+            let new_balance = Balances::free_balance(&account_id.into());
+            let new_stake = <stake::Module<Runtime>>::stakes(stake_id);
+
+            assert_eq!(
+                get_stake_balance(new_stake),
+                stake_amount as u128 - slashing_stake_amount
+            );
+            assert_eq!(new_balance, old_balance);
         });
+    }
 
-        set_mint_capacity(member_id, account_id, 999999, 3, false);
-
-        fill_opening(
-            member_id,
-            account_id,
-            opening_id,
-            expected_application_id,
-            reward_policy,
-            4,
-        );
-
-        let leader_worker_id = ContentDirectoryWorkingGroup::current_lead().unwrap();
-
-        let stake_id = 1;
-        let old_balance = Balances::free_balance(&account_id.into());
-        let old_stake = <stake::Module<Runtime>>::stakes(stake_id);
-
-        assert_eq!(get_stake_balance(old_stake), stake_amount);
-
-        terminate_role(member_id, account_id, leader_worker_id, true, 5);
-
-        assert!(ContentDirectoryWorkingGroup::current_lead().is_none());
-
-        let new_balance = Balances::free_balance(&account_id.into());
-        let new_stake = <stake::Module<Runtime>>::stakes(stake_id);
-
-        assert_eq!(new_stake.staking_status, stake::StakingStatus::NotStaked);
-        assert_eq!(new_balance, old_balance);
-    });
+    #[test]
+    fn create_set_working_group_mint_capacity_proposal_execution_succeeds() {
+        // This uses strum crate for enum iteration
+        for group in WorkingGroup::iter() {
+            match group {
+                WorkingGroup::Content => {
+                    run_create_set_working_group_mint_capacity_proposal_execution_succeeds::<
+                        Runtime,
+                        ContentDirectoryWorkingGroupInstance,
+                    >(group);
+                }
+                WorkingGroup::Storage => {
+                    run_create_set_working_group_mint_capacity_proposal_execution_succeeds::<
+                        Runtime,
+                        StorageWorkingGroupInstance,
+                    >(group);
+                }
+            }
+        }
+
+        fn run_create_set_working_group_mint_capacity_proposal_execution_succeeds<
+            T: working_group::Trait<I> + system::Trait + minting::Trait,
+            I: working_group::Instance,
+        >(
+            working_group: WorkingGroup,
+        ) where
+            <T as system::Trait>::AccountId: From<[u8; 32]>,
+            <T as membership::Trait>::MemberId: From<u64>,
+            <T as minting::Trait>::MintId: From<u64>,
+            <<T as minting::Trait>::Currency as traits::Currency<
+                <T as system::Trait>::AccountId,
+            >>::Balance: From<u128>,
+        {
+            initial_test_ext().execute_with(|| {
+                let member_id: MemberId = 1;
+                let account_id: [u8; 32] = [member_id as u8; 32];
+
+                assert_eq!(WorkingGroupInstance::<T, I>::mint(), 0.into());
+
+                let mint_capacity = 999999;
+                set_mint_capacity::<T, I>(
+                    member_id,
+                    account_id,
+                    mint_capacity,
+                    1,
+                    true,
+                    working_group,
+                );
+
+                let mint_id = WorkingGroupInstance::<T, I>::mint();
+                let mint = <minting::Module<T>>::mints(mint_id);
+
+                assert_eq!(mint.capacity(), mint_capacity.into());
+            });
+        }
+
+        #[test]
+        fn create_set_group_leader_reward_proposal_execution_succeeds() {
+            // This uses strum crate for enum iteration
+            for group in WorkingGroup::iter() {
+                match group {
+                    WorkingGroup::Content => {
+                        run_create_set_working_group_mint_capacity_proposal_execution_succeeds::<
+                            Runtime,
+                            ContentDirectoryWorkingGroupInstance,
+                        >(group);
+                    }
+                    WorkingGroup::Storage => {
+                        run_create_set_working_group_mint_capacity_proposal_execution_succeeds::<
+                            Runtime,
+                            StorageWorkingGroupInstance,
+                        >(group);
+                    }
+                }
+            }
+        }
+
+        fn run_create_set_group_leader_reward_proposal_execution_succeeds<
+            T: working_group::Trait<I> + system::Trait + minting::Trait,
+            I: working_group::Instance,
+        >(
+            working_group: WorkingGroup,
+        ) where
+            <T as system::Trait>::AccountId: From<[u8; 32]>,
+            <T as membership::Trait>::MemberId: From<u64>,
+            <T as membership::Trait>::ActorId: Into<u64>,
+            <T as minting::Trait>::MintId: From<u64>,
+            <T as hiring::Trait>::OpeningId: From<u64>,
+            <<T as minting::Trait>::Currency as traits::Currency<
+                <T as system::Trait>::AccountId,
+            >>::Balance: From<u128>,
+        {
+            initial_test_ext().execute_with(|| {
+                let member_id: MemberId = 1;
+                let account_id: [u8; 32] = [member_id as u8; 32];
+                let stake_amount = 100;
+
+                let opening_policy_commitment = OpeningPolicyCommitment {
+                    role_staking_policy: Some(hiring::StakingPolicy {
+                        amount: 100,
+                        amount_mode: hiring::StakingAmountLimitMode::AtLeast,
+                        crowded_out_unstaking_period_length: None,
+                        review_period_expired_unstaking_period_length: None,
+                    }),
+                    ..OpeningPolicyCommitment::default()
+                };
+
+                let opening_id = add_opening(
+                    member_id,
+                    account_id,
+                    ActivateOpeningAt::CurrentBlock,
+                    Some(opening_policy_commitment),
+                    1,
+                    working_group,
+                );
+
+                let apply_result = WorkingGroupInstance::<T, I>::apply_on_opening(
+                    RawOrigin::Signed(account_id.into()).into(),
+                    member_id.into(),
+                    opening_id.into(),
+                    account_id.into(),
+                    Some(stake_amount.into()),
+                    None,
+                    Vec::new(),
+                );
+
+                assert_eq!(apply_result, Ok(()));
+
+                let expected_application_id = 0;
+
+                begin_review_applications(member_id, account_id, opening_id, 2, working_group);
+
+                let lead = WorkingGroupInstance::<T, I>::current_lead();
+                assert!(lead.is_none());
+
+                let old_reward_amount = 100;
+                let reward_policy = Some(RewardPolicy {
+                    amount_per_payout: old_reward_amount,
+                    next_payment_at_block: 9999,
+                    payout_interval: None,
+                });
+
+                set_mint_capacity::<T, I>(member_id, account_id, 999999, 3, false, working_group);
+
+                fill_opening(
+                    member_id,
+                    account_id,
+                    opening_id,
+                    expected_application_id,
+                    reward_policy,
+                    4,
+                    working_group,
+                );
+
+                let leader_worker_id = WorkingGroupInstance::<T, I>::current_lead().unwrap();
+
+                let worker = WorkingGroupInstance::<T, I>::worker_by_id(leader_worker_id);
+                let relationship_id = worker.reward_relationship.unwrap();
+
+                let relationship =
+                    recurring_rewards::RewardRelationships::<T>::get(relationship_id);
+                assert_eq!(relationship.amount_per_payout, old_reward_amount.into());
+
+                let new_reward_amount = 999;
+                set_reward(
+                    member_id,
+                    account_id,
+                    leader_worker_id.into(),
+                    new_reward_amount,
+                    5,
+                    working_group,
+                );
+
+                let relationship =
+                    recurring_rewards::RewardRelationships::<T>::get(relationship_id);
+                assert_eq!(relationship.amount_per_payout, new_reward_amount.into());
+            });
+        }
+
+        #[test]
+        fn create_terminate_group_leader_role_proposal_execution_succeeds() {
+            // This uses strum crate for enum iteration
+            for group in WorkingGroup::iter() {
+                match group {
+                    WorkingGroup::Content => {
+                        run_create_terminate_group_leader_role_proposal_execution_succeeds::<
+                            Runtime,
+                            ContentDirectoryWorkingGroupInstance,
+                        >(group);
+                    }
+                    WorkingGroup::Storage => {
+                        run_create_terminate_group_leader_role_proposal_execution_succeeds::<
+                            Runtime,
+                            StorageWorkingGroupInstance,
+                        >(group);
+                    }
+                }
+            }
+        }
+
+        fn run_create_terminate_group_leader_role_proposal_execution_succeeds<
+            T: working_group::Trait<I> + system::Trait + minting::Trait,
+            I: working_group::Instance,
+        >(
+            working_group: WorkingGroup,
+        ) where
+            <T as system::Trait>::AccountId: From<[u8; 32]>,
+            <T as membership::Trait>::MemberId: From<u64>,
+            <T as membership::Trait>::ActorId: Into<u64>,
+            <T as minting::Trait>::MintId: From<u64>,
+            <T as hiring::Trait>::OpeningId: From<u64>,
+            <<T as stake::Trait>::Currency as traits::Currency<
+                <T as system::Trait>::AccountId,
+            >>::Balance: From<u128>,
+        {
+            initial_test_ext().execute_with(|| {
+                let member_id: MemberId = 1;
+                let account_id: [u8; 32] = [0; 32];
+                let stake_amount = 100_u128;
+
+                let opening_policy_commitment = OpeningPolicyCommitment {
+                    role_staking_policy: Some(hiring::StakingPolicy {
+                        amount: 100,
+                        amount_mode: hiring::StakingAmountLimitMode::AtLeast,
+                        crowded_out_unstaking_period_length: None,
+                        review_period_expired_unstaking_period_length: None,
+                    }),
+                    ..OpeningPolicyCommitment::default()
+                };
+
+                let opening_id = add_opening(
+                    member_id.into(),
+                    account_id,
+                    ActivateOpeningAt::CurrentBlock,
+                    Some(opening_policy_commitment),
+                    1,
+                    working_group,
+                );
+
+                let apply_result = WorkingGroupInstance::<T, I>::apply_on_opening(
+                    RawOrigin::Signed(account_id.into()).into(),
+                    member_id.into(),
+                    opening_id.into(),
+                    account_id.into(),
+                    Some(stake_amount.into()),
+                    None,
+                    Vec::new(),
+                );
+
+                assert_eq!(apply_result, Ok(()));
+
+                let expected_application_id = 0;
+
+                begin_review_applications(member_id, account_id, opening_id, 2, working_group);
+
+                let lead = WorkingGroupInstance::<T, I>::current_lead();
+                assert!(lead.is_none());
+
+                let old_reward_amount = 100;
+                let reward_policy = Some(RewardPolicy {
+                    amount_per_payout: old_reward_amount,
+                    next_payment_at_block: 9999,
+                    payout_interval: None,
+                });
+
+                set_mint_capacity::<T, I>(member_id, account_id, 999999, 3, false, working_group);
+
+                fill_opening(
+                    member_id,
+                    account_id,
+                    opening_id,
+                    expected_application_id,
+                    reward_policy,
+                    4,
+                    working_group,
+                );
+
+                let stake_id = 1;
+                let old_balance = Balances::free_balance(&account_id.into());
+                let old_stake = <stake::Module<Runtime>>::stakes(stake_id);
+
+                assert_eq!(get_stake_balance(old_stake), stake_amount);
+
+                let leader_worker_id = WorkingGroupInstance::<T, I>::current_lead().unwrap();
+
+                terminate_role(
+                    member_id,
+                    account_id,
+                    leader_worker_id.into(),
+                    false,
+                    5,
+                    working_group,
+                );
+
+                assert!(WorkingGroupInstance::<T, I>::current_lead().is_none());
+
+                let new_balance = Balances::free_balance(&account_id.into());
+                let new_stake = <stake::Module<Runtime>>::stakes(stake_id);
+
+                assert_eq!(new_stake.staking_status, stake::StakingStatus::NotStaked);
+                assert_eq!(new_balance, old_balance + stake_amount);
+            });
+        }
+
+        #[test]
+        fn create_terminate_group_leader_role_proposal_with_slashing_execution_succeeds() {
+            // This uses strum crate for enum iteration
+            for group in WorkingGroup::iter() {
+                match group {
+                    WorkingGroup::Content => {
+                        run_create_terminate_group_leader_role_proposal_with_slashing_execution_succeeds::<Runtime, ContentDirectoryWorkingGroupInstance>(group);
+                    }
+                    WorkingGroup::Storage => {
+                        run_create_terminate_group_leader_role_proposal_with_slashing_execution_succeeds::<Runtime, StorageWorkingGroupInstance>(group);
+                    }
+                }
+            }
+        }
+
+        fn run_create_terminate_group_leader_role_proposal_with_slashing_execution_succeeds<
+            T: working_group::Trait<I> + system::Trait + minting::Trait,
+            I: working_group::Instance,
+        >(
+            working_group: WorkingGroup,
+        ) where
+            <T as system::Trait>::AccountId: From<[u8; 32]>,
+            <T as membership::Trait>::MemberId: From<u64>,
+            <T as membership::Trait>::ActorId: Into<u64>,
+            <T as minting::Trait>::MintId: From<u64>,
+            <T as hiring::Trait>::OpeningId: From<u64>,
+            <<T as stake::Trait>::Currency as traits::Currency<
+                <T as system::Trait>::AccountId,
+            >>::Balance: From<u128>,
+        {
+            initial_test_ext().execute_with(|| {
+                let member_id: MemberId = 1;
+                let account_id: [u8; 32] = [0; 32];
+                let stake_amount = 100_u128;
+
+                let opening_policy_commitment = OpeningPolicyCommitment {
+                    role_staking_policy: Some(hiring::StakingPolicy {
+                        amount: 100,
+                        amount_mode: hiring::StakingAmountLimitMode::AtLeast,
+                        crowded_out_unstaking_period_length: None,
+                        review_period_expired_unstaking_period_length: None,
+                    }),
+                    ..OpeningPolicyCommitment::default()
+                };
+
+                let opening_id = add_opening(
+                    member_id,
+                    account_id,
+                    ActivateOpeningAt::CurrentBlock,
+                    Some(opening_policy_commitment),
+                    1,
+                    working_group,
+                );
+
+                let apply_result = WorkingGroupInstance::<T, I>::apply_on_opening(
+                    RawOrigin::Signed(account_id.into()).into(),
+                    member_id.into(),
+                    opening_id.into(),
+                    account_id.into(),
+                    Some(stake_amount.into()),
+                    None,
+                    Vec::new(),
+                );
+
+                assert_eq!(apply_result, Ok(()));
+
+                let expected_application_id = 0;
+
+                begin_review_applications(
+                    member_id,
+                    account_id,
+                    opening_id.into(),
+                    2,
+                    working_group,
+                );
+
+                let lead = WorkingGroupInstance::<T, I>::current_lead();
+                assert!(lead.is_none());
+
+                let old_reward_amount = 100;
+                let reward_policy = Some(RewardPolicy {
+                    amount_per_payout: old_reward_amount,
+                    next_payment_at_block: 9999,
+                    payout_interval: None,
+                });
+
+                set_mint_capacity::<T, I>(member_id, account_id, 999999, 3, false, working_group);
+
+                fill_opening(
+                    member_id,
+                    account_id,
+                    opening_id,
+                    expected_application_id,
+                    reward_policy,
+                    4,
+                    working_group,
+                );
+
+                let stake_id = 1;
+                let old_balance = Balances::free_balance(&account_id.into());
+                let old_stake = <stake::Module<Runtime>>::stakes(stake_id);
+
+                assert_eq!(get_stake_balance(old_stake), stake_amount);
+
+                let leader_worker_id = WorkingGroupInstance::<T, I>::current_lead().unwrap();
+
+                terminate_role(
+                    member_id,
+                    account_id,
+                    leader_worker_id.into(),
+                    true,
+                    5,
+                    working_group,
+                );
+
+                assert!(WorkingGroupInstance::<T, I>::current_lead().is_none());
+
+                let new_balance = Balances::free_balance(&account_id.into());
+                let new_stake = <stake::Module<Runtime>>::stakes(stake_id);
+
+                assert_eq!(new_stake.staking_status, stake::StakingStatus::NotStaked);
+                assert_eq!(new_balance, old_balance);
+            });
+        }
+    }
 }