Browse Source

Merge branch 'babylon' into docker-apps

Mokhtar Naamani 4 years ago
parent
commit
153fe8db9c
100 changed files with 4622 additions and 5155 deletions
  1. 4 4
      .github/workflows/run-network-tests.yml
  2. 1 1
      README.md
  3. 1 0
      cli/.eslintignore
  4. 7 3
      cli/.eslintrc.js
  5. 246 18
      cli/README.md
  6. 5 1
      cli/package.json
  7. 40 1
      cli/src/Api.ts
  8. 5 1
      cli/src/Types.ts
  9. 58 37
      cli/src/base/ApiCommandBase.ts
  10. 166 0
      cli/src/base/ContentDirectoryCommandBase.ts
  11. 1 1
      cli/src/commands/api/setUri.ts
  12. 67 0
      cli/src/commands/content-directory/addClassSchema.ts
  13. 42 0
      cli/src/commands/content-directory/addCuratorToGroup.ts
  14. 44 0
      cli/src/commands/content-directory/addMaintainerToClass.ts
  15. 54 0
      cli/src/commands/content-directory/class.ts
  16. 24 0
      cli/src/commands/content-directory/classes.ts
  17. 44 0
      cli/src/commands/content-directory/createClass.ts
  18. 18 0
      cli/src/commands/content-directory/createCuratorGroup.ts
  19. 39 0
      cli/src/commands/content-directory/curatorGroup.ts
  20. 21 0
      cli/src/commands/content-directory/curatorGroups.ts
  21. 40 0
      cli/src/commands/content-directory/entities.ts
  22. 37 0
      cli/src/commands/content-directory/entity.ts
  23. 30 0
      cli/src/commands/content-directory/removeCuratorGroup.ts
  24. 44 0
      cli/src/commands/content-directory/removeMaintainerFromClass.ts
  25. 61 0
      cli/src/commands/content-directory/setCuratorGroupStatus.ts
  26. 55 0
      cli/src/commands/content-directory/updateClassPermissions.ts
  27. 1 3
      cli/src/commands/working-groups/createOpening.ts
  28. 1 1
      cli/src/commands/working-groups/decreaseWorkerStake.ts
  29. 1 1
      cli/src/commands/working-groups/evictWorker.ts
  30. 1 1
      cli/src/commands/working-groups/fillOpening.ts
  31. 1 4
      cli/src/commands/working-groups/increaseStake.ts
  32. 1 1
      cli/src/commands/working-groups/leaveRole.ts
  33. 1 1
      cli/src/commands/working-groups/slashWorker.ts
  34. 1 1
      cli/src/commands/working-groups/startAcceptingApplications.ts
  35. 1 1
      cli/src/commands/working-groups/startReviewPeriod.ts
  36. 1 1
      cli/src/commands/working-groups/terminateApplication.ts
  37. 1 1
      cli/src/commands/working-groups/updateRewardAccount.ts
  38. 1 1
      cli/src/commands/working-groups/updateRoleAccount.ts
  39. 1 1
      cli/src/commands/working-groups/updateWorkerReward.ts
  40. 68 0
      cli/src/helpers/InputOutput.ts
  41. 206 0
      cli/src/helpers/JsonSchemaPrompt.ts
  42. 1 0
      cli/src/helpers/display.ts
  43. 9 0
      cli/src/helpers/prompting.ts
  44. 3 1
      cli/tsconfig.json
  45. 1 10
      docker-compose-with-storage.yml
  46. 1 1
      docker-compose.yml
  47. 2 2
      tests/network-tests/.env
  48. 5 6
      tests/network-tests/package.json
  49. 2 1
      tests/network-tests/run-tests.sh
  50. 313 330
      tests/network-tests/src/Api.ts
  51. 30 0
      tests/network-tests/src/Fixture.ts
  52. 34 0
      tests/network-tests/src/fixtures/councilElectionHappyCase.ts
  53. 112 0
      tests/network-tests/src/fixtures/councilElectionModule.ts
  54. 94 0
      tests/network-tests/src/fixtures/membershipModule.ts
  55. 801 0
      tests/network-tests/src/fixtures/proposalsModule.ts
  56. 100 0
      tests/network-tests/src/fixtures/sudoHireLead.ts
  57. 770 0
      tests/network-tests/src/fixtures/workingGroupModule.ts
  58. 36 0
      tests/network-tests/src/flows/membership/creatingMemberships.ts
  59. 46 0
      tests/network-tests/src/flows/proposals/councilSetup.ts
  60. 15 0
      tests/network-tests/src/flows/proposals/electionParametersProposal.ts
  61. 179 0
      tests/network-tests/src/flows/proposals/manageLeaderRole.ts
  62. 20 0
      tests/network-tests/src/flows/proposals/spendingProposal.ts
  63. 14 0
      tests/network-tests/src/flows/proposals/textProposal.ts
  64. 28 0
      tests/network-tests/src/flows/proposals/updateRuntime.ts
  65. 21 0
      tests/network-tests/src/flows/proposals/validatorCountProposal.ts
  66. 32 0
      tests/network-tests/src/flows/proposals/workingGroupMintCapacityProposal.ts
  67. 45 0
      tests/network-tests/src/flows/workingGroup/atLeastValueBug.ts
  68. 40 0
      tests/network-tests/src/flows/workingGroup/leaderSetup.ts
  69. 94 0
      tests/network-tests/src/flows/workingGroup/manageWorkerAsLead.ts
  70. 90 0
      tests/network-tests/src/flows/workingGroup/manageWorkerAsWorker.ts
  71. 98 0
      tests/network-tests/src/flows/workingGroup/workerPayout.ts
  72. 74 0
      tests/network-tests/src/scenarios/full.ts
  73. 71 0
      tests/network-tests/src/sender.ts
  74. 0 106
      tests/network-tests/src/services/dbService.ts
  75. 0 0
      tests/network-tests/src/tap-parallel-not-ok
  76. 0 64
      tests/network-tests/src/tests/council/electingCouncilTest.ts
  77. 0 57
      tests/network-tests/src/tests/councilSetup.ts
  78. 0 68
      tests/network-tests/src/tests/fixtures/councilElectionHappyCase.ts
  79. 0 140
      tests/network-tests/src/tests/fixtures/councilElectionModule.ts
  80. 0 3
      tests/network-tests/src/tests/fixtures/interfaces/fixture.ts
  81. 0 126
      tests/network-tests/src/tests/fixtures/leaderHiringHappyCase.ts
  82. 0 112
      tests/network-tests/src/tests/fixtures/membershipModule.ts
  83. 0 1095
      tests/network-tests/src/tests/fixtures/proposalsModule.ts
  84. 0 1147
      tests/network-tests/src/tests/fixtures/workingGroupModule.ts
  85. 0 62
      tests/network-tests/src/tests/leaderSetup.ts
  86. 0 56
      tests/network-tests/src/tests/membership/membershipCreationTest.ts
  87. 0 70
      tests/network-tests/src/tests/proposals/contentWorkingGroupMintCapacityProposalTest.ts
  88. 0 65
      tests/network-tests/src/tests/proposals/electionParametersProposalTest.ts
  89. 0 298
      tests/network-tests/src/tests/proposals/manageLeaderRoleTest.ts
  90. 0 65
      tests/network-tests/src/tests/proposals/setLeadProposalTest.ts
  91. 0 68
      tests/network-tests/src/tests/proposals/spendingProposalTest.ts
  92. 0 59
      tests/network-tests/src/tests/proposals/textProposalTest.ts
  93. 0 75
      tests/network-tests/src/tests/proposals/updateRuntime.ts
  94. 0 66
      tests/network-tests/src/tests/proposals/validatorCountProposalTest.ts
  95. 0 87
      tests/network-tests/src/tests/proposals/workingGroupMintCapacityProposalTest.ts
  96. 0 107
      tests/network-tests/src/tests/workingGroup/atLeastValueBugTest.ts
  97. 0 238
      tests/network-tests/src/tests/workingGroup/manageWorkerAsLeadTest.ts
  98. 0 165
      tests/network-tests/src/tests/workingGroup/manageWorkerAsWorkerTest.ts
  99. 0 160
      tests/network-tests/src/tests/workingGroup/workerApplicationHappyCaseTest.ts
  100. 0 160
      tests/network-tests/src/tests/workingGroup/workerApplicationRejectionCaseTest.ts

+ 4 - 4
.github/workflows/run-network-tests.yml

@@ -104,7 +104,7 @@ jobs:
         run: tests/network-tests/run-tests.sh
 
   network_tests_2:
-    name: Query Node Tests (Placeholder)
+    name: Content Directory Initialization
     if: contains(github.event.pull_request.labels.*.name, 'run-network-tests')
     needs: build_images
     runs-on: ubuntu-latest
@@ -124,11 +124,11 @@ jobs:
       - name: Install packages and dependencies
         run: yarn install --frozen-lockfile
       - name: Ensure tests are runnable
-        run: yarn workspace network-tests build
+        run: yarn workspace cd-schemas checks --quiet
       - name: Start chain
         run: docker-compose up -d
-      # - name: Execute network tests
-      #   run: yarn workspace network-tests test
+      - name: Initialize the content directory
+        run: yarn workspace cd-schemas initialize:dev
 
   network_tests_3:
     name: Storage Node Tests

+ 1 - 1
README.md

@@ -109,7 +109,7 @@ A step by step guide to setup a full node and validator on the Joystream testnet
 
 ```bash
 docker-compose up -d
-yarn workspace network-tests test
+DEBUG=* yarn workspace network-tests test-run src/scenarios/full.ts
 docker-compose down
 ```
 

+ 1 - 0
cli/.eslintignore

@@ -1 +1,2 @@
 /lib
+.eslintrc.js

+ 7 - 3
cli/.eslintrc.js

@@ -2,6 +2,9 @@ module.exports = {
   env: {
     mocha: true,
   },
+  parserOptions: {
+    project: './tsconfig.json'
+  },
   extends: [
     // The oclif rules have some code-style/formatting rules which may conflict with
     // our prettier global settings. Disabling for now
@@ -11,7 +14,8 @@ module.exports = {
     // "oclif-typescript",
   ],
   rules: {
-    "no-unused-vars": "off", // Required by the typescript rule below
-    "@typescript-eslint/no-unused-vars": ["error"]
-  }
+    'no-unused-vars': 'off', // Required by the typescript rule below
+    '@typescript-eslint/no-unused-vars': ['error'],
+    '@typescript-eslint/no-floating-promises': 'error',
+  },
 }

+ 246 - 18
cli/README.md

@@ -44,7 +44,7 @@ $ npm install -g @joystream/cli
 $ joystream-cli COMMAND
 running command...
 $ joystream-cli (-v|--version|version)
-@joystream/cli/0.1.0 linux-x64 node-v13.12.0
+@joystream/cli/0.2.0 linux-x64 node-v13.12.0
 $ joystream-cli --help [COMMAND]
 USAGE
   $ joystream-cli COMMAND
@@ -76,6 +76,21 @@ When using the CLI for the first time there are a few common steps you might wan
 * [`joystream-cli api:inspect`](#joystream-cli-apiinspect)
 * [`joystream-cli api:setUri [URI]`](#joystream-cli-apiseturi-uri)
 * [`joystream-cli autocomplete [SHELL]`](#joystream-cli-autocomplete-shell)
+* [`joystream-cli content-directory:addClassSchema`](#joystream-cli-content-directoryaddclassschema)
+* [`joystream-cli content-directory:addCuratorToGroup [GROUPID] [CURATORID]`](#joystream-cli-content-directoryaddcuratortogroup-groupid-curatorid)
+* [`joystream-cli content-directory:addMaintainerToClass [CLASSNAME] [GROUPID]`](#joystream-cli-content-directoryaddmaintainertoclass-classname-groupid)
+* [`joystream-cli content-directory:class CLASSNAME`](#joystream-cli-content-directoryclass-classname)
+* [`joystream-cli content-directory:classes`](#joystream-cli-content-directoryclasses)
+* [`joystream-cli content-directory:createClass`](#joystream-cli-content-directorycreateclass)
+* [`joystream-cli content-directory:createCuratorGroup`](#joystream-cli-content-directorycreatecuratorgroup)
+* [`joystream-cli content-directory:curatorGroup ID`](#joystream-cli-content-directorycuratorgroup-id)
+* [`joystream-cli content-directory:curatorGroups`](#joystream-cli-content-directorycuratorgroups)
+* [`joystream-cli content-directory:entities CLASSNAME [PROPERTIES]`](#joystream-cli-content-directoryentities-classname-properties)
+* [`joystream-cli content-directory:entity ID`](#joystream-cli-content-directoryentity-id)
+* [`joystream-cli content-directory:removeCuratorGroup [ID]`](#joystream-cli-content-directoryremovecuratorgroup-id)
+* [`joystream-cli content-directory:removeMaintainerFromClass [CLASSNAME] [GROUPID]`](#joystream-cli-content-directoryremovemaintainerfromclass-classname-groupid)
+* [`joystream-cli content-directory:setCuratorGroupStatus [ID] [STATUS]`](#joystream-cli-content-directorysetcuratorgroupstatus-id-status)
+* [`joystream-cli content-directory:updateClassPermissions [CLASSNAME]`](#joystream-cli-content-directoryupdateclasspermissions-classname)
 * [`joystream-cli council:info`](#joystream-cli-councilinfo)
 * [`joystream-cli help [COMMAND]`](#joystream-cli-help-command)
 * [`joystream-cli working-groups:application WGAPPLICATIONID`](#joystream-cli-working-groupsapplication-wgapplicationid)
@@ -288,6 +303,219 @@ EXAMPLES
 
 _See code: [@oclif/plugin-autocomplete](https://github.com/oclif/plugin-autocomplete/blob/v0.2.0/src/commands/autocomplete/index.ts)_
 
+## `joystream-cli content-directory:addClassSchema`
+
+Add a new schema to a class inside content directory. Requires lead access.
+
+```
+USAGE
+  $ joystream-cli content-directory:addClassSchema
+
+OPTIONS
+  -i, --input=input    Path to JSON file to use as input (if not specified - the input can be provided interactively)
+  -o, --output=output  Path where the output JSON file should be placed (can be then reused as input)
+```
+
+_See code: [src/commands/content-directory/addClassSchema.ts](https://github.com/Joystream/joystream/blob/master/cli/src/commands/content-directory/addClassSchema.ts)_
+
+## `joystream-cli content-directory:addCuratorToGroup [GROUPID] [CURATORID]`
+
+Add Curator to existing Curator Group.
+
+```
+USAGE
+  $ joystream-cli content-directory:addCuratorToGroup [GROUPID] [CURATORID]
+
+ARGUMENTS
+  GROUPID    ID of the Curator Group
+  CURATORID  ID of the curator
+```
+
+_See code: [src/commands/content-directory/addCuratorToGroup.ts](https://github.com/Joystream/joystream/blob/master/cli/src/commands/content-directory/addCuratorToGroup.ts)_
+
+## `joystream-cli content-directory:addMaintainerToClass [CLASSNAME] [GROUPID]`
+
+Add maintainer (Curator Group) to a class.
+
+```
+USAGE
+  $ joystream-cli content-directory:addMaintainerToClass [CLASSNAME] [GROUPID]
+
+ARGUMENTS
+  CLASSNAME  Name or ID of the class (ie. Video)
+  GROUPID    ID of the Curator Group to add as class maintainer
+```
+
+_See code: [src/commands/content-directory/addMaintainerToClass.ts](https://github.com/Joystream/joystream/blob/master/cli/src/commands/content-directory/addMaintainerToClass.ts)_
+
+## `joystream-cli content-directory:class CLASSNAME`
+
+Show Class details by id or name.
+
+```
+USAGE
+  $ joystream-cli content-directory:class CLASSNAME
+
+ARGUMENTS
+  CLASSNAME  Name or ID of the Class
+```
+
+_See code: [src/commands/content-directory/class.ts](https://github.com/Joystream/joystream/blob/master/cli/src/commands/content-directory/class.ts)_
+
+## `joystream-cli content-directory:classes`
+
+List existing content directory classes.
+
+```
+USAGE
+  $ joystream-cli content-directory:classes
+```
+
+_See code: [src/commands/content-directory/classes.ts](https://github.com/Joystream/joystream/blob/master/cli/src/commands/content-directory/classes.ts)_
+
+## `joystream-cli content-directory:createClass`
+
+Create class inside content directory. Requires lead access.
+
+```
+USAGE
+  $ joystream-cli content-directory:createClass
+
+OPTIONS
+  -i, --input=input    Path to JSON file to use as input (if not specified - the input can be provided interactively)
+  -o, --output=output  Path where the output JSON file should be placed (can be then reused as input)
+```
+
+_See code: [src/commands/content-directory/createClass.ts](https://github.com/Joystream/joystream/blob/master/cli/src/commands/content-directory/createClass.ts)_
+
+## `joystream-cli content-directory:createCuratorGroup`
+
+Create new Curator Group.
+
+```
+USAGE
+  $ joystream-cli content-directory:createCuratorGroup
+
+ALIASES
+  $ joystream-cli addCuratorGroup
+```
+
+_See code: [src/commands/content-directory/createCuratorGroup.ts](https://github.com/Joystream/joystream/blob/master/cli/src/commands/content-directory/createCuratorGroup.ts)_
+
+## `joystream-cli content-directory:curatorGroup ID`
+
+Show Curator Group details by ID.
+
+```
+USAGE
+  $ joystream-cli content-directory:curatorGroup ID
+
+ARGUMENTS
+  ID  ID of the Curator Group
+```
+
+_See code: [src/commands/content-directory/curatorGroup.ts](https://github.com/Joystream/joystream/blob/master/cli/src/commands/content-directory/curatorGroup.ts)_
+
+## `joystream-cli content-directory:curatorGroups`
+
+List existing Curator Groups.
+
+```
+USAGE
+  $ joystream-cli content-directory:curatorGroups
+```
+
+_See code: [src/commands/content-directory/curatorGroups.ts](https://github.com/Joystream/joystream/blob/master/cli/src/commands/content-directory/curatorGroups.ts)_
+
+## `joystream-cli content-directory:entities CLASSNAME [PROPERTIES]`
+
+Show entities list by class id or name.
+
+```
+USAGE
+  $ joystream-cli content-directory:entities CLASSNAME [PROPERTIES]
+
+ARGUMENTS
+  CLASSNAME   Name or ID of the Class
+
+  PROPERTIES  Comma-separated properties to include in the results table (ie. code,name). By default all property values
+              will be included.
+```
+
+_See code: [src/commands/content-directory/entities.ts](https://github.com/Joystream/joystream/blob/master/cli/src/commands/content-directory/entities.ts)_
+
+## `joystream-cli content-directory:entity ID`
+
+Show Entity details by id.
+
+```
+USAGE
+  $ joystream-cli content-directory:entity ID
+
+ARGUMENTS
+  ID  ID of the Entity
+```
+
+_See code: [src/commands/content-directory/entity.ts](https://github.com/Joystream/joystream/blob/master/cli/src/commands/content-directory/entity.ts)_
+
+## `joystream-cli content-directory:removeCuratorGroup [ID]`
+
+Remove existing Curator Group.
+
+```
+USAGE
+  $ joystream-cli content-directory:removeCuratorGroup [ID]
+
+ARGUMENTS
+  ID  ID of the Curator Group to remove
+```
+
+_See code: [src/commands/content-directory/removeCuratorGroup.ts](https://github.com/Joystream/joystream/blob/master/cli/src/commands/content-directory/removeCuratorGroup.ts)_
+
+## `joystream-cli content-directory:removeMaintainerFromClass [CLASSNAME] [GROUPID]`
+
+Remove maintainer (Curator Group) from class.
+
+```
+USAGE
+  $ joystream-cli content-directory:removeMaintainerFromClass [CLASSNAME] [GROUPID]
+
+ARGUMENTS
+  CLASSNAME  Name or ID of the class (ie. Video)
+  GROUPID    ID of the Curator Group to remove from maintainers
+```
+
+_See code: [src/commands/content-directory/removeMaintainerFromClass.ts](https://github.com/Joystream/joystream/blob/master/cli/src/commands/content-directory/removeMaintainerFromClass.ts)_
+
+## `joystream-cli content-directory:setCuratorGroupStatus [ID] [STATUS]`
+
+Set Curator Group status (Active/Inactive).
+
+```
+USAGE
+  $ joystream-cli content-directory:setCuratorGroupStatus [ID] [STATUS]
+
+ARGUMENTS
+  ID      ID of the Curator Group
+  STATUS  New status of the group (1 - active, 0 - inactive)
+```
+
+_See code: [src/commands/content-directory/setCuratorGroupStatus.ts](https://github.com/Joystream/joystream/blob/master/cli/src/commands/content-directory/setCuratorGroupStatus.ts)_
+
+## `joystream-cli content-directory:updateClassPermissions [CLASSNAME]`
+
+Update permissions in given class.
+
+```
+USAGE
+  $ joystream-cli content-directory:updateClassPermissions [CLASSNAME]
+
+ARGUMENTS
+  CLASSNAME  Name or ID of the class (ie. Video)
+```
+
+_See code: [src/commands/content-directory/updateClassPermissions.ts](https://github.com/Joystream/joystream/blob/master/cli/src/commands/content-directory/updateClassPermissions.ts)_
+
 ## `joystream-cli council:info`
 
 Get current council and council elections information
@@ -330,7 +558,7 @@ ARGUMENTS
 OPTIONS
   -g, --group=group  (required) [default: storageProviders] The working group context in which the command should be
                      executed
-                     Available values are: storageProviders.
+                     Available values are: storageProviders, curators.
 ```
 
 _See code: [src/commands/working-groups/application.ts](https://github.com/Joystream/joystream/blob/master/cli/src/commands/working-groups/application.ts)_
@@ -352,7 +580,7 @@ OPTIONS
 
   -g, --group=group          (required) [default: storageProviders] The working group context in which the command
                              should be executed
-                             Available values are: storageProviders.
+                             Available values are: storageProviders, curators.
 
   -n, --draftName=draftName  Name of the draft to create the opening from.
 
@@ -375,7 +603,7 @@ ARGUMENTS
 OPTIONS
   -g, --group=group  (required) [default: storageProviders] The working group context in which the command should be
                      executed
-                     Available values are: storageProviders.
+                     Available values are: storageProviders, curators.
 ```
 
 _See code: [src/commands/working-groups/decreaseWorkerStake.ts](https://github.com/Joystream/joystream/blob/master/cli/src/commands/working-groups/decreaseWorkerStake.ts)_
@@ -394,7 +622,7 @@ ARGUMENTS
 OPTIONS
   -g, --group=group  (required) [default: storageProviders] The working group context in which the command should be
                      executed
-                     Available values are: storageProviders.
+                     Available values are: storageProviders, curators.
 ```
 
 _See code: [src/commands/working-groups/evictWorker.ts](https://github.com/Joystream/joystream/blob/master/cli/src/commands/working-groups/evictWorker.ts)_
@@ -413,7 +641,7 @@ ARGUMENTS
 OPTIONS
   -g, --group=group  (required) [default: storageProviders] The working group context in which the command should be
                      executed
-                     Available values are: storageProviders.
+                     Available values are: storageProviders, curators.
 ```
 
 _See code: [src/commands/working-groups/fillOpening.ts](https://github.com/Joystream/joystream/blob/master/cli/src/commands/working-groups/fillOpening.ts)_
@@ -429,7 +657,7 @@ USAGE
 OPTIONS
   -g, --group=group  (required) [default: storageProviders] The working group context in which the command should be
                      executed
-                     Available values are: storageProviders.
+                     Available values are: storageProviders, curators.
 ```
 
 _See code: [src/commands/working-groups/increaseStake.ts](https://github.com/Joystream/joystream/blob/master/cli/src/commands/working-groups/increaseStake.ts)_
@@ -445,7 +673,7 @@ USAGE
 OPTIONS
   -g, --group=group  (required) [default: storageProviders] The working group context in which the command should be
                      executed
-                     Available values are: storageProviders.
+                     Available values are: storageProviders, curators.
 ```
 
 _See code: [src/commands/working-groups/leaveRole.ts](https://github.com/Joystream/joystream/blob/master/cli/src/commands/working-groups/leaveRole.ts)_
@@ -464,7 +692,7 @@ ARGUMENTS
 OPTIONS
   -g, --group=group  (required) [default: storageProviders] The working group context in which the command should be
                      executed
-                     Available values are: storageProviders.
+                     Available values are: storageProviders, curators.
 ```
 
 _See code: [src/commands/working-groups/opening.ts](https://github.com/Joystream/joystream/blob/master/cli/src/commands/working-groups/opening.ts)_
@@ -480,7 +708,7 @@ USAGE
 OPTIONS
   -g, --group=group  (required) [default: storageProviders] The working group context in which the command should be
                      executed
-                     Available values are: storageProviders.
+                     Available values are: storageProviders, curators.
 ```
 
 _See code: [src/commands/working-groups/openings.ts](https://github.com/Joystream/joystream/blob/master/cli/src/commands/working-groups/openings.ts)_
@@ -496,7 +724,7 @@ USAGE
 OPTIONS
   -g, --group=group  (required) [default: storageProviders] The working group context in which the command should be
                      executed
-                     Available values are: storageProviders.
+                     Available values are: storageProviders, curators.
 ```
 
 _See code: [src/commands/working-groups/overview.ts](https://github.com/Joystream/joystream/blob/master/cli/src/commands/working-groups/overview.ts)_
@@ -515,7 +743,7 @@ ARGUMENTS
 OPTIONS
   -g, --group=group  (required) [default: storageProviders] The working group context in which the command should be
                      executed
-                     Available values are: storageProviders.
+                     Available values are: storageProviders, curators.
 ```
 
 _See code: [src/commands/working-groups/slashWorker.ts](https://github.com/Joystream/joystream/blob/master/cli/src/commands/working-groups/slashWorker.ts)_
@@ -534,7 +762,7 @@ ARGUMENTS
 OPTIONS
   -g, --group=group  (required) [default: storageProviders] The working group context in which the command should be
                      executed
-                     Available values are: storageProviders.
+                     Available values are: storageProviders, curators.
 ```
 
 _See code: [src/commands/working-groups/startAcceptingApplications.ts](https://github.com/Joystream/joystream/blob/master/cli/src/commands/working-groups/startAcceptingApplications.ts)_
@@ -553,7 +781,7 @@ ARGUMENTS
 OPTIONS
   -g, --group=group  (required) [default: storageProviders] The working group context in which the command should be
                      executed
-                     Available values are: storageProviders.
+                     Available values are: storageProviders, curators.
 ```
 
 _See code: [src/commands/working-groups/startReviewPeriod.ts](https://github.com/Joystream/joystream/blob/master/cli/src/commands/working-groups/startReviewPeriod.ts)_
@@ -572,7 +800,7 @@ ARGUMENTS
 OPTIONS
   -g, --group=group  (required) [default: storageProviders] The working group context in which the command should be
                      executed
-                     Available values are: storageProviders.
+                     Available values are: storageProviders, curators.
 ```
 
 _See code: [src/commands/working-groups/terminateApplication.ts](https://github.com/Joystream/joystream/blob/master/cli/src/commands/working-groups/terminateApplication.ts)_
@@ -591,7 +819,7 @@ ARGUMENTS
 OPTIONS
   -g, --group=group  (required) [default: storageProviders] The working group context in which the command should be
                      executed
-                     Available values are: storageProviders.
+                     Available values are: storageProviders, curators.
 ```
 
 _See code: [src/commands/working-groups/updateRewardAccount.ts](https://github.com/Joystream/joystream/blob/master/cli/src/commands/working-groups/updateRewardAccount.ts)_
@@ -610,7 +838,7 @@ ARGUMENTS
 OPTIONS
   -g, --group=group  (required) [default: storageProviders] The working group context in which the command should be
                      executed
-                     Available values are: storageProviders.
+                     Available values are: storageProviders, curators.
 ```
 
 _See code: [src/commands/working-groups/updateRoleAccount.ts](https://github.com/Joystream/joystream/blob/master/cli/src/commands/working-groups/updateRoleAccount.ts)_
@@ -629,7 +857,7 @@ ARGUMENTS
 OPTIONS
   -g, --group=group  (required) [default: storageProviders] The working group context in which the command should be
                      executed
-                     Available values are: storageProviders.
+                     Available values are: storageProviders, curators.
 ```
 
 _See code: [src/commands/working-groups/updateWorkerReward.ts](https://github.com/Joystream/joystream/blob/master/cli/src/commands/working-groups/updateWorkerReward.ts)_

+ 5 - 1
cli/package.json

@@ -8,6 +8,7 @@
   },
   "bugs": "https://github.com/Joystream/joystream/issues",
   "dependencies": {
+    "@apidevtools/json-schema-ref-parser": "^9.0.6",
     "@joystream/types": "^0.14.0",
     "@oclif/command": "^1.5.19",
     "@oclif/config": "^1.14.0",
@@ -86,6 +87,9 @@
       },
       "working-groups": {
         "description": "Working group lead and worker actions"
+      },
+      "content-directory": {
+        "description": "Interactions with content directory module - managing classes, schemas, entities and permissions"
       }
     }
   },
@@ -101,7 +105,7 @@
     "test": "nyc --extension .ts mocha --forbid-only \"test/**/*.test.ts\"",
     "build": "tsc --build tsconfig.json",
     "version": "oclif-dev readme && git add README.md",
-    "lint": "eslint ./ --ext .ts",
+    "lint": "eslint ./src --ext .ts",
     "checks": "tsc --noEmit --pretty && prettier ./ --check && yarn lint",
     "format": "prettier ./ --write"
   },

+ 40 - 1
cli/src/Api.ts

@@ -47,6 +47,7 @@ import { RewardRelationship, RewardRelationshipId } from '@joystream/types/recur
 import { Stake, StakeId } from '@joystream/types/stake'
 
 import { InputValidationLengthConstraint } from '@joystream/types/common'
+import { Class, ClassId, CuratorGroup, CuratorGroupId, Entity, EntityId } from '@joystream/types/content-directory'
 
 export const DEFAULT_API_URI = 'ws://localhost:9944/'
 const DEFAULT_DECIMALS = new BN(12)
@@ -54,6 +55,7 @@ const DEFAULT_DECIMALS = new BN(12)
 // Mapping of working group to api module
 export const apiModuleByGroup: { [key in WorkingGroups]: string } = {
   [WorkingGroups.StorageProviders]: 'storageWorkingGroup',
+  [WorkingGroups.Curators]: 'contentDirectoryWorkingGroup',
 }
 
 // Api wrapper for handling most common api calls and allowing easy API implementation switch in the future
@@ -284,7 +286,7 @@ export default class Api {
   }
 
   async groupMembers(group: WorkingGroups): Promise<GroupMember[]> {
-    const workerEntries = await this.entriesByIds<WorkerId, Worker>(this.workingGroupApiQuery(group).workerById)
+    const workerEntries = await this.groupWorkers(group)
 
     const groupMembers: GroupMember[] = await Promise.all(
       workerEntries.map(([id, worker]) => this.parseGroupMember(id, worker))
@@ -293,6 +295,10 @@ export default class Api {
     return groupMembers.reverse() // Sort by newest
   }
 
+  groupWorkers(group: WorkingGroups): Promise<[WorkerId, Worker][]> {
+    return this.entriesByIds<WorkerId, Worker>(this.workingGroupApiQuery(group).workerById)
+  }
+
   async openingsByGroup(group: WorkingGroups): Promise<GroupOpening[]> {
     let openings: GroupOpening[] = []
     const nextId = await this.workingGroupApiQuery(group).nextOpeningId<OpeningId>()
@@ -473,4 +479,37 @@ export default class Api {
   async workerExitRationaleConstraint(group: WorkingGroups): Promise<InputValidationLengthConstraint> {
     return await this.workingGroupApiQuery(group).workerExitRationaleText<InputValidationLengthConstraint>()
   }
+
+  // Content directory
+  availableClasses(): Promise<[ClassId, Class][]> {
+    return this.entriesByIds<ClassId, Class>(this._api.query.contentDirectory.classById)
+  }
+
+  availableCuratorGroups(): Promise<[CuratorGroupId, CuratorGroup][]> {
+    return this.entriesByIds<CuratorGroupId, CuratorGroup>(this._api.query.contentDirectory.curatorGroupById)
+  }
+
+  async curatorGroupById(id: number): Promise<CuratorGroup | null> {
+    const exists = !!(await this._api.query.contentDirectory.curatorGroupById.size(id))
+    return exists ? await this._api.query.contentDirectory.curatorGroupById<CuratorGroup>(id) : null
+  }
+
+  async nextCuratorGroupId(): Promise<number> {
+    return (await this._api.query.contentDirectory.nextCuratorGroupId<CuratorGroupId>()).toNumber()
+  }
+
+  async classById(id: number): Promise<Class | null> {
+    const c = await this._api.query.contentDirectory.classById<Class>(id)
+    return c.isEmpty ? null : c
+  }
+
+  async entitiesByClassId(classId: number): Promise<[EntityId, Entity][]> {
+    const entityEntries = await this.entriesByIds<EntityId, Entity>(this._api.query.contentDirectory.entityById)
+    return entityEntries.filter(([, entity]) => entity.class_id.toNumber() === classId)
+  }
+
+  async entityById(id: number): Promise<Entity | null> {
+    const exists = !!(await this._api.query.contentDirectory.curatorGroupById.size(id))
+    return exists ? await this._api.query.contentDirectory.entityById<Entity>(id) : null
+  }
 }

+ 5 - 1
cli/src/Types.ts

@@ -87,10 +87,14 @@ export type NameValueObj = { name: string; value: string }
 // Working groups related types
 export enum WorkingGroups {
   StorageProviders = 'storageProviders',
+  Curators = 'curators',
 }
 
 // In contrast to Pioneer, currently only StorageProviders group is available in CLI
-export const AvailableGroups: readonly WorkingGroups[] = [WorkingGroups.StorageProviders] as const
+export const AvailableGroups: readonly WorkingGroups[] = [
+  WorkingGroups.StorageProviders,
+  WorkingGroups.Curators,
+] as const
 
 export type Reward = {
   totalRecieved: Balance

+ 58 - 37
cli/src/base/ApiCommandBase.ts

@@ -13,6 +13,9 @@ import { InterfaceTypes } from '@polkadot/types/types/registry'
 import ajv from 'ajv'
 import { ApiMethodArg, ApiMethodNamedArgs, ApiParamsOptions, ApiParamOptions } from '../Types'
 import { createParamOptions } from '../helpers/promptOptions'
+import { SubmittableExtrinsic } from '@polkadot/api/types'
+import { DistinctQuestion } from 'inquirer'
+import { BOOL_PROMPT_OPTIONS } from '../helpers/prompting'
 
 class ExtrinsicFailedError extends Error {}
 
@@ -131,9 +134,15 @@ export default abstract class ApiCommandBase extends StateAwareCommandBase {
     // If no default provided - get default value resulting from providing empty string
     const defaultValueString =
       paramOptions?.value?.default?.toString() || this.createType(typeDef.type as any, '').toString()
+
+    let typeSpecificOptions: DistinctQuestion = { type: 'input' }
+    if (typeDef.type === 'bool') {
+      typeSpecificOptions = BOOL_PROMPT_OPTIONS
+    }
+
     const providedValue = await this.simplePrompt({
       message: `Provide value for ${this.paramName(typeDef)}`,
-      type: 'input',
+      ...typeSpecificOptions,
       // We want to avoid showing default value like '0x', because it falsely suggests
       // that user needs to provide the value as hex
       default: (defaultValueString === '0x' ? '' : defaultValueString) || undefined,
@@ -313,6 +322,11 @@ export default abstract class ApiCommandBase extends StateAwareCommandBase {
     }
   }
 
+  // More typesafe version
+  async promptForType(type: keyof InterfaceTypes, options?: ApiParamOptions) {
+    return await this.promptForParam(type, options)
+  }
+
   async promptForJsonBytes(
     jsonStruct: Constructor<Struct>,
     argName?: string,
@@ -379,32 +393,30 @@ export default abstract class ApiCommandBase extends StateAwareCommandBase {
     return values
   }
 
-  sendExtrinsic(account: KeyringPair, module: string, method: string, params: CodecArg[]) {
+  sendExtrinsic(account: KeyringPair, tx: SubmittableExtrinsic<'promise'>) {
     return new Promise((resolve, reject) => {
-      const extrinsicMethod = this.getOriginalApi().tx[module][method]
       let unsubscribe: () => void
-      extrinsicMethod(...params)
-        .signAndSend(account, {}, (result) => {
-          // Implementation loosely based on /pioneer/packages/react-signer/src/Modal.tsx
-          if (!result || !result.status) {
-            return
-          }
-
-          if (result.status.isInBlock) {
-            unsubscribe()
-            result.events
-              .filter(({ event: { section } }): boolean => section === 'system')
-              .forEach(({ event: { method } }): void => {
-                if (method === 'ExtrinsicFailed') {
-                  reject(new ExtrinsicFailedError('Extrinsic execution error!'))
-                } else if (method === 'ExtrinsicSuccess') {
-                  resolve()
-                }
-              })
-          } else if (result.isError) {
-            reject(new ExtrinsicFailedError('Extrinsic execution error!'))
-          }
-        })
+      tx.signAndSend(account, {}, (result) => {
+        // Implementation loosely based on /pioneer/packages/react-signer/src/Modal.tsx
+        if (!result || !result.status) {
+          return
+        }
+
+        if (result.status.isInBlock) {
+          unsubscribe()
+          result.events
+            .filter(({ event: { section } }): boolean => section === 'system')
+            .forEach(({ event: { method } }): void => {
+              if (method === 'ExtrinsicFailed') {
+                reject(new ExtrinsicFailedError('Extrinsic execution error!'))
+              } else if (method === 'ExtrinsicSuccess') {
+                resolve()
+              }
+            })
+        } else if (result.isError) {
+          reject(new ExtrinsicFailedError('Extrinsic execution error!'))
+        }
+      })
         .then((unsubFunc) => (unsubscribe = unsubFunc))
         .catch((e) =>
           reject(new ExtrinsicFailedError(`Cannot send the extrinsic: ${e.message ? e.message : JSON.stringify(e)}`))
@@ -412,37 +424,46 @@ export default abstract class ApiCommandBase extends StateAwareCommandBase {
     })
   }
 
-  async sendAndFollowExtrinsic(
+  async sendAndFollowTx(
     account: KeyringPair,
-    module: string,
-    method: string,
-    params: CodecArg[],
-    warnOnly = false // If specified - only warning will be displayed (instead of error beeing thrown)
-  ) {
+    tx: SubmittableExtrinsic<'promise'>,
+    warnOnly = true // If specified - only warning will be displayed in case of failure (instead of error beeing thrown)
+  ): Promise<void> {
     try {
-      this.log(chalk.white(`\nSending ${module}.${method} extrinsic...`))
-      await this.sendExtrinsic(account, module, method, params)
+      await this.sendExtrinsic(account, tx)
       this.log(chalk.green(`Extrinsic successful!`))
     } catch (e) {
       if (e instanceof ExtrinsicFailedError && warnOnly) {
-        this.warn(`${module}.${method} extrinsic failed! ${e.message}`)
+        this.warn(`Extrinsic failed! ${e.message}`)
       } else if (e instanceof ExtrinsicFailedError) {
-        throw new CLIError(`${module}.${method} extrinsic failed! ${e.message}`, { exit: ExitCodes.ApiError })
+        throw new CLIError(`Extrinsic failed! ${e.message}`, { exit: ExitCodes.ApiError })
       } else {
         throw e
       }
     }
   }
 
+  async sendAndFollowNamedTx(
+    account: KeyringPair,
+    module: string,
+    method: string,
+    params: CodecArg[],
+    warnOnly = false
+  ): Promise<void> {
+    this.log(chalk.white(`\nSending ${module}.${method} extrinsic...`))
+    const tx = await this.getOriginalApi().tx[module][method](...params)
+    await this.sendAndFollowTx(account, tx, warnOnly)
+  }
+
   async buildAndSendExtrinsic(
     account: KeyringPair,
     module: string,
     method: string,
-    paramsOptions: ApiParamsOptions,
+    paramsOptions?: ApiParamsOptions,
     warnOnly = false // If specified - only warning will be displayed (instead of error beeing thrown)
   ): Promise<ApiMethodArg[]> {
     const params = await this.promptForExtrinsicParams(module, method, paramsOptions)
-    await this.sendAndFollowExtrinsic(account, module, method, params, warnOnly)
+    await this.sendAndFollowNamedTx(account, module, method, params, warnOnly)
 
     return params
   }

+ 166 - 0
cli/src/base/ContentDirectoryCommandBase.ts

@@ -0,0 +1,166 @@
+import ExitCodes from '../ExitCodes'
+import AccountsCommandBase from './AccountsCommandBase'
+import { WorkingGroups, NamedKeyringPair } from '../Types'
+import { ReferenceProperty } from 'cd-schemas/types/extrinsics/AddClassSchema'
+import { BOOL_PROMPT_OPTIONS } from '../helpers/prompting'
+import { Class, ClassId, CuratorGroup, CuratorGroupId, Entity } from '@joystream/types/content-directory'
+import { Worker } from '@joystream/types/working-group'
+import { CLIError } from '@oclif/errors'
+import { Codec } from '@polkadot/types/types'
+
+/**
+ * Abstract base class for commands related to working groups
+ */
+export default abstract class ContentDirectoryCommandBase extends AccountsCommandBase {
+  // Use when lead access is required in given command
+  async requireLead(): Promise<void> {
+    const selectedAccount: NamedKeyringPair = await this.getRequiredSelectedAccount()
+    const lead = await this.getApi().groupLead(WorkingGroups.Curators)
+
+    if (!lead || lead.roleAccount.toString() !== selectedAccount.address) {
+      this.error('Content Working Group Lead access required for this command!', { exit: ExitCodes.AccessDenied })
+    }
+  }
+
+  async promptForClass(message = 'Select a class'): Promise<Class> {
+    const classes = await this.getApi().availableClasses()
+    const choices = classes.map(([, c]) => ({ name: c.name.toString(), value: c }))
+    if (!choices.length) {
+      this.warn('No classes exist to choose from!')
+      this.exit(ExitCodes.InvalidInput)
+    }
+
+    const selectedClass = await this.simplePrompt({ message, type: 'list', choices })
+
+    return selectedClass
+  }
+
+  async classEntryByNameOrId(classNameOrId: string): Promise<[ClassId, Class]> {
+    const classes = await this.getApi().availableClasses()
+    const foundClass = classes.find(([id, c]) => id.toString() === classNameOrId || c.name.toString() === classNameOrId)
+    if (!foundClass) {
+      this.error(`Class id not found by class name or id: "${classNameOrId}"!`)
+    }
+
+    return foundClass
+  }
+
+  private async curatorGroupChoices(ids?: CuratorGroupId[]) {
+    const groups = await this.getApi().availableCuratorGroups()
+    return groups
+      .filter(([id]) => (ids ? ids.some((allowedId) => allowedId.eq(id)) : true))
+      .map(([id, group]) => ({
+        name:
+          `Group ${id.toString()} (` +
+          `${group.active.valueOf() ? 'Active' : 'Inactive'}, ` +
+          `${group.curators.toArray().length} member(s), ` +
+          `${group.number_of_classes_maintained.toNumber()} classes maintained)`,
+        value: id.toNumber(),
+      }))
+  }
+
+  async promptForCuratorGroup(message = 'Select a Curator Group', ids?: CuratorGroupId[]): Promise<number> {
+    const choices = await this.curatorGroupChoices(ids)
+    if (!choices.length) {
+      this.warn('No Curator Groups to choose from!')
+      this.exit(ExitCodes.InvalidInput)
+    }
+    const selectedId = await this.simplePrompt({ message, type: 'list', choices })
+
+    return selectedId
+  }
+
+  async promptForCuratorGroups(message = 'Select Curator Groups'): Promise<number[]> {
+    const choices = await this.curatorGroupChoices()
+    const selectedIds = await this.simplePrompt({ message, type: 'checkbox', choices })
+
+    return selectedIds
+  }
+
+  async promptForClassReference(): Promise<ReferenceProperty['Reference']> {
+    const selectedClass = await this.promptForClass()
+    const sameOwner = await this.simplePrompt({ message: 'Same owner required?', ...BOOL_PROMPT_OPTIONS })
+    return { className: selectedClass.name.toString(), sameOwner }
+  }
+
+  async promptForCurator(message = 'Choose a Curator'): Promise<number> {
+    const curators = await this.getApi().groupMembers(WorkingGroups.Curators)
+    const selectedCuratorId = await this.simplePrompt({
+      message,
+      type: 'list',
+      choices: curators.map((c) => ({
+        name: `${c.profile.handle.toString()} (Worker ID: ${c.workerId})`,
+        value: c.workerId,
+      })),
+    })
+
+    return selectedCuratorId
+  }
+
+  async getCurator(id: string | number): Promise<Worker> {
+    if (typeof id === 'string') {
+      id = parseInt(id)
+    }
+
+    let curator
+    try {
+      curator = await this.getApi().workerByWorkerId(WorkingGroups.Curators, id)
+    } catch (e) {
+      if (e instanceof CLIError) {
+        throw new CLIError('Invalid Curator id!')
+      }
+      throw e
+    }
+
+    return curator
+  }
+
+  async getCuratorGroup(id: string | number): Promise<CuratorGroup> {
+    if (typeof id === 'string') {
+      id = parseInt(id)
+    }
+
+    const group = await this.getApi().curatorGroupById(id)
+
+    if (!group) {
+      this.error('Invalid Curator Group id!', { exit: ExitCodes.InvalidInput })
+    }
+
+    return group
+  }
+
+  async getEntity(id: string | number): Promise<Entity> {
+    if (typeof id === 'string') {
+      id = parseInt(id)
+    }
+
+    const entity = await this.getApi().entityById(id)
+
+    if (!entity) {
+      this.error('Invalid entity id!', { exit: ExitCodes.InvalidInput })
+    }
+
+    return entity
+  }
+
+  parseEntityPropertyValues(
+    entity: Entity,
+    entityClass: Class,
+    includedProperties?: string[]
+  ): Record<string, { value: Codec; type: string }> {
+    const { properties } = entityClass
+    return Array.from(entity.getField('values').entries()).reduce((columns, [propId, propValue]) => {
+      const prop = properties[propId.toNumber()]
+      const propName = prop.name.toString()
+      const included = !includedProperties || includedProperties.some((p) => p.toLowerCase() === propName.toLowerCase())
+
+      if (included) {
+        columns[propName] = {
+          value: propValue.getValue(),
+          type: `${prop.property_type.type}<${prop.property_type.subtype}>`,
+        }
+      }
+      return columns
+    }, {} as Record<string, { value: Codec; type: string }>)
+  }
+}

+ 1 - 1
cli/src/commands/api/setUri.ts

@@ -16,7 +16,7 @@ export default class ApiSetUri extends ApiCommandBase {
 
   async init() {
     this.forceSkipApiUriPrompt = true
-    super.init()
+    await super.init()
   }
 
   async run() {

+ 67 - 0
cli/src/commands/content-directory/addClassSchema.ts

@@ -0,0 +1,67 @@
+import ContentDirectoryCommandBase from '../../base/ContentDirectoryCommandBase'
+import AddClassSchemaSchema from 'cd-schemas/schemas/extrinsics/AddClassSchema.schema.json'
+import { AddClassSchema } from 'cd-schemas/types/extrinsics/AddClassSchema'
+import { InputParser } from 'cd-schemas'
+import { JsonSchemaPrompter, JsonSchemaCustomPrompts } from '../../helpers/JsonSchemaPrompt'
+import { JSONSchema } from '@apidevtools/json-schema-ref-parser'
+import { IOFlags, getInputJson, saveOutputJson } from '../../helpers/InputOutput'
+import { Class } from '@joystream/types/content-directory'
+
+export default class AddClassSchemaCommand extends ContentDirectoryCommandBase {
+  static description = 'Add a new schema to a class inside content directory. Requires lead access.'
+
+  static flags = {
+    ...IOFlags,
+  }
+
+  async run() {
+    const account = await this.getRequiredSelectedAccount()
+    await this.requireLead()
+
+    const { input, output } = this.parse(AddClassSchemaCommand).flags
+
+    let inputJson = getInputJson<AddClassSchema>(input)
+    if (!inputJson) {
+      let selectedClass: Class | undefined
+      const customPrompts: JsonSchemaCustomPrompts = [
+        [
+          'className',
+          async () => {
+            selectedClass = await this.promptForClass('Select a class to add schema to')
+            return selectedClass.name.toString()
+          },
+        ],
+        [
+          'existingProperties',
+          async () =>
+            this.simplePrompt({
+              type: 'checkbox',
+              message: 'Choose existing properties to keep',
+              choices: selectedClass!.properties.map((p, i) => ({ name: `${i}: ${p.name.toString()}`, value: i })),
+            }),
+        ],
+        [/^newProperties\[\d+\]\.property_type\.Single\.Reference/, async () => this.promptForClassReference()],
+      ]
+
+      const prompter = new JsonSchemaPrompter<AddClassSchema>(
+        AddClassSchemaSchema as JSONSchema,
+        undefined,
+        customPrompts
+      )
+
+      inputJson = await prompter.promptAll()
+    }
+
+    this.jsonPrettyPrint(JSON.stringify(inputJson))
+    const confirmed = await this.simplePrompt({ type: 'confirm', message: 'Do you confirm the provided input?' })
+
+    if (confirmed) {
+      await this.requestAccountDecoding(account)
+      const inputParser = new InputParser(this.getOriginalApi())
+      this.log('Sending the extrinsic...')
+      await this.sendAndFollowTx(account, await inputParser.parseAddClassSchemaExtrinsic(inputJson), true)
+
+      saveOutputJson(output, `${inputJson.className}Schema.json`, inputJson)
+    }
+  }
+}

+ 42 - 0
cli/src/commands/content-directory/addCuratorToGroup.ts

@@ -0,0 +1,42 @@
+import ContentDirectoryCommandBase from '../../base/ContentDirectoryCommandBase'
+import chalk from 'chalk'
+
+export default class AddCuratorToGroupCommand extends ContentDirectoryCommandBase {
+  static description = 'Add Curator to existing Curator Group.'
+  static args = [
+    {
+      name: 'groupId',
+      required: false,
+      description: 'ID of the Curator Group',
+    },
+    {
+      name: 'curatorId',
+      required: false,
+      description: 'ID of the curator',
+    },
+  ]
+
+  async run() {
+    const account = await this.getRequiredSelectedAccount()
+    await this.requireLead()
+
+    let { groupId, curatorId } = this.parse(AddCuratorToGroupCommand).args
+
+    if (groupId === undefined) {
+      groupId = await this.promptForCuratorGroup()
+    } else {
+      await this.getCuratorGroup(groupId)
+    }
+
+    if (curatorId === undefined) {
+      curatorId = await this.promptForCurator()
+    } else {
+      await this.getCurator(curatorId)
+    }
+
+    await this.requestAccountDecoding(account)
+    await this.sendAndFollowNamedTx(account, 'contentDirectory', 'addCuratorToGroup', [groupId, curatorId])
+
+    console.log(chalk.green(`Curator ${chalk.white(curatorId)} succesfully added to group ${chalk.white(groupId)}!`))
+  }
+}

+ 44 - 0
cli/src/commands/content-directory/addMaintainerToClass.ts

@@ -0,0 +1,44 @@
+import ContentDirectoryCommandBase from '../../base/ContentDirectoryCommandBase'
+import chalk from 'chalk'
+
+export default class AddMaintainerToClassCommand extends ContentDirectoryCommandBase {
+  static description = 'Add maintainer (Curator Group) to a class.'
+  static args = [
+    {
+      name: 'className',
+      required: false,
+      description: 'Name or ID of the class (ie. Video)',
+    },
+    {
+      name: 'groupId',
+      required: false,
+      description: 'ID of the Curator Group to add as class maintainer',
+    },
+  ]
+
+  async run() {
+    const account = await this.getRequiredSelectedAccount()
+    await this.requireLead()
+
+    let { groupId, className } = this.parse(AddMaintainerToClassCommand).args
+
+    if (className === undefined) {
+      className = (await this.promptForClass()).name.toString()
+    }
+
+    const classId = (await this.classEntryByNameOrId(className))[0].toNumber()
+
+    if (groupId === undefined) {
+      groupId = await this.promptForCuratorGroup()
+    } else {
+      await this.getCuratorGroup(groupId)
+    }
+
+    await this.requestAccountDecoding(account)
+    await this.sendAndFollowNamedTx(account, 'contentDirectory', 'addMaintainerToClass', [classId, groupId])
+
+    console.log(
+      chalk.green(`Curator Group ${chalk.white(groupId)} added as maintainer to ${chalk.white(className)} class!`)
+    )
+  }
+}

+ 54 - 0
cli/src/commands/content-directory/class.ts

@@ -0,0 +1,54 @@
+import ContentDirectoryCommandBase from '../../base/ContentDirectoryCommandBase'
+import chalk from 'chalk'
+import { displayCollapsedRow, displayHeader, displayTable } from '../../helpers/display'
+
+export default class ClassCommand extends ContentDirectoryCommandBase {
+  static description = 'Show Class details by id or name.'
+  static args = [
+    {
+      name: 'className',
+      required: true,
+      description: 'Name or ID of the Class',
+    },
+  ]
+
+  async run() {
+    const { className } = this.parse(ClassCommand).args
+    const [id, aClass] = await this.classEntryByNameOrId(className)
+    const permissions = aClass.class_permissions
+    const maintainers = permissions.maintainers.toArray()
+
+    displayCollapsedRow({
+      'Name': aClass.name.toString(),
+      'ID': id.toString(),
+      'Any member': permissions.any_member.toString(),
+      'Entity creation blocked': permissions.entity_creation_blocked.toString(),
+      'All property values locked': permissions.all_entity_property_values_locked.toString(),
+      'Number of entities': aClass.current_number_of_entities.toNumber(),
+      'Max. number of entities': aClass.maximum_entities_count.toNumber(),
+      'Default entity creation voucher max.': aClass.default_entity_creation_voucher_upper_bound.toNumber(),
+    })
+
+    displayHeader(`Maintainers`)
+    this.log(
+      maintainers.length ? maintainers.map((groupId) => chalk.white(`Group ${groupId.toString()}`)).join(', ') : 'NONE'
+    )
+
+    displayHeader(`Properties`)
+    if (aClass.properties.length) {
+      displayTable(
+        aClass.properties.map((p) => ({
+          'Name': p.name.toString(),
+          'Type': JSON.stringify(p.property_type.toJSON()),
+          'Required': p.required.toString(),
+          'Unique': p.unique.toString(),
+          'Controller lock': p.locking_policy.is_locked_from_controller.toString(),
+          'Maintainer lock': p.locking_policy.is_locked_from_maintainer.toString(),
+        })),
+        3
+      )
+    } else {
+      this.log('NONE')
+    }
+  }
+}

+ 24 - 0
cli/src/commands/content-directory/classes.ts

@@ -0,0 +1,24 @@
+import ContentDirectoryCommandBase from '../../base/ContentDirectoryCommandBase'
+// import chalk from 'chalk'
+import { displayTable } from '../../helpers/display'
+
+export default class ClassesCommand extends ContentDirectoryCommandBase {
+  static description = 'List existing content directory classes.'
+
+  async run() {
+    const classes = await this.getApi().availableClasses()
+
+    displayTable(
+      classes.map(([id, c]) => ({
+        'ID': id.toString(),
+        'Name': c.name.toString(),
+        'Any member': c.class_permissions.any_member.toString(),
+        'Entities': c.current_number_of_entities.toNumber(),
+        'Schemas': c.schemas.length,
+        'Maintainers': c.class_permissions.maintainers.toArray().length,
+        'Properties': c.properties.length,
+      })),
+      3
+    )
+  }
+}

+ 44 - 0
cli/src/commands/content-directory/createClass.ts

@@ -0,0 +1,44 @@
+import ContentDirectoryCommandBase from '../../base/ContentDirectoryCommandBase'
+import CreateClassSchema from 'cd-schemas/schemas/extrinsics/CreateClass.schema.json'
+import { CreateClass } from 'cd-schemas/types/extrinsics/CreateClass'
+import { InputParser } from 'cd-schemas'
+import { JsonSchemaPrompter, JsonSchemaCustomPrompts } from '../../helpers/JsonSchemaPrompt'
+import { JSONSchema } from '@apidevtools/json-schema-ref-parser'
+import { IOFlags, getInputJson, saveOutputJson } from '../../helpers/InputOutput'
+
+export default class CreateClassCommand extends ContentDirectoryCommandBase {
+  static description = 'Create class inside content directory. Requires lead access.'
+  static flags = {
+    ...IOFlags,
+  }
+
+  async run() {
+    const account = await this.getRequiredSelectedAccount()
+    await this.requireLead()
+
+    const { input, output } = this.parse(CreateClassCommand).flags
+
+    let inputJson = getInputJson<CreateClass>(input)
+    if (!inputJson) {
+      const customPrompts: JsonSchemaCustomPrompts = [
+        ['class_permissions.maintainers', () => this.promptForCuratorGroups('Select class maintainers')],
+      ]
+
+      const prompter = new JsonSchemaPrompter<CreateClass>(CreateClassSchema as JSONSchema, undefined, customPrompts)
+
+      inputJson = await prompter.promptAll()
+    }
+
+    this.jsonPrettyPrint(JSON.stringify(inputJson))
+    const confirmed = await this.simplePrompt({ type: 'confirm', message: 'Do you confirm the provided input?' })
+
+    if (confirmed) {
+      await this.requestAccountDecoding(account)
+      this.log('Sending the extrinsic...')
+      const inputParser = new InputParser(this.getOriginalApi())
+      await this.sendAndFollowTx(account, inputParser.parseCreateClassExtrinsic(inputJson))
+
+      saveOutputJson(output, `${inputJson.name}Class.json`, inputJson)
+    }
+  }
+}

+ 18 - 0
cli/src/commands/content-directory/createCuratorGroup.ts

@@ -0,0 +1,18 @@
+import ContentDirectoryCommandBase from '../../base/ContentDirectoryCommandBase'
+import chalk from 'chalk'
+
+export default class AddCuratorGroupCommand extends ContentDirectoryCommandBase {
+  static description = 'Create new Curator Group.'
+  static aliases = ['addCuratorGroup']
+
+  async run() {
+    const account = await this.getRequiredSelectedAccount()
+    await this.requireLead()
+
+    await this.requestAccountDecoding(account)
+    await this.buildAndSendExtrinsic(account, 'contentDirectory', 'addCuratorGroup')
+
+    const newGroupId = (await this.getApi().nextCuratorGroupId()) - 1
+    console.log(chalk.green(`New group succesfully created! (ID: ${chalk.white(newGroupId)})`))
+  }
+}

+ 39 - 0
cli/src/commands/content-directory/curatorGroup.ts

@@ -0,0 +1,39 @@
+import { WorkingGroups } from '../../Types'
+import ContentDirectoryCommandBase from '../../base/ContentDirectoryCommandBase'
+import chalk from 'chalk'
+import { displayCollapsedRow, displayHeader } from '../../helpers/display'
+
+export default class CuratorGroupCommand extends ContentDirectoryCommandBase {
+  static description = 'Show Curator Group details by ID.'
+  static args = [
+    {
+      name: 'id',
+      required: true,
+      description: 'ID of the Curator Group',
+    },
+  ]
+
+  async run() {
+    const { id } = this.parse(CuratorGroupCommand).args
+    const group = await this.getCuratorGroup(id)
+    const classesMaintained = (await this.getApi().availableClasses()).filter(([, c]) =>
+      c.class_permissions.maintainers.toArray().some((gId) => gId.toNumber() === parseInt(id))
+    )
+    const members = (await this.getApi().groupMembers(WorkingGroups.Curators)).filter((curator) =>
+      group.curators.toArray().some((groupCurator) => groupCurator.eq(curator.workerId))
+    )
+
+    displayCollapsedRow({
+      'ID': id,
+      'Status': group.active.valueOf() ? 'Active' : 'Inactive',
+    })
+    displayHeader(`Classes maintained (${classesMaintained.length})`)
+    this.log(classesMaintained.map(([, c]) => chalk.white(c.name.toString())).join(', '))
+    displayHeader(`Group Members (${members.length})`)
+    this.log(
+      members
+        .map((curator) => chalk.white(`${curator.profile.handle} (WorkerID: ${curator.workerId.toString()})`))
+        .join(', ')
+    )
+  }
+}

+ 21 - 0
cli/src/commands/content-directory/curatorGroups.ts

@@ -0,0 +1,21 @@
+import ContentDirectoryCommandBase from '../../base/ContentDirectoryCommandBase'
+// import chalk from 'chalk'
+import { displayTable } from '../../helpers/display'
+
+export default class CuratorGroupsCommand extends ContentDirectoryCommandBase {
+  static description = 'List existing Curator Groups.'
+
+  async run() {
+    const groups = await this.getApi().availableCuratorGroups()
+
+    displayTable(
+      groups.map(([id, group]) => ({
+        'ID': id.toString(),
+        'Status': group.active.valueOf() ? 'Active' : 'Inactive',
+        'Classes maintained': group.number_of_classes_maintained.toNumber(),
+        'Members': group.curators.toArray().length,
+      })),
+      5
+    )
+  }
+}

+ 40 - 0
cli/src/commands/content-directory/entities.ts

@@ -0,0 +1,40 @@
+import ContentDirectoryCommandBase from '../../base/ContentDirectoryCommandBase'
+import { displayTable } from '../../helpers/display'
+import _ from 'lodash'
+
+export default class ClassCommand extends ContentDirectoryCommandBase {
+  static description = 'Show entities list by class id or name.'
+  static args = [
+    {
+      name: 'className',
+      required: true,
+      description: 'Name or ID of the Class',
+    },
+    {
+      name: 'properties',
+      required: false,
+      description:
+        'Comma-separated properties to include in the results table (ie. code,name). ' +
+        'By default all property values will be included.',
+    },
+  ]
+
+  async run() {
+    const { className, properties } = this.parse(ClassCommand).args
+    const [classId, entityClass] = await this.classEntryByNameOrId(className)
+    const entityEntries = await this.getApi().entitiesByClassId(classId.toNumber())
+    const propertiesToInclude = properties && (properties as string).split(',')
+
+    displayTable(
+      await Promise.all(
+        entityEntries.map(([id, entity]) => ({
+          'ID': id.toString(),
+          ..._.mapValues(this.parseEntityPropertyValues(entity, entityClass, propertiesToInclude), (v) =>
+            v.value.toString()
+          ),
+        }))
+      ),
+      3
+    )
+  }
+}

+ 37 - 0
cli/src/commands/content-directory/entity.ts

@@ -0,0 +1,37 @@
+import ContentDirectoryCommandBase from '../../base/ContentDirectoryCommandBase'
+import chalk from 'chalk'
+import { displayCollapsedRow, displayHeader } from '../../helpers/display'
+import _ from 'lodash'
+
+export default class EntityCommand extends ContentDirectoryCommandBase {
+  static description = 'Show Entity details by id.'
+  static args = [
+    {
+      name: 'id',
+      required: true,
+      description: 'ID of the Entity',
+    },
+  ]
+
+  async run() {
+    const { id } = this.parse(EntityCommand).args
+    const entity = await this.getEntity(id)
+    const { controller, frozen, referenceable } = entity.entity_permissions
+    const [classId, entityClass] = await this.classEntryByNameOrId(entity.class_id.toString())
+    const propertyValues = this.parseEntityPropertyValues(entity, entityClass)
+
+    displayCollapsedRow({
+      'ID': id,
+      'Class name': entityClass.name.toString(),
+      'Class ID': classId.toNumber(),
+      'Supported schemas': JSON.stringify(entity.supported_schemas.toJSON()),
+      'Controller': controller.type + (controller.isOfType('Member') ? `(${controller.asType('Member')})` : ''),
+      'Frozen': frozen.toString(),
+      'Refrecencable': referenceable.toString(),
+      'Same owner references': entity.reference_counter.same_owner.toNumber(),
+      'Total references': entity.reference_counter.total.toNumber(),
+    })
+    displayHeader('Property values')
+    displayCollapsedRow(_.mapValues(propertyValues, (v) => `${v.value.toString()} ${chalk.green(`${v.type}`)}`))
+  }
+}

+ 30 - 0
cli/src/commands/content-directory/removeCuratorGroup.ts

@@ -0,0 +1,30 @@
+import ContentDirectoryCommandBase from '../../base/ContentDirectoryCommandBase'
+import chalk from 'chalk'
+
+export default class AddCuratorGroupCommand extends ContentDirectoryCommandBase {
+  static description = 'Remove existing Curator Group.'
+  static args = [
+    {
+      name: 'id',
+      required: false,
+      description: 'ID of the Curator Group to remove',
+    },
+  ]
+
+  async run() {
+    const account = await this.getRequiredSelectedAccount()
+    await this.requireLead()
+
+    let { id } = this.parse(AddCuratorGroupCommand).args
+    if (id === undefined) {
+      id = await this.promptForCuratorGroup('Select Curator Group to remove')
+    } else {
+      await this.getCuratorGroup(id)
+    }
+
+    await this.requestAccountDecoding(account)
+    await this.sendAndFollowNamedTx(account, 'contentDirectory', 'removeCuratorGroup', [id])
+
+    console.log(chalk.green(`Curator Group ${chalk.white(id)} succesfully removed!`))
+  }
+}

+ 44 - 0
cli/src/commands/content-directory/removeMaintainerFromClass.ts

@@ -0,0 +1,44 @@
+import ContentDirectoryCommandBase from '../../base/ContentDirectoryCommandBase'
+import chalk from 'chalk'
+
+export default class AddMaintainerToClassCommand extends ContentDirectoryCommandBase {
+  static description = 'Remove maintainer (Curator Group) from class.'
+  static args = [
+    {
+      name: 'className',
+      required: false,
+      description: 'Name or ID of the class (ie. Video)',
+    },
+    {
+      name: 'groupId',
+      required: false,
+      description: 'ID of the Curator Group to remove from maintainers',
+    },
+  ]
+
+  async run() {
+    const account = await this.getRequiredSelectedAccount()
+    await this.requireLead()
+
+    let { groupId, className } = this.parse(AddMaintainerToClassCommand).args
+
+    if (className === undefined) {
+      className = (await this.promptForClass()).name.toString()
+    }
+
+    const [classId, aClass] = await this.classEntryByNameOrId(className)
+
+    if (groupId === undefined) {
+      groupId = await this.promptForCuratorGroup('Select a maintainer', aClass.class_permissions.maintainers.toArray())
+    } else {
+      await this.getCuratorGroup(groupId)
+    }
+
+    await this.requestAccountDecoding(account)
+    await this.sendAndFollowNamedTx(account, 'contentDirectory', 'removeMaintainerFromClass', [classId, groupId])
+
+    console.log(
+      chalk.green(`Curator Group ${chalk.white(groupId)} removed as maintainer of ${chalk.white(className)} class!`)
+    )
+  }
+}

+ 61 - 0
cli/src/commands/content-directory/setCuratorGroupStatus.ts

@@ -0,0 +1,61 @@
+import ContentDirectoryCommandBase from '../../base/ContentDirectoryCommandBase'
+import chalk from 'chalk'
+import ExitCodes from '../../ExitCodes'
+
+export default class SetCuratorGroupStatusCommand extends ContentDirectoryCommandBase {
+  static description = 'Set Curator Group status (Active/Inactive).'
+  static args = [
+    {
+      name: 'id',
+      required: false,
+      description: 'ID of the Curator Group',
+    },
+    {
+      name: 'status',
+      required: false,
+      description: 'New status of the group (1 - active, 0 - inactive)',
+    },
+  ]
+
+  async run() {
+    const account = await this.getRequiredSelectedAccount()
+    await this.requireLead()
+
+    let { id, status } = this.parse(SetCuratorGroupStatusCommand).args
+
+    if (id === undefined) {
+      id = await this.promptForCuratorGroup()
+    } else {
+      await this.getCuratorGroup(id)
+    }
+
+    if (status === undefined) {
+      status = await this.simplePrompt({
+        type: 'list',
+        message: 'Select new status',
+        choices: [
+          { name: 'Active', value: true },
+          { name: 'Inactive', value: false },
+        ],
+      })
+    } else {
+      if (status !== '0' && status !== '1') {
+        this.error('Invalid status provided. Use "1" for Active and "0" for Inactive.', {
+          exit: ExitCodes.InvalidInput,
+        })
+      }
+      status = !!parseInt(status)
+    }
+
+    await this.requestAccountDecoding(account)
+    await this.sendAndFollowNamedTx(account, 'contentDirectory', 'setCuratorGroupStatus', [id, status])
+
+    console.log(
+      chalk.green(
+        `Curator Group ${chalk.white(id)} status succesfully changed to: ${chalk.white(
+          status ? 'Active' : 'Inactive'
+        )}!`
+      )
+    )
+  }
+}

+ 55 - 0
cli/src/commands/content-directory/updateClassPermissions.ts

@@ -0,0 +1,55 @@
+import ContentDirectoryCommandBase from '../../base/ContentDirectoryCommandBase'
+import CreateClassSchema from 'cd-schemas/schemas/extrinsics/CreateClass.schema.json'
+import chalk from 'chalk'
+import { JsonSchemaCustomPrompts, JsonSchemaPrompter } from '../../helpers/JsonSchemaPrompt'
+import { CreateClass } from 'cd-schemas/types/extrinsics/CreateClass'
+import { JSONSchema } from '@apidevtools/json-schema-ref-parser'
+
+export default class UpdateClassPermissionsCommand extends ContentDirectoryCommandBase {
+  static description = 'Update permissions in given class.'
+  static args = [
+    {
+      name: 'className',
+      required: false,
+      description: 'Name or ID of the class (ie. Video)',
+    },
+  ]
+
+  async run() {
+    const account = await this.getRequiredSelectedAccount()
+    await this.requireLead()
+
+    let { className } = this.parse(UpdateClassPermissionsCommand).args
+
+    if (className === undefined) {
+      className = (await this.promptForClass()).name.toString()
+    }
+
+    const [classId, aClass] = await this.classEntryByNameOrId(className)
+    const currentPermissions = aClass.class_permissions
+
+    const customPrompts: JsonSchemaCustomPrompts = [
+      ['class_permissions.maintainers', () => this.promptForCuratorGroups('Select class maintainers')],
+    ]
+
+    const prompter = new JsonSchemaPrompter<CreateClass>(
+      CreateClassSchema as JSONSchema,
+      { class_permissions: currentPermissions.toJSON() as CreateClass['class_permissions'] },
+      customPrompts
+    )
+
+    const newPermissions = await prompter.promptSingleProp('class_permissions')
+
+    await this.requestAccountDecoding(account)
+    await this.sendAndFollowNamedTx(account, 'contentDirectory', 'updateClassPermissions', [
+      classId,
+      newPermissions.any_member,
+      newPermissions.entity_creation_blocked,
+      newPermissions.all_entity_property_values_locked,
+      newPermissions.maintainers,
+    ])
+
+    console.log(chalk.green(`${chalk.white(className)} class permissions updated to:`))
+    this.jsonPrettyPrint(JSON.stringify(newPermissions))
+  }
+}

+ 1 - 3
cli/src/commands/working-groups/createOpening.ts

@@ -79,9 +79,7 @@ export default class WorkingGroupsCreateOpening extends WorkingGroupsCommandBase
       this.log(chalk.white('Sending the extrinsic...'))
       await this.sendExtrinsic(
         account,
-        apiModuleByGroup[this.group],
-        'addOpening',
-        defaultValues!.map((v) => v.value)
+        this.getOriginalApi().tx[apiModuleByGroup[this.group]].addOpening(...defaultValues!.map((v) => v.value))
       )
       this.log(chalk.green('Opening succesfully created!'))
     }

+ 1 - 1
cli/src/commands/working-groups/decreaseWorkerStake.ts

@@ -42,7 +42,7 @@ export default class WorkingGroupsDecreaseWorkerStake extends WorkingGroupsComma
 
     await this.requestAccountDecoding(account)
 
-    await this.sendAndFollowExtrinsic(account, apiModuleByGroup[this.group], 'decreaseStake', [workerId, balance])
+    await this.sendAndFollowNamedTx(account, apiModuleByGroup[this.group], 'decreaseStake', [workerId, balance])
 
     this.log(
       chalk.green(

+ 1 - 1
cli/src/commands/working-groups/evictWorker.ts

@@ -41,7 +41,7 @@ export default class WorkingGroupsEvictWorker extends WorkingGroupsCommandBase {
 
     await this.requestAccountDecoding(account)
 
-    await this.sendAndFollowExtrinsic(account, apiModuleByGroup[this.group], 'terminateRole', [
+    await this.sendAndFollowNamedTx(account, apiModuleByGroup[this.group], 'terminateRole', [
       workerId,
       rationale,
       shouldSlash,

+ 1 - 1
cli/src/commands/working-groups/fillOpening.ts

@@ -33,7 +33,7 @@ export default class WorkingGroupsFillOpening extends WorkingGroupsCommandBase {
 
     await this.requestAccountDecoding(account)
 
-    await this.sendAndFollowExtrinsic(account, apiModuleByGroup[this.group], 'fillOpening', [
+    await this.sendAndFollowNamedTx(account, apiModuleByGroup[this.group], 'fillOpening', [
       openingId,
       applicationIds,
       rewardPolicyOpt,

+ 1 - 4
cli/src/commands/working-groups/increaseStake.ts

@@ -30,10 +30,7 @@ export default class WorkingGroupsIncreaseStake extends WorkingGroupsCommandBase
 
     await this.requestAccountDecoding(account)
 
-    await this.sendAndFollowExtrinsic(account, apiModuleByGroup[this.group], 'increaseStake', [
-      worker.workerId,
-      balance,
-    ])
+    await this.sendAndFollowNamedTx(account, apiModuleByGroup[this.group], 'increaseStake', [worker.workerId, balance])
 
     this.log(
       chalk.green(

+ 1 - 1
cli/src/commands/working-groups/leaveRole.ts

@@ -21,7 +21,7 @@ export default class WorkingGroupsLeaveRole extends WorkingGroupsCommandBase {
 
     await this.requestAccountDecoding(account)
 
-    await this.sendAndFollowExtrinsic(account, apiModuleByGroup[this.group], 'leaveRole', [worker.workerId, rationale])
+    await this.sendAndFollowNamedTx(account, apiModuleByGroup[this.group], 'leaveRole', [worker.workerId, rationale])
 
     this.log(chalk.green(`Succesfully left the role! (worker id: ${chalk.white(worker.workerId.toNumber())})`))
   }

+ 1 - 1
cli/src/commands/working-groups/slashWorker.ts

@@ -39,7 +39,7 @@ export default class WorkingGroupsSlashWorker extends WorkingGroupsCommandBase {
 
     await this.requestAccountDecoding(account)
 
-    await this.sendAndFollowExtrinsic(account, apiModuleByGroup[this.group], 'slashStake', [workerId, balance])
+    await this.sendAndFollowNamedTx(account, apiModuleByGroup[this.group], 'slashStake', [workerId, balance])
 
     this.log(
       chalk.green(

+ 1 - 1
cli/src/commands/working-groups/startAcceptingApplications.ts

@@ -29,7 +29,7 @@ export default class WorkingGroupsStartAcceptingApplications extends WorkingGrou
 
     await this.requestAccountDecoding(account)
 
-    await this.sendAndFollowExtrinsic(account, apiModuleByGroup[this.group], 'acceptApplications', [openingId])
+    await this.sendAndFollowNamedTx(account, apiModuleByGroup[this.group], 'acceptApplications', [openingId])
 
     this.log(
       chalk.green(`Opening ${chalk.white(openingId)} status changed to: ${chalk.white('Accepting Applications')}`)

+ 1 - 1
cli/src/commands/working-groups/startReviewPeriod.ts

@@ -29,7 +29,7 @@ export default class WorkingGroupsStartReviewPeriod extends WorkingGroupsCommand
 
     await this.requestAccountDecoding(account)
 
-    await this.sendAndFollowExtrinsic(account, apiModuleByGroup[this.group], 'beginApplicantReview', [openingId])
+    await this.sendAndFollowNamedTx(account, apiModuleByGroup[this.group], 'beginApplicantReview', [openingId])
 
     this.log(chalk.green(`Opening ${chalk.white(openingId)} status changed to: ${chalk.white('In Review')}`))
   }

+ 1 - 1
cli/src/commands/working-groups/terminateApplication.ts

@@ -30,7 +30,7 @@ export default class WorkingGroupsTerminateApplication extends WorkingGroupsComm
 
     await this.requestAccountDecoding(account)
 
-    await this.sendAndFollowExtrinsic(account, apiModuleByGroup[this.group], 'terminateApplication', [applicationId])
+    await this.sendAndFollowNamedTx(account, apiModuleByGroup[this.group], 'terminateApplication', [applicationId])
 
     this.log(chalk.green(`Application ${chalk.white(applicationId)} has been succesfully terminated!`))
   }

+ 1 - 1
cli/src/commands/working-groups/updateRewardAccount.ts

@@ -38,7 +38,7 @@ export default class WorkingGroupsUpdateRewardAccount extends WorkingGroupsComma
 
     await this.requestAccountDecoding(account)
 
-    await this.sendAndFollowExtrinsic(account, apiModuleByGroup[this.group], 'updateRewardAccount', [
+    await this.sendAndFollowNamedTx(account, apiModuleByGroup[this.group], 'updateRewardAccount', [
       worker.workerId,
       newRewardAccount,
     ])

+ 1 - 1
cli/src/commands/working-groups/updateRoleAccount.ts

@@ -32,7 +32,7 @@ export default class WorkingGroupsUpdateRoleAccount extends WorkingGroupsCommand
 
     await this.requestAccountDecoding(account)
 
-    await this.sendAndFollowExtrinsic(account, apiModuleByGroup[this.group], 'updateRoleAccount', [
+    await this.sendAndFollowNamedTx(account, apiModuleByGroup[this.group], 'updateRoleAccount', [
       worker.workerId,
       newRoleAccount,
     ])

+ 1 - 1
cli/src/commands/working-groups/updateWorkerReward.ts

@@ -55,7 +55,7 @@ export default class WorkingGroupsUpdateWorkerReward extends WorkingGroupsComman
 
     await this.requestAccountDecoding(account)
 
-    await this.sendAndFollowExtrinsic(account, apiModuleByGroup[this.group], 'updateRewardAmount', [
+    await this.sendAndFollowNamedTx(account, apiModuleByGroup[this.group], 'updateRewardAmount', [
       workerId,
       newRewardValue,
     ])

+ 68 - 0
cli/src/helpers/InputOutput.ts

@@ -0,0 +1,68 @@
+import { flags } from '@oclif/command'
+import { CLIError } from '@oclif/errors'
+import ExitCodes from '../ExitCodes'
+import fs from 'fs'
+import path from 'path'
+import Ajv from 'ajv'
+import { JSONSchema7 } from 'json-schema'
+import chalk from 'chalk'
+
+export const IOFlags = {
+  input: flags.string({
+    char: 'i',
+    required: false,
+    description: `Path to JSON file to use as input (if not specified - the input can be provided interactively)`,
+  }),
+  output: flags.string({
+    char: 'o',
+    required: false,
+    description: 'Path where the output JSON file should be placed (can be then reused as input)',
+  }),
+}
+
+export function getInputJson<T>(inputPath?: string, schema?: JSONSchema7): T | null {
+  if (inputPath) {
+    let content, jsonObj
+    try {
+      content = fs.readFileSync(inputPath).toString()
+    } catch (e) {
+      throw new CLIError(`Cannot access the input file at: ${inputPath}`, { exit: ExitCodes.FsOperationFailed })
+    }
+    try {
+      jsonObj = JSON.parse(content)
+    } catch (e) {
+      throw new CLIError(`JSON parsing failed for file: ${inputPath}`, { exit: ExitCodes.InvalidInput })
+    }
+    if (schema) {
+      const ajv = new Ajv()
+      const valid = ajv.validate(schema, jsonObj)
+      if (!valid) {
+        throw new CLIError(`Input JSON file is not valid: ${ajv.errorsText()}`)
+      }
+    }
+
+    return jsonObj as T
+  }
+
+  return null
+}
+
+export function saveOutputJson(outputPath: string | undefined, fileName: string, data: any): void {
+  if (outputPath) {
+    let outputFilePath = path.join(outputPath, fileName)
+    let postfix = 0
+    while (fs.existsSync(outputFilePath)) {
+      fileName = fileName.replace(/(_[0-9]+)?\.json/, `_${++postfix}.json`)
+      outputFilePath = path.join(outputPath, fileName)
+    }
+    try {
+      fs.writeFileSync(outputFilePath, JSON.stringify(data, null, 4))
+    } catch (e) {
+      throw new CLIError(`Could not save the output to: ${outputFilePath}. Check directory permissions`, {
+        exit: ExitCodes.FsOperationFailed,
+      })
+    }
+
+    console.log(`${chalk.green('Output succesfully saved to:')} ${chalk.white(outputFilePath)}`)
+  }
+}

+ 206 - 0
cli/src/helpers/JsonSchemaPrompt.ts

@@ -0,0 +1,206 @@
+import Ajv from 'ajv'
+import inquirer, { DistinctQuestion } from 'inquirer'
+import _ from 'lodash'
+import RefParser, { JSONSchema } from '@apidevtools/json-schema-ref-parser'
+import chalk from 'chalk'
+import { BOOL_PROMPT_OPTIONS } from './prompting'
+
+type CustomPromptMethod = () => Promise<any>
+type CustomPrompt = DistinctQuestion | CustomPromptMethod | { $item: CustomPrompt }
+
+export type JsonSchemaCustomPrompts = [string | RegExp, CustomPrompt][]
+
+export class JsonSchemaPrompter<JsonResult> {
+  schema: JSONSchema
+  customPropmpts?: JsonSchemaCustomPrompts
+  ajv: Ajv.Ajv
+  filledObject: Partial<JsonResult>
+
+  constructor(schema: JSONSchema, defaults?: Partial<JsonResult>, customPrompts?: JsonSchemaCustomPrompts) {
+    this.customPropmpts = customPrompts
+    this.schema = schema
+    this.ajv = new Ajv()
+    this.filledObject = defaults || {}
+  }
+
+  private oneOfToChoices(oneOf: JSONSchema[]) {
+    const choices: { name: string; value: number | string }[] = []
+
+    oneOf.forEach((pSchema, index) => {
+      if (pSchema.description) {
+        choices.push({ name: pSchema.description, value: index })
+      } else if (pSchema.type === 'object' && pSchema.properties) {
+        choices.push({ name: `{ ${Object.keys(pSchema.properties).join(', ')} }`, value: index })
+      } else {
+        choices.push({ name: index.toString(), value: index })
+      }
+    })
+
+    return choices
+  }
+
+  private getCustomPrompt(propertyPath: string): CustomPrompt | undefined {
+    const found = this.customPropmpts?.find(([pathToMatch]) =>
+      typeof pathToMatch === 'string' ? propertyPath === pathToMatch : pathToMatch.test(propertyPath)
+    )
+
+    return found ? found[1] : undefined
+  }
+
+  private propertyDisplayName(propertyPath: string) {
+    return chalk.green(propertyPath)
+  }
+
+  private async prompt(schema: JSONSchema, propertyPath = ''): Promise<any> {
+    const customPrompt: CustomPrompt | undefined = this.getCustomPrompt(propertyPath)
+    const propDisplayName = this.propertyDisplayName(propertyPath)
+
+    // Custom prompt
+    if (typeof customPrompt === 'function') {
+      return await this.promptWithRetry(customPrompt, propertyPath, true)
+    }
+
+    // oneOf
+    if (schema.oneOf) {
+      const oneOf = schema.oneOf as JSONSchema[]
+      const choices = this.oneOfToChoices(oneOf)
+      const { choosen } = await inquirer.prompt({ name: 'choosen', message: propDisplayName, type: 'list', choices })
+      return await this.prompt(oneOf[choosen], propertyPath)
+    }
+
+    // object
+    if (schema.type === 'object' && schema.properties) {
+      const value: Record<string, any> = {}
+      for (const [pName, pSchema] of Object.entries(schema.properties)) {
+        value[pName] = await this.prompt(pSchema, propertyPath ? `${propertyPath}.${pName}` : pName)
+      }
+      return value
+    }
+
+    // array
+    if (schema.type === 'array' && schema.items) {
+      return await this.promptWithRetry(() => this.promptArray(schema, propertyPath), propertyPath, true)
+    }
+
+    // "primitive" values:
+    const basicPromptOptions: DistinctQuestion = {
+      message: propDisplayName,
+      default: _.get(this.filledObject, propertyPath) || schema.default,
+    }
+
+    let additionalPromptOptions: DistinctQuestion | undefined
+    let normalizer: (v: any) => any = (v) => v
+
+    // Prompt options
+    if (schema.enum) {
+      additionalPromptOptions = { type: 'list', choices: schema.enum as any[] }
+    } else if (schema.type === 'boolean') {
+      additionalPromptOptions = BOOL_PROMPT_OPTIONS
+    }
+
+    // Normalizers
+    if (schema.type === 'integer') {
+      normalizer = (v) => parseInt(v)
+    }
+
+    if (schema.type === 'number') {
+      normalizer = (v) => Number(v)
+    }
+
+    const promptOptions = { ...basicPromptOptions, ...additionalPromptOptions, ...customPrompt }
+    // Need to wrap in retry, because "validate" will not get called if "type" is "list" etc.
+    return await this.promptWithRetry(
+      async () => normalizer(await this.promptSimple(promptOptions, propertyPath, schema, normalizer)),
+      propertyPath
+    )
+  }
+
+  private setValueAndGetError(propertyPath: string, value: any, nestedErrors = false): string | null {
+    _.set(this.filledObject as Record<string, unknown>, propertyPath, value)
+    this.ajv.validate(this.schema, this.filledObject) as boolean
+    return this.ajv.errors
+      ? this.ajv.errors
+          .filter((e) => (nestedErrors ? e.dataPath.startsWith(`.${propertyPath}`) : e.dataPath === `.${propertyPath}`))
+          .map((e) => (e.dataPath.replace(`.${propertyPath}`, '') || 'This value') + ` ${e.message}`)
+          .join(', ')
+      : null
+  }
+
+  private async promptArray(schema: JSONSchema, propertyPath: string) {
+    if (!schema.items) {
+      return []
+    }
+    const { maxItems = Number.MAX_SAFE_INTEGER } = schema
+    let currItem = 0
+    const result = []
+    while (currItem < maxItems) {
+      const { next } = await inquirer.prompt([
+        {
+          ...BOOL_PROMPT_OPTIONS,
+          name: 'next',
+          message: `Do you want to add another item to ${this.propertyDisplayName(propertyPath)} array?`,
+        },
+      ])
+      if (!next) {
+        break
+      }
+      const itemSchema = Array.isArray(schema.items) ? schema.items[schema.items.length % currItem] : schema.items
+      result.push(await this.prompt(typeof itemSchema === 'boolean' ? {} : itemSchema, `${propertyPath}[${currItem}]`))
+
+      ++currItem
+    }
+
+    return result
+  }
+
+  private async promptSimple(
+    promptOptions: DistinctQuestion,
+    propertyPath: string,
+    schema: JSONSchema,
+    normalize?: (v: any) => any
+  ) {
+    const { result } = await inquirer.prompt([
+      {
+        ...promptOptions,
+        name: 'result',
+        validate: (v) => {
+          v = normalize ? normalize(v) : v
+          return (
+            this.setValueAndGetError(propertyPath, v) ||
+            (promptOptions.validate ? promptOptions.validate(v) : true) ||
+            true
+          )
+        },
+      },
+    ])
+
+    return result
+  }
+
+  private async promptWithRetry(customMethod: CustomPromptMethod, propertyPath: string, nestedErrors = false) {
+    let error: string | null
+    let value: any
+    do {
+      value = await customMethod()
+      error = this.setValueAndGetError(propertyPath, value, nestedErrors)
+      if (error) {
+        console.log('\n')
+        console.warn(error)
+        console.warn(`Try providing the input for ${propertyPath} again...`)
+      }
+    } while (error)
+
+    return value
+  }
+
+  async promptAll() {
+    await this.prompt(await RefParser.dereference(this.schema))
+    return this.filledObject as JsonResult
+  }
+
+  async promptSingleProp<P extends keyof JsonResult & string>(p: P): Promise<Exclude<JsonResult[P], undefined>> {
+    const dereferenced = await RefParser.dereference(this.schema)
+    await this.prompt(dereferenced.properties![p] as JSONSchema, p)
+    return this.filledObject[p] as Exclude<JsonResult[P], undefined>
+  }
+}

+ 1 - 0
cli/src/helpers/display.ts

@@ -48,6 +48,7 @@ export function displayTable(rows: { [k: string]: string | number }[], cellHoriz
       return Math.max(maxLength, valLength)
     }, columnName.length)
   const columnDef = (columnName: string) => ({
+    header: columnName,
     get: (row: typeof rows[number]) => chalk.white(`${row[columnName]}`),
     minWidth: maxLength(columnName) + cellHorizontalPadding,
   })

+ 9 - 0
cli/src/helpers/prompting.ts

@@ -0,0 +1,9 @@
+import { DistinctQuestion } from 'inquirer'
+
+export const BOOL_PROMPT_OPTIONS: DistinctQuestion = {
+  type: 'list',
+  choices: [
+    { name: 'Yes', value: true },
+    { name: 'No', value: false },
+  ],
+}

+ 3 - 1
cli/tsconfig.json

@@ -13,7 +13,9 @@
     "baseUrl": ".",
     "paths": {
       "@polkadot/types/augment": ["../types/augment-codec/augment-types.ts"],
-    }
+    },
+    "resolveJsonModule": true,
+    "skipLibCheck": true
   },
   "include": [
     "src/**/*"

+ 1 - 10
docker-compose-with-storage.yml

@@ -5,8 +5,6 @@ services:
     ports:
       - '127.0.0.1:5001:5001'
       - '127.0.0.1:8080:8080'
-    volumes:
-      - ipfs-data:/data/ipfs
     entrypoint: ''
     command: |
       /bin/sh -c "
@@ -22,9 +20,7 @@ services:
       dockerfile: joystream-node.Dockerfile
     ports:
       - '127.0.0.1:9944:9944'
-    volumes:
-      - chain-data:/data
-    command: --dev --ws-external --base-path /data
+    command: --dev --ws-external --base-path /data --log runtime
 
   colossus:
     image: joystream/apps
@@ -41,8 +37,3 @@ services:
     environment:
       - DEBUG=*
 
-volumes:
-  ipfs-data:
-    driver: local
-  chain-data:
-    driver: local

+ 1 - 1
docker-compose.yml

@@ -11,7 +11,7 @@ services:
       # dockerfile is relative to the context
       dockerfile: joystream-node.Dockerfile
     container_name: joystream-node
-    command: --dev --alice --validator --unsafe-ws-external --rpc-cors=all
+    command: --dev --alice --validator --unsafe-ws-external --rpc-cors=all --log runtime
     ports:
       - "9944:9944"
   

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

@@ -1,8 +1,8 @@
 # Address of the Joystream node.
 NODE_URL = ws://127.0.0.1:9944
-# Path to the database for shared keys and nonce
-DB_PATH = .tmp/db.json
 # Account which is expected to provide sufficient funds to test accounts.
+TREASURY_ACCOUNT_URI = //Alice
+# Sudo Account
 SUDO_ACCOUNT_URI = //Alice
 # Amount of members able to buy membership in membership creation test.
 MEMBERSHIP_CREATION_N = 2

+ 5 - 6
tests/network-tests/package.json

@@ -4,32 +4,31 @@
   "license": "GPL-3.0-only",
   "scripts": {
     "build": "tsc --noEmit",
-    "test": "yarn db-path-setup && tap --files src/tests/unknown.unknown src/tests/councilSetup.ts src/tests/proposals/*Test.ts src/tests/leaderSetup.ts src/tests/workingGroup/*Test.ts -T",
+    "run-tests": "./run-tests.sh",
+    "test-run": "node -r ts-node/register --unhandled-rejections=strict",
     "lint": "eslint . --quiet --ext .ts",
     "checks": "tsc --noEmit --pretty && prettier ./ --check && yarn lint",
-    "format": "prettier ./ --write ",
-    "db-path-setup": "mkdir .tmp/ || rm .tmp/db.json || echo ''"
+    "format": "prettier ./ --write "
   },
   "dependencies": {
     "@joystream/types": "link:../../types",
     "@polkadot/api": "1.26.1",
     "@polkadot/keyring": "3.0.1",
+    "@types/async-lock": "^1.1.2",
     "@types/bn.js": "^4.11.5",
     "@types/lowdb": "^1.0.9",
+    "async-lock": "^1.2.0",
     "bn.js": "^4.11.8",
     "dotenv": "^8.2.0",
     "fs": "^0.0.1-security",
-    "lowdb": "^1.0.0",
     "uuid": "^7.0.3"
   },
   "devDependencies": {
     "@polkadot/ts": "^0.3.14",
     "@types/chai": "^4.2.11",
-    "@types/tap": "^14.10.0",
     "@types/uuid": "^7.0.2",
     "chai": "^4.2.0",
     "prettier": "2.0.2",
-    "tap": "^14.10.7",
     "ts-node": "^8.8.1",
     "typescript": "^3.8.3"
   }

+ 2 - 1
tests/network-tests/run-tests.sh

@@ -50,6 +50,7 @@ CONTAINER_ID=`docker run -d -v ${DATA_PATH}:/data -p 9944:9944 joystream/node \
   --chain /data/chain-spec-raw.json`
 
 function cleanup() {
+    docker logs ${CONTAINER_ID} --tail 15
     docker stop ${CONTAINER_ID}
     docker rm ${CONTAINER_ID}
 }
@@ -57,4 +58,4 @@ function cleanup() {
 trap cleanup EXIT
 
 # Execute the tests
-yarn workspace network-tests test
+time DEBUG=* yarn workspace network-tests test-run src/scenarios/full.ts

File diff suppressed because it is too large
+ 313 - 330
tests/network-tests/src/Api.ts


+ 30 - 0
tests/network-tests/src/Fixture.ts

@@ -0,0 +1,30 @@
+import { Api } from './Api'
+
+export interface Fixture {
+  runner(expectFailure: boolean): Promise<void>
+}
+
+// Fixture that measures start and end blocks
+// ensures fixture only runs once
+export class BaseFixture implements Fixture {
+  protected api: Api
+  private ran = false
+
+  constructor(api: Api) {
+    this.api = api
+    // record starting block
+  }
+
+  public async runner(expectFailure: boolean): Promise<void> {
+    if (this.ran) {
+      return
+    }
+    this.ran = true
+    return this.execute(expectFailure)
+    // record end blocks
+  }
+
+  protected async execute(expectFailure: boolean): Promise<void> {
+    return
+  }
+}

+ 34 - 0
tests/network-tests/src/fixtures/councilElectionHappyCase.ts

@@ -0,0 +1,34 @@
+import { Fixture } from '../Fixture'
+import { ElectCouncilFixture } from './councilElectionModule'
+import { Api } from '../Api'
+import BN from 'bn.js'
+
+export class CouncilElectionHappyCaseFixture implements Fixture {
+  private api: Api
+  private voters: string[]
+  private applicants: string[]
+  private k: number
+  private greaterStake: BN
+  private lesserStake: BN
+
+  constructor(api: Api, voters: string[], applicants: string[], k: number, greaterStake: BN, lesserStake: BN) {
+    this.api = api
+    this.voters = voters
+    this.applicants = applicants
+    this.k = k
+    this.greaterStake = greaterStake
+    this.lesserStake = lesserStake
+  }
+
+  public async runner(expectFailure: boolean): Promise<void> {
+    const electCouncilFixture: ElectCouncilFixture = new ElectCouncilFixture(
+      this.api,
+      this.voters,
+      this.applicants,
+      this.k,
+      this.greaterStake,
+      this.lesserStake
+    )
+    await electCouncilFixture.runner(false)
+  }
+}

+ 112 - 0
tests/network-tests/src/fixtures/councilElectionModule.ts

@@ -0,0 +1,112 @@
+import { Api } from '../Api'
+import BN from 'bn.js'
+import { assert } from 'chai'
+import { Seat } from '@joystream/types/council'
+import { v4 as uuid } from 'uuid'
+import { Utils } from '../utils'
+import { Fixture } from '../Fixture'
+
+export class ElectCouncilFixture implements Fixture {
+  private api: Api
+  private voters: string[]
+  private applicants: string[]
+  private k: number
+  private greaterStake: BN
+  private lesserStake: BN
+
+  public constructor(api: Api, voters: string[], applicants: string[], k: number, greaterStake: BN, lesserStake: BN) {
+    this.api = api
+    this.voters = voters
+    this.applicants = applicants
+    this.k = k
+    this.greaterStake = greaterStake
+    this.lesserStake = lesserStake
+  }
+
+  public async runner(expectFailure: boolean): Promise<void> {
+    // Assert no council exists
+    assert((await this.api.getCouncil()).length === 0)
+
+    let now = await this.api.getBestBlock()
+    const applyForCouncilFee: BN = this.api.estimateApplyForCouncilFee(this.greaterStake)
+    const voteForCouncilFee: BN = this.api.estimateVoteForCouncilFee(
+      this.applicants[0],
+      this.applicants[0],
+      this.greaterStake
+    )
+    const salt: string[] = this.voters.map(() => {
+      return ''.concat(uuid().replace(/-/g, ''))
+    })
+    const revealVoteFee: BN = this.api.estimateRevealVoteFee(this.applicants[0], salt[0])
+
+    // Topping the balances
+    this.api.treasuryTransferBalanceToAccounts(this.applicants, applyForCouncilFee.add(this.greaterStake))
+    this.api.treasuryTransferBalanceToAccounts(this.voters, voteForCouncilFee.add(revealVoteFee).add(this.greaterStake))
+
+    // First K members stake more
+    await this.api.sudoStartAnnouncingPeriod(now.addn(100))
+    await this.api.batchApplyForCouncilElection(this.applicants.slice(0, this.k), this.greaterStake)
+    this.applicants.slice(0, this.k).forEach((account) =>
+      this.api.getCouncilElectionStake(account).then((stake) => {
+        assert(
+          stake.eq(this.greaterStake),
+          `${account} not applied correctly for council election with stake ${stake} versus expected ${this.greaterStake}`
+        )
+      })
+    )
+
+    // Last members stake less
+    await this.api.batchApplyForCouncilElection(this.applicants.slice(this.k), this.lesserStake)
+    this.applicants.slice(this.k).forEach((account) =>
+      this.api.getCouncilElectionStake(account).then((stake) => {
+        assert(
+          stake.eq(this.lesserStake),
+          `${account} not applied correctrly for council election with stake ${stake} versus expected ${this.lesserStake}`
+        )
+      })
+    )
+
+    // Voting
+    await this.api.sudoStartVotingPeriod(now.addn(100))
+    await this.api.batchVoteForCouncilMember(
+      this.voters.slice(0, this.k),
+      this.applicants.slice(0, this.k),
+      salt.slice(0, this.k),
+      this.lesserStake
+    )
+    await this.api.batchVoteForCouncilMember(
+      this.voters.slice(this.k),
+      this.applicants.slice(this.k),
+      salt.slice(this.k),
+      this.greaterStake
+    )
+
+    // Revealing
+    await this.api.sudoStartRevealingPeriod(now.addn(100))
+    await this.api.batchRevealVote(
+      this.voters.slice(0, this.k),
+      this.applicants.slice(0, this.k),
+      salt.slice(0, this.k)
+    )
+    await this.api.batchRevealVote(this.voters.slice(this.k), this.applicants.slice(this.k), salt.slice(this.k))
+    now = await this.api.getBestBlock()
+
+    // Resolving election
+    // 3 is to ensure the revealing block is in future
+    await this.api.sudoStartRevealingPeriod(now.addn(3))
+    await Utils.wait(this.api.getBlockDuration().muln(2.5).toNumber())
+    const seats: Seat[] = await this.api.getCouncil()
+
+    // Assert a council was created
+    assert(seats.length)
+
+    // const applicantAddresses: string[] = this.applicantKeyPairs.map((keyPair) => keyPair.address)
+    // const voterAddresses: string[] = this.voterKeyPairs.map((keyPair) => keyPair.address)
+    // const councilMembers: string[] = seats.map((seat) => seat.member.toString())
+    // const backers: string[] = seats.map((seat) => seat.backers.map((backer) => backer.member.toString())).flat()
+
+    if (expectFailure) {
+      throw new Error('Successful fixture run while expecting failure')
+    }
+  }
+}

+ 94 - 0
tests/network-tests/src/fixtures/membershipModule.ts

@@ -0,0 +1,94 @@
+import { Api } from '../Api'
+import BN from 'bn.js'
+import { assert } from 'chai'
+import { Fixture, BaseFixture } from '../Fixture'
+import { PaidTermId, MemberId } from '@joystream/types/members'
+import Debugger from 'debug'
+
+export class BuyMembershipHappyCaseFixture extends BaseFixture {
+  private accounts: string[]
+  private paidTerms: PaidTermId
+  private debug: Debugger.Debugger
+  private memberIds: MemberId[] = []
+
+  public constructor(api: Api, accounts: string[], paidTerms: PaidTermId) {
+    super(api)
+    this.accounts = accounts
+    this.paidTerms = paidTerms
+    this.debug = Debugger('fixture:BuyMembershipHappyCaseFixture')
+  }
+
+  public getCreatedMembers(): MemberId[] {
+    return this.memberIds.slice()
+  }
+
+  public async execute(expectFailure: boolean): Promise<void> {
+    this.debug(`Registering ${this.accounts.length} new members`)
+    // Fee estimation and transfer
+    const membershipFee: BN = await this.api.getMembershipFee(this.paidTerms)
+    const membershipTransactionFee: BN = this.api.estimateBuyMembershipFee(
+      this.accounts[0],
+      this.paidTerms,
+      'member_name_which_is_longer_than_expected'
+    )
+    this.api.treasuryTransferBalanceToAccounts(this.accounts, membershipTransactionFee.add(new BN(membershipFee)))
+
+    this.memberIds = (
+      await Promise.all(
+        this.accounts.map((account) =>
+          this.api.buyMembership(account, this.paidTerms, `member${account.substring(0, 14)}`)
+        )
+      )
+    ).map(({ events }) => this.api.expectMemberRegisteredEvent(events))
+
+    this.debug(`New member ids: ${this.memberIds}`)
+    if (expectFailure) {
+      throw new Error('Successful fixture run while expecting failure')
+    }
+  }
+}
+
+export class BuyMembershipWithInsufficienFundsFixture implements Fixture {
+  private api: Api
+  private account: string
+  private paidTerms: PaidTermId
+
+  public constructor(api: Api, account: string, paidTerms: PaidTermId) {
+    this.api = api
+    this.account = account
+    this.paidTerms = paidTerms
+  }
+
+  public async runner(expectFailure: boolean) {
+    // Assertions
+    this.api.getMemberIds(this.account).then((membership) => assert(membership.length === 0, 'Account A is a member'))
+
+    // Fee estimation and transfer
+    const membershipFee: BN = await this.api.getMembershipFee(this.paidTerms)
+    const membershipTransactionFee: BN = this.api.estimateBuyMembershipFee(
+      this.account,
+      this.paidTerms,
+      'member_name_which_is_longer_than_expected'
+    )
+    this.api.treasuryTransferBalance(this.account, membershipTransactionFee)
+
+    // Balance assertion
+    await this.api
+      .getBalance(this.account)
+      .then((balance) =>
+        assert(
+          balance.toBn() < membershipFee.add(membershipTransactionFee),
+          'Account A already have sufficient balance to purchase membership'
+        )
+      )
+
+    // Buying memebership
+    await this.api.buyMembership(this.account, this.paidTerms, `late_member_${this.account.substring(0, 8)}`, true)
+
+    // Assertions
+    this.api.getMemberIds(this.account).then((membership) => assert(membership.length === 0, 'Account A is a member'))
+    if (expectFailure) {
+      throw new Error('Successful fixture run while expecting failure')
+    }
+  }
+}

+ 801 - 0
tests/network-tests/src/fixtures/proposalsModule.ts

@@ -0,0 +1,801 @@
+import { Api, WorkingGroups } from '../Api'
+import { v4 as uuid } from 'uuid'
+import BN from 'bn.js'
+import { ProposalId } from '@joystream/types/proposals'
+import { Fixture } from '../Fixture'
+import { assert } from 'chai'
+import { ApplicationId, OpeningId } from '@joystream/types/hiring'
+import { WorkerId } from '@joystream/types/working-group'
+import { Utils } from '../utils'
+import { EventRecord } from '@polkadot/types/interfaces'
+
+export class CreateWorkingGroupLeaderOpeningFixture implements Fixture {
+  private api: Api
+  private proposer: string
+  private applicationStake: BN
+  private roleStake: BN
+  private workingGroup: string
+
+  private result: ProposalId | undefined
+
+  constructor(api: Api, proposer: string, applicationStake: BN, roleStake: BN, workingGroup: string) {
+    this.api = api
+    this.proposer = proposer
+    this.applicationStake = applicationStake
+    this.roleStake = roleStake
+    this.workingGroup = workingGroup
+  }
+
+  public getCreatedProposalId(): ProposalId | undefined {
+    return this.result
+  }
+
+  public async runner(expectFailure: boolean): Promise<void> {
+    // Setup
+    const proposalTitle: string = 'Testing proposal ' + uuid().substring(0, 8)
+    const description: string = 'Testing working group lead opening proposal ' + uuid().substring(0, 8)
+
+    // Proposal stake calculation
+    const proposalStake: BN = new BN(100000)
+    const proposalFee: BN = this.api.estimateProposeCreateWorkingGroupLeaderOpeningFee()
+    this.api.treasuryTransferBalance(this.proposer, proposalFee.add(proposalStake))
+
+    // Proposal creation
+    const result = await this.api.proposeCreateWorkingGroupLeaderOpening({
+      account: this.proposer,
+      title: proposalTitle,
+      description: description,
+      proposalStake: proposalStake,
+      actiavteAt: 'CurrentBlock',
+      maxActiveApplicants: new BN(10),
+      maxReviewPeriodLength: new BN(32),
+      applicationStakingPolicyAmount: this.applicationStake,
+      applicationCrowdedOutUnstakingPeriodLength: new BN(1),
+      applicationReviewPeriodExpiredUnstakingPeriodLength: new BN(1),
+      roleStakingPolicyAmount: this.roleStake,
+      roleCrowdedOutUnstakingPeriodLength: new BN(1),
+      roleReviewPeriodExpiredUnstakingPeriodLength: new BN(1),
+      slashableMaxCount: new BN(1),
+      slashableMaxPercentPtsPerTime: new BN(100),
+      fillOpeningSuccessfulApplicantApplicationStakeUnstakingPeriod: new BN(1),
+      fillOpeningFailedApplicantApplicationStakeUnstakingPeriod: new BN(1),
+      fillOpeningFailedApplicantRoleStakeUnstakingPeriod: new BN(1),
+      terminateApplicationStakeUnstakingPeriod: new BN(1),
+      terminateRoleStakeUnstakingPeriod: new BN(1),
+      exitRoleApplicationStakeUnstakingPeriod: new BN(1),
+      exitRoleStakeUnstakingPeriod: new BN(1),
+      text: uuid().substring(0, 8),
+      workingGroup: this.workingGroup,
+    })
+
+    this.result = this.api.expectProposalCreatedEvent(result.events)
+
+    if (expectFailure) {
+      throw new Error('Successful fixture run while expecting failure')
+    }
+  }
+}
+
+export class BeginWorkingGroupLeaderApplicationReviewFixture implements Fixture {
+  private api: Api
+  private proposer: string
+  private openingId: OpeningId
+  private workingGroup: string
+
+  private result: ProposalId | undefined
+
+  constructor(api: Api, proposer: string, openingId: OpeningId, workingGroup: string) {
+    this.api = api
+    this.proposer = proposer
+    this.openingId = openingId
+    this.workingGroup = workingGroup
+  }
+
+  public getCreatedProposalId(): ProposalId | undefined {
+    return this.result
+  }
+
+  public async runner(expectFailure: boolean): Promise<void> {
+    // 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)
+
+    // Proposal stake calculation
+    const proposalStake: BN = new BN(25000)
+    const proposalFee: BN = this.api.estimateProposeBeginWorkingGroupLeaderApplicationReviewFee()
+    this.api.treasuryTransferBalance(this.proposer, proposalFee.add(proposalStake))
+
+    // Proposal creation
+    const result = await this.api.proposeBeginWorkingGroupLeaderApplicationReview(
+      this.proposer,
+      proposalTitle,
+      description,
+      proposalStake,
+      this.openingId,
+      this.workingGroup
+    )
+    this.result = this.api.expectProposalCreatedEvent(result.events)
+
+    if (expectFailure) {
+      throw new Error('Successful fixture run while expecting failure')
+    }
+  }
+}
+
+export class FillLeaderOpeningProposalFixture implements Fixture {
+  private api: Api
+  private proposer: string
+  private applicationId: ApplicationId
+  private firstRewardInterval: BN
+  private rewardInterval: BN
+  private payoutAmount: BN
+  private openingId: OpeningId
+  private workingGroup: WorkingGroups
+
+  private result: ProposalId | undefined
+
+  constructor(
+    api: Api,
+    proposer: string,
+    applicationId: ApplicationId,
+    firstRewardInterval: BN,
+    rewardInterval: BN,
+    payoutAmount: BN,
+    openingId: OpeningId,
+    workingGroup: WorkingGroups
+  ) {
+    this.api = api
+    this.proposer = proposer
+    this.applicationId = applicationId
+    this.firstRewardInterval = firstRewardInterval
+    this.rewardInterval = rewardInterval
+    this.payoutAmount = payoutAmount
+    this.openingId = openingId
+    this.workingGroup = workingGroup
+  }
+
+  public getCreatedProposalId(): ProposalId | undefined {
+    return this.result
+  }
+
+  public async runner(expectFailure: boolean): Promise<void> {
+    // Setup
+    const proposalTitle: string = 'Testing proposal ' + uuid().substring(0, 8)
+    const description: string = 'Testing fill opening proposal ' + uuid().substring(0, 8)
+    const workingGroupString: string = this.api.getWorkingGroupString(this.workingGroup)
+
+    // Proposal stake calculation
+    const proposalStake: BN = new BN(50000)
+    const proposalFee: BN = this.api.estimateProposeFillLeaderOpeningFee()
+    this.api.treasuryTransferBalance(this.proposer, proposalFee.add(proposalStake))
+
+    const now: BN = await this.api.getBestBlock()
+
+    // Proposal creation
+    const result = await this.api.proposeFillLeaderOpening({
+      account: this.proposer,
+      title: proposalTitle,
+      description: description,
+      proposalStake: proposalStake,
+      openingId: this.openingId,
+      successfulApplicationId: this.applicationId,
+      amountPerPayout: this.payoutAmount,
+      nextPaymentAtBlock: now.add(this.firstRewardInterval),
+      payoutInterval: this.rewardInterval,
+      workingGroup: workingGroupString,
+    })
+
+    this.result = this.api.expectProposalCreatedEvent(result.events)
+
+    if (expectFailure) {
+      throw new Error('Successful fixture run while expecting failure')
+    }
+  }
+}
+
+export class TerminateLeaderRoleProposalFixture implements Fixture {
+  private api: Api
+  private proposer: string
+  private slash: boolean
+  private workingGroup: WorkingGroups
+
+  private result: ProposalId | undefined
+
+  constructor(api: Api, proposer: string, slash: boolean, workingGroup: WorkingGroups) {
+    this.api = api
+    this.proposer = proposer
+    this.slash = slash
+    this.workingGroup = workingGroup
+  }
+
+  public getCreatedProposalId(): ProposalId | undefined {
+    return this.result
+  }
+
+  public async runner(expectFailure: boolean): Promise<void> {
+    // 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)
+    const rationale: string = 'Testing leader termination ' + uuid().substring(0, 8)
+    const workingGroupString: string = this.api.getWorkingGroupString(this.workingGroup)
+    // assert worker exists
+    const workerId: WorkerId = (await this.api.getLeadWorkerId(this.workingGroup))!
+
+    // Proposal stake calculation
+    const proposalStake: BN = new BN(100000)
+    const proposalFee: BN = this.api.estimateProposeTerminateLeaderRoleFee()
+    this.api.treasuryTransferBalance(this.proposer, proposalFee.add(proposalStake))
+
+    // Proposal creation
+    const result = await this.api.proposeTerminateLeaderRole(
+      this.proposer,
+      proposalTitle,
+      description,
+      proposalStake,
+      workerId,
+      rationale,
+      this.slash,
+      workingGroupString
+    )
+    this.result = this.api.expectProposalCreatedEvent(result.events)
+
+    if (expectFailure) {
+      throw new Error('Successful fixture run while expecting failure')
+    }
+  }
+}
+
+export class SetLeaderRewardProposalFixture implements Fixture {
+  private api: Api
+  private proposer: string
+  private payoutAmount: BN
+  private workingGroup: WorkingGroups
+
+  private result: ProposalId | undefined
+
+  constructor(api: Api, proposer: string, payoutAmount: BN, workingGroup: WorkingGroups) {
+    this.api = api
+    this.proposer = proposer
+    this.payoutAmount = payoutAmount
+    this.workingGroup = workingGroup
+  }
+
+  public getCreatedProposalId(): ProposalId | undefined {
+    return this.result
+  }
+
+  public async runner(expectFailure: boolean): Promise<void> {
+    // Setup
+    const proposalTitle: string = 'Testing proposal ' + uuid().substring(0, 8)
+    const description: string = 'Testing set leader reward proposal ' + uuid().substring(0, 8)
+    const workingGroupString: string = this.api.getWorkingGroupString(this.workingGroup)
+    // assert worker exists?
+    const workerId: WorkerId = (await this.api.getLeadWorkerId(this.workingGroup))!
+
+    // Proposal stake calculation
+    const proposalStake: BN = new BN(50000)
+    const proposalFee: BN = this.api.estimateProposeLeaderRewardFee()
+    this.api.treasuryTransferBalance(this.proposer, proposalFee.add(proposalStake))
+
+    // Proposal creation
+    const result = await this.api.proposeLeaderReward(
+      this.proposer,
+      proposalTitle,
+      description,
+      proposalStake,
+      workerId,
+      this.payoutAmount,
+      workingGroupString
+    )
+
+    this.result = this.api.expectProposalCreatedEvent(result.events)
+
+    if (expectFailure) {
+      throw new Error('Successful fixture run while expecting failure')
+    }
+  }
+}
+
+export class DecreaseLeaderStakeProposalFixture implements Fixture {
+  private api: Api
+  private proposer: string
+  private stakeDecrement: BN
+  private workingGroup: WorkingGroups
+
+  private result: ProposalId | undefined
+
+  constructor(api: Api, proposer: string, stakeDecrement: BN, workingGroup: WorkingGroups) {
+    this.api = api
+    this.proposer = proposer
+    this.stakeDecrement = stakeDecrement
+    this.workingGroup = workingGroup
+  }
+
+  public getCreatedProposalId(): ProposalId | undefined {
+    return this.result
+  }
+
+  public async runner(expectFailure: boolean): Promise<void> {
+    // Setup
+    const proposalTitle: string = 'Testing proposal ' + uuid().substring(0, 8)
+    const description: string = 'Testing decrease leader stake proposal ' + uuid().substring(0, 8)
+    const workingGroupString: string = this.api.getWorkingGroupString(this.workingGroup)
+    // assert worker exists ?
+    const workerId: WorkerId = (await this.api.getLeadWorkerId(this.workingGroup))!
+
+    // Proposal stake calculation
+    const proposalStake: BN = new BN(50000)
+    const proposalFee: BN = this.api.estimateProposeDecreaseLeaderStakeFee()
+    this.api.treasuryTransferBalance(this.proposer, proposalFee.add(proposalStake))
+
+    // Proposal creation
+    const result = await this.api.proposeDecreaseLeaderStake(
+      this.proposer,
+      proposalTitle,
+      description,
+      proposalStake,
+      workerId,
+      this.stakeDecrement,
+      workingGroupString
+    )
+
+    this.result = this.api.expectProposalCreatedEvent(result.events)
+
+    if (expectFailure) {
+      throw new Error('Successful fixture run while expecting failure')
+    }
+  }
+}
+
+export class SlashLeaderProposalFixture implements Fixture {
+  private api: Api
+  private proposer: string
+  private slashAmount: BN
+  private workingGroup: WorkingGroups
+
+  private result: ProposalId | undefined
+
+  constructor(api: Api, proposer: string, slashAmount: BN, workingGroup: WorkingGroups) {
+    this.api = api
+    this.proposer = proposer
+    this.slashAmount = slashAmount
+    this.workingGroup = workingGroup
+  }
+
+  public getCreatedProposalId(): ProposalId | undefined {
+    return this.result
+  }
+
+  public async runner(expectFailure: boolean): Promise<void> {
+    // Setup
+    const proposalTitle: string = 'Testing proposal ' + uuid().substring(0, 8)
+    const description: string = 'Testing slash leader stake proposal ' + uuid().substring(0, 8)
+    const workingGroupString: string = this.api.getWorkingGroupString(this.workingGroup)
+    const workerId: WorkerId = (await this.api.getLeadWorkerId(this.workingGroup))!
+
+    // Proposal stake calculation
+    const proposalStake: BN = new BN(50000)
+    const proposalFee: BN = this.api.estimateProposeSlashLeaderStakeFee()
+    this.api.treasuryTransferBalance(this.proposer, proposalFee.add(proposalStake))
+
+    // Proposal creation
+    const result = await this.api.proposeSlashLeaderStake(
+      this.proposer,
+      proposalTitle,
+      description,
+      proposalStake,
+      workerId,
+      this.slashAmount,
+      workingGroupString
+    )
+    this.result = this.api.expectProposalCreatedEvent(result.events)
+    if (expectFailure) {
+      throw new Error('Successful fixture run while expecting failure')
+    }
+  }
+}
+
+export class WorkingGroupMintCapacityProposalFixture implements Fixture {
+  private api: Api
+  private proposer: string
+  private mintCapacity: BN
+  private workingGroup: WorkingGroups
+
+  private result: ProposalId | undefined
+
+  constructor(api: Api, proposer: string, mintCapacity: BN, workingGroup: WorkingGroups) {
+    this.api = api
+    this.proposer = proposer
+    this.mintCapacity = mintCapacity
+    this.workingGroup = workingGroup
+  }
+
+  public getCreatedProposalId(): ProposalId | undefined {
+    return this.result
+  }
+
+  public async runner(expectFailure: boolean): Promise<void> {
+    // Setup
+    const proposalTitle: string = 'Testing proposal ' + uuid().substring(0, 8)
+    const description: string = 'Testing working group mint capacity proposal ' + uuid().substring(0, 8)
+    const workingGroupString: string = this.api.getWorkingGroupString(this.workingGroup)
+
+    // Proposal stake calculation
+    const proposalStake: BN = new BN(50000)
+    const proposalFee: BN = this.api.estimateProposeWorkingGroupMintCapacityFee()
+    this.api.treasuryTransferBalance(this.proposer, proposalFee.add(proposalStake))
+
+    // Proposal creation
+    const result = await this.api.proposeWorkingGroupMintCapacity(
+      this.proposer,
+      proposalTitle,
+      description,
+      proposalStake,
+      this.mintCapacity,
+      workingGroupString
+    )
+    this.result = this.api.expectProposalCreatedEvent(result.events)
+    if (expectFailure) {
+      throw new Error('Successful fixture run while expecting failure')
+    }
+  }
+}
+
+export class ElectionParametersProposalFixture implements Fixture {
+  private api: Api
+  private proposerAccount: string
+
+  constructor(api: Api, proposerAccount: string) {
+    this.api = api
+    this.proposerAccount = proposerAccount
+  }
+
+  public async runner(expectFailure: boolean): Promise<void> {
+    // Setup
+    const proposalTitle: string = 'Testing proposal ' + uuid().substring(0, 8)
+    const description: string = 'Testing validator count proposal ' + uuid().substring(0, 8)
+
+    // Council accounts enough balance to ensure they can vote
+    const councilAccounts = await this.api.getCouncilAccounts()
+    const runtimeVoteFee: BN = this.api.estimateVoteForProposalFee()
+    this.api.treasuryTransferBalanceToAccounts(councilAccounts, runtimeVoteFee)
+
+    const announcingPeriod: BN = new BN(28800)
+    const votingPeriod: BN = new BN(14400)
+    const revealingPeriod: BN = new BN(14400)
+    const councilSize: BN = await this.api.getCouncilSize()
+    const candidacyLimit: BN = await this.api.getCandidacyLimit()
+    const newTermDuration: BN = new BN(144000)
+    const minCouncilStake: BN = await this.api.getMinCouncilStake()
+    const minVotingStake: BN = await this.api.getMinVotingStake()
+
+    // Proposal stake calculation
+    // Required stake is hardcoded in runtime-module (but not available as const)
+    const proposalStake: BN = new BN(200000)
+    const proposalFee: BN = this.api.estimateProposeElectionParametersFee(
+      description,
+      description,
+      proposalStake,
+      announcingPeriod,
+      votingPeriod,
+      revealingPeriod,
+      councilSize,
+      candidacyLimit,
+      newTermDuration,
+      minCouncilStake,
+      minVotingStake
+    )
+
+    this.api.treasuryTransferBalance(this.proposerAccount, proposalFee.add(proposalStake))
+
+    // Proposal creation
+    const proposedAnnouncingPeriod: BN = announcingPeriod.subn(1)
+    const proposedVotingPeriod: BN = votingPeriod.addn(1)
+    const proposedRevealingPeriod: BN = revealingPeriod.addn(1)
+    const proposedCouncilSize: BN = councilSize.addn(1)
+    const proposedCandidacyLimit: BN = candidacyLimit.addn(1)
+    const proposedNewTermDuration: BN = newTermDuration.addn(1)
+    const proposedMinCouncilStake: BN = minCouncilStake.addn(1)
+    const proposedMinVotingStake: BN = minVotingStake.addn(1)
+
+    const proposalCreationResult = await this.api.proposeElectionParameters(
+      this.proposerAccount,
+      proposalTitle,
+      description,
+      proposalStake,
+      proposedAnnouncingPeriod,
+      proposedVotingPeriod,
+      proposedRevealingPeriod,
+      proposedCouncilSize,
+      proposedCandidacyLimit,
+      proposedNewTermDuration,
+      proposedMinCouncilStake,
+      proposedMinVotingStake
+    )
+    const proposalNumber = this.api.expectProposalCreatedEvent(proposalCreationResult.events)
+
+    // Approving the proposal
+    this.api.batchApproveProposal(proposalNumber)
+    await this.api.waitForProposalToFinalize(proposalNumber)
+
+    // Assertions
+    const newAnnouncingPeriod: BN = await this.api.getAnnouncingPeriod()
+    const newVotingPeriod: BN = await this.api.getVotingPeriod()
+    const newRevealingPeriod: BN = await this.api.getRevealingPeriod()
+    const newCouncilSize: BN = await this.api.getCouncilSize()
+    const newCandidacyLimit: BN = await this.api.getCandidacyLimit()
+    const newNewTermDuration: BN = await this.api.getNewTermDuration()
+    const newMinCouncilStake: BN = await this.api.getMinCouncilStake()
+    const newMinVotingStake: BN = await this.api.getMinVotingStake()
+    assert(
+      proposedAnnouncingPeriod.eq(newAnnouncingPeriod),
+      `Announcing period has unexpected value ${newAnnouncingPeriod}, expected ${proposedAnnouncingPeriod}`
+    )
+    assert(
+      proposedVotingPeriod.eq(newVotingPeriod),
+      `Voting period has unexpected value ${newVotingPeriod}, expected ${proposedVotingPeriod}`
+    )
+    assert(
+      proposedRevealingPeriod.eq(newRevealingPeriod),
+      `Revealing has unexpected value ${newRevealingPeriod}, expected ${proposedRevealingPeriod}`
+    )
+    assert(
+      proposedCouncilSize.eq(newCouncilSize),
+      `Council size has unexpected value ${newCouncilSize}, expected ${proposedCouncilSize}`
+    )
+    assert(
+      proposedCandidacyLimit.eq(newCandidacyLimit),
+      `Candidacy limit has unexpected value ${newCandidacyLimit}, expected ${proposedCandidacyLimit}`
+    )
+    assert(
+      proposedNewTermDuration.eq(newNewTermDuration),
+      `New term duration has unexpected value ${newNewTermDuration}, expected ${proposedNewTermDuration}`
+    )
+    assert(
+      proposedMinCouncilStake.eq(newMinCouncilStake),
+      `Min council stake has unexpected value ${newMinCouncilStake}, expected ${proposedMinCouncilStake}`
+    )
+    assert(
+      proposedMinVotingStake.eq(newMinVotingStake),
+      `Min voting stake has unexpected value ${newMinVotingStake}, expected ${proposedMinVotingStake}`
+    )
+    if (expectFailure) {
+      throw new Error('Successful fixture run while expecting failure')
+    }
+  }
+}
+
+export class SpendingProposalFixture implements Fixture {
+  private api: Api
+  private proposer: string
+  private spendingBalance: BN
+  private mintCapacity: BN
+
+  constructor(api: Api, proposer: string, spendingBalance: BN, mintCapacity: BN) {
+    this.api = api
+    this.proposer = proposer
+    this.spendingBalance = spendingBalance
+    this.mintCapacity = mintCapacity
+  }
+
+  public async runner(expectFailure: boolean): Promise<void> {
+    // Setup
+    const description = 'spending proposal which is used for API network testing with some mock data'
+    const runtimeVoteFee: BN = this.api.estimateVoteForProposalFee()
+
+    // Topping the balances
+    const proposalStake: BN = new BN(25000)
+    const runtimeProposalFee: BN = this.api.estimateProposeSpendingFee(
+      description,
+      description,
+      proposalStake,
+      this.spendingBalance,
+      this.proposer
+    )
+    this.api.treasuryTransferBalance(this.proposer, runtimeProposalFee.add(proposalStake))
+    const councilAccounts = await this.api.getCouncilAccounts()
+    this.api.treasuryTransferBalanceToAccounts(councilAccounts, runtimeVoteFee)
+    await this.api.sudoSetCouncilMintCapacity(this.mintCapacity)
+
+    const fundingRecipient = this.api.createKeyPairs(1)[0].address
+
+    // Proposal creation
+    const result = await this.api.proposeSpending(
+      this.proposer,
+      'testing spending' + uuid().substring(0, 8),
+      'spending to test proposal functionality' + uuid().substring(0, 8),
+      proposalStake,
+      this.spendingBalance,
+      fundingRecipient
+    )
+    const proposalNumber: ProposalId = this.api.expectProposalCreatedEvent(result.events)
+
+    // Approving spending proposal
+    const balanceBeforeMinting: BN = await this.api.getBalance(fundingRecipient)
+    this.api.batchApproveProposal(proposalNumber)
+    await this.api.waitForProposalToFinalize(proposalNumber)
+
+    const balanceAfterMinting: BN = await this.api.getBalance(fundingRecipient)
+    assert(
+      balanceAfterMinting.eq(balanceBeforeMinting.add(this.spendingBalance)),
+      `member ${fundingRecipient} has unexpected balance ${balanceAfterMinting}, expected ${balanceBeforeMinting.add(
+        this.spendingBalance
+      )}`
+    )
+    if (expectFailure) {
+      throw new Error('Successful fixture run while expecting failure')
+    }
+  }
+}
+
+export class TextProposalFixture implements Fixture {
+  private api: Api
+  private proposer: string
+
+  constructor(api: Api, proposer: string) {
+    this.api = api
+    this.proposer = proposer
+  }
+
+  public async runner(expectFailure: boolean): Promise<void> {
+    // Setup
+    const proposalTitle: string = 'Testing proposal ' + uuid().substring(0, 8)
+    const description: string = 'Testing text proposal ' + uuid().substring(0, 8)
+    const proposalText: string = 'Text of the testing proposal ' + uuid().substring(0, 8)
+    const runtimeVoteFee: BN = this.api.estimateVoteForProposalFee()
+    const councilAccounts = await this.api.getCouncilAccounts()
+    this.api.treasuryTransferBalanceToAccounts(councilAccounts, runtimeVoteFee)
+
+    // Proposal stake calculation
+    const proposalStake: BN = new BN(25000)
+    const runtimeProposalFee: BN = this.api.estimateProposeTextFee(
+      proposalStake,
+      description,
+      description,
+      proposalText
+    )
+    this.api.treasuryTransferBalance(this.proposer, runtimeProposalFee.add(proposalStake))
+
+    // Proposal creation
+
+    const result = await this.api.proposeText(this.proposer, proposalStake, proposalTitle, description, proposalText)
+    const proposalNumber: ProposalId = this.api.expectProposalCreatedEvent(result.events)
+
+    // Approving text proposal
+    this.api.batchApproveProposal(proposalNumber)
+    await this.api.waitForProposalToFinalize(proposalNumber)
+
+    if (expectFailure) {
+      throw new Error('Successful fixture run while expecting failure')
+    }
+  }
+}
+
+export class ValidatorCountProposalFixture implements Fixture {
+  private api: Api
+  private proposer: string
+  private validatorCountIncrement: BN
+
+  constructor(api: Api, proposer: string, validatorCountIncrement: BN) {
+    this.api = api
+    this.proposer = proposer
+    this.validatorCountIncrement = validatorCountIncrement
+  }
+
+  public async runner(expectFailure: boolean): Promise<void> {
+    // Setup
+    const proposalTitle: string = 'Testing proposal ' + uuid().substring(0, 8)
+    const description: string = 'Testing validator count proposal ' + uuid().substring(0, 8)
+    const runtimeVoteFee: BN = this.api.estimateVoteForProposalFee()
+    const councilAccounts = await this.api.getCouncilAccounts()
+    this.api.treasuryTransferBalanceToAccounts(councilAccounts, runtimeVoteFee)
+
+    // Proposal stake calculation
+    const proposalStake: BN = new BN(100000)
+    const proposalFee: BN = this.api.estimateProposeValidatorCountFee(description, description, proposalStake)
+    this.api.treasuryTransferBalance(this.proposer, proposalFee.add(proposalStake))
+    const validatorCount: BN = await this.api.getValidatorCount()
+
+    // Proposal creation
+    const proposedValidatorCount: BN = validatorCount.add(this.validatorCountIncrement)
+    const result = await this.api.proposeValidatorCount(
+      this.proposer,
+      proposalTitle,
+      description,
+      proposalStake,
+      proposedValidatorCount
+    )
+    const proposalNumber: ProposalId = this.api.expectProposalCreatedEvent(result.events)
+
+    // Approving the proposal
+    this.api.batchApproveProposal(proposalNumber)
+    await this.api.waitForProposalToFinalize(proposalNumber)
+
+    const newValidatorCount: BN = await this.api.getValidatorCount()
+    assert(
+      proposedValidatorCount.eq(newValidatorCount),
+      `Validator count has unexpeccted value ${newValidatorCount}, expected ${proposedValidatorCount}`
+    )
+    if (expectFailure) {
+      throw new Error('Successful fixture run while expecting failure')
+    }
+  }
+}
+
+export class UpdateRuntimeFixture implements Fixture {
+  private api: Api
+  private proposer: string
+  private runtimePath: string
+
+  constructor(api: Api, proposer: string, runtimePath: string) {
+    this.api = api
+    this.proposer = proposer
+    this.runtimePath = runtimePath
+  }
+
+  public async runner(expectFailure: boolean): Promise<void> {
+    // Setup
+    const runtime: string = Utils.readRuntimeFromFile(this.runtimePath)
+    const description = 'runtime upgrade proposal which is used for API network testing'
+    const runtimeVoteFee: BN = this.api.estimateVoteForProposalFee()
+
+    // Topping the balances
+    const proposalStake: BN = new BN(1000000)
+    const runtimeProposalFee: BN = this.api.estimateProposeRuntimeUpgradeFee(
+      proposalStake,
+      description,
+      description,
+      runtime
+    )
+    this.api.treasuryTransferBalance(this.proposer, runtimeProposalFee.add(proposalStake))
+    const councilAccounts = await this.api.getCouncilAccounts()
+    this.api.treasuryTransferBalanceToAccounts(councilAccounts, runtimeVoteFee)
+
+    // Proposal creation
+    const result = await this.api.proposeRuntime(
+      this.proposer,
+      proposalStake,
+      'testing runtime' + uuid().substring(0, 8),
+      'runtime to test proposal functionality' + uuid().substring(0, 8),
+      runtime
+    )
+    const proposalNumber: ProposalId = this.api.expectProposalCreatedEvent(result.events)
+
+    // Approving runtime update proposal
+    this.api.batchApproveProposal(proposalNumber)
+    await this.api.waitForProposalToFinalize(proposalNumber)
+
+    if (expectFailure) {
+      throw new Error('Successful fixture run while expecting failure')
+    }
+  }
+}
+
+export class VoteForProposalFixture implements Fixture {
+  private api: Api
+  private proposalNumber: ProposalId
+  private events: EventRecord[] = []
+
+  constructor(api: Api, proposalNumber: ProposalId) {
+    this.api = api
+    this.proposalNumber = proposalNumber
+  }
+
+  public getEvents(): EventRecord[] {
+    return this.events
+  }
+
+  public async runner(expectFailure: boolean): Promise<void> {
+    const proposalVoteFee: BN = this.api.estimateVoteForProposalFee()
+    const councilAccounts = await this.api.getCouncilAccounts()
+    this.api.treasuryTransferBalanceToAccounts(councilAccounts, proposalVoteFee)
+
+    // Approving the proposal
+    this.api.batchApproveProposal(this.proposalNumber)
+    this.events = await this.api.waitForProposalToFinalize(this.proposalNumber)
+
+    if (expectFailure) {
+      throw new Error('Successful fixture run while expecting failure')
+    }
+  }
+}

+ 100 - 0
tests/network-tests/src/fixtures/sudoHireLead.ts

@@ -0,0 +1,100 @@
+import { Fixture } from '../Fixture'
+import {
+  SudoAddLeaderOpeningFixture,
+  ApplyForOpeningFixture,
+  SudoBeginLeaderApplicationReviewFixture,
+  SudoFillLeaderOpeningFixture,
+} from './workingGroupModule'
+import { BuyMembershipHappyCaseFixture } from './membershipModule'
+import { Api, WorkingGroups } from '../Api'
+import { OpeningId } from '@joystream/types/hiring'
+import { PaidTermId } from '@joystream/types/members'
+import BN from 'bn.js'
+import { assert } from 'chai'
+
+export class SudoHireLeadFixture implements Fixture {
+  private api: Api
+  private leadAccount: string
+  private paidTerms: PaidTermId
+  private applicationStake: BN
+  private roleStake: BN
+  private openingActivationDelay: BN
+  private rewardInterval: BN
+  private firstRewardInterval: BN
+  private payoutAmount: BN
+  private workingGroup: WorkingGroups
+
+  constructor(
+    api: Api,
+    leadAccount: string,
+    paidTerms: PaidTermId,
+    applicationStake: BN,
+    roleStake: BN,
+    openingActivationDelay: BN,
+    rewardInterval: BN,
+    firstRewardInterval: BN,
+    payoutAmount: BN,
+    workingGroup: WorkingGroups
+  ) {
+    this.api = api
+    this.leadAccount = leadAccount
+    this.paidTerms = paidTerms
+    this.applicationStake = applicationStake
+    this.roleStake = roleStake
+    this.openingActivationDelay = openingActivationDelay
+    this.rewardInterval = rewardInterval
+    this.firstRewardInterval = firstRewardInterval
+    this.payoutAmount = payoutAmount
+    this.workingGroup = workingGroup
+  }
+
+  public async runner(expectFailure: boolean): Promise<void> {
+    const leaderHappyCaseFixture: BuyMembershipHappyCaseFixture = new BuyMembershipHappyCaseFixture(
+      this.api,
+      [this.leadAccount],
+      this.paidTerms
+    )
+    // Buying membership for leader account
+    await leaderHappyCaseFixture.runner(false)
+
+    const addLeaderOpeningFixture: SudoAddLeaderOpeningFixture = new SudoAddLeaderOpeningFixture(
+      this.api,
+      this.applicationStake,
+      this.roleStake,
+      this.openingActivationDelay,
+      this.workingGroup
+    )
+    // Add lead opening
+    await addLeaderOpeningFixture.runner(false)
+
+    const applyForLeaderOpeningFixture = new ApplyForOpeningFixture(
+      this.api,
+      [this.leadAccount],
+      this.applicationStake,
+      this.roleStake,
+      addLeaderOpeningFixture.getCreatedOpeningId() as OpeningId,
+      this.workingGroup
+    )
+    await applyForLeaderOpeningFixture.runner(false)
+
+    assert(applyForLeaderOpeningFixture.getApplicationIds().length === 1)
+
+    const beginLeaderApplicationReviewFixture = new SudoBeginLeaderApplicationReviewFixture(
+      this.api,
+      addLeaderOpeningFixture.getCreatedOpeningId() as OpeningId,
+      this.workingGroup
+    )
+    await beginLeaderApplicationReviewFixture.runner(false)
+
+    const fillLeaderOpeningFixture = new SudoFillLeaderOpeningFixture(
+      this.api,
+      applyForLeaderOpeningFixture.getApplicationIds()[0],
+      addLeaderOpeningFixture.getCreatedOpeningId() as OpeningId,
+      this.firstRewardInterval,
+      this.rewardInterval,
+      this.payoutAmount,
+      this.workingGroup
+    )
+    await fillLeaderOpeningFixture.runner(false)
+  }
+}

+ 770 - 0
tests/network-tests/src/fixtures/workingGroupModule.ts

@@ -0,0 +1,770 @@
+import BN from 'bn.js'
+import { assert } from 'chai'
+import { Api, WorkingGroups } from '../Api'
+import { KeyringPair } from '@polkadot/keyring/types'
+import { v4 as uuid } from 'uuid'
+import { RewardRelationship } from '@joystream/types/recurring-rewards'
+import { Application, ApplicationIdToWorkerIdMap, Worker, WorkerId } from '@joystream/types/working-group'
+import { Utils } from '../utils'
+import { ApplicationId, Opening as HiringOpening, OpeningId } from '@joystream/types/hiring'
+import { Fixture } from '../Fixture'
+
+export class AddWorkerOpeningFixture implements Fixture {
+  private api: Api
+  private applicationStake: BN
+  private roleStake: BN
+  private activationDelay: BN
+  private unstakingPeriod: BN
+  private module: WorkingGroups
+
+  private result: OpeningId | undefined
+
+  public getCreatedOpeningId(): OpeningId | undefined {
+    return this.result
+  }
+
+  public constructor(
+    api: Api,
+    applicationStake: BN,
+    roleStake: BN,
+    activationDelay: BN,
+    unstakingPeriod: BN,
+    module: WorkingGroups
+  ) {
+    this.api = api
+    this.applicationStake = applicationStake
+    this.roleStake = roleStake
+    this.activationDelay = activationDelay
+    this.unstakingPeriod = unstakingPeriod
+    this.module = module
+  }
+
+  public async runner(expectFailure: boolean): Promise<void> {
+    const lead = await this.api.getGroupLead(this.module)
+    if (!lead) {
+      throw new Error('No Lead')
+    }
+    // Fee estimation and transfer
+    const addOpeningFee: BN = this.api.estimateAddOpeningFee(this.module)
+    this.api.treasuryTransferBalance(lead.role_account_id.toString(), addOpeningFee)
+
+    // Worker opening creation
+    const result = await this.api.addOpening(
+      lead.role_account_id.toString(),
+      {
+        activationDelay: this.activationDelay,
+        maxActiveApplicants: new BN(10),
+        maxReviewPeriodLength: new BN(32),
+        applicationStakingPolicyAmount: this.applicationStake,
+        applicationCrowdedOutUnstakingPeriodLength: new BN(1),
+        applicationReviewPeriodExpiredUnstakingPeriodLength: new BN(1),
+        roleStakingPolicyAmount: this.roleStake,
+        roleCrowdedOutUnstakingPeriodLength: new BN(1),
+        roleReviewPeriodExpiredUnstakingPeriodLength: new BN(1),
+        slashableMaxCount: new BN(1),
+        slashableMaxPercentPtsPerTime: new BN(100),
+        fillOpeningSuccessfulApplicantApplicationStakeUnstakingPeriod: this.unstakingPeriod,
+        fillOpeningFailedApplicantApplicationStakeUnstakingPeriod: this.unstakingPeriod,
+        fillOpeningFailedApplicantRoleStakeUnstakingPeriod: this.unstakingPeriod,
+        terminateApplicationStakeUnstakingPeriod: this.unstakingPeriod,
+        terminateRoleStakeUnstakingPeriod: this.unstakingPeriod,
+        exitRoleApplicationStakeUnstakingPeriod: this.unstakingPeriod,
+        exitRoleStakeUnstakingPeriod: this.unstakingPeriod,
+        text: uuid().substring(0, 8),
+        type: 'Worker',
+      },
+      this.module,
+      expectFailure
+    )
+
+    if (!expectFailure) {
+      this.result = this.api.expectOpeningAddedEvent(result.events)
+    }
+  }
+}
+
+export class SudoAddLeaderOpeningFixture implements Fixture {
+  private api: Api
+  private applicationStake: BN
+  private roleStake: BN
+  private activationDelay: BN
+  private module: WorkingGroups
+
+  private result: OpeningId | undefined
+
+  public getCreatedOpeningId(): OpeningId | undefined {
+    return this.result
+  }
+
+  public constructor(api: Api, applicationStake: BN, roleStake: BN, activationDelay: BN, module: WorkingGroups) {
+    this.api = api
+    this.applicationStake = applicationStake
+    this.roleStake = roleStake
+    this.activationDelay = activationDelay
+    this.module = module
+  }
+
+  public async runner(expectFailure: boolean): Promise<void> {
+    const result = await this.api.sudoAddOpening(
+      {
+        activationDelay: this.activationDelay,
+        maxActiveApplicants: new BN(10),
+        maxReviewPeriodLength: new BN(32),
+        applicationStakingPolicyAmount: this.applicationStake,
+        applicationCrowdedOutUnstakingPeriodLength: new BN(1),
+        applicationReviewPeriodExpiredUnstakingPeriodLength: new BN(1),
+        roleStakingPolicyAmount: this.roleStake,
+        roleCrowdedOutUnstakingPeriodLength: new BN(1),
+        roleReviewPeriodExpiredUnstakingPeriodLength: new BN(1),
+        slashableMaxCount: new BN(1),
+        slashableMaxPercentPtsPerTime: new BN(100),
+        fillOpeningSuccessfulApplicantApplicationStakeUnstakingPeriod: new BN(1),
+        fillOpeningFailedApplicantApplicationStakeUnstakingPeriod: new BN(1),
+        fillOpeningFailedApplicantRoleStakeUnstakingPeriod: new BN(1),
+        terminateApplicationStakeUnstakingPeriod: new BN(1),
+        terminateRoleStakeUnstakingPeriod: new BN(1),
+        exitRoleApplicationStakeUnstakingPeriod: new BN(1),
+        exitRoleStakeUnstakingPeriod: new BN(1),
+        text: uuid().substring(0, 8),
+        type: 'Leader',
+      },
+      this.module
+    )
+
+    if (expectFailure) {
+      throw new Error('Successful fixture run while expecting failure')
+    } else {
+      this.result = this.api.expectOpeningAddedEvent(result.events)
+    }
+  }
+}
+
+export class AcceptApplicationsFixture implements Fixture {
+  private api: Api
+  private openingId: OpeningId
+  private module: WorkingGroups
+
+  public constructor(api: Api, openingId: OpeningId, module: WorkingGroups) {
+    this.api = api
+    this.openingId = openingId
+    this.module = module
+  }
+
+  public async runner(expectFailure: boolean): Promise<void> {
+    const lead = await this.api.getGroupLead(this.module)
+    if (!lead) {
+      throw new Error('No Lead')
+    }
+    const leadAccount = lead.role_account_id.toString()
+    // Fee estimation and transfer
+    const acceptApplicationsFee: BN = this.api.estimateAcceptApplicationsFee(this.module)
+    this.api.treasuryTransferBalance(leadAccount, acceptApplicationsFee)
+
+    // Begin accepting applications
+    await this.api.acceptApplications(leadAccount, this.openingId, this.module)
+    const wgOpening = await this.api.getWorkingGroupOpening(this.openingId, this.module)
+    const opening: HiringOpening = await this.api.getHiringOpening(wgOpening.hiring_opening_id)
+    assert(opening.is_active, `${this.module} Opening ${this.openingId} is not active`)
+    if (expectFailure) {
+      throw new Error('Successful fixture run while expecting failure')
+    }
+  }
+}
+
+export class ApplyForOpeningFixture implements Fixture {
+  private api: Api
+  private applicants: string[]
+  private applicationStake: BN
+  private roleStake: BN
+  private openingId: OpeningId
+  private module: WorkingGroups
+  private result: ApplicationId[] = []
+
+  public constructor(
+    api: Api,
+    applicants: string[],
+    applicationStake: BN,
+    roleStake: BN,
+    openingId: OpeningId,
+    module: WorkingGroups
+  ) {
+    this.api = api
+    this.applicants = applicants
+    this.applicationStake = applicationStake
+    this.roleStake = roleStake
+    this.openingId = openingId
+    this.module = module
+  }
+
+  public getApplicationIds(): ApplicationId[] {
+    return this.result
+  }
+
+  public async runner(expectFailure: boolean): Promise<void> {
+    // Fee estimation and transfer
+    const applyOnOpeningFee: BN = this.api
+      .estimateApplyOnOpeningFee(this.applicants[0], this.module)
+      .add(this.applicationStake)
+      .add(this.roleStake)
+    this.api.treasuryTransferBalanceToAccounts(this.applicants, applyOnOpeningFee)
+
+    // Applying for created worker opening
+    const results = await this.api.batchApplyOnOpening(
+      this.applicants,
+      this.openingId,
+      this.roleStake,
+      this.applicationStake,
+      uuid().substring(0, 8),
+      this.module,
+      expectFailure
+    )
+
+    const applicationIds = results.map(({ events }) => {
+      const record = events.find(
+        (record) => record.event.method && record.event.method.toString() === 'AppliedOnOpening'
+      )
+      if (record) {
+        return (record.event.data[1] as unknown) as ApplicationId
+      }
+      throw new Error('Application on opening failed')
+    })
+
+    this.result = applicationIds
+  }
+}
+
+export class WithdrawApplicationFixture implements Fixture {
+  private api: Api
+  private applicationIds: ApplicationId[]
+  private module: WorkingGroups
+
+  constructor(api: Api, applicationIds: ApplicationId[], module: WorkingGroups) {
+    this.api = api
+    this.applicationIds = applicationIds
+    this.module = module
+  }
+
+  public async runner(expectFailure: boolean): Promise<void> {
+    // Fee estimation and transfer
+    const withdrawApplicaitonFee: BN = this.api.estimateWithdrawApplicationFee(this.module)
+
+    // get role accounts of applicants
+    const roleAccounts = await this.api.getApplicantRoleAccounts(this.applicationIds, this.module)
+    this.api.treasuryTransferBalanceToAccounts(roleAccounts, withdrawApplicaitonFee)
+
+    // Application withdrawal
+    await this.api.batchWithdrawActiveApplications(this.applicationIds, this.module)
+
+    if (expectFailure) {
+      throw new Error('Successful fixture run while expecting failure')
+    }
+  }
+}
+
+export class BeginApplicationReviewFixture implements Fixture {
+  private api: Api
+  private openingId: OpeningId
+  private module: WorkingGroups
+
+  constructor(api: Api, openingId: OpeningId, module: WorkingGroups) {
+    this.api = api
+    this.openingId = openingId
+    this.module = module
+  }
+
+  public async runner(expectFailure: boolean): Promise<void> {
+    const lead = await this.api.getGroupLead(this.module)
+    if (!lead) {
+      throw new Error('No Lead')
+    }
+    const leadAccount = lead.role_account_id.toString()
+    // Fee estimation and transfer
+    const beginReviewFee: BN = this.api.estimateBeginApplicantReviewFee(this.module)
+    this.api.treasuryTransferBalance(leadAccount, beginReviewFee)
+
+    // Begin application review
+    // const beginApplicantReviewPromise: Promise<ApplicationId> = this.api.expectApplicationReviewBegan()
+    const result = await this.api.beginApplicantReview(leadAccount, this.openingId, this.module)
+
+    this.api.expectApplicationReviewBeganEvent(result.events)
+
+    if (expectFailure) {
+      throw new Error('Successful fixture run while expecting failure')
+    }
+  }
+}
+
+export class SudoBeginLeaderApplicationReviewFixture implements Fixture {
+  private api: Api
+  private openingId: OpeningId
+  private module: WorkingGroups
+
+  constructor(api: Api, openingId: OpeningId, module: WorkingGroups) {
+    this.api = api
+    this.openingId = openingId
+    this.module = module
+  }
+
+  public async runner(expectFailure: boolean): Promise<void> {
+    // Begin application review
+    await this.api.sudoBeginApplicantReview(this.openingId, this.module)
+    if (expectFailure) {
+      throw new Error('Successful fixture run while expecting failure')
+    }
+  }
+}
+
+export class FillOpeningFixture implements Fixture {
+  private api: Api
+  private applicationIds: ApplicationId[]
+  private openingId: OpeningId
+  private firstPayoutInterval: BN
+  private payoutInterval: BN
+  private amountPerPayout: BN
+  private module: WorkingGroups
+  private workerIds: WorkerId[] = []
+
+  constructor(
+    api: Api,
+    applicationIds: ApplicationId[],
+    openingId: OpeningId,
+    firstPayoutInterval: BN,
+    payoutInterval: BN,
+    amountPerPayout: BN,
+    module: WorkingGroups
+  ) {
+    this.api = api
+    this.applicationIds = applicationIds
+    this.openingId = openingId
+    this.firstPayoutInterval = firstPayoutInterval
+    this.payoutInterval = payoutInterval
+    this.amountPerPayout = amountPerPayout
+    this.module = module
+  }
+
+  public getWorkerIds(): WorkerId[] {
+    return this.workerIds
+  }
+
+  public async runner(expectFailure: boolean): Promise<void> {
+    const lead = await this.api.getGroupLead(this.module)
+    if (!lead) {
+      throw new Error('No Lead')
+    }
+    const leadAccount = lead.role_account_id.toString()
+    // Fee estimation and transfer
+    const beginReviewFee: BN = this.api.estimateFillOpeningFee(this.module)
+    this.api.treasuryTransferBalance(leadAccount, beginReviewFee)
+
+    // Assert max number of workers is not exceeded
+    const activeWorkersCount: BN = await this.api.getActiveWorkersCount(this.module)
+    const maxWorkersCount: BN = this.api.getMaxWorkersCount(this.module)
+    assert(
+      activeWorkersCount.addn(this.applicationIds.length).lte(maxWorkersCount),
+      `The number of workers ${activeWorkersCount.addn(
+        this.applicationIds.length
+      )} will exceed max workers count ${maxWorkersCount}`
+    )
+
+    // Fill worker opening
+    const now: BN = await this.api.getBestBlock()
+    const result = await this.api.fillOpening(
+      leadAccount,
+      this.openingId,
+      this.applicationIds,
+      this.amountPerPayout,
+      now.add(this.firstPayoutInterval),
+      this.payoutInterval,
+      this.module
+    )
+    const applicationIdToWorkerIdMap: ApplicationIdToWorkerIdMap = this.api.expectOpeningFilledEvent(result.events)
+    this.workerIds = []
+    applicationIdToWorkerIdMap.forEach((workerId) => this.workerIds.push(workerId))
+
+    // Assertions
+    applicationIdToWorkerIdMap.forEach(async (workerId, applicationId) => {
+      const worker: Worker = await this.api.getWorkerById(workerId, this.module)
+      const application: Application = await this.api.getApplicationById(applicationId, this.module)
+      assert(
+        worker.role_account_id.toString() === application.role_account_id.toString(),
+        `Role account ids does not match, worker account: ${worker.role_account_id}, application account ${application.role_account_id}`
+      )
+    })
+
+    if (expectFailure) {
+      throw new Error('Successful fixture run while expecting failure')
+    }
+  }
+}
+
+export class SudoFillLeaderOpeningFixture implements Fixture {
+  private api: Api
+  private applicationId: ApplicationId
+  private openingId: OpeningId
+  private firstPayoutInterval: BN
+  private payoutInterval: BN
+  private amountPerPayout: BN
+  private module: WorkingGroups
+
+  constructor(
+    api: Api,
+    applicationId: ApplicationId,
+    openingId: OpeningId,
+    firstPayoutInterval: BN,
+    payoutInterval: BN,
+    amountPerPayout: BN,
+    module: WorkingGroups
+  ) {
+    this.api = api
+    this.applicationId = applicationId
+    this.openingId = openingId
+    this.firstPayoutInterval = firstPayoutInterval
+    this.payoutInterval = payoutInterval
+    this.amountPerPayout = amountPerPayout
+    this.module = module
+  }
+
+  public async runner(expectFailure: boolean): Promise<void> {
+    // Fill leader opening
+    const now: BN = await this.api.getBestBlock()
+    const result = await this.api.sudoFillOpening(
+      this.openingId,
+      [this.applicationId],
+      this.amountPerPayout,
+      now.add(this.firstPayoutInterval),
+      this.payoutInterval,
+      this.module
+    )
+
+    // Assertions
+    const applicationIdToWorkerIdMap = this.api.expectOpeningFilledEvent(result.events)
+    assert(applicationIdToWorkerIdMap.size === 1)
+    applicationIdToWorkerIdMap.forEach(async (workerId, applicationId) => {
+      const worker: Worker = await this.api.getWorkerById(workerId, this.module)
+      const application: Application = await this.api.getApplicationById(applicationId, this.module)
+      const leadWorkerId: WorkerId = (await this.api.getLeadWorkerId(this.module))!
+      assert(
+        worker.role_account_id.toString() === application.role_account_id.toString(),
+        `Role account ids does not match, leader account: ${worker.role_account_id}, application account ${application.role_account_id}`
+      )
+      assert(
+        leadWorkerId.eq(workerId),
+        `Role account ids does not match, leader account: ${worker.role_account_id}, application account ${application.role_account_id}`
+      )
+    })
+
+    if (expectFailure) {
+      throw new Error('Successful fixture run while expecting failure')
+    }
+  }
+}
+
+export class IncreaseStakeFixture implements Fixture {
+  private api: Api
+  private workerId: WorkerId
+  private module: WorkingGroups
+
+  constructor(api: Api, workerId: WorkerId, module: WorkingGroups) {
+    this.api = api
+    this.workerId = workerId
+    this.module = module
+  }
+
+  public async runner(expectFailure: boolean): Promise<void> {
+    // Fee estimation and transfer
+    const increaseStakeFee: BN = this.api.estimateIncreaseStakeFee(this.module)
+    const stakeIncrement: BN = new BN(1)
+    const worker = await this.api.getWorkerById(this.workerId, this.module)
+    const workerRoleAccount = worker.role_account_id.toString()
+    this.api.treasuryTransferBalance(workerRoleAccount, increaseStakeFee.add(stakeIncrement))
+
+    // Increase worker stake
+    const increasedWorkerStake: BN = (await this.api.getWorkerStakeAmount(this.workerId, this.module)).add(
+      stakeIncrement
+    )
+    await this.api.increaseStake(workerRoleAccount, this.workerId, stakeIncrement, this.module)
+    const newWorkerStake: BN = await this.api.getWorkerStakeAmount(this.workerId, this.module)
+    assert(
+      increasedWorkerStake.eq(newWorkerStake),
+      `Unexpected worker stake ${newWorkerStake}, expected ${increasedWorkerStake}`
+    )
+    if (expectFailure) {
+      throw new Error('Successful fixture run while expecting failure')
+    }
+  }
+}
+
+export class UpdateRewardAccountFixture implements Fixture {
+  public api: Api
+  public workerId: WorkerId
+  public module: WorkingGroups
+
+  constructor(api: Api, workerId: WorkerId, module: WorkingGroups) {
+    this.api = api
+    this.workerId = workerId
+    this.module = module
+  }
+
+  public async runner(expectFailure: boolean): Promise<void> {
+    const worker = await this.api.getWorkerById(this.workerId, this.module)
+    const workerRoleAccount = worker.role_account_id.toString()
+    // Fee estimation and transfer
+    const updateRewardAccountFee: BN = this.api.estimateUpdateRewardAccountFee(workerRoleAccount, this.module)
+    this.api.treasuryTransferBalance(workerRoleAccount, updateRewardAccountFee)
+
+    // Update reward account
+    const createdAccount: KeyringPair = this.api.createKeyPairs(1)[0]
+    await this.api.updateRewardAccount(workerRoleAccount, this.workerId, createdAccount.address, this.module)
+    const newRewardAccount: string = await this.api.getWorkerRewardAccount(this.workerId, this.module)
+    assert(
+      newRewardAccount === createdAccount.address,
+      `Unexpected role account ${newRewardAccount}, expected ${createdAccount.address}`
+    )
+    if (expectFailure) {
+      throw new Error('Successful fixture run while expecting failure')
+    }
+  }
+}
+
+export class UpdateRoleAccountFixture implements Fixture {
+  private api: Api
+  private workerId: WorkerId
+  private module: WorkingGroups
+
+  constructor(api: Api, workerId: WorkerId, module: WorkingGroups) {
+    this.api = api
+    this.workerId = workerId
+    this.module = module
+  }
+
+  public async runner(expectFailure: boolean): Promise<void> {
+    const worker = await this.api.getWorkerById(this.workerId, this.module)
+    const workerRoleAccount = worker.role_account_id.toString()
+    // Fee estimation and transfer
+    const updateRoleAccountFee: BN = this.api.estimateUpdateRoleAccountFee(workerRoleAccount, this.module)
+
+    this.api.treasuryTransferBalance(workerRoleAccount, updateRoleAccountFee)
+
+    // Update role account
+    const createdAccount: KeyringPair = this.api.createKeyPairs(1)[0]
+    await this.api.updateRoleAccount(workerRoleAccount, this.workerId, createdAccount.address, this.module)
+    const newRoleAccount: string = (await this.api.getWorkerById(this.workerId, this.module)).role_account_id.toString()
+    assert(
+      newRoleAccount === createdAccount.address,
+      `Unexpected role account ${newRoleAccount}, expected ${createdAccount.address}`
+    )
+
+    if (expectFailure) {
+      throw new Error('Successful fixture run while expecting failure')
+    }
+  }
+}
+
+export class TerminateApplicationsFixture implements Fixture {
+  private api: Api
+  private applicationIds: ApplicationId[]
+  private module: WorkingGroups
+
+  constructor(api: Api, applicationIds: ApplicationId[], module: WorkingGroups) {
+    this.api = api
+    this.applicationIds = applicationIds
+    this.module = module
+  }
+
+  public async runner(expectFailure: boolean): Promise<void> {
+    const lead = await this.api.getGroupLead(this.module)
+    if (!lead) {
+      throw new Error('No Lead')
+    }
+    const leadAccount = lead.role_account_id.toString()
+
+    // Fee estimation and transfer
+    const terminateApplicationFee: BN = this.api.estimateTerminateApplicationFee(this.module)
+    this.api.treasuryTransferBalance(leadAccount, terminateApplicationFee.muln(this.applicationIds.length))
+
+    // Terminate worker applications
+    await this.api.batchTerminateApplication(leadAccount, this.applicationIds, this.module)
+
+    if (expectFailure) {
+      throw new Error('Successful fixture run while expecting failure')
+    }
+  }
+}
+
+export class DecreaseStakeFixture implements Fixture {
+  private api: Api
+  private workerId: WorkerId
+  private module: WorkingGroups
+
+  constructor(api: Api, workerId: WorkerId, module: WorkingGroups) {
+    this.api = api
+    this.workerId = workerId
+    this.module = module
+  }
+
+  public async runner(expectFailure: boolean): Promise<void> {
+    const lead = await this.api.getGroupLead(this.module)
+    if (!lead) {
+      throw new Error('No Lead')
+    }
+    const leadAccount = lead.role_account_id.toString()
+
+    // Fee estimation and transfer
+    const decreaseStakeFee: BN = this.api.estimateDecreaseStakeFee(this.module)
+    this.api.treasuryTransferBalance(leadAccount, decreaseStakeFee)
+    const workerStakeDecrement: BN = new BN(1)
+
+    // Worker stake decrement
+    const decreasedWorkerStake: BN = (await this.api.getWorkerStakeAmount(this.workerId, this.module)).sub(
+      workerStakeDecrement
+    )
+    await this.api.decreaseStake(leadAccount, this.workerId, workerStakeDecrement, this.module, expectFailure)
+    const newWorkerStake: BN = await this.api.getWorkerStakeAmount(this.workerId, this.module)
+
+    // Assertions
+    if (!expectFailure) {
+      assert(
+        decreasedWorkerStake.eq(newWorkerStake),
+        `Unexpected worker stake ${newWorkerStake}, expected ${decreasedWorkerStake}`
+      )
+    }
+  }
+}
+
+export class SlashFixture implements Fixture {
+  private api: Api
+  private workerId: WorkerId
+  private module: WorkingGroups
+
+  constructor(api: Api, workerId: WorkerId, module: WorkingGroups) {
+    this.api = api
+    this.workerId = workerId
+    this.module = module
+  }
+
+  public async runner(expectFailure: boolean): Promise<void> {
+    const lead = await this.api.getGroupLead(this.module)
+    if (!lead) {
+      throw new Error('No Lead')
+    }
+    const leadAccount = lead.role_account_id.toString()
+
+    // Fee estimation and transfer
+    const slashStakeFee: BN = this.api.estimateSlashStakeFee(this.module)
+    this.api.treasuryTransferBalance(leadAccount, slashStakeFee)
+    const slashAmount: BN = new BN(1)
+
+    // Slash worker
+    const slashedStake: BN = (await this.api.getWorkerStakeAmount(this.workerId, this.module)).sub(slashAmount)
+    await this.api.slashStake(leadAccount, this.workerId, slashAmount, this.module, expectFailure)
+    const newStake: BN = await this.api.getWorkerStakeAmount(this.workerId, this.module)
+
+    // Assertions
+    assert(slashedStake.eq(newStake), `Unexpected worker stake ${newStake}, expected ${slashedStake}`)
+  }
+}
+
+export class TerminateRoleFixture implements Fixture {
+  private api: Api
+  private workerId: WorkerId
+  private module: WorkingGroups
+
+  constructor(api: Api, workerId: WorkerId, module: WorkingGroups) {
+    this.api = api
+    this.workerId = workerId
+    this.module = module
+  }
+
+  public async runner(expectFailure: boolean): Promise<void> {
+    const lead = await this.api.getGroupLead(this.module)
+    if (!lead) {
+      throw new Error('No Lead')
+    }
+    const leadAccount = lead.role_account_id.toString()
+
+    // Fee estimation and transfer
+    const terminateRoleFee: BN = this.api.estimateTerminateRoleFee(this.module)
+    this.api.treasuryTransferBalance(leadAccount, terminateRoleFee)
+
+    // Terminate worker role
+    await this.api.terminateRole(leadAccount, this.workerId, uuid().substring(0, 8), this.module, expectFailure)
+
+    // Assertions
+    const isWorker: boolean = await this.api.isWorker(this.workerId, this.module)
+    assert(!isWorker, `Worker ${this.workerId} is not terminated`)
+  }
+}
+
+export class LeaveRoleFixture implements Fixture {
+  private api: Api
+  private workerIds: WorkerId[]
+  private module: WorkingGroups
+
+  constructor(api: Api, workerIds: WorkerId[], module: WorkingGroups) {
+    this.api = api
+    this.workerIds = workerIds
+    this.module = module
+  }
+
+  public async runner(expectFailure: boolean): Promise<void> {
+    const roleAccounts = await this.api.getWorkerRoleAccounts(this.workerIds, this.module)
+    // Fee estimation and transfer
+    const leaveRoleFee: BN = this.api.estimateLeaveRoleFee(this.module)
+    this.api.treasuryTransferBalanceToAccounts(roleAccounts, leaveRoleFee)
+
+    await this.api.batchLeaveRole(this.workerIds, uuid().substring(0, 8), expectFailure, this.module)
+
+    // Assertions
+    this.workerIds.forEach(async (workerId) => {
+      const isWorker: boolean = await this.api.isWorker(workerId, this.module)
+      assert(!isWorker, `Worker${workerId} is not terminated`)
+    })
+  }
+}
+
+export class AwaitPayoutFixture implements Fixture {
+  private api: Api
+  private workerId: WorkerId
+  private module: WorkingGroups
+
+  constructor(api: Api, workerId: WorkerId, module: WorkingGroups) {
+    this.api = api
+    this.workerId = workerId
+    this.module = module
+  }
+
+  public async runner(expectFailure: boolean): Promise<void> {
+    const worker: Worker = await this.api.getWorkerById(this.workerId, this.module)
+    const reward: RewardRelationship = await this.api.getRewardRelationship(worker.reward_relationship.unwrap())
+    const now: BN = await this.api.getBestBlock()
+    const nextPaymentBlock: BN = new BN(reward.getField('next_payment_at_block').toString())
+    const payoutInterval: BN = new BN(reward.getField('payout_interval').toString())
+    const amountPerPayout: BN = new BN(reward.getField('amount_per_payout').toString())
+
+    assert(now.lt(nextPaymentBlock), `Payout already happened in block ${nextPaymentBlock} now ${now}`)
+    const balance: BN = await this.api.getBalance(reward.account.toString())
+
+    const firstPayoutWaitingPeriod: BN = nextPaymentBlock.sub(now).addn(1)
+    await Utils.wait(this.api.getBlockDuration().mul(firstPayoutWaitingPeriod).toNumber())
+
+    const balanceAfterFirstPayout: BN = await this.api.getBalance(reward.account.toString())
+    const expectedBalanceFirst: BN = balance.add(amountPerPayout)
+    assert(
+      balanceAfterFirstPayout.eq(expectedBalanceFirst),
+      `Unexpected balance, expected ${expectedBalanceFirst} got ${balanceAfterFirstPayout}`
+    )
+
+    const secondPayoutWaitingPeriod: BN = payoutInterval.addn(1)
+    await Utils.wait(this.api.getBlockDuration().mul(secondPayoutWaitingPeriod).toNumber())
+
+    const balanceAfterSecondPayout: BN = await this.api.getBalance(reward.account.toString())
+    const expectedBalanceSecond: BN = expectedBalanceFirst.add(amountPerPayout)
+    assert(
+      balanceAfterSecondPayout.eq(expectedBalanceSecond),
+      `Unexpected balance, expected ${expectedBalanceSecond} got ${balanceAfterSecondPayout}`
+    )
+    if (expectFailure) {
+      throw new Error('Successful fixture run while expecting failure')
+    }
+  }
+}

+ 36 - 0
tests/network-tests/src/flows/membership/creatingMemberships.ts

@@ -0,0 +1,36 @@
+import { Api } from '../../Api'
+import {
+  BuyMembershipHappyCaseFixture,
+  BuyMembershipWithInsufficienFundsFixture,
+} from '../../fixtures/membershipModule'
+import { PaidTermId } from '@joystream/types/members'
+import BN from 'bn.js'
+import Debugger from 'debug'
+
+export default async function membershipCreation(api: Api, env: NodeJS.ProcessEnv) {
+  const debug = Debugger('flow:memberships')
+  debug('started')
+
+  const N: number = +env.MEMBERSHIP_CREATION_N!
+  const nAccounts = api.createKeyPairs(N).map((key) => key.address)
+  const aAccount = api.createKeyPairs(1)[0].address
+  const paidTerms: PaidTermId = api.createPaidTermId(new BN(+env.MEMBERSHIP_PAID_TERMS!))
+
+  const happyCaseFixture = new BuyMembershipHappyCaseFixture(api, nAccounts, paidTerms)
+  // Buy membeship is accepted with sufficient funds
+  await happyCaseFixture.runner(false)
+
+  const insufficientFundsFixture: BuyMembershipWithInsufficienFundsFixture = new BuyMembershipWithInsufficienFundsFixture(
+    api,
+    aAccount,
+    paidTerms
+  )
+  // Account A can not buy the membership with insufficient funds
+  await insufficientFundsFixture.runner(false)
+
+  const buyMembershipAfterAccountTopUp = new BuyMembershipHappyCaseFixture(api, [aAccount], paidTerms)
+
+  // Account A was able to buy the membership with sufficient funds
+  await buyMembershipAfterAccountTopUp.runner(false)
+  debug('finished')
+}

+ 46 - 0
tests/network-tests/src/flows/proposals/councilSetup.ts

@@ -0,0 +1,46 @@
+import BN from 'bn.js'
+import { PaidTermId } from '@joystream/types/members'
+import { Api } from '../../Api'
+import { CouncilElectionHappyCaseFixture } from '../../fixtures/councilElectionHappyCase'
+import { BuyMembershipHappyCaseFixture } from '../../fixtures/membershipModule'
+import Debugger from 'debug'
+import { assert } from 'chai'
+
+const debug = Debugger('flow:councilSetup')
+
+export default async function councilSetup(api: Api, env: NodeJS.ProcessEnv) {
+  // Skip creating council if already elected
+  if ((await api.getCouncil()).length) {
+    debug('Skipping Council Setup, Council already elected')
+    return
+  }
+
+  const numberOfApplicants = (await api.getCouncilSize()).toNumber() * 2
+  const applicants = api.createKeyPairs(numberOfApplicants).map((key) => key.address)
+  const voters = api.createKeyPairs(5).map((key) => key.address)
+
+  const paidTerms: PaidTermId = api.createPaidTermId(new BN(+env.MEMBERSHIP_PAID_TERMS!))
+  const K: number = +env.COUNCIL_ELECTION_K!
+  const greaterStake: BN = new BN(+env.COUNCIL_STAKE_GREATER_AMOUNT!)
+  const lesserStake: BN = new BN(+env.COUNCIL_STAKE_LESSER_AMOUNT!)
+
+  const createMembersFixture = new BuyMembershipHappyCaseFixture(api, [...voters, ...applicants], paidTerms)
+  await createMembersFixture.runner(false)
+
+  // The fixture moves manually with sudo the election stages, so proper processing
+  // that normally occurs during stage transitions does not happen. This can lead to a council
+  // that is smaller than the council size if not enough members apply.
+  const councilElectionHappyCaseFixture = new CouncilElectionHappyCaseFixture(
+    api,
+    voters, // should be member ids
+    applicants, // should be member ids
+    K,
+    greaterStake,
+    lesserStake
+  )
+
+  await councilElectionHappyCaseFixture.runner(false)
+
+  // Elected council
+  assert((await api.getCouncil()).length)
+}

+ 15 - 0
tests/network-tests/src/flows/proposals/electionParametersProposal.ts

@@ -0,0 +1,15 @@
+import { Api } from '../../Api'
+import { ElectionParametersProposalFixture } from '../../fixtures/proposalsModule'
+import { assert } from 'chai'
+
+// Election parameters proposal scenario
+export default async function electionParametersProposal(api: Api, env: NodeJS.ProcessEnv) {
+  // Pre-Conditions: some members and an elected council
+  const council = await api.getCouncil()
+  assert(council.length)
+
+  const proposer = council[0].member.toString()
+
+  const electionParametersProposalFixture = new ElectionParametersProposalFixture(api, proposer)
+  await electionParametersProposalFixture.runner(false)
+}

+ 179 - 0
tests/network-tests/src/flows/proposals/manageLeaderRole.ts

@@ -0,0 +1,179 @@
+import BN from 'bn.js'
+import { Api, WorkingGroups } from '../../Api'
+import { BuyMembershipHappyCaseFixture } from '../../fixtures/membershipModule'
+import {
+  BeginWorkingGroupLeaderApplicationReviewFixture,
+  CreateWorkingGroupLeaderOpeningFixture,
+  DecreaseLeaderStakeProposalFixture,
+  FillLeaderOpeningProposalFixture,
+  SetLeaderRewardProposalFixture,
+  SlashLeaderProposalFixture,
+  TerminateLeaderRoleProposalFixture,
+  VoteForProposalFixture,
+} from '../../fixtures/proposalsModule'
+import { ApplyForOpeningFixture } from '../../fixtures/workingGroupModule'
+import { PaidTermId } from '@joystream/types/members'
+import { OpeningId } from '@joystream/types/hiring'
+import { ProposalId } from '@joystream/types/proposals'
+import { assert } from 'chai'
+
+export default async function manageLeaderRole(api: Api, env: NodeJS.ProcessEnv, group: WorkingGroups) {
+  const leaderAccount = api.createKeyPairs(1)[0].address
+
+  const paidTerms: PaidTermId = api.createPaidTermId(new BN(+env.MEMBERSHIP_PAID_TERMS!))
+  const applicationStake: BN = new BN(env.WORKING_GROUP_APPLICATION_STAKE!)
+  const roleStake: BN = new BN(env.WORKING_GROUP_ROLE_STAKE!)
+  const firstRewardInterval: BN = new BN(env.LONG_REWARD_INTERVAL!)
+  const rewardInterval: BN = new BN(env.LONG_REWARD_INTERVAL!)
+  const payoutAmount: BN = new BN(env.PAYOUT_AMOUNT!)
+  const alteredPayoutAmount: BN = new BN(env.ALTERED_PAYOUT_AMOUNT!)
+  const stakeDecrement: BN = new BN(env.STAKE_DECREMENT!)
+  const slashAmount: BN = new BN(env.SLASH_AMOUNT!)
+
+  // Pre-conditions - members and council
+  // No Hired Lead
+  const existingLead = await api.getGroupLead(group)
+  assert(!existingLead)
+
+  const council = await api.getCouncil()
+  assert(council.length)
+  const proposer = council[0].member.toString()
+
+  const leaderMembershipFixture: BuyMembershipHappyCaseFixture = new BuyMembershipHappyCaseFixture(
+    api,
+    [leaderAccount],
+    paidTerms
+  )
+  // Buy membership for lead
+  await leaderMembershipFixture.runner(false)
+
+  const createWorkingGroupLeaderOpeningFixture: CreateWorkingGroupLeaderOpeningFixture = new CreateWorkingGroupLeaderOpeningFixture(
+    api,
+    proposer,
+    applicationStake,
+    roleStake,
+    api.getWorkingGroupString(group)
+  )
+  // Propose create leader opening
+  await createWorkingGroupLeaderOpeningFixture.runner(false)
+
+  // Approve add opening proposal
+  const voteForCreateOpeningProposalFixture = new VoteForProposalFixture(
+    api,
+    createWorkingGroupLeaderOpeningFixture.getCreatedProposalId() as OpeningId
+  )
+
+  await voteForCreateOpeningProposalFixture.runner(false)
+  const openingId = api.expectOpeningAddedEvent(voteForCreateOpeningProposalFixture.getEvents())
+
+  const applyForLeaderOpeningFixture = new ApplyForOpeningFixture(
+    api,
+    [leaderAccount],
+    applicationStake,
+    roleStake,
+    openingId,
+    group
+  )
+  await applyForLeaderOpeningFixture.runner(false)
+  const applicationId = applyForLeaderOpeningFixture.getApplicationIds()[0]
+
+  const beginWorkingGroupLeaderApplicationReviewFixture = new BeginWorkingGroupLeaderApplicationReviewFixture(
+    api,
+    proposer,
+    openingId,
+    api.getWorkingGroupString(group)
+  )
+  // Propose begin leader application review
+  await beginWorkingGroupLeaderApplicationReviewFixture.runner(false)
+
+  const voteForBeginReviewProposal = new VoteForProposalFixture(
+    api,
+    beginWorkingGroupLeaderApplicationReviewFixture.getCreatedProposalId() as ProposalId
+  )
+  await voteForBeginReviewProposal.runner(false)
+
+  const fillLeaderOpeningProposalFixture = new FillLeaderOpeningProposalFixture(
+    api,
+    proposer,
+    applicationId,
+    firstRewardInterval,
+    rewardInterval,
+    payoutAmount,
+    openingId,
+    group
+  )
+  // Propose fill leader opening
+  await fillLeaderOpeningProposalFixture.runner(false)
+
+  const voteForFillLeaderProposalFixture = new VoteForProposalFixture(
+    api,
+    fillLeaderOpeningProposalFixture.getCreatedProposalId() as ProposalId
+  )
+  // Approve fill leader opening
+  await voteForFillLeaderProposalFixture.runner(false)
+
+  const hiredLead = await api.getGroupLead(group)
+  assert(hiredLead)
+
+  const setLeaderRewardProposalFixture = new SetLeaderRewardProposalFixture(api, proposer, alteredPayoutAmount, group)
+  // Propose leader reward
+  await setLeaderRewardProposalFixture.runner(false)
+
+  const voteForeLeaderRewardFixture = new VoteForProposalFixture(
+    api,
+    setLeaderRewardProposalFixture.getCreatedProposalId() as ProposalId
+  )
+
+  // Approve new leader reward
+  await voteForeLeaderRewardFixture.runner(false)
+
+  const leadId = await api.getLeadWorkerId(group)
+  // This check is prone to failure if more than one worker's reward amount was updated
+  const workerId = api.expectWorkerRewardAmountUpdatedEvent(voteForeLeaderRewardFixture.getEvents())
+  assert(leadId!.eq(workerId))
+  const rewardRelationship = await api.getWorkerRewardRelationship(leadId!, group)
+  assert(rewardRelationship.amount_per_payout.eq(alteredPayoutAmount))
+
+  const decreaseLeaderStakeProposalFixture = new DecreaseLeaderStakeProposalFixture(
+    api,
+    proposer,
+    stakeDecrement,
+    group
+  )
+
+  // Propose decrease stake
+  await decreaseLeaderStakeProposalFixture.runner(false)
+
+  let newStake: BN = applicationStake.sub(stakeDecrement)
+  // Approve decreased leader stake
+  const voteForDecreaseStakeProposal = new VoteForProposalFixture(
+    api,
+    decreaseLeaderStakeProposalFixture.getCreatedProposalId() as ProposalId
+  )
+  await voteForDecreaseStakeProposal.runner(false)
+
+  const slashLeaderProposalFixture = new SlashLeaderProposalFixture(api, proposer, slashAmount, group)
+  // Propose leader slash
+  await slashLeaderProposalFixture.runner(false)
+
+  // Approve leader slash
+  newStake = newStake.sub(slashAmount)
+  const voteForSlashProposalFixture = new VoteForProposalFixture(
+    api,
+    slashLeaderProposalFixture.getCreatedProposalId() as ProposalId
+  )
+  await voteForSlashProposalFixture.runner(false)
+
+  const terminateLeaderRoleProposalFixture = new TerminateLeaderRoleProposalFixture(api, proposer, false, group)
+  // Propose terminate leader role
+  await terminateLeaderRoleProposalFixture.runner(false)
+
+  const voteForLeaderRoleTerminationFixture = new VoteForProposalFixture(
+    api,
+    terminateLeaderRoleProposalFixture.getCreatedProposalId() as ProposalId
+  )
+  await voteForLeaderRoleTerminationFixture.runner(false)
+
+  const maybeLead = await api.getGroupLead(group)
+  assert(!maybeLead)
+}

+ 20 - 0
tests/network-tests/src/flows/proposals/spendingProposal.ts

@@ -0,0 +1,20 @@
+import BN from 'bn.js'
+import { Api } from '../../Api'
+import { SpendingProposalFixture } from '../../fixtures/proposalsModule'
+import { assert } from 'chai'
+
+export default async function spendingProposal(api: Api, env: NodeJS.ProcessEnv) {
+  const spendingBalance: BN = new BN(+env.SPENDING_BALANCE!)
+  const mintCapacity: BN = new BN(+env.COUNCIL_MINTING_CAPACITY!)
+
+  // Pre-conditions, members and council
+  const council = await api.getCouncil()
+  assert(council.length)
+
+  const proposer = council[0].member.toString()
+
+  const spendingProposalFixture = new SpendingProposalFixture(api, proposer, spendingBalance, mintCapacity)
+
+  // Spending proposal test
+  await spendingProposalFixture.runner(false)
+}

+ 14 - 0
tests/network-tests/src/flows/proposals/textProposal.ts

@@ -0,0 +1,14 @@
+import { Api } from '../../Api'
+import { TextProposalFixture } from '../../fixtures/proposalsModule'
+import { assert } from 'chai'
+
+export default async function textProposal(api: Api, env: NodeJS.ProcessEnv) {
+  // Pre-conditions: members and council
+  const council = await api.getCouncil()
+  assert(council.length)
+
+  const proposer = council[0].member.toString()
+
+  const textProposalFixture: TextProposalFixture = new TextProposalFixture(api, proposer)
+  await textProposalFixture.runner(false)
+}

+ 28 - 0
tests/network-tests/src/flows/proposals/updateRuntime.ts

@@ -0,0 +1,28 @@
+import BN from 'bn.js'
+import { Api } from '../../Api'
+import { BuyMembershipHappyCaseFixture } from '../../fixtures/membershipModule'
+import { UpdateRuntimeFixture } from '../../fixtures/proposalsModule'
+import { PaidTermId } from '@joystream/types/members'
+import { assert } from 'chai'
+
+export default async function updateRuntime(api: Api, env: NodeJS.ProcessEnv) {
+  const paidTerms: PaidTermId = api.createPaidTermId(new BN(+env.MEMBERSHIP_PAID_TERMS!))
+  const runtimePath: string = env.RUNTIME_WASM_PATH!
+
+  // Pre-conditions: members and council
+  const council = await api.getCouncil()
+  assert(council.length)
+
+  const proposer = council[0].member.toString()
+
+  const updateRuntimeFixture: UpdateRuntimeFixture = new UpdateRuntimeFixture(api, proposer, runtimePath)
+  await updateRuntimeFixture.runner(false)
+
+  // Some tests after runtime update
+  const createMembershipsFixture = new BuyMembershipHappyCaseFixture(
+    api,
+    api.createKeyPairs(1).map((key) => key.address),
+    paidTerms
+  )
+  await createMembershipsFixture.runner(false)
+}

+ 21 - 0
tests/network-tests/src/flows/proposals/validatorCountProposal.ts

@@ -0,0 +1,21 @@
+import BN from 'bn.js'
+import { Api } from '../../Api'
+import { ValidatorCountProposalFixture } from '../../fixtures/proposalsModule'
+import { assert } from 'chai'
+
+export default async function validatorCount(api: Api, env: NodeJS.ProcessEnv) {
+  // Pre-conditions: members and council
+  const council = await api.getCouncil()
+  assert(council.length)
+
+  const proposer = council[0].member.toString()
+
+  const validatorCountIncrement: BN = new BN(+env.VALIDATOR_COUNT_INCREMENT!)
+
+  const validatorCountProposalFixture: ValidatorCountProposalFixture = new ValidatorCountProposalFixture(
+    api,
+    proposer,
+    validatorCountIncrement
+  )
+  await validatorCountProposalFixture.runner(false)
+}

+ 32 - 0
tests/network-tests/src/flows/proposals/workingGroupMintCapacityProposal.ts

@@ -0,0 +1,32 @@
+import BN from 'bn.js'
+import { Api, WorkingGroups } from '../../Api'
+import { VoteForProposalFixture, WorkingGroupMintCapacityProposalFixture } from '../../fixtures/proposalsModule'
+import { ProposalId } from '@joystream/types/proposals'
+import { assert } from 'chai'
+
+export default async function workingGroupMintCapactiy(api: Api, env: NodeJS.ProcessEnv, group: WorkingGroups) {
+  const mintCapacityIncrement: BN = new BN(env.MINT_CAPACITY_INCREMENT!)
+
+  // Pre-conditions: members and council
+  const council = await api.getCouncil()
+  assert(council.length)
+
+  const proposer = council[0].member.toString()
+  const newMintCapacity: BN = (await api.getWorkingGroupMintCapacity(group)).add(mintCapacityIncrement)
+  const workingGroupMintCapacityProposalFixture: WorkingGroupMintCapacityProposalFixture = new WorkingGroupMintCapacityProposalFixture(
+    api,
+    proposer,
+    newMintCapacity,
+    group
+  )
+  // Propose mint capacity
+  await workingGroupMintCapacityProposalFixture.runner(false)
+
+  const voteForProposalFixture: VoteForProposalFixture = new VoteForProposalFixture(
+    api,
+    workingGroupMintCapacityProposalFixture.getCreatedProposalId() as ProposalId
+  )
+
+  // Approve mint capacity
+  await voteForProposalFixture.runner(false)
+}

+ 45 - 0
tests/network-tests/src/flows/workingGroup/atLeastValueBug.ts

@@ -0,0 +1,45 @@
+import { Api, WorkingGroups } from '../../Api'
+import { AddWorkerOpeningFixture } from '../../fixtures/workingGroupModule'
+import BN from 'bn.js'
+import { assert } from 'chai'
+import Debugger from 'debug'
+const debug = Debugger('flow:atLeastValueBug')
+
+// Zero at least value bug scenario
+export default async function zeroAtLeastValueBug(api: Api, env: NodeJS.ProcessEnv) {
+  debug('Started')
+  const applicationStake: BN = new BN(env.WORKING_GROUP_APPLICATION_STAKE!)
+  const roleStake: BN = new BN(env.WORKING_GROUP_ROLE_STAKE!)
+  const unstakingPeriod: BN = new BN(env.STORAGE_WORKING_GROUP_UNSTAKING_PERIOD!)
+  const openingActivationDelay: BN = new BN(0)
+
+  // Pre-conditions
+  // A hired lead
+  const lead = await api.getGroupLead(WorkingGroups.StorageWorkingGroup)
+  assert(lead)
+
+  const addWorkerOpeningWithoutStakeFixture = new AddWorkerOpeningFixture(
+    api,
+    new BN(0),
+    new BN(0),
+    openingActivationDelay,
+    unstakingPeriod,
+    WorkingGroups.StorageWorkingGroup
+  )
+  // Add worker opening with 0 stake, expect failure
+  await addWorkerOpeningWithoutStakeFixture.runner(true)
+
+  const addWorkerOpeningWithoutUnstakingPeriodFixture = new AddWorkerOpeningFixture(
+    api,
+    applicationStake,
+    roleStake,
+    openingActivationDelay,
+    new BN(0),
+    WorkingGroups.StorageWorkingGroup
+  )
+  // Add worker opening with 0 unstaking period, expect failure
+  await addWorkerOpeningWithoutUnstakingPeriodFixture.runner(true)
+
+  // TODO: close openings
+  debug('Passed')
+}

+ 40 - 0
tests/network-tests/src/flows/workingGroup/leaderSetup.ts

@@ -0,0 +1,40 @@
+import { Api, WorkingGroups } from '../../Api'
+import BN from 'bn.js'
+import { PaidTermId } from '@joystream/types/members'
+import { SudoHireLeadFixture } from '../../fixtures/sudoHireLead'
+import { assert } from 'chai'
+
+// Worker application happy case scenario
+export default async function leaderSetup(api: Api, env: NodeJS.ProcessEnv, group: WorkingGroups) {
+  const lead = await api.getGroupLead(group)
+  if (lead) {
+    return
+  }
+
+  const leadKeyPair = api.createKeyPairs(1)[0]
+  const paidTerms: PaidTermId = api.createPaidTermId(new BN(+env.MEMBERSHIP_PAID_TERMS!))
+  const applicationStake: BN = new BN(env.WORKING_GROUP_APPLICATION_STAKE!)
+  const roleStake: BN = new BN(env.WORKING_GROUP_ROLE_STAKE!)
+  const firstRewardInterval: BN = new BN(env.LONG_REWARD_INTERVAL!)
+  const rewardInterval: BN = new BN(env.LONG_REWARD_INTERVAL!)
+  const payoutAmount: BN = new BN(env.PAYOUT_AMOUNT!)
+  const openingActivationDelay: BN = new BN(0)
+
+  const leaderHiringHappyCaseFixture = new SudoHireLeadFixture(
+    api,
+    leadKeyPair.address,
+    paidTerms,
+    applicationStake,
+    roleStake,
+    openingActivationDelay,
+    rewardInterval,
+    firstRewardInterval,
+    payoutAmount,
+    group
+  )
+  await leaderHiringHappyCaseFixture.runner(false)
+
+  const hiredLead = await api.getGroupLead(group)
+  assert(hiredLead, `${group} group Lead was not hired!`)
+  assert(hiredLead!.role_account_id.eq(leadKeyPair.address))
+}

+ 94 - 0
tests/network-tests/src/flows/workingGroup/manageWorkerAsLead.ts

@@ -0,0 +1,94 @@
+import { Api, WorkingGroups } from '../../Api'
+import {
+  ApplyForOpeningFixture,
+  AddWorkerOpeningFixture,
+  BeginApplicationReviewFixture,
+  FillOpeningFixture,
+  DecreaseStakeFixture,
+  SlashFixture,
+  TerminateRoleFixture,
+} from '../../fixtures/workingGroupModule'
+import { BuyMembershipHappyCaseFixture } from '../../fixtures/membershipModule'
+import BN from 'bn.js'
+import { OpeningId } from '@joystream/types/hiring'
+import { assert } from 'chai'
+import Debugger from 'debug'
+
+// Manage worker as lead scenario
+export default async function manageWorker(api: Api, env: NodeJS.ProcessEnv, group: WorkingGroups) {
+  const debug = Debugger(`manageWorker:${group}`)
+  const applicationStake: BN = new BN(env.WORKING_GROUP_APPLICATION_STAKE!)
+  const roleStake: BN = new BN(env.WORKING_GROUP_ROLE_STAKE!)
+  const firstRewardInterval: BN = new BN(env.LONG_REWARD_INTERVAL!)
+  const rewardInterval: BN = new BN(env.LONG_REWARD_INTERVAL!)
+  const payoutAmount: BN = new BN(env.PAYOUT_AMOUNT!)
+  const unstakingPeriod: BN = new BN(env.STORAGE_WORKING_GROUP_UNSTAKING_PERIOD!)
+  const openingActivationDelay: BN = new BN(0)
+  const paidTerms = api.createPaidTermId(new BN(+env.MEMBERSHIP_PAID_TERMS!))
+
+  const lead = await api.getGroupLead(group)
+  assert(lead)
+
+  const applicants = api.createKeyPairs(5).map((key) => key.address)
+  const memberSetFixture = new BuyMembershipHappyCaseFixture(api, applicants, paidTerms)
+  await memberSetFixture.runner(false)
+
+  const addWorkerOpeningFixture: AddWorkerOpeningFixture = new AddWorkerOpeningFixture(
+    api,
+    applicationStake,
+    roleStake,
+    openingActivationDelay,
+    unstakingPeriod,
+    group
+  )
+  // Add worker opening
+  await addWorkerOpeningFixture.runner(false)
+
+  // First apply for worker opening
+  const applyForWorkerOpeningFixture = new ApplyForOpeningFixture(
+    api,
+    applicants,
+    applicationStake,
+    roleStake,
+    addWorkerOpeningFixture.getCreatedOpeningId() as OpeningId,
+    group
+  )
+  await applyForWorkerOpeningFixture.runner(false)
+
+  const applicationIdsToHire = applyForWorkerOpeningFixture.getApplicationIds().slice(0, 2)
+
+  // Begin application review
+  const beginApplicationReviewFixture = new BeginApplicationReviewFixture(
+    api,
+    addWorkerOpeningFixture.getCreatedOpeningId() as OpeningId,
+    group
+  )
+  await beginApplicationReviewFixture.runner(false)
+
+  // Fill worker opening
+  const fillOpeningFixture = new FillOpeningFixture(
+    api,
+    applicationIdsToHire,
+    addWorkerOpeningFixture.getCreatedOpeningId() as OpeningId,
+    firstRewardInterval,
+    rewardInterval,
+    payoutAmount,
+    group
+  )
+  await fillOpeningFixture.runner(false)
+
+  const firstWorkerId = fillOpeningFixture.getWorkerIds()[0]
+
+  const decreaseStakeFixture = new DecreaseStakeFixture(api, firstWorkerId, group)
+  // Decrease worker stake
+  await decreaseStakeFixture.runner(false)
+
+  const slashFixture: SlashFixture = new SlashFixture(api, firstWorkerId, group)
+  // Slash worker
+  await slashFixture.runner(false)
+
+  const terminateRoleFixture = new TerminateRoleFixture(api, firstWorkerId, group)
+
+  // Terminate workers
+  await terminateRoleFixture.runner(false)
+}

+ 90 - 0
tests/network-tests/src/flows/workingGroup/manageWorkerAsWorker.ts

@@ -0,0 +1,90 @@
+import { Api, WorkingGroups } from '../../Api'
+import {
+  AddWorkerOpeningFixture,
+  ApplyForOpeningFixture,
+  BeginApplicationReviewFixture,
+  FillOpeningFixture,
+  IncreaseStakeFixture,
+  UpdateRewardAccountFixture,
+} from '../../fixtures/workingGroupModule'
+import BN from 'bn.js'
+import { OpeningId } from '@joystream/types/hiring'
+import { BuyMembershipHappyCaseFixture } from '../../fixtures/membershipModule'
+import { assert } from 'chai'
+
+// Manage worker as worker
+export default async function manageWorkerAsWorker(api: Api, env: NodeJS.ProcessEnv, group: WorkingGroups) {
+  const applicationStake: BN = new BN(env.WORKING_GROUP_APPLICATION_STAKE!)
+  const roleStake: BN = new BN(env.WORKING_GROUP_ROLE_STAKE!)
+  const firstRewardInterval: BN = new BN(env.LONG_REWARD_INTERVAL!)
+  const rewardInterval: BN = new BN(env.LONG_REWARD_INTERVAL!)
+  const payoutAmount: BN = new BN(env.PAYOUT_AMOUNT!)
+  const unstakingPeriod: BN = new BN(env.STORAGE_WORKING_GROUP_UNSTAKING_PERIOD!)
+  const openingActivationDelay: BN = new BN(0)
+  const paidTerms = api.createPaidTermId(new BN(+env.MEMBERSHIP_PAID_TERMS!))
+
+  const lead = await api.getGroupLead(group)
+  assert(lead)
+
+  const newMembers = api.createKeyPairs(1).map((key) => key.address)
+
+  const memberSetFixture = new BuyMembershipHappyCaseFixture(api, newMembers, paidTerms)
+  // Recreating set of members
+  await memberSetFixture.runner(false)
+  const applicant = newMembers[0]
+
+  const addWorkerOpeningFixture = new AddWorkerOpeningFixture(
+    api,
+    applicationStake,
+    roleStake,
+    openingActivationDelay,
+    unstakingPeriod,
+    group
+  )
+  // Add worker opening
+  await addWorkerOpeningFixture.runner(false)
+
+  // First apply for worker opening
+  const applyForWorkerOpeningFixture = new ApplyForOpeningFixture(
+    api,
+    [applicant],
+    applicationStake,
+    roleStake,
+    addWorkerOpeningFixture.getCreatedOpeningId() as OpeningId,
+    group
+  )
+  await applyForWorkerOpeningFixture.runner(false)
+  const applicationIdToHire = applyForWorkerOpeningFixture.getApplicationIds()[0]
+
+  // Begin application review
+  const beginApplicationReviewFixture = new BeginApplicationReviewFixture(
+    api,
+    addWorkerOpeningFixture.getCreatedOpeningId() as OpeningId,
+    group
+  )
+  await beginApplicationReviewFixture.runner(false)
+
+  // Fill worker opening
+  const fillOpeningFixture = new FillOpeningFixture(
+    api,
+    [applicationIdToHire],
+    addWorkerOpeningFixture.getCreatedOpeningId() as OpeningId,
+    firstRewardInterval,
+    rewardInterval,
+    payoutAmount,
+    group
+  )
+  await fillOpeningFixture.runner(false)
+  const workerId = fillOpeningFixture.getWorkerIds()[0]
+  const increaseStakeFixture: IncreaseStakeFixture = new IncreaseStakeFixture(api, workerId, group)
+  // Increase worker stake
+  await increaseStakeFixture.runner(false)
+
+  const updateRewardAccountFixture: UpdateRewardAccountFixture = new UpdateRewardAccountFixture(api, workerId, group)
+  // Update reward account
+  await updateRewardAccountFixture.runner(false)
+
+  const updateRoleAccountFixture: UpdateRewardAccountFixture = new UpdateRewardAccountFixture(api, workerId, group)
+  // Update role account
+  await updateRoleAccountFixture.runner(false)
+}

+ 98 - 0
tests/network-tests/src/flows/workingGroup/workerPayout.ts

@@ -0,0 +1,98 @@
+import { Api, WorkingGroups } from '../../Api'
+import {
+  AddWorkerOpeningFixture,
+  ApplyForOpeningFixture,
+  AwaitPayoutFixture,
+  BeginApplicationReviewFixture,
+  FillOpeningFixture,
+} from '../../fixtures/workingGroupModule'
+import BN from 'bn.js'
+import { VoteForProposalFixture, WorkingGroupMintCapacityProposalFixture } from '../../fixtures/proposalsModule'
+import { PaidTermId } from '@joystream/types/members'
+import { OpeningId } from '@joystream/types/hiring'
+import { ProposalId } from '@joystream/types/proposals'
+import { BuyMembershipHappyCaseFixture } from '../../fixtures/membershipModule'
+import { assert } from 'chai'
+
+// Worker payout scenario
+export default async function workerPayouts(api: Api, env: NodeJS.ProcessEnv, group: WorkingGroups) {
+  const paidTerms: PaidTermId = api.createPaidTermId(new BN(+env.MEMBERSHIP_PAID_TERMS!))
+  const applicationStake: BN = new BN(env.WORKING_GROUP_APPLICATION_STAKE!)
+  const roleStake: BN = new BN(env.WORKING_GROUP_ROLE_STAKE!)
+  const firstRewardInterval: BN = new BN(env.SHORT_FIRST_REWARD_INTERVAL!)
+  const rewardInterval: BN = new BN(env.SHORT_REWARD_INTERVAL!)
+  const payoutAmount: BN = new BN(env.PAYOUT_AMOUNT!)
+  const unstakingPeriod: BN = new BN(env.STORAGE_WORKING_GROUP_UNSTAKING_PERIOD!)
+  const mintCapacity: BN = new BN(env.STORAGE_WORKING_GROUP_MINTING_CAPACITY!)
+  const openingActivationDelay: BN = new BN(0)
+
+  const lead = await api.getGroupLead(group)
+  const newMembers = api.createKeyPairs(5).map((key) => key.address)
+
+  const memberSetFixture = new BuyMembershipHappyCaseFixture(api, newMembers, paidTerms)
+  // Recreating set of members
+  await memberSetFixture.runner(false)
+
+  const workingGroupMintCapacityProposalFixture = new WorkingGroupMintCapacityProposalFixture(
+    api,
+    newMembers[0],
+    mintCapacity,
+    group
+  )
+  // Propose mint capacity
+  await workingGroupMintCapacityProposalFixture.runner(false)
+
+  // Approve mint capacity
+  const voteForProposalFixture = new VoteForProposalFixture(
+    api,
+    workingGroupMintCapacityProposalFixture.getCreatedProposalId() as ProposalId
+  )
+  await voteForProposalFixture.runner(false)
+
+  const addWorkerOpeningFixture = new AddWorkerOpeningFixture(
+    api,
+    applicationStake,
+    roleStake,
+    openingActivationDelay,
+    unstakingPeriod,
+    group
+  )
+  // Add worker opening
+  await addWorkerOpeningFixture.runner(false)
+
+  // First apply for worker opening
+  const applyForWorkerOpeningFixture = new ApplyForOpeningFixture(
+    api,
+    newMembers,
+    applicationStake,
+    roleStake,
+    addWorkerOpeningFixture.getCreatedOpeningId() as OpeningId,
+    group
+  )
+  await applyForWorkerOpeningFixture.runner(false)
+  const applicationId = applyForWorkerOpeningFixture.getApplicationIds()[0]
+
+  // Begin application review
+  const beginApplicationReviewFixture = new BeginApplicationReviewFixture(
+    api,
+    addWorkerOpeningFixture.getCreatedOpeningId() as OpeningId,
+    group
+  )
+  await beginApplicationReviewFixture.runner(false)
+
+  // Fill worker opening
+  const fillOpeningFixture = new FillOpeningFixture(
+    api,
+    [applicationId],
+    addWorkerOpeningFixture.getCreatedOpeningId() as OpeningId,
+    firstRewardInterval,
+    rewardInterval,
+    payoutAmount,
+    group
+  )
+  await fillOpeningFixture.runner(false)
+  const workerId = fillOpeningFixture.getWorkerIds()[0]
+  const awaitPayoutFixture: AwaitPayoutFixture = new AwaitPayoutFixture(api, workerId, group)
+  // Await worker payout
+  await awaitPayoutFixture.runner(false)
+}

+ 74 - 0
tests/network-tests/src/scenarios/full.ts

@@ -0,0 +1,74 @@
+import { WsProvider } from '@polkadot/api'
+import { Api, WorkingGroups } from '../Api'
+import { config } from 'dotenv'
+import Debugger from 'debug'
+
+import creatingMemberships from '../flows/membership/creatingMemberships'
+import councilSetup from '../flows/proposals/councilSetup'
+import leaderSetup from '../flows/workingGroup/leaderSetup'
+import electionParametersProposal from '../flows/proposals/electionParametersProposal'
+import manageLeaderRole from '../flows/proposals/manageLeaderRole'
+import spendingProposal from '../flows/proposals/spendingProposal'
+import textProposal from '../flows/proposals/textProposal'
+import validatorCountProposal from '../flows/proposals/validatorCountProposal'
+import workingGroupMintCapacityProposal from '../flows/proposals/workingGroupMintCapacityProposal'
+import atLeastValueBug from '../flows/workingGroup/atLeastValueBug'
+import manageWorkerAsLead from '../flows/workingGroup/manageWorkerAsLead'
+import manageWorkerAsWorker from '../flows/workingGroup/manageWorkerAsWorker'
+import workerPayout from '../flows/workingGroup/workerPayout'
+
+const scenario = async () => {
+  const debug = Debugger('scenario:full')
+
+  // Load env variables
+  config()
+  const env = process.env
+
+  // Connect api to the chain
+  const nodeUrl: string = env.NODE_URL || 'ws://127.0.0.1:9944'
+  const provider = new WsProvider(nodeUrl)
+  const api: Api = await Api.create(provider, env.TREASURY_ACCOUNT_URI || '//Alice', env.SUDO_ACCOUNT_URI || '//Alice')
+
+  await Promise.all([creatingMemberships(api, env), councilSetup(api, env)])
+
+  // Runtime is configured for MaxActiveProposalLimit = 5
+  // So we should ensure we don't exceed that number of active proposals
+  // which limits the number of concurrent tests that create proposals
+  await Promise.all([
+    electionParametersProposal(api, env),
+    spendingProposal(api, env),
+    textProposal(api, env),
+    validatorCountProposal(api, env),
+  ])
+
+  await Promise.all([
+    workingGroupMintCapacityProposal(api, env, WorkingGroups.StorageWorkingGroup),
+    workingGroupMintCapacityProposal(api, env, WorkingGroups.ContentDirectoryWorkingGroup),
+    manageLeaderRole(api, env, WorkingGroups.StorageWorkingGroup),
+    manageLeaderRole(api, env, WorkingGroups.ContentDirectoryWorkingGroup),
+  ])
+
+  await Promise.all([
+    leaderSetup(api, env, WorkingGroups.StorageWorkingGroup),
+    leaderSetup(api, env, WorkingGroups.ContentDirectoryWorkingGroup),
+  ])
+
+  // All tests below require an active Lead for each group
+  // Test bug only on one instance of working group is sufficient
+  await atLeastValueBug(api, env)
+
+  await Promise.all([
+    manageWorkerAsLead(api, env, WorkingGroups.StorageWorkingGroup),
+    manageWorkerAsWorker(api, env, WorkingGroups.StorageWorkingGroup),
+    workerPayout(api, env, WorkingGroups.StorageWorkingGroup),
+    manageWorkerAsLead(api, env, WorkingGroups.ContentDirectoryWorkingGroup),
+    manageWorkerAsWorker(api, env, WorkingGroups.ContentDirectoryWorkingGroup),
+    workerPayout(api, env, WorkingGroups.ContentDirectoryWorkingGroup),
+  ])
+
+  // Note: disconnecting and then reconnecting to the chain in the same process
+  // doesn't seem to work!
+  api.close()
+}
+
+scenario()

+ 71 - 0
tests/network-tests/src/sender.ts

@@ -0,0 +1,71 @@
+import { ApiPromise, Keyring } from '@polkadot/api'
+import { SubmittableExtrinsic } from '@polkadot/api/types'
+import { ISubmittableResult } from '@polkadot/types/types/'
+import { AccountId } from '@polkadot/types/interfaces'
+import { KeyringPair } from '@polkadot/keyring/types'
+import Debugger from 'debug'
+import AsyncLock from 'async-lock'
+
+const debug = Debugger('sender')
+
+export class Sender {
+  private readonly api: ApiPromise
+  private readonly asyncLock: AsyncLock
+  private readonly keyring: Keyring
+
+  constructor(api: ApiPromise, keyring: Keyring) {
+    this.api = api
+    this.asyncLock = new AsyncLock()
+    this.keyring = keyring
+  }
+
+  // Synchronize all sending of transactions into mempool, so we can always safely read
+  // the next account nonce taking mempool into account. This is safe as long as all sending of transactions
+  // from same account occurs in the same process.
+  // Returns a promise that resolves or rejects only after the extrinsic is finalized into a block.
+  public async signAndSend(
+    tx: SubmittableExtrinsic<'promise'>,
+    account: AccountId | string,
+    shouldFail = false
+  ): Promise<ISubmittableResult> {
+    const addr = this.keyring.encodeAddress(account)
+    const senderKeyPair: KeyringPair = this.keyring.getPair(addr)
+
+    let finalizedResolve: { (result: ISubmittableResult): void }
+    let finalizedReject: { (err: Error): void }
+    const finalized: Promise<ISubmittableResult> = new Promise(async (resolve, reject) => {
+      finalizedResolve = resolve
+      finalizedReject = reject
+    })
+
+    const handleEvents = (result: ISubmittableResult) => {
+      if (result.status.isInBlock && result.events !== undefined) {
+        result.events.forEach((event) => {
+          if (event.event.method === 'ExtrinsicFailed') {
+            if (shouldFail) {
+              finalizedResolve(result)
+            } else {
+              finalizedReject(new Error('Extrinsic failed unexpectedly'))
+            }
+          }
+        })
+        finalizedResolve(result)
+      }
+
+      if (result.status.isFuture) {
+        // Its virtually impossible for use to continue with tests
+        // when this occurs and we don't expect the tests to handle this correctly
+        // so just abort!
+        process.exit(-1)
+      }
+    }
+
+    await this.asyncLock.acquire(`${senderKeyPair.address}`, async () => {
+      const nonce = await this.api.rpc.system.accountNextIndex(senderKeyPair.address)
+      const signedTx = tx.sign(senderKeyPair, { nonce })
+      await signedTx.send(handleEvents)
+    })
+
+    return finalized
+  }
+}

+ 0 - 106
tests/network-tests/src/services/dbService.ts

@@ -1,106 +0,0 @@
-import lowdb from 'lowdb/lib/main'
-import FileSync from 'lowdb/adapters/FileSync'
-import { KeyringPair, KeyringPair$Json } from '@polkadot/keyring/types'
-import Keyring from '@polkadot/keyring'
-import BN from 'bn.js'
-
-export class DbService {
-  private static instance: DbService
-
-  private adapter: any
-  private db: any
-  private keyring: Keyring
-  private dbPath: string
-
-  private static MEMBERS_KEY = 'members'
-  private static COUNCIL_KEY = 'council'
-  private static LEADER_KEY = 'leader'
-  private static NONCE_KEY = 'nonce'
-
-  private constructor() {
-    this.keyring = new Keyring({ type: 'sr25519' })
-    this.dbPath = process.env.DB_PATH!
-    this.adapter = new FileSync(this.dbPath)
-    this.db = lowdb(this.adapter)
-  }
-
-  public static getInstance(): DbService {
-    if (!DbService.instance) {
-      DbService.instance = new DbService()
-    }
-    return DbService.instance
-  }
-
-  public setCouncil(council: KeyringPair[]): void {
-    council.forEach((keyPair, index) => {
-      this.db.set(`${DbService.COUNCIL_KEY}.${index}`, keyPair.toJson()).write()
-    })
-  }
-
-  public getCouncil(): KeyringPair[] {
-    const council: KeyringPair[] = []
-    const jsonKeyringPairs: KeyringPair$Json[] = this.db.get(DbService.COUNCIL_KEY).value()
-    jsonKeyringPairs.forEach((jsonKeyringPair) => {
-      const keyPair: KeyringPair = this.keyring.addFromJson(jsonKeyringPair)
-      keyPair.decodePkcs8()
-      council.push(keyPair)
-    })
-    return council
-  }
-
-  public hasCouncil(): boolean {
-    return this.db.has(DbService.COUNCIL_KEY).value()
-  }
-
-  public setMembers(members: KeyringPair[]): void {
-    members.forEach((keyPair, index) => {
-      this.db.set(`${DbService.MEMBERS_KEY}.${index}`, keyPair.toJson()).write()
-    })
-  }
-
-  public getMembers(): KeyringPair[] {
-    const members: KeyringPair[] = []
-    const jsonKeyringPairs: KeyringPair$Json[] = this.db.get(DbService.MEMBERS_KEY).value()
-    jsonKeyringPairs.forEach((jsonKeyringPair) => {
-      const keyPair: KeyringPair = this.keyring.addFromJson(jsonKeyringPair)
-      keyPair.decodePkcs8()
-      members.push(keyPair)
-    })
-    return members
-  }
-
-  public hasMembers(): boolean {
-    return this.db.has(DbService.MEMBERS_KEY).value()
-  }
-
-  public setLeader(leader: KeyringPair, workingGroup: string): void {
-    this.db.set(`${workingGroup}.${DbService.LEADER_KEY}`, leader.toJson()).write()
-  }
-
-  public getLeader(workingGroup: string): KeyringPair {
-    const jsonKeyringPair: KeyringPair$Json = this.db.get(`${workingGroup}.${DbService.LEADER_KEY}`).value()
-    const keyPair: KeyringPair = this.keyring.addFromJson(jsonKeyringPair)
-    keyPair.decodePkcs8()
-    return keyPair
-  }
-
-  public hasLeader(workingGroup: string): boolean {
-    return this.db.has(`${workingGroup}.${DbService.LEADER_KEY}`).value()
-  }
-
-  public setNonce(address: string, nonce: BN): void {
-    this.db.set(`${DbService.NONCE_KEY}.${address}`, nonce.toString()).write()
-  }
-
-  public getNonce(address: string): BN {
-    return new BN(this.db.get(`${DbService.NONCE_KEY}.${address}`).value() as string)
-  }
-
-  public hasNonce(address: string): boolean {
-    return this.db.has(`${DbService.NONCE_KEY}.${address}`).value()
-  }
-
-  public removeNonce(address: string): void {
-    this.db.unset(`${DbService.NONCE_KEY}.${address}`).write()
-  }
-}

+ 0 - 0
tests/network-tests/src/tap-parallel-not-ok


+ 0 - 64
tests/network-tests/src/tests/council/electingCouncilTest.ts

@@ -1,64 +0,0 @@
-import { KeyringPair } from '@polkadot/keyring/types'
-import { initConfig } from '../../utils/config'
-import { Keyring, WsProvider } from '@polkadot/api'
-import { setTestTimeout } from '../../utils/setTestTimeout'
-import BN from 'bn.js'
-import tap from 'tap'
-import { ApiWrapper } from '../../utils/apiWrapper'
-import { closeApi } from '../../utils/closeApi'
-import { BuyMembershipHappyCaseFixture } from '../fixtures/membershipModule'
-import { ElectCouncilFixture } from '../fixtures/councilElectionModule'
-import { Utils } from '../../utils/utils'
-import { PaidTermId } from '@joystream/types/members'
-
-tap.mocha.describe('Electing council scenario', async () => {
-  initConfig()
-
-  const nodeUrl: string = process.env.NODE_URL!
-  const sudoUri: string = process.env.SUDO_ACCOUNT_URI!
-  const keyring = new Keyring({ type: 'sr25519' })
-  const provider = new WsProvider(nodeUrl)
-  const apiWrapper: ApiWrapper = await ApiWrapper.create(provider)
-  const sudo: KeyringPair = keyring.addFromUri(sudoUri)
-
-  const N: number = +process.env.MEMBERSHIP_CREATION_N!
-  const m1KeyPairs: KeyringPair[] = Utils.createKeyPairs(keyring, N)
-  const m2KeyPairs: KeyringPair[] = Utils.createKeyPairs(keyring, N)
-  const paidTerms: PaidTermId = apiWrapper.createPaidTermId(new BN(+process.env.MEMBERSHIP_PAID_TERMS!))
-  const K: number = +process.env.COUNCIL_ELECTION_K!
-  const greaterStake: BN = new BN(+process.env.COUNCIL_STAKE_GREATER_AMOUNT!)
-  const lesserStake: BN = new BN(+process.env.COUNCIL_STAKE_LESSER_AMOUNT!)
-
-  const durationInBlocks = 25
-
-  setTestTimeout(apiWrapper, durationInBlocks)
-
-  const firstMemberSetFixture: BuyMembershipHappyCaseFixture = new BuyMembershipHappyCaseFixture(
-    apiWrapper,
-    sudo,
-    m1KeyPairs,
-    paidTerms
-  )
-  tap.test('Creating first set of members', async () => firstMemberSetFixture.runner(false))
-
-  const secondMemberSetFixture: BuyMembershipHappyCaseFixture = new BuyMembershipHappyCaseFixture(
-    apiWrapper,
-    sudo,
-    m2KeyPairs,
-    paidTerms
-  )
-  tap.test('Creating second set of members', async () => secondMemberSetFixture.runner(false))
-
-  const electCouncilFixture: ElectCouncilFixture = new ElectCouncilFixture(
-    apiWrapper,
-    m1KeyPairs,
-    m2KeyPairs,
-    K,
-    sudo,
-    greaterStake,
-    lesserStake
-  )
-  tap.test('Elect council', async () => electCouncilFixture.runner(false))
-
-  closeApi(apiWrapper)
-})

+ 0 - 57
tests/network-tests/src/tests/councilSetup.ts

@@ -1,57 +0,0 @@
-import { KeyringPair } from '@polkadot/keyring/types'
-import { Keyring, WsProvider } from '@polkadot/api'
-import BN from 'bn.js'
-import tap from 'tap'
-import { PaidTermId } from '@joystream/types/members'
-import { DbService } from '../services/dbService'
-import { initConfig } from '../utils/config'
-import { ApiWrapper } from '../utils/apiWrapper'
-import { Utils } from '../utils/utils'
-import { setTestTimeout } from '../utils/setTestTimeout'
-import { closeApi } from '../utils/closeApi'
-import { CouncilElectionHappyCaseFixture } from './fixtures/councilElectionHappyCase'
-
-tap.mocha.describe('Electing council scenario', async () => {
-  initConfig()
-
-  const nodeUrl: string = process.env.NODE_URL!
-  const sudoUri: string = process.env.SUDO_ACCOUNT_URI!
-  const keyring = new Keyring({ type: 'sr25519' })
-  const db: DbService = DbService.getInstance()
-  if (db.hasCouncil()) {
-    return
-  }
-
-  const provider = new WsProvider(nodeUrl)
-  const apiWrapper: ApiWrapper = await ApiWrapper.create(provider)
-  const sudo: KeyringPair = keyring.addFromUri(sudoUri)
-
-  const N: number = +process.env.MEMBERSHIP_CREATION_N!
-  const m1KeyPairs: KeyringPair[] = Utils.createKeyPairs(keyring, N)
-  const m2KeyPairs: KeyringPair[] = Utils.createKeyPairs(keyring, N)
-  const paidTerms: PaidTermId = apiWrapper.createPaidTermId(new BN(+process.env.MEMBERSHIP_PAID_TERMS!))
-  const K: number = +process.env.COUNCIL_ELECTION_K!
-  const greaterStake: BN = new BN(+process.env.COUNCIL_STAKE_GREATER_AMOUNT!)
-  const lesserStake: BN = new BN(+process.env.COUNCIL_STAKE_LESSER_AMOUNT!)
-
-  const durationInBlocks = 25
-
-  setTestTimeout(apiWrapper, durationInBlocks)
-
-  const councilElectionHappyCaseFixture = new CouncilElectionHappyCaseFixture(
-    apiWrapper,
-    sudo,
-    m1KeyPairs,
-    m2KeyPairs,
-    paidTerms,
-    K,
-    greaterStake,
-    lesserStake
-  )
-  await councilElectionHappyCaseFixture.runner(false)
-
-  db.setMembers(m1KeyPairs)
-  db.setCouncil(m2KeyPairs)
-
-  closeApi(apiWrapper)
-})

+ 0 - 68
tests/network-tests/src/tests/fixtures/councilElectionHappyCase.ts

@@ -1,68 +0,0 @@
-import { Fixture } from './interfaces/fixture'
-import { BuyMembershipHappyCaseFixture } from './membershipModule'
-import tap from 'tap'
-import { ElectCouncilFixture } from './councilElectionModule'
-import { ApiWrapper } from '../../utils/apiWrapper'
-import { KeyringPair } from '@polkadot/keyring/types'
-import { PaidTermId } from '@joystream/types/members'
-import BN from 'bn.js'
-
-export class CouncilElectionHappyCaseFixture implements Fixture {
-  private apiWrapper: ApiWrapper
-  private sudo: KeyringPair
-  private membersKeyPairs: KeyringPair[]
-  private councilKeyPairs: KeyringPair[]
-  private paidTerms: PaidTermId
-  private k: number
-  private greaterStake: BN
-  private lesserStake: BN
-
-  constructor(
-    apiWrapper: ApiWrapper,
-    sudo: KeyringPair,
-    membersKeyPairs: KeyringPair[],
-    councilKeyPairs: KeyringPair[],
-    paidTerms: PaidTermId,
-    k: number,
-    greaterStake: BN,
-    lesserStake: BN
-  ) {
-    this.apiWrapper = apiWrapper
-    this.sudo = sudo
-    this.membersKeyPairs = membersKeyPairs
-    this.councilKeyPairs = councilKeyPairs
-    this.paidTerms = paidTerms
-    this.k = k
-    this.greaterStake = greaterStake
-    this.lesserStake = lesserStake
-  }
-
-  public async runner(expectFailure: boolean): Promise<void> {
-    const firstMemberSetFixture: BuyMembershipHappyCaseFixture = new BuyMembershipHappyCaseFixture(
-      this.apiWrapper,
-      this.sudo,
-      this.membersKeyPairs,
-      this.paidTerms
-    )
-    tap.test('Creating first set of members', async () => firstMemberSetFixture.runner(false))
-
-    const secondMemberSetFixture: BuyMembershipHappyCaseFixture = new BuyMembershipHappyCaseFixture(
-      this.apiWrapper,
-      this.sudo,
-      this.councilKeyPairs,
-      this.paidTerms
-    )
-    tap.test('Creating second set of members', async () => secondMemberSetFixture.runner(false))
-
-    const electCouncilFixture: ElectCouncilFixture = new ElectCouncilFixture(
-      this.apiWrapper,
-      this.membersKeyPairs,
-      this.councilKeyPairs,
-      this.k,
-      this.sudo,
-      this.greaterStake,
-      this.lesserStake
-    )
-    tap.test('Elect council', async () => electCouncilFixture.runner(false))
-  }
-}

+ 0 - 140
tests/network-tests/src/tests/fixtures/councilElectionModule.ts

@@ -1,140 +0,0 @@
-import { ApiWrapper } from '../../utils/apiWrapper'
-import { KeyringPair } from '@polkadot/keyring/types'
-import BN from 'bn.js'
-import { assert } from 'chai'
-import { Seat } from '@joystream/types/council'
-import { v4 as uuid } from 'uuid'
-import { Utils } from '../../utils/utils'
-import { Fixture } from './interfaces/fixture'
-
-export class ElectCouncilFixture implements Fixture {
-  private apiWrapper: ApiWrapper
-  private membersKeyPairs: KeyringPair[]
-  private councilKeyPairs: KeyringPair[]
-  private k: number
-  private sudo: KeyringPair
-  private greaterStake: BN
-  private lesserStake: BN
-
-  public constructor(
-    apiWrapper: ApiWrapper,
-    membersKeyPairs: KeyringPair[],
-    councilKeyPairs: KeyringPair[],
-    k: number,
-    sudo: KeyringPair,
-    greaterStake: BN,
-    lesserStake: BN
-  ) {
-    this.apiWrapper = apiWrapper
-    this.membersKeyPairs = membersKeyPairs
-    this.councilKeyPairs = councilKeyPairs
-    this.k = k
-    this.sudo = sudo
-    this.greaterStake = greaterStake
-    this.lesserStake = lesserStake
-  }
-
-  public async runner(expectFailure: boolean): Promise<void> {
-    let now = await this.apiWrapper.getBestBlock()
-    const applyForCouncilFee: BN = this.apiWrapper.estimateApplyForCouncilFee(this.greaterStake)
-    const voteForCouncilFee: BN = this.apiWrapper.estimateVoteForCouncilFee(
-      this.sudo.address,
-      this.sudo.address,
-      this.greaterStake
-    )
-    const salt: string[] = []
-    this.membersKeyPairs.forEach(() => {
-      salt.push(''.concat(uuid().replace(/-/g, '')))
-    })
-    const revealVoteFee: BN = this.apiWrapper.estimateRevealVoteFee(this.sudo.address, salt[0])
-
-    // Topping the balances
-    await this.apiWrapper.transferBalanceToAccounts(
-      this.sudo,
-      this.councilKeyPairs,
-      applyForCouncilFee.add(this.greaterStake)
-    )
-    await this.apiWrapper.transferBalanceToAccounts(
-      this.sudo,
-      this.membersKeyPairs,
-      voteForCouncilFee.add(revealVoteFee).add(this.greaterStake)
-    )
-
-    // First K members stake more
-    await this.apiWrapper.sudoStartAnnouncingPerion(this.sudo, now.addn(100))
-    await this.apiWrapper.batchApplyForCouncilElection(this.councilKeyPairs.slice(0, this.k), this.greaterStake)
-    this.councilKeyPairs.slice(0, this.k).forEach((keyPair) =>
-      this.apiWrapper.getCouncilElectionStake(keyPair.address).then((stake) => {
-        assert(
-          stake.eq(this.greaterStake),
-          `${keyPair.address} not applied correctly for council election with stake ${stake} versus expected ${this.greaterStake}`
-        )
-      })
-    )
-
-    // Last members stake less
-    await this.apiWrapper.batchApplyForCouncilElection(this.councilKeyPairs.slice(this.k), this.lesserStake)
-    this.councilKeyPairs.slice(this.k).forEach((keyPair) =>
-      this.apiWrapper.getCouncilElectionStake(keyPair.address).then((stake) => {
-        assert(
-          stake.eq(this.lesserStake),
-          `${keyPair.address} not applied correctrly for council election with stake ${stake} versus expected ${this.lesserStake}`
-        )
-      })
-    )
-
-    // Voting
-    await this.apiWrapper.sudoStartVotingPerion(this.sudo, now.addn(100))
-    await this.apiWrapper.batchVoteForCouncilMember(
-      this.membersKeyPairs.slice(0, this.k),
-      this.councilKeyPairs.slice(0, this.k),
-      salt.slice(0, this.k),
-      this.lesserStake
-    )
-    await this.apiWrapper.batchVoteForCouncilMember(
-      this.membersKeyPairs.slice(this.k),
-      this.councilKeyPairs.slice(this.k),
-      salt.slice(this.k),
-      this.greaterStake
-    )
-
-    // Revealing
-    await this.apiWrapper.sudoStartRevealingPerion(this.sudo, now.addn(100))
-    await this.apiWrapper.batchRevealVote(
-      this.membersKeyPairs.slice(0, this.k),
-      this.councilKeyPairs.slice(0, this.k),
-      salt.slice(0, this.k)
-    )
-    await this.apiWrapper.batchRevealVote(
-      this.membersKeyPairs.slice(this.k),
-      this.councilKeyPairs.slice(this.k),
-      salt.slice(this.k)
-    )
-    now = await this.apiWrapper.getBestBlock()
-
-    // Resolving election
-    // 3 is to ensure the revealing block is in future
-    await this.apiWrapper.sudoStartRevealingPerion(this.sudo, now.addn(3))
-    await Utils.wait(this.apiWrapper.getBlockDuration().muln(2.5).toNumber())
-    const seats: Seat[] = await this.apiWrapper.getCouncil()
-
-    // Preparing collections to increase assertion readability
-    const councilAddresses: string[] = this.councilKeyPairs.map((keyPair) => keyPair.address)
-    const membersAddresses: string[] = this.membersKeyPairs.map((keyPair) => keyPair.address)
-    const members: string[] = seats.map((seat) => seat.member.toString())
-    const bakers: string[] = seats.map((seat) => seat.backers.map((baker) => baker.member.toString())).flat()
-
-    // Assertions
-    councilAddresses.forEach((address) => assert(members.includes(address), `Account ${address} is not in the council`))
-    membersAddresses.forEach((address) => assert(bakers.includes(address), `Account ${address} is not in the voters`))
-    seats.forEach((seat) =>
-      assert(
-        Utils.getTotalStake(seat).eq(this.greaterStake.add(this.lesserStake)),
-        `Member ${seat.member} has unexpected stake ${Utils.getTotalStake(seat)}`
-      )
-    )
-    if (expectFailure) {
-      throw new Error('Successful fixture run while expecting failure')
-    }
-  }
-}

+ 0 - 3
tests/network-tests/src/tests/fixtures/interfaces/fixture.ts

@@ -1,3 +0,0 @@
-export interface Fixture {
-  runner(expectFailure: boolean): Promise<void>
-}

+ 0 - 126
tests/network-tests/src/tests/fixtures/leaderHiringHappyCase.ts

@@ -1,126 +0,0 @@
-import { Fixture } from './interfaces/fixture'
-import tap from 'tap'
-import {
-  AddLeaderOpeningFixture,
-  ApplyForOpeningFixture,
-  BeginLeaderApplicationReviewFixture,
-  FillLeaderOpeningFixture,
-} from './workingGroupModule'
-import { BuyMembershipHappyCaseFixture } from './membershipModule'
-import { ApiWrapper, WorkingGroups } from '../../utils/apiWrapper'
-import { OpeningId } from '@joystream/types/hiring'
-import { KeyringPair } from '@polkadot/keyring/types'
-import { PaidTermId } from '@joystream/types/members'
-import BN from 'bn.js'
-
-export class LeaderHiringHappyCaseFixture implements Fixture {
-  private apiWrapper: ApiWrapper
-  private sudo: KeyringPair
-  private nKeyPairs: KeyringPair[]
-  private leadKeyPair: KeyringPair[]
-  private paidTerms: PaidTermId
-  private applicationStake: BN
-  private roleStake: BN
-  private openingActivationDelay: BN
-  private rewardInterval: BN
-  private firstRewardInterval: BN
-  private payoutAmount: BN
-  private workingGroup: WorkingGroups
-
-  constructor(
-    apiWrapper: ApiWrapper,
-    sudo: KeyringPair,
-    nKeyPairs: KeyringPair[],
-    leadKeyPair: KeyringPair[],
-    paidTerms: PaidTermId,
-    applicationStake: BN,
-    roleStake: BN,
-    openingActivationDelay: BN,
-    rewardInterval: BN,
-    firstRewardInterval: BN,
-    payoutAmount: BN,
-    workingGroup: WorkingGroups
-  ) {
-    this.apiWrapper = apiWrapper
-    this.sudo = sudo
-    this.nKeyPairs = nKeyPairs
-    this.leadKeyPair = leadKeyPair
-    this.paidTerms = paidTerms
-    this.applicationStake = applicationStake
-    this.roleStake = roleStake
-    this.openingActivationDelay = openingActivationDelay
-    this.rewardInterval = rewardInterval
-    this.firstRewardInterval = firstRewardInterval
-    this.payoutAmount = payoutAmount
-    this.workingGroup = workingGroup
-  }
-
-  public async runner(expectFailure: boolean): Promise<void> {
-    const happyCaseFixture: BuyMembershipHappyCaseFixture = new BuyMembershipHappyCaseFixture(
-      this.apiWrapper,
-      this.sudo,
-      this.nKeyPairs,
-      this.paidTerms
-    )
-    tap.test('Creating a set of members', async () => happyCaseFixture.runner(false))
-
-    const leaderHappyCaseFixture: BuyMembershipHappyCaseFixture = new BuyMembershipHappyCaseFixture(
-      this.apiWrapper,
-      this.sudo,
-      this.leadKeyPair,
-      this.paidTerms
-    )
-    tap.test('Buying membership for leader account', async () => leaderHappyCaseFixture.runner(false))
-
-    const addLeaderOpeningFixture: AddLeaderOpeningFixture = new AddLeaderOpeningFixture(
-      this.apiWrapper,
-      this.nKeyPairs,
-      this.sudo,
-      this.applicationStake,
-      this.roleStake,
-      this.openingActivationDelay,
-      this.workingGroup
-    )
-    tap.test('Add lead opening', async () => await addLeaderOpeningFixture.runner(false))
-
-    let applyForLeaderOpeningFixture: ApplyForOpeningFixture
-    tap.test('Apply for lead opening', async () => {
-      applyForLeaderOpeningFixture = new ApplyForOpeningFixture(
-        this.apiWrapper,
-        this.leadKeyPair,
-        this.sudo,
-        this.applicationStake,
-        this.roleStake,
-        addLeaderOpeningFixture.getCreatedOpeningId() as OpeningId,
-        this.workingGroup
-      )
-      await applyForLeaderOpeningFixture.runner(false)
-    })
-
-    let beginLeaderApplicationReviewFixture: BeginLeaderApplicationReviewFixture
-    tap.test('Begin lead application review', async () => {
-      beginLeaderApplicationReviewFixture = new BeginLeaderApplicationReviewFixture(
-        this.apiWrapper,
-        this.sudo,
-        addLeaderOpeningFixture.getCreatedOpeningId() as OpeningId,
-        this.workingGroup
-      )
-      await beginLeaderApplicationReviewFixture.runner(false)
-    })
-
-    let fillLeaderOpeningFixture: FillLeaderOpeningFixture
-    tap.test('Fill lead opening', async () => {
-      fillLeaderOpeningFixture = new FillLeaderOpeningFixture(
-        this.apiWrapper,
-        this.leadKeyPair,
-        this.sudo,
-        addLeaderOpeningFixture.getCreatedOpeningId() as OpeningId,
-        this.firstRewardInterval,
-        this.rewardInterval,
-        this.payoutAmount,
-        this.workingGroup
-      )
-      await fillLeaderOpeningFixture.runner(false)
-    })
-  }
-}

+ 0 - 112
tests/network-tests/src/tests/fixtures/membershipModule.ts

@@ -1,112 +0,0 @@
-import { ApiWrapper } from '../../utils/apiWrapper'
-import { KeyringPair } from '@polkadot/keyring/types'
-import BN from 'bn.js'
-import { assert } from 'chai'
-import { Fixture } from './interfaces/fixture'
-import { PaidTermId } from '@joystream/types/members'
-
-export class BuyMembershipHappyCaseFixture implements Fixture {
-  private apiWrapper: ApiWrapper
-  private treasury: KeyringPair
-  private keyPairs: KeyringPair[]
-  private paidTerms: PaidTermId
-
-  public constructor(apiWrapper: ApiWrapper, treasury: KeyringPair, keyPairs: KeyringPair[], paidTerms: PaidTermId) {
-    this.apiWrapper = apiWrapper
-    this.treasury = treasury
-    this.keyPairs = keyPairs
-    this.paidTerms = paidTerms
-  }
-
-  public async runner(expectFailure: boolean): Promise<void> {
-    // Fee estimation and transfer
-    const membershipFee: BN = await this.apiWrapper.getMembershipFee(this.paidTerms)
-    const membershipTransactionFee: BN = this.apiWrapper.estimateBuyMembershipFee(
-      this.treasury,
-      this.paidTerms,
-      'member_name_which_is_longer_than_expected'
-    )
-    await this.apiWrapper.transferBalanceToAccounts(
-      this.treasury,
-      this.keyPairs,
-      membershipTransactionFee.add(new BN(membershipFee))
-    )
-
-    // Buying membership
-    await Promise.all(
-      this.keyPairs.map(async (keyPair, index) => {
-        await this.apiWrapper.buyMembership(
-          keyPair,
-          this.paidTerms,
-          `new_member_${index}${keyPair.address.substring(0, 8)}`
-        )
-      })
-    )
-
-    // Assertions
-    this.keyPairs.forEach((keyPair) =>
-      this.apiWrapper
-        .getMemberIds(keyPair.address)
-        .then((membership) => assert(membership.length > 0, `Account ${keyPair.address} is not a member`))
-    )
-    if (expectFailure) {
-      throw new Error('Successful fixture run while expecting failure')
-    }
-  }
-}
-
-export class BuyMembershipWithInsufficienFundsFixture implements Fixture {
-  private apiWrapper: ApiWrapper
-  private treasury: KeyringPair
-  private aKeyPair: KeyringPair
-  private paidTerms: PaidTermId
-
-  public constructor(apiWrapper: ApiWrapper, treasury: KeyringPair, aKeyPair: KeyringPair, paidTerms: PaidTermId) {
-    this.apiWrapper = apiWrapper
-    this.treasury = treasury
-    this.aKeyPair = aKeyPair
-    this.paidTerms = paidTerms
-  }
-
-  public async runner(expectFailure: boolean) {
-    // Assertions
-    this.apiWrapper
-      .getMemberIds(this.aKeyPair.address)
-      .then((membership) => assert(membership.length === 0, 'Account A is a member'))
-
-    // Fee estimation and transfer
-    const membershipFee: BN = await this.apiWrapper.getMembershipFee(this.paidTerms)
-    const membershipTransactionFee: BN = this.apiWrapper.estimateBuyMembershipFee(
-      this.treasury,
-      this.paidTerms,
-      'member_name_which_is_longer_than_expected'
-    )
-    await this.apiWrapper.transferBalance(this.treasury, this.aKeyPair.address, membershipTransactionFee)
-
-    // Balance assertion
-    await this.apiWrapper
-      .getBalance(this.aKeyPair.address)
-      .then((balance) =>
-        assert(
-          balance.toBn() < membershipFee.add(membershipTransactionFee),
-          'Account A already have sufficient balance to purchase membership'
-        )
-      )
-
-    // Buying memebership
-    await this.apiWrapper.buyMembership(
-      this.aKeyPair,
-      this.paidTerms,
-      `late_member_${this.aKeyPair.address.substring(0, 8)}`,
-      true
-    )
-
-    // Assertions
-    this.apiWrapper
-      .getMemberIds(this.aKeyPair.address)
-      .then((membership) => assert(membership.length === 0, 'Account A is a member'))
-    if (expectFailure) {
-      throw new Error('Successful fixture run while expecting failure')
-    }
-  }
-}

+ 0 - 1095
tests/network-tests/src/tests/fixtures/proposalsModule.ts

@@ -1,1095 +0,0 @@
-import { KeyringPair } from '@polkadot/keyring/types'
-import { ApiWrapper, WorkingGroups } from '../../utils/apiWrapper'
-import { v4 as uuid } from 'uuid'
-import BN from 'bn.js'
-import { ProposalId } from '@joystream/types/proposals'
-import { Fixture } from './interfaces/fixture'
-import { assert } from 'chai'
-import { ApplicationId, OpeningId } from '@joystream/types/hiring'
-import { WorkerId } from '@joystream/types/working-group'
-import { Utils } from '../../utils/utils'
-
-export class CreateWorkingGroupLeaderOpeningFixture implements Fixture {
-  private apiWrapper: ApiWrapper
-  private membersKeyPairs: KeyringPair[]
-  private treasury: KeyringPair
-  private applicationStake: BN
-  private roleStake: BN
-  private workingGroup: string
-
-  private result: ProposalId | undefined
-
-  constructor(
-    apiWrapper: ApiWrapper,
-    membersKeyPairs: KeyringPair[],
-    treasury: KeyringPair,
-    applicationStake: BN,
-    roleStake: BN,
-    workingGroup: string
-  ) {
-    this.apiWrapper = apiWrapper
-    this.membersKeyPairs = membersKeyPairs
-    this.treasury = treasury
-    this.applicationStake = applicationStake
-    this.roleStake = roleStake
-    this.workingGroup = workingGroup
-  }
-
-  public getCreatedProposalId(): ProposalId | undefined {
-    return this.result
-  }
-
-  public async runner(expectFailure: boolean): Promise<void> {
-    // Setup
-    const proposalTitle: string = 'Testing proposal ' + uuid().substring(0, 8)
-    const description: string = 'Testing working group lead opening proposal ' + uuid().substring(0, 8)
-
-    // Proposal stake calculation
-    const proposalStake: BN = new BN(100000)
-    const proposalFee: BN = this.apiWrapper.estimateProposeCreateWorkingGroupLeaderOpeningFee()
-    await this.apiWrapper.transferBalance(
-      this.treasury,
-      this.membersKeyPairs[0].address,
-      proposalFee.add(proposalStake)
-    )
-
-    // Proposal creation
-    const proposalPromise: Promise<ProposalId> = this.apiWrapper.expectProposalCreated()
-    await this.apiWrapper.proposeCreateWorkingGroupLeaderOpening({
-      account: this.membersKeyPairs[0],
-      title: proposalTitle,
-      description: description,
-      proposalStake: proposalStake,
-      actiavteAt: 'CurrentBlock',
-      maxActiveApplicants: new BN(this.membersKeyPairs.length),
-      maxReviewPeriodLength: new BN(32),
-      applicationStakingPolicyAmount: this.applicationStake,
-      applicationCrowdedOutUnstakingPeriodLength: new BN(1),
-      applicationReviewPeriodExpiredUnstakingPeriodLength: new BN(1),
-      roleStakingPolicyAmount: this.roleStake,
-      roleCrowdedOutUnstakingPeriodLength: new BN(1),
-      roleReviewPeriodExpiredUnstakingPeriodLength: new BN(1),
-      slashableMaxCount: new BN(1),
-      slashableMaxPercentPtsPerTime: new BN(100),
-      fillOpeningSuccessfulApplicantApplicationStakeUnstakingPeriod: new BN(1),
-      fillOpeningFailedApplicantApplicationStakeUnstakingPeriod: new BN(1),
-      fillOpeningFailedApplicantRoleStakeUnstakingPeriod: new BN(1),
-      terminateApplicationStakeUnstakingPeriod: new BN(1),
-      terminateRoleStakeUnstakingPeriod: new BN(1),
-      exitRoleApplicationStakeUnstakingPeriod: new BN(1),
-      exitRoleStakeUnstakingPeriod: new BN(1),
-      text: uuid().substring(0, 8),
-      workingGroup: this.workingGroup,
-    })
-    this.result = await proposalPromise
-    if (expectFailure) {
-      throw new Error('Successful fixture run while expecting failure')
-    }
-  }
-}
-
-export class BeginWorkingGroupLeaderApplicationReviewFixture implements Fixture {
-  private apiWrapper: ApiWrapper
-  private membersKeyPairs: KeyringPair[]
-  private treasury: KeyringPair
-  private openingId: OpeningId
-  private workingGroup: string
-
-  private result: ProposalId | undefined
-
-  constructor(
-    apiWrapper: ApiWrapper,
-    membersKeyPairs: KeyringPair[],
-    treasury: KeyringPair,
-    openingId: OpeningId,
-    workingGroup: string
-  ) {
-    this.apiWrapper = apiWrapper
-    this.membersKeyPairs = membersKeyPairs
-    this.treasury = treasury
-    this.openingId = openingId
-    this.workingGroup = workingGroup
-  }
-
-  public getCreatedProposalId(): ProposalId | undefined {
-    return this.result
-  }
-
-  public async runner(expectFailure: boolean): Promise<void> {
-    // 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)
-
-    // Proposal stake calculation
-    const proposalStake: BN = new BN(25000)
-    const proposalFee: BN = this.apiWrapper.estimateProposeBeginWorkingGroupLeaderApplicationReviewFee()
-    await this.apiWrapper.transferBalance(
-      this.treasury,
-      this.membersKeyPairs[0].address,
-      proposalFee.add(proposalStake)
-    )
-
-    // Proposal creation
-    const proposalPromise: Promise<ProposalId> = this.apiWrapper.expectProposalCreated()
-    await this.apiWrapper.proposeBeginWorkingGroupLeaderApplicationReview(
-      this.membersKeyPairs[0],
-      proposalTitle,
-      description,
-      proposalStake,
-      this.openingId,
-      this.workingGroup
-    )
-    this.result = await proposalPromise
-    if (expectFailure) {
-      throw new Error('Successful fixture run while expecting failure')
-    }
-  }
-}
-
-export class FillLeaderOpeningProposalFixture implements Fixture {
-  private apiWrapper: ApiWrapper
-  private membersKeyPairs: KeyringPair[]
-  private applicantRoleAccountAddress: string
-  private treasury: KeyringPair
-  private firstRewardInterval: BN
-  private rewardInterval: BN
-  private payoutAmount: BN
-  private openingId: OpeningId
-  private workingGroup: WorkingGroups
-
-  private result: ProposalId | undefined
-
-  constructor(
-    apiWrapper: ApiWrapper,
-    membersKeyPairs: KeyringPair[],
-    applicantRoleAccountAddress: string,
-    treasury: KeyringPair,
-    firstRewardInterval: BN,
-    rewardInterval: BN,
-    payoutAmount: BN,
-    openingId: OpeningId,
-    workingGroup: WorkingGroups
-  ) {
-    this.apiWrapper = apiWrapper
-    this.membersKeyPairs = membersKeyPairs
-    this.applicantRoleAccountAddress = applicantRoleAccountAddress
-    this.treasury = treasury
-    this.firstRewardInterval = firstRewardInterval
-    this.rewardInterval = rewardInterval
-    this.payoutAmount = payoutAmount
-    this.openingId = openingId
-    this.workingGroup = workingGroup
-  }
-
-  public getCreatedProposalId(): ProposalId | undefined {
-    return this.result
-  }
-
-  public async runner(expectFailure: boolean): Promise<void> {
-    // Setup
-    const proposalTitle: string = 'Testing proposal ' + uuid().substring(0, 8)
-    const description: string = 'Testing fill opening proposal ' + uuid().substring(0, 8)
-    const workingGroupString: string = this.apiWrapper.getWorkingGroupString(this.workingGroup)
-
-    // Proposal stake calculation
-    const proposalStake: BN = new BN(50000)
-    const proposalFee: BN = this.apiWrapper.estimateProposeFillLeaderOpeningFee()
-    await this.apiWrapper.transferBalance(
-      this.treasury,
-      this.membersKeyPairs[0].address,
-      proposalFee.add(proposalStake)
-    )
-
-    // Proposal creation
-    const applicationId: ApplicationId = (
-      await this.apiWrapper.getActiveApplicationsIdsByRoleAccount(this.applicantRoleAccountAddress, this.workingGroup)
-    )[0]
-    const now: BN = await this.apiWrapper.getBestBlock()
-
-    const proposalPromise: Promise<ProposalId> = this.apiWrapper.expectProposalCreated()
-    await this.apiWrapper.proposeFillLeaderOpening({
-      account: this.membersKeyPairs[0],
-      title: proposalTitle,
-      description: description,
-      proposalStake: proposalStake,
-      openingId: this.openingId,
-      successfulApplicationId: applicationId,
-      amountPerPayout: this.payoutAmount,
-      nextPaymentAtBlock: now.add(this.firstRewardInterval),
-      payoutInterval: this.rewardInterval,
-      workingGroup: workingGroupString,
-    })
-    this.result = await proposalPromise
-    if (expectFailure) {
-      throw new Error('Successful fixture run while expecting failure')
-    }
-  }
-}
-
-export class TerminateLeaderRoleProposalFixture implements Fixture {
-  private apiWrapper: ApiWrapper
-  private membersKeyPairs: KeyringPair[]
-  private leaderRoleAccountAddress: string
-  private treasury: KeyringPair
-  private slash: boolean
-  private workingGroup: WorkingGroups
-
-  private result: ProposalId | undefined
-
-  constructor(
-    apiWrapper: ApiWrapper,
-    membersKeyPairs: KeyringPair[],
-    leaderRoleAccountAddress: string,
-    treasury: KeyringPair,
-    slash: boolean,
-    workingGroup: WorkingGroups
-  ) {
-    this.apiWrapper = apiWrapper
-    this.membersKeyPairs = membersKeyPairs
-    this.leaderRoleAccountAddress = leaderRoleAccountAddress
-    this.treasury = treasury
-    this.slash = slash
-    this.workingGroup = workingGroup
-  }
-
-  public getCreatedProposalId(): ProposalId | undefined {
-    return this.result
-  }
-
-  public async runner(expectFailure: boolean): Promise<void> {
-    // 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)
-    const rationale: string = 'Testing leader termination ' + uuid().substring(0, 8)
-    const workingGroupString: string = this.apiWrapper.getWorkingGroupString(this.workingGroup)
-    const workerId: WorkerId = await this.apiWrapper.getWorkerIdByRoleAccount(
-      this.leaderRoleAccountAddress,
-      this.workingGroup
-    )
-
-    // Proposal stake calculation
-    const proposalStake: BN = new BN(100000)
-    const proposalFee: BN = this.apiWrapper.estimateProposeTerminateLeaderRoleFee()
-    await this.apiWrapper.transferBalance(
-      this.treasury,
-      this.membersKeyPairs[0].address,
-      proposalFee.add(proposalStake)
-    )
-
-    // Proposal creation
-    const proposalPromise: Promise<ProposalId> = this.apiWrapper.expectProposalCreated()
-    await this.apiWrapper.proposeTerminateLeaderRole(
-      this.membersKeyPairs[0],
-      proposalTitle,
-      description,
-      proposalStake,
-      workerId,
-      rationale,
-      this.slash,
-      workingGroupString
-    )
-    this.result = await proposalPromise
-    if (expectFailure) {
-      throw new Error('Successful fixture run while expecting failure')
-    }
-  }
-}
-
-export class SetLeaderRewardProposalFixture implements Fixture {
-  private apiWrapper: ApiWrapper
-  private membersKeyPairs: KeyringPair[]
-  private treasury: KeyringPair
-  private payoutAmount: BN
-  private workingGroup: WorkingGroups
-
-  private result: ProposalId | undefined
-
-  constructor(
-    apiWrapper: ApiWrapper,
-    membersKeyPairs: KeyringPair[],
-    treasury: KeyringPair,
-    payoutAmount: BN,
-    workingGroup: WorkingGroups
-  ) {
-    this.apiWrapper = apiWrapper
-    this.membersKeyPairs = membersKeyPairs
-    this.treasury = treasury
-    this.payoutAmount = payoutAmount
-    this.workingGroup = workingGroup
-  }
-
-  public getCreatedProposalId(): ProposalId | undefined {
-    return this.result
-  }
-
-  public async runner(expectFailure: boolean): Promise<void> {
-    // Setup
-    const proposalTitle: string = 'Testing proposal ' + uuid().substring(0, 8)
-    const description: string = 'Testing set leader reward proposal ' + uuid().substring(0, 8)
-    const workingGroupString: string = this.apiWrapper.getWorkingGroupString(this.workingGroup)
-    const workerId: WorkerId = (await this.apiWrapper.getLeadWorkerId(this.workingGroup))!
-
-    // Proposal stake calculation
-    const proposalStake: BN = new BN(50000)
-    const proposalFee: BN = this.apiWrapper.estimateProposeLeaderRewardFee()
-    await this.apiWrapper.transferBalance(
-      this.treasury,
-      this.membersKeyPairs[0].address,
-      proposalFee.add(proposalStake)
-    )
-
-    // Proposal creation
-    const proposalPromise: Promise<ProposalId> = this.apiWrapper.expectProposalCreated()
-    await this.apiWrapper.proposeLeaderReward(
-      this.membersKeyPairs[0],
-      proposalTitle,
-      description,
-      proposalStake,
-      workerId,
-      this.payoutAmount,
-      workingGroupString
-    )
-    this.result = await proposalPromise
-    if (expectFailure) {
-      throw new Error('Successful fixture run while expecting failure')
-    }
-  }
-}
-
-export class DecreaseLeaderStakeProposalFixture implements Fixture {
-  private apiWrapper: ApiWrapper
-  private membersKeyPairs: KeyringPair[]
-  private treasury: KeyringPair
-  private stakeDecrement: BN
-  private workingGroup: WorkingGroups
-
-  private result: ProposalId | undefined
-
-  constructor(
-    apiWrapper: ApiWrapper,
-    membersKeyPairs: KeyringPair[],
-    treasury: KeyringPair,
-    stakeDecrement: BN,
-    workingGroup: WorkingGroups
-  ) {
-    this.apiWrapper = apiWrapper
-    this.membersKeyPairs = membersKeyPairs
-    this.treasury = treasury
-    this.stakeDecrement = stakeDecrement
-    this.workingGroup = workingGroup
-  }
-
-  public getCreatedProposalId(): ProposalId | undefined {
-    return this.result
-  }
-
-  public async runner(expectFailure: boolean): Promise<void> {
-    // Setup
-    const proposalTitle: string = 'Testing proposal ' + uuid().substring(0, 8)
-    const description: string = 'Testing decrease leader stake proposal ' + uuid().substring(0, 8)
-    const workingGroupString: string = this.apiWrapper.getWorkingGroupString(this.workingGroup)
-    const workerId: WorkerId = (await this.apiWrapper.getLeadWorkerId(this.workingGroup))!
-
-    // Proposal stake calculation
-    const proposalStake: BN = new BN(50000)
-    const proposalFee: BN = this.apiWrapper.estimateProposeDecreaseLeaderStakeFee()
-    await this.apiWrapper.transferBalance(
-      this.treasury,
-      this.membersKeyPairs[0].address,
-      proposalFee.add(proposalStake)
-    )
-
-    // Proposal creation
-    const proposalPromise: Promise<ProposalId> = this.apiWrapper.expectProposalCreated()
-    await this.apiWrapper.proposeDecreaseLeaderStake(
-      this.membersKeyPairs[0],
-      proposalTitle,
-      description,
-      proposalStake,
-      workerId,
-      this.stakeDecrement,
-      workingGroupString
-    )
-    this.result = await proposalPromise
-    if (expectFailure) {
-      throw new Error('Successful fixture run while expecting failure')
-    }
-  }
-}
-
-export class SlashLeaderProposalFixture implements Fixture {
-  private apiWrapper: ApiWrapper
-  private membersKeyPairs: KeyringPair[]
-  private treasury: KeyringPair
-  private slashAmount: BN
-  private workingGroup: WorkingGroups
-
-  private result: ProposalId | undefined
-
-  constructor(
-    apiWrapper: ApiWrapper,
-    membersKeyPairs: KeyringPair[],
-    treasury: KeyringPair,
-    slashAmount: BN,
-    workingGroup: WorkingGroups
-  ) {
-    this.apiWrapper = apiWrapper
-    this.membersKeyPairs = membersKeyPairs
-    this.treasury = treasury
-    this.slashAmount = slashAmount
-    this.workingGroup = workingGroup
-  }
-
-  public getCreatedProposalId(): ProposalId | undefined {
-    return this.result
-  }
-
-  public async runner(expectFailure: boolean): Promise<void> {
-    // Setup
-    const proposalTitle: string = 'Testing proposal ' + uuid().substring(0, 8)
-    const description: string = 'Testing slash leader stake proposal ' + uuid().substring(0, 8)
-    const workingGroupString: string = this.apiWrapper.getWorkingGroupString(this.workingGroup)
-    const workerId: WorkerId = (await this.apiWrapper.getLeadWorkerId(this.workingGroup))!
-
-    // Proposal stake calculation
-    const proposalStake: BN = new BN(50000)
-    const proposalFee: BN = this.apiWrapper.estimateProposeSlashLeaderStakeFee()
-    await this.apiWrapper.transferBalance(
-      this.treasury,
-      this.membersKeyPairs[0].address,
-      proposalFee.add(proposalStake)
-    )
-
-    // Proposal creation
-    const proposalPromise: Promise<ProposalId> = this.apiWrapper.expectProposalCreated()
-    await this.apiWrapper.proposeSlashLeaderStake(
-      this.membersKeyPairs[0],
-      proposalTitle,
-      description,
-      proposalStake,
-      workerId,
-      this.slashAmount,
-      workingGroupString
-    )
-    this.result = await proposalPromise
-    if (expectFailure) {
-      throw new Error('Successful fixture run while expecting failure')
-    }
-  }
-}
-
-export class WorkingGroupMintCapacityProposalFixture implements Fixture {
-  private apiWrapper: ApiWrapper
-  private membersKeyPairs: KeyringPair[]
-  private treasury: KeyringPair
-  private mintCapacity: BN
-  private workingGroup: WorkingGroups
-
-  private result: ProposalId | undefined
-
-  constructor(
-    apiWrapper: ApiWrapper,
-    membersKeyPairs: KeyringPair[],
-    treasury: KeyringPair,
-    mintCapacity: BN,
-    workingGroup: WorkingGroups
-  ) {
-    this.apiWrapper = apiWrapper
-    this.membersKeyPairs = membersKeyPairs
-    this.treasury = treasury
-    this.mintCapacity = mintCapacity
-    this.workingGroup = workingGroup
-  }
-
-  public getCreatedProposalId(): ProposalId | undefined {
-    return this.result
-  }
-
-  public async runner(expectFailure: boolean): Promise<void> {
-    // Setup
-    const proposalTitle: string = 'Testing proposal ' + uuid().substring(0, 8)
-    const description: string = 'Testing working group mint capacity proposal ' + uuid().substring(0, 8)
-    const workingGroupString: string = this.apiWrapper.getWorkingGroupString(this.workingGroup)
-
-    // Proposal stake calculation
-    const proposalStake: BN = new BN(50000)
-    const proposalFee: BN = this.apiWrapper.estimateProposeWorkingGroupMintCapacityFee()
-    await this.apiWrapper.transferBalance(
-      this.treasury,
-      this.membersKeyPairs[0].address,
-      proposalFee.add(proposalStake)
-    )
-
-    // Proposal creation
-    const proposalPromise: Promise<ProposalId> = this.apiWrapper.expectProposalCreated()
-    await this.apiWrapper.proposeWorkingGroupMintCapacity(
-      this.membersKeyPairs[0],
-      proposalTitle,
-      description,
-      proposalStake,
-      this.mintCapacity,
-      workingGroupString
-    )
-    this.result = await proposalPromise
-    if (expectFailure) {
-      throw new Error('Successful fixture run while expecting failure')
-    }
-  }
-}
-
-export class ElectionParametersProposalFixture implements Fixture {
-  private apiWrapper: ApiWrapper
-  private membersKeyPairs: KeyringPair[]
-  private councilKeyPairs: KeyringPair[]
-  private treasury: KeyringPair
-
-  constructor(
-    apiWrapper: ApiWrapper,
-    membersKeyPairs: KeyringPair[],
-    councilKeyPairs: KeyringPair[],
-    treasury: KeyringPair
-  ) {
-    this.apiWrapper = apiWrapper
-    this.membersKeyPairs = membersKeyPairs
-    this.councilKeyPairs = councilKeyPairs
-    this.treasury = treasury
-  }
-
-  public async runner(expectFailure: boolean): Promise<void> {
-    // Setup
-    const proposalTitle: string = 'Testing proposal ' + uuid().substring(0, 8)
-    const description: string = 'Testing validator count proposal ' + uuid().substring(0, 8)
-    const runtimeVoteFee: BN = this.apiWrapper.estimateVoteForProposalFee()
-    await this.apiWrapper.transferBalanceToAccounts(this.treasury, this.councilKeyPairs, runtimeVoteFee)
-    const announcingPeriod: BN = new BN(28800)
-    const votingPeriod: BN = new BN(14400)
-    const revealingPeriod: BN = new BN(14400)
-    const councilSize: BN = await this.apiWrapper.getCouncilSize()
-    const candidacyLimit: BN = await this.apiWrapper.getCandidacyLimit()
-    const newTermDuration: BN = new BN(144000)
-    const minCouncilStake: BN = await this.apiWrapper.getMinCouncilStake()
-    const minVotingStake: BN = await this.apiWrapper.getMinVotingStake()
-
-    // Proposal stake calculation
-    const proposalStake: BN = new BN(200000)
-    const proposalFee: BN = this.apiWrapper.estimateProposeElectionParametersFee(
-      description,
-      description,
-      proposalStake,
-      announcingPeriod,
-      votingPeriod,
-      revealingPeriod,
-      councilSize,
-      candidacyLimit,
-      newTermDuration,
-      minCouncilStake,
-      minVotingStake
-    )
-    await this.apiWrapper.transferBalance(
-      this.treasury,
-      this.membersKeyPairs[0].address,
-      proposalFee.add(proposalStake)
-    )
-
-    // Proposal creation
-    const proposedAnnouncingPeriod: BN = announcingPeriod.subn(1)
-    const proposedVotingPeriod: BN = votingPeriod.addn(1)
-    const proposedRevealingPeriod: BN = revealingPeriod.addn(1)
-    const proposedCouncilSize: BN = councilSize.addn(1)
-    const proposedCandidacyLimit: BN = candidacyLimit.addn(1)
-    const proposedNewTermDuration: BN = newTermDuration.addn(1)
-    const proposedMinCouncilStake: BN = minCouncilStake.addn(1)
-    const proposedMinVotingStake: BN = minVotingStake.addn(1)
-    const proposalPromise: Promise<ProposalId> = this.apiWrapper.expectProposalCreated()
-    await this.apiWrapper.proposeElectionParameters(
-      this.membersKeyPairs[0],
-      proposalTitle,
-      description,
-      proposalStake,
-      proposedAnnouncingPeriod,
-      proposedVotingPeriod,
-      proposedRevealingPeriod,
-      proposedCouncilSize,
-      proposedCandidacyLimit,
-      proposedNewTermDuration,
-      proposedMinCouncilStake,
-      proposedMinVotingStake
-    )
-    const proposalNumber: ProposalId = await proposalPromise
-
-    // Approving the proposal
-    const proposalExecutionPromise: Promise<void> = this.apiWrapper.expectProposalFinalized()
-    await this.apiWrapper.batchApproveProposal(this.councilKeyPairs, proposalNumber)
-    await proposalExecutionPromise
-
-    // Assertions
-    const newAnnouncingPeriod: BN = await this.apiWrapper.getAnnouncingPeriod()
-    const newVotingPeriod: BN = await this.apiWrapper.getVotingPeriod()
-    const newRevealingPeriod: BN = await this.apiWrapper.getRevealingPeriod()
-    const newCouncilSize: BN = await this.apiWrapper.getCouncilSize()
-    const newCandidacyLimit: BN = await this.apiWrapper.getCandidacyLimit()
-    const newNewTermDuration: BN = await this.apiWrapper.getNewTermDuration()
-    const newMinCouncilStake: BN = await this.apiWrapper.getMinCouncilStake()
-    const newMinVotingStake: BN = await this.apiWrapper.getMinVotingStake()
-    assert(
-      proposedAnnouncingPeriod.eq(newAnnouncingPeriod),
-      `Announcing period has unexpected value ${newAnnouncingPeriod}, expected ${proposedAnnouncingPeriod}`
-    )
-    assert(
-      proposedVotingPeriod.eq(newVotingPeriod),
-      `Voting period has unexpected value ${newVotingPeriod}, expected ${proposedVotingPeriod}`
-    )
-    assert(
-      proposedRevealingPeriod.eq(newRevealingPeriod),
-      `Revealing has unexpected value ${newRevealingPeriod}, expected ${proposedRevealingPeriod}`
-    )
-    assert(
-      proposedCouncilSize.eq(newCouncilSize),
-      `Council size has unexpected value ${newCouncilSize}, expected ${proposedCouncilSize}`
-    )
-    assert(
-      proposedCandidacyLimit.eq(newCandidacyLimit),
-      `Candidacy limit has unexpected value ${newCandidacyLimit}, expected ${proposedCandidacyLimit}`
-    )
-    assert(
-      proposedNewTermDuration.eq(newNewTermDuration),
-      `New term duration has unexpected value ${newNewTermDuration}, expected ${proposedNewTermDuration}`
-    )
-    assert(
-      proposedMinCouncilStake.eq(newMinCouncilStake),
-      `Min council stake has unexpected value ${newMinCouncilStake}, expected ${proposedMinCouncilStake}`
-    )
-    assert(
-      proposedMinVotingStake.eq(newMinVotingStake),
-      `Min voting stake has unexpected value ${newMinVotingStake}, expected ${proposedMinVotingStake}`
-    )
-    if (expectFailure) {
-      throw new Error('Successful fixture run while expecting failure')
-    }
-  }
-}
-
-export class SetLeadProposalFixture implements Fixture {
-  private apiWrapper: ApiWrapper
-  private membersKeyPairs: KeyringPair[]
-  private councilKeyPairs: KeyringPair[]
-  private treasury: KeyringPair
-
-  constructor(
-    apiWrapper: ApiWrapper,
-    membersKeyPairs: KeyringPair[],
-    councilKeyPairs: KeyringPair[],
-    treasury: KeyringPair
-  ) {
-    this.apiWrapper = apiWrapper
-    this.membersKeyPairs = membersKeyPairs
-    this.councilKeyPairs = councilKeyPairs
-    this.treasury = treasury
-  }
-
-  public async runner(expectFailure: boolean): Promise<void> {
-    // Setup
-    const proposalTitle: string = 'Testing proposal ' + uuid().substring(0, 8)
-    const description: string = 'Testing validator count proposal ' + uuid().substring(0, 8)
-    const runtimeVoteFee: BN = this.apiWrapper.estimateVoteForProposalFee()
-    await this.apiWrapper.transferBalanceToAccounts(this.treasury, this.councilKeyPairs, runtimeVoteFee)
-
-    // Proposal stake calculation
-    const proposalStake: BN = new BN(50000)
-    const proposalFee: BN = this.apiWrapper.estimateProposeLeadFee(
-      description,
-      description,
-      proposalStake,
-      this.treasury.address
-    )
-    await this.apiWrapper.transferBalance(
-      this.treasury,
-      this.membersKeyPairs[0].address,
-      proposalFee.add(proposalStake)
-    )
-
-    // Proposal creation
-    const proposalPromise: Promise<ProposalId> = this.apiWrapper.expectProposalCreated()
-    await this.apiWrapper.proposeLead(
-      this.membersKeyPairs[0],
-      proposalTitle,
-      description,
-      proposalStake,
-      this.membersKeyPairs[1]
-    )
-    const proposalNumber: ProposalId = await proposalPromise
-
-    // Approving the proposal
-    const proposalExecutionPromise: Promise<void> = this.apiWrapper.expectProposalFinalized()
-    await this.apiWrapper.batchApproveProposal(this.councilKeyPairs, proposalNumber)
-    await proposalExecutionPromise
-    const newLead: string = await this.apiWrapper.getCurrentLeadAddress()
-    assert(
-      newLead === this.membersKeyPairs[1].address,
-      `New lead has unexpected value ${newLead}, expected ${this.membersKeyPairs[1].address}`
-    )
-    if (expectFailure) {
-      throw new Error('Successful fixture run while expecting failure')
-    }
-  }
-}
-
-export class SpendingProposalFixture implements Fixture {
-  private apiWrapper: ApiWrapper
-  private membersKeyPairs: KeyringPair[]
-  private councilKeyPairs: KeyringPair[]
-  private sudo: KeyringPair
-  private spendingBalance: BN
-  private mintCapacity: BN
-
-  constructor(
-    apiWrapper: ApiWrapper,
-    membersKeyPairs: KeyringPair[],
-    councilKeyPairs: KeyringPair[],
-    sudo: KeyringPair,
-    spendingBalance: BN,
-    mintCapacity: BN
-  ) {
-    this.apiWrapper = apiWrapper
-    this.membersKeyPairs = membersKeyPairs
-    this.councilKeyPairs = councilKeyPairs
-    this.sudo = sudo
-    this.spendingBalance = spendingBalance
-    this.mintCapacity = mintCapacity
-  }
-
-  public async runner(expectFailure: boolean): Promise<void> {
-    // Setup
-    const description = 'spending proposal which is used for API network testing with some mock data'
-    const runtimeVoteFee: BN = this.apiWrapper.estimateVoteForProposalFee()
-
-    // Topping the balances
-    const proposalStake: BN = new BN(25000)
-    const runtimeProposalFee: BN = this.apiWrapper.estimateProposeSpendingFee(
-      description,
-      description,
-      proposalStake,
-      this.spendingBalance,
-      this.sudo.address
-    )
-    await this.apiWrapper.transferBalance(
-      this.sudo,
-      this.membersKeyPairs[0].address,
-      runtimeProposalFee.add(proposalStake)
-    )
-    await this.apiWrapper.transferBalanceToAccounts(this.sudo, this.councilKeyPairs, runtimeVoteFee)
-    await this.apiWrapper.sudoSetCouncilMintCapacity(this.sudo, this.mintCapacity)
-
-    // Proposal creation
-    const proposalPromise: Promise<ProposalId> = this.apiWrapper.expectProposalCreated()
-    await this.apiWrapper.proposeSpending(
-      this.membersKeyPairs[0],
-      'testing spending' + uuid().substring(0, 8),
-      'spending to test proposal functionality' + uuid().substring(0, 8),
-      proposalStake,
-      this.spendingBalance,
-      this.sudo.address
-    )
-    const proposalNumber: ProposalId = await proposalPromise
-
-    // Approving spending proposal
-    const balanceBeforeMinting: BN = await this.apiWrapper.getBalance(this.sudo.address)
-    const spendingPromise: Promise<void> = this.apiWrapper.expectProposalFinalized()
-    await this.apiWrapper.batchApproveProposal(this.councilKeyPairs, proposalNumber)
-    await spendingPromise
-    const balanceAfterMinting: BN = await this.apiWrapper.getBalance(this.sudo.address)
-    assert(
-      balanceAfterMinting.sub(balanceBeforeMinting).eq(this.spendingBalance),
-      `member ${
-        this.membersKeyPairs[0].address
-      } has unexpected balance ${balanceAfterMinting}, expected ${balanceBeforeMinting.add(this.spendingBalance)}`
-    )
-    if (expectFailure) {
-      throw new Error('Successful fixture run while expecting failure')
-    }
-  }
-}
-
-export class TextProposalFixture implements Fixture {
-  private apiWrapper: ApiWrapper
-  private membersKeyPairs: KeyringPair[]
-  private councilKeyPairs: KeyringPair[]
-  private treasury: KeyringPair
-
-  constructor(
-    apiWrapper: ApiWrapper,
-    membersKeyPairs: KeyringPair[],
-    councilKeyPairs: KeyringPair[],
-    treasury: KeyringPair
-  ) {
-    this.apiWrapper = apiWrapper
-    this.membersKeyPairs = membersKeyPairs
-    this.councilKeyPairs = councilKeyPairs
-    this.treasury = treasury
-  }
-
-  public async runner(expectFailure: boolean): Promise<void> {
-    // Setup
-    const proposalTitle: string = 'Testing proposal ' + uuid().substring(0, 8)
-    const description: string = 'Testing text proposal ' + uuid().substring(0, 8)
-    const proposalText: string = 'Text of the testing proposal ' + uuid().substring(0, 8)
-    const runtimeVoteFee: BN = this.apiWrapper.estimateVoteForProposalFee()
-    await this.apiWrapper.transferBalanceToAccounts(this.treasury, this.councilKeyPairs, runtimeVoteFee)
-
-    // Proposal stake calculation
-    const proposalStake: BN = new BN(25000)
-    const runtimeProposalFee: BN = this.apiWrapper.estimateProposeTextFee(
-      proposalStake,
-      description,
-      description,
-      proposalText
-    )
-    await this.apiWrapper.transferBalance(
-      this.treasury,
-      this.membersKeyPairs[0].address,
-      runtimeProposalFee.add(proposalStake)
-    )
-
-    // Proposal creation
-    const proposalPromise: Promise<ProposalId> = this.apiWrapper.expectProposalCreated()
-    await this.apiWrapper.proposeText(this.membersKeyPairs[0], proposalStake, proposalTitle, description, proposalText)
-    const proposalNumber: ProposalId = await proposalPromise
-
-    // Approving text proposal
-    const textProposalPromise: Promise<void> = this.apiWrapper.expectProposalFinalized()
-    await this.apiWrapper.batchApproveProposal(this.councilKeyPairs, proposalNumber)
-    await textProposalPromise
-    if (expectFailure) {
-      throw new Error('Successful fixture run while expecting failure')
-    }
-  }
-}
-
-export class ValidatorCountProposalFixture implements Fixture {
-  private apiWrapper: ApiWrapper
-  private membersKeyPairs: KeyringPair[]
-  private councilKeyPairs: KeyringPair[]
-  private treasury: KeyringPair
-  private validatorCountIncrement: BN
-
-  constructor(
-    apiWrapper: ApiWrapper,
-    membersKeyPairs: KeyringPair[],
-    councilKeyPairs: KeyringPair[],
-    treasury: KeyringPair,
-    validatorCountIncrement: BN
-  ) {
-    this.apiWrapper = apiWrapper
-    this.membersKeyPairs = membersKeyPairs
-    this.councilKeyPairs = councilKeyPairs
-    this.treasury = treasury
-    this.validatorCountIncrement = validatorCountIncrement
-  }
-
-  public async runner(expectFailure: boolean): Promise<void> {
-    // Setup
-    const proposalTitle: string = 'Testing proposal ' + uuid().substring(0, 8)
-    const description: string = 'Testing validator count proposal ' + uuid().substring(0, 8)
-    const runtimeVoteFee: BN = this.apiWrapper.estimateVoteForProposalFee()
-    await this.apiWrapper.transferBalanceToAccounts(this.treasury, this.councilKeyPairs, runtimeVoteFee)
-
-    // Proposal stake calculation
-    const proposalStake: BN = new BN(100000)
-    const proposalFee: BN = this.apiWrapper.estimateProposeValidatorCountFee(description, description, proposalStake)
-    await this.apiWrapper.transferBalance(
-      this.treasury,
-      this.membersKeyPairs[0].address,
-      proposalFee.add(proposalStake)
-    )
-    const validatorCount: BN = await this.apiWrapper.getValidatorCount()
-
-    // Proposal creation
-    const proposedValidatorCount: BN = validatorCount.add(this.validatorCountIncrement)
-    const proposalPromise: Promise<ProposalId> = this.apiWrapper.expectProposalCreated()
-    await this.apiWrapper.proposeValidatorCount(
-      this.membersKeyPairs[0],
-      proposalTitle,
-      description,
-      proposalStake,
-      proposedValidatorCount
-    )
-    const proposalNumber: ProposalId = await proposalPromise
-
-    // Approving the proposal
-    const proposalExecutionPromise: Promise<void> = this.apiWrapper.expectProposalFinalized()
-    await this.apiWrapper.batchApproveProposal(this.councilKeyPairs, proposalNumber)
-    await proposalExecutionPromise
-    const newValidatorCount: BN = await this.apiWrapper.getValidatorCount()
-    assert(
-      proposedValidatorCount.eq(newValidatorCount),
-      `Validator count has unexpeccted value ${newValidatorCount}, expected ${proposedValidatorCount}`
-    )
-    if (expectFailure) {
-      throw new Error('Successful fixture run while expecting failure')
-    }
-  }
-}
-
-export class ContentWorkingGroupMintCapacityProposalFixture implements Fixture {
-  private apiWrapper: ApiWrapper
-  private membersKeyPairs: KeyringPair[]
-  private councilKeyPairs: KeyringPair[]
-  private treasury: KeyringPair
-  private mintingCapacityIncrement: BN
-
-  constructor(
-    apiWrapper: ApiWrapper,
-    membersKeyPairs: KeyringPair[],
-    councilKeyPairs: KeyringPair[],
-    treasury: KeyringPair,
-    mintingCapacityIncrement: BN
-  ) {
-    this.apiWrapper = apiWrapper
-    this.membersKeyPairs = membersKeyPairs
-    this.councilKeyPairs = councilKeyPairs
-    this.treasury = treasury
-    this.mintingCapacityIncrement = mintingCapacityIncrement
-  }
-
-  public async runner(expectFailure: boolean): Promise<void> {
-    // Setup
-    const description = 'Mint capacity proposal which is used for API network testing'
-    const runtimeVoteFee: BN = this.apiWrapper.estimateVoteForProposalFee()
-    const initialMintingCapacity: BN = await this.apiWrapper.getContentWorkingGroupMintCapacity()
-
-    // Topping the balances
-    const proposalStake: BN = new BN(50000)
-    const runtimeProposalFee: BN = this.apiWrapper.estimateProposeContentWorkingGroupMintCapacityFee(
-      description,
-      description,
-      proposalStake,
-      initialMintingCapacity.add(this.mintingCapacityIncrement)
-    )
-    await this.apiWrapper.transferBalance(
-      this.treasury,
-      this.membersKeyPairs[0].address,
-      runtimeProposalFee.add(proposalStake)
-    )
-    await this.apiWrapper.transferBalanceToAccounts(this.treasury, this.councilKeyPairs, runtimeVoteFee)
-
-    // Proposal creation
-    const proposedMintingCapacity: BN = initialMintingCapacity.add(this.mintingCapacityIncrement)
-    const proposalPromise: Promise<ProposalId> = this.apiWrapper.expectProposalCreated()
-    await this.apiWrapper.proposeContentWorkingGroupMintCapacity(
-      this.membersKeyPairs[0],
-      'testing mint capacity' + uuid().substring(0, 8),
-      'mint capacity to test proposal functionality' + uuid().substring(0, 8),
-      proposalStake,
-      proposedMintingCapacity
-    )
-    const proposalNumber: ProposalId = await proposalPromise
-
-    // Approving mint capacity proposal
-    const mintCapacityPromise: Promise<void> = this.apiWrapper.expectProposalFinalized()
-    await this.apiWrapper.batchApproveProposal(this.councilKeyPairs, proposalNumber)
-    await mintCapacityPromise
-    const newMintingCapacity: BN = await this.apiWrapper.getContentWorkingGroupMintCapacity()
-    assert(
-      proposedMintingCapacity.eq(newMintingCapacity),
-      `Content working group has unexpected minting capacity ${newMintingCapacity}, expected ${proposedMintingCapacity}`
-    )
-    if (expectFailure) {
-      throw new Error('Successful fixture run while expecting failure')
-    }
-  }
-}
-
-export class UpdateRuntimeFixture implements Fixture {
-  private apiWrapper: ApiWrapper
-  private membersKeyPairs: KeyringPair[]
-  private councilKeyPairs: KeyringPair[]
-  private treasury: KeyringPair
-  private runtimePath: string
-
-  constructor(
-    apiWrapper: ApiWrapper,
-    membersKeyPairs: KeyringPair[],
-    councilKeyPairs: KeyringPair[],
-    treasury: KeyringPair,
-    runtimePath: string
-  ) {
-    this.apiWrapper = apiWrapper
-    this.membersKeyPairs = membersKeyPairs
-    this.councilKeyPairs = councilKeyPairs
-    this.treasury = treasury
-    this.runtimePath = runtimePath
-  }
-
-  public async runner(expectFailure: boolean): Promise<void> {
-    // Setup
-    const runtime: string = Utils.readRuntimeFromFile(this.runtimePath)
-    const description = 'runtime upgrade proposal which is used for API network testing'
-    const runtimeVoteFee: BN = this.apiWrapper.estimateVoteForProposalFee()
-
-    // Topping the balances
-    const proposalStake: BN = new BN(1000000)
-    const runtimeProposalFee: BN = this.apiWrapper.estimateProposeRuntimeUpgradeFee(
-      proposalStake,
-      description,
-      description,
-      runtime
-    )
-    await this.apiWrapper.transferBalance(
-      this.treasury,
-      this.membersKeyPairs[0].address,
-      runtimeProposalFee.add(proposalStake)
-    )
-    await this.apiWrapper.transferBalanceToAccounts(this.treasury, this.councilKeyPairs, runtimeVoteFee)
-
-    // Proposal creation
-    const proposalPromise: Promise<ProposalId> = this.apiWrapper.expectProposalCreated()
-    await this.apiWrapper.proposeRuntime(
-      this.membersKeyPairs[0],
-      proposalStake,
-      'testing runtime' + uuid().substring(0, 8),
-      'runtime to test proposal functionality' + uuid().substring(0, 8),
-      runtime
-    )
-    const proposalNumber: ProposalId = await proposalPromise
-
-    // Approving runtime update proposal
-    const runtimePromise: Promise<void> = this.apiWrapper.expectProposalFinalized()
-    await this.apiWrapper.batchApproveProposal(this.councilKeyPairs, proposalNumber)
-    await runtimePromise
-    if (expectFailure) {
-      throw new Error('Successful fixture run while expecting failure')
-    }
-  }
-}
-
-export class VoteForProposalFixture implements Fixture {
-  private apiWrapper: ApiWrapper
-  private councilKeyPairs: KeyringPair[]
-  private treasury: KeyringPair
-  private proposalNumber: ProposalId
-
-  constructor(
-    apiWrapper: ApiWrapper,
-    councilKeyPairs: KeyringPair[],
-    treasury: KeyringPair,
-    proposalNumber: ProposalId
-  ) {
-    this.apiWrapper = apiWrapper
-    this.councilKeyPairs = councilKeyPairs
-    this.treasury = treasury
-    this.proposalNumber = proposalNumber
-  }
-
-  public async runner(expectFailure: boolean): Promise<void> {
-    const proposalVoteFee: BN = this.apiWrapper.estimateVoteForProposalFee()
-    await this.apiWrapper.transferBalanceToAccounts(this.treasury, this.councilKeyPairs, proposalVoteFee)
-
-    // Approving the proposal
-    const proposalExecutionPromise: Promise<void> = this.apiWrapper.expectProposalFinalized()
-    await this.apiWrapper.batchApproveProposal(this.councilKeyPairs, this.proposalNumber)
-    await proposalExecutionPromise
-    if (expectFailure) {
-      throw new Error('Successful fixture run while expecting failure')
-    }
-  }
-}

+ 0 - 1147
tests/network-tests/src/tests/fixtures/workingGroupModule.ts

@@ -1,1147 +0,0 @@
-import BN from 'bn.js'
-import { assert } from 'chai'
-import { ApiWrapper, WorkingGroups } from '../../utils/apiWrapper'
-import { KeyringPair } from '@polkadot/keyring/types'
-import { Event } from '@polkadot/types/interfaces'
-import { Keyring } from '@polkadot/api'
-import { v4 as uuid } from 'uuid'
-import { RewardRelationship } from '@joystream/types/recurring-rewards'
-import { Application, ApplicationIdToWorkerIdMap, Worker, WorkerId } from '@joystream/types/working-group'
-import { Utils } from '../../utils/utils'
-import { ApplicationId, Opening as HiringOpening, OpeningId } from '@joystream/types/hiring'
-import { Fixture } from './interfaces/fixture'
-
-export class AddWorkerOpeningFixture implements Fixture {
-  private apiWrapper: ApiWrapper
-  private membersKeyPairs: KeyringPair[]
-  private lead: KeyringPair
-  private treasury: KeyringPair
-  private applicationStake: BN
-  private roleStake: BN
-  private activationDelay: BN
-  private unstakingPeriod: BN
-  private module: WorkingGroups
-
-  private result: OpeningId | undefined
-
-  public getCreatedOpeningId(): OpeningId | undefined {
-    return this.result
-  }
-
-  public constructor(
-    apiWrapper: ApiWrapper,
-    membersKeyPairs: KeyringPair[],
-    lead: KeyringPair,
-    treasury: KeyringPair,
-    applicationStake: BN,
-    roleStake: BN,
-    activationDelay: BN,
-    unstakingPeriod: BN,
-    module: WorkingGroups
-  ) {
-    this.apiWrapper = apiWrapper
-    this.membersKeyPairs = membersKeyPairs
-    this.lead = lead
-    this.treasury = treasury
-    this.applicationStake = applicationStake
-    this.roleStake = roleStake
-    this.activationDelay = activationDelay
-    this.unstakingPeriod = unstakingPeriod
-    this.module = module
-  }
-
-  public async runner(expectFailure: boolean): Promise<void> {
-    // Fee estimation and transfer
-    const addOpeningFee: BN = this.apiWrapper.estimateAddOpeningFee(this.module)
-    await this.apiWrapper.transferBalance(this.treasury, this.lead.address, addOpeningFee)
-
-    // Worker opening creation
-    const addOpeningPromise: Promise<Event> = this.apiWrapper.expectEvent('OpeningAdded')
-    await this.apiWrapper.addOpening({
-      leader: this.lead,
-      activationDelay: this.activationDelay,
-      maxActiveApplicants: new BN(this.membersKeyPairs.length),
-      maxReviewPeriodLength: new BN(32),
-      applicationStakingPolicyAmount: this.applicationStake,
-      applicationCrowdedOutUnstakingPeriodLength: new BN(1),
-      applicationReviewPeriodExpiredUnstakingPeriodLength: new BN(1),
-      roleStakingPolicyAmount: this.roleStake,
-      roleCrowdedOutUnstakingPeriodLength: new BN(1),
-      roleReviewPeriodExpiredUnstakingPeriodLength: new BN(1),
-      slashableMaxCount: new BN(1),
-      slashableMaxPercentPtsPerTime: new BN(100),
-      fillOpeningSuccessfulApplicantApplicationStakeUnstakingPeriod: this.unstakingPeriod,
-      fillOpeningFailedApplicantApplicationStakeUnstakingPeriod: this.unstakingPeriod,
-      fillOpeningFailedApplicantRoleStakeUnstakingPeriod: this.unstakingPeriod,
-      terminateApplicationStakeUnstakingPeriod: this.unstakingPeriod,
-      terminateRoleStakeUnstakingPeriod: this.unstakingPeriod,
-      exitRoleApplicationStakeUnstakingPeriod: this.unstakingPeriod,
-      exitRoleStakeUnstakingPeriod: this.unstakingPeriod,
-      text: uuid().substring(0, 8),
-      type: 'Worker',
-      module: this.module,
-      expectFailure: expectFailure,
-    })
-    if (!expectFailure) {
-      const openingId: OpeningId = (await addOpeningPromise).data[0] as OpeningId
-      this.result = openingId
-    }
-  }
-}
-
-export class AddLeaderOpeningFixture implements Fixture {
-  private apiWrapper: ApiWrapper
-  private membersKeyPairs: KeyringPair[]
-  private sudo: KeyringPair
-  private applicationStake: BN
-  private roleStake: BN
-  private activationDelay: BN
-  private module: WorkingGroups
-
-  private result: OpeningId | undefined
-
-  public getCreatedOpeningId(): OpeningId | undefined {
-    return this.result
-  }
-
-  public constructor(
-    apiWrapper: ApiWrapper,
-    membersKeyPairs: KeyringPair[],
-    sudo: KeyringPair,
-    applicationStake: BN,
-    roleStake: BN,
-    activationDelay: BN,
-    module: WorkingGroups
-  ) {
-    this.apiWrapper = apiWrapper
-    this.membersKeyPairs = membersKeyPairs
-    this.sudo = sudo
-    this.applicationStake = applicationStake
-    this.roleStake = roleStake
-    this.activationDelay = activationDelay
-    this.module = module
-  }
-
-  public async runner(expectFailure: boolean): Promise<void> {
-    const addOpeningPromise: Promise<Event> = this.apiWrapper.expectEvent('OpeningAdded')
-    await this.apiWrapper.sudoAddOpening({
-      sudo: this.sudo,
-      activationDelay: this.activationDelay,
-      maxActiveApplicants: new BN(this.membersKeyPairs.length),
-      maxReviewPeriodLength: new BN(32),
-      applicationStakingPolicyAmount: this.applicationStake,
-      applicationCrowdedOutUnstakingPeriodLength: new BN(1),
-      applicationReviewPeriodExpiredUnstakingPeriodLength: new BN(1),
-      roleStakingPolicyAmount: this.roleStake,
-      roleCrowdedOutUnstakingPeriodLength: new BN(1),
-      roleReviewPeriodExpiredUnstakingPeriodLength: new BN(1),
-      slashableMaxCount: new BN(1),
-      slashableMaxPercentPtsPerTime: new BN(100),
-      fillOpeningSuccessfulApplicantApplicationStakeUnstakingPeriod: new BN(1),
-      fillOpeningFailedApplicantApplicationStakeUnstakingPeriod: new BN(1),
-      fillOpeningFailedApplicantRoleStakeUnstakingPeriod: new BN(1),
-      terminateApplicationStakeUnstakingPeriod: new BN(1),
-      terminateRoleStakeUnstakingPeriod: new BN(1),
-      exitRoleApplicationStakeUnstakingPeriod: new BN(1),
-      exitRoleStakeUnstakingPeriod: new BN(1),
-      text: uuid().substring(0, 8),
-      type: 'Leader',
-      module: this.module,
-    })
-    this.result = (await addOpeningPromise).data[0] as OpeningId
-    if (expectFailure) {
-      throw new Error('Successful fixture run while expecting failure')
-    }
-  }
-}
-
-export class AcceptApplicationsFixture implements Fixture {
-  private apiWrapper: ApiWrapper
-  private lead: KeyringPair
-  private treasury: KeyringPair
-  private openingId: OpeningId
-  private module: WorkingGroups
-
-  public constructor(
-    apiWrapper: ApiWrapper,
-    lead: KeyringPair,
-    treasury: KeyringPair,
-    openingId: OpeningId,
-    module: WorkingGroups
-  ) {
-    this.apiWrapper = apiWrapper
-    this.lead = lead
-    this.treasury = treasury
-    this.openingId = openingId
-    this.module = module
-  }
-
-  public async runner(expectFailure: boolean): Promise<void> {
-    // Fee estimation and transfer
-    const acceptApplicationsFee: BN = this.apiWrapper.estimateAcceptApplicationsFee(this.module)
-    await this.apiWrapper.transferBalance(this.treasury, this.lead.address, acceptApplicationsFee)
-
-    // Begin accepting applications
-    await this.apiWrapper.acceptApplications(this.lead, this.openingId, this.module)
-
-    const opening: HiringOpening = await this.apiWrapper.getHiringOpening(this.openingId)
-    assert(opening.is_active, `Opening ${this.openingId} is not active`)
-    if (expectFailure) {
-      throw new Error('Successful fixture run while expecting failure')
-    }
-  }
-}
-
-export class ApplyForOpeningFixture implements Fixture {
-  private apiWrapper: ApiWrapper
-  private membersKeyPairs: KeyringPair[]
-  private treasury: KeyringPair
-  private applicationStake: BN
-  private roleStake: BN
-  private openingId: OpeningId
-  private module: WorkingGroups
-
-  public constructor(
-    apiWrapper: ApiWrapper,
-    membersKeyPairs: KeyringPair[],
-    treasury: KeyringPair,
-    applicationStake: BN,
-    roleStake: BN,
-    openingId: OpeningId,
-    module: WorkingGroups
-  ) {
-    this.apiWrapper = apiWrapper
-    this.membersKeyPairs = membersKeyPairs
-    this.treasury = treasury
-    this.applicationStake = applicationStake
-    this.roleStake = roleStake
-    this.openingId = openingId
-    this.module = module
-  }
-
-  public async runner(expectFailure: boolean): Promise<void> {
-    // Fee estimation and transfer
-    const applyOnOpeningFee: BN = this.apiWrapper
-      .estimateApplyOnOpeningFee(this.treasury, this.module)
-      .add(this.applicationStake)
-      .add(this.roleStake)
-    await this.apiWrapper.transferBalanceToAccounts(this.treasury, this.membersKeyPairs, applyOnOpeningFee)
-
-    // Applying for created worker opening
-    await this.apiWrapper.batchApplyOnOpening(
-      this.membersKeyPairs,
-      this.openingId,
-      this.roleStake,
-      this.applicationStake,
-      uuid().substring(0, 8),
-      this.module,
-      expectFailure
-    )
-  }
-}
-
-export class WithdrawApplicationFixture implements Fixture {
-  private apiWrapper: ApiWrapper
-  private membersKeyPairs: KeyringPair[]
-  private treasury: KeyringPair
-  private module: WorkingGroups
-
-  constructor(apiWrapper: ApiWrapper, membersKeyPairs: KeyringPair[], treasury: KeyringPair, module: WorkingGroups) {
-    this.apiWrapper = apiWrapper
-    this.membersKeyPairs = membersKeyPairs
-    this.treasury = treasury
-    this.module = module
-  }
-
-  public async runner(expectFailure: boolean): Promise<void> {
-    // Fee estimation and transfer
-    const withdrawApplicaitonFee: BN = this.apiWrapper.estimateWithdrawApplicationFee(this.module)
-    await this.apiWrapper.transferBalanceToAccounts(this.treasury, this.membersKeyPairs, withdrawApplicaitonFee)
-
-    // Application withdrawal
-    await this.apiWrapper.batchWithdrawApplication(this.membersKeyPairs, this.module)
-
-    // Assertions
-    this.membersKeyPairs.forEach(async (keyPair) => {
-      const activeApplications: ApplicationId[] = await this.apiWrapper.getActiveApplicationsIdsByRoleAccount(
-        keyPair.address,
-        this.module
-      )
-      assert(activeApplications.length === 0, `Unexpected active application found for ${keyPair.address}`)
-    })
-    if (expectFailure) {
-      throw new Error('Successful fixture run while expecting failure')
-    }
-  }
-}
-
-export class BeginApplicationReviewFixture implements Fixture {
-  private apiWrapper: ApiWrapper
-  private lead: KeyringPair
-  private treasury: KeyringPair
-  private openingId: OpeningId
-  private module: WorkingGroups
-
-  constructor(
-    apiWrapper: ApiWrapper,
-    lead: KeyringPair,
-    treasury: KeyringPair,
-    openingId: OpeningId,
-    module: WorkingGroups
-  ) {
-    this.apiWrapper = apiWrapper
-    this.lead = lead
-    this.treasury = treasury
-    this.openingId = openingId
-    this.module = module
-  }
-
-  public async runner(expectFailure: boolean): Promise<void> {
-    // Fee estimation and transfer
-    const beginReviewFee: BN = this.apiWrapper.estimateBeginApplicantReviewFee(this.module)
-    await this.apiWrapper.transferBalance(this.treasury, this.lead.address, beginReviewFee)
-
-    // Begin application review
-    const beginApplicantReviewPromise: Promise<ApplicationId> = this.apiWrapper.expectApplicationReviewBegan()
-    await this.apiWrapper.beginApplicantReview(this.lead, this.openingId, this.module)
-    await beginApplicantReviewPromise
-    if (expectFailure) {
-      throw new Error('Successful fixture run while expecting failure')
-    }
-  }
-}
-
-export class BeginLeaderApplicationReviewFixture implements Fixture {
-  private apiWrapper: ApiWrapper
-  private sudo: KeyringPair
-  private openingId: OpeningId
-  private module: WorkingGroups
-
-  constructor(apiWrapper: ApiWrapper, sudo: KeyringPair, openingId: OpeningId, module: WorkingGroups) {
-    this.apiWrapper = apiWrapper
-    this.sudo = sudo
-    this.openingId = openingId
-    this.module = module
-  }
-
-  public async runner(expectFailure: boolean): Promise<void> {
-    // Begin application review
-    await this.apiWrapper.sudoBeginApplicantReview(this.sudo, this.openingId, this.module)
-    if (expectFailure) {
-      throw new Error('Successful fixture run while expecting failure')
-    }
-  }
-}
-
-export class FillOpeningFixture implements Fixture {
-  private apiWrapper: ApiWrapper
-  private membersKeyPairs: KeyringPair[]
-  private lead: KeyringPair
-  private treasury: KeyringPair
-  private openingId: OpeningId
-  private firstPayoutInterval: BN
-  private payoutInterval: BN
-  private amountPerPayout: BN
-  private module: WorkingGroups
-
-  constructor(
-    apiWrapper: ApiWrapper,
-    membersKeyPairs: KeyringPair[],
-    lead: KeyringPair,
-    treasury: KeyringPair,
-    openingId: OpeningId,
-    firstPayoutInterval: BN,
-    payoutInterval: BN,
-    amountPerPayout: BN,
-    module: WorkingGroups
-  ) {
-    this.apiWrapper = apiWrapper
-    this.membersKeyPairs = membersKeyPairs
-    this.lead = lead
-    this.treasury = treasury
-    this.openingId = openingId
-    this.firstPayoutInterval = firstPayoutInterval
-    this.payoutInterval = payoutInterval
-    this.amountPerPayout = amountPerPayout
-    this.module = module
-  }
-
-  public async runner(expectFailure: boolean): Promise<void> {
-    // Fee estimation and transfer
-    const beginReviewFee: BN = this.apiWrapper.estimateFillOpeningFee(this.module)
-    await this.apiWrapper.transferBalance(this.treasury, this.lead.address, beginReviewFee)
-    const applicationIds: ApplicationId[] = (
-      await Promise.all(
-        this.membersKeyPairs.map(async (keypair) =>
-          this.apiWrapper.getActiveApplicationsIdsByRoleAccount(keypair.address, this.module)
-        )
-      )
-    ).flat()
-    // Assert max number of workers is not exceeded
-    const activeWorkersCount: BN = await this.apiWrapper.getActiveWorkersCount(this.module)
-    const maxWorkersCount: BN = this.apiWrapper.getMaxWorkersCount(this.module)
-    assert(
-      activeWorkersCount.addn(applicationIds.length).lte(maxWorkersCount),
-      `The number of workers ${activeWorkersCount.addn(
-        applicationIds.length
-      )} will exceed max workers count ${maxWorkersCount}`
-    )
-
-    // Fill worker opening
-    const now: BN = await this.apiWrapper.getBestBlock()
-    const fillOpeningPromise: Promise<ApplicationIdToWorkerIdMap> = this.apiWrapper.expectOpeningFilled()
-    await this.apiWrapper.fillOpening(
-      this.lead,
-      this.openingId,
-      applicationIds,
-      this.amountPerPayout,
-      now.add(this.firstPayoutInterval),
-      this.payoutInterval,
-      this.module
-    )
-    const applicationIdToWorkerIdMap: ApplicationIdToWorkerIdMap = await fillOpeningPromise
-
-    // Assertions
-    applicationIdToWorkerIdMap.forEach(async (workerId, applicationId) => {
-      const worker: Worker = await this.apiWrapper.getWorkerById(workerId, this.module)
-      const application: Application = await this.apiWrapper.getApplicationById(applicationId, this.module)
-      assert(
-        worker.role_account_id.toString() === application.role_account_id.toString(),
-        `Role account ids does not match, worker account: ${worker.role_account_id}, application account ${application.role_account_id}`
-      )
-    })
-    const openingWorkersAccounts: string[] = (await this.apiWrapper.getWorkers(this.module)).map((worker) =>
-      worker.role_account_id.toString()
-    )
-    this.membersKeyPairs.forEach((keyPair) =>
-      assert(openingWorkersAccounts.includes(keyPair.address), `Account ${keyPair.address} is not worker`)
-    )
-    if (expectFailure) {
-      throw new Error('Successful fixture run while expecting failure')
-    }
-  }
-}
-
-export class FillLeaderOpeningFixture implements Fixture {
-  private apiWrapper: ApiWrapper
-  private membersKeyPairs: KeyringPair[]
-  private sudo: KeyringPair
-  private openingId: OpeningId
-  private firstPayoutInterval: BN
-  private payoutInterval: BN
-  private amountPerPayout: BN
-  private module: WorkingGroups
-
-  constructor(
-    apiWrapper: ApiWrapper,
-    membersKeyPairs: KeyringPair[],
-    sudo: KeyringPair,
-    openingId: OpeningId,
-    firstPayoutInterval: BN,
-    payoutInterval: BN,
-    amountPerPayout: BN,
-    module: WorkingGroups
-  ) {
-    this.apiWrapper = apiWrapper
-    this.membersKeyPairs = membersKeyPairs
-    this.sudo = sudo
-    this.openingId = openingId
-    this.firstPayoutInterval = firstPayoutInterval
-    this.payoutInterval = payoutInterval
-    this.amountPerPayout = amountPerPayout
-    this.module = module
-  }
-
-  public async runner(expectFailure: boolean): Promise<void> {
-    const applicationIds: ApplicationId[] = (
-      await Promise.all(
-        this.membersKeyPairs.map(async (keypair) =>
-          this.apiWrapper.getActiveApplicationsIdsByRoleAccount(keypair.address, this.module)
-        )
-      )
-    ).flat()
-
-    // Fill leader opening
-    const now: BN = await this.apiWrapper.getBestBlock()
-    const fillOpeningPromise: Promise<ApplicationIdToWorkerIdMap> = this.apiWrapper.expectOpeningFilled()
-    await this.apiWrapper.sudoFillOpening(
-      this.sudo,
-      this.openingId,
-      applicationIds,
-      this.amountPerPayout,
-      now.add(this.firstPayoutInterval),
-      this.payoutInterval,
-      this.module
-    )
-
-    // Assertions
-    const applicationIdToWorkerIdMap: ApplicationIdToWorkerIdMap = await fillOpeningPromise
-    applicationIdToWorkerIdMap.forEach(async (workerId, applicationId) => {
-      const worker: Worker = await this.apiWrapper.getWorkerById(workerId, this.module)
-      const application: Application = await this.apiWrapper.getApplicationById(applicationId, this.module)
-      assert(
-        worker.role_account_id.toString() === application.role_account_id.toString(),
-        `Role account ids does not match, leader account: ${worker.role_account_id}, application account ${application.role_account_id}`
-      )
-    })
-    const leadWorkerId: WorkerId = (await this.apiWrapper.getLeadWorkerId(this.module))!
-    const openingLeaderAccount: string = (
-      await this.apiWrapper.getWorkerById(leadWorkerId, this.module)
-    ).role_account_id.toString()
-    assert(
-      openingLeaderAccount === this.membersKeyPairs[0].address,
-      `Unexpected leader account ${openingLeaderAccount}, expected ${this.membersKeyPairs[0].address}`
-    )
-    if (expectFailure) {
-      throw new Error('Successful fixture run while expecting failure')
-    }
-  }
-}
-
-export class IncreaseStakeFixture implements Fixture {
-  private apiWrapper: ApiWrapper
-  private membersKeyPairs: KeyringPair[]
-  private treasury: KeyringPair
-  private module: WorkingGroups
-
-  constructor(apiWrapper: ApiWrapper, membersKeyPairs: KeyringPair[], treasury: KeyringPair, module: WorkingGroups) {
-    this.apiWrapper = apiWrapper
-    this.membersKeyPairs = membersKeyPairs
-    this.treasury = treasury
-    this.module = module
-  }
-
-  public async runner(expectFailure: boolean): Promise<void> {
-    // Fee estimation and transfer
-    const increaseStakeFee: BN = this.apiWrapper.estimateIncreaseStakeFee(this.module)
-    const stakeIncrement: BN = new BN(1)
-    await this.apiWrapper.transferBalance(
-      this.treasury,
-      this.membersKeyPairs[0].address,
-      increaseStakeFee.add(stakeIncrement)
-    )
-    const workerId: WorkerId = await this.apiWrapper.getWorkerIdByRoleAccount(
-      this.membersKeyPairs[0].address,
-      this.module
-    )
-
-    // Increase worker stake
-    const increasedWorkerStake: BN = (await this.apiWrapper.getWorkerStakeAmount(workerId, this.module)).add(
-      stakeIncrement
-    )
-    await this.apiWrapper.increaseStake(this.membersKeyPairs[0], workerId, stakeIncrement, this.module)
-    const newWorkerStake: BN = await this.apiWrapper.getWorkerStakeAmount(workerId, this.module)
-    assert(
-      increasedWorkerStake.eq(newWorkerStake),
-      `Unexpected worker stake ${newWorkerStake}, expected ${increasedWorkerStake}`
-    )
-    if (expectFailure) {
-      throw new Error('Successful fixture run while expecting failure')
-    }
-  }
-}
-
-export class UpdateRewardAccountFixture implements Fixture {
-  public apiWrapper: ApiWrapper
-  public membersKeyPairs: KeyringPair[]
-  public keyring: Keyring
-  public treasury: KeyringPair
-  public module: WorkingGroups
-
-  constructor(
-    apiWrapper: ApiWrapper,
-    membersKeyPairs: KeyringPair[],
-    keyring: Keyring,
-    treasury: KeyringPair,
-    module: WorkingGroups
-  ) {
-    this.apiWrapper = apiWrapper
-    this.membersKeyPairs = membersKeyPairs
-    this.keyring = keyring
-    this.treasury = treasury
-    this.module = module
-  }
-
-  public async runner(expectFailure: boolean): Promise<void> {
-    // Fee estimation and transfer
-    const updateRewardAccountFee: BN = this.apiWrapper.estimateUpdateRewardAccountFee(
-      this.treasury.address,
-      this.module
-    )
-    await this.apiWrapper.transferBalance(this.treasury, this.membersKeyPairs[0].address, updateRewardAccountFee)
-    const workerId: WorkerId = await this.apiWrapper.getWorkerIdByRoleAccount(
-      this.membersKeyPairs[0].address,
-      this.module
-    )
-
-    // Update reward account
-    const createdAccount: KeyringPair = this.keyring.addFromUri(uuid().substring(0, 8))
-    await this.apiWrapper.updateRewardAccount(this.membersKeyPairs[0], workerId, createdAccount.address, this.module)
-    const newRewardAccount: string = await this.apiWrapper.getWorkerRewardAccount(workerId, this.module)
-    assert(
-      newRewardAccount === createdAccount.address,
-      `Unexpected role account ${newRewardAccount}, expected ${createdAccount.address}`
-    )
-    if (expectFailure) {
-      throw new Error('Successful fixture run while expecting failure')
-    }
-  }
-}
-
-export class UpdateRoleAccountFixture implements Fixture {
-  private apiWrapper: ApiWrapper
-  private membersKeyPairs: KeyringPair[]
-  private keyring: Keyring
-  private treasury: KeyringPair
-  private module: WorkingGroups
-
-  constructor(
-    apiWrapper: ApiWrapper,
-    membersKeyPairs: KeyringPair[],
-    keyring: Keyring,
-    treasury: KeyringPair,
-    module: WorkingGroups
-  ) {
-    this.apiWrapper = apiWrapper
-    this.membersKeyPairs = membersKeyPairs
-    this.keyring = keyring
-    this.treasury = treasury
-    this.module = module
-  }
-
-  public async runner(expectFailure: boolean): Promise<void> {
-    // Fee estimation and transfer
-    const updateRoleAccountFee: BN = this.apiWrapper.estimateUpdateRoleAccountFee(this.treasury.address, this.module)
-    await this.apiWrapper.transferBalance(this.treasury, this.membersKeyPairs[0].address, updateRoleAccountFee)
-    const workerId: WorkerId = await this.apiWrapper.getWorkerIdByRoleAccount(
-      this.membersKeyPairs[0].address,
-      this.module
-    )
-
-    // Update role account
-    const createdAccount: KeyringPair = this.keyring.addFromUri(uuid().substring(0, 8))
-    await this.apiWrapper.updateRoleAccount(this.membersKeyPairs[0], workerId, createdAccount.address, this.module)
-    const newRoleAccount: string = (
-      await this.apiWrapper.getWorkerById(workerId, this.module)
-    ).role_account_id.toString()
-    assert(
-      newRoleAccount === createdAccount.address,
-      `Unexpected role account ${newRoleAccount}, expected ${createdAccount.address}`
-    )
-
-    this.membersKeyPairs[0] = createdAccount
-    if (expectFailure) {
-      throw new Error('Successful fixture run while expecting failure')
-    }
-  }
-}
-
-export class TerminateApplicationsFixture implements Fixture {
-  private apiWrapper: ApiWrapper
-  private membersKeyPairs: KeyringPair[]
-  private lead: KeyringPair
-  private treasury: KeyringPair
-  private module: WorkingGroups
-
-  constructor(
-    apiWrapper: ApiWrapper,
-    membersKeyPairs: KeyringPair[],
-    lead: KeyringPair,
-    treasury: KeyringPair,
-    module: WorkingGroups
-  ) {
-    this.apiWrapper = apiWrapper
-    this.membersKeyPairs = membersKeyPairs
-    this.lead = lead
-    this.treasury = treasury
-    this.module = module
-  }
-
-  public async runner(expectFailure: boolean): Promise<void> {
-    // Fee estimation and transfer
-    const terminateApplicationFee: BN = this.apiWrapper.estimateTerminateApplicationFee(this.module)
-    await this.apiWrapper.transferBalance(
-      this.treasury,
-      this.lead.address,
-      terminateApplicationFee.muln(this.membersKeyPairs.length)
-    )
-
-    // Terminate worker applications
-    await this.apiWrapper.batchTerminateApplication(this.lead, this.membersKeyPairs, this.module)
-    this.membersKeyPairs.forEach(async (keyPair) => {
-      const activeApplications: ApplicationId[] = await this.apiWrapper.getActiveApplicationsIdsByRoleAccount(
-        keyPair.address,
-        this.module
-      )
-      assert(activeApplications.length === 0, `Account ${keyPair.address} has unexpected active applications`)
-    })
-    if (expectFailure) {
-      throw new Error('Successful fixture run while expecting failure')
-    }
-  }
-}
-
-export class DecreaseStakeFixture implements Fixture {
-  private apiWrapper: ApiWrapper
-  private membersKeyPairs: KeyringPair[]
-  private lead: KeyringPair
-  private treasury: KeyringPair
-  private module: WorkingGroups
-
-  constructor(
-    apiWrapper: ApiWrapper,
-    membersKeyPairs: KeyringPair[],
-    lead: KeyringPair,
-    treasury: KeyringPair,
-    module: WorkingGroups
-  ) {
-    this.apiWrapper = apiWrapper
-    this.membersKeyPairs = membersKeyPairs
-    this.lead = lead
-    this.treasury = treasury
-    this.module = module
-  }
-
-  public async runner(expectFailure: boolean): Promise<void> {
-    // Fee estimation and transfer
-    const decreaseStakeFee: BN = this.apiWrapper.estimateDecreaseStakeFee(this.module)
-    await this.apiWrapper.transferBalance(this.treasury, this.lead.address, decreaseStakeFee)
-    const workerStakeDecrement: BN = new BN(1)
-    const workerId: WorkerId = await this.apiWrapper.getWorkerIdByRoleAccount(
-      this.membersKeyPairs[0].address,
-      this.module
-    )
-
-    // Worker stake decrement
-    const decreasedWorkerStake: BN = (await this.apiWrapper.getWorkerStakeAmount(workerId, this.module)).sub(
-      workerStakeDecrement
-    )
-    await this.apiWrapper.decreaseStake(this.lead, workerId, workerStakeDecrement, this.module, expectFailure)
-    const newWorkerStake: BN = await this.apiWrapper.getWorkerStakeAmount(workerId, this.module)
-
-    // Assertions
-    if (!expectFailure) {
-      assert(
-        decreasedWorkerStake.eq(newWorkerStake),
-        `Unexpected worker stake ${newWorkerStake}, expected ${decreasedWorkerStake}`
-      )
-    }
-  }
-}
-
-export class SlashFixture implements Fixture {
-  private apiWrapper: ApiWrapper
-  private membersKeyPairs: KeyringPair[]
-  private lead: KeyringPair
-  private treasury: KeyringPair
-  private module: WorkingGroups
-
-  constructor(
-    apiWrapper: ApiWrapper,
-    membersKeyPairs: KeyringPair[],
-    lead: KeyringPair,
-    treasury: KeyringPair,
-    module: WorkingGroups
-  ) {
-    this.apiWrapper = apiWrapper
-    this.membersKeyPairs = membersKeyPairs
-    this.lead = lead
-    this.treasury = treasury
-    this.module = module
-  }
-
-  public async runner(expectFailure: boolean): Promise<void> {
-    // Fee estimation and transfer
-    const slashStakeFee: BN = this.apiWrapper.estimateSlashStakeFee(this.module)
-    await this.apiWrapper.transferBalance(this.treasury, this.lead.address, slashStakeFee)
-    const slashAmount: BN = new BN(1)
-    const workerId: WorkerId = await this.apiWrapper.getWorkerIdByRoleAccount(
-      this.membersKeyPairs[0].address,
-      this.module
-    )
-
-    // Slash worker
-    const slashedStake: BN = (await this.apiWrapper.getWorkerStakeAmount(workerId, this.module)).sub(slashAmount)
-    await this.apiWrapper.slashStake(this.lead, workerId, slashAmount, this.module, expectFailure)
-    const newStake: BN = await this.apiWrapper.getWorkerStakeAmount(workerId, this.module)
-
-    // Assertions
-    assert(slashedStake.eq(newStake), `Unexpected worker stake ${newStake}, expected ${slashedStake}`)
-  }
-}
-
-export class TerminateRoleFixture implements Fixture {
-  private apiWrapper: ApiWrapper
-  private membersKeyPairs: KeyringPair[]
-  private lead: KeyringPair
-  private treasury: KeyringPair
-  private module: WorkingGroups
-
-  constructor(
-    apiWrapper: ApiWrapper,
-    membersKeyPairs: KeyringPair[],
-    lead: KeyringPair,
-    treasury: KeyringPair,
-    module: WorkingGroups
-  ) {
-    this.apiWrapper = apiWrapper
-    this.membersKeyPairs = membersKeyPairs
-    this.lead = lead
-    this.treasury = treasury
-    this.module = module
-  }
-
-  public async runner(expectFailure: boolean): Promise<void> {
-    // Fee estimation and transfer
-    const terminateRoleFee: BN = this.apiWrapper.estimateTerminateRoleFee(this.module)
-    await this.apiWrapper.transferBalance(this.treasury, this.lead.address, terminateRoleFee)
-    const workerId: WorkerId = await this.apiWrapper.getWorkerIdByRoleAccount(
-      this.membersKeyPairs[0].address,
-      this.module
-    )
-
-    // Terminate worker role
-    await this.apiWrapper.terminateRole(this.lead, workerId, uuid().substring(0, 8), this.module, expectFailure)
-
-    // Assertions
-    const isWorker: boolean = await this.apiWrapper.isWorker(this.membersKeyPairs[0].address, this.module)
-    assert(!isWorker, `Worker with account ${this.membersKeyPairs[0].address} is not terminated`)
-  }
-}
-
-export class LeaveRoleFixture implements Fixture {
-  private apiWrapper: ApiWrapper
-  private membersKeyPairs: KeyringPair[]
-  private treasury: KeyringPair
-  private module: WorkingGroups
-
-  constructor(apiWrapper: ApiWrapper, membersKeyPairs: KeyringPair[], treasury: KeyringPair, module: WorkingGroups) {
-    this.apiWrapper = apiWrapper
-    this.membersKeyPairs = membersKeyPairs
-    this.treasury = treasury
-    this.module = module
-  }
-
-  public async runner(expectFailure: boolean): Promise<void> {
-    // Fee estimation and transfer
-    const leaveRoleFee: BN = this.apiWrapper.estimateLeaveRoleFee(this.module)
-    await this.apiWrapper.transferBalanceToAccounts(this.treasury, this.membersKeyPairs, leaveRoleFee)
-
-    await this.apiWrapper.batchLeaveRole(this.membersKeyPairs, uuid().substring(0, 8), expectFailure, this.module)
-
-    // Assertions
-    this.membersKeyPairs.forEach(async (keyPair) => {
-      const isWorker: boolean = await this.apiWrapper.isWorker(this.membersKeyPairs[0].address, this.module)
-      assert(!isWorker, `Worker with account ${keyPair.address} is not terminated`)
-    })
-  }
-}
-
-export class AwaitPayoutFixture implements Fixture {
-  private apiWrapper: ApiWrapper
-  private membersKeyPairs: KeyringPair[]
-  private module: WorkingGroups
-
-  constructor(apiWrapper: ApiWrapper, membersKeyPairs: KeyringPair[], module: WorkingGroups) {
-    this.apiWrapper = apiWrapper
-    this.membersKeyPairs = membersKeyPairs
-    this.module = module
-  }
-
-  public async runner(expectFailure: boolean): Promise<void> {
-    const workerId: WorkerId = await this.apiWrapper.getWorkerIdByRoleAccount(
-      this.membersKeyPairs[0].address,
-      this.module
-    )
-    const worker: Worker = await this.apiWrapper.getWorkerById(workerId, this.module)
-    const reward: RewardRelationship = await this.apiWrapper.getRewardRelationship(worker.reward_relationship.unwrap())
-    const now: BN = await this.apiWrapper.getBestBlock()
-    const nextPaymentBlock: BN = new BN(reward.getField('next_payment_at_block').toString())
-    const payoutInterval: BN = new BN(reward.getField('payout_interval').toString())
-    const amountPerPayout: BN = new BN(reward.getField('amount_per_payout').toString())
-
-    assert(now.lt(nextPaymentBlock), `Payout already happened in block ${nextPaymentBlock} now ${now}`)
-    const balance: BN = await this.apiWrapper.getBalance(this.membersKeyPairs[0].address)
-
-    const firstPayoutWaitingPeriod: BN = nextPaymentBlock.sub(now).addn(1)
-    await Utils.wait(this.apiWrapper.getBlockDuration().mul(firstPayoutWaitingPeriod).toNumber())
-
-    const balanceAfterFirstPayout: BN = await this.apiWrapper.getBalance(this.membersKeyPairs[0].address)
-    const expectedBalanceFirst: BN = balance.add(amountPerPayout)
-    assert(
-      balanceAfterFirstPayout.eq(expectedBalanceFirst),
-      `Unexpected balance, expected ${expectedBalanceFirst} got ${balanceAfterFirstPayout}`
-    )
-
-    const secondPayoutWaitingPeriod: BN = payoutInterval.addn(1)
-    console.log('waiting period ' + secondPayoutWaitingPeriod)
-    await Utils.wait(this.apiWrapper.getBlockDuration().mul(secondPayoutWaitingPeriod).toNumber())
-
-    const balanceAfterSecondPayout: BN = await this.apiWrapper.getBalance(this.membersKeyPairs[0].address)
-    const expectedBalanceSecond: BN = expectedBalanceFirst.add(amountPerPayout)
-    assert(
-      balanceAfterSecondPayout.eq(expectedBalanceSecond),
-      `Unexpected balance, expected ${expectedBalanceSecond} got ${balanceAfterSecondPayout}`
-    )
-    if (expectFailure) {
-      throw new Error('Successful fixture run while expecting failure')
-    }
-  }
-}
-
-export class ExpectLeadOpeningAddedFixture implements Fixture {
-  private apiWrapper: ApiWrapper
-
-  private result: OpeningId | undefined
-  private events: Event[] = []
-
-  constructor(apiWrapper: ApiWrapper) {
-    this.apiWrapper = apiWrapper
-  }
-
-  public getCreatedOpeningId(): OpeningId | undefined {
-    return this.result
-  }
-
-  public getEvents(): Event[] {
-    return this.events
-  }
-
-  public async runner(expectFailure: boolean): Promise<void> {
-    const event: Event = await this.apiWrapper.expectEvent('OpeningAdded')
-    this.events.push(event)
-    this.result = (event.data as unknown) as OpeningId
-    if (expectFailure) {
-      throw new Error('Successful fixture run while expecting failure')
-    }
-  }
-}
-
-export class ExpectLeaderSetFixture implements Fixture {
-  private apiWrapper: ApiWrapper
-  private leaderAddress: string
-  private module: WorkingGroups
-
-  private result: WorkerId | undefined
-  private events: Event[] = []
-
-  constructor(apiWrapper: ApiWrapper, leaderAddress: string, module: WorkingGroups) {
-    this.apiWrapper = apiWrapper
-    this.leaderAddress = leaderAddress
-    this.module = module
-  }
-
-  public getLeaderWorkerId(): WorkerId | undefined {
-    return this.result
-  }
-
-  public getEvents(): Event[] {
-    return this.events
-  }
-
-  public async runner(expectFailure: boolean): Promise<void> {
-    const event: Event = await this.apiWrapper.expectEvent('LeaderSet')
-    this.events.push(event)
-    const leadWorkerId: WorkerId = (event.data as unknown) as WorkerId
-    const worker: Worker = await this.apiWrapper.getWorkerById(leadWorkerId, this.module)
-    const leaderApplicationId: ApplicationId = (
-      await this.apiWrapper.getApplicationsIdsByRoleAccount(this.leaderAddress, this.module)
-    )[0]
-    const application: Application = await this.apiWrapper.getApplicationById(leaderApplicationId, this.module)
-    assert(
-      worker.role_account_id.eq(application.role_account_id),
-      `Role account ids does not match, leader account: ${worker.role_account_id}, application account ${application.role_account_id}`
-    )
-    this.result = leadWorkerId
-    if (expectFailure) {
-      throw new Error('Successful fixture run while expecting failure')
-    }
-  }
-}
-
-export class ExpectBeganApplicationReviewFixture implements Fixture {
-  private apiWrapper: ApiWrapper
-
-  private result: ApplicationId | undefined
-  private events: Event[] = []
-
-  constructor(apiWrapper: ApiWrapper) {
-    this.apiWrapper = apiWrapper
-  }
-
-  public getApplicationId(): ApplicationId | undefined {
-    return this.result
-  }
-
-  public getEvents(): Event[] {
-    return this.events
-  }
-
-  public async runner(expectFailure: boolean): Promise<void> {
-    const event: Event = await this.apiWrapper.expectEvent('BeganApplicationReview')
-    this.events.push(event)
-    this.result = (event.data as unknown) as ApplicationId
-    if (expectFailure) {
-      throw new Error('Successful fixture run while expecting failure')
-    }
-  }
-}
-
-export class ExpectLeaderRoleTerminatedFixture implements Fixture {
-  private apiWrapper: ApiWrapper
-  private module: WorkingGroups
-
-  private events: Event[] = []
-
-  constructor(apiWrapper: ApiWrapper, module: WorkingGroups) {
-    this.apiWrapper = apiWrapper
-    this.module = module
-  }
-
-  public getEvents(): Event[] {
-    return this.events
-  }
-
-  public async runner(expectFailure: boolean): Promise<void> {
-    const event: Event = await this.apiWrapper.expectEvent('TerminatedLeader')
-    this.events.push(event)
-    const leadWorkerId: WorkerId | undefined = await this.apiWrapper.getLeadWorkerId(this.module)
-    assert(leadWorkerId === undefined, `Unexpected lead worker id: ${leadWorkerId}, expected none`)
-    if (expectFailure) {
-      throw new Error('Successful fixture run while expecting failure')
-    }
-  }
-}
-
-export class ExpectLeaderRewardAmountUpdatedFixture implements Fixture {
-  private apiWrapper: ApiWrapper
-  private expectedReward: BN
-  private module: WorkingGroups
-
-  private events: Event[] = []
-
-  constructor(apiWrapper: ApiWrapper, expectedReward: BN, module: WorkingGroups) {
-    this.apiWrapper = apiWrapper
-    this.expectedReward = expectedReward
-    this.module = module
-  }
-
-  public getEvents(): Event[] {
-    return this.events
-  }
-
-  public async runner(expectFailure: boolean): Promise<void> {
-    const event: Event = await this.apiWrapper.expectEvent('WorkerRewardAmountUpdated')
-    this.events.push(event)
-    const leadWorkerId: WorkerId = (await this.apiWrapper.getLeadWorkerId(this.module))!
-    const leadWorker: Worker = await this.apiWrapper.getWorkerById(leadWorkerId, this.module)
-    const receivedReward: BN = (await this.apiWrapper.getRewardRelationship(leadWorker.reward_relationship.unwrap()))
-      .amount_per_payout
-    assert(
-      receivedReward.eq(this.expectedReward),
-      `Unexpected reward amount for worker with id ${leadWorkerId}: ${receivedReward}, expected ${this.expectedReward}`
-    )
-    if (expectFailure) {
-      throw new Error('Successful fixture run while expecting failure')
-    }
-  }
-}
-
-export class ExpectLeaderStakeDecreasedFixture implements Fixture {
-  private apiWrapper: ApiWrapper
-  private expectedStake: BN
-  private module: WorkingGroups
-
-  private events: Event[] = []
-
-  constructor(apiWrapper: ApiWrapper, expectedStake: BN, module: WorkingGroups) {
-    this.apiWrapper = apiWrapper
-    this.expectedStake = expectedStake
-    this.module = module
-  }
-
-  public getEvents(): Event[] {
-    return this.events
-  }
-
-  public async runner(expectFailure: boolean): Promise<void> {
-    const event: Event = await this.apiWrapper.expectEvent('StakeDecreased')
-    this.events.push(event)
-    const leadWorkerId: WorkerId = (await this.apiWrapper.getLeadWorkerId(this.module))!
-    const receivedStake: BN = await this.apiWrapper.getWorkerStakeAmount(leadWorkerId, this.module)
-    assert(
-      receivedStake.eq(this.expectedStake),
-      `Unexpected stake amount for worker with id ${leadWorkerId}: ${receivedStake}, expected ${this.expectedStake}`
-    )
-    if (expectFailure) {
-      throw new Error('Successful fixture run while expecting failure')
-    }
-  }
-}
-
-export class ExpectLeaderSlashedFixture implements Fixture {
-  private apiWrapper: ApiWrapper
-  private expectedStake: BN
-  private module: WorkingGroups
-
-  private events: Event[] = []
-
-  constructor(apiWrapper: ApiWrapper, expectedStake: BN, module: WorkingGroups) {
-    this.apiWrapper = apiWrapper
-    this.expectedStake = expectedStake
-    this.module = module
-  }
-
-  public getEvents(): Event[] {
-    return this.events
-  }
-
-  public async runner(expectFailure: boolean): Promise<void> {
-    const event: Event = await this.apiWrapper.expectEvent('StakeSlashed')
-    this.events.push(event)
-    const leadWorkerId: WorkerId = (await this.apiWrapper.getLeadWorkerId(this.module))!
-    const receivedStake: BN = await this.apiWrapper.getWorkerStakeAmount(leadWorkerId, this.module)
-    assert(
-      receivedStake.eq(this.expectedStake),
-      `Unexpected stake amount for worker with id after slash ${leadWorkerId}: ${receivedStake}, expected ${this.expectedStake}`
-    )
-    if (expectFailure) {
-      throw new Error('Successful fixture run while expecting failure')
-    }
-  }
-}
-
-export class ExpectMintCapacityChangedFixture implements Fixture {
-  private apiWrapper: ApiWrapper
-  private expectedMintCapacity: BN
-
-  private result: BN | undefined
-  private events: Event[] = []
-
-  constructor(apiWrapper: ApiWrapper, expectedMintCapacity: BN) {
-    this.apiWrapper = apiWrapper
-    this.expectedMintCapacity = expectedMintCapacity
-  }
-
-  public getResult(): BN | undefined {
-    return this.result
-  }
-
-  public getEvents(): Event[] {
-    return this.events
-  }
-
-  public async runner(expectFailure: boolean): Promise<void> {
-    const event: Event = await this.apiWrapper.expectEvent('MintCapacityChanged')
-    this.events.push(event)
-    const receivedMintCapacity: BN = (event.data[1] as unknown) as BN
-    assert(
-      receivedMintCapacity.eq(this.expectedMintCapacity),
-      `Unexpected mint capacity: ${receivedMintCapacity}, expected ${this.expectedMintCapacity}`
-    )
-    this.result = receivedMintCapacity
-    if (expectFailure) {
-      throw new Error('Successful fixture run while expecting failure')
-    }
-  }
-}

+ 0 - 62
tests/network-tests/src/tests/leaderSetup.ts

@@ -1,62 +0,0 @@
-import { initConfig } from '../utils/config'
-import { closeApi } from '../utils/closeApi'
-import { ApiWrapper, WorkingGroups } from '../utils/apiWrapper'
-import { WsProvider, Keyring } from '@polkadot/api'
-import { KeyringPair } from '@polkadot/keyring/types'
-import { setTestTimeout } from '../utils/setTestTimeout'
-import BN from 'bn.js'
-import tap from 'tap'
-import { Utils } from '../utils/utils'
-import { PaidTermId } from '@joystream/types/members'
-import { DbService } from '../services/dbService'
-import { LeaderHiringHappyCaseFixture } from './fixtures/leaderHiringHappyCase'
-
-tap.mocha.describe('Worker application happy case scenario', async () => {
-  initConfig()
-  const nodeUrl: string = process.env.NODE_URL!
-  const sudoUri: string = process.env.SUDO_ACCOUNT_URI!
-  const keyring = new Keyring({ type: 'sr25519' })
-  const db: DbService = DbService.getInstance()
-  const provider = new WsProvider(nodeUrl)
-  const apiWrapper: ApiWrapper = await ApiWrapper.create(provider)
-  if (db.hasLeader(apiWrapper.getWorkingGroupString(WorkingGroups.StorageWorkingGroup))) {
-    return
-  }
-
-  const sudo: KeyringPair = keyring.addFromUri(sudoUri)
-  const N: number = +process.env.WORKING_GROUP_N!
-  const nKeyPairs: KeyringPair[] = Utils.createKeyPairs(keyring, N)
-  const leadKeyPair: KeyringPair[] = Utils.createKeyPairs(keyring, 1)
-
-  const paidTerms: PaidTermId = apiWrapper.createPaidTermId(new BN(+process.env.MEMBERSHIP_PAID_TERMS!))
-  const applicationStake: BN = new BN(process.env.WORKING_GROUP_APPLICATION_STAKE!)
-  const roleStake: BN = new BN(process.env.WORKING_GROUP_ROLE_STAKE!)
-  const firstRewardInterval: BN = new BN(process.env.LONG_REWARD_INTERVAL!)
-  const rewardInterval: BN = new BN(process.env.LONG_REWARD_INTERVAL!)
-  const payoutAmount: BN = new BN(process.env.PAYOUT_AMOUNT!)
-  const durationInBlocks = 48
-  const openingActivationDelay: BN = new BN(0)
-
-  setTestTimeout(apiWrapper, durationInBlocks)
-
-  const leaderHiringHappyCaseFixture: LeaderHiringHappyCaseFixture = new LeaderHiringHappyCaseFixture(
-    apiWrapper,
-    sudo,
-    nKeyPairs,
-    leadKeyPair,
-    paidTerms,
-    applicationStake,
-    roleStake,
-    openingActivationDelay,
-    rewardInterval,
-    firstRewardInterval,
-    payoutAmount,
-    WorkingGroups.StorageWorkingGroup
-  )
-  await leaderHiringHappyCaseFixture.runner(false)
-
-  db.setMembers(nKeyPairs)
-  db.setLeader(leadKeyPair[0], apiWrapper.getWorkingGroupString(WorkingGroups.StorageWorkingGroup))
-
-  closeApi(apiWrapper)
-})

+ 0 - 56
tests/network-tests/src/tests/membership/membershipCreationTest.ts

@@ -1,56 +0,0 @@
-import { KeyringPair } from '@polkadot/keyring/types'
-import { Keyring, WsProvider } from '@polkadot/api'
-import { initConfig } from '../../utils/config'
-import { setTestTimeout } from '../../utils/setTestTimeout'
-import tap from 'tap'
-import { ApiWrapper } from '../../utils/apiWrapper'
-import { closeApi } from '../../utils/closeApi'
-import { BuyMembershipHappyCaseFixture, BuyMembershipWithInsufficienFundsFixture } from '../fixtures/membershipModule'
-import { Utils } from '../../utils/utils'
-import { PaidTermId } from '@joystream/types/members'
-import BN from 'bn.js'
-
-tap.mocha.describe('Membership creation scenario', async () => {
-  initConfig()
-
-  const nodeUrl: string = process.env.NODE_URL!
-  const sudoUri: string = process.env.SUDO_ACCOUNT_URI!
-  const keyring = new Keyring({ type: 'sr25519' })
-  const provider = new WsProvider(nodeUrl)
-  const apiWrapper: ApiWrapper = await ApiWrapper.create(provider)
-  const sudo: KeyringPair = keyring.addFromUri(sudoUri)
-
-  const N: number = +process.env.MEMBERSHIP_CREATION_N!
-  const nKeyPairs: KeyringPair[] = Utils.createKeyPairs(keyring, N)
-  const aKeyPair: KeyringPair[] = Utils.createKeyPairs(keyring, 1)
-  const paidTerms: PaidTermId = apiWrapper.createPaidTermId(new BN(+process.env.MEMBERSHIP_PAID_TERMS!))
-
-  const durationInBlocks = 7
-
-  setTestTimeout(apiWrapper, durationInBlocks)
-
-  const happyCaseFixture: BuyMembershipHappyCaseFixture = new BuyMembershipHappyCaseFixture(
-    apiWrapper,
-    sudo,
-    nKeyPairs,
-    paidTerms
-  )
-  tap.test('Buy membeship is accepted with sufficient funds', async () => happyCaseFixture.runner(false))
-
-  const insufficientFundsFixture: BuyMembershipWithInsufficienFundsFixture = new BuyMembershipWithInsufficienFundsFixture(
-    apiWrapper,
-    sudo,
-    aKeyPair[0],
-    paidTerms
-  )
-  tap.test('Account A can not buy the membership with insufficient funds', async () =>
-    insufficientFundsFixture.runner(false)
-  )
-
-  const buyMembershipAfterAccountTopUp = new BuyMembershipHappyCaseFixture(apiWrapper, sudo, aKeyPair, paidTerms)
-  tap.test('Account A was able to buy the membership with sufficient funds', async () =>
-    buyMembershipAfterAccountTopUp.runner(false)
-  )
-
-  closeApi(apiWrapper)
-})

+ 0 - 70
tests/network-tests/src/tests/proposals/contentWorkingGroupMintCapacityProposalTest.ts

@@ -1,70 +0,0 @@
-import { KeyringPair } from '@polkadot/keyring/types'
-import { initConfig } from '../../utils/config'
-import { Keyring, WsProvider } from '@polkadot/api'
-import BN from 'bn.js'
-import { setTestTimeout } from '../../utils/setTestTimeout'
-import tap from 'tap'
-import { closeApi } from '../../utils/closeApi'
-import { ApiWrapper } from '../../utils/apiWrapper'
-import { ContentWorkingGroupMintCapacityProposalFixture } from '../fixtures/proposalsModule'
-import { Utils } from '../../utils/utils'
-import { PaidTermId } from '@joystream/types/members'
-import { CouncilElectionHappyCaseFixture } from '../fixtures/councilElectionHappyCase'
-import { DbService } from '../../services/dbService'
-
-tap.mocha.describe('Validator count proposal scenario', async () => {
-  initConfig()
-
-  const nodeUrl: string = process.env.NODE_URL!
-  const sudoUri: string = process.env.SUDO_ACCOUNT_URI!
-  const keyring = new Keyring({ type: 'sr25519' })
-  const provider = new WsProvider(nodeUrl)
-  const apiWrapper: ApiWrapper = await ApiWrapper.create(provider)
-  const sudo: KeyringPair = keyring.addFromUri(sudoUri)
-  const db: DbService = DbService.getInstance()
-
-  const N: number = +process.env.MEMBERSHIP_CREATION_N!
-  let m1KeyPairs: KeyringPair[] = Utils.createKeyPairs(keyring, N)
-  let m2KeyPairs: KeyringPair[] = Utils.createKeyPairs(keyring, N)
-
-  const paidTerms: PaidTermId = apiWrapper.createPaidTermId(new BN(+process.env.MEMBERSHIP_PAID_TERMS!))
-  const K: number = +process.env.COUNCIL_ELECTION_K!
-  const greaterStake: BN = new BN(+process.env.COUNCIL_STAKE_GREATER_AMOUNT!)
-  const lesserStake: BN = new BN(+process.env.COUNCIL_STAKE_LESSER_AMOUNT!)
-  const mintingCapacityIncrement: BN = new BN(+process.env.MINTING_CAPACITY_INCREMENT!)
-
-  const durationInBlocks = 29
-
-  setTestTimeout(apiWrapper, durationInBlocks)
-
-  if (db.hasCouncil()) {
-    m1KeyPairs = db.getMembers()
-    m2KeyPairs = db.getCouncil()
-  } else {
-    const councilElectionHappyCaseFixture = new CouncilElectionHappyCaseFixture(
-      apiWrapper,
-      sudo,
-      m1KeyPairs,
-      m2KeyPairs,
-      paidTerms,
-      K,
-      greaterStake,
-      lesserStake
-    )
-    await councilElectionHappyCaseFixture.runner(false)
-  }
-
-  const contentWorkingGroupMintCapacityProposalFixture: ContentWorkingGroupMintCapacityProposalFixture = new ContentWorkingGroupMintCapacityProposalFixture(
-    apiWrapper,
-    m1KeyPairs,
-    m2KeyPairs,
-    sudo,
-    mintingCapacityIncrement
-  )
-  tap.test(
-    'Content working group mint capacity test',
-    async () => await contentWorkingGroupMintCapacityProposalFixture.runner(false)
-  )
-
-  closeApi(apiWrapper)
-})

+ 0 - 65
tests/network-tests/src/tests/proposals/electionParametersProposalTest.ts

@@ -1,65 +0,0 @@
-import { KeyringPair } from '@polkadot/keyring/types'
-import { initConfig } from '../../utils/config'
-import { Keyring, WsProvider } from '@polkadot/api'
-import BN from 'bn.js'
-import { setTestTimeout } from '../../utils/setTestTimeout'
-import tap from 'tap'
-import { closeApi } from '../../utils/closeApi'
-import { ApiWrapper } from '../../utils/apiWrapper'
-import { Utils } from '../../utils/utils'
-import { ElectionParametersProposalFixture } from '../fixtures/proposalsModule'
-import { PaidTermId } from '@joystream/types/members'
-import { CouncilElectionHappyCaseFixture } from '../fixtures/councilElectionHappyCase'
-import { DbService } from '../../services/dbService'
-
-tap.mocha.describe('Election parameters proposal scenario', async () => {
-  initConfig()
-
-  const nodeUrl: string = process.env.NODE_URL!
-  const sudoUri: string = process.env.SUDO_ACCOUNT_URI!
-  const keyring = new Keyring({ type: 'sr25519' })
-  const provider = new WsProvider(nodeUrl)
-  const apiWrapper: ApiWrapper = await ApiWrapper.create(provider)
-  const sudo: KeyringPair = keyring.addFromUri(sudoUri)
-  const db: DbService = DbService.getInstance()
-
-  const N: number = +process.env.MEMBERSHIP_CREATION_N!
-  let m1KeyPairs: KeyringPair[] = Utils.createKeyPairs(keyring, N)
-  let m2KeyPairs: KeyringPair[] = Utils.createKeyPairs(keyring, N)
-
-  const paidTerms: PaidTermId = apiWrapper.createPaidTermId(new BN(+process.env.MEMBERSHIP_PAID_TERMS!))
-  const K: number = +process.env.COUNCIL_ELECTION_K!
-  const greaterStake: BN = new BN(+process.env.COUNCIL_STAKE_GREATER_AMOUNT!)
-  const lesserStake: BN = new BN(+process.env.COUNCIL_STAKE_LESSER_AMOUNT!)
-
-  const durationInBlocks = 29
-
-  setTestTimeout(apiWrapper, durationInBlocks)
-
-  if (db.hasCouncil()) {
-    m1KeyPairs = db.getMembers()
-    m2KeyPairs = db.getCouncil()
-  } else {
-    const councilElectionHappyCaseFixture = new CouncilElectionHappyCaseFixture(
-      apiWrapper,
-      sudo,
-      m1KeyPairs,
-      m2KeyPairs,
-      paidTerms,
-      K,
-      greaterStake,
-      lesserStake
-    )
-    await councilElectionHappyCaseFixture.runner(false)
-  }
-
-  const electionParametersProposalFixture: ElectionParametersProposalFixture = new ElectionParametersProposalFixture(
-    apiWrapper,
-    m1KeyPairs,
-    m2KeyPairs,
-    sudo
-  )
-  tap.test('Election parameters proposal test', async () => await electionParametersProposalFixture.runner(false))
-
-  closeApi(apiWrapper)
-})

+ 0 - 298
tests/network-tests/src/tests/proposals/manageLeaderRoleTest.ts

@@ -1,298 +0,0 @@
-import { KeyringPair } from '@polkadot/keyring/types'
-import { initConfig } from '../../utils/config'
-import { Keyring, WsProvider } from '@polkadot/api'
-import BN from 'bn.js'
-import { setTestTimeout } from '../../utils/setTestTimeout'
-import tap from 'tap'
-import { closeApi } from '../../utils/closeApi'
-import { ApiWrapper, WorkingGroups } from '../../utils/apiWrapper'
-import { BuyMembershipHappyCaseFixture } from '../fixtures/membershipModule'
-import {
-  BeginWorkingGroupLeaderApplicationReviewFixture,
-  CreateWorkingGroupLeaderOpeningFixture,
-  DecreaseLeaderStakeProposalFixture,
-  FillLeaderOpeningProposalFixture,
-  SetLeaderRewardProposalFixture,
-  SlashLeaderProposalFixture,
-  TerminateLeaderRoleProposalFixture,
-  VoteForProposalFixture,
-} from '../fixtures/proposalsModule'
-import {
-  ApplyForOpeningFixture,
-  ExpectBeganApplicationReviewFixture,
-  ExpectLeaderRewardAmountUpdatedFixture,
-  ExpectLeaderRoleTerminatedFixture,
-  ExpectLeaderSetFixture,
-  ExpectLeaderSlashedFixture,
-  ExpectLeaderStakeDecreasedFixture,
-  ExpectLeadOpeningAddedFixture,
-} from '../fixtures/workingGroupModule'
-import { Utils } from '../../utils/utils'
-import { PaidTermId } from '@joystream/types/members'
-import { OpeningId } from '@joystream/types/hiring'
-import { ProposalId } from '@joystream/types/proposals'
-import { DbService } from '../../services/dbService'
-import { CouncilElectionHappyCaseFixture } from '../fixtures/councilElectionHappyCase'
-
-tap.mocha.describe('Set lead proposal scenario', async () => {
-  initConfig()
-
-  const nodeUrl: string = process.env.NODE_URL!
-  const sudoUri: string = process.env.SUDO_ACCOUNT_URI!
-  const keyring = new Keyring({ type: 'sr25519' })
-  const db: DbService = DbService.getInstance()
-
-  const provider = new WsProvider(nodeUrl)
-  const apiWrapper: ApiWrapper = await ApiWrapper.create(provider)
-  const sudo: KeyringPair = keyring.addFromUri(sudoUri)
-
-  const N: number = +process.env.MEMBERSHIP_CREATION_N!
-  let m1KeyPairs: KeyringPair[] = Utils.createKeyPairs(keyring, N)
-  let m2KeyPairs: KeyringPair[] = Utils.createKeyPairs(keyring, N)
-  const leadKeyPair: KeyringPair[] = Utils.createKeyPairs(keyring, 1)
-
-  const paidTerms: PaidTermId = apiWrapper.createPaidTermId(new BN(+process.env.MEMBERSHIP_PAID_TERMS!))
-  const K: number = +process.env.COUNCIL_ELECTION_K!
-  const greaterStake: BN = new BN(+process.env.COUNCIL_STAKE_GREATER_AMOUNT!)
-  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 firstRewardInterval: BN = new BN(process.env.LONG_REWARD_INTERVAL!)
-  const rewardInterval: BN = new BN(process.env.LONG_REWARD_INTERVAL!)
-  const payoutAmount: BN = new BN(process.env.PAYOUT_AMOUNT!)
-  const alteredPayoutAmount: BN = new BN(process.env.ALTERED_PAYOUT_AMOUNT!)
-  const stakeDecrement: BN = new BN(process.env.STAKE_DECREMENT!)
-  const slashAmount: BN = new BN(process.env.SLASH_AMOUNT!)
-  const durationInBlocks = 70
-
-  setTestTimeout(apiWrapper, durationInBlocks)
-
-  if (db.hasCouncil()) {
-    m1KeyPairs = db.getMembers()
-    m2KeyPairs = db.getCouncil()
-  } else {
-    const councilElectionHappyCaseFixture = new CouncilElectionHappyCaseFixture(
-      apiWrapper,
-      sudo,
-      m1KeyPairs,
-      m2KeyPairs,
-      paidTerms,
-      K,
-      greaterStake,
-      lesserStake
-    )
-    await councilElectionHappyCaseFixture.runner(false)
-  }
-
-  const leaderMembershipFixture: BuyMembershipHappyCaseFixture = new BuyMembershipHappyCaseFixture(
-    apiWrapper,
-    sudo,
-    leadKeyPair,
-    paidTerms
-  )
-  tap.test('Buy membership for lead', async () => await leaderMembershipFixture.runner(false))
-
-  const createWorkingGroupLeaderOpeningFixture: CreateWorkingGroupLeaderOpeningFixture = new CreateWorkingGroupLeaderOpeningFixture(
-    apiWrapper,
-    m1KeyPairs,
-    sudo,
-    applicationStake,
-    roleStake,
-    'Storage'
-  )
-  tap.test('Propose create leader opening', async () => await createWorkingGroupLeaderOpeningFixture.runner(false))
-
-  let voteForCreateOpeningProposalFixture: VoteForProposalFixture
-  const expectLeadOpeningAddedFixture: ExpectLeadOpeningAddedFixture = new ExpectLeadOpeningAddedFixture(apiWrapper)
-  tap.test('Approve add opening proposal', async () => {
-    voteForCreateOpeningProposalFixture = new VoteForProposalFixture(
-      apiWrapper,
-      m2KeyPairs,
-      sudo,
-      createWorkingGroupLeaderOpeningFixture.getCreatedProposalId() as OpeningId
-    )
-    voteForCreateOpeningProposalFixture.runner(false)
-    await expectLeadOpeningAddedFixture.runner(false)
-  })
-
-  let applyForLeaderOpeningFixture: ApplyForOpeningFixture
-  tap.test('Apply for lead opening', async () => {
-    applyForLeaderOpeningFixture = new ApplyForOpeningFixture(
-      apiWrapper,
-      leadKeyPair,
-      sudo,
-      applicationStake,
-      roleStake,
-      expectLeadOpeningAddedFixture.getCreatedOpeningId() as OpeningId,
-      WorkingGroups.StorageWorkingGroup
-    )
-    await applyForLeaderOpeningFixture.runner(false)
-  })
-
-  let beginWorkingGroupLeaderApplicationReviewFixture: BeginWorkingGroupLeaderApplicationReviewFixture
-  tap.test('Propose begin leader application review', async () => {
-    beginWorkingGroupLeaderApplicationReviewFixture = new BeginWorkingGroupLeaderApplicationReviewFixture(
-      apiWrapper,
-      m1KeyPairs,
-      sudo,
-      expectLeadOpeningAddedFixture.getCreatedOpeningId() as OpeningId,
-      'Storage'
-    )
-    await beginWorkingGroupLeaderApplicationReviewFixture.runner(false)
-  })
-
-  let voteForBeginReviewProposal: VoteForProposalFixture
-  const expectBeganApplicationReviewFixture: ExpectBeganApplicationReviewFixture = new ExpectBeganApplicationReviewFixture(
-    apiWrapper
-  )
-  tap.test('Approve begin application review', async () => {
-    voteForBeginReviewProposal = new VoteForProposalFixture(
-      apiWrapper,
-      m2KeyPairs,
-      sudo,
-      beginWorkingGroupLeaderApplicationReviewFixture.getCreatedProposalId() as ProposalId
-    )
-    voteForBeginReviewProposal.runner(false)
-    await expectBeganApplicationReviewFixture.runner(false)
-  })
-
-  let fillLeaderOpeningProposalFixture: FillLeaderOpeningProposalFixture
-  tap.test('Propose fill leader opening', async () => {
-    fillLeaderOpeningProposalFixture = new FillLeaderOpeningProposalFixture(
-      apiWrapper,
-      m1KeyPairs,
-      leadKeyPair[0].address,
-      sudo,
-      firstRewardInterval,
-      rewardInterval,
-      payoutAmount,
-      expectLeadOpeningAddedFixture.getCreatedOpeningId() as OpeningId,
-      WorkingGroups.StorageWorkingGroup
-    )
-    await fillLeaderOpeningProposalFixture.runner(false)
-  })
-
-  let voteForFillLeaderProposalFixture: VoteForProposalFixture
-  const expectLeaderSetFixture: ExpectLeaderSetFixture = new ExpectLeaderSetFixture(
-    apiWrapper,
-    leadKeyPair[0].address,
-    WorkingGroups.StorageWorkingGroup
-  )
-  tap.test('Approve fill leader opening', async () => {
-    voteForFillLeaderProposalFixture = new VoteForProposalFixture(
-      apiWrapper,
-      m2KeyPairs,
-      sudo,
-      fillLeaderOpeningProposalFixture.getCreatedProposalId() as ProposalId
-    )
-    voteForFillLeaderProposalFixture.runner(false)
-    await expectLeaderSetFixture.runner(false)
-  })
-
-  const setLeaderRewardProposalFixture: SetLeaderRewardProposalFixture = new SetLeaderRewardProposalFixture(
-    apiWrapper,
-    m1KeyPairs,
-    sudo,
-    alteredPayoutAmount,
-    WorkingGroups.StorageWorkingGroup
-  )
-  tap.test('Propose leader reward', async () => setLeaderRewardProposalFixture.runner(false))
-
-  let voteForeLeaderRewardFixture: VoteForProposalFixture
-  const expectLeaderRewardAmountUpdatedFixture: ExpectLeaderRewardAmountUpdatedFixture = new ExpectLeaderRewardAmountUpdatedFixture(
-    apiWrapper,
-    alteredPayoutAmount,
-    WorkingGroups.StorageWorkingGroup
-  )
-  tap.test('Approve new leader reward', async () => {
-    voteForeLeaderRewardFixture = new VoteForProposalFixture(
-      apiWrapper,
-      m2KeyPairs,
-      sudo,
-      setLeaderRewardProposalFixture.getCreatedProposalId() as ProposalId
-    )
-    voteForeLeaderRewardFixture.runner(false)
-    await expectLeaderRewardAmountUpdatedFixture.runner(false)
-  })
-
-  const decreaseLeaderStakeProposalFixture: DecreaseLeaderStakeProposalFixture = new DecreaseLeaderStakeProposalFixture(
-    apiWrapper,
-    m1KeyPairs,
-    sudo,
-    stakeDecrement,
-    WorkingGroups.StorageWorkingGroup
-  )
-  let newStake: BN
-  tap.test('Propose decrease stake', async () => decreaseLeaderStakeProposalFixture.runner(false))
-
-  let voteForDecreaseStakeProposal: VoteForProposalFixture
-  let expectLeaderStakeDecreasedFixture: ExpectLeaderStakeDecreasedFixture
-  tap.test('Approve decreased leader stake', async () => {
-    newStake = applicationStake.sub(stakeDecrement)
-    voteForDecreaseStakeProposal = new VoteForProposalFixture(
-      apiWrapper,
-      m2KeyPairs,
-      sudo,
-      decreaseLeaderStakeProposalFixture.getCreatedProposalId() as ProposalId
-    )
-    voteForDecreaseStakeProposal.runner(false)
-    expectLeaderStakeDecreasedFixture = new ExpectLeaderStakeDecreasedFixture(
-      apiWrapper,
-      newStake,
-      WorkingGroups.StorageWorkingGroup
-    )
-    await expectLeaderStakeDecreasedFixture.runner(false)
-  })
-
-  const slashLeaderProposalFixture: SlashLeaderProposalFixture = new SlashLeaderProposalFixture(
-    apiWrapper,
-    m1KeyPairs,
-    sudo,
-    slashAmount,
-    WorkingGroups.StorageWorkingGroup
-  )
-  tap.test('Propose leader slash', async () => await slashLeaderProposalFixture.runner(false))
-
-  let voteForSlashProposalFixture: VoteForProposalFixture
-  let expectLeaderSlashedFixture: ExpectLeaderSlashedFixture
-  tap.test('Approve leader slash', async () => {
-    newStake = newStake.sub(slashAmount)
-    voteForSlashProposalFixture = new VoteForProposalFixture(
-      apiWrapper,
-      m2KeyPairs,
-      sudo,
-      slashLeaderProposalFixture.getCreatedProposalId() as ProposalId
-    )
-    voteForSlashProposalFixture.runner(false)
-    expectLeaderSlashedFixture = new ExpectLeaderSlashedFixture(apiWrapper, newStake, WorkingGroups.StorageWorkingGroup)
-    await expectLeaderSlashedFixture.runner(false)
-  })
-
-  const terminateLeaderRoleProposalFixture: TerminateLeaderRoleProposalFixture = new TerminateLeaderRoleProposalFixture(
-    apiWrapper,
-    m1KeyPairs,
-    leadKeyPair[0].address,
-    sudo,
-    false,
-    WorkingGroups.StorageWorkingGroup
-  )
-  tap.test('Propose terminate leader role', async () => await terminateLeaderRoleProposalFixture.runner(false))
-
-  let voteForLeaderRoleTerminationFixture: VoteForProposalFixture
-  const expectLeaderRoleTerminatedFixture: ExpectLeaderRoleTerminatedFixture = new ExpectLeaderRoleTerminatedFixture(
-    apiWrapper,
-    WorkingGroups.StorageWorkingGroup
-  )
-  tap.test('Approve leader role termination', async () => {
-    voteForLeaderRoleTerminationFixture = new VoteForProposalFixture(
-      apiWrapper,
-      m2KeyPairs,
-      sudo,
-      terminateLeaderRoleProposalFixture.getCreatedProposalId() as ProposalId
-    )
-    voteForLeaderRoleTerminationFixture.runner(false)
-    await expectLeaderRoleTerminatedFixture.runner(false)
-  })
-
-  closeApi(apiWrapper)
-})

+ 0 - 65
tests/network-tests/src/tests/proposals/setLeadProposalTest.ts

@@ -1,65 +0,0 @@
-import { KeyringPair } from '@polkadot/keyring/types'
-import { initConfig } from '../../utils/config'
-import { Keyring, WsProvider } from '@polkadot/api'
-import BN from 'bn.js'
-import { setTestTimeout } from '../../utils/setTestTimeout'
-import tap from 'tap'
-import { closeApi } from '../../utils/closeApi'
-import { ApiWrapper } from '../../utils/apiWrapper'
-import { Utils } from '../../utils/utils'
-import { SetLeadProposalFixture } from '../fixtures/proposalsModule'
-import { PaidTermId } from '@joystream/types/members'
-import { CouncilElectionHappyCaseFixture } from '../fixtures/councilElectionHappyCase'
-import { DbService } from '../../services/dbService'
-
-tap.mocha.describe('Set lead proposal scenario', async () => {
-  initConfig()
-
-  const nodeUrl: string = process.env.NODE_URL!
-  const sudoUri: string = process.env.SUDO_ACCOUNT_URI!
-  const keyring = new Keyring({ type: 'sr25519' })
-  const provider = new WsProvider(nodeUrl)
-  const apiWrapper: ApiWrapper = await ApiWrapper.create(provider)
-  const sudo: KeyringPair = keyring.addFromUri(sudoUri)
-  const db: DbService = DbService.getInstance()
-
-  const N: number = +process.env.MEMBERSHIP_CREATION_N!
-  let m1KeyPairs: KeyringPair[] = Utils.createKeyPairs(keyring, N)
-  let m2KeyPairs: KeyringPair[] = Utils.createKeyPairs(keyring, N)
-
-  const paidTerms: PaidTermId = apiWrapper.createPaidTermId(new BN(+process.env.MEMBERSHIP_PAID_TERMS!))
-  const K: number = +process.env.COUNCIL_ELECTION_K!
-  const greaterStake: BN = new BN(+process.env.COUNCIL_STAKE_GREATER_AMOUNT!)
-  const lesserStake: BN = new BN(+process.env.COUNCIL_STAKE_LESSER_AMOUNT!)
-
-  const durationInBlocks = 29
-
-  setTestTimeout(apiWrapper, durationInBlocks)
-
-  if (db.hasCouncil()) {
-    m1KeyPairs = db.getMembers()
-    m2KeyPairs = db.getCouncil()
-  } else {
-    const councilElectionHappyCaseFixture = new CouncilElectionHappyCaseFixture(
-      apiWrapper,
-      sudo,
-      m1KeyPairs,
-      m2KeyPairs,
-      paidTerms,
-      K,
-      greaterStake,
-      lesserStake
-    )
-    await councilElectionHappyCaseFixture.runner(false)
-  }
-
-  const setLeadProposalFixture: SetLeadProposalFixture = new SetLeadProposalFixture(
-    apiWrapper,
-    m1KeyPairs,
-    m2KeyPairs,
-    sudo
-  )
-  tap.test('Set lead proposal test', async () => await setLeadProposalFixture.runner(false))
-
-  closeApi(apiWrapper)
-})

+ 0 - 68
tests/network-tests/src/tests/proposals/spendingProposalTest.ts

@@ -1,68 +0,0 @@
-import { KeyringPair } from '@polkadot/keyring/types'
-import { initConfig } from '../../utils/config'
-import { Keyring, WsProvider } from '@polkadot/api'
-import BN from 'bn.js'
-import { setTestTimeout } from '../../utils/setTestTimeout'
-import tap from 'tap'
-import { closeApi } from '../../utils/closeApi'
-import { ApiWrapper } from '../../utils/apiWrapper'
-import { Utils } from '../../utils/utils'
-import { SpendingProposalFixture } from '../fixtures/proposalsModule'
-import { PaidTermId } from '@joystream/types/members'
-import { CouncilElectionHappyCaseFixture } from '../fixtures/councilElectionHappyCase'
-import { DbService } from '../../services/dbService'
-
-tap.mocha.describe('Spending proposal scenario', async () => {
-  initConfig()
-
-  const nodeUrl: string = process.env.NODE_URL!
-  const sudoUri: string = process.env.SUDO_ACCOUNT_URI!
-  const keyring = new Keyring({ type: 'sr25519' })
-  const provider = new WsProvider(nodeUrl)
-  const apiWrapper: ApiWrapper = await ApiWrapper.create(provider)
-  const sudo: KeyringPair = keyring.addFromUri(sudoUri)
-  const db: DbService = DbService.getInstance()
-
-  const N: number = +process.env.MEMBERSHIP_CREATION_N!
-  let m1KeyPairs: KeyringPair[] = Utils.createKeyPairs(keyring, N)
-  let m2KeyPairs: KeyringPair[] = Utils.createKeyPairs(keyring, N)
-
-  const paidTerms: PaidTermId = apiWrapper.createPaidTermId(new BN(+process.env.MEMBERSHIP_PAID_TERMS!))
-  const K: number = +process.env.COUNCIL_ELECTION_K!
-  const greaterStake: BN = new BN(+process.env.COUNCIL_STAKE_GREATER_AMOUNT!)
-  const lesserStake: BN = new BN(+process.env.COUNCIL_STAKE_LESSER_AMOUNT!)
-  const spendingBalance: BN = new BN(+process.env.SPENDING_BALANCE!)
-  const mintCapacity: BN = new BN(+process.env.COUNCIL_MINTING_CAPACITY!)
-  const durationInBlocks = 29
-
-  setTestTimeout(apiWrapper, durationInBlocks)
-
-  if (db.hasCouncil()) {
-    m1KeyPairs = db.getMembers()
-    m2KeyPairs = db.getCouncil()
-  } else {
-    const councilElectionHappyCaseFixture = new CouncilElectionHappyCaseFixture(
-      apiWrapper,
-      sudo,
-      m1KeyPairs,
-      m2KeyPairs,
-      paidTerms,
-      K,
-      greaterStake,
-      lesserStake
-    )
-    await councilElectionHappyCaseFixture.runner(false)
-  }
-
-  const spendingProposalFixture: SpendingProposalFixture = new SpendingProposalFixture(
-    apiWrapper,
-    m1KeyPairs,
-    m2KeyPairs,
-    sudo,
-    spendingBalance,
-    mintCapacity
-  )
-  tap.test('Spending proposal test', async () => await spendingProposalFixture.runner(false))
-
-  closeApi(apiWrapper)
-})

+ 0 - 59
tests/network-tests/src/tests/proposals/textProposalTest.ts

@@ -1,59 +0,0 @@
-import { KeyringPair } from '@polkadot/keyring/types'
-import { initConfig } from '../../utils/config'
-import { Keyring, WsProvider } from '@polkadot/api'
-import BN from 'bn.js'
-import { setTestTimeout } from '../../utils/setTestTimeout'
-import tap from 'tap'
-import { closeApi } from '../../utils/closeApi'
-import { ApiWrapper } from '../../utils/apiWrapper'
-import { Utils } from '../../utils/utils'
-import { TextProposalFixture } from '../fixtures/proposalsModule'
-import { PaidTermId } from '@joystream/types/members'
-import { CouncilElectionHappyCaseFixture } from '../fixtures/councilElectionHappyCase'
-import { DbService } from '../../services/dbService'
-
-tap.mocha.describe('Text proposal scenario', async () => {
-  initConfig()
-
-  const nodeUrl: string = process.env.NODE_URL!
-  const sudoUri: string = process.env.SUDO_ACCOUNT_URI!
-  const keyring = new Keyring({ type: 'sr25519' })
-  const provider = new WsProvider(nodeUrl)
-  const apiWrapper: ApiWrapper = await ApiWrapper.create(provider)
-  const sudo: KeyringPair = keyring.addFromUri(sudoUri)
-  const db: DbService = DbService.getInstance()
-
-  const N: number = +process.env.MEMBERSHIP_CREATION_N!
-  let m1KeyPairs: KeyringPair[] = Utils.createKeyPairs(keyring, N)
-  let m2KeyPairs: KeyringPair[] = Utils.createKeyPairs(keyring, N)
-
-  const paidTerms: PaidTermId = apiWrapper.createPaidTermId(new BN(+process.env.MEMBERSHIP_PAID_TERMS!))
-  const K: number = +process.env.COUNCIL_ELECTION_K!
-  const greaterStake: BN = new BN(+process.env.COUNCIL_STAKE_GREATER_AMOUNT!)
-  const lesserStake: BN = new BN(+process.env.COUNCIL_STAKE_LESSER_AMOUNT!)
-  const durationInBlocks = 28
-
-  setTestTimeout(apiWrapper, durationInBlocks)
-
-  if (db.hasCouncil()) {
-    m1KeyPairs = db.getMembers()
-    m2KeyPairs = db.getCouncil()
-  } else {
-    const councilElectionHappyCaseFixture = new CouncilElectionHappyCaseFixture(
-      apiWrapper,
-      sudo,
-      m1KeyPairs,
-      m2KeyPairs,
-      paidTerms,
-      K,
-      greaterStake,
-      lesserStake
-    )
-    await councilElectionHappyCaseFixture.runner(false)
-  }
-
-  const textProposalFixture: TextProposalFixture = new TextProposalFixture(apiWrapper, m1KeyPairs, m2KeyPairs, sudo)
-  tap.test('Text proposal test', async () => await textProposalFixture.runner(false))
-
-  closeApi(apiWrapper)
-})

+ 0 - 75
tests/network-tests/src/tests/proposals/updateRuntime.ts

@@ -1,75 +0,0 @@
-import { KeyringPair } from '@polkadot/keyring/types'
-import { initConfig } from '../../utils/config'
-import { Keyring, WsProvider } from '@polkadot/api'
-import BN from 'bn.js'
-import { setTestTimeout } from '../../utils/setTestTimeout'
-import tap from 'tap'
-import { closeApi } from '../../utils/closeApi'
-import { ApiWrapper } from '../../utils/apiWrapper'
-import { Utils } from '../../utils/utils'
-import { BuyMembershipHappyCaseFixture } from '../fixtures/membershipModule'
-import { UpdateRuntimeFixture } from '../fixtures/proposalsModule'
-import { PaidTermId } from '@joystream/types/members'
-import { CouncilElectionHappyCaseFixture } from '../fixtures/councilElectionHappyCase'
-import { DbService } from '../../services/dbService'
-
-tap.mocha.describe('Update runtime scenario', async () => {
-  initConfig()
-
-  const nodeUrl: string = process.env.NODE_URL!
-  const sudoUri: string = process.env.SUDO_ACCOUNT_URI!
-  const keyring = new Keyring({ type: 'sr25519' })
-  const provider = new WsProvider(nodeUrl)
-  const apiWrapper: ApiWrapper = await ApiWrapper.create(provider)
-  const sudo: KeyringPair = keyring.addFromUri(sudoUri)
-  const db: DbService = DbService.getInstance()
-
-  const N: number = +process.env.MEMBERSHIP_CREATION_N!
-  let m1KeyPairs: KeyringPair[] = Utils.createKeyPairs(keyring, N)
-  let m2KeyPairs: KeyringPair[] = Utils.createKeyPairs(keyring, N)
-
-  const paidTerms: PaidTermId = apiWrapper.createPaidTermId(new BN(+process.env.MEMBERSHIP_PAID_TERMS!))
-  const K: number = +process.env.COUNCIL_ELECTION_K!
-  const greaterStake: BN = new BN(+process.env.COUNCIL_STAKE_GREATER_AMOUNT!)
-  const lesserStake: BN = new BN(+process.env.COUNCIL_STAKE_LESSER_AMOUNT!)
-  const runtimePath: string = process.env.RUNTIME_WASM_PATH!
-  const durationInBlocks = 54
-
-  setTestTimeout(apiWrapper, durationInBlocks)
-
-  if (db.hasCouncil()) {
-    m1KeyPairs = db.getMembers()
-    m2KeyPairs = db.getCouncil()
-  } else {
-    const councilElectionHappyCaseFixture = new CouncilElectionHappyCaseFixture(
-      apiWrapper,
-      sudo,
-      m1KeyPairs,
-      m2KeyPairs,
-      paidTerms,
-      K,
-      greaterStake,
-      lesserStake
-    )
-    await councilElectionHappyCaseFixture.runner(false)
-  }
-
-  const updateRuntimeFixture: UpdateRuntimeFixture = new UpdateRuntimeFixture(
-    apiWrapper,
-    m1KeyPairs,
-    m2KeyPairs,
-    sudo,
-    runtimePath
-  )
-  tap.test('Upgrade runtime', async () => await updateRuntimeFixture.runner(false))
-
-  const thirdMemberSetFixture: BuyMembershipHappyCaseFixture = new BuyMembershipHappyCaseFixture(
-    apiWrapper,
-    sudo,
-    Utils.createKeyPairs(keyring, N),
-    paidTerms
-  )
-  tap.test('Creating third set of members', async () => await thirdMemberSetFixture.runner(false))
-
-  closeApi(apiWrapper)
-})

+ 0 - 66
tests/network-tests/src/tests/proposals/validatorCountProposalTest.ts

@@ -1,66 +0,0 @@
-import { KeyringPair } from '@polkadot/keyring/types'
-import { initConfig } from '../../utils/config'
-import { Keyring, WsProvider } from '@polkadot/api'
-import BN from 'bn.js'
-import { setTestTimeout } from '../../utils/setTestTimeout'
-import tap from 'tap'
-import { closeApi } from '../../utils/closeApi'
-import { ApiWrapper } from '../../utils/apiWrapper'
-import { Utils } from '../../utils/utils'
-import { ValidatorCountProposalFixture } from '../fixtures/proposalsModule'
-import { PaidTermId } from '@joystream/types/members'
-import { CouncilElectionHappyCaseFixture } from '../fixtures/councilElectionHappyCase'
-import { DbService } from '../../services/dbService'
-
-tap.mocha.describe('Validator count proposal scenario', async () => {
-  initConfig()
-
-  const nodeUrl: string = process.env.NODE_URL!
-  const sudoUri: string = process.env.SUDO_ACCOUNT_URI!
-  const keyring = new Keyring({ type: 'sr25519' })
-  const provider = new WsProvider(nodeUrl)
-  const apiWrapper: ApiWrapper = await ApiWrapper.create(provider)
-  const sudo: KeyringPair = keyring.addFromUri(sudoUri)
-  const db: DbService = DbService.getInstance()
-
-  const N: number = +process.env.MEMBERSHIP_CREATION_N!
-  let m1KeyPairs: KeyringPair[] = Utils.createKeyPairs(keyring, N)
-  let m2KeyPairs: KeyringPair[] = Utils.createKeyPairs(keyring, N)
-
-  const paidTerms: PaidTermId = apiWrapper.createPaidTermId(new BN(+process.env.MEMBERSHIP_PAID_TERMS!))
-  const K: number = +process.env.COUNCIL_ELECTION_K!
-  const greaterStake: BN = new BN(+process.env.COUNCIL_STAKE_GREATER_AMOUNT!)
-  const lesserStake: BN = new BN(+process.env.COUNCIL_STAKE_LESSER_AMOUNT!)
-  const validatorCountIncrement: BN = new BN(+process.env.VALIDATOR_COUNT_INCREMENT!)
-  const durationInBlocks = 29
-
-  setTestTimeout(apiWrapper, durationInBlocks)
-
-  if (db.hasCouncil()) {
-    m1KeyPairs = db.getMembers()
-    m2KeyPairs = db.getCouncil()
-  } else {
-    const councilElectionHappyCaseFixture = new CouncilElectionHappyCaseFixture(
-      apiWrapper,
-      sudo,
-      m1KeyPairs,
-      m2KeyPairs,
-      paidTerms,
-      K,
-      greaterStake,
-      lesserStake
-    )
-    await councilElectionHappyCaseFixture.runner(false)
-  }
-
-  const validatorCountProposalFixture: ValidatorCountProposalFixture = new ValidatorCountProposalFixture(
-    apiWrapper,
-    m1KeyPairs,
-    m2KeyPairs,
-    sudo,
-    validatorCountIncrement
-  )
-  tap.test('Validator count proposal', async () => await validatorCountProposalFixture.runner(false))
-
-  closeApi(apiWrapper)
-})

+ 0 - 87
tests/network-tests/src/tests/proposals/workingGroupMintCapacityProposalTest.ts

@@ -1,87 +0,0 @@
-import { KeyringPair } from '@polkadot/keyring/types'
-import { initConfig } from '../../utils/config'
-import { Keyring, WsProvider } from '@polkadot/api'
-import BN from 'bn.js'
-import { setTestTimeout } from '../../utils/setTestTimeout'
-import tap from 'tap'
-import { closeApi } from '../../utils/closeApi'
-import { ApiWrapper, WorkingGroups } from '../../utils/apiWrapper'
-import { Utils } from '../../utils/utils'
-import { VoteForProposalFixture, WorkingGroupMintCapacityProposalFixture } from '../fixtures/proposalsModule'
-import { ExpectMintCapacityChangedFixture } from '../fixtures/workingGroupModule'
-import { PaidTermId } from '@joystream/types/members'
-import { ProposalId } from '@joystream/types/proposals'
-import { CouncilElectionHappyCaseFixture } from '../fixtures/councilElectionHappyCase'
-import { DbService } from '../../services/dbService'
-
-tap.mocha.describe('Set storage working group mint capacity scenario', async () => {
-  initConfig()
-
-  const nodeUrl: string = process.env.NODE_URL!
-  const sudoUri: string = process.env.SUDO_ACCOUNT_URI!
-  const keyring = new Keyring({ type: 'sr25519' })
-  const provider = new WsProvider(nodeUrl)
-  const apiWrapper: ApiWrapper = await ApiWrapper.create(provider)
-  const sudo: KeyringPair = keyring.addFromUri(sudoUri)
-  const db: DbService = DbService.getInstance()
-
-  const N: number = +process.env.MEMBERSHIP_CREATION_N!
-  let m1KeyPairs: KeyringPair[] = Utils.createKeyPairs(keyring, N)
-  let m2KeyPairs: KeyringPair[] = Utils.createKeyPairs(keyring, N)
-
-  const paidTerms: PaidTermId = apiWrapper.createPaidTermId(new BN(+process.env.MEMBERSHIP_PAID_TERMS!))
-  const K: number = +process.env.COUNCIL_ELECTION_K!
-  const greaterStake: BN = new BN(+process.env.COUNCIL_STAKE_GREATER_AMOUNT!)
-  const lesserStake: BN = new BN(+process.env.COUNCIL_STAKE_LESSER_AMOUNT!)
-  const mintCapacityIncrement: BN = new BN(process.env.MINT_CAPACITY_INCREMENT!)
-  const durationInBlocks = 30
-
-  setTestTimeout(apiWrapper, durationInBlocks)
-
-  if (db.hasCouncil()) {
-    m1KeyPairs = db.getMembers()
-    m2KeyPairs = db.getCouncil()
-  } else {
-    const councilElectionHappyCaseFixture = new CouncilElectionHappyCaseFixture(
-      apiWrapper,
-      sudo,
-      m1KeyPairs,
-      m2KeyPairs,
-      paidTerms,
-      K,
-      greaterStake,
-      lesserStake
-    )
-    await councilElectionHappyCaseFixture.runner(false)
-  }
-
-  const newMintCapacity: BN = (await apiWrapper.getWorkingGroupMintCapacity(WorkingGroups.StorageWorkingGroup)).add(
-    mintCapacityIncrement
-  )
-  const workingGroupMintCapacityProposalFixture: WorkingGroupMintCapacityProposalFixture = new WorkingGroupMintCapacityProposalFixture(
-    apiWrapper,
-    m1KeyPairs,
-    sudo,
-    newMintCapacity,
-    WorkingGroups.StorageWorkingGroup
-  )
-  tap.test('Propose mint capacity', async () => await workingGroupMintCapacityProposalFixture.runner(false))
-
-  let voteForProposalFixture: VoteForProposalFixture
-  const expectMintCapacityChanged: ExpectMintCapacityChangedFixture = new ExpectMintCapacityChangedFixture(
-    apiWrapper,
-    newMintCapacity
-  )
-  tap.test('Approve mint capacity', async () => {
-    voteForProposalFixture = new VoteForProposalFixture(
-      apiWrapper,
-      m2KeyPairs,
-      sudo,
-      workingGroupMintCapacityProposalFixture.getCreatedProposalId() as ProposalId
-    )
-    voteForProposalFixture.runner(false)
-    await expectMintCapacityChanged.runner(false)
-  })
-
-  closeApi(apiWrapper)
-})

+ 0 - 107
tests/network-tests/src/tests/workingGroup/atLeastValueBugTest.ts

@@ -1,107 +0,0 @@
-import { initConfig } from '../../utils/config'
-import { closeApi } from '../../utils/closeApi'
-import { ApiWrapper, WorkingGroups } from '../../utils/apiWrapper'
-import { WsProvider, Keyring } from '@polkadot/api'
-import { KeyringPair } from '@polkadot/keyring/types'
-import { setTestTimeout } from '../../utils/setTestTimeout'
-import { AddWorkerOpeningFixture, LeaveRoleFixture } from '../fixtures/workingGroupModule'
-import BN from 'bn.js'
-import tap from 'tap'
-import { Utils } from '../../utils/utils'
-import { PaidTermId } from '@joystream/types/members'
-import { DbService } from '../../services/dbService'
-import { LeaderHiringHappyCaseFixture } from '../fixtures/leaderHiringHappyCase'
-
-tap.mocha.describe('Zero at least value bug scenario', async () => {
-  initConfig()
-
-  const nodeUrl: string = process.env.NODE_URL!
-  const sudoUri: string = process.env.SUDO_ACCOUNT_URI!
-  const keyring = new Keyring({ type: 'sr25519' })
-  const db: DbService = DbService.getInstance()
-
-  const provider = new WsProvider(nodeUrl)
-  const apiWrapper: ApiWrapper = await ApiWrapper.create(provider)
-  const sudo: KeyringPair = keyring.addFromUri(sudoUri)
-
-  const N: number = +process.env.WORKING_GROUP_N!
-  let nKeyPairs: KeyringPair[] = Utils.createKeyPairs(keyring, N)
-  const leadKeyPair: KeyringPair[] = Utils.createKeyPairs(keyring, 1)
-
-  const paidTerms: PaidTermId = apiWrapper.createPaidTermId(new BN(+process.env.MEMBERSHIP_PAID_TERMS!))
-  const applicationStake: BN = new BN(process.env.WORKING_GROUP_APPLICATION_STAKE!)
-  const roleStake: BN = new BN(process.env.WORKING_GROUP_ROLE_STAKE!)
-  const firstRewardInterval: BN = new BN(process.env.LONG_REWARD_INTERVAL!)
-  const rewardInterval: BN = new BN(process.env.LONG_REWARD_INTERVAL!)
-  const payoutAmount: BN = new BN(process.env.PAYOUT_AMOUNT!)
-  const unstakingPeriod: BN = new BN(process.env.STORAGE_WORKING_GROUP_UNSTAKING_PERIOD!)
-  const durationInBlocks = 48
-  const openingActivationDelay: BN = new BN(0)
-
-  setTestTimeout(apiWrapper, durationInBlocks)
-
-  if (db.hasLeader(apiWrapper.getWorkingGroupString(WorkingGroups.StorageWorkingGroup))) {
-    nKeyPairs = db.getMembers()
-    leadKeyPair[0] = db.getLeader(apiWrapper.getWorkingGroupString(WorkingGroups.StorageWorkingGroup))
-  } else {
-    const leaderHiringHappyCaseFixture: LeaderHiringHappyCaseFixture = new LeaderHiringHappyCaseFixture(
-      apiWrapper,
-      sudo,
-      nKeyPairs,
-      leadKeyPair,
-      paidTerms,
-      applicationStake,
-      roleStake,
-      openingActivationDelay,
-      rewardInterval,
-      firstRewardInterval,
-      payoutAmount,
-      WorkingGroups.StorageWorkingGroup
-    )
-    await leaderHiringHappyCaseFixture.runner(false)
-  }
-
-  const addWorkerOpeningWithoutStakeFixture: AddWorkerOpeningFixture = new AddWorkerOpeningFixture(
-    apiWrapper,
-    nKeyPairs,
-    leadKeyPair[0],
-    sudo,
-    new BN(0),
-    new BN(0),
-    openingActivationDelay,
-    unstakingPeriod,
-    WorkingGroups.StorageWorkingGroup
-  )
-  tap.test(
-    'Add worker opening with 0 stake, expect failure',
-    async () => await addWorkerOpeningWithoutStakeFixture.runner(true)
-  )
-
-  const addWorkerOpeningWithoutUnstakingPeriodFixture: AddWorkerOpeningFixture = new AddWorkerOpeningFixture(
-    apiWrapper,
-    nKeyPairs,
-    leadKeyPair[0],
-    sudo,
-    applicationStake,
-    roleStake,
-    openingActivationDelay,
-    new BN(0),
-    WorkingGroups.StorageWorkingGroup
-  )
-  tap.test(
-    'Add worker opening with 0 unstaking period, expect failure',
-    async () => await addWorkerOpeningWithoutUnstakingPeriodFixture.runner(true)
-  )
-
-  if (!db.hasLeader(apiWrapper.getWorkingGroupString(WorkingGroups.StorageWorkingGroup))) {
-    const leaveRoleFixture: LeaveRoleFixture = new LeaveRoleFixture(
-      apiWrapper,
-      leadKeyPair,
-      sudo,
-      WorkingGroups.StorageWorkingGroup
-    )
-    tap.test('Leaving lead role', async () => await leaveRoleFixture.runner(false))
-  }
-
-  closeApi(apiWrapper)
-})

+ 0 - 238
tests/network-tests/src/tests/workingGroup/manageWorkerAsLeadTest.ts

@@ -1,238 +0,0 @@
-import { initConfig } from '../../utils/config'
-import { closeApi } from '../../utils/closeApi'
-import { ApiWrapper, WorkingGroups } from '../../utils/apiWrapper'
-import { WsProvider, Keyring } from '@polkadot/api'
-import { KeyringPair } from '@polkadot/keyring/types'
-import { setTestTimeout } from '../../utils/setTestTimeout'
-import {
-  AddLeaderOpeningFixture,
-  ApplyForOpeningFixture,
-  BeginLeaderApplicationReviewFixture,
-  FillLeaderOpeningFixture,
-  AddWorkerOpeningFixture,
-  BeginApplicationReviewFixture,
-  FillOpeningFixture,
-  LeaveRoleFixture,
-  DecreaseStakeFixture,
-  SlashFixture,
-  TerminateRoleFixture,
-} from '../fixtures/workingGroupModule'
-import { Utils } from '../../utils/utils'
-import BN from 'bn.js'
-import tap from 'tap'
-import { PaidTermId } from '@joystream/types/members'
-import { OpeningId } from '@joystream/types/hiring'
-import { DbService } from '../../services/dbService'
-import { LeaderHiringHappyCaseFixture } from '../fixtures/leaderHiringHappyCase'
-
-tap.mocha.describe('Manage worker as lead scenario', async () => {
-  initConfig()
-
-  const nodeUrl: string = process.env.NODE_URL!
-  const sudoUri: string = process.env.SUDO_ACCOUNT_URI!
-  const keyring = new Keyring({ type: 'sr25519' })
-  const db: DbService = DbService.getInstance()
-
-  const provider = new WsProvider(nodeUrl)
-  const apiWrapper: ApiWrapper = await ApiWrapper.create(provider)
-  const sudo: KeyringPair = keyring.addFromUri(sudoUri)
-
-  const N: number = +process.env.WORKING_GROUP_N!
-  let nKeyPairs: KeyringPair[] = Utils.createKeyPairs(keyring, N)
-  const leadKeyPair: KeyringPair[] = Utils.createKeyPairs(keyring, 1)
-
-  const paidTerms: PaidTermId = apiWrapper.createPaidTermId(new BN(+process.env.MEMBERSHIP_PAID_TERMS!))
-  const applicationStake: BN = new BN(process.env.WORKING_GROUP_APPLICATION_STAKE!)
-  const roleStake: BN = new BN(process.env.WORKING_GROUP_ROLE_STAKE!)
-  const firstRewardInterval: BN = new BN(process.env.LONG_REWARD_INTERVAL!)
-  const rewardInterval: BN = new BN(process.env.LONG_REWARD_INTERVAL!)
-  const payoutAmount: BN = new BN(process.env.PAYOUT_AMOUNT!)
-  const unstakingPeriod: BN = new BN(process.env.STORAGE_WORKING_GROUP_UNSTAKING_PERIOD!)
-  const durationInBlocks = 60
-  const openingActivationDelay: BN = new BN(0)
-
-  setTestTimeout(apiWrapper, durationInBlocks)
-
-  if (db.hasLeader(apiWrapper.getWorkingGroupString(WorkingGroups.StorageWorkingGroup))) {
-    nKeyPairs = db.getMembers()
-    leadKeyPair[0] = db.getLeader(apiWrapper.getWorkingGroupString(WorkingGroups.StorageWorkingGroup))
-  } else {
-    const leaderHiringHappyCaseFixture: LeaderHiringHappyCaseFixture = new LeaderHiringHappyCaseFixture(
-      apiWrapper,
-      sudo,
-      nKeyPairs,
-      leadKeyPair,
-      paidTerms,
-      applicationStake,
-      roleStake,
-      openingActivationDelay,
-      rewardInterval,
-      firstRewardInterval,
-      payoutAmount,
-      WorkingGroups.StorageWorkingGroup
-    )
-    await leaderHiringHappyCaseFixture.runner(false)
-  }
-
-  const addWorkerOpeningFixture: AddWorkerOpeningFixture = new AddWorkerOpeningFixture(
-    apiWrapper,
-    nKeyPairs,
-    leadKeyPair[0],
-    sudo,
-    applicationStake,
-    roleStake,
-    openingActivationDelay,
-    unstakingPeriod,
-    WorkingGroups.StorageWorkingGroup
-  )
-  tap.test('Add worker opening', async () => await addWorkerOpeningFixture.runner(false))
-
-  let applyForWorkerOpeningFixture: ApplyForOpeningFixture
-  tap.test('First apply for worker opening', async () => {
-    applyForWorkerOpeningFixture = new ApplyForOpeningFixture(
-      apiWrapper,
-      nKeyPairs,
-      sudo,
-      applicationStake,
-      roleStake,
-      addWorkerOpeningFixture.getCreatedOpeningId() as OpeningId,
-      WorkingGroups.StorageWorkingGroup
-    )
-    await applyForWorkerOpeningFixture.runner(false)
-  })
-
-  let beginApplicationReviewFixture: BeginApplicationReviewFixture
-  tap.test('Begin application review', async () => {
-    beginApplicationReviewFixture = new BeginApplicationReviewFixture(
-      apiWrapper,
-      leadKeyPair[0],
-      sudo,
-      addWorkerOpeningFixture.getCreatedOpeningId() as OpeningId,
-      WorkingGroups.StorageWorkingGroup
-    )
-    await beginApplicationReviewFixture.runner(false)
-  })
-
-  let fillOpeningFixture: FillOpeningFixture
-  tap.test('Fill worker opening', async () => {
-    fillOpeningFixture = new FillOpeningFixture(
-      apiWrapper,
-      nKeyPairs,
-      leadKeyPair[0],
-      sudo,
-      addWorkerOpeningFixture.getCreatedOpeningId() as OpeningId,
-      firstRewardInterval,
-      rewardInterval,
-      payoutAmount,
-      WorkingGroups.StorageWorkingGroup
-    )
-    await fillOpeningFixture.runner(false)
-  })
-
-  const leaveRoleFixture: LeaveRoleFixture = new LeaveRoleFixture(
-    apiWrapper,
-    leadKeyPair,
-    sudo,
-    WorkingGroups.StorageWorkingGroup
-  )
-  tap.test('Leaving lead role', async () => await leaveRoleFixture.runner(false))
-
-  const decreaseStakeFailureFixture: DecreaseStakeFixture = new DecreaseStakeFixture(
-    apiWrapper,
-    nKeyPairs,
-    leadKeyPair[0],
-    sudo,
-    WorkingGroups.StorageWorkingGroup
-  )
-  tap.test('Decrease worker stake, expect failure', async () => await decreaseStakeFailureFixture.runner(true))
-
-  const addNewLeaderOpeningFixture: AddLeaderOpeningFixture = new AddLeaderOpeningFixture(
-    apiWrapper,
-    nKeyPairs,
-    sudo,
-    applicationStake,
-    roleStake,
-    openingActivationDelay,
-    WorkingGroups.StorageWorkingGroup
-  )
-  tap.test('Add lead opening', async () => await addNewLeaderOpeningFixture.runner(false))
-
-  let applyForNewLeaderOpeningFixture: ApplyForOpeningFixture
-  tap.test('Apply for lead opening', async () => {
-    applyForNewLeaderOpeningFixture = new ApplyForOpeningFixture(
-      apiWrapper,
-      leadKeyPair,
-      sudo,
-      applicationStake,
-      roleStake,
-      addNewLeaderOpeningFixture.getCreatedOpeningId() as OpeningId,
-      WorkingGroups.StorageWorkingGroup
-    )
-    await applyForNewLeaderOpeningFixture.runner(false)
-  })
-
-  let beginNewLeaderApplicationReviewFixture: BeginLeaderApplicationReviewFixture
-  tap.test('Begin lead application review', async () => {
-    beginNewLeaderApplicationReviewFixture = new BeginLeaderApplicationReviewFixture(
-      apiWrapper,
-      sudo,
-      addNewLeaderOpeningFixture.getCreatedOpeningId() as OpeningId,
-      WorkingGroups.StorageWorkingGroup
-    )
-    await beginNewLeaderApplicationReviewFixture.runner(false)
-  })
-
-  let fillNewLeaderOpeningFixture: FillLeaderOpeningFixture
-  tap.test('Fill lead opening', async () => {
-    fillNewLeaderOpeningFixture = new FillLeaderOpeningFixture(
-      apiWrapper,
-      leadKeyPair,
-      sudo,
-      addNewLeaderOpeningFixture.getCreatedOpeningId() as OpeningId,
-      firstRewardInterval,
-      rewardInterval,
-      payoutAmount,
-      WorkingGroups.StorageWorkingGroup
-    )
-    await fillNewLeaderOpeningFixture.runner(false)
-  })
-
-  const decreaseStakeFixture: DecreaseStakeFixture = new DecreaseStakeFixture(
-    apiWrapper,
-    nKeyPairs,
-    leadKeyPair[0],
-    sudo,
-    WorkingGroups.StorageWorkingGroup
-  )
-  tap.test('Decrease worker stake', async () => await decreaseStakeFixture.runner(false))
-
-  const slashFixture: SlashFixture = new SlashFixture(
-    apiWrapper,
-    nKeyPairs,
-    leadKeyPair[0],
-    sudo,
-    WorkingGroups.StorageWorkingGroup
-  )
-  tap.test('Slash worker', async () => await slashFixture.runner(false))
-
-  const terminateRoleFixture: TerminateRoleFixture = new TerminateRoleFixture(
-    apiWrapper,
-    nKeyPairs,
-    leadKeyPair[0],
-    sudo,
-    WorkingGroups.StorageWorkingGroup
-  )
-  tap.test('Terminate worker role', async () => await terminateRoleFixture.runner(false))
-
-  if (!db.hasLeader(apiWrapper.getWorkingGroupString(WorkingGroups.StorageWorkingGroup))) {
-    const leaveRoleFixture: LeaveRoleFixture = new LeaveRoleFixture(
-      apiWrapper,
-      leadKeyPair,
-      sudo,
-      WorkingGroups.StorageWorkingGroup
-    )
-    tap.test('Leaving lead role', async () => await leaveRoleFixture.runner(false))
-  }
-
-  closeApi(apiWrapper)
-})

+ 0 - 165
tests/network-tests/src/tests/workingGroup/manageWorkerAsWorkerTest.ts

@@ -1,165 +0,0 @@
-import { initConfig } from '../../utils/config'
-import { closeApi } from '../../utils/closeApi'
-import { ApiWrapper, WorkingGroups } from '../../utils/apiWrapper'
-import { WsProvider, Keyring } from '@polkadot/api'
-import { KeyringPair } from '@polkadot/keyring/types'
-import { setTestTimeout } from '../../utils/setTestTimeout'
-import {
-  AddWorkerOpeningFixture,
-  ApplyForOpeningFixture,
-  BeginApplicationReviewFixture,
-  FillOpeningFixture,
-  IncreaseStakeFixture,
-  LeaveRoleFixture,
-  UpdateRewardAccountFixture,
-} from '../fixtures/workingGroupModule'
-import { Utils } from '../../utils/utils'
-import BN from 'bn.js'
-import tap from 'tap'
-import { PaidTermId } from '@joystream/types/members'
-import { OpeningId } from '@joystream/types/hiring'
-import { DbService } from '../../services/dbService'
-import { LeaderHiringHappyCaseFixture } from '../fixtures/leaderHiringHappyCase'
-
-tap.mocha.describe('Manage worker as worker scenario', async () => {
-  initConfig()
-
-  const nodeUrl: string = process.env.NODE_URL!
-  const sudoUri: string = process.env.SUDO_ACCOUNT_URI!
-  const keyring = new Keyring({ type: 'sr25519' })
-  const db: DbService = DbService.getInstance()
-
-  const provider = new WsProvider(nodeUrl)
-  const apiWrapper: ApiWrapper = await ApiWrapper.create(provider)
-  const sudo: KeyringPair = keyring.addFromUri(sudoUri)
-
-  const N: number = +process.env.WORKING_GROUP_N!
-  let nKeyPairs: KeyringPair[] = Utils.createKeyPairs(keyring, N)
-  const leadKeyPair: KeyringPair[] = Utils.createKeyPairs(keyring, 1)
-
-  const paidTerms: PaidTermId = apiWrapper.createPaidTermId(new BN(+process.env.MEMBERSHIP_PAID_TERMS!))
-  const applicationStake: BN = new BN(process.env.WORKING_GROUP_APPLICATION_STAKE!)
-  const roleStake: BN = new BN(process.env.WORKING_GROUP_ROLE_STAKE!)
-  const firstRewardInterval: BN = new BN(process.env.LONG_REWARD_INTERVAL!)
-  const rewardInterval: BN = new BN(process.env.LONG_REWARD_INTERVAL!)
-  const payoutAmount: BN = new BN(process.env.PAYOUT_AMOUNT!)
-  const unstakingPeriod: BN = new BN(process.env.STORAGE_WORKING_GROUP_UNSTAKING_PERIOD!)
-  const durationInBlocks = 38
-  const openingActivationDelay: BN = new BN(0)
-
-  setTestTimeout(apiWrapper, durationInBlocks)
-
-  if (db.hasLeader(apiWrapper.getWorkingGroupString(WorkingGroups.StorageWorkingGroup))) {
-    nKeyPairs = db.getMembers()
-    leadKeyPair[0] = db.getLeader(apiWrapper.getWorkingGroupString(WorkingGroups.StorageWorkingGroup))
-  } else {
-    const leaderHiringHappyCaseFixture: LeaderHiringHappyCaseFixture = new LeaderHiringHappyCaseFixture(
-      apiWrapper,
-      sudo,
-      nKeyPairs,
-      leadKeyPair,
-      paidTerms,
-      applicationStake,
-      roleStake,
-      openingActivationDelay,
-      rewardInterval,
-      firstRewardInterval,
-      payoutAmount,
-      WorkingGroups.StorageWorkingGroup
-    )
-    await leaderHiringHappyCaseFixture.runner(false)
-  }
-
-  const addWorkerOpeningFixture: AddWorkerOpeningFixture = new AddWorkerOpeningFixture(
-    apiWrapper,
-    nKeyPairs,
-    leadKeyPair[0],
-    sudo,
-    applicationStake,
-    roleStake,
-    openingActivationDelay,
-    unstakingPeriod,
-    WorkingGroups.StorageWorkingGroup
-  )
-  tap.test('Add worker opening', async () => await addWorkerOpeningFixture.runner(false))
-
-  let applyForWorkerOpeningFixture: ApplyForOpeningFixture
-  tap.test('First apply for worker opening', async () => {
-    applyForWorkerOpeningFixture = new ApplyForOpeningFixture(
-      apiWrapper,
-      nKeyPairs,
-      sudo,
-      applicationStake,
-      roleStake,
-      addWorkerOpeningFixture.getCreatedOpeningId() as OpeningId,
-      WorkingGroups.StorageWorkingGroup
-    )
-    await applyForWorkerOpeningFixture.runner(false)
-  })
-
-  let beginApplicationReviewFixture: BeginApplicationReviewFixture
-  tap.test('Begin application review', async () => {
-    beginApplicationReviewFixture = new BeginApplicationReviewFixture(
-      apiWrapper,
-      leadKeyPair[0],
-      sudo,
-      addWorkerOpeningFixture.getCreatedOpeningId() as OpeningId,
-      WorkingGroups.StorageWorkingGroup
-    )
-    await beginApplicationReviewFixture.runner(false)
-  })
-
-  let fillOpeningFixture: FillOpeningFixture
-  tap.test('Fill worker opening', async () => {
-    fillOpeningFixture = new FillOpeningFixture(
-      apiWrapper,
-      nKeyPairs,
-      leadKeyPair[0],
-      sudo,
-      addWorkerOpeningFixture.getCreatedOpeningId() as OpeningId,
-      firstRewardInterval,
-      rewardInterval,
-      payoutAmount,
-      WorkingGroups.StorageWorkingGroup
-    )
-    await fillOpeningFixture.runner(false)
-  })
-
-  const increaseStakeFixture: IncreaseStakeFixture = new IncreaseStakeFixture(
-    apiWrapper,
-    nKeyPairs,
-    sudo,
-    WorkingGroups.StorageWorkingGroup
-  )
-  tap.test('Increase worker stake', async () => await increaseStakeFixture.runner(false))
-
-  const updateRewardAccountFixture: UpdateRewardAccountFixture = new UpdateRewardAccountFixture(
-    apiWrapper,
-    nKeyPairs,
-    keyring,
-    sudo,
-    WorkingGroups.StorageWorkingGroup
-  )
-  tap.test('Update reward account', async () => await updateRewardAccountFixture.runner(false))
-
-  const updateRoleAccountFixture: UpdateRewardAccountFixture = new UpdateRewardAccountFixture(
-    apiWrapper,
-    nKeyPairs,
-    keyring,
-    sudo,
-    WorkingGroups.StorageWorkingGroup
-  )
-  tap.test('Update role account', async () => await updateRoleAccountFixture.runner(false))
-
-  if (!db.hasLeader(apiWrapper.getWorkingGroupString(WorkingGroups.StorageWorkingGroup))) {
-    const leaveRoleFixture: LeaveRoleFixture = new LeaveRoleFixture(
-      apiWrapper,
-      leadKeyPair,
-      sudo,
-      WorkingGroups.StorageWorkingGroup
-    )
-    tap.test('Leaving lead role', async () => await leaveRoleFixture.runner(false))
-  }
-
-  closeApi(apiWrapper)
-})

+ 0 - 160
tests/network-tests/src/tests/workingGroup/workerApplicationHappyCaseTest.ts

@@ -1,160 +0,0 @@
-import { initConfig } from '../../utils/config'
-import { closeApi } from '../../utils/closeApi'
-import { ApiWrapper, WorkingGroups } from '../../utils/apiWrapper'
-import { WsProvider, Keyring } from '@polkadot/api'
-import { KeyringPair } from '@polkadot/keyring/types'
-import { setTestTimeout } from '../../utils/setTestTimeout'
-import BN from 'bn.js'
-import tap from 'tap'
-import { Utils } from '../../utils/utils'
-import {
-  AddWorkerOpeningFixture,
-  ApplyForOpeningFixture,
-  BeginApplicationReviewFixture,
-  FillOpeningFixture,
-  LeaveRoleFixture,
-  WithdrawApplicationFixture,
-} from '../fixtures/workingGroupModule'
-import { PaidTermId } from '@joystream/types/members'
-import { OpeningId } from '@joystream/types/hiring'
-import { DbService } from '../../services/dbService'
-import { LeaderHiringHappyCaseFixture } from '../fixtures/leaderHiringHappyCase'
-
-tap.mocha.describe('Worker application happy case scenario', async () => {
-  initConfig()
-
-  const nodeUrl: string = process.env.NODE_URL!
-  const sudoUri: string = process.env.SUDO_ACCOUNT_URI!
-  const keyring = new Keyring({ type: 'sr25519' })
-  const db: DbService = DbService.getInstance()
-
-  const provider = new WsProvider(nodeUrl)
-  const apiWrapper: ApiWrapper = await ApiWrapper.create(provider)
-  const sudo: KeyringPair = keyring.addFromUri(sudoUri)
-
-  const N: number = +process.env.WORKING_GROUP_N!
-  let nKeyPairs: KeyringPair[] = Utils.createKeyPairs(keyring, N)
-  const leadKeyPair: KeyringPair[] = Utils.createKeyPairs(keyring, 1)
-
-  const paidTerms: PaidTermId = apiWrapper.createPaidTermId(new BN(+process.env.MEMBERSHIP_PAID_TERMS!))
-  const applicationStake: BN = new BN(process.env.WORKING_GROUP_APPLICATION_STAKE!)
-  const roleStake: BN = new BN(process.env.WORKING_GROUP_ROLE_STAKE!)
-  const firstRewardInterval: BN = new BN(process.env.LONG_REWARD_INTERVAL!)
-  const rewardInterval: BN = new BN(process.env.LONG_REWARD_INTERVAL!)
-  const payoutAmount: BN = new BN(process.env.PAYOUT_AMOUNT!)
-  const unstakingPeriod: BN = new BN(process.env.STORAGE_WORKING_GROUP_UNSTAKING_PERIOD!)
-  const durationInBlocks = 48
-  const openingActivationDelay: BN = new BN(0)
-
-  setTestTimeout(apiWrapper, durationInBlocks)
-
-  if (db.hasLeader(apiWrapper.getWorkingGroupString(WorkingGroups.StorageWorkingGroup))) {
-    nKeyPairs = db.getMembers()
-    leadKeyPair[0] = db.getLeader(apiWrapper.getWorkingGroupString(WorkingGroups.StorageWorkingGroup))
-  } else {
-    const leaderHiringHappyCaseFixture: LeaderHiringHappyCaseFixture = new LeaderHiringHappyCaseFixture(
-      apiWrapper,
-      sudo,
-      nKeyPairs,
-      leadKeyPair,
-      paidTerms,
-      applicationStake,
-      roleStake,
-      openingActivationDelay,
-      rewardInterval,
-      firstRewardInterval,
-      payoutAmount,
-      WorkingGroups.StorageWorkingGroup
-    )
-    await leaderHiringHappyCaseFixture.runner(false)
-  }
-
-  const addWorkerOpeningFixture: AddWorkerOpeningFixture = new AddWorkerOpeningFixture(
-    apiWrapper,
-    nKeyPairs,
-    leadKeyPair[0],
-    sudo,
-    applicationStake,
-    roleStake,
-    openingActivationDelay,
-    unstakingPeriod,
-    WorkingGroups.StorageWorkingGroup
-  )
-  tap.test('Add worker opening', async () => await addWorkerOpeningFixture.runner(false))
-
-  let firstApplyForWorkerOpeningFixture: ApplyForOpeningFixture
-  tap.test('First apply for worker opening', async () => {
-    firstApplyForWorkerOpeningFixture = new ApplyForOpeningFixture(
-      apiWrapper,
-      nKeyPairs,
-      sudo,
-      applicationStake,
-      roleStake,
-      addWorkerOpeningFixture.getCreatedOpeningId() as OpeningId,
-      WorkingGroups.StorageWorkingGroup
-    )
-    await firstApplyForWorkerOpeningFixture.runner(false)
-  })
-
-  const withdrawApplicationFixture: WithdrawApplicationFixture = new WithdrawApplicationFixture(
-    apiWrapper,
-    nKeyPairs,
-    sudo,
-    WorkingGroups.StorageWorkingGroup
-  )
-  tap.test('Withdraw worker application', async () => await withdrawApplicationFixture.runner(false))
-
-  let secondApplyForWorkerOpeningFixture: ApplyForOpeningFixture
-  tap.test('Second apply for worker opening', async () => {
-    secondApplyForWorkerOpeningFixture = new ApplyForOpeningFixture(
-      apiWrapper,
-      nKeyPairs,
-      sudo,
-      applicationStake,
-      roleStake,
-      addWorkerOpeningFixture.getCreatedOpeningId() as OpeningId,
-      WorkingGroups.StorageWorkingGroup
-    )
-    await secondApplyForWorkerOpeningFixture.runner(false)
-  })
-
-  let beginApplicationReviewFixture: BeginApplicationReviewFixture
-  tap.test('Begin application review', async () => {
-    beginApplicationReviewFixture = new BeginApplicationReviewFixture(
-      apiWrapper,
-      leadKeyPair[0],
-      sudo,
-      addWorkerOpeningFixture.getCreatedOpeningId() as OpeningId,
-      WorkingGroups.StorageWorkingGroup
-    )
-    await beginApplicationReviewFixture.runner(false)
-  })
-
-  let fillOpeningFixture: FillOpeningFixture
-  tap.test('Fill worker opening', async () => {
-    fillOpeningFixture = new FillOpeningFixture(
-      apiWrapper,
-      nKeyPairs,
-      leadKeyPair[0],
-      sudo,
-      addWorkerOpeningFixture.getCreatedOpeningId() as OpeningId,
-      firstRewardInterval,
-      rewardInterval,
-      payoutAmount,
-      WorkingGroups.StorageWorkingGroup
-    )
-    await fillOpeningFixture.runner(false)
-  })
-
-  if (!db.hasLeader(apiWrapper.getWorkingGroupString(WorkingGroups.StorageWorkingGroup))) {
-    const leaveRoleFixture: LeaveRoleFixture = new LeaveRoleFixture(
-      apiWrapper,
-      leadKeyPair,
-      sudo,
-      WorkingGroups.StorageWorkingGroup
-    )
-    tap.test('Leaving lead role', async () => await leaveRoleFixture.runner(false))
-  }
-
-  closeApi(apiWrapper)
-})

+ 0 - 160
tests/network-tests/src/tests/workingGroup/workerApplicationRejectionCaseTest.ts

@@ -1,160 +0,0 @@
-import { initConfig } from '../../utils/config'
-import { closeApi } from '../../utils/closeApi'
-import { ApiWrapper, WorkingGroups } from '../../utils/apiWrapper'
-import { WsProvider, Keyring } from '@polkadot/api'
-import { KeyringPair } from '@polkadot/keyring/types'
-import { setTestTimeout } from '../../utils/setTestTimeout'
-import BN from 'bn.js'
-import tap from 'tap'
-import { Utils } from '../../utils/utils'
-import {
-  AcceptApplicationsFixture,
-  AddWorkerOpeningFixture,
-  ApplyForOpeningFixture,
-  LeaveRoleFixture,
-  TerminateApplicationsFixture,
-} from '../fixtures/workingGroupModule'
-import { PaidTermId } from '@joystream/types/members'
-import { OpeningId } from '@joystream/types/hiring'
-import { DbService } from '../../services/dbService'
-import { LeaderHiringHappyCaseFixture } from '../fixtures/leaderHiringHappyCase'
-
-tap.mocha.describe('Worker application rejection case scenario', async () => {
-  initConfig()
-
-  const nodeUrl: string = process.env.NODE_URL!
-  const sudoUri: string = process.env.SUDO_ACCOUNT_URI!
-  const keyring = new Keyring({ type: 'sr25519' })
-  const db: DbService = DbService.getInstance()
-
-  const provider = new WsProvider(nodeUrl)
-  const apiWrapper: ApiWrapper = await ApiWrapper.create(provider)
-  const sudo: KeyringPair = keyring.addFromUri(sudoUri)
-
-  const N: number = +process.env.WORKING_GROUP_N!
-  let nKeyPairs: KeyringPair[] = Utils.createKeyPairs(keyring, N)
-  const leadKeyPair: KeyringPair[] = Utils.createKeyPairs(keyring, 1)
-  const nonMemberKeyPairs = Utils.createKeyPairs(keyring, N)
-
-  const paidTerms: PaidTermId = apiWrapper.createPaidTermId(new BN(+process.env.MEMBERSHIP_PAID_TERMS!))
-  const applicationStake: BN = new BN(process.env.WORKING_GROUP_APPLICATION_STAKE!)
-  const roleStake: BN = new BN(process.env.WORKING_GROUP_ROLE_STAKE!)
-  const firstRewardInterval: BN = new BN(process.env.LONG_REWARD_INTERVAL!)
-  const rewardInterval: BN = new BN(process.env.LONG_REWARD_INTERVAL!)
-  const payoutAmount: BN = new BN(process.env.PAYOUT_AMOUNT!)
-  const unstakingPeriod: BN = new BN(process.env.STORAGE_WORKING_GROUP_UNSTAKING_PERIOD!)
-  const durationInBlocks = 38
-  const openingActivationDelay: BN = new BN(100)
-  const leadOpeningActivationDelay: BN = new BN(0)
-
-  setTestTimeout(apiWrapper, durationInBlocks)
-
-  if (db.hasLeader(apiWrapper.getWorkingGroupString(WorkingGroups.StorageWorkingGroup))) {
-    nKeyPairs = db.getMembers()
-    leadKeyPair[0] = db.getLeader(apiWrapper.getWorkingGroupString(WorkingGroups.StorageWorkingGroup))
-  } else {
-    const leaderHiringHappyCaseFixture: LeaderHiringHappyCaseFixture = new LeaderHiringHappyCaseFixture(
-      apiWrapper,
-      sudo,
-      nKeyPairs,
-      leadKeyPair,
-      paidTerms,
-      applicationStake,
-      roleStake,
-      leadOpeningActivationDelay,
-      rewardInterval,
-      firstRewardInterval,
-      payoutAmount,
-      WorkingGroups.StorageWorkingGroup
-    )
-    await leaderHiringHappyCaseFixture.runner(false)
-  }
-
-  const addWorkerOpeningFixture: AddWorkerOpeningFixture = new AddWorkerOpeningFixture(
-    apiWrapper,
-    nKeyPairs,
-    leadKeyPair[0],
-    sudo,
-    applicationStake,
-    roleStake,
-    openingActivationDelay,
-    unstakingPeriod,
-    WorkingGroups.StorageWorkingGroup
-  )
-  tap.test('Add worker opening', async () => await addWorkerOpeningFixture.runner(false))
-
-  let applyForWorkerOpeningBeforeAcceptanceFixture: ApplyForOpeningFixture
-  tap.test('Apply for worker opening, expect failure', async () => {
-    applyForWorkerOpeningBeforeAcceptanceFixture = new ApplyForOpeningFixture(
-      apiWrapper,
-      nKeyPairs,
-      sudo,
-      applicationStake,
-      roleStake,
-      addWorkerOpeningFixture.getCreatedOpeningId() as OpeningId,
-      WorkingGroups.StorageWorkingGroup
-    )
-    await applyForWorkerOpeningBeforeAcceptanceFixture.runner(true)
-  })
-
-  let acceptApplicationsFixture: AcceptApplicationsFixture
-  tap.test('Begin accepting worker applications', async () => {
-    acceptApplicationsFixture = new AcceptApplicationsFixture(
-      apiWrapper,
-      leadKeyPair[0],
-      sudo,
-      addWorkerOpeningFixture.getCreatedOpeningId() as OpeningId,
-      WorkingGroups.StorageWorkingGroup
-    )
-    await acceptApplicationsFixture.runner(false)
-  })
-
-  let applyForWorkerOpeningAsNonMemberFixture: ApplyForOpeningFixture
-  tap.test('Apply for worker opening as non-member, expect failure', async () => {
-    applyForWorkerOpeningAsNonMemberFixture = new ApplyForOpeningFixture(
-      apiWrapper,
-      nonMemberKeyPairs,
-      sudo,
-      applicationStake,
-      roleStake,
-      addWorkerOpeningFixture.getCreatedOpeningId() as OpeningId,
-      WorkingGroups.StorageWorkingGroup
-    )
-    await applyForWorkerOpeningAsNonMemberFixture.runner(true)
-  })
-
-  let applyForWorkerOpeningFixture: ApplyForOpeningFixture
-  tap.test('Apply for worker opening', async () => {
-    applyForWorkerOpeningFixture = new ApplyForOpeningFixture(
-      apiWrapper,
-      nKeyPairs,
-      sudo,
-      applicationStake,
-      roleStake,
-      addWorkerOpeningFixture.getCreatedOpeningId() as OpeningId,
-      WorkingGroups.StorageWorkingGroup
-    )
-    await applyForWorkerOpeningFixture.runner(false)
-  })
-
-  const terminateApplicationsFixture: TerminateApplicationsFixture = new TerminateApplicationsFixture(
-    apiWrapper,
-    nKeyPairs,
-    leadKeyPair[0],
-    sudo,
-    WorkingGroups.StorageWorkingGroup
-  )
-  tap.test('Terminate worker applicaitons', async () => await terminateApplicationsFixture.runner(false))
-
-  if (!db.hasLeader(apiWrapper.getWorkingGroupString(WorkingGroups.StorageWorkingGroup))) {
-    const leaveRoleFixture: LeaveRoleFixture = new LeaveRoleFixture(
-      apiWrapper,
-      leadKeyPair,
-      sudo,
-      WorkingGroups.StorageWorkingGroup
-    )
-    tap.test('Leaving lead role', async () => await leaveRoleFixture.runner(false))
-  }
-
-  closeApi(apiWrapper)
-})

Some files were not shown because too many files changed in this diff