difftreelog
Add helpers and refactor some tests
in: master
2 files changed
tests/src/app-promotion.test.tsdiffbeforeafterboth292830import {encodeAddress, mnemonicGenerate} from '@polkadot/util-crypto';29import {encodeAddress, mnemonicGenerate} from '@polkadot/util-crypto';31import {stringToU8a} from '@polkadot/util';30import {stringToU8a} from '@polkadot/util';32import {UniqueHelper} from './util/playgrounds/unique';33import {ApiPromise} from '@polkadot/api';31import {ApiPromise} from '@polkadot/api';34chai.use(chaiAsPromised);32chai.use(chaiAsPromised);35const expect = chai.expect;33const expect = chai.expect;40let nominal: bigint;38let nominal: bigint;41let promotionStartBlock: number | null = null;39let promotionStartBlock: number | null = null;4041before(async function () {42 await usingPlaygrounds(async (helper, privateKeyWrapper) => {43 if (!getModuleNames(helper.api!).includes(Pallets.AppPromotion)) this.skip();44 alice = privateKeyWrapper('//Alice');45 bob = privateKeyWrapper('//Bob');46 palletAdmin = privateKeyWrapper('//palletAdmin');47 const tx = helper.api!.tx.sudo.sudo(helper.api!.tx.promotion.setAdminAddress(normalizeAccountId(palletAdmin)));48 await helper.signTransaction(alice, tx);49 nominal = helper.balance.getOneTokenNominal();50 });51});425243describe('app-promotions.stake extrinsic', () => {53describe('app-promotions.stake extrinsic', () => {44 before(async function() {45 await usingPlaygrounds(async (helper, privateKeyWrapper) => {46 if (!getModuleNames(helper.api!).includes(Pallets.AppPromotion)) this.skip();47 alice = privateKeyWrapper('//Alice');48 bob = privateKeyWrapper('//Bob');49 palletAdmin = privateKeyWrapper('//palletAdmin');50 const tx = helper.api!.tx.sudo.sudo(helper.api!.tx.promotion.setAdminAddress(normalizeAccountId(palletAdmin)));51 nominal = helper.balance.getOneTokenNominal();52 await helper.signTransaction(alice, tx);53 });54 });55 it('will change balance state to "locked", add it to "staked" map, and increase "totalStaked" amount', async () => {54 it('will change balance state to "locked", add it to "staked" map, and increase "totalStaked" amount', async () => {56 // arrange: Alice balance = 100057 // act: Alice calls appPromotion.stake(100)58 // assert: Alice locked balance equal 10059 // assert: Alice free balance closeTo 90060 // assert: query appPromotion.staked(Alice) equal [100]61 // assert: query appPromotion.totalStaked() increased by 10062 // act: Alice extrinsic appPromotion.stake(200)63 64 // assert: Alice locked balance equal 30065 // assert: query appPromotion.staked(Alice) equal [100, 200]66 // assert: query appPromotion.totalStaked() increased by 20067 68 await usingPlaygrounds(async (helper) => {55 await usingPlaygrounds(async (helper) => {69 const totalStakedBefore = await helper.staking.getTotalStaked();56 const totalStakedBefore = await helper.staking.getTotalStaked();70 const staker = await createUser();57 const [staker] = await helper.arrange.creteAccounts([10n], alice);71 58 72 await expect(helper.staking.stake(staker, nominal - 1n)).to.be.eventually.rejected;59 await expect(helper.staking.stake(staker, nominal - 1n)).to.be.eventually.rejected; // minimum stake amount is 173 await expect(helper.staking.stake(staker, nominal * 1n)).to.be.eventually.fulfilled;60 await expect(helper.staking.stake(staker, nominal)).to.be.eventually.fulfilled;74 expect(await helper.staking.getTotalStakingLocked({Substrate: staker.address})).to.be.equal(nominal);61 expect(await helper.staking.getTotalStakingLocked({Substrate: staker.address})).to.be.equal(nominal);756276 // TODO add helpers to assert bigints. Check balance close to 1063 // TODO add helpers to assert bigints. Check balance close to 1077 expect(await helper.balance.getSubstrate(staker.address) - 9n * nominal >= (nominal / 2n)).to.be.true;64 expect(await helper.balance.getSubstrate(staker.address) - 9n * nominal >= (nominal / 2n)).to.be.true;78 expect(await helper.staking.getTotalStaked({Substrate: staker.address})).to.be.equal(nominal);65 expect(await helper.staking.getTotalStaked({Substrate: staker.address})).to.be.equal(nominal);66 // it is potentially flaky test. Promotion can credited some tokens. Maybe we need to use closeTo? 79 expect(await helper.staking.getTotalStaked()).to.be.equal(totalStakedBefore + nominal);67 expect(await helper.staking.getTotalStaked()).to.be.equal(totalStakedBefore + nominal); // total tokens amount staked in app-promotion increased 806881 await helper.arrange.waitNewBlocks(1);82 83 await expect(helper.staking.stake(staker, 2n * nominal)).to.be.eventually.fulfilled;69 await expect(helper.staking.stake(staker, 2n * nominal)).to.be.eventually.fulfilled;84 expect(await helper.staking.getTotalStakingLocked({Substrate: staker.address})).to.be.equal(3n * nominal);70 expect(await helper.staking.getTotalStakingLocked({Substrate: staker.address})).to.be.equal(3n * nominal);89 });75 });90 76 91 it('will throws if stake amount is more than total free balance', async () => {77 it('will throws if stake amount is more than total free balance', async () => {92 // arrange: Alice balance = 100093 // assert: Alice calls appPromotion.stake(1000) throws /// because Alice needs some fee9495 // act: Alice calls appPromotion.stake(700)96 // assert: Alice calls appPromotion.stake(400) throws /// because Alice has ~300 free QTZ and 700 locked9798 await usingPlaygrounds(async helper => { 78 await usingPlaygrounds(async helper => { 99 100 const staker = await createUser();79 const [staker] = await helper.arrange.creteAccounts([10n], alice);101 80102 await expect(helper.signTransaction(staker, helper.api!.tx.promotion.stake(10n * nominal))).to.be.eventually.rejected;81 await expect(helper.staking.stake(staker, 10n * nominal)).to.be.eventually.rejected; // because Alice needs some fee103 await expect(helper.signTransaction(staker, helper.api!.tx.promotion.stake(7n * nominal))).to.be.eventually.fulfilled;82 await expect(helper.staking.stake(staker, 7n * nominal)).to.be.eventually.fulfilled;104 await expect(helper.signTransaction(staker, helper.api!.tx.promotion.stake(4n * nominal))).to.be.eventually.rejected;83 await expect(helper.staking.stake(staker, 4n * nominal)).to.be.eventually.rejected; // because Alice has ~300 free QTZ and 700 locked105 84 expect(await helper.staking.getTotalStaked({Substrate: staker.address})).to.be.equal(7n * nominal);106 });85 });107 });86 });108 87 109 it('for different accounts in one block is possible', async () => {88 it('for different accounts in one block is possible', async () => {110 // arrange: Alice, Bob, Charlie, Dave balance = 1000111 // arrange: Alice, Bob, Charlie, Dave calls appPromotion.stake(100) in the same time112113 // assert: query appPromotion.staked(Alice/Bob/Charlie/Dave) equal [100]114 await usingPlaygrounds(async helper => {89 await usingPlaygrounds(async helper => {115 const crowd = [];90 const crowd = await helper.arrange.creteAccounts([10n, 10n, 10n, 10n], alice);116 for(let i = 4; i--;) crowd.push(await createUser());91 117 118 const promises = crowd.map(async user => helper.signTransaction(user, helper.api!.tx.promotion.stake(nominal)));92 const crowdStartsToStake = crowd.map(user => helper.staking.stake(user, nominal));119 await expect(Promise.all(promises)).to.be.eventually.fulfilled;93 await expect(Promise.all(crowdStartsToStake)).to.be.eventually.fulfilled;120 94121 for (let i = 0; i < crowd.length; i++){95 const crowdStakes = await Promise.all(crowd.map(address => helper.staking.getTotalStaked({Substrate: address.address})));122 expect((await helper.api!.rpc.unique.totalStaked(normalizeAccountId(crowd[i]))).toBigInt()).to.be.equal(nominal); 96 expect(crowdStakes).to.deep.equal([nominal, nominal, nominal, nominal]);123 }124 });97 });125 });98 });12699 // TODO it('Staker stakes 5 times in one block with nonce');100 // TODO it('Staked balance appears as locked in the balance pallet')127});101});128102129describe('unstake balance extrinsic', () => {103describe('unstake balance extrinsic', () => { 130 before(async function () {131 await usingPlaygrounds(async (helper, privateKeyWrapper) => {132 if (!getModuleNames(helper.api!).includes(Pallets.AppPromotion)) this.skip();133 alice = privateKeyWrapper('//Alice');134 bob = privateKeyWrapper('//Bob');135 palletAdmin = privateKeyWrapper('//palletAdmin');136 const tx = helper.api!.tx.sudo.sudo(helper.api!.tx.promotion.setAdminAddress(normalizeAccountId(palletAdmin)));137 nominal = helper.balance.getOneTokenNominal();138 await helper.signTransaction(alice, tx);139 });140 });141 142 it('will change balance state to "reserved", add it to "pendingUnstake" map, and subtract it from totalStaked', async () => {104 it('will change balance state to "reserved", add it to "pendingUnstake" map, and subtract it from totalStaked', async () => {143 // arrange: Alice balance = 1000144 // arrange: Alice calls appPromotion.stake(Alice, 500)145146 // act: Alice calls appPromotion.unstake(300)147 // assert: Alice reserved balance to equal 300148 // assert: query appPromotion.staked(Alice) equal [200] /// 500 - 300149 // assert: query appPromotion.pendingUnstake(Alice) to equal [300]150 // assert: query appPromotion.totalStaked() decreased by 300151 await usingPlaygrounds(async helper => {105 await usingPlaygrounds(async helper => {152 const totalStakedBefore = (await helper.api!.rpc.unique.totalStaked()).toBigInt();106 const totalStakedBefore = await helper.staking.getTotalStaked();153 const staker = await createUser();107 const [staker] = await helper.arrange.creteAccounts([10n], alice);154 await expect(helper.signTransaction(staker, helper.api!.tx.promotion.stake(5n * nominal))).to.be.eventually.fulfilled;108 await expect(helper.staking.stake(staker, 5n * nominal)).to.be.eventually.fulfilled;155 await expect(helper.signTransaction(staker, helper.api!.tx.promotion.unstake(3n * nominal))).to.be.eventually.fulfilled;109 await expect(helper.staking.unstake(staker, 3n * nominal)).to.be.eventually.fulfilled;110156 expect((await helper.api!.rpc.unique.pendingUnstake(normalizeAccountId(staker.address))).toBigInt()).to.be.equal(3n * nominal);111 expect(await helper.staking.getPendingUnstake({Substrate: staker.address})).to.be.equal(3n * nominal);157 expect((await helper.api!.rpc.unique.totalStaked(normalizeAccountId(staker))).toBigInt()).to.be.equal(2n * nominal);112 expect(await helper.staking.getTotalStaked({Substrate: staker.address})).to.be.equal(2n * nominal);158 expect((await helper.api!.rpc.unique.totalStaked()).toBigInt()).to.be.equal(totalStakedBefore + 2n * nominal);113 expect(await helper.staking.getTotalStaked()).to.be.equal(totalStakedBefore + 2n * nominal);159 });114 });160 });115 });161 it('will remove from the "staked" map starting from the oldest entry', async () => {116 it('will remove from the "staked" map starting from the oldest entry', async () => {838 });793 });839}794}840795841const creteAccounts = async (balances: bigint[], donor: IKeyringPair, helper: UniqueHelper) => {842 let nonce = await helper.chain.getNonce(donor.address);843 const tokenNominal = helper.balance.getOneTokenNominal();844 const transactions = [];845 const accounts = [];846 for (const balance of balances) {847 const recepient = helper.util.fromSeed(mnemonicGenerate());848 accounts.push(recepient);849 if (balance !== 0n){850 const tx = helper.constructApiCall('api.tx.balances.transfer', [{Id: recepient.address}, balance * tokenNominal]);851 transactions.push(helper.signTransaction(donor, tx, 'account generation', {nonce}));852 nonce++;853 }854 }855856 await Promise.all(transactions);857 return accounts;858};tests/src/util/playgrounds/unique.tsdiffbeforeafterboth--- a/tests/src/util/playgrounds/unique.ts
+++ b/tests/src/util/playgrounds/unique.ts
@@ -1983,20 +1983,39 @@
/**
* Stake tokens for App Promotion
* @param signer keyring of signer
- * @param amount amount of tokens to stake
+ * @param amountToStake amount of tokens to stake
* @param label extra label for log
+ * @returns
*/
- async stake(signer: TSigner, amount: bigint, label?: string): Promise<boolean> {
- if(typeof label === 'undefined') label = `${signer.address} amount: ${amount}`;
+ async stake(signer: TSigner, amountToStake: bigint, label?: string): Promise<boolean> {
+ if(typeof label === 'undefined') label = `${signer.address} amount: ${amountToStake}`;
const stakeResult = await this.helper.executeExtrinsic(
signer,
- 'api.tx.promotion.stake', [amount],
+ 'api.tx.promotion.stake', [amountToStake],
true, `stake failed for ${label}`,
);
- // TODO extract info from events
+ // TODO extract info from stakeResult
return true;
}
+ /**
+ * Unstake tokens for App Promotion
+ * @param signer keyring of signer
+ * @param amountToUnstake amount of tokens to unstake
+ * @param label extra label for log
+ * @returns
+ */
+ async unstake(signer: TSigner, amountToUnstake: bigint, label?: string): Promise<boolean> {
+ if(typeof label === 'undefined') label = `${signer.address} amount: ${amountToUnstake}`;
+ const unstakeResult = await this.helper.executeExtrinsic(
+ signer,
+ 'api.tx.promotion.unstake', [amountToUnstake],
+ true, `unstake failed for ${label}`,
+ );
+ // TODO extract info from unstakeResult
+ return true;
+ }
+
async getTotalStaked(address?: ICrossAccountId): Promise<bigint> {
if (address) return (await this.helper.callRpc('api.rpc.unique.totalStaked', [address])).toBigInt();
return (await this.helper.callRpc('api.rpc.unique.totalStaked')).toBigInt();
@@ -2009,6 +2028,10 @@
async getTotalStakedPerBlock(address: ICrossAccountId): Promise<bigint[][]> {
return (await this.helper.callRpc('api.rpc.unique.totalStakedPerBlock', [address])).map(([block, amount]: any[]) => [block.toBigInt(), amount.toBigInt()]);
}
+
+ async getPendingUnstake(address: ICrossAccountId): Promise<bigint> {
+ return (await this.helper.callRpc('api.rpc.unique.pendingUnstake')).toBigInt();
+ }
}
export class UniqueHelper extends ChainHelperBase {