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

difftreelog

Tests/fin council (#1070)

Max Andreev2024-06-05parent: #c9b1f9a.patch.diff
in: master

7 files changed

modifiedjs-packages/.vscode/settings.jsondiffbeforeafterboth
--- a/js-packages/.vscode/settings.json
+++ b/js-packages/.vscode/settings.json
@@ -5,6 +5,8 @@
 	},
 	"mochaExplorer.files": "tests/**/*.test.ts",
 	"mochaExplorer.require": "ts-node/register",
+  "mochaExplorer.esmLoader": true,
+  "mochaExplorer.nodeArgv": ["--loader", "ts-node/esm"],
 	"eslint.format.enable": true,
 	"[javascript]": {
 		"editor.defaultFormatter": "dbaeumer.vscode-eslint"
modifiedjs-packages/test-utils/index.tsdiffbeforeafterboth
--- a/js-packages/test-utils/index.ts
+++ b/js-packages/test-utils/index.ts
@@ -214,6 +214,23 @@
     }));
   };
 
+  static FinCouncil = class extends EventSection('financialCouncil') {
+    static Proposed = this.Method('Proposed', data => ({
+      account: eventHumanData(data, 0),
+      proposalIndex: eventJsonData<number>(data, 1),
+      proposalHash: eventHumanData(data, 2),
+      threshold: eventJsonData<number>(data, 3),
+    }));
+    static Closed = this.Method('Closed', data => ({
+      proposalHash: eventHumanData(data, 0),
+      yes: eventJsonData<number>(data, 1),
+      no: eventJsonData<number>(data, 2),
+    }));
+    static Executed = this.Method('Executed', data => ({
+      proposalHash: eventHumanData(data, 0),
+    }));
+  };
+
   static TechnicalCommittee = class extends EventSection('technicalCommittee') {
     static Proposed = this.Method('Proposed', data => ({
       account: eventHumanData(data, 0),
@@ -475,6 +492,7 @@
   scheduler: SchedulerGroup;
   collatorSelection: CollatorSelectionGroup;
   council: ICollectiveGroup;
+  finCouncil: ICollectiveGroup;
   technicalCommittee: ICollectiveGroup;
   fellowship: IFellowshipGroup;
   democracy: DemocracyGroup;
@@ -498,6 +516,10 @@
       collective: new CollectiveGroup(this, 'council'),
       membership: new CollectiveMembershipGroup(this, 'councilMembership'),
     };
+    this.finCouncil = {
+      collective: new CollectiveGroup(this, 'financialCouncil'),
+      membership: new CollectiveMembershipGroup(this, 'financialCouncilMembership'),
+    };
     this.technicalCommittee = {
       collective: new CollectiveGroup(this, 'technicalCommittee'),
       membership: new CollectiveMembershipGroup(this, 'technicalCommitteeMembership'),
modifiedjs-packages/test-utils/util.tsdiffbeforeafterboth
--- a/js-packages/test-utils/util.ts
+++ b/js-packages/test-utils/util.ts
@@ -117,6 +117,7 @@
   Identity = 'identity',
   Democracy = 'democracy',
   Council = 'council',
+  FinancialCouncil = 'financialcouncil',
   //CouncilMembership = 'councilmembership',
   TechnicalCommittee = 'technicalcommittee',
   Fellowship = 'fellowshipcollective',
modifiedjs-packages/tests/sub/governance/council.test.tsdiffbeforeafterboth
--- a/js-packages/tests/sub/governance/council.test.ts
+++ b/js-packages/tests/sub/governance/council.test.ts
@@ -2,7 +2,7 @@
 import type {IKeyringPair} from '@polkadot/types/types';
 import {usingPlaygrounds, itSub, expect, Pallets, requirePalletsOrSkip, describeGov} from '@unique/test-utils/util.js';
 import {Event} from '@unique/test-utils';
-import {initCouncil, democracyLaunchPeriod, democracyVotingPeriod, democracyEnactmentPeriod, councilMotionDuration, democracyFastTrackVotingPeriod, fellowshipRankLimit, clearCouncil, clearTechComm, initTechComm, clearFellowship, dummyProposal, dummyProposalCall, initFellowship, defaultEnactmentMoment, fellowshipPropositionOrigin} from './util.js';
+import {initCouncil, democracyLaunchPeriod, democracyVotingPeriod, democracyEnactmentPeriod, councilMotionDuration, democracyFastTrackVotingPeriod, fellowshipRankLimit, clearCouncil, clearTechComm, initTechComm, clearFellowship, dummyProposal, dummyProposalCall, initFellowship, defaultEnactmentMoment, fellowshipPropositionOrigin, initFinCouncil} from './util.js';
 import type {ICounselors} from './util.js';
 
 describeGov('Governance: Council tests', () => {
@@ -192,6 +192,25 @@
     expect(techCommMembers).to.not.contains(techComm.andy.address);
   });
 
+  itSub('Council can remove FinCouncil member', async ({helper}) => {
+    const finCouncil = await initFinCouncil(donor, sudoer);
+    const removeMemberPrpoposal = helper.finCouncil.membership.removeMemberCall(finCouncil.andy.address);
+    await proposalFromMoreThanHalfCouncil(removeMemberPrpoposal);
+
+    const finCouncilMembers = await helper.finCouncil.membership.getMembers();
+    expect(finCouncilMembers).to.not.contains(finCouncil.andy.address);
+  });
+
+  itSub('Council can add FinCouncil member', async ({helper}) => {
+    await initFinCouncil(donor, sudoer);
+    const newFinCouncilMember = helper.arrange.createEmptyAccount();
+    const addMemberPrpoposal = helper.finCouncil.membership.addMemberCall(newFinCouncilMember.address);
+    await proposalFromMoreThanHalfCouncil(addMemberPrpoposal);
+
+    const finCouncilMembers = await helper.finCouncil.membership.getMembers();
+    expect(finCouncilMembers).to.contains(newFinCouncilMember.address);
+  });
+
   itSub.skip('Council member can add Fellowship member', async ({helper}) => {
     const newFellowshipMember = helper.arrange.createEmptyAccount();
     await expect(helper.council.collective.execute(
@@ -328,6 +347,22 @@
     )).to.be.rejectedWith('BadOrigin');
   });
 
+  itSub('[Negative] Council member can\'t add FinCouncil member', async ({helper}) => {
+    const newFinCouncilMember = helper.arrange.createEmptyAccount();
+    await expect(helper.council.collective.execute(
+      counselors.alex,
+      helper.finCouncil.membership.addMemberCall(newFinCouncilMember.address),
+    )).rejectedWith('BadOrigin');
+  });
+
+  itSub('[Negative] Council member can\'t remove FinCouncil member', async ({helper}) => {
+    const finCouncil = await initFinCouncil(donor, sudoer);
+    await expect(helper.council.collective.execute(
+      counselors.alex,
+      helper.finCouncil.membership.removeMemberCall(finCouncil.ildar.address),
+    )).rejectedWith('BadOrigin');
+  });
+
   itSub('[Negative] Council member cannot promote/demote a Fellowship member', async ({helper}) => {
     const fellowship = await initFellowship(donor, sudoer);
     const memberWithRankOne = fellowship[1][0];
addedjs-packages/tests/sub/governance/financialCouncil.test.tsdiffbeforeafterboth
after · js-packages/tests/sub/governance/financialCouncil.test.ts
1import type {IKeyringPair} from '@polkadot/types/types';2import {usingPlaygrounds, itSub, expect, Pallets, requirePalletsOrSkip, describeGov} from '@unique/test-utils/util.js';3import {Event} from '@unique/test-utils';4import {democracyFastTrackVotingPeriod, IFinCounselors, clearTechComm, dummyProposalCall, initFinCouncil, clearFinCouncil, democracyLaunchPeriod, initFellowship, dummyProposal, fellowshipPropositionOrigin, defaultEnactmentMoment, initCouncil, clearCouncil, clearFellowship} from './util.js';567describeGov('Governance: Financial Council tests', () => {8  let donor: IKeyringPair;9  let finCounselors: IFinCounselors;10  let sudoer: IKeyringPair;1112  const moreThanHalfCouncilThreshold = 2;1314  before(async function() {15    await usingPlaygrounds(async (helper, privateKey) => {16      requirePalletsOrSkip(this, helper, [Pallets.FinancialCouncil]);17      sudoer = await privateKey('//Alice');18      donor = await privateKey({url: import.meta.url});19    });20  });2122  beforeEach(async () => {23    finCounselors = await initFinCouncil(donor, sudoer);24  });2526  afterEach(async () => {27    await clearFinCouncil(sudoer);28    await clearTechComm(sudoer);29  });3031  async function proposalFromMoreThanHalfCouncil(proposal: any) {32    return await usingPlaygrounds(async (helper) => {33      expect((await helper.finCouncil.membership.getMembers()).length).to.be.equal(3);34      const proposeResult = await helper.finCouncil.collective.propose(35        finCounselors.ildar,36        proposal,37        moreThanHalfCouncilThreshold,38      );3940      const councilProposedEvent = Event.FinCouncil.Proposed.expect(proposeResult);41      const proposalIndex = councilProposedEvent.proposalIndex;42      const proposalHash = councilProposedEvent.proposalHash;4344      await helper.finCouncil.collective.vote(finCounselors.greg, proposalHash, proposalIndex, true);45      await helper.finCouncil.collective.vote(finCounselors.ildar, proposalHash, proposalIndex, true);4647      return await helper.finCouncil.collective.close(finCounselors.ildar, proposalHash, proposalIndex);48    });49  }5051  async function proposalFromAllCouncil(proposal: any) {52    return await usingPlaygrounds(async (helper) => {53      expect((await helper.finCouncil.membership.getMembers()).length).to.be.equal(3);54      const proposeResult = await helper.finCouncil.collective.propose(55        finCounselors.ildar,56        proposal,57        moreThanHalfCouncilThreshold,58      );5960      const councilProposedEvent = Event.FinCouncil.Proposed.expect(proposeResult);61      const proposalIndex = councilProposedEvent.proposalIndex;62      const proposalHash = councilProposedEvent.proposalHash;6364      await helper.finCouncil.collective.vote(finCounselors.greg, proposalHash, proposalIndex, true);65      await helper.finCouncil.collective.vote(finCounselors.ildar, proposalHash, proposalIndex, true);66      await helper.finCouncil.collective.vote(finCounselors.andy, proposalHash, proposalIndex, true);6768      return await helper.finCouncil.collective.close(finCounselors.andy, proposalHash, proposalIndex);69    });70  }7172  itSub('FinCouncil member can register foreign asset', async ({helper}) => {73    const location = {74      parents: 1,75      interior: {X3: [76        {77          Parachain: 1000,78        },79        {80          PalletInstance: 50,81        },82        {83          GeneralIndex: 1984,84        },85      ]},86    };87    const assetId = {Concrete: location};8889    const registerForeignAssetCall = helper.constructApiCall(90      'api.tx.foreignAssets.forceRegisterForeignAsset',91      [{V3: assetId}, helper.util.str2vec('New Asset'), 'NEW', {Fungible: 10}],92    );9394    await helper.finCouncil.collective.execute(finCounselors.andy, registerForeignAssetCall);9596    const asset = await helper.foreignAssets.foreignCollectionId(location);97    expect(asset).not.null;98  });99100  itSub('[Negative] FinCouncil can\'t fast-track Democracy proposals', async ({helper}) => {101    const preimageHash = await helper.preimage.notePreimageFromCall(sudoer, dummyProposalCall(helper), true);102    await helper.wait.parachainBlockMultiplesOf(35n);103104    await helper.getSudo().democracy.externalProposeDefaultWithPreimage(sudoer, preimageHash);105106    await expect(proposalFromAllCouncil(helper.democracy.fastTrackCall(preimageHash, democracyFastTrackVotingPeriod, 0)))107      .rejectedWith('BadOrigin');108  });109110  itSub('[Negative] FinCouncil member cannot fast-track Democracy proposals', async ({helper}) => {111    const preimageHash = await helper.preimage.notePreimageFromCall(sudoer, dummyProposalCall(helper), true);112    await helper.getSudo().democracy.externalProposeDefaultWithPreimage(sudoer, preimageHash);113114    await expect(helper.finCouncil.collective.execute(115      finCounselors.andy,116      helper.democracy.fastTrackCall(preimageHash, democracyFastTrackVotingPeriod, 0),117    )).to.be.rejectedWith('BadOrigin');118  });119120  itSub('[Negative] FinCouncil can\'t cancel Democracy proposals', async ({helper}) => {121    const proposeResult = await helper.getSudo().democracy.propose(sudoer, dummyProposalCall(helper), 0n);122    const proposalIndex = Event.Democracy.Proposed.expect(proposeResult).proposalIndex;123124    await expect(proposalFromAllCouncil(helper.democracy.cancelProposalCall(proposalIndex)))125      .rejectedWith('BadOrigin');126  });127128  itSub('[Negative] FinCouncil member cannot cancel Democracy proposals', async ({helper}) => {129    const proposeResult = await helper.getSudo().democracy.propose(sudoer, dummyProposalCall(helper), 0n);130    const proposalIndex = Event.Democracy.Proposed.expect(proposeResult).proposalIndex;131132    await expect(helper.finCouncil.collective.execute(133      finCounselors.andy,134      helper.democracy.cancelProposalCall(proposalIndex),135    ))136      .to.be.rejectedWith('BadOrigin');137  });138139  itSub('[Negative] FinCouncil can\'t cancel ongoing Democracy referendums', async ({helper}) => {140    await helper.getSudo().democracy.externalProposeDefault(sudoer, dummyProposalCall(helper));141    const startedEvent = await helper.wait.expectEvent(democracyLaunchPeriod, Event.Democracy.Started);142    const referendumIndex = startedEvent.referendumIndex;143144    await expect(proposalFromAllCouncil(helper.democracy.emergencyCancelCall(referendumIndex)))145      .rejectedWith('BadOrigin');146  });147148  itSub('[Negative] FinCouncil member cannot cancel ongoing Democracy referendums', async ({helper}) => {149    await helper.getSudo().democracy.externalProposeDefault(sudoer, dummyProposalCall(helper));150    const startedEvent = await helper.wait.expectEvent(democracyLaunchPeriod, Event.Democracy.Started);151    const referendumIndex = startedEvent.referendumIndex;152153    await expect(helper.finCouncil.collective.execute(154      finCounselors.andy,155      helper.democracy.emergencyCancelCall(referendumIndex),156    )).to.be.rejectedWith('BadOrigin');157  });158159  itSub('[Negative] FinCouncil member can\'t veto Democracy proposals', async ({helper}) => {160    const preimageHash = await helper.preimage.notePreimageFromCall(sudoer, dummyProposalCall(helper), true);161    await helper.getSudo().democracy.externalProposeDefaultWithPreimage(sudoer, preimageHash);162163    await expect(helper.finCouncil.collective.execute(164      finCounselors.andy,165      helper.democracy.vetoExternalCall(preimageHash),166    )).rejectedWith('BadOrigin');167  });168169  itSub('[Negative] FinCouncil cannot blacklist Democracy proposals', async ({helper}) => {170    const preimageHash = await helper.preimage.notePreimageFromCall(sudoer, dummyProposalCall(helper), true);171    await helper.getSudo().democracy.externalProposeDefaultWithPreimage(sudoer, preimageHash);172173    await expect(proposalFromAllCouncil(helper.democracy.blacklistCall(preimageHash))).to.be.rejectedWith('BadOrigin');174  });175176  itSub('[Negative] FinCouncil member cannot blacklist Democracy proposals', async ({helper}) => {177    const preimageHash = await helper.preimage.notePreimageFromCall(sudoer, dummyProposalCall(helper), true);178    await helper.getSudo().democracy.externalProposeDefaultWithPreimage(sudoer, preimageHash);179180    await expect(helper.finCouncil.collective.execute(181      finCounselors.andy,182      helper.democracy.blacklistCall(preimageHash),183    )).to.be.rejectedWith('BadOrigin');184  });185186  itSub('[Negative] FinCouncil can\' cancel Fellowship referendums', async ({helper}) => {187    const fellowship = await initFellowship(donor, sudoer);188    const fellowshipProposer = fellowship[5][0];189    const proposal = dummyProposal(helper);190191    const submitResult = await helper.fellowship.referenda.submit(192      fellowshipProposer,193      fellowshipPropositionOrigin,194      proposal,195      defaultEnactmentMoment,196    );197    const referendumIndex = Event.FellowshipReferenda.Submitted.expect(submitResult).referendumIndex;198    await expect(proposalFromAllCouncil(helper.fellowship.referenda.cancelCall(referendumIndex)))199      .rejectedWith('BadOrigin');200  });201202  itSub('[Negative] TechComm member cannot cancel Fellowship referendums', async ({helper}) => {203    const fellowship = await initFellowship(donor, sudoer);204    const fellowshipProposer = fellowship[5][0];205    const proposal = dummyProposal(helper);206207    const submitResult = await helper.fellowship.referenda.submit(208      fellowshipProposer,209      fellowshipPropositionOrigin,210      proposal,211      defaultEnactmentMoment,212    );213214    const referendumIndex = Event.FellowshipReferenda.Submitted.expect(submitResult).referendumIndex;215216    await expect(helper.finCouncil.collective.execute(217      finCounselors.andy,218      helper.fellowship.referenda.cancelCall(referendumIndex),219    )).to.be.rejectedWith('BadOrigin');220  });221222  itSub('[Negative] FinCouncil member can\' add a Fellowship member', async ({helper}) => {223    const newFellowshipMember = helper.arrange.createEmptyAccount();224    await expect(helper.finCouncil.collective.execute(225      finCounselors.andy,226      helper.fellowship.collective.addMemberCall(newFellowshipMember.address),227    )).to.be.rejectedWith('BadOrigin');228  });229230  itSub('[Negative] FinCouncil cannot submit regular democracy proposal', async ({helper}) => {231    const councilProposal = await helper.democracy.proposeCall(dummyProposalCall(helper), 0n);232233    await expect(proposalFromAllCouncil(councilProposal)).to.be.rejectedWith('BadOrigin');234  });235236  itSub('[Negative] FinCouncil cannot externally propose SuperMajorityAgainst', async ({helper}) => {237    const commiteeProposal = await helper.democracy.externalProposeDefaultCall(dummyProposalCall(helper));238239    await expect(proposalFromAllCouncil(commiteeProposal)).to.be.rejectedWith('BadOrigin');240  });241242  itSub('[Negative] FinCouncil member cannot submit regular democracy proposal', async ({helper}) => {243    const memberProposal = await helper.democracy.proposeCall(dummyProposalCall(helper), 0n);244245    await expect(helper.finCouncil.collective.execute(246      finCounselors.andy,247      memberProposal,248    )).to.be.rejectedWith('BadOrigin');249  });250251  itSub('[Negative] FinCouncil cannot externally propose SimpleMajority', async ({helper}) => {252    const commiteeProposal = await helper.democracy.externalProposeMajorityCall(dummyProposalCall(helper));253254    await expect(proposalFromAllCouncil(commiteeProposal)).to.be.rejectedWith('BadOrigin');255  });256257  itSub('[Negative] FinCouncil cannot externally propose SuperMajorityApprove', async ({helper}) => {258    const commiteeProposal = await helper.democracy.externalProposeCall(dummyProposalCall(helper));259260    await expect(proposalFromAllCouncil(commiteeProposal)).to.be.rejectedWith('BadOrigin');261  });262263  itSub('[Negative] FinCouncil member cannot externally propose SuperMajorityAgainst', async ({helper}) => {264    const memberProposal = await helper.democracy.externalProposeDefaultCall(dummyProposalCall(helper));265266    await expect(helper.finCouncil.collective.execute(267      finCounselors.andy,268      memberProposal,269    )).to.be.rejectedWith('BadOrigin');270  });271272  itSub('[Negative] FinCouncil member cannot externally propose SimpleMajority', async ({helper}) => {273    const memberProposal = await helper.democracy.externalProposeMajorityCall(dummyProposalCall(helper));274275    await expect(helper.finCouncil.collective.execute(276      finCounselors.andy,277      memberProposal,278    )).to.be.rejectedWith('BadOrigin');279  });280281  itSub('[Negative] FinCouncil member cannot externally propose SuperMajorityApprove', async ({helper}) => {282    const memberProposal = await helper.democracy.externalProposeCall(dummyProposalCall(helper));283284    await expect(helper.finCouncil.collective.execute(285      finCounselors.andy,286      memberProposal,287    )).to.be.rejectedWith('BadOrigin');288  });289290  itSub('[Negative] FinCouncil cannot add/remove a Council member', async ({helper}) => {291    const newCouncilMember = helper.arrange.createEmptyAccount();292    const addMemberProposal = helper.council.membership.addMemberCall(newCouncilMember.address);293    const removeMemberProposal = helper.council.membership.removeMemberCall(newCouncilMember.address);294295    await expect(proposalFromAllCouncil(addMemberProposal)).to.be.rejectedWith('BadOrigin');296    await expect(proposalFromAllCouncil(removeMemberProposal)).to.be.rejectedWith('BadOrigin');297  });298299  itSub('[Negative] FinCouncil member cannot add/remove a Council member', async ({helper}) => {300    const newCouncilMember = helper.arrange.createEmptyAccount();301    const addMemberProposal = helper.council.membership.addMemberCall(newCouncilMember.address);302    const removeMemberProposal = helper.council.membership.removeMemberCall(newCouncilMember.address);303304    await expect(helper.finCouncil.collective.execute(305      finCounselors.andy,306      addMemberProposal,307    )).to.be.rejectedWith('BadOrigin');308    await expect(helper.finCouncil.collective.execute(309      finCounselors.andy,310      removeMemberProposal,311    )).to.be.rejectedWith('BadOrigin');312  });313314  itSub('[Negative] FinCouncil cannot add/remove a FinCouncil member', async ({helper}) => {315    const newCouncilMember = helper.arrange.createEmptyAccount();316    const addMemberProposal = helper.finCouncil.membership.addMemberCall(newCouncilMember.address);317    const removeMemberProposal = helper.finCouncil.membership.removeMemberCall(finCounselors.ildar.address);318319    await expect(proposalFromAllCouncil(addMemberProposal)).to.be.rejectedWith('BadOrigin');320    await expect(proposalFromAllCouncil(removeMemberProposal)).to.be.rejectedWith('BadOrigin');321  });322323  itSub('[Negative] FinCouncil member cannot add/remove a FinCouncil member', async ({helper}) => {324    const newCouncilMember = helper.arrange.createEmptyAccount();325    const addMemberProposal = helper.finCouncil.membership.addMemberCall(newCouncilMember.address);326    const removeMemberProposal = helper.finCouncil.membership.removeMemberCall(finCounselors.ildar.address);327328    await expect(helper.finCouncil.collective.execute(329      finCounselors.andy,330      addMemberProposal,331    )).to.be.rejectedWith('BadOrigin');332    await expect(helper.finCouncil.collective.execute(333      finCounselors.andy,334      removeMemberProposal,335    )).to.be.rejectedWith('BadOrigin');336  });337338  itSub('[Negative] FinCouncil cannot set/clear Council prime member', async ({helper}) => {339    const counselors = await initCouncil(donor, sudoer);340    const proposalForSet = await helper.council.membership.setPrimeCall(counselors.charu.address);341    const proposalForClear = await helper.council.membership.clearPrimeCall();342343    await expect(proposalFromAllCouncil(proposalForSet)).to.be.rejectedWith('BadOrigin');344    await expect(proposalFromAllCouncil(proposalForClear)).to.be.rejectedWith('BadOrigin');345    await clearCouncil(sudoer);346  });347348  itSub('[Negative] FinCouncil member cannot set/clear Council prime member', async ({helper}) => {349    const counselors = await initCouncil(donor, sudoer);350    const proposalForSet = await helper.council.membership.setPrimeCall(counselors.charu.address);351    const proposalForClear = await helper.council.membership.clearPrimeCall();352353    await expect(helper.finCouncil.collective.execute(354      finCounselors.andy,355      proposalForSet,356    )).to.be.rejectedWith('BadOrigin');357    await expect(helper.finCouncil.collective.execute(358      finCounselors.andy,359      proposalForClear,360    )).to.be.rejectedWith('BadOrigin');361    await clearCouncil(sudoer);362  });363364  itSub('[Negative] FinCouncil cannot add/remove a TechComm member', async ({helper}) => {365    const newCommMember = helper.arrange.createEmptyAccount();366    const addMemberProposal = helper.council.membership.addMemberCall(newCommMember.address);367    const removeMemberProposal = helper.council.membership.removeMemberCall(newCommMember.address);368369    await expect(proposalFromAllCouncil(addMemberProposal)).to.be.rejectedWith('BadOrigin');370    await expect(proposalFromAllCouncil(removeMemberProposal)).to.be.rejectedWith('BadOrigin');371  });372373  itSub('[Negative] FinCouncil member cannot add/remove a TechComm member', async ({helper}) => {374    const newCommMember = helper.arrange.createEmptyAccount();375    const addMemberProposal = helper.council.membership.addMemberCall(newCommMember.address);376    const removeMemberProposal = helper.council.membership.removeMemberCall(newCommMember.address);377378    await expect(helper.finCouncil.collective.execute(379      finCounselors.andy,380      addMemberProposal,381    )).to.be.rejectedWith('BadOrigin');382    await expect(helper.finCouncil.collective.execute(383      finCounselors.andy,384      removeMemberProposal,385    )).to.be.rejectedWith('BadOrigin');386  });387388  itSub('[Negative] FinCouncil cannot remove a Fellowship member', async ({helper}) => {389    const fellowship = await initFellowship(donor, sudoer);390391    await expect(proposalFromAllCouncil(helper.fellowship.collective.removeMemberCall(fellowship[5][0].address, 5))).to.be.rejectedWith('BadOrigin');392    await clearFellowship(sudoer);393  });394395  itSub('[Negative] FinCouncil member cannot remove a Fellowship member', async ({helper}) => {396    const fellowship = await initFellowship(donor, sudoer);397398    await expect(helper.finCouncil.collective.execute(399      finCounselors.andy,400      helper.fellowship.collective.removeMemberCall(fellowship[5][0].address, 5),401    )).to.be.rejectedWith('BadOrigin');402    await clearFellowship(sudoer);403  });404405406});
modifiedjs-packages/tests/sub/governance/technicalCommittee.test.tsdiffbeforeafterboth
--- a/js-packages/tests/sub/governance/technicalCommittee.test.ts
+++ b/js-packages/tests/sub/governance/technicalCommittee.test.ts
@@ -130,6 +130,50 @@
     await clearFellowship(sudoer);
   });
 
+  itSub('[Negative] TechComm can\'t add FinCouncil member', async ({helper}) => {
+    const newFinCouncilMember = helper.arrange.createEmptyAccount();
+    const addMemberProposal = helper.finCouncil.membership.addMemberCall(newFinCouncilMember.address);
+    await proposalFromAllCommittee(addMemberProposal);
+
+    const finCouncilMembers = await helper.finCouncil.membership.getMembers();
+    expect(finCouncilMembers).to.contains(newFinCouncilMember.address);
+  });
+
+
+  itSub('[Negative] TechComm member can\'t add FinCouncil member', async ({helper}) => {
+    const newFinCouncilMember = helper.arrange.createEmptyAccount();
+    await expect(helper.technicalCommittee.collective.execute(
+      techcomms.greg,
+      helper.finCouncil.membership.addMemberCall(newFinCouncilMember.address),
+    )).rejectedWith('BadOrigin');
+  });
+
+  itSub('[Negative] TechComm member cannot register foreign asset', async ({helper}) => {
+    const location = {
+      parents: 1,
+      interior: {X3: [
+        {
+          Parachain: 1000,
+        },
+        {
+          PalletInstance: 50,
+        },
+        {
+          GeneralIndex: 1985,
+        },
+      ]},
+    };
+    const assetId = {Concrete: location};
+
+    const foreignAssetProposal = helper.constructApiCall(
+      'api.tx.foreignAssets.forceRegisterForeignAsset',
+      [{V3: assetId}, helper.util.str2vec('New Asset2'), 'NEW', {Fungible: 10}],
+    );
+
+    await expect(helper.technicalCommittee.collective.execute(techcomms.andy, foreignAssetProposal))
+      .to.be.rejectedWith('BadOrigin');
+  });
+
   itSub('[Negative] TechComm cannot submit regular democracy proposal', async ({helper}) => {
     const councilProposal = await helper.democracy.proposeCall(dummyProposalCall(helper), 0n);
 
modifiedjs-packages/tests/sub/governance/util.tsdiffbeforeafterboth
--- a/js-packages/tests/sub/governance/util.ts
+++ b/js-packages/tests/sub/governance/util.ts
@@ -29,12 +29,62 @@
   filip: IKeyringPair;
   irina: IKeyringPair;
 }
+
+export interface IFinCounselors {
+  greg: IKeyringPair;
+  ildar: IKeyringPair;
+  andy: IKeyringPair;
+}
+
 export interface ITechComms {
     greg: IKeyringPair;
     andy: IKeyringPair;
     constantine: IKeyringPair;
 }
 
+export function initFinCouncil(donor: IKeyringPair, superuser: IKeyringPair): Promise<IFinCounselors> {
+  return usingPlaygrounds(async (helper) => {
+    const [greg, ildar, andy] = await helper.arrange.createAccounts([10_000n, 10_000n, 10_000n], donor);
+    const sudo = helper.getSudo();
+    {
+      const members = (await helper.callRpc('api.query.financialCouncilMembership.members')).toJSON() as [];
+      if(members.length != 0) {
+        await clearFinCouncil(superuser);
+      }
+    }
+    const expectedMembers = [greg, ildar, andy];
+    for(const member of expectedMembers) {
+      await sudo.executeExtrinsic(superuser, 'api.tx.financialCouncilMembership.addMember', [member.address]);
+    }
+    await sudo.executeExtrinsic(superuser, 'api.tx.financialCouncilMembership.setPrime', [greg.address]);
+    {
+      const members = (await helper.callRpc('api.query.financialCouncilMembership.members')).toJSON();
+      expect(members).to.containSubset(expectedMembers.map((x: IKeyringPair) => x.address));
+      expect(members.length).to.be.equal(expectedMembers.length);
+    }
+
+    return {
+      greg,
+      ildar,
+      andy,
+    };
+  });
+}
+
+export async function clearFinCouncil(superuser: IKeyringPair) {
+  await usingPlaygrounds(async (helper) => {
+    let members = (await helper.callRpc('api.query.financialCouncilMembership.members')).toJSON();
+    if(members.length) {
+      const sudo = helper.getSudo();
+      for(const address of members) {
+        await sudo.executeExtrinsic(superuser, 'api.tx.financialCouncilMembership.removeMember', [address]);
+      }
+      members = (await helper.callRpc('api.query.financialCouncilMembership.members')).toJSON();
+    }
+    expect(members).to.be.deep.equal([]);
+  });
+}
+
 export async function initCouncil(donor: IKeyringPair, superuser: IKeyringPair) {
   let counselors: IKeyringPair[] = [];