Browse Source

manage worker scenarios implemented

Gleb Urvanov 4 years ago
parent
commit
95bca88a9f

+ 2 - 2
tests/network-tests/.env

@@ -27,7 +27,7 @@ RUNTIME_WASM_PATH = ../../target/release/wbuild/joystream-node-runtime/joystream
 # Working group size N
 WORKING_GROUP_N = 3
 # Working group application stake
-WORKING_GROUP_APPLICATION_STAKE = 1
+WORKING_GROUP_APPLICATION_STAKE = 10
 # Working group role stake
-WORKING_GROUP_ROLE_STAKE = 1
+WORKING_GROUP_ROLE_STAKE = 10
 

+ 1 - 1
tests/network-tests/package.json

@@ -6,7 +6,7 @@
     "build": "tsc --build tsconfig.json",
     "test": "tap --files ts-node/register src/constantinople/tests/proposals/*Test.ts --files ts-node/register src/nicaea/tests/bureaucracy/*Test.ts",
     "test-migration": "tap --files src/rome/tests/romeRuntimeUpgradeTest.ts --files src/constantinople/tests/electingCouncilTest.ts",
-    "debug": "tap --files src/nicaea/tests/bureaucracy/workerApplicationHappyCaseTest.ts -T",
+    "debug": "tap --files src/nicaea/tests/bureaucracy/manageWorkerAsLeadTest.ts -T",
     "lint": "tslint --project tsconfig.json",
     "prettier": "prettier --write ./src"
   },

+ 121 - 3
tests/network-tests/src/nicaea/tests/bureaucracy/impl/workingGroupModule.ts

@@ -10,13 +10,18 @@ export async function setLead(apiWrapper: ApiWrapper, lead: KeyringPair, sudo: K
   await apiWrapper.sudoSetLead(sudo, lead);
 }
 
+export async function unsetLead(apiWrapper: ApiWrapper, sudo: KeyringPair) {
+  await apiWrapper.sudoUnsetLead(sudo);
+}
+
 export async function addWorkerOpening(
   apiWrapper: ApiWrapper,
   membersKeyPairs: KeyringPair[],
   lead: KeyringPair,
   sudo: KeyringPair,
   applicationStake: BN,
-  roleStake: BN
+  roleStake: BN,
+  activationDelay: BN
 ): Promise<BN> {
   let workerOpeningId: BN;
 
@@ -26,7 +31,11 @@ export async function addWorkerOpening(
 
   // Worker opening creation
   workerOpeningId = await apiWrapper.getNextWorkerOpeningId();
+  const activateAtBlock: BN | undefined = activationDelay.eqn(0)
+    ? undefined
+    : (await apiWrapper.getBestBlock()).add(activationDelay);
   await apiWrapper.addWorkerOpening(
+    activateAtBlock,
     lead,
     new BN(membersKeyPairs.length),
     new BN(32),
@@ -51,13 +60,28 @@ export async function addWorkerOpening(
   return workerOpeningId;
 }
 
+export async function acceptWorkerApplications(
+  apiWrapper: ApiWrapper,
+  lead: KeyringPair,
+  sudo: KeyringPair,
+  workerOpeningId: BN
+) {
+  // Fee estimation and transfer
+  const acceptWorkerApplicationsFee = apiWrapper.estimateAcceptWorkerApplicationsFee();
+  await apiWrapper.transferBalance(sudo, lead.address, acceptWorkerApplicationsFee);
+
+  // Begin accepting applications
+  await apiWrapper.acceptWorkerApplications(lead, workerOpeningId);
+}
+
 export async function applyForWorkerOpening(
   apiWrapper: ApiWrapper,
   membersKeyPairs: KeyringPair[],
   sudo: KeyringPair,
   applicationStake: BN,
   roleStake: BN,
-  workerOpeningId: BN
+  workerOpeningId: BN,
+  expectFailure: boolean
 ): Promise<BN> {
   let nextApplicationId: BN;
 
@@ -67,7 +91,14 @@ export async function applyForWorkerOpening(
 
   // Applying for created worker opening
   nextApplicationId = await apiWrapper.getNextApplicationId();
-  await apiWrapper.batchApplyOnWorkerOpening(membersKeyPairs, workerOpeningId, roleStake, applicationStake, '');
+  await apiWrapper.batchApplyOnWorkerOpening(
+    membersKeyPairs,
+    workerOpeningId,
+    roleStake,
+    applicationStake,
+    '',
+    expectFailure
+  );
 
   return nextApplicationId;
 }
@@ -199,3 +230,90 @@ export async function updateRoleAccount(
 
   membersKeyPairs[0] = createdAccount;
 }
+
+export async function terminateWorkerApplications(
+  apiWrapper: ApiWrapper,
+  membersKeyPairs: KeyringPair[],
+  lead: KeyringPair,
+  sudo: KeyringPair
+) {
+  // Fee estimation and transfer
+  const terminateWorkerApplicationFee = apiWrapper.estimateTerminateWorkerApplicationFee();
+  await apiWrapper.transferBalance(sudo, lead.address, terminateWorkerApplicationFee.muln(membersKeyPairs.length));
+
+  // Terminate worker applications
+  await apiWrapper.batchTerminateWorkerApplication(lead, membersKeyPairs);
+}
+
+export async function decreaseWorkerStake(
+  apiWrapper: ApiWrapper,
+  membersKeyPairs: KeyringPair[],
+  lead: KeyringPair,
+  sudo: KeyringPair,
+  expectFailure: boolean
+) {
+  // Fee estimation and transfer
+  const decreaseWorkerStakeFee = apiWrapper.estimateDecreaseWorkerStakeFee();
+  await apiWrapper.transferBalance(sudo, lead.address, decreaseWorkerStakeFee);
+  const workerStakeDecrement = new BN(1);
+  const workerId: BN = await apiWrapper.getWorkerIdByRoleAccount(membersKeyPairs[0].address);
+
+  // Worker stake decrement
+  const decreasedWorkerStake: BN = (await apiWrapper.getWorkerStakeAmount(workerId)).sub(workerStakeDecrement);
+  await apiWrapper.decreaseWorkerStake(lead, workerId, workerStakeDecrement, expectFailure);
+  const newWorkerStake: BN = await apiWrapper.getWorkerStakeAmount(workerId);
+
+  // Assertions
+  if (!expectFailure) {
+    assert(
+      decreasedWorkerStake.eq(newWorkerStake),
+      `Unexpected worker stake ${newWorkerStake}, expected ${decreasedWorkerStake}`
+    );
+  }
+}
+
+export async function slashWorker(
+  apiWrapper: ApiWrapper,
+  membersKeyPairs: KeyringPair[],
+  lead: KeyringPair,
+  sudo: KeyringPair,
+  expectFailure: boolean
+) {
+  // Fee estimation and transfer
+  const slashWorkerStakeFee = apiWrapper.estimateSlashWorkerStakeFee();
+  await apiWrapper.transferBalance(sudo, lead.address, slashWorkerStakeFee);
+  const slashAmount = new BN(1);
+  const workerId: BN = await apiWrapper.getWorkerIdByRoleAccount(membersKeyPairs[0].address);
+
+  // Slash worker
+  const slashedWorkerStake: BN = (await apiWrapper.getWorkerStakeAmount(workerId)).sub(slashAmount);
+  await apiWrapper.slashWorkerStake(lead, workerId, slashAmount, expectFailure);
+  const newWorkerStake: BN = await apiWrapper.getWorkerStakeAmount(workerId);
+
+  // Assertions
+  assert(
+    slashedWorkerStake.eq(newWorkerStake),
+    `Unexpected worker stake ${newWorkerStake}, expected ${slashedWorkerStake}`
+  );
+}
+
+export async function terminateWorkerRole(
+  apiWrapper: ApiWrapper,
+  membersKeyPairs: KeyringPair[],
+  lead: KeyringPair,
+  sudo: KeyringPair,
+  expectFailure: boolean
+) {
+  // Fee estimation and transfer
+  const terminateWorkerRoleFee = apiWrapper.estimateTerminateWorkerRoleFee();
+  await apiWrapper.transferBalance(sudo, lead.address, terminateWorkerRoleFee);
+  const workerId: BN = await apiWrapper.getWorkerIdByRoleAccount(membersKeyPairs[0].address);
+
+  // Slash worker
+  await apiWrapper.terminateWorkerRole(lead, workerId, '', expectFailure);
+
+  // Assertions
+  apiWrapper.getWorkerIdByRoleAccount(membersKeyPairs[0].address);
+  const newWorkerId = await apiWrapper.getWorkerIdByRoleAccount(membersKeyPairs[0].address);
+  assert(newWorkerId === undefined, `Worker with account ${membersKeyPairs[0].address} is not terminated`);
+}

+ 86 - 0
tests/network-tests/src/nicaea/tests/bureaucracy/manageWorkerAsLeadTest.ts

@@ -0,0 +1,86 @@
+import tap = require('tap');
+import { initConfig } from '../../utils/config';
+import { registerJoystreamTypes } from '@nicaea/types';
+import { closeApi } from '../impl/closeApi';
+import { ApiWrapper } from '../../utils/apiWrapper';
+import { WsProvider, Keyring } from '@polkadot/api';
+import { KeyringPair } from '@polkadot/keyring/types';
+import { setTestTimeout } from '../../utils/setTestTimeout';
+import { membershipTest } from '../impl/membershipCreation';
+import {
+  addWorkerOpening,
+  applyForWorkerOpening,
+  beginApplicationReview,
+  fillWorkerOpening,
+  setLead,
+  unsetLead,
+  decreaseWorkerStake,
+  slashWorker,
+  terminateWorkerRole,
+} from './impl/workingGroupModule';
+import BN from 'bn.js';
+
+tap.mocha.describe('Manage worker as worker scenario', async () => {
+  initConfig();
+  registerJoystreamTypes();
+
+  const nKeyPairs: KeyringPair[] = new Array();
+  const leadKeyPair: KeyringPair[] = new Array();
+
+  const keyring = new Keyring({ type: 'sr25519' });
+  const N: number = +process.env.WORKING_GROUP_N!;
+  const paidTerms: number = +process.env.MEMBERSHIP_PAID_TERMS!;
+  const nodeUrl: string = process.env.NODE_URL!;
+  const sudoUri: string = process.env.SUDO_ACCOUNT_URI!;
+  const applicationStake: BN = new BN(process.env.WORKING_GROUP_APPLICATION_STAKE!);
+  const roleStake: BN = new BN(process.env.WORKING_GROUP_ROLE_STAKE!);
+  const durationInBlocks: number = 40;
+  const openingActivationDelay: BN = new BN(0);
+
+  const provider = new WsProvider(nodeUrl);
+  const apiWrapper: ApiWrapper = await ApiWrapper.create(provider);
+  const sudo: KeyringPair = keyring.addFromUri(sudoUri);
+
+  setTestTimeout(apiWrapper, durationInBlocks);
+  membershipTest(apiWrapper, nKeyPairs, keyring, N, paidTerms, sudoUri);
+  membershipTest(apiWrapper, leadKeyPair, keyring, N, paidTerms, sudoUri);
+
+  tap.test('Set lead', async () => setLead(apiWrapper, leadKeyPair[0], sudo));
+  let openignId: BN;
+  tap.test(
+    'Add worker opening',
+    async () =>
+      (openignId = await addWorkerOpening(
+        apiWrapper,
+        nKeyPairs,
+        leadKeyPair[0],
+        sudo,
+        applicationStake,
+        roleStake,
+        openingActivationDelay
+      ))
+  );
+  tap.test(
+    'Apply for worker opening',
+    async () => await applyForWorkerOpening(apiWrapper, nKeyPairs, sudo, applicationStake, roleStake, openignId, false)
+  );
+  tap.test('Begin application review', async () => beginApplicationReview(apiWrapper, leadKeyPair[0], sudo, openignId));
+  tap.test('Fill worker opening', async () =>
+    fillWorkerOpening(apiWrapper, nKeyPairs, leadKeyPair[0], sudo, openignId)
+  );
+
+  tap.test('Unset lead', async () => unsetLead(apiWrapper, sudo));
+  tap.test('Decrease worker stake, expect failure', async () =>
+    decreaseWorkerStake(apiWrapper, nKeyPairs, leadKeyPair[0], sudo, true)
+  );
+  tap.test('Set lead', async () => setLead(apiWrapper, leadKeyPair[0], sudo));
+  tap.test('Decrease worker stake', async () =>
+    decreaseWorkerStake(apiWrapper, nKeyPairs, leadKeyPair[0], sudo, false)
+  );
+  tap.test('Slash worker', async () => slashWorker(apiWrapper, nKeyPairs, leadKeyPair[0], sudo, false));
+  tap.test('Terminate worker role', async () =>
+    terminateWorkerRole(apiWrapper, nKeyPairs, leadKeyPair[0], sudo, false)
+  );
+
+  closeApi(apiWrapper);
+});

+ 11 - 2
tests/network-tests/src/nicaea/tests/bureaucracy/manageWorkerAsWorkerTest.ts

@@ -34,6 +34,7 @@ tap.mocha.describe('Manage worker as worker scenario', async () => {
   const applicationStake: BN = new BN(process.env.WORKING_GROUP_APPLICATION_STAKE!);
   const roleStake: BN = new BN(process.env.WORKING_GROUP_ROLE_STAKE!);
   const durationInBlocks: number = 30;
+  const openingActivationDelay: BN = new BN(0);
 
   const provider = new WsProvider(nodeUrl);
   const apiWrapper: ApiWrapper = await ApiWrapper.create(provider);
@@ -48,11 +49,19 @@ tap.mocha.describe('Manage worker as worker scenario', async () => {
   tap.test(
     'Add worker opening',
     async () =>
-      (openignId = await addWorkerOpening(apiWrapper, nKeyPairs, leadKeyPair[0], sudo, applicationStake, roleStake))
+      (openignId = await addWorkerOpening(
+        apiWrapper,
+        nKeyPairs,
+        leadKeyPair[0],
+        sudo,
+        applicationStake,
+        roleStake,
+        openingActivationDelay
+      ))
   );
   tap.test(
     'Apply for worker opening',
-    async () => await applyForWorkerOpening(apiWrapper, nKeyPairs, sudo, applicationStake, roleStake, openignId)
+    async () => await applyForWorkerOpening(apiWrapper, nKeyPairs, sudo, applicationStake, roleStake, openignId, false)
   );
   tap.test('Begin application review', async () => beginApplicationReview(apiWrapper, leadKeyPair[0], sudo, openignId));
   tap.test('Fill worker opening', async () =>

+ 12 - 3
tests/network-tests/src/nicaea/tests/bureaucracy/workerApplicationHappyCaseTest.ts

@@ -32,6 +32,7 @@ tap.mocha.describe('Worker application happy case scenario', async () => {
   const applicationStake: BN = new BN(process.env.WORKING_GROUP_APPLICATION_STAKE!);
   const roleStake: BN = new BN(process.env.WORKING_GROUP_ROLE_STAKE!);
   const durationInBlocks: number = 28;
+  const openingActivationDelay: BN = new BN(0);
 
   const provider = new WsProvider(nodeUrl);
   const apiWrapper: ApiWrapper = await ApiWrapper.create(provider);
@@ -46,14 +47,22 @@ tap.mocha.describe('Worker application happy case scenario', async () => {
   tap.test(
     'Add worker opening',
     async () =>
-      (openignId = await addWorkerOpening(apiWrapper, nKeyPairs, leadKeyPair[0], sudo, applicationStake, roleStake))
+      (openignId = await addWorkerOpening(
+        apiWrapper,
+        nKeyPairs,
+        leadKeyPair[0],
+        sudo,
+        applicationStake,
+        roleStake,
+        openingActivationDelay
+      ))
   );
   tap.test('Apply for worker opening', async () =>
-    applyForWorkerOpening(apiWrapper, nKeyPairs, sudo, applicationStake, roleStake, openignId)
+    applyForWorkerOpening(apiWrapper, nKeyPairs, sudo, applicationStake, roleStake, openignId, false)
   );
   tap.test('Withdraw worker application', async () => withdrawWorkerApplicaiton(apiWrapper, nKeyPairs, sudo));
   tap.test('Apply for worker opening', async () =>
-    applyForWorkerOpening(apiWrapper, nKeyPairs, sudo, applicationStake, roleStake, openignId)
+    applyForWorkerOpening(apiWrapper, nKeyPairs, sudo, applicationStake, roleStake, openignId, false)
   );
   tap.test('Begin application review', async () => beginApplicationReview(apiWrapper, leadKeyPair[0], sudo, openignId));
   tap.test('Fill worker opening', async () =>

+ 79 - 0
tests/network-tests/src/nicaea/tests/bureaucracy/workerApplicationRejectionCaseTest.ts

@@ -0,0 +1,79 @@
+import tap from 'tap';
+import { initConfig } from '../../utils/config';
+import { registerJoystreamTypes } from '@nicaea/types';
+import { closeApi } from '../impl/closeApi';
+import { ApiWrapper } from '../../utils/apiWrapper';
+import { WsProvider, Keyring } from '@polkadot/api';
+import { KeyringPair } from '@polkadot/keyring/types';
+import { setTestTimeout } from '../../utils/setTestTimeout';
+import { membershipTest, createKeyPairs } from '../impl/membershipCreation';
+import {
+  addWorkerOpening,
+  applyForWorkerOpening,
+  setLead,
+  acceptWorkerApplications,
+  terminateWorkerApplications,
+  unsetLead,
+} from './impl/workingGroupModule';
+import BN from 'bn.js';
+
+tap.mocha.describe('Worker application happy case scenario', async () => {
+  initConfig();
+  registerJoystreamTypes();
+
+  const nKeyPairs: KeyringPair[] = new Array();
+  const leadKeyPair: KeyringPair[] = new Array();
+
+  const keyring = new Keyring({ type: 'sr25519' });
+  const N: number = +process.env.WORKING_GROUP_N!;
+  const paidTerms: number = +process.env.MEMBERSHIP_PAID_TERMS!;
+  const nodeUrl: string = process.env.NODE_URL!;
+  const sudoUri: string = process.env.SUDO_ACCOUNT_URI!;
+  const applicationStake: BN = new BN(process.env.WORKING_GROUP_APPLICATION_STAKE!);
+  const roleStake: BN = new BN(process.env.WORKING_GROUP_ROLE_STAKE!);
+  const durationInBlocks: number = 28;
+  const openingActivationDelay: BN = new BN(100);
+
+  const provider = new WsProvider(nodeUrl);
+  const apiWrapper: ApiWrapper = await ApiWrapper.create(provider);
+  const sudo: KeyringPair = keyring.addFromUri(sudoUri);
+  const nonMemberKeyPairs = createKeyPairs(keyring, N);
+
+  setTestTimeout(apiWrapper, durationInBlocks);
+  membershipTest(apiWrapper, nKeyPairs, keyring, N, paidTerms, sudoUri);
+  membershipTest(apiWrapper, leadKeyPair, keyring, N, paidTerms, sudoUri);
+
+  tap.test('Set lead', async () => setLead(apiWrapper, leadKeyPair[0], sudo));
+  let openignId: BN;
+  tap.test(
+    'Add worker opening',
+    async () =>
+      (openignId = await addWorkerOpening(
+        apiWrapper,
+        nKeyPairs,
+        leadKeyPair[0],
+        sudo,
+        applicationStake,
+        roleStake,
+        openingActivationDelay
+      ))
+  );
+  tap.test('Apply for worker opening, expect failure', async () =>
+    applyForWorkerOpening(apiWrapper, nKeyPairs, sudo, applicationStake, roleStake, openignId, true)
+  );
+  tap.test('Begin accepting worker applications', async () =>
+    acceptWorkerApplications(apiWrapper, leadKeyPair[0], sudo, openignId)
+  );
+  tap.test('Apply for worker opening as non-member, expect failure', async () =>
+    applyForWorkerOpening(apiWrapper, nonMemberKeyPairs, sudo, applicationStake, roleStake, openignId, true)
+  );
+  tap.test('Apply for worker opening as member', async () =>
+    applyForWorkerOpening(apiWrapper, nKeyPairs, sudo, applicationStake, roleStake, openignId, false)
+  );
+  tap.test('Terminate worker applicaitons', async () =>
+    terminateWorkerApplications(apiWrapper, nKeyPairs, leadKeyPair[0], sudo)
+  );
+  tap.test('Unset lead', async () => unsetLead(apiWrapper, sudo));
+
+  closeApi(apiWrapper);
+});

+ 8 - 0
tests/network-tests/src/nicaea/tests/impl/membershipCreation.ts

@@ -76,3 +76,11 @@ export function membershipTest(
       .then(membership => assert(membership.length > 0, 'Account A is a not member'));
   });
 }
+
+export function createKeyPairs(keyring: Keyring, N: number): KeyringPair[] {
+  const nKeyPairs: KeyringPair[] = new Array();
+  for (let i = 0; i < N; i++) {
+    nKeyPairs.push(keyring.addFromUri(i + uuid().substring(0, 8)));
+  }
+  return nKeyPairs;
+}

+ 88 - 57
tests/network-tests/src/nicaea/utils/apiWrapper.ts

@@ -293,6 +293,10 @@ export class ApiWrapper {
     return this.estimateTxFee(this.api.tx.forumBureaucracy.increaseWorkerStake(0, 0));
   }
 
+  public estimateDecreaseWorkerStakeFee(): BN {
+    return this.estimateTxFee(this.api.tx.forumBureaucracy.decreaseWorkerStake(0, 0));
+  }
+
   public estimateUpdateRoleAccountFee(address: string): BN {
     return this.estimateTxFee(this.api.tx.forumBureaucracy.updateWorkerRoleAccount(0, address));
   }
@@ -309,6 +313,23 @@ export class ApiWrapper {
     return this.estimateTxFee(this.api.tx.forumBureaucracy.withdrawWorkerApplication(0));
   }
 
+  public estimateTerminateWorkerApplicationFee(): BN {
+    return this.estimateTxFee(this.api.tx.forumBureaucracy.terminateWorkerApplication(0));
+  }
+
+  public estimateSlashWorkerStakeFee(): BN {
+    return this.estimateTxFee(this.api.tx.forumBureaucracy.slashWorkerStake(0, 0));
+  }
+
+  public estimateTerminateWorkerRoleFee(): BN {
+    return this.estimateTxFee(
+      this.api.tx.forumBureaucracy.terminateWorkerRole(
+        0,
+        'Long justification text explaining why the worker role will be terminated'
+      )
+    );
+  }
+
   private applyForCouncilElection(account: KeyringPair, amount: BN): Promise<void> {
     return this.sender.signAndSend(this.api.tx.councilElection.apply(amount), account, false);
   }
@@ -708,7 +729,12 @@ export class ApiWrapper {
     );
   }
 
+  public async sudoUnsetLead(sudo: KeyringPair): Promise<void> {
+    return this.sender.signAndSend(this.api.tx.sudo.sudo(this.api.tx.forumBureaucracy.unsetLead()), sudo, false);
+  }
+
   public async addWorkerOpening(
+    activateAtBlock: BN | undefined,
     account: KeyringPair,
     maxActiveApplicants: BN,
     maxReviewPeriodLength: BN,
@@ -729,6 +755,7 @@ export class ApiWrapper {
     exitCuratorRoleStakeUnstakingPeriod: BN,
     text: string
   ): Promise<void> {
+    const activateAt = activateAtBlock == undefined ? 'CurrentBlock' : { ExactBlock: activateAtBlock };
     const commitment = {
       application_rationing_policy: { max_active_applicants: maxActiveApplicants },
       max_review_period_length: maxReviewPeriodLength,
@@ -759,61 +786,12 @@ export class ApiWrapper {
       exit_curator_role_stake_unstaking_period: exitCuratorRoleStakeUnstakingPeriod,
     };
     await this.sender.signAndSend(
-      this.api.tx.forumBureaucracy.addWorkerOpening('CurrentBlock', commitment, text),
+      this.api.tx.forumBureaucracy.addWorkerOpening(activateAt, commitment, text),
       account,
       false
     );
   }
 
-  public async batchAddWorkerOpening(
-    lead: KeyringPair,
-    accounts: KeyringPair[],
-    maxActiveApplicants: BN,
-    maxReviewPeriodLength: BN,
-    applicationStakingPolicyAmount: BN,
-    applicationCrowdedOutUnstakingPeriodLength: BN,
-    applicationExpiredUnstakingPeriodLength: BN,
-    roleStakingPolicyAmount: BN,
-    roleCrowdedOutUnstakingPeriodLength: BN,
-    roleExpiredUnstakingPeriodLength: BN,
-    slashableMaxCount: BN,
-    slashableMaxPercentPtsPerTime: BN,
-    successfulApplicantApplicationStakeUnstakingPeriod: BN,
-    failedApplicantApplicationStakeUnstakingPeriod: BN,
-    failedApplicantRoleStakeUnstakingPeriod: BN,
-    terminateCuratorApplicationStakeUnstakingPeriod: BN,
-    terminateCuratorRoleStakeUnstakingPeriod: BN,
-    exitCuratorRoleApplicationStakeUnstakingPeriod: BN,
-    exitCuratorRoleStakeUnstakingPeriod: BN,
-    text: string
-  ): Promise<void[]> {
-    return Promise.all(
-      accounts.map(async () => {
-        await this.addWorkerOpening(
-          lead,
-          maxActiveApplicants,
-          maxReviewPeriodLength,
-          applicationStakingPolicyAmount,
-          applicationCrowdedOutUnstakingPeriodLength,
-          applicationExpiredUnstakingPeriodLength,
-          roleStakingPolicyAmount,
-          roleCrowdedOutUnstakingPeriodLength,
-          roleExpiredUnstakingPeriodLength,
-          slashableMaxCount,
-          slashableMaxPercentPtsPerTime,
-          successfulApplicantApplicationStakeUnstakingPeriod,
-          failedApplicantApplicationStakeUnstakingPeriod,
-          failedApplicantRoleStakeUnstakingPeriod,
-          terminateCuratorApplicationStakeUnstakingPeriod,
-          terminateCuratorRoleStakeUnstakingPeriod,
-          exitCuratorRoleApplicationStakeUnstakingPeriod,
-          exitCuratorRoleStakeUnstakingPeriod,
-          text
-        );
-      })
-    );
-  }
-
   public async acceptWorkerApplications(account: KeyringPair, workerOpeningId: BN): Promise<void> {
     return this.sender.signAndSend(
       this.api.tx.forumBureaucracy.acceptWorkerApplications(workerOpeningId),
@@ -835,9 +813,10 @@ export class ApiWrapper {
     workerOpeningId: BN,
     roleStake: BN,
     applicantStake: BN,
-    text: string
+    text: string,
+    expectFailure: boolean
   ): Promise<void> {
-    const memberId: BN = (await this.getMemberIds(account.address))[0].toBn();
+    const memberId: BN = (await this.getMemberIds(account.address))[0];
     return this.sender.signAndSend(
       this.api.tx.forumBureaucracy.applyOnWorkerOpening(
         memberId,
@@ -848,7 +827,7 @@ export class ApiWrapper {
         text
       ),
       account,
-      false
+      expectFailure
     );
   }
 
@@ -857,11 +836,12 @@ export class ApiWrapper {
     workerOpeningId: BN,
     roleStake: BN,
     applicantStake: BN,
-    text: string
+    text: string,
+    expectFailure: boolean
   ): Promise<void[]> {
     return Promise.all(
       accounts.map(async keyPair => {
-        await this.applyOnWorkerOpening(keyPair, workerOpeningId, roleStake, applicantStake, text);
+        await this.applyOnWorkerOpening(keyPair, workerOpeningId, roleStake, applicantStake, text, expectFailure);
       })
     );
   }
@@ -889,6 +869,27 @@ export class ApiWrapper {
     return this.sender.signAndSend(this.api.tx.forumBureaucracy.increaseWorkerStake(workerId, stake), account, false);
   }
 
+  public async decreaseWorkerStake(
+    account: KeyringPair,
+    workerId: BN,
+    stake: BN,
+    expectFailure: boolean
+  ): Promise<void> {
+    return this.sender.signAndSend(
+      this.api.tx.forumBureaucracy.decreaseWorkerStake(workerId, stake),
+      account,
+      expectFailure
+    );
+  }
+
+  public async slashWorkerStake(account: KeyringPair, workerId: BN, stake: BN, expectFailure: boolean): Promise<void> {
+    return this.sender.signAndSend(
+      this.api.tx.forumBureaucracy.slashWorkerStake(workerId, stake),
+      account,
+      expectFailure
+    );
+  }
+
   public async updateRoleAccount(account: KeyringPair, workerId: BN, newRoleAccount: string): Promise<void> {
     return this.sender.signAndSend(
       this.api.tx.forumBureaucracy.updateWorkerRoleAccount(workerId, newRoleAccount),
@@ -912,12 +913,42 @@ export class ApiWrapper {
   public async batchWithdrawWorkerApplication(accounts: KeyringPair[]): Promise<void[]> {
     return Promise.all(
       accounts.map(async keyPair => {
-        const applicationIds: BN[] = await this.getWorkerApplicationIdsByRoleAccount(keyPair.address);
+        const applicationIds: BN[] = await this.getWorkerApplicationsIdsByRoleAccount(keyPair.address);
         await this.withdrawWorkerApplication(keyPair, applicationIds[0]);
       })
     );
   }
 
+  public async terminateWorkerApplication(account: KeyringPair, applicationId: BN): Promise<void> {
+    return this.sender.signAndSend(
+      this.api.tx.forumBureaucracy.terminateWorkerApplication(applicationId),
+      account,
+      false
+    );
+  }
+
+  public async batchTerminateWorkerApplication(account: KeyringPair, roleAccounts: KeyringPair[]): Promise<void[]> {
+    return Promise.all(
+      roleAccounts.map(async keyPair => {
+        const applicationIds: BN[] = await this.getActiveWorkerApplicationsIdsByRoleAccount(keyPair.address);
+        await this.terminateWorkerApplication(account, applicationIds[0]);
+      })
+    );
+  }
+
+  public async terminateWorkerRole(
+    account: KeyringPair,
+    applicationId: BN,
+    text: string,
+    expectFailure: boolean
+  ): Promise<void> {
+    return this.sender.signAndSend(
+      this.api.tx.forumBureaucracy.terminateWorkerRole(applicationId, text),
+      account,
+      expectFailure
+    );
+  }
+
   public async getStorageRoleParameters(): Promise<RoleParameters> {
     return (await this.api.query.actors.parameters<Option<RoleParameters>>('StorageProvider')).unwrap();
   }
@@ -985,7 +1016,7 @@ export class ApiWrapper {
     return ids[index!];
   }
 
-  public async getWorkerApplicationIdsByRoleAccount(address: string): Promise<BN[]> {
+  public async getWorkerApplicationsIdsByRoleAccount(address: string): Promise<BN[]> {
     const applicationsAndIds = await this.api.query.forumBureaucracy.workerApplicationById<Codec[]>();
     const applications: WorkerApplication[] = (applicationsAndIds[1] as unknown) as WorkerApplication[];
     const ids: WorkerApplicationId[] = (applicationsAndIds[0] as unknown) as WorkerApplicationId[];