git.delta.rocks / unique-network / refs/commits / 5bf7ef40777d

difftreelog

source

js-packages/tests/sub/governance/council.test.ts20.5 KiBsourcehistory
12import type {IKeyringPair} from '@polkadot/types/types';3import {usingPlaygrounds, itSub, expect, Pallets, requirePalletsOrSkip, describeGov} from '../../util/index.js';4import {Event} from '@unique/playgrounds/unique.dev.js';5import {initCouncil, democracyLaunchPeriod, democracyVotingPeriod, democracyEnactmentPeriod, councilMotionDuration, democracyFastTrackVotingPeriod, fellowshipRankLimit, clearCouncil, clearTechComm, initTechComm, clearFellowship, dummyProposal, dummyProposalCall, initFellowship, defaultEnactmentMoment, fellowshipPropositionOrigin} from './util.js';6import type {ICounselors} from './util.js';78describeGov('Governance: Council tests', () => {9  let donor: IKeyringPair;10  let counselors: ICounselors;11  let sudoer: IKeyringPair;1213  const moreThanHalfCouncilThreshold = 3;1415  before(async function() {16    await usingPlaygrounds(async (helper, privateKey) => {17      requirePalletsOrSkip(this, helper, [Pallets.Council]);1819      donor = await privateKey({url: import.meta.url});20      sudoer = await privateKey('//Alice');21    });22  });2324  beforeEach(async () => {25    counselors = await initCouncil(donor, sudoer);26  });2728  afterEach(async () => {29    await clearCouncil(sudoer);30    await clearTechComm(sudoer);31  });3233  async function proposalFromMoreThanHalfCouncil(proposal: any) {34    return await usingPlaygrounds(async (helper) => {35      expect((await helper.callRpc('api.query.councilMembership.members')).toJSON().length).to.be.equal(5);36      const proposeResult = await helper.council.collective.propose(37        counselors.filip,38        proposal,39        moreThanHalfCouncilThreshold,40      );4142      const councilProposedEvent = Event.Council.Proposed.expect(proposeResult);43      const proposalIndex = councilProposedEvent.proposalIndex;44      const proposalHash = councilProposedEvent.proposalHash;454647      await helper.council.collective.vote(counselors.alex, proposalHash, proposalIndex, true);48      await helper.council.collective.vote(counselors.charu, proposalHash, proposalIndex, true);49      await helper.council.collective.vote(counselors.filip, proposalHash, proposalIndex, true);5051      return await helper.council.collective.close(counselors.filip, proposalHash, proposalIndex);52    });53  }5455  async function proposalFromAllCouncil(proposal: any) {56    return await usingPlaygrounds(async (helper) => {57      expect((await helper.callRpc('api.query.councilMembership.members')).toJSON().length).to.be.equal(5);58      const proposeResult = await helper.council.collective.propose(59        counselors.filip,60        proposal,61        moreThanHalfCouncilThreshold,62      );6364      const councilProposedEvent = Event.Council.Proposed.expect(proposeResult);65      const proposalIndex = councilProposedEvent.proposalIndex;66      const proposalHash = councilProposedEvent.proposalHash;676869      await helper.council.collective.vote(counselors.alex, proposalHash, proposalIndex, true);70      await helper.council.collective.vote(counselors.charu, proposalHash, proposalIndex, true);71      await helper.council.collective.vote(counselors.ildar, proposalHash, proposalIndex, true);72      await helper.council.collective.vote(counselors.irina, proposalHash, proposalIndex, true);73      await helper.council.collective.vote(counselors.filip, proposalHash, proposalIndex, true);7475      return await helper.council.collective.close(counselors.filip, proposalHash, proposalIndex);76    });77  }7879  itSub('>50% of Council can externally propose SuperMajorityAgainst', async ({helper}) => {80    const forceSetBalanceReceiver = helper.arrange.createEmptyAccount();81    const forceSetBalanceTestValue = 20n * 10n ** 25n;8283    const democracyProposal = await helper.constructApiCall('api.tx.balances.forceSetBalance', [84      forceSetBalanceReceiver.address, forceSetBalanceTestValue,85    ]);8687    const councilProposal = await helper.democracy.externalProposeDefaultCall(democracyProposal);8889    const proposeResult = await helper.council.collective.propose(90      counselors.filip,91      councilProposal,92      moreThanHalfCouncilThreshold,93    );9495    const councilProposedEvent = Event.Council.Proposed.expect(proposeResult);96    const proposalIndex = councilProposedEvent.proposalIndex;97    const proposalHash = councilProposedEvent.proposalHash;9899    await helper.council.collective.vote(counselors.alex, proposalHash, proposalIndex, true);100    await helper.council.collective.vote(counselors.charu, proposalHash, proposalIndex, true);101    await helper.council.collective.vote(counselors.filip, proposalHash, proposalIndex, true);102103    await helper.council.collective.close(counselors.filip, proposalHash, proposalIndex);104105    const democracyStartedEvent = await helper.wait.expectEvent(democracyLaunchPeriod, Event.Democracy.Started);106    const democracyReferendumIndex = democracyStartedEvent.referendumIndex;107    const democracyThreshold = democracyStartedEvent.threshold;108109    expect(democracyThreshold).to.be.equal('SuperMajorityAgainst');110111    await helper.democracy.vote(counselors.filip, democracyReferendumIndex, {112      Standard: {113        vote: {114          aye: true,115          conviction: 1,116        },117        balance: 10_000n,118      },119    });120121    await helper.democracy.vote(counselors.charu, democracyReferendumIndex, {122      Standard: {123        vote: {124          aye: false,125          conviction: 1,126        },127        balance: 50_000n,128      },129    });130131    const passedReferendumEvent = await helper.wait.expectEvent(democracyVotingPeriod, Event.Democracy.Passed);132    expect(passedReferendumEvent.referendumIndex).to.be.equal(democracyReferendumIndex);133134    await helper.wait.expectEvent(democracyEnactmentPeriod, Event.Scheduler.Dispatched);135    const receiverBalance = await helper.balance.getSubstrate(forceSetBalanceReceiver.address);136    expect(receiverBalance).to.be.equal(forceSetBalanceTestValue);137  });138139  itSub('Council prime member vote is the default', async ({helper}) => {140    const newTechCommMember = helper.arrange.createEmptyAccount();141    const addMemberProposal = helper.technicalCommittee.membership.addMemberCall(newTechCommMember.address);142    const proposeResult = await helper.council.collective.propose(143      counselors.filip,144      addMemberProposal,145      moreThanHalfCouncilThreshold,146    );147148    const councilProposedEvent = Event.Council.Proposed.expect(proposeResult);149    const proposalIndex = councilProposedEvent.proposalIndex;150    const proposalHash = councilProposedEvent.proposalHash;151152    await helper.council.collective.vote(counselors.alex, proposalHash, proposalIndex, true);153154    await helper.wait.newBlocks(councilMotionDuration);155    const closeResult = await helper.council.collective.close(counselors.filip, proposalHash, proposalIndex);156    const closeEvent = Event.Council.Closed.expect(closeResult);157    const members = (await helper.callRpc('api.query.councilMembership.members')).toJSON() as string[];158    expect(closeEvent.yes).to.be.equal(members.length);159  });160161  itSub('Superuser can add a member', async ({helper}) => {162    const newMember = helper.arrange.createEmptyAccount();163    await expect(helper.getSudo().council.membership.addMember(sudoer, newMember.address)).to.be.fulfilled;164165    const members = (await helper.callRpc('api.query.councilMembership.members')).toJSON();166    expect(members).to.contains(newMember.address);167  });168169  itSub('Superuser can remove a member', async ({helper}) => {170    await expect(helper.getSudo().council.membership.removeMember(sudoer, counselors.alex.address)).to.be.fulfilled;171172    const members = (await helper.callRpc('api.query.councilMembership.members')).toJSON();173    expect(members).to.not.contains(counselors.alex.address);174  });175176  itSub('>50% Council can add TechComm member', async ({helper}) => {177    const newTechCommMember = helper.arrange.createEmptyAccount();178    const addMemberProposal = helper.technicalCommittee.membership.addMemberCall(newTechCommMember.address);179180    await proposalFromMoreThanHalfCouncil(addMemberProposal);181182    const techCommMembers = (await helper.callRpc('api.query.technicalCommitteeMembership.members')).toJSON();183    expect(techCommMembers).to.contains(newTechCommMember.address);184  });185186  itSub('Council can remove TechComm member', async ({helper}) => {187    const techComm = await initTechComm(donor, sudoer);188    const removeMemberPrpoposal = helper.technicalCommittee.membership.removeMemberCall(techComm.andy.address);189    await proposalFromMoreThanHalfCouncil(removeMemberPrpoposal);190191    const techCommMembers = (await helper.callRpc('api.query.technicalCommitteeMembership.members')).toJSON();192    expect(techCommMembers).to.not.contains(techComm.andy.address);193  });194195  itSub.skip('Council member can add Fellowship member', async ({helper}) => {196    const newFellowshipMember = helper.arrange.createEmptyAccount();197    await expect(helper.council.collective.execute(198      counselors.alex,199      helper.fellowship.collective.addMemberCall(newFellowshipMember.address),200    )).to.be.fulfilled;201    const fellowshipMembers = (await helper.callRpc('api.query.fellowshipCollective.members')).toJSON();202    expect(fellowshipMembers).to.contains(newFellowshipMember.address);203  });204205  itSub('>50% Council can promote Fellowship member', async ({helper}) => {206    const fellowship = await initFellowship(donor, sudoer);207    const memberWithZeroRank = fellowship[0][0];208209    const proposal = helper.fellowship.collective.promoteCall(memberWithZeroRank.address);210    await proposalFromMoreThanHalfCouncil(proposal);211    const record = (await helper.callRpc('api.query.fellowshipCollective.members', [memberWithZeroRank.address])).toJSON();212    expect(record).to.be.deep.equal({rank: 1});213214    await clearFellowship(sudoer);215  });216217  itSub('>50% Council can demote Fellowship member', async ({helper}) => {218    const fellowship = await initFellowship(donor, sudoer);219    const memberWithRankOne = fellowship[1][0];220221    const proposal = helper.fellowship.collective.demoteCall(memberWithRankOne.address);222    await proposalFromMoreThanHalfCouncil(proposal);223224    const record = (await helper.callRpc('api.query.fellowshipCollective.members', [memberWithRankOne.address])).toJSON();225    expect(record).to.be.deep.equal({rank: 0});226227    await clearFellowship(sudoer);228  });229230  itSub('>50% Council can add\remove Fellowship member', async ({helper}) => {231    try {232      const newMember = helper.arrange.createEmptyAccount();233234      const proposalAdd = helper.fellowship.collective.addMemberCall(newMember.address);235      const proposalRemove = helper.fellowship.collective.removeMemberCall(newMember.address, fellowshipRankLimit);236      await expect(proposalFromMoreThanHalfCouncil(proposalAdd)).to.be.fulfilled;237      expect(await helper.fellowship.collective.getMembers()).to.be.deep.contain(newMember.address);238      await expect(proposalFromMoreThanHalfCouncil(proposalRemove)).to.be.fulfilled;239      expect(await helper.fellowship.collective.getMembers()).to.be.not.deep.contain(newMember.address);240    }241    finally {242      await clearFellowship(sudoer);243    }244  });245246  itSub('Council can blacklist Democracy proposals', async ({helper}) => {247    const preimageHash = await helper.preimage.notePreimageFromCall(sudoer, dummyProposalCall(helper), true);248    await expect(proposalFromAllCouncil(helper.democracy.blacklistCall(preimageHash, null))).to.be.fulfilled;249  });250251  itSub('Sudo can blacklist Democracy proposals', async ({helper}) => {252    const preimageHash = await helper.preimage.notePreimageFromCall(sudoer, dummyProposalCall(helper), true);253    await expect(helper.getSudo().democracy.blacklist(sudoer, preimageHash)).to.be.fulfilled;254  });255256  itSub('[Negative] Council cannot add Council member', async ({helper}) => {257    const newCouncilMember = helper.arrange.createEmptyAccount();258    const addMemberProposal = helper.council.membership.addMemberCall(newCouncilMember.address);259260    await expect(proposalFromAllCouncil(addMemberProposal)).to.be.rejected;261  });262263  itSub('[Negative] Council cannot remove Council member', async ({helper}) => {264    const removeMemberProposal = helper.council.membership.removeMemberCall(counselors.alex.address);265266    await expect(proposalFromAllCouncil(removeMemberProposal)).to.be.rejected;267  });268269  itSub('[Negative] Council cannot submit regular democracy proposal', async ({helper}) => {270    const councilProposal = await helper.democracy.proposeCall(dummyProposalCall(helper), 0n);271272    await expect(proposalFromAllCouncil(councilProposal)).to.be.rejectedWith(/BadOrigin/);273  });274275  itSub('[Negative] Council cannot externally propose SimpleMajority', async ({helper}) => {276    const councilProposal = await helper.democracy.externalProposeMajorityCall(dummyProposalCall(helper));277278    await expect(proposalFromAllCouncil(councilProposal)).to.be.rejectedWith(/BadOrigin/);279  });280281  itSub('[Negative] Council cannot externally propose SuperMajorityApprove', async ({helper}) => {282    const councilProposal = await helper.democracy.externalProposeCall(dummyProposalCall(helper));283284    await expect(proposalFromAllCouncil(councilProposal)).to.be.rejectedWith(/BadOrigin/);285  });286287  itSub('[Negative] Council member cannot add/remove a Council member', async ({helper}) => {288    const newCouncilMember = helper.arrange.createEmptyAccount();289    await expect(helper.council.collective.execute(290      counselors.alex,291      helper.council.membership.addMemberCall(newCouncilMember.address),292    )).to.be.rejectedWith('BadOrigin');293    await expect(helper.council.collective.execute(294      counselors.alex,295      helper.council.membership.removeMemberCall(counselors.charu.address),296    )).to.be.rejectedWith('BadOrigin');297  });298299  itSub('[Negative] Council cannot set/clear Council prime member', async ({helper}) => {300    const proposalForSet = await helper.council.membership.setPrimeCall(counselors.charu.address);301    const proposalForClear = await helper.council.membership.clearPrimeCall();302303    await expect(proposalFromAllCouncil(proposalForSet)).to.be.rejectedWith(/BadOrigin/);304    await expect(proposalFromAllCouncil(proposalForClear)).to.be.rejectedWith(/BadOrigin/);305306  });307308  itSub('[Negative] Council member cannot set/clear Council prime member', async ({helper}) => {309    await expect(helper.council.collective.execute(310      counselors.alex,311      helper.council.membership.setPrimeCall(counselors.charu.address),312    )).to.be.rejectedWith('BadOrigin');313    await expect(helper.council.collective.execute(314      counselors.alex,315      helper.council.membership.clearPrimeCall(),316    )).to.be.rejectedWith('BadOrigin');317  });318319  itSub('[Negative] Council member cannot add/remove a TechComm member', async ({helper}) => {320    const newTechCommMember = helper.arrange.createEmptyAccount();321    await expect(helper.council.collective.execute(322      counselors.alex,323      helper.technicalCommittee.membership.addMemberCall(newTechCommMember.address),324    )).to.be.rejectedWith('BadOrigin');325    await expect(helper.council.collective.execute(326      counselors.alex,327      helper.technicalCommittee.membership.removeMemberCall(newTechCommMember.address),328    )).to.be.rejectedWith('BadOrigin');329  });330331  itSub('[Negative] Council member cannot promote/demote a Fellowship member', async ({helper}) => {332    const fellowship = await initFellowship(donor, sudoer);333    const memberWithRankOne = fellowship[1][0];334335    await expect(helper.council.collective.execute(336      counselors.alex,337      helper.fellowship.collective.promoteCall(memberWithRankOne.address),338    )).to.be.rejectedWith('BadOrigin');339    await expect(helper.council.collective.execute(340      counselors.alex,341      helper.fellowship.collective.demoteCall(memberWithRankOne.address),342    )).to.be.rejectedWith('BadOrigin');343    await clearFellowship(sudoer);344  });345346  itSub('[Negative] Council cannot fast-track Democracy proposals', async ({helper}) => {347    const preimageHash = await helper.preimage.notePreimageFromCall(sudoer, dummyProposalCall(helper), true);348    await helper.getSudo().democracy.externalProposeDefaultWithPreimage(sudoer, preimageHash);349350    await expect(proposalFromAllCouncil(helper.democracy.fastTrackCall(preimageHash, democracyFastTrackVotingPeriod, 0)))351      .to.be.rejectedWith(/BadOrigin/);352  });353354  itSub('[Negative] Council member cannot fast-track Democracy proposals', async ({helper}) => {355    const preimageHash = await helper.preimage.notePreimageFromCall(sudoer, dummyProposalCall(helper), true);356    await helper.getSudo().democracy.externalProposeDefaultWithPreimage(sudoer, preimageHash);357358    await expect(helper.council.collective.execute(359      counselors.alex,360      helper.democracy.fastTrackCall(preimageHash, democracyFastTrackVotingPeriod, 0),361    )).to.be.rejectedWith('BadOrigin');362  });363364  itSub('[Negative] Council cannot cancel Democracy proposals', async ({helper}) => {365    const proposeResult = await helper.getSudo().democracy.propose(sudoer, dummyProposalCall(helper), 0n);366    const proposalIndex = Event.Democracy.Proposed.expect(proposeResult).proposalIndex;367368    await expect(proposalFromAllCouncil(helper.democracy.cancelProposalCall(proposalIndex)))369      .to.be.rejectedWith(/BadOrigin/);370  });371372  itSub('[Negative] Council member cannot cancel Democracy proposals', async ({helper}) => {373374    const proposeResult = await helper.getSudo().democracy.propose(sudoer, dummyProposalCall(helper), 0n);375    const proposalIndex = Event.Democracy.Proposed.expect(proposeResult).proposalIndex;376377    await expect(helper.council.collective.execute(378      counselors.alex,379      helper.democracy.cancelProposalCall(proposalIndex),380    )).to.be.rejectedWith('BadOrigin');381  });382383  itSub('[Negative] Council cannot cancel ongoing Democracy referendums', async ({helper}) => {384    await helper.getSudo().democracy.externalProposeDefault(sudoer, dummyProposalCall(helper));385    const startedEvent = await helper.wait.expectEvent(democracyLaunchPeriod, Event.Democracy.Started);386    const referendumIndex = startedEvent.referendumIndex;387388    await expect(proposalFromAllCouncil(helper.democracy.emergencyCancelCall(referendumIndex)))389      .to.be.rejectedWith(/BadOrigin/);390  });391392  itSub('[Negative] Council member cannot cancel ongoing Democracy referendums', async ({helper}) => {393    await helper.getSudo().democracy.externalProposeDefault(sudoer, dummyProposalCall(helper));394    const startedEvent = await helper.wait.expectEvent(democracyLaunchPeriod, Event.Democracy.Started);395    const referendumIndex = startedEvent.referendumIndex;396397    await expect(helper.council.collective.execute(398      counselors.alex,399      helper.democracy.emergencyCancelCall(referendumIndex),400    )).to.be.rejectedWith('BadOrigin');401  });402403  itSub('[Negative] Council cannot cancel Fellowship referendums', async ({helper}) => {404    const fellowship = await initFellowship(donor, sudoer);405    const fellowshipProposer = fellowship[5][0];406    const proposal = dummyProposal(helper);407408    const submitResult = await helper.fellowship.referenda.submit(409      fellowshipProposer,410      fellowshipPropositionOrigin,411      proposal,412      defaultEnactmentMoment,413    );414415    const referendumIndex = Event.FellowshipReferenda.Submitted.expect(submitResult).referendumIndex;416417    await expect(proposalFromAllCouncil(helper.fellowship.referenda.cancelCall(referendumIndex)))418      .to.be.rejectedWith(/BadOrigin/);419  });420421  itSub('[Negative] Council member cannot cancel Fellowship referendums', async ({helper}) => {422    const fellowship = await initFellowship(donor, sudoer);423    const fellowshipProposer = fellowship[5][0];424    const proposal = dummyProposal(helper);425426    const submitResult = await helper.fellowship.referenda.submit(427      fellowshipProposer,428      fellowshipPropositionOrigin,429      proposal,430      defaultEnactmentMoment,431    );432    const referendumIndex = Event.FellowshipReferenda.Submitted.expect(submitResult).referendumIndex;433    await expect(helper.council.collective.execute(434      counselors.alex,435      helper.fellowship.referenda.cancelCall(referendumIndex),436    )).to.be.rejectedWith('BadOrigin');437  });438439  itSub('[Negative] Council referendum cannot be closed until the voting threshold is met', async ({helper}) => {440    const councilSize = (await helper.callRpc('api.query.councilMembership.members')).toJSON().length as any as number;441    expect(councilSize).is.greaterThan(1);442    const proposeResult = await helper.council.collective.propose(443      counselors.filip,444      dummyProposalCall(helper),445      councilSize,446    );447448    const councilProposedEvent = Event.Council.Proposed.expect(proposeResult);449    const proposalIndex = councilProposedEvent.proposalIndex;450    const proposalHash = councilProposedEvent.proposalHash;451452453    await helper.council.collective.vote(counselors.alex, proposalHash, proposalIndex, true);454    await expect(helper.council.collective.close(counselors.filip, proposalHash, proposalIndex)).to.be.rejectedWith('TooEarly');455  });456457});