git.delta.rocks / unique-network / refs/commits / 218082ec0405

difftreelog

tests(collator-selection): further fixes

Fahrrader2022-12-21parent: #33d3cd0.patch.diff
in: master

2 files changed

modifiedpallets/collator-selection/src/tests.rsdiffbeforeafterboth
--- a/pallets/collator-selection/src/tests.rs
+++ b/pallets/collator-selection/src/tests.rs
@@ -73,10 +73,7 @@
 
 		// cannot set invulnerables without associated validator keys
 		assert_noop!(
-			CollatorSelection::add_invulnerable(
-				RuntimeOrigin::signed(RootAccount::get()),
-				7
-			),
+			CollatorSelection::add_invulnerable(RuntimeOrigin::signed(RootAccount::get()), 7),
 			Error::<Test>::ValidatorNotRegistered
 		);
 	});
@@ -108,11 +105,8 @@
 
 		// cannot remove an invulnerable if there would be 0 invulnerables.
 		assert_noop!(
-			CollatorSelection::add_invulnerable(
-				RuntimeOrigin::signed(RootAccount::get()), 
-				1
-			),
-			Error::<Test>::NotInvulnerable
+			CollatorSelection::remove_invulnerable(RuntimeOrigin::signed(RootAccount::get()), 1),
+			Error::<Test>::TooFewInvulnerables
 		);
 	});
 }
modifiedtests/src/collatorSelection.seqtest.tsdiffbeforeafterboth
before · tests/src/collatorSelection.seqtest.ts
1// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.2// This file is part of Unique Network.34// Unique Network is free software: you can redistribute it and/or modify5// it under the terms of the GNU General Public License as published by6// the Free Software Foundation, either version 3 of the License, or7// (at your option) any later version.89// Unique Network is distributed in the hope that it will be useful,10// but WITHOUT ANY WARRANTY; without even the implied warranty of11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the12// GNU General Public License for more details.1314// You should have received a copy of the GNU General Public License15// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.1617import {IKeyringPair} from '@polkadot/types/types';18import {usingPlaygrounds, expect, itSub, Pallets, requirePalletsOrSkip} from './util';1920async function resetInvulnerables() {21  await usingPlaygrounds(async (helper, privateKey) => {22    const superuser = await privateKey('//Alice');23    const alice = await privateKey('//Alice');24    const bob = await privateKey('//Bob');25    const invulnerables = await helper.collatorSelection.getInvulnerables();26    if (!invulnerables.includes(alice.address) || !invulnerables.includes(bob.address) || invulnerables.length != 2) {27      console.warn('Alice and Bob are not the invulnerables! Reinstating them back. ' 28        + 'Current invulnerables\' size: ' + invulnerables.length);29      30      let nonce = await helper.chain.getNonce(alice.address);31      await Promise.all([32        helper.getSudo().executeExtrinsic(superuser, 'api.tx.collatorSelection.addInvulnerable', [alice.address], true, {nonce: nonce++}),33        helper.getSudo().executeExtrinsic(superuser, 'api.tx.collatorSelection.addInvulnerable', [bob.address], true, {nonce: nonce++}),34      ]);3536      nonce = await helper.chain.getNonce(alice.address);37      await Promise.all(invulnerables.map((invulnerable: any) => {38        if (invulnerable == alice.address || invulnerable == bob.address) return new Promise<void>(res => res());39        return helper.getSudo().executeExtrinsic(superuser, 'api.tx.collatorSelection.removeInvulnerable', [invulnerable], true, {nonce: nonce++});40      }));41    }42  });43}4445// todo:collator Most preferable to launch this test in parallel somehow -- or change the session period (1 hr).46// + 18 tests: 5 (1+4) on session change47describe('Integration Test: Collator Selection', () => {48  let superuser: IKeyringPair;4950  before(async function() {  51    await usingPlaygrounds(async (helper, privateKey) => {52      requirePalletsOrSkip(this, helper, [Pallets.CollatorSelection]);53      superuser = await privateKey('//Alice');54    });55  });5657  describe('Dynamic shuffling of collators', () => {58    // These two are the default invulnerables, and should return to be invulnerables after this suite.59    let alice: IKeyringPair;60    let bob: IKeyringPair;6162    let charlie: IKeyringPair;63    let dave: IKeyringPair;64    65    before(async function() {  66      await usingPlaygrounds(async (helper, privateKey) => {67        alice = await privateKey('//Alice');68        bob = await privateKey('//Bob');69        charlie = await privateKey('//Charlie');70        dave = await privateKey('//Dave');7172        expect((await helper.collatorSelection.setOwnKeys(charlie))73          .status.toLowerCase()).to.be.equal('success');74        expect((await helper.collatorSelection.setOwnKeys(dave))75          .status.toLowerCase()).to.be.equal('success');76  77        // todo:collator check necessity + add RPC for invulnerables / just improve in general78        // validators = await helper.callRpc('api.query.session.validators');79        const invulnerables = await helper.collatorSelection.getInvulnerables();80        if (!invulnerables.includes(alice.address) || !invulnerables.includes(bob.address) || invulnerables.length != 2) {81          console.warn('Alice and Bob are not the invulnerables! Reinstating them back. ' 82            + 'Current invulnerables\' size: ' + invulnerables.length);83          84          let nonce = await helper.chain.getNonce(superuser.address);85          await Promise.all([86            helper.getSudo().executeExtrinsic(superuser, 'api.tx.collatorSelection.addInvulnerable', [alice.address], true, {nonce: nonce++}),87            helper.getSudo().executeExtrinsic(superuser, 'api.tx.collatorSelection.addInvulnerable', [bob.address], true, {nonce: nonce++}),88          ]);89  90          nonce = await helper.chain.getNonce(superuser.address);91          await Promise.all(invulnerables.map((invulnerable: any) => {92            if (invulnerable == alice.address || invulnerable == bob.address) return new Promise((res) => res);93            return helper.getSudo().executeExtrinsic(superuser, 'api.tx.collatorSelection.removeInvulnerable', [invulnerable], true, {nonce: nonce++});94          }));95        }96      });97    });98  99    itSub('Change invulnerables and make sure they start producing blocks', async ({helper}) => {100      let nonce = await helper.chain.getNonce(superuser.address);101      await expect(Promise.all([102        helper.getSudo().executeExtrinsic(superuser, 'api.tx.collatorSelection.addInvulnerable', [charlie.address], true, {nonce: nonce++}),103        helper.getSudo().executeExtrinsic(superuser, 'api.tx.collatorSelection.addInvulnerable', [dave.address], true, {nonce: nonce++}),104      ])).to.be.fulfilled;105  106      nonce = await helper.chain.getNonce(superuser.address);107      await expect(Promise.all([108        helper.getSudo().executeExtrinsic(superuser, 'api.tx.collatorSelection.removeInvulnerable', [alice.address], true, {nonce: nonce++}),109        helper.getSudo().executeExtrinsic(superuser, 'api.tx.collatorSelection.removeInvulnerable', [bob.address], true, {nonce: nonce++}),110      ])).to.be.fulfilled;111  112      const newInvulnerables = await helper.collatorSelection.getInvulnerables();113      expect(newInvulnerables).to.contain(charlie.address).and.contain(dave.address).and.be.length(2);114  115      const expectedSessionIndex = (await helper.callRpc('api.query.session.currentIndex')).toNumber() + 2;116      let currentSessionIndex = -1;117      console.log('Waiting for the session after the next.' 118        + ' This might take a while -- check SessionPeriod in pallet_session::Config for session time.');119  120      while (currentSessionIndex < expectedSessionIndex) {121        // eslint-disable-next-line no-async-promise-executor122        currentSessionIndex = await expect(helper.wait.withTimeout(new Promise(async (resolve) => {123          await helper.wait.newBlocks(1);124          const res = (await helper.callRpc('api.query.session.currentIndex')).toNumber();125          resolve(res);126        }), 24000, 'The chain has stopped producing blocks!')).to.be.fulfilled;127      }128  129      const newValidators = await helper.callRpc('api.query.session.validators');130      expect(newValidators).to.contain(charlie.address).and.contain(dave.address).and.be.length(2);131  132      const lastBlockNumber = await helper.chain.getLatestBlockNumber();133      await helper.wait.newBlocks(1);134      const lastCharlieBlock = (await helper.callRpc('api.query.collatorSelection.lastAuthoredBlock', [charlie.address])).toNumber();135      const lastDaveBlock = (await helper.callRpc('api.query.collatorSelection.lastAuthoredBlock', [dave.address])).toNumber();136      expect(lastCharlieBlock >= lastBlockNumber || lastDaveBlock >= lastBlockNumber).to.be.true;137    });138  139    // todo:collator keyless invulnerables? will hang, so, a breaking test, eh140    // register candidate without sudos and the like141  142    after(async () => {143      await usingPlaygrounds(async (helper) => {144        if (helper.fetchMissingPalletNames([Pallets.CollatorSelection]).length != 0) return;145146        let nonce = await helper.chain.getNonce(superuser.address);147        await Promise.all([148          helper.getSudo().executeExtrinsic(superuser, 'api.tx.collatorSelection.addInvulnerable', [alice.address], true, {nonce: nonce++}),149          helper.getSudo().executeExtrinsic(superuser, 'api.tx.collatorSelection.addInvulnerable', [bob.address], true, {nonce: nonce++}),150        ]);151  152        nonce = await helper.chain.getNonce(superuser.address);153        await Promise.all([154          await helper.getSudo().executeExtrinsic(superuser, 'api.tx.collatorSelection.removeInvulnerable', [charlie.address], true, {nonce: nonce++}),155          await helper.getSudo().executeExtrinsic(superuser, 'api.tx.collatorSelection.removeInvulnerable', [dave.address], true, {nonce: nonce++}),156        ]);157      });158    });159  });160161  // todo:collator make sure that there is enough session time for a set of tests162  // 28 non-functioning collators, teehee.163164  describe.skip('Addition and removal of invulnerables', () => {165    before(async function() {166      await resetInvulnerables();167    });168169    describe('Positive', () => {170      itSub('Adds an invulnerable', async ({helper}) => {171        const [account] = await helper.arrange.createAccounts([10n], superuser);172        const invulnerables = await helper.collatorSelection.getInvulnerables();173174        await helper.collatorSelection.setOwnKeys(account);175        await helper.getSudo().collatorSelection.addInvulnerable(superuser, account.address);176        177        const newInvulnerables = await helper.collatorSelection.getInvulnerables();178        expect(invulnerables.concat(account.address)).to.have.all.members(newInvulnerables);179      });180181      itSub('Removes an invulnerable', async ({helper}) => {182        const invulnerables = await helper.collatorSelection.getInvulnerables();183        const lastInvulnerable = invulnerables.pop();184185        await helper.getSudo().collatorSelection.removeInvulnerable(superuser, lastInvulnerable);186        const newInvulnerables = await helper.collatorSelection.getInvulnerables();187        // invulnerables had its last element removed, so they should be equal188        expect(newInvulnerables).to.have.all.members(invulnerables);189      });190    });191192    describe('Negative', () => {193      itSub('Does not duplicate an invulnerable', async ({helper}) => {194        const invulnerables = await helper.collatorSelection.getInvulnerables();195        // adding an already invulnerable should not fail, but should not duplicate it either196        await expect(helper.getSudo().collatorSelection.addInvulnerable(superuser, invulnerables[0]))197          .to.be.fulfilled;198        const newInvulnerables = await helper.collatorSelection.getInvulnerables();199        expect(newInvulnerables).to.have.all.members(invulnerables);200      });201202      itSub('Cannot allow invulnerables to be empty', async ({helper}) => {203        const invulnerables = await helper.collatorSelection.getInvulnerables();204        const lastInvulnerable = invulnerables.pop();205206        let nonce = await helper.chain.getNonce(superuser.address);207        await Promise.all(invulnerables.map((i: any) => 208          helper.getSudo().executeExtrinsic(superuser, 'api.tx.collatorSelection.removeInvulnerable', [i], true, {nonce: nonce++})));209210        await expect(helper.getSudo().collatorSelection.removeInvulnerable(superuser, lastInvulnerable))211          .to.be.rejected;//todo:collator With(/collatorSelection.TooFewInvulnerables/);212213        const newInvulnerables = await helper.collatorSelection.getInvulnerables();214        expect(newInvulnerables).to.be.deep.equal([lastInvulnerable]);215        216        // restore the invulnerables to the previous state217        nonce = await helper.chain.getNonce(superuser.address);218        await Promise.all(invulnerables.map((i: any) => 219          helper.getSudo().executeExtrinsic(superuser, 'api.tx.collatorSelection.addInvulnerable', [i], true, {nonce: nonce++})));220      });221222      itSub('Cannot have too many invulnerables', async ({helper}) => {223        const invulnerablesLength = (await helper.collatorSelection.getInvulnerables()).length;224        const invulnerablesUntilLimit = 30 - invulnerablesLength;225        const newInvulnerables = await helper.arrange.createAccounts(Array(invulnerablesUntilLimit).fill(10n), superuser);226        const [lastInvulnerable] = await helper.arrange.createAccounts([10n], superuser);227228        await Promise.all(newInvulnerables.map((i: IKeyringPair) => 229          helper.collatorSelection.setOwnKeys(i)));230        await helper.collatorSelection.setOwnKeys(lastInvulnerable);231232        let nonce = await helper.chain.getNonce(superuser.address);233        await Promise.all(newInvulnerables.map((i: IKeyringPair) => 234          helper.getSudo().executeExtrinsic(superuser, 'api.tx.collatorSelection.addInvulnerable', [i.address], true, {nonce: nonce++})));235236        await expect(helper.getSudo().collatorSelection.addInvulnerable(superuser, lastInvulnerable.address))237          .to.be.rejected; // todo:collator With(/collatorSelection.TooManyInvulnerables/);238        239        // restore the invulnerables to the previous state240        nonce = await helper.chain.getNonce(superuser.address);241        await Promise.all(newInvulnerables.map((i: IKeyringPair) => 242          helper.getSudo().executeExtrinsic(superuser, 'api.tx.collatorSelection.removeInvulnerable', [i.address], true, {nonce: nonce++})));243      });244245      itSub('Forbids a non-sudo to add an invulnerable', async ({helper}) => {246        const [account] = await helper.arrange.createAccounts([10n], superuser);247        const invulnerables = await helper.collatorSelection.getInvulnerables();248249        await helper.collatorSelection.setOwnKeys(account);250        await expect(helper.collatorSelection.addInvulnerable(superuser, account.address))251          .to.be.rejectedWith(/BadOrigin/);252253        const newInvulnerables = await helper.collatorSelection.getInvulnerables();254        expect(newInvulnerables).to.be.members(invulnerables);255      });256257      itSub('Forbids a non-sudo to remove an invulnerable', async ({helper}) => {258        const invulnerables = await helper.collatorSelection.getInvulnerables();259        await expect(helper.collatorSelection.removeInvulnerable(superuser, invulnerables[0]))260          .to.be.rejectedWith(/BadOrigin/);261        expect(await helper.collatorSelection.getInvulnerables()).to.have.all.members(invulnerables);262      });263    });264    265    // todo:collator after266  });267});
after · tests/src/collatorSelection.seqtest.ts
1// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.2// This file is part of Unique Network.34// Unique Network is free software: you can redistribute it and/or modify5// it under the terms of the GNU General Public License as published by6// the Free Software Foundation, either version 3 of the License, or7// (at your option) any later version.89// Unique Network is distributed in the hope that it will be useful,10// but WITHOUT ANY WARRANTY; without even the implied warranty of11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the12// GNU General Public License for more details.1314// You should have received a copy of the GNU General Public License15// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.1617import {IKeyringPair} from '@polkadot/types/types';18import {usingPlaygrounds, expect, itSub, Pallets, requirePalletsOrSkip} from './util';1920async function resetInvulnerables() {21  await usingPlaygrounds(async (helper, privateKey) => {22    const superuser = await privateKey('//Alice');23    const alice = await privateKey('//Alice');24    const bob = await privateKey('//Bob');25    const invulnerables = await helper.collatorSelection.getInvulnerables();26    if (!invulnerables.includes(alice.address) || !invulnerables.includes(bob.address) || invulnerables.length != 2) {27      console.warn('Alice and Bob are not the invulnerables! Reinstating them back. ' 28        + 'Current invulnerables\' size: ' + invulnerables.length);29      30      let nonce = await helper.chain.getNonce(alice.address);31      await Promise.all([32        helper.getSudo().executeExtrinsic(superuser, 'api.tx.collatorSelection.addInvulnerable', [alice.address], true, {nonce: nonce++}),33        helper.getSudo().executeExtrinsic(superuser, 'api.tx.collatorSelection.addInvulnerable', [bob.address], true, {nonce: nonce++}),34      ]);3536      nonce = await helper.chain.getNonce(alice.address);37      await Promise.all(invulnerables.map((invulnerable: any) => {38        if (invulnerable == alice.address || invulnerable == bob.address) return new Promise<void>(res => res());39        return helper.getSudo().executeExtrinsic(superuser, 'api.tx.collatorSelection.removeInvulnerable', [invulnerable], true, {nonce: nonce++});40      }));41    }42  });43}4445// todo:collator Most preferable to launch this test in parallel somehow -- or change the session period (1 hr).46// + 18 tests: 5 (1+4) on session change47describe('Integration Test: Collator Selection', () => {48  let superuser: IKeyringPair;4950  before(async function() {  51    await usingPlaygrounds(async (helper, privateKey) => {52      requirePalletsOrSkip(this, helper, [Pallets.CollatorSelection]);53      superuser = await privateKey('//Alice');54    });55  });5657  describe('Dynamic shuffling of collators', () => {58    // These two are the default invulnerables, and should return to be invulnerables after this suite.59    let alice: IKeyringPair;60    let bob: IKeyringPair;6162    let charlie: IKeyringPair;63    let dave: IKeyringPair;64    65    before(async function() {  66      await usingPlaygrounds(async (helper, privateKey) => {67        // todo:collator see again if blocks start to be finalized in dev mode 68        // Skip the collator block production in dev mode, since the blocks are sealed automatically.69        if (await helper.arrange.isDevNode()) this.skip();7071        alice = await privateKey('//Alice');72        bob = await privateKey('//Bob');73        charlie = await privateKey('//Charlie');74        dave = await privateKey('//Dave');7576        expect((await helper.collatorSelection.setOwnKeys(charlie))77          .status.toLowerCase()).to.be.equal('success');78        expect((await helper.collatorSelection.setOwnKeys(dave))79          .status.toLowerCase()).to.be.equal('success');80  81        // todo:collator check necessity + add RPC for invulnerables / just improve in general82        // validators = await helper.callRpc('api.query.session.validators');83        const invulnerables = await helper.collatorSelection.getInvulnerables();84        if (!invulnerables.includes(alice.address) || !invulnerables.includes(bob.address) || invulnerables.length != 2) {85          console.warn('Alice and Bob are not the invulnerables! Reinstating them back. ' 86            + 'Current invulnerables\' size: ' + invulnerables.length);87          88          let nonce = await helper.chain.getNonce(superuser.address);89          await Promise.all([90            helper.getSudo().executeExtrinsic(superuser, 'api.tx.collatorSelection.addInvulnerable', [alice.address], true, {nonce: nonce++}),91            helper.getSudo().executeExtrinsic(superuser, 'api.tx.collatorSelection.addInvulnerable', [bob.address], true, {nonce: nonce++}),92          ]);93  94          nonce = await helper.chain.getNonce(superuser.address);95          await Promise.all(invulnerables.map((invulnerable: any) => {96            if (invulnerable == alice.address || invulnerable == bob.address) return new Promise((res) => res);97            return helper.getSudo().executeExtrinsic(superuser, 'api.tx.collatorSelection.removeInvulnerable', [invulnerable], true, {nonce: nonce++});98          }));99        }100      });101    });102  103    itSub('Change invulnerables and make sure they start producing blocks', async ({helper}) => {104      let nonce = await helper.chain.getNonce(superuser.address);105      await expect(Promise.all([106        helper.getSudo().executeExtrinsic(superuser, 'api.tx.collatorSelection.addInvulnerable', [charlie.address], true, {nonce: nonce++}),107        helper.getSudo().executeExtrinsic(superuser, 'api.tx.collatorSelection.addInvulnerable', [dave.address], true, {nonce: nonce++}),108      ])).to.be.fulfilled;109  110      nonce = await helper.chain.getNonce(superuser.address);111      await expect(Promise.all([112        helper.getSudo().executeExtrinsic(superuser, 'api.tx.collatorSelection.removeInvulnerable', [alice.address], true, {nonce: nonce++}),113        helper.getSudo().executeExtrinsic(superuser, 'api.tx.collatorSelection.removeInvulnerable', [bob.address], true, {nonce: nonce++}),114      ])).to.be.fulfilled;115  116      const newInvulnerables = await helper.collatorSelection.getInvulnerables();117      expect(newInvulnerables).to.contain(charlie.address).and.contain(dave.address).and.be.length(2);118  119      const expectedSessionIndex = (await helper.callRpc('api.query.session.currentIndex')).toNumber() + 2;120      let currentSessionIndex = -1;121      console.log('Waiting for the session after the next.' 122        + ' This might take a while -- check SessionPeriod in pallet_session::Config for session time.');123  124      while (currentSessionIndex < expectedSessionIndex) {125        // eslint-disable-next-line no-async-promise-executor126        currentSessionIndex = await expect(helper.wait.withTimeout(new Promise(async (resolve) => {127          await helper.wait.newBlocks(1);128          const res = (await helper.callRpc('api.query.session.currentIndex')).toNumber();129          resolve(res);130        }), 24000, 'The chain has stopped producing blocks!')).to.be.fulfilled;131      }132  133      const newValidators = await helper.callRpc('api.query.session.validators');134      expect(newValidators).to.contain(charlie.address).and.contain(dave.address).and.be.length(2);135  136      const lastBlockNumber = await helper.chain.getLatestBlockNumber();137      await helper.wait.newBlocks(1);138      const lastCharlieBlock = (await helper.callRpc('api.query.collatorSelection.lastAuthoredBlock', [charlie.address])).toNumber();139      const lastDaveBlock = (await helper.callRpc('api.query.collatorSelection.lastAuthoredBlock', [dave.address])).toNumber();140      expect(lastCharlieBlock >= lastBlockNumber || lastDaveBlock >= lastBlockNumber).to.be.true;141    });142  143    // todo:collator keyless invulnerables? will hang, so, a breaking test, eh144    // register candidate without sudos and the like145  146    after(async () => {147      await usingPlaygrounds(async (helper) => {148        if (await helper.arrange.isDevNode()) return;149150        let nonce = await helper.chain.getNonce(superuser.address);151        await Promise.all([152          helper.getSudo().executeExtrinsic(superuser, 'api.tx.collatorSelection.addInvulnerable', [alice.address], true, {nonce: nonce++}),153          helper.getSudo().executeExtrinsic(superuser, 'api.tx.collatorSelection.addInvulnerable', [bob.address], true, {nonce: nonce++}),154        ]);155  156        nonce = await helper.chain.getNonce(superuser.address);157        await Promise.all([158          await helper.getSudo().executeExtrinsic(superuser, 'api.tx.collatorSelection.removeInvulnerable', [charlie.address], true, {nonce: nonce++}),159          await helper.getSudo().executeExtrinsic(superuser, 'api.tx.collatorSelection.removeInvulnerable', [dave.address], true, {nonce: nonce++}),160        ]);161      });162    });163  });164165  // todo:collator make sure that there is enough session time for a set of tests166  // 28 non-functioning collators, teehee.167168  describe('Addition and removal of invulnerables', () => {169    before(async function() {170      await resetInvulnerables();171    });172173    describe('Positive', () => {174      itSub('Adds an invulnerable', async ({helper}) => {175        const [account] = await helper.arrange.createAccounts([10n], superuser);176        const invulnerables = await helper.collatorSelection.getInvulnerables();177178        await helper.collatorSelection.setOwnKeys(account);179        await helper.getSudo().collatorSelection.addInvulnerable(superuser, account.address);180        181        const newInvulnerables = await helper.collatorSelection.getInvulnerables();182        expect(invulnerables.concat(account.address)).to.have.all.members(newInvulnerables);183      });184185      itSub('Removes an invulnerable', async ({helper}) => {186        const invulnerables = await helper.collatorSelection.getInvulnerables();187        const lastInvulnerable = invulnerables.pop();188189        await helper.getSudo().collatorSelection.removeInvulnerable(superuser, lastInvulnerable);190        const newInvulnerables = await helper.collatorSelection.getInvulnerables();191        // invulnerables had its last element removed, so they should be equal192        expect(newInvulnerables).to.have.all.members(invulnerables);193      });194    });195196    describe('Negative', () => {197      itSub('Does not duplicate an invulnerable', async ({helper}) => {198        const invulnerables = await helper.collatorSelection.getInvulnerables();199        // adding an already invulnerable should not fail, but should not duplicate it either200        await expect(helper.getSudo().collatorSelection.addInvulnerable(superuser, invulnerables[0]))201          .to.be.fulfilled;202        const newInvulnerables = await helper.collatorSelection.getInvulnerables();203        expect(newInvulnerables).to.have.all.members(invulnerables);204      });205206      itSub('Cannot allow invulnerables to be empty', async ({helper}) => {207        const invulnerables = await helper.collatorSelection.getInvulnerables();208        const lastInvulnerable = invulnerables.pop();209210        let nonce = await helper.chain.getNonce(superuser.address);211        await Promise.all(invulnerables.map((i: any) => 212          helper.getSudo().executeExtrinsic(superuser, 'api.tx.collatorSelection.removeInvulnerable', [i], true, {nonce: nonce++})));213214        await expect(helper.getSudo().collatorSelection.removeInvulnerable(superuser, lastInvulnerable))215          .to.be.rejected;//todo:collator With(/collatorSelection.TooFewInvulnerables/);216217        const newInvulnerables = await helper.collatorSelection.getInvulnerables();218        expect(newInvulnerables).to.be.deep.equal([lastInvulnerable]);219        220        // restore the invulnerables to the previous state221        nonce = await helper.chain.getNonce(superuser.address);222        await Promise.all(invulnerables.map((i: any) => 223          helper.getSudo().executeExtrinsic(superuser, 'api.tx.collatorSelection.addInvulnerable', [i], true, {nonce: nonce++})));224      });225226      itSub('Cannot have too many invulnerables', async ({helper}) => {227        const invulnerablesLength = (await helper.collatorSelection.getInvulnerables()).length;228        const invulnerablesUntilLimit = 30 - invulnerablesLength;229        const newInvulnerables = await helper.arrange.createAccounts(Array(invulnerablesUntilLimit).fill(10n), superuser);230        const [lastInvulnerable] = await helper.arrange.createAccounts([10n], superuser);231232        await Promise.all(newInvulnerables.map((i: IKeyringPair) => 233          helper.collatorSelection.setOwnKeys(i)));234        await helper.collatorSelection.setOwnKeys(lastInvulnerable);235236        let nonce = await helper.chain.getNonce(superuser.address);237        await Promise.all(newInvulnerables.map((i: IKeyringPair) => 238          helper.getSudo().executeExtrinsic(superuser, 'api.tx.collatorSelection.addInvulnerable', [i.address], true, {nonce: nonce++})));239240        await expect(helper.getSudo().collatorSelection.addInvulnerable(superuser, lastInvulnerable.address))241          .to.be.rejected; // todo:collator With(/collatorSelection.TooManyInvulnerables/);242        243        // restore the invulnerables to the previous state244        nonce = await helper.chain.getNonce(superuser.address);245        await Promise.all(newInvulnerables.map((i: IKeyringPair) => 246          helper.getSudo().executeExtrinsic(superuser, 'api.tx.collatorSelection.removeInvulnerable', [i.address], true, {nonce: nonce++})));247      });248249      itSub('Forbids a non-sudo to add an invulnerable', async ({helper}) => {250        const [account] = await helper.arrange.createAccounts([10n], superuser);251        const invulnerables = await helper.collatorSelection.getInvulnerables();252253        await helper.collatorSelection.setOwnKeys(account);254        await expect(helper.collatorSelection.addInvulnerable(superuser, account.address))255          .to.be.rejectedWith(/BadOrigin/);256257        const newInvulnerables = await helper.collatorSelection.getInvulnerables();258        expect(newInvulnerables).to.be.members(invulnerables);259      });260261      itSub('Forbids a non-sudo to remove an invulnerable', async ({helper}) => {262        const invulnerables = await helper.collatorSelection.getInvulnerables();263        await expect(helper.collatorSelection.removeInvulnerable(superuser, invulnerables[0]))264          .to.be.rejectedWith(/BadOrigin/);265        expect(await helper.collatorSelection.getInvulnerables()).to.have.all.members(invulnerables);266      });267    });268  269    after(async () => {270      // eslint-disable-next-line require-await271      await usingPlaygrounds(async (helper) => {272        if (helper.fetchMissingPalletNames([Pallets.CollatorSelection]).length != 0) return;273    274        // todo:collator after275      });276    });277  });278});