git.delta.rocks / unique-network / refs/commits / f3f56ccc5d6d

difftreelog

Merge pull request #988 from UniqueNetwork/fix/collector-selection-tests-release-60

Yaroslav Bolyukin2023-09-08parents: #e6eb884 #095843d.patch.diff
in: master

9 files changed

modified.baedeker/collator-selection.jsonnetdiffbeforeafterboth
--- a/.baedeker/collator-selection.jsonnet
+++ b/.baedeker/collator-selection.jsonnet
@@ -31,7 +31,7 @@
 			bin: $.bin,
 			wantedKeys: 'para',
 		},
-		for name in ['alice', 'bob']
+		for name in ['alpha', 'beta', 'gamma', 'delta']
 	},
 };
 
modified.docker/docker-compose.gov.j2diffbeforeafterboth
--- a/.docker/docker-compose.gov.j2
+++ b/.docker/docker-compose.gov.j2
@@ -21,4 +21,4 @@
       options:
         max-size: "1m"
         max-file: "3"
-    command: cargo run --release --features={{ NETWORK }}-runtime,{{ WASM_NAME }}-runtime/gov-test-timings -- --dev -linfo --rpc-cors=all --unsafe-rpc-external
+    command: cargo run --release --features={{ NETWORK }}-runtime,gov-test-timings -- --dev -linfo --rpc-cors=all --unsafe-rpc-external
modified.github/workflows/collator-selection.ymldiffbeforeafterboth
after · .github/workflows/collator-selection.yml
1name: collator-selection-test-run23# Controls when the action will run.4on:5  workflow_call:67  # Allows you to run this workflow manually from the Actions tab8  workflow_dispatch:910#Define Workflow variables11env:12  REPO_URL: ${{ github.server_url }}/${{ github.repository }}1314# A workflow run is made up of one or more jobs that can run sequentially or in parallel15jobs:1617  prepare-execution-marix:1819    name: Prepare execution matrix2021    runs-on: [self-hosted-ci]22    outputs:23      matrix: ${{ steps.create_matrix.outputs.matrix }}2425    steps:2627      - name: Clean Workspace28        uses: AutoModality/action-clean@v1.1.02930      # Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it31      - uses: actions/checkout@v3.1.032        with:33          ref: ${{ github.head_ref }}  #Checking out head commit3435      - name: Read .env file36        uses: xom9ikk/dotenv@v23738      - name: Create Execution matrix39        uses: CertainLach/create-matrix-action@v440        id: create_matrix41        with:42          matrix: |43            network {opal}, relay_branch {${{ env.UNIQUEWEST_MAINNET_BRANCH }}}44            network {quartz}, relay_branch {${{ env.KUSAMA_MAINNET_BRANCH }}}45            network {sapphire}, relay_branch {${{ env.UNIQUEEAST_MAINNET_BRANCH }}}4647  collator-selection:4849    needs: prepare-execution-marix50    # The type of runner that the job will run on51    runs-on: [self-hosted-ci,large]5253    timeout-minutes: 13805455    name: ${{ matrix.network }}5657    continue-on-error: true         #Do not stop testing of matrix runs failed.  As it decided during PR review - it required 50/50& Let's check it with false.5859    strategy:60      matrix:61        include: ${{fromJson(needs.prepare-execution-marix.outputs.matrix)}}6263    steps:64      - name: Skip if pull request is in Draft65        if: github.event.pull_request.draft == true66        run: exit 16768      - name: Clean Workspace69        uses: AutoModality/action-clean@v1.1.07071      # Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it72      - uses: actions/checkout@v3.1.073        with:74          ref: ${{ github.head_ref }}  #Checking out head commit7576      # Prepare SHA77      - name: Prepare SHA78        uses: ./.github/actions/prepare7980      - name: Read .env file81        uses: xom9ikk/dotenv@v28283      - name: Log in to Docker Hub84        uses: docker/login-action@v2.1.085        with:86          username: ${{ secrets.CORE_DOCKERHUB_USERNAME }}87          password: ${{ secrets.CORE_DOCKERHUB_TOKEN }}8889      - name: Run find-and-replace to remove slashes from branch name90        uses: mad9000/actions-find-and-replace-string@491        id: branchname92        with:93          source: ${{ github.head_ref }}94          find: '/'95          replace: '-'9697      - name: Generate ENV related extend Dockerfile file for POLKADOT98        uses: cuchi/jinja2-action@v1.2.099        with:100          template: .docker/Dockerfile-polkadot.j2101          output_file: .docker/Dockerfile-polkadot.${{ matrix.relay_branch }}.yml102          variables: |103            RUST_TOOLCHAIN=${{ env.RUST_TOOLCHAIN }}104            POLKADOT_BUILD_BRANCH=${{ matrix.relay_branch }}105            106      - name: Prepare polkadot107        uses: ./.github/actions/buildContainer108        id: polkadot109        with:110          container: uniquenetwork/builder-polkadot111          tag: ${{ matrix.relay_branch }}112          context: .docker113          dockerfile: Dockerfile-polkadot.${{ matrix.relay_branch }}.yml114          dockerhub_username: ${{ secrets.CORE_DOCKERHUB_USERNAME }}115          dockerhub_token: ${{ secrets.CORE_DOCKERHUB_TOKEN }}116117      # - name: pull needed images118      #   run: |119      #     docker pull uniquenetwork/builder-polkadot:${{ matrix.relay_branch }}120121      # - name: Build unique-chain122      #   run: |123      #     docker build --file .docker/Dockerfile-unique \124      #       --build-arg RUNTIME_FEATURES=${{ matrix.runtime_features }} \125      #       --build-arg RUST_TOOLCHAIN=${{ env.RUST_TOOLCHAIN }} \126      #       --tag uniquenetwork/ci-collator-selection-local:${{ matrix.network }}-${{ steps.branchname.outputs.value }}-${{ env.BUILD_SHA }} \127      #       .128129      # - name: Push docker image version130      #   run: docker push uniquenetwork/ci-collator-selection-local:${{ matrix.network }}-${{ steps.branchname.outputs.value }}-${{ env.BUILD_SHA }}131132133      - name: Prepare latest134        uses: ./.github/actions/buildContainer135        id: latest136        with:137          container: uniquenetwork/ci-collator-selection-local138          tag: ${{ matrix.network }}-${{ env.REF_SLUG }}-${{ env.BUILD_SHA }}139          context: .140          dockerfile: .docker/Dockerfile-unique141          args: |142            --build-arg RUNTIME_FEATURES=${{ matrix.runtime_features }},session-test-timings143            --build-arg RUST_TOOLCHAIN=${{ env.RUST_TOOLCHAIN }}144          dockerhub_username: ${{ secrets.CORE_DOCKERHUB_USERNAME }}145          dockerhub_token: ${{ secrets.CORE_DOCKERHUB_TOKEN }}146147      - uses: actions/setup-node@v3.5.1148        with:149          node-version: 18150151      - name: Install baedeker152        uses: UniqueNetwork/baedeker-action/setup@built153154      - name: Setup library155        run: mkdir -p .baedeker/vendor/ && git clone https://github.com/UniqueNetwork/baedeker-library .baedeker/vendor/baedeker-library156157      - name: Start network158        uses: UniqueNetwork/baedeker-action@built159        id: bdk160        with:161          jpath: |162            .baedeker/vendor163          tla-str: |164            relay_spec=westend-local165          inputs: |166            .baedeker/collator-selection.jsonnet167            snippet:(import 'baedeker-library/ops/rewrites.libsonnet').rewriteNodePaths({'bin/polkadot':{dockerImage:'${{ steps.polkadot.outputs.name }}'}})168            snippet:(import 'baedeker-library/ops/rewrites.libsonnet').rewriteNodePaths({'bin/unique':{dockerImage:'${{ steps.latest.outputs.name }}'}})169170      - name: Upload network config171        uses: actions/upload-artifact@v3172        with:173          name: ${{ matrix.network }}-network-config174          path: ${{ steps.bdk.outputs.composeProject }}175          retention-days: 2176177      - name: Run tests178        working-directory: tests179        run: |180          yarn install181          yarn add mochawesome182          # Wanted by both wait_for_first_block183          # export RPC_URL="${RELAY_SAPPHIRE_HTTP_URL:-${RELAY_OPAL_HTTP_URL:-${RELAY_QUARTZ_HTTP_URL:-${RELAY_UNIQUE_HTTP_URL:-}}}}"184          ./scripts/wait_for_first_block.sh185          echo "Ready to start tests"186          NOW=$(date +%s) && yarn testCollators --reporter mochawesome --reporter-options reportFilename=test-collators-${NOW}187        env:188          RPC_URL: ${{ env.RELAY_UNIQUE_HTTP_URL }}          189190      - name: Test Report191        uses: phoenix-actions/test-reporting@v10192        id: test-report193        if: success() || failure()194        with:195          name: Collator Selection Tests ${{ matrix.network }}196          path: tests/mochawesome-report/test-collators-*.json197          reporter: mochawesome-json198          fail-on-error: 'false'199200      - name: Clean Workspace201        if: always()202        uses: AutoModality/action-clean@v1.1.0203204      - name: Remove builder cache205        if: always()206        run: |207          docker system prune -a -f
modifiednode/cli/Cargo.tomldiffbeforeafterboth
--- a/node/cli/Cargo.toml
+++ b/node/cli/Cargo.toml
@@ -108,3 +108,5 @@
 runtime-benchmarks = ['opal-runtime/runtime-benchmarks', 'polkadot-cli/runtime-benchmarks', 'polkadot-service/runtime-benchmarks', 'quartz-runtime?/runtime-benchmarks', 'sc-service/runtime-benchmarks', 'unique-runtime?/runtime-benchmarks']
 sapphire-runtime = ['quartz-runtime', 'quartz-runtime/become-sapphire']
 try-runtime = ['opal-runtime?/try-runtime', 'quartz-runtime?/try-runtime', 'try-runtime-cli/try-runtime', 'unique-runtime?/try-runtime']
+session-test-timings = ['opal-runtime/session-test-timings', 'quartz-runtime?/session-test-timings', 'unique-runtime?/session-test-timings']
+gov-test-timings = ['opal-runtime/gov-test-timings', 'quartz-runtime?/gov-test-timings', 'unique-runtime?/gov-test-timings']
modifiedruntime/common/config/pallets/mod.rsdiffbeforeafterboth
--- a/runtime/common/config/pallets/mod.rs
+++ b/runtime/common/config/pallets/mod.rs
@@ -125,10 +125,20 @@
 	pub AppPromotionDailyRate: Perbill = Perbill::from_rational(5u32, 10_000);
 	pub const MaxCollators: u32 = MAX_COLLATORS;
 	pub const LicenseBond: Balance = GENESIS_LICENSE_BOND;
+
+	pub const DayRelayBlocks: BlockNumber = RELAY_DAYS;
+}
+
+#[cfg(not(feature = "session-test-timings"))]
+parameter_types! {
 	pub const SessionPeriod: BlockNumber = SESSION_LENGTH;
-	pub const DayRelayBlocks: BlockNumber = RELAY_DAYS;
 }
 
+#[cfg(feature = "session-test-timings")]
+parameter_types! {
+	pub const SessionPeriod: BlockNumber = 5 * MINUTES;
+}
+
 impl pallet_configuration::Config for Runtime {
 	type RuntimeEvent = RuntimeEvent;
 	type Balance = Balance;
modifiedruntime/opal/Cargo.tomldiffbeforeafterboth
--- a/runtime/opal/Cargo.toml
+++ b/runtime/opal/Cargo.toml
@@ -229,6 +229,7 @@
 preimage = []
 refungible = []
 unique-scheduler = []
+session-test-timings = []
 
 ################################################################################
 # local dependencies
modifiedruntime/quartz/Cargo.tomldiffbeforeafterboth
--- a/runtime/quartz/Cargo.toml
+++ b/runtime/quartz/Cargo.toml
@@ -218,6 +218,7 @@
 refungible = []
 unique-scheduler = []
 gov-test-timings = []
+session-test-timings = []
 
 ################################################################################
 # local dependencies
modifiedruntime/unique/Cargo.tomldiffbeforeafterboth
--- a/runtime/unique/Cargo.toml
+++ b/runtime/unique/Cargo.toml
@@ -221,6 +221,7 @@
 refungible = []
 unique-scheduler = []
 gov-test-timings = []
+session-test-timings = []
 
 ################################################################################
 # local dependencies
modifiedtests/src/collator-selection/collatorSelection.seqtest.tsdiffbeforeafterboth
--- a/tests/src/collator-selection/collatorSelection.seqtest.ts
+++ b/tests/src/collator-selection/collatorSelection.seqtest.ts
@@ -17,36 +17,48 @@
 import {IKeyringPair} from '@polkadot/types/types';
 import {usingPlaygrounds, expect, itSub, Pallets, requirePalletsOrSkip} from '../util';
 
+async function nodeAddress(name: string) {
+  // eslint-disable-next-line require-await
+  return await usingPlaygrounds(async (helper, _) => {
+    const envNodeStash = `RELAY_UNIQUE_NODE_${name.toUpperCase()}_STASH`;
+
+    const nodeStash = process.env[envNodeStash];
+    if(nodeStash) {
+      return helper.address.normalizeSubstrateToChainFormat(nodeStash);
+    } else {
+      throw Error(`"${envNodeStash}" env var is not set`);
+    }
+  });
+}
+
+async function getInitialInvulnerables() {
+  return await Promise.all([
+    nodeAddress('alpha'),
+    nodeAddress('beta'),
+    nodeAddress('gamma'),
+    nodeAddress('delta'),
+  ]);
+}
+
 async function resetInvulnerables() {
   await usingPlaygrounds(async (helper, privateKey) => {
     const superuser = await privateKey('//Alice');
-    const alice = await privateKey('//Alice');
-    const bob = await privateKey('//Bob');
+    const initialInvulnerables = await getInitialInvulnerables();
+
     const invulnerables = await helper.collatorSelection.getInvulnerables();
-    if(!invulnerables.includes(alice.address) || !invulnerables.includes(bob.address) || invulnerables.length != 2) {
-      console.warn('Alice and Bob are not the invulnerables! Reinstating them back. '
-        + 'Current invulnerables\' size: ' + invulnerables.length);
 
-      let nonce = await helper.chain.getNonce(alice.address);
-      // In case there are too many invulnerables already, remove some of them, leaving space for Alice and Bob.
-      if(invulnerables.length + 2 >= helper.collatorSelection.maxCollators()) {
-        await Promise.all([
-          helper.getSudo().executeExtrinsic(superuser, 'api.tx.collatorSelection.removeInvulnerable', [invulnerables.pop()!], true, {nonce: nonce++}),
-          helper.getSudo().executeExtrinsic(superuser, 'api.tx.collatorSelection.removeInvulnerable', [invulnerables.pop()!], true, {nonce: nonce++}),
-        ]);
-      }
+    // Remove all invulnerables but the first one
+    const firstInvulnerable = invulnerables[0];
+
+    let nonce = await helper.chain.getNonce(superuser.address);
+    await Promise.all(invulnerables.slice(1).map(invulnerable => helper.getSudo().executeExtrinsic(superuser, 'api.tx.collatorSelection.removeInvulnerable', [invulnerable], true, {nonce: nonce++})));
 
-      nonce = await helper.chain.getNonce(alice.address);
-      await Promise.all([
-        helper.getSudo().executeExtrinsic(superuser, 'api.tx.collatorSelection.addInvulnerable', [alice.address], true, {nonce: nonce++}),
-        helper.getSudo().executeExtrinsic(superuser, 'api.tx.collatorSelection.addInvulnerable', [bob.address], true, {nonce: nonce++}),
-      ]);
+    // Add the initial invulnerables
+    await Promise.all(initialInvulnerables.map(invulnerable => helper.getSudo().executeExtrinsic(superuser, 'api.tx.collatorSelection.addInvulnerable', [invulnerable], true, {nonce: nonce++})));
 
-      nonce = await helper.chain.getNonce(alice.address);
-      await Promise.all(invulnerables.map((invulnerable: any) => {
-        if(invulnerable == alice.address || invulnerable == bob.address) return new Promise<void>(res => res());
-        return helper.getSudo().executeExtrinsic(superuser, 'api.tx.collatorSelection.removeInvulnerable', [invulnerable], true, {nonce: nonce++});
-      }));
+    // Remove the first invulnerable if it's not an initial one
+    if(!initialInvulnerables.includes(firstInvulnerable)) {
+      await helper.getSudo().executeExtrinsic(superuser, 'api.tx.collatorSelection.addInvulnerable', [firstInvulnerable]);
     }
   });
 }
@@ -72,11 +84,11 @@
 
   describe('Dynamic shuffling of collators', () => {
     // These two are the default invulnerables, and should return to be invulnerables after this suite.
-    let alice: IKeyringPair;
-    let bob: IKeyringPair;
+    let alphaNode: string;
+    let betaNode: string;
 
-    let charlie: IKeyringPair;
-    let dave: IKeyringPair;
+    let gammaNode: string;
+    let deltaNode: string;
 
     before(async function() {
       await usingPlaygrounds(async (helper, privateKey) => {
@@ -84,99 +96,51 @@
         // Skip the collator block production in dev mode, since the blocks are sealed automatically.
         if(await helper.arrange.isDevNode()) this.skip();
 
-        alice = await privateKey('//Alice');
-        bob = await privateKey('//Bob');
-        charlie = await privateKey('//Charlie');
-        dave = await privateKey('//Dave');
-
-        expect((await helper.session.setOwnKeysFromAddress(charlie))
-          .status.toLowerCase()).to.be.equal('success');
-        expect((await helper.session.setOwnKeysFromAddress(dave))
-          .status.toLowerCase()).to.be.equal('success');
+        [alphaNode, betaNode, gammaNode, deltaNode] = await getInitialInvulnerables();
 
         const invulnerables = await helper.collatorSelection.getInvulnerables();
-        if(!invulnerables.includes(alice.address) || !invulnerables.includes(bob.address) || invulnerables.length != 2) {
-          console.warn('Alice and Bob are not the invulnerables! Reinstating them back. '
-            + 'Current invulnerables\' size: ' + invulnerables.length);
-
-          let nonce = await helper.chain.getNonce(superuser.address);
-          await Promise.all([
-            helper.getSudo().executeExtrinsic(superuser, 'api.tx.collatorSelection.addInvulnerable', [alice.address], true, {nonce: nonce++}),
-            helper.getSudo().executeExtrinsic(superuser, 'api.tx.collatorSelection.addInvulnerable', [bob.address], true, {nonce: nonce++}),
-          ]);
-
-          nonce = await helper.chain.getNonce(superuser.address);
-          await Promise.all(invulnerables.map((invulnerable: any) => {
-            if(invulnerable == alice.address || invulnerable == bob.address) return new Promise((res) => res);
-            return helper.getSudo().executeExtrinsic(superuser, 'api.tx.collatorSelection.removeInvulnerable', [invulnerable], true, {nonce: nonce++});
-          }));
-        }
+        expect(invulnerables.length, 'Invalid initial invulnerables number').to.be.equal(4);
+        expect(invulnerables, 'Invalid initial invulnerables').containSubset([alphaNode, betaNode, gammaNode, deltaNode]);
       });
     });
 
     itSub('Change invulnerables and make sure they start producing blocks', async ({helper}) => {
       let nonce = await helper.chain.getNonce(superuser.address);
-      await expect(Promise.all([
-        helper.getSudo().executeExtrinsic(superuser, 'api.tx.collatorSelection.addInvulnerable', [charlie.address], true, {nonce: nonce++}),
-        helper.getSudo().executeExtrinsic(superuser, 'api.tx.collatorSelection.addInvulnerable', [dave.address], true, {nonce: nonce++}),
-      ])).to.be.fulfilled;
 
       nonce = await helper.chain.getNonce(superuser.address);
       await expect(Promise.all([
-        helper.getSudo().executeExtrinsic(superuser, 'api.tx.collatorSelection.removeInvulnerable', [alice.address], true, {nonce: nonce++}),
-        helper.getSudo().executeExtrinsic(superuser, 'api.tx.collatorSelection.removeInvulnerable', [bob.address], true, {nonce: nonce++}),
+        helper.getSudo().executeExtrinsic(superuser, 'api.tx.collatorSelection.removeInvulnerable', [alphaNode], true, {nonce: nonce++}),
+        helper.getSudo().executeExtrinsic(superuser, 'api.tx.collatorSelection.removeInvulnerable', [betaNode], true, {nonce: nonce++}),
       ])).to.be.fulfilled;
 
       const newInvulnerables = await helper.collatorSelection.getInvulnerables();
-      expect(newInvulnerables).to.contain(charlie.address).and.contain(dave.address).and.be.length(2);
+      expect(newInvulnerables).to.contain(gammaNode).and.contain(deltaNode).and.be.length(2);
 
       await helper.wait.newSessions(2);
 
       const newValidators = await helper.callRpc('api.query.session.validators');
-      expect(newValidators).to.contain(charlie.address).and.contain(dave.address).and.be.length(2);
+      expect(newValidators).to.contain(gammaNode).and.contain(deltaNode).and.be.length(2);
 
       const lastBlockNumber = await helper.chain.getLatestBlockNumber();
       await helper.wait.newBlocks(1);
-      const lastCharlieBlock = (await helper.callRpc('api.query.collatorSelection.lastAuthoredBlock', [charlie.address])).toNumber();
-      const lastDaveBlock = (await helper.callRpc('api.query.collatorSelection.lastAuthoredBlock', [dave.address])).toNumber();
-      expect(lastCharlieBlock >= lastBlockNumber || lastDaveBlock >= lastBlockNumber).to.be.true;
+      const lastGammaBlock = (await helper.callRpc('api.query.collatorSelection.lastAuthoredBlock', [gammaNode])).toNumber();
+      const lastDeltaBlock = (await helper.callRpc('api.query.collatorSelection.lastAuthoredBlock', [deltaNode])).toNumber();
+      expect(lastGammaBlock >= lastBlockNumber || lastDeltaBlock >= lastBlockNumber).to.be.true;
     });
 
     after(async () => {
-      await usingPlaygrounds(async (helper) => {
-        if(await helper.arrange.isDevNode()) return;
-
-        let nonce = await helper.chain.getNonce(superuser.address);
-        await Promise.all([
-          helper.getSudo().executeExtrinsic(superuser, 'api.tx.collatorSelection.addInvulnerable', [alice.address], true, {nonce: nonce++}),
-          helper.getSudo().executeExtrinsic(superuser, 'api.tx.collatorSelection.addInvulnerable', [bob.address], true, {nonce: nonce++}),
-        ]);
-
-        nonce = await helper.chain.getNonce(superuser.address);
-        await Promise.all([
-          await helper.getSudo().executeExtrinsic(superuser, 'api.tx.collatorSelection.removeInvulnerable', [charlie.address], true, {nonce: nonce++}),
-          await helper.getSudo().executeExtrinsic(superuser, 'api.tx.collatorSelection.removeInvulnerable', [dave.address], true, {nonce: nonce++}),
-        ]);
-      });
+      await resetInvulnerables();
     });
   });
 
   describe('Getting and releasing licenses to collate', () => {
-    let charlie: IKeyringPair;
-    let dave: IKeyringPair;
     let crowd: IKeyringPair[];
 
     before(async function() {
       await usingPlaygrounds(async (helper, privateKey) => {
-        charlie = await privateKey('//Charlie');
-        dave = await privateKey('//Dave');
         crowd = await helper.arrange.createCrowd(20, 100n, superuser);
 
         // set session keys for everyone
-        expect((await helper.session.setOwnKeysFromAddress(charlie))
-          .status.toLowerCase()).to.be.equal('success');
-        expect((await helper.session.setOwnKeysFromAddress(dave))
-          .status.toLowerCase()).to.be.equal('success');
         await Promise.all(crowd.map(acc => helper.session.setOwnKeysFromAddress(acc)));
       });
     });
@@ -252,22 +216,13 @@
   });
 
   describe('Onboarding, collating, and offboarding as collator candidates', () => {
-    // These two are the default invulnerables, and should return to be invulnerables after this suite.
-    let charlie: IKeyringPair;
-    let dave: IKeyringPair;
     let crowd: IKeyringPair[];
 
     before(async function() {
       await usingPlaygrounds(async (helper, privateKey) => {
-        charlie = await privateKey('//Charlie');
-        dave = await privateKey('//Dave');
         crowd = await helper.arrange.createCrowd(20, 100n, superuser);
 
         // set session keys for everyone
-        expect((await helper.session.setOwnKeysFromAddress(charlie))
-          .status.toLowerCase()).to.be.equal('success');
-        expect((await helper.session.setOwnKeysFromAddress(dave))
-          .status.toLowerCase()).to.be.equal('success');
         await Promise.all(crowd.map(acc => helper.session.setOwnKeysFromAddress(acc)));
       });
     });
@@ -341,10 +296,6 @@
   });
 
   describe('Addition and removal of invulnerables', () => {
-    before(async function() {
-      await resetInvulnerables();
-    });
-
     describe('Positive', () => {
       itSub('Adds an invulnerable', async ({helper}) => {
         const [account] = await helper.arrange.createAccounts([10n], superuser);
@@ -409,7 +360,7 @@
         // 28 non-functioning collators, teehee.
 
         const invulnerablesLength = (await helper.collatorSelection.getInvulnerables()).length;
-        const invulnerablesUntilLimit = helper.collatorSelection.maxCollators() - invulnerablesLength;
+        const invulnerablesUntilLimit = (await helper.collatorSelection.getDesiredCollators()) - invulnerablesLength;
         const newInvulnerables = await helper.arrange.createAccounts(Array(invulnerablesUntilLimit).fill(10n), superuser);
         const [lastInvulnerable] = await helper.arrange.createAccounts([10n], superuser);
 
@@ -448,6 +399,10 @@
           .to.be.rejectedWith(/BadOrigin/);
         expect(await helper.collatorSelection.getInvulnerables()).to.have.all.members(invulnerables);
       });
+
+      after(async function() {
+        await resetInvulnerables();
+      });
     });
   });