Procházet zdrojové kódy

lead hiring flow implemented

Gleb Urvanov před 4 roky
rodič
revize
e57b4f538b

+ 2 - 1
scripts/create-test-chainspec.sh

@@ -9,4 +9,5 @@ perl -i -pe's/"setContentWorkingGroupMintCapacityProposalGracePeriod":.*/"setCon
 perl -i -pe's/"setLeadProposalGracePeriod":.*/"setLeadProposalGracePeriod": 0,/' .tmp/chainspec.json
 perl -i -pe's/"spendingProposalGracePeriod":.*/"spendingProposalGracePeriod": 0,/' .tmp/chainspec.json
 perl -i -pe's/"evictStorageProviderProposalGracePeriod":.*/"evictStorageProviderProposalGracePeriod": 0,/' .tmp/chainspec.json
-perl -i -pe's/"setStorageRoleParametersProposalGracePeriod":.*/"setStorageRoleParametersProposalGracePeriod": 0/' .tmp/chainspec.json
+perl -i -pe's/"beginReviewWorkingGroupLeaderApplicationsProposalGracePeriod":.*/"beginReviewWorkingGroupLeaderApplicationsProposalGracePeriod": 0,/' .tmp/chainspec.json
+perl -i -pe's/"setStorageRoleParametersProposalGracePeriod":.*/"setStorageRoleParametersProposalGracePeriod": 0,/' .tmp/chainspec.json

+ 77 - 0
tests/network-tests/src/nicaea/dto/fillOpeningParameters.ts

@@ -0,0 +1,77 @@
+import BN from 'bn.js';
+
+export class FillOpeningParameters {
+  private amountPerPayout!: BN;
+  private nextPaymentAtBlock!: BN;
+  private payoutInterval!: BN;
+  private openingId!: BN;
+  private successfulApplicationId!: BN;
+  private workingGroup!: string;
+
+  public getAmountPerPayout(): BN {
+    return this.amountPerPayout;
+  }
+
+  public getNextPaymentAtBlock(): BN {
+    return this.nextPaymentAtBlock;
+  }
+
+  public getPayoutInterval(): BN {
+    return this.payoutInterval;
+  }
+
+  public getOpeningId(): BN {
+    return this.openingId;
+  }
+
+  public getSuccessfulApplicationId(): BN {
+    return this.successfulApplicationId;
+  }
+
+  public getWorkingGroup(): string {
+    return this.workingGroup;
+  }
+
+  public setAmountPerPayout(value: BN) {
+    this.amountPerPayout = value;
+  }
+
+  public setNextPaymentAtBlock(value: BN) {
+    this.nextPaymentAtBlock = value;
+  }
+
+  public setPayoutInterval(value: BN) {
+    this.payoutInterval = value;
+  }
+
+  public setOpeningId(value: BN) {
+    this.openingId = value;
+  }
+
+  public setSuccessfulApplicationId(value: BN) {
+    this.successfulApplicationId = value;
+  }
+
+  public setWorkingGroup(value: string) {
+    this.workingGroup = value;
+  }
+
+  constructor() {}
+
+  public getRewardPolicy() {
+    return {
+      amount_per_payout: this.amountPerPayout,
+      next_payment_at_block: this.nextPaymentAtBlock,
+      payout_interval: this.payoutInterval,
+    };
+  }
+
+  public getFillOpeningParameters() {
+    return {
+      opening_id: this.openingId,
+      successful_application_id: this.successfulApplicationId,
+      reward_policy: this.getRewardPolicy(),
+      working_group: this.workingGroup,
+    };
+  }
+}

+ 0 - 1
tests/network-tests/src/nicaea/dto/workingGroupOpening.ts

@@ -1,5 +1,4 @@
 import BN from 'bn.js';
-import { KeyringPair } from '@polkadot/keyring/types';
 
 export class WorkingGroupOpening {
   private activateAtBlock: BN | undefined;

+ 37 - 7
tests/network-tests/src/nicaea/tests/proposals/addWorkingGroupLeaderTest.ts

@@ -14,6 +14,7 @@ import {
   voteForProposal,
   beginWorkingGroupLeaderApplicationReview,
   expectLeadOpeningAdded,
+  fillLeaderOpeningProposal,
 } from './impl/proposalsModule';
 import { applyForOpening } from '../workingGroup/impl/workingGroupModule';
 
@@ -35,7 +36,7 @@ tap.mocha.describe('Set lead proposal scenario', async () => {
   const lesserStake: BN = new BN(+process.env.COUNCIL_STAKE_LESSER_AMOUNT!);
   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 durationInBlocks: number = 60;
 
   const provider = new WsProvider(nodeUrl);
   const apiWrapper: ApiWrapper = await ApiWrapper.create(provider);
@@ -47,12 +48,12 @@ tap.mocha.describe('Set lead proposal scenario', async () => {
   membershipTest(apiWrapper, leadKeyPair, keyring, 1, paidTerms, sudoUri);
   councilTest(apiWrapper, m1KeyPairs, m2KeyPairs, keyring, K, sudoUri, greaterStake, lesserStake);
 
-  let proposalId: BN;
+  let createOpeningProposalId: BN;
   let openingId: BN;
   tap.test(
     'Create leader opening',
     async () =>
-      (proposalId = await createWorkingGroupLeaderOpening(
+      (createOpeningProposalId = await createWorkingGroupLeaderOpening(
         apiWrapper,
         m1KeyPairs,
         sudo,
@@ -61,10 +62,11 @@ tap.mocha.describe('Set lead proposal scenario', async () => {
         'Storage'
       ))
   );
-  tap.test('Approve proposal', async () => {
-    voteForProposal(apiWrapper, m2KeyPairs, sudo, proposalId);
+  tap.test('Approve add opening proposal', async () => {
+    voteForProposal(apiWrapper, m2KeyPairs, sudo, createOpeningProposalId);
     openingId = await expectLeadOpeningAdded(apiWrapper);
   });
+  let applicationId: BN;
   tap.test(
     'Apply for lead opening',
     async () =>
@@ -79,8 +81,36 @@ tap.mocha.describe('Set lead proposal scenario', async () => {
         false
       )
   );
-  tap.test('Begin leader application review', async () =>
-    beginWorkingGroupLeaderApplicationReview(apiWrapper, m1KeyPairs, sudo, new BN(openingId), 'Storage')
+  let beginReviewProposalId: BN;
+  tap.test(
+    'Begin leader application review',
+    async () =>
+      (beginReviewProposalId = await beginWorkingGroupLeaderApplicationReview(
+        apiWrapper,
+        m1KeyPairs,
+        sudo,
+        new BN(openingId),
+        'Storage'
+      ))
+  );
+  tap.test('Approve begin review proposal', async () =>
+    voteForProposal(apiWrapper, m2KeyPairs, sudo, beginReviewProposalId)
+  );
+  let fillLeaderOpeningProposalId: BN;
+  tap.test(
+    'Fill leader opening',
+    async () =>
+      (fillLeaderOpeningProposalId = await fillLeaderOpeningProposal(
+        apiWrapper,
+        m1KeyPairs,
+        leadKeyPair[0].address,
+        sudo,
+        new BN(openingId),
+        'Storage'
+      ))
+  );
+  tap.test('Approve fill leaвer opening', async () =>
+    voteForProposal(apiWrapper, m2KeyPairs, sudo, fillLeaderOpeningProposalId)
   );
 
   closeApi(apiWrapper);

+ 56 - 1
tests/network-tests/src/nicaea/tests/proposals/impl/proposalsModule.ts

@@ -1,9 +1,13 @@
 import { KeyringPair } from '@polkadot/keyring/types';
+import { Balance } from '@polkadot/types/interfaces';
+import { u32, u64 } from '@polkadot/types';
 import { ApiWrapper, WorkingGroups } from '../../../utils/apiWrapper';
 import { v4 as uuid } from 'uuid';
 import BN from 'bn.js';
 import { assert } from 'chai';
 import { WorkingGroupOpening } from '../../../dto/workingGroupOpening';
+import { RewardPolicy } from '@nicaea/types/working-group';
+import { FillOpeningParameters } from '../../../dto/fillOpeningParameters';
 
 export async function createWorkingGroupLeaderOpening(
   apiWrapper: ApiWrapper,
@@ -63,7 +67,7 @@ export async function beginWorkingGroupLeaderApplicationReview(
   sudo: KeyringPair,
   openingId: BN,
   workingGroup: string
-) {
+): Promise<BN> {
   // Setup
   const proposalTitle: string = 'Testing proposal ' + uuid().substring(0, 8);
   const description: string = 'Testing begin working group lead application review proposal ' + uuid().substring(0, 8);
@@ -87,6 +91,57 @@ export async function beginWorkingGroupLeaderApplicationReview(
   return proposalNumber;
 }
 
+export async function fillLeaderOpeningProposal(
+  apiWrapper: ApiWrapper,
+  m1KeyPairs: KeyringPair[],
+  applicantRoleAccountAddress: string,
+  sudo: KeyringPair,
+  openingId: BN,
+  workingGroup: string
+): Promise<BN> {
+  // Setup
+  console.log('lead address: ' + applicantRoleAccountAddress);
+  const proposalTitle: string = 'Testing proposal ' + uuid().substring(0, 8);
+  const description: string = 'Testing fill opening proposal ' + uuid().substring(0, 8);
+  console.log('================================= 1');
+
+  // Proposal stake calculation
+  const proposalStake: BN = new BN(50000);
+  const proposalFee: BN = apiWrapper.estimateProposeFillLeaderOpening();
+  await apiWrapper.transferBalance(sudo, m1KeyPairs[0].address, proposalFee.add(proposalStake));
+  console.log('================================= 2');
+
+  // Proposal creation
+  const applicationId: BN = (
+    await apiWrapper.getActiveApplicationsIdsByRoleAccount(
+      applicantRoleAccountAddress,
+      WorkingGroups.storageWorkingGroup
+    )
+  )[0];
+  let fillOpeningParameters: FillOpeningParameters = new FillOpeningParameters();
+  fillOpeningParameters.setAmountPerPayout(new BN(1));
+  fillOpeningParameters.setNextPaymentAtBlock(new BN(99999));
+  fillOpeningParameters.setPayoutInterval(new BN(99999));
+  fillOpeningParameters.setOpeningId(openingId);
+  fillOpeningParameters.setSuccessfulApplicationId(applicationId);
+  fillOpeningParameters.setWorkingGroup(workingGroup);
+
+  console.log('================================= 3');
+
+  const proposalPromise = apiWrapper.expectProposalCreated();
+  await apiWrapper.proposeFillLeaderOpening(
+    m1KeyPairs[0],
+    proposalTitle,
+    description,
+    proposalStake,
+    fillOpeningParameters
+  );
+  console.log('================================= 4');
+  const proposalNumber: BN = await proposalPromise;
+  console.log('================================= 5');
+  return proposalNumber;
+}
+
 export async function voteForProposal(
   apiWrapper: ApiWrapper,
   m2KeyPairs: KeyringPair[],

+ 54 - 5
tests/network-tests/src/nicaea/utils/apiWrapper.ts

@@ -1,11 +1,18 @@
 import { ApiPromise, WsProvider } from '@polkadot/api';
-import { Option, Vec, Bytes, u32 } from '@polkadot/types';
+import { Option, Vec, Bytes, u32, u64 } from '@polkadot/types';
 import { Codec } from '@polkadot/types/types';
 import { KeyringPair } from '@polkadot/keyring/types';
 import { UserInfo, PaidMembershipTerms, MemberId } from '@nicaea/types/members';
 import { Mint, MintId } from '@nicaea/types/mint';
 import { Lead, LeadId } from '@nicaea/types/content-working-group';
-import { Application, WorkerId, Worker, ApplicationIdToWorkerIdMap, Opening } from '@nicaea/types/working-group';
+import {
+  Application,
+  WorkerId,
+  Worker,
+  ApplicationIdToWorkerIdMap,
+  Opening,
+  RewardPolicy,
+} from '@nicaea/types/working-group';
 import { Application as HiringApplication } from '@nicaea/types/hiring';
 import { RoleParameters } from '@nicaea/types/roles';
 import { Seat } from '@nicaea/types/lib/council';
@@ -18,6 +25,7 @@ import { Stake, StakedState } from '@nicaea/types/stake';
 import { RewardRelationship } from '@nicaea/types/recurring-rewards';
 import { Opening as HiringOpening, ApplicationId } from '@nicaea/types/hiring';
 import { WorkingGroupOpening } from '../dto/workingGroupOpening';
+import { FillOpeningParameters } from '../dto/fillOpeningParameters';
 
 export enum WorkingGroups {
   storageWorkingGroup = 'storageWorkingGroup',
@@ -368,6 +376,26 @@ export class ApiWrapper {
     );
   }
 
+  public estimateProposeFillLeaderOpening(): BN {
+    let fillOpeningParameters: FillOpeningParameters = new FillOpeningParameters();
+    fillOpeningParameters.setAmountPerPayout(new BN(1));
+    fillOpeningParameters.setNextPaymentAtBlock(new BN(99999));
+    fillOpeningParameters.setPayoutInterval(new BN(99999));
+    fillOpeningParameters.setOpeningId(new BN(0));
+    fillOpeningParameters.setSuccessfulApplicationId(new BN(0));
+    fillOpeningParameters.setWorkingGroup('Storage');
+
+    return this.estimateTxFee(
+      this.api.tx.proposalsCodex.createFillWorkingGroupLeaderOpeningProposal(
+        0,
+        'Some testing text used for estimation purposes which is longer than text expected during the test',
+        'Some testing text used for estimation purposes which is longer than text expected during the test',
+        0,
+        fillOpeningParameters.getFillOpeningParameters()
+      )
+    );
+  }
+
   private applyForCouncilElection(account: KeyringPair, amount: BN): Promise<void> {
     return this.sender.signAndSend(this.api.tx.councilElection.apply(amount), account, false);
   }
@@ -828,11 +856,11 @@ export class ApiWrapper {
     module: WorkingGroups,
     expectFailure: boolean
   ): Promise<void> {
-    await this.sender.signAndSend(this.createAddOpeningTransaction(opening, module), leader, expectFailure);
+    return this.sender.signAndSend(this.createAddOpeningTransaction(opening, module), leader, expectFailure);
   }
 
   public async sudoAddOpening(sudo: KeyringPair, opening: WorkingGroupOpening, module: WorkingGroups): Promise<void> {
-    await this.sender.signAndSend(
+    return this.sender.signAndSend(
       this.api.tx.sudo.sudo(this.createAddOpeningTransaction(opening, module)),
       sudo,
       false
@@ -848,7 +876,7 @@ export class ApiWrapper {
     workingGroup: string
   ): Promise<void> {
     const memberId: BN = (await this.getMemberIds(account.address))[0];
-    await this.sender.signAndSend(
+    return this.sender.signAndSend(
       this.api.tx.proposalsCodex.createAddWorkingGroupLeaderOpeningProposal(
         memberId,
         title,
@@ -866,6 +894,27 @@ export class ApiWrapper {
     );
   }
 
+  public async proposeFillLeaderOpening(
+    account: KeyringPair,
+    title: string,
+    description: string,
+    proposalStake: BN,
+    fillOpeningParameters: FillOpeningParameters
+  ): Promise<void> {
+    const memberId: BN = (await this.getMemberIds(account.address))[0];
+    return this.sender.signAndSend(
+      this.api.tx.proposalsCodex.createFillWorkingGroupLeaderOpeningProposal(
+        memberId,
+        title,
+        description,
+        proposalStake,
+        fillOpeningParameters.getFillOpeningParameters()
+      ),
+      account,
+      false
+    );
+  }
+
   private createAddOpeningTransaction(
     opening: WorkingGroupOpening,
     module: WorkingGroups