Browse Source

Add withdraw_curator_application() with tests

Shamil Gadelshin 4 years ago
parent
commit
75afdfae1b

+ 2 - 0
runtime-modules/bureaucracy/src/errors.rs

@@ -20,6 +20,8 @@ pub mod bureaucracy_errors {
     pub static MSG_CURATOR_APPLICATION_TEXT_TOO_SHORT: &str = "Curator application text too short";
     pub static MSG_INSUFFICIENT_BALANCE_TO_COVER_STAKE: &str =
         "Insuffieicnt balance to cover stake";
+    pub static MSG_ORIGIN_IS_NOT_APPLICANT: &str = "Origin is not applicant";
+    pub static MSG_CURATOR_APPLICATION_DOES_NOT_EXIST: &str = "Curator application does not exist";
 }
 /*
  * The errors below, while in many cases encoding similar outcomes,

+ 80 - 3
runtime-modules/bureaucracy/src/lib.rs

@@ -21,12 +21,15 @@ use errors::bureaucracy_errors::*;
 use errors::WrappedError;
 use types::{CuratorApplication, CuratorOpening, Lead, OpeningPolicyCommitment};
 
+//TODO: docs
+//TODO: migrate to decl_error
+
 /*
 + add_curator_opening
 + accept_curator_applications
 - begin_curator_applicant_review
 - fill_curator_opening
-- withdraw_curator_application
++ withdraw_curator_application
 - terminate_curator_application
 + apply_on_curator_opening
 */
@@ -62,7 +65,7 @@ pub trait Trait<I: Instance>: system::Trait + membership::members::Trait + hirin
 }
 
 // Type simplification
-type CuratorOpeningData<T> = (
+type CuratorOpeningInfo<T> = (
     CuratorOpening<
         <T as hiring::Trait>::OpeningId,
         <T as system::Trait>::BlockNumber,
@@ -76,6 +79,23 @@ type CuratorOpeningData<T> = (
     >,
 );
 
+// Type simplification
+type CuratorApplicationInfo<T> = (
+    CuratorApplication<
+        <T as system::Trait>::AccountId,
+        CuratorOpeningId<T>,
+        <T as membership::members::Trait>::MemberId,
+        <T as hiring::Trait>::ApplicationId,
+    >,
+    CuratorApplicationId<T>,
+    CuratorOpening<
+        <T as hiring::Trait>::OpeningId,
+        <T as system::Trait>::BlockNumber,
+        BalanceOf<T>,
+        CuratorApplicationId<T>,
+    >,
+);
+
 decl_event!(
     /// Proposals engine events
     pub enum Event<T, I>
@@ -103,6 +123,10 @@ decl_event!(
         /// - Curator opening id
         /// - Curator application id
         AppliedOnCuratorOpening(CuratorOpeningId, CuratorApplicationId),
+        /// Emits on withdrawing the application for the curator opening.
+        /// Params:
+        /// - Curator application id
+        CuratorApplicationWithdrawn(CuratorApplicationId),
     }
 );
 
@@ -321,6 +345,39 @@ decl_module! {
             // Trigger event
             Self::deposit_event(RawEvent::AppliedOnCuratorOpening(curator_opening_id, new_curator_application_id));
         }
+
+        pub fn withdraw_curator_application(
+            origin,
+            curator_application_id: CuratorApplicationId<T>
+        ) {
+            // Ensuring curator application actually exists
+            let (curator_application, _, curator_opening) = Self::ensure_curator_application_exists(&curator_application_id)?;
+
+            // Ensure that it is signed
+            let signer_account = ensure_signed(origin)?;
+
+            // Ensure that signer is applicant role account
+            ensure!(
+                signer_account == curator_application.role_account,
+                MSG_ORIGIN_IS_NOT_APPLICANT
+            );
+
+            // Attempt to deactivate application
+            // NB: Combined ensure check and mutation in hiring module
+            ensure_on_wrapped_error!(
+                hiring::Module::<T>::deactive_application(
+                    curator_application.application_id,
+                    curator_opening.policy_commitment.exit_curator_role_application_stake_unstaking_period,
+                    curator_opening.policy_commitment.exit_curator_role_stake_unstaking_period
+                )
+            )?;
+
+            // mutation
+
+            // Trigger event
+            Self::deposit_event(RawEvent::CuratorApplicationWithdrawn(curator_application_id));
+
+        }
     }
 }
 
@@ -375,7 +432,7 @@ impl<T: Trait<I>, I: Instance> Module<T, I> {
 
     fn ensure_curator_opening_exists(
         curator_opening_id: &CuratorOpeningId<T>,
-    ) -> Result<CuratorOpeningData<T>, &'static str> {
+    ) -> Result<CuratorOpeningInfo<T>, &'static str> {
         ensure!(
             CuratorOpeningById::<T, I>::exists(curator_opening_id),
             MSG_CURATOR_OPENING_DOES_NOT_EXIST
@@ -477,4 +534,24 @@ impl<T: Trait<I>, I: Instance> Module<T, I> {
             Ok(())
         }
     }
+
+    fn ensure_curator_application_exists(
+        curator_application_id: &CuratorApplicationId<T>,
+    ) -> Result<CuratorApplicationInfo<T>, &'static str> {
+        ensure!(
+            CuratorApplicationById::<T, I>::exists(curator_application_id),
+            MSG_CURATOR_APPLICATION_DOES_NOT_EXIST
+        );
+
+        let curator_application = CuratorApplicationById::<T, I>::get(curator_application_id);
+
+        let curator_opening =
+            CuratorOpeningById::<T, I>::get(curator_application.curator_opening_id);
+
+        Ok((
+            curator_application,
+            *curator_application_id,
+            curator_opening,
+        ))
+    }
 }

+ 159 - 0
runtime-modules/bureaucracy/src/tests/mod.rs

@@ -8,6 +8,36 @@ use srml_support::StorageValue;
 use std::collections::BTreeSet;
 use system::{EventRecord, Phase, RawOrigin};
 
+struct WithdrawApplicationFixture {
+    origin: RawOrigin<u64>,
+    curator_application_id: u64,
+}
+
+impl WithdrawApplicationFixture {
+    fn with_signer(self, account_id: u64) -> Self {
+        WithdrawApplicationFixture {
+            origin: RawOrigin::Signed(account_id),
+            ..self
+        }
+    }
+    fn with_origin(self, origin: RawOrigin<u64>) -> Self {
+        WithdrawApplicationFixture { origin, ..self }
+    }
+    pub fn default_for_application_id(application_id: u64) -> Self {
+        WithdrawApplicationFixture {
+            origin: RawOrigin::Signed(1),
+            curator_application_id: application_id,
+        }
+    }
+    pub fn call_and_assert(&self, expected_result: Result<(), &str>) {
+        let actual_result = Bureaucracy1::withdraw_curator_application(
+            self.origin.clone().into(),
+            self.curator_application_id,
+        );
+        assert_eq!(actual_result.clone(), expected_result);
+    }
+}
+
 pub(crate) fn increase_total_balance_issuance_using_account_id(account_id: u64, balance: u64) {
     let _ =
         <Balances as srml_support::traits::Currency<u64>>::deposit_creating(&account_id, balance);
@@ -579,3 +609,132 @@ fn apply_on_curator_opening_fails_with_already_active_application() {
             .call_and_assert(Err(crate::MSG_MEMBER_HAS_ACTIVE_APPLICATION_ON_OPENING));
     });
 }
+
+#[test]
+fn withdraw_curator_application_succeeds() {
+    build_test_externalities().execute_with(|| {
+        let lead_account_id = 1;
+        SetLeadFixture::set_lead(lead_account_id);
+
+        setup_members(2);
+
+        let add_curator_opening_fixture = AddCuratorOpeningFixture::default();
+        add_curator_opening_fixture.call_and_assert(Ok(()));
+
+        let opening_id = 0; // newly created opening
+
+        let appy_on_curator_opening_fixture =
+            ApplyOnCuratorOpeningFixture::default_for_opening_id(opening_id);
+        appy_on_curator_opening_fixture.call_and_assert(Ok(()));
+
+        let application_id = 0; // newly created application
+
+        let withdraw_application_fixture =
+            WithdrawApplicationFixture::default_for_application_id(application_id);
+        withdraw_application_fixture.call_and_assert(Ok(()));
+
+        EventFixture::assert_global_events(vec![
+            TestEvent::bureaucracy_Instance1(RawEvent::LeaderSet(1, lead_account_id)),
+            TestEvent::membership_mod(membership::members::RawEvent::MemberRegistered(0, 0)),
+            TestEvent::membership_mod(membership::members::RawEvent::MemberRegistered(1, 1)),
+            TestEvent::bureaucracy_Instance1(RawEvent::CuratorOpeningAdded(opening_id)),
+            TestEvent::bureaucracy_Instance1(RawEvent::AppliedOnCuratorOpening(
+                opening_id,
+                application_id,
+            )),
+            TestEvent::bureaucracy_Instance1(RawEvent::CuratorApplicationWithdrawn(application_id)),
+        ]);
+    });
+}
+
+#[test]
+fn withdraw_curator_application_fails_invalid_application_id() {
+    build_test_externalities().execute_with(|| {
+        let invalid_application_id = 6;
+
+        let withdraw_application_fixture =
+            WithdrawApplicationFixture::default_for_application_id(invalid_application_id);
+        withdraw_application_fixture
+            .call_and_assert(Err(crate::MSG_CURATOR_APPLICATION_DOES_NOT_EXIST));
+    });
+}
+
+#[test]
+fn withdraw_curator_application_fails_invalid_origin() {
+    build_test_externalities().execute_with(|| {
+        let lead_account_id = 1;
+        SetLeadFixture::set_lead(lead_account_id);
+
+        setup_members(2);
+
+        let add_curator_opening_fixture = AddCuratorOpeningFixture::default();
+        add_curator_opening_fixture.call_and_assert(Ok(()));
+
+        let opening_id = 0; // newly created opening
+
+        let appy_on_curator_opening_fixture =
+            ApplyOnCuratorOpeningFixture::default_for_opening_id(opening_id);
+        appy_on_curator_opening_fixture.call_and_assert(Ok(()));
+
+        let application_id = 0; // newly created application
+
+        let withdraw_application_fixture =
+            WithdrawApplicationFixture::default_for_application_id(application_id)
+                .with_origin(RawOrigin::None);
+        withdraw_application_fixture.call_and_assert(Err("RequireSignedOrigin"));
+    });
+}
+
+#[test]
+fn withdraw_curator_application_fails_with_invalid_application_author() {
+    build_test_externalities().execute_with(|| {
+        let lead_account_id = 1;
+        SetLeadFixture::set_lead(lead_account_id);
+
+        setup_members(2);
+
+        let add_curator_opening_fixture = AddCuratorOpeningFixture::default();
+        add_curator_opening_fixture.call_and_assert(Ok(()));
+
+        let opening_id = 0; // newly created opening
+
+        let appy_on_curator_opening_fixture =
+            ApplyOnCuratorOpeningFixture::default_for_opening_id(opening_id);
+        appy_on_curator_opening_fixture.call_and_assert(Ok(()));
+
+        let application_id = 0; // newly created application
+        let invalid_author_account_id = 55;
+        let withdraw_application_fixture =
+            WithdrawApplicationFixture::default_for_application_id(application_id)
+                .with_signer(invalid_author_account_id);
+        withdraw_application_fixture.call_and_assert(Err(crate::MSG_ORIGIN_IS_NOT_APPLICANT));
+    });
+}
+
+#[test]
+fn withdraw_curator_application_fails_with_hiring_error() {
+    build_test_externalities().execute_with(|| {
+        let lead_account_id = 1;
+        SetLeadFixture::set_lead(lead_account_id);
+
+        setup_members(2);
+
+        let add_curator_opening_fixture = AddCuratorOpeningFixture::default();
+        add_curator_opening_fixture.call_and_assert(Ok(()));
+
+        let opening_id = 0; // newly created opening
+
+        let appy_on_curator_opening_fixture =
+            ApplyOnCuratorOpeningFixture::default_for_opening_id(opening_id);
+        appy_on_curator_opening_fixture.call_and_assert(Ok(()));
+
+        let application_id = 0; // newly created application
+
+        let withdraw_application_fixture =
+            WithdrawApplicationFixture::default_for_application_id(application_id);
+        withdraw_application_fixture.call_and_assert(Ok(()));
+        withdraw_application_fixture.call_and_assert(Err(
+            crate::errors::MSG_WITHDRAW_CURATOR_APPLICATION_APPLICATION_NOT_ACTIVE,
+        ));
+    });
+}