difftreelog
Merge pull request #988 from UniqueNetwork/fix/collector-selection-tests-release-60
in: master
9 files changed
.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']
},
};
.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
.github/workflows/collator-selection.ymldiffbeforeafterboth1name: 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 -fnode/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']
runtime/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;
runtime/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
runtime/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
runtime/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
tests/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();
+ });
});
});