Browse Source

manage worker as worker test impelemented

Gleb Urvanov 4 years ago
parent
commit
e7a33df795

+ 12 - 0
devops/ansible/build-and-run-tests-single-node-playbook.yml

@@ -0,0 +1,12 @@
+- name: install dependencies
+  import_playbook: install-dependencies-playbook.yml
+
+- hosts: 127.0.0.1
+  user: root
+  become: yes
+  become_method: sudo
+  tasks:
+    - name: build node
+      include: build-image-tasklist.yml
+- name: run tests
+  import_playbook: run-tests-single-node-playbook.yml

+ 4 - 0
devops/ansible/build-image-tasklist.yml

@@ -0,0 +1,4 @@
+- name: create testing node docker image
+  shell: ./scripts/build-joystream-testing-node-docker-image.sh
+  args:
+    chdir: ../../

+ 34 - 0
devops/ansible/docker-compose.yml

@@ -0,0 +1,34 @@
+version: "3"
+services:
+  node_alice:
+    image: joystream/node-testing
+    container_name: alice
+    entrypoint: ./node --chain=chainspec.json --alice --validator --ws-external --rpc-cors=all
+    ports:
+      - "30333:30333"
+      - "9933:9933"
+      - "9944:9944"
+    networks:
+      testing_net:
+        ipv4_address: 172.28.1.1
+
+  node_bob:
+    image: joystream/node-testing
+    container_name: bob
+    entrypoint: ./node --chain=chainspec.json --bob --ws-external --rpc-cors=all
+    ports:
+      - "30335:30333"
+      - "9935:9933"
+      - "9945:9944"
+    links:
+      - "node_alice:alice"
+    networks:
+      testing_net:
+        ipv4_address: 172.28.1.2
+
+networks:
+  testing_net:
+    ipam:
+      driver: default
+      config:
+        - subnet: 172.28.0.0/16

+ 2 - 0
devops/ansible/hosts

@@ -0,0 +1,2 @@
+[sites]
+127.0.0.1 ansible_connection=local

+ 15 - 0
devops/ansible/install-dependencies-playbook.yml

@@ -0,0 +1,15 @@
+- hosts: 127.0.0.1
+  user: root
+  become: yes
+  become_method: sudo
+  tasks:
+    - name: install pip
+      apt: name=python-pip state=present
+    - name: install docker
+      pip: name=docker
+    - name: Install yarn with npm
+      npm:
+        name: yarn
+        global: yes
+    - name: Install docker compose
+      pip: name=docker-compose

+ 24 - 0
devops/ansible/run-tests-single-node-playbook.yml

@@ -0,0 +1,24 @@
+- hosts: 127.0.0.1
+  user: root
+  become: yes
+  become_method: sudo
+  tasks:
+    - name: run network
+      block:
+        - name: run docker container
+          docker_container:
+            name: "joystream-node-testing"
+            image: "joystream/node-testing"
+            ports:
+              - "9944:9944"
+            entrypoint: ./node --chain=chainspec.json --alice --validator --ws-external --rpc-cors=all
+            state: started
+        - name: execute network tests
+          shell: yarn debug >> ../../.tmp/tests.log
+          args:
+            chdir: ../../tests/network-tests/
+      always:
+        - name: stop docker container
+          docker_container:
+            name: "joystream-node-testing"
+            state: absent

+ 24 - 0
devops/ansible/run-tests-two-nodes-playbook.yml

@@ -0,0 +1,24 @@
+- name: install dependencies
+  import_playbook: install-dependencies-playbook.yml
+
+- hosts: 127.0.0.1
+  user: root
+  become: yes
+  become_method: sudo
+
+  tasks:
+    - name: run network
+      block:
+        - name: run two nodes containerized network
+          docker_compose:
+            project_src: ./
+            state: present
+        - name: execute network tests
+          shell: yarn test >> ../../.tmp/tests.log
+          args:
+            chdir: ../../tests/network-tests/
+      always:
+        - name: stop containers
+          docker_compose:
+            project_src: ./
+            state: absent

+ 37 - 0
devops/dockerfiles/ansible-node/Dockerfile

@@ -0,0 +1,37 @@
+FROM joystream/rust-builder AS builder
+LABEL description="Compiles all workspace artifacts"
+WORKDIR /joystream
+COPY . /joystream
+
+# Build joystream-node and its dependencies - runtime
+RUN cargo build --release -p joystream-node
+RUN /joystream/scripts/create-test-chainspec.sh
+
+FROM debian:stretch
+LABEL description="Joystream node"
+WORKDIR /joystream
+COPY --from=builder /joystream/target/release/joystream-node /joystream/node
+COPY --from=builder /joystream/target/release/wbuild/joystream-node-runtime/joystream_node_runtime.compact.wasm /joystream/runtime.compact.wasm
+COPY --from=builder /joystream/.tmp/chainspec.json /joystream/chainspec.json
+
+# confirm it works
+RUN /joystream/node --version
+
+# https://manpages.debian.org/stretch/coreutils/b2sum.1.en.html
+# RUN apt-get install coreutils
+# print the blake2 256 hash of the wasm blob
+RUN b2sum -l 256 /joystream/runtime.compact.wasm
+# print the blake2 512 hash of the wasm blob
+RUN b2sum -l 512 /joystream/runtime.compact.wasm
+
+EXPOSE 30333 9933 9944
+
+# Use these volumes to persits chain state and keystore, eg.:
+# --base-path /data
+# optionally separate keystore (otherwise it will be stored in the base path)
+# --keystore-path /keystore
+# if base-path isn't specified, chain state is stored inside container in ~/.local/share/joystream-node/
+# which is not ideal
+VOLUME ["/data", "/keystore"]
+
+ENTRYPOINT ["/joystream/node"]

+ 7 - 0
scripts/build-joystream-testing-node-docker-image.sh

@@ -0,0 +1,7 @@
+#!/usr/bin/env bash
+
+# Build the node and runtime image
+docker build --tag joystream/node-testing \
+    --file ./devops/dockerfiles/ansible-node/Dockerfile \
+    .
+

+ 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/manageWorkerAsWorkerTest.ts -T",
     "lint": "tslint --project tsconfig.json",
     "prettier": "prettier --write ./src"
   },

+ 60 - 0
tests/network-tests/src/nicaea/tests/bureaucracy/impl/manageWorkerAsWorker.ts

@@ -0,0 +1,60 @@
+import tap from 'tap';
+import BN from 'bn.js';
+import { assert } from 'chai';
+import { ApiWrapper } from '../../../utils/apiWrapper';
+import { KeyringPair } from '@polkadot/keyring/types';
+import { Keyring } from '@polkadot/api';
+import { v4 as uuid } from 'uuid';
+
+export function manageWorkerAsWorker(
+  apiWrapper: ApiWrapper,
+  membersKeyPairs: KeyringPair[],
+  keyring: Keyring,
+  sudoUri: string
+) {
+  let sudo: KeyringPair;
+
+  tap.test('Manage worker as worker', async () => {
+    // Fee estimation and transfer
+    sudo = keyring.addFromUri(sudoUri);
+    const increaseStakeFee: BN = apiWrapper.estimateIncreaseWorkerStakeFee();
+    const updateRoleAccountFee: BN = apiWrapper.estimateUpdateRoleAccountFee(sudo.address);
+    const updateRewardAccountFee: BN = apiWrapper.estimateUpdateRewardAccountFee(sudo.address);
+    const stakeIncrement: BN = new BN(4);
+    const leaveWorkerRole: BN = apiWrapper.estimateLeaveWorkerRoleFee('some text longer than expected in test');
+    await apiWrapper.transferBalance(
+      sudo,
+      membersKeyPairs[0].address,
+      increaseStakeFee.add(updateRoleAccountFee).add(updateRewardAccountFee).add(leaveWorkerRole).add(stakeIncrement)
+    );
+    const workerId: BN = await apiWrapper.getWorkerIdByRoleAccount(membersKeyPairs[0].address);
+
+    // Increase worker stake
+    const increasedWorkerStake: BN = (await apiWrapper.getWorkerStakeAmount(workerId)).add(stakeIncrement);
+    await apiWrapper.increaseWorkerStake(membersKeyPairs[0], workerId, stakeIncrement);
+    const newWorkerStake: BN = await apiWrapper.getWorkerStakeAmount(workerId);
+    assert(
+      increasedWorkerStake.eq(newWorkerStake),
+      `Unexpected worker stake ${newWorkerStake}, expected ${increasedWorkerStake}`
+    );
+
+    // Update reward account
+    const createdAccount: KeyringPair = keyring.addFromUri(uuid().substring(0, 8));
+    await apiWrapper.updateRewardAccount(membersKeyPairs[0], workerId, createdAccount.address);
+    const newRewardAccount: string = await apiWrapper.getWorkerRewardAccount(workerId);
+    assert(
+      newRewardAccount === createdAccount.address,
+      `Unexpected role account ${newRewardAccount}, expected ${createdAccount.address}`
+    );
+
+    // Update role account
+    await apiWrapper.updateRoleAccount(membersKeyPairs[0], workerId, createdAccount.address);
+    const newRoleAccount: string = (await apiWrapper.getWorker(workerId)).role_account.toString();
+    assert(
+      newRoleAccount === createdAccount.address,
+      `Unexpected role account ${newRoleAccount}, expected ${createdAccount.address}`
+    );
+
+    membersKeyPairs[0] = createdAccount;
+  });
+}

+ 40 - 0
tests/network-tests/src/nicaea/tests/bureaucracy/manageWorkerAsWorkerTest.ts

@@ -0,0 +1,40 @@
+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 { workerApplicationHappyCase } from './impl/workerApplicationHappyCase';
+import BN from 'bn.js';
+import { manageWorkerAsWorker } from './impl/manageWorkerAsWorker';
+
+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 = 100;
+
+  const provider = new WsProvider(nodeUrl);
+  const apiWrapper: ApiWrapper = await ApiWrapper.create(provider);
+
+  setTestTimeout(apiWrapper, durationInBlocks);
+  membershipTest(apiWrapper, nKeyPairs, keyring, N, paidTerms, sudoUri);
+  membershipTest(apiWrapper, leadKeyPair, keyring, N, paidTerms, sudoUri);
+  workerApplicationHappyCase(apiWrapper, nKeyPairs, leadKeyPair, keyring, sudoUri, applicationStake, roleStake);
+  manageWorkerAsWorker(apiWrapper, nKeyPairs, keyring, sudoUri);
+
+  closeApi(apiWrapper);
+});

+ 1 - 1
tests/network-tests/src/nicaea/tests/bureaucracy/workerApplicationHappyCaseTest.ts

@@ -24,7 +24,7 @@ tap.mocha.describe('Worker application happy case scenario', async () => {
   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 = 100;
+  const durationInBlocks: number = 21;
 
   const provider = new WsProvider(nodeUrl);
   const apiWrapper: ApiWrapper = await ApiWrapper.create(provider);

+ 70 - 1
tests/network-tests/src/nicaea/utils/apiWrapper.ts

@@ -5,7 +5,7 @@ import { KeyringPair } from '@polkadot/keyring/types';
 import { UserInfo, PaidMembershipTerms, MemberId } from '@nicaea/types/lib/members';
 import { Mint, MintId } from '@nicaea/types/lib/mint';
 import { Lead, LeadId } from '@nicaea/types/lib/content-working-group';
-import { WorkerOpening, Worker } from '@nicaea/types/lib/bureaucracy';
+import { WorkerOpening, Worker, WorkerId } from '@nicaea/types/lib/bureaucracy';
 import { RoleParameters } from '@nicaea/types/lib/roles';
 import { Seat } from '@nicaea/types';
 import { Balance, EventRecord, AccountId, BlockNumber, BalanceOf } from '@polkadot/types/interfaces';
@@ -13,6 +13,8 @@ import BN from 'bn.js';
 import { SubmittableExtrinsic } from '@polkadot/api/types';
 import { Sender } from './sender';
 import { Utils } from './utils';
+import { Stake, StakedState } from '@nicaea/types/lib/stake';
+import { RewardRelationship } from '@nicaea/types/lib/recurring-rewards';
 
 export class ApiWrapper {
   private readonly api: ApiPromise;
@@ -286,6 +288,22 @@ export class ApiWrapper {
     );
   }
 
+  public estimateIncreaseWorkerStakeFee(): BN {
+    return this.estimateTxFee(this.api.tx.forumBureaucracy.increaseWorkerStake(0, 0));
+  }
+
+  public estimateUpdateRoleAccountFee(address: string): BN {
+    return this.estimateTxFee(this.api.tx.forumBureaucracy.updateWorkerRoleAccount(0, address));
+  }
+
+  public estimateUpdateRewardAccountFee(address: string): BN {
+    return this.estimateTxFee(this.api.tx.forumBureaucracy.updateWorkerRewardAccount(0, address));
+  }
+
+  public estimateLeaveWorkerRoleFee(text: string): BN {
+    return this.estimateTxFee(this.api.tx.forumBureaucracy.leaveWorkerRole(0, text));
+  }
+
   private applyForCouncilElection(account: KeyringPair, amount: BN): Promise<void> {
     return this.sender.signAndSend(this.api.tx.councilElection.apply(amount), account, false);
   }
@@ -862,6 +880,26 @@ export class ApiWrapper {
     );
   }
 
+  public async increaseWorkerStake(account: KeyringPair, workerId: BN, stake: BN): Promise<void> {
+    return this.sender.signAndSend(this.api.tx.forumBureaucracy.increaseWorkerStake(workerId, stake), account, false);
+  }
+
+  public async updateRoleAccount(account: KeyringPair, workerId: BN, newRoleAccount: string): Promise<void> {
+    return this.sender.signAndSend(
+      this.api.tx.forumBureaucracy.updateWorkerRoleAccount(workerId, newRoleAccount),
+      account,
+      false
+    );
+  }
+
+  public async updateRewardAccount(account: KeyringPair, workerId: BN, newRewardAccount: string): Promise<void> {
+    return this.sender.signAndSend(
+      this.api.tx.forumBureaucracy.updateWorkerRewardAccount(workerId, newRewardAccount),
+      account,
+      false
+    );
+  }
+
   public async getStorageRoleParameters(): Promise<RoleParameters> {
     return (await this.api.query.actors.parameters<Option<RoleParameters>>('StorageProvider')).unwrap();
   }
@@ -917,4 +955,35 @@ export class ApiWrapper {
   public async getWorker(id: BN): Promise<Worker> {
     return ((await this.api.query.forumBureaucracy.workerById<Codec[]>(id))[0] as unknown) as Worker;
   }
+
+  public async getWorkerIdByRoleAccount(address: string): Promise<BN> {
+    const workersAndIds = await this.api.query.forumBureaucracy.workerById<Codec[]>();
+    const workers: Worker[] = (workersAndIds[1] as unknown) as Worker[];
+    const ids: WorkerId[] = (workersAndIds[0] as unknown) as WorkerId[];
+    let index: number;
+    workers.forEach((worker, i) => {
+      if (worker.role_account.toString() === address) index = i;
+    });
+    return ids[index!];
+  }
+
+  public async getStake(id: BN): Promise<Stake> {
+    return ((await this.api.query.stake.stakes<Codec[]>(id))[0] as unknown) as Stake;
+  }
+
+  public async getWorkerStakeAmount(workerId: BN): Promise<BN> {
+    let stakeId: BN = (await this.getWorker(workerId)).role_stake_profile.unwrap().stake_id;
+    return (((await this.getStake(stakeId)).staking_status.value as unknown) as StakedState).staked_amount;
+  }
+
+  public async getRewardRelationship(id: BN): Promise<RewardRelationship> {
+    return ((
+      await this.api.query.recurringRewards.rewardRelationships<Codec[]>(id)
+    )[0] as unknown) as RewardRelationship;
+  }
+
+  public async getWorkerRewardAccount(workerId: BN): Promise<string> {
+    let rewardRelationshipId: BN = (await this.getWorker(workerId)).reward_relationship.unwrap();
+    return (await this.getRewardRelationship(rewardRelationshipId)).getField('account').toString();
+  }
 }