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

difftreelog

test(scheduler) enable and add more coverage, leave sponsorship disabled

Farhad Hakimov2022-07-20parent: #4025130.patch.diff
in: master

2 files changed

modifiedtests/src/.outdated/scheduler.test.tsdiffbeforeafterboth
--- a/tests/src/.outdated/scheduler.test.ts
+++ b/tests/src/.outdated/scheduler.test.ts
@@ -17,7 +17,7 @@
 import chai, {expect} from 'chai';
 import chaiAsPromised from 'chai-as-promised';
 import {
-  default as usingApi, 
+  default as usingApi,
   submitTransactionAsync,
 } from '../substrate/substrate-api';
 import {
@@ -35,78 +35,247 @@
   normalizeAccountId,
   getTokenOwner,
   getGenericResult,
-  scheduleTransferFundsPeriodicExpectSuccess,
+  scheduleTransferFundsExpectSuccess,
   getFreeBalance,
   confirmSponsorshipByKeyExpectSuccess,
   scheduleExpectFailure,
+  scheduleAfter,
+  cancelScheduled,
 } from '../deprecated-helpers/helpers';
 import {IKeyringPair} from '@polkadot/types/types';
+import {ApiPromise} from '@polkadot/api';
 
 chai.use(chaiAsPromised);
 
-// todo:playgrounds skipped ~ postponed
-describe.skip('Scheduling token and balance transfers', () => {
+const scheduledIdBase: string = '0x' + '0'.repeat(31);
+let scheduledIdSlider = 0;
+
+// Loop scheduledId around 10. Unless there are concurrent tasks with long periods/repetitions, tests' tasks' ids shouldn't ovelap.
+function makeScheduledId(): string {
+  return scheduledIdBase + ((scheduledIdSlider++) % 10);
+}
+
+// Check that there are no failing Dispatched events in the block
+function checkForFailedSchedulerEvents(api: ApiPromise, events: any[]) {
+  return new Promise((res, rej) => {
+    let schedulerEventPresent = false;
+    
+    for (const {event} of events) {
+      if (api.events.scheduler.Dispatched.is(event)) {
+        schedulerEventPresent = true;
+        const result = event.data.result;
+        if (result.isErr) {
+          const decoded = api.registry.findMetaError(result.asErr.asModule);
+          const {method, section} = decoded;
+          rej(new Error(`${section}.${method}`));
+        }
+      }
+    }
+    res(schedulerEventPresent);
+  });
+}
+
+describe('Scheduling token and balance transfers', () => {
   let alice: IKeyringPair;
   let bob: IKeyringPair;
-  let scheduledIdBase: string;
-  let scheduledIdSlider: number;
 
   before(async() => {
-    await usingApi(async (api, privateKeyWrapper) => {
+    await usingApi(async (_, privateKeyWrapper) => {
       alice = privateKeyWrapper('//Alice');
       bob = privateKeyWrapper('//Bob');
     });
-
-    scheduledIdBase = '0x' + '0'.repeat(31);
-    scheduledIdSlider = 0;
   });
 
-  // Loop scheduledId around 10. Unless there are concurrent tasks with long periods/repetitions, tests' tasks' ids shouldn't ovelap.
-  function makeScheduledId(): string {
-    return scheduledIdBase + ((scheduledIdSlider++) % 10);
-  }
 
-  it('Can schedule a transfer of an owned token with delay', async () => {
-    await usingApi(async () => {
-      const nftCollectionId = await createCollectionExpectSuccess();
-      const newNftTokenId = await createItemExpectSuccess(alice, nftCollectionId, 'NFT');
-      await setCollectionSponsorExpectSuccess(nftCollectionId, alice.address);
-      await confirmSponsorshipExpectSuccess(nftCollectionId);
+  it('Can delay a transfer of an owned token', async () => {
+    await usingApi(async api => {
+      const collectionId = await createCollectionExpectSuccess();
+      const tokenId = await createItemExpectSuccess(alice, collectionId, 'NFT');
 
-      await scheduleTransferAndWaitExpectSuccess(nftCollectionId, newNftTokenId, alice, bob, 1, 4, makeScheduledId());
+      await scheduleTransferAndWaitExpectSuccess(api, collectionId, tokenId, alice, bob, 1, 4, makeScheduledId());
     });
   });
 
   it('Can transfer funds periodically', async () => {
-    await usingApi(async () => {
-      const waitForBlocks = 4;
+    await usingApi(async api => {
+      const waitForBlocks = 1;
       const period = 2;
-      await scheduleTransferFundsPeriodicExpectSuccess(1n * UNIQUE, alice, bob, waitForBlocks, makeScheduledId(), period, 2);
+      const repetitions = 2;
+
+      await scheduleTransferFundsExpectSuccess(api, 1n * UNIQUE, alice, bob, waitForBlocks, makeScheduledId(), period, repetitions);
       const bobsBalanceBefore = await getFreeBalance(bob);
 
-      // discounting already waited-for operations
-      await waitNewBlocks(waitForBlocks - 2);
+      await waitNewBlocks(waitForBlocks + 1);
       const bobsBalanceAfterFirst = await getFreeBalance(bob);
-      expect(bobsBalanceAfterFirst > bobsBalanceBefore).to.be.true;
+      expect(bobsBalanceAfterFirst > bobsBalanceBefore, '#1 Balance of the recipient did not increase').to.be.true;
 
       await waitNewBlocks(period);
       const bobsBalanceAfterSecond = await getFreeBalance(bob);
-      expect(bobsBalanceAfterSecond > bobsBalanceAfterFirst).to.be.true;
+      expect(bobsBalanceAfterSecond > bobsBalanceAfterFirst, '#2 Balance of the recipient did not increase').to.be.true;
     });
   });
 
+  it('Can cancel a scheduled operation which has not yet taken effect', async () => {
+    await usingApi(async api => {
+      const collectionId = await createCollectionExpectSuccess();
+      const tokenId = await createItemExpectSuccess(alice, collectionId, 'NFT');
+      const scheduledId = makeScheduledId();
+      const waitForBlocks = 4;
+
+      await scheduleTransferExpectSuccess(api, collectionId, tokenId, alice, bob, 1, waitForBlocks, scheduledId);
+      await expect(cancelScheduled(api, alice, scheduledId)).to.not.be.rejected;
+
+      await waitNewBlocks(waitForBlocks);
+
+      expect(await getTokenOwner(api, collectionId, tokenId)).to.be.deep.equal(normalizeAccountId(alice.address));
+    });
+  });
+
+  it('Can cancel a periodic operation (transfer of funds)', async () => {
+    await usingApi(async api => {
+      const waitForBlocks = 1;
+      const period = 3;
+      const repetitions = 2;
+
+      const scheduledId = makeScheduledId();
+
+      const bobsBalanceBefore = await getFreeBalance(bob);
+      await scheduleTransferFundsExpectSuccess(api, 1n * UNIQUE, alice, bob, waitForBlocks, scheduledId, period, repetitions);
+
+      await waitNewBlocks(waitForBlocks + 1);
+      const bobsBalanceAfterFirst = await getFreeBalance(bob);
+      expect(bobsBalanceAfterFirst > bobsBalanceBefore, '#1 Balance of the recipient did not increase').to.be.true;
+
+      await expect(cancelScheduled(api, alice, scheduledId)).to.not.be.rejected;
+
+      await waitNewBlocks(period);
+      const bobsBalanceAfterSecond = await getFreeBalance(bob);
+      expect(bobsBalanceAfterSecond == bobsBalanceAfterFirst, '#2 Balance of the recipient changed').to.be.true;
+    });
+  });
+
+  it('Can schedule a scheduled operation of canceling the scheduled operation', async () => {
+    await usingApi(async api => {
+      const scheduledId = makeScheduledId();
+
+      const waitForBlocks = 2;
+      const period = 3;
+      const repetitions = 2;
+
+      await expect(scheduleAfter(
+        api, 
+        api.tx.scheduler.cancelNamed(scheduledId), 
+        alice, 
+        waitForBlocks, 
+        scheduledId, 
+        period, 
+        repetitions,
+      )).to.not.be.rejected;
+
+
+      await waitNewBlocks(waitForBlocks);
+
+      // todo:scheduler debug line; doesn't work (and doesn't appear in events) when executed in the same block as the scheduled transaction
+      //await expect(executeTransaction(api, alice, api.tx.scheduler.cancelNamed(scheduledId))).to.not.be.rejected;
+
+      let schedulerEvents = 0;
+      for (let i = 0; i < period * repetitions; i++) {
+        const events = await api.query.system.events();
+        schedulerEvents += await expect(checkForFailedSchedulerEvents(api, events)).to.not.be.rejected;
+        await waitNewBlocks(1);
+      }
+      expect(schedulerEvents).to.be.equal(repetitions);
+    });
+  });
+
+  after(async () => {
+    // todo:scheduler to avoid the failed results of the previous test interfering with the next, delete after the problem is fixed
+    await waitNewBlocks(6);
+  });
+});
+
+describe('Negative Test: Scheduling', () => {
+  let alice: IKeyringPair;
+  let bob: IKeyringPair;
+
+  before(async() => {
+    await usingApi(async (_, privateKeyWrapper) => {
+      alice = privateKeyWrapper('//Alice');
+      bob = privateKeyWrapper('//Bob');
+    });
+  });
+
+  it('Can\'t overwrite a scheduled ID', async () => {
+    await usingApi(async api => {
+      const collectionId = await createCollectionExpectSuccess();
+      const tokenId = await createItemExpectSuccess(alice, collectionId, 'NFT');
+      const scheduledId = makeScheduledId();
+
+      await scheduleTransferExpectSuccess(api, collectionId, tokenId, alice, bob, 1, 4, scheduledId);
+      await expect(scheduleAfter(
+        api, 
+        api.tx.balances.transfer(alice.address, 1n * UNIQUE), 
+        bob, 
+        2, 
+        scheduledId,
+      )).to.be.rejectedWith(/scheduler\.FailedToSchedule/);
+      const bobsBalance = await getFreeBalance(bob);
+
+      await waitNewBlocks(3);
+
+      expect(await getTokenOwner(api, collectionId, tokenId)).to.be.deep.equal(normalizeAccountId(bob.address));
+      expect(bobsBalance).to.be.equal(await getFreeBalance(bob));
+    });
+  });
+
+  it('Can\'t cancel an operation which is not scheduled', async () => {
+    await usingApi(async api => {
+      const scheduledId = makeScheduledId();
+      await expect(cancelScheduled(api, alice, scheduledId)).to.be.rejectedWith(/scheduler\.NotFound/);
+    });
+  });
+
+  it('Can\'t cancel a non-owned scheduled operation', async () => {
+    await usingApi(async api => {
+      const collectionId = await createCollectionExpectSuccess();
+      const tokenId = await createItemExpectSuccess(alice, collectionId, 'NFT');
+      const scheduledId = makeScheduledId();
+      const waitForBlocks = 3;
+
+      await scheduleTransferExpectSuccess(api, collectionId, tokenId, alice, bob, 1, waitForBlocks, scheduledId);
+      await expect(cancelScheduled(api, bob, scheduledId)).to.be.rejected;
+
+      await waitNewBlocks(waitForBlocks);
+
+      expect(await getTokenOwner(api, collectionId, tokenId)).to.be.deep.equal(normalizeAccountId(alice.address));
+    });
+  });
+});
+
+// Implementation of the functionality tested here was postponed/shelved
+describe.skip('Sponsoring scheduling', () => {
+  let alice: IKeyringPair;
+  let bob: IKeyringPair;
+
+  before(async() => {
+    await usingApi(async (_, privateKeyWrapper) => {
+      alice = privateKeyWrapper('//Alice');
+      bob = privateKeyWrapper('//Bob');
+    });
+  });
+
   it('Can sponsor scheduling a transaction', async () => {
     const collectionId = await createCollectionExpectSuccess();
     await setCollectionSponsorExpectSuccess(collectionId, bob.address);
     await confirmSponsorshipExpectSuccess(collectionId, '//Bob');
 
-    await usingApi(async () => {
+    await usingApi(async api => {
       const tokenId = await createItemExpectSuccess(alice, collectionId, 'NFT', alice.address);
 
       const bobBalanceBefore = await getFreeBalance(bob);
       const waitForBlocks = 4;
       // no need to wait to check, fees must be deducted on scheduling, immediately
-      await scheduleTransferExpectSuccess(collectionId, tokenId, alice, bob, 0, waitForBlocks, makeScheduledId());
+      await scheduleTransferExpectSuccess(api, collectionId, tokenId, alice, bob, 0, waitForBlocks, makeScheduledId());
       const bobBalanceAfter = await getFreeBalance(bob);
       // expect(aliceBalanceAfter == aliceBalanceBefore).to.be.true;
       expect(bobBalanceAfter < bobBalanceBefore).to.be.true;
@@ -135,7 +304,7 @@
 
       // Schedule transfer of the NFT a few blocks ahead
       const waitForBlocks = 5;
-      await scheduleTransferExpectSuccess(collectionId, tokenId, zeroBalance, alice, 1, waitForBlocks, makeScheduledId());
+      await scheduleTransferExpectSuccess(api, collectionId, tokenId, zeroBalance, alice, 1, waitForBlocks, makeScheduledId());
 
       // Get rid of the account's funds before the scheduled transaction takes place
       const balanceTx2 = api.tx.balances.transfer(alice.address, UNIQUE * 68n / 100n);
@@ -167,7 +336,7 @@
       const tokenId = await createItemExpectSuccess(alice, collectionId, 'NFT', alice.address);
 
       const waitForBlocks = 5;
-      await scheduleTransferExpectSuccess(collectionId, tokenId, alice, zeroBalance, 1, waitForBlocks, makeScheduledId());
+      await scheduleTransferExpectSuccess(api, collectionId, tokenId, alice, zeroBalance, 1, waitForBlocks, makeScheduledId());
 
       const emptyBalanceSponsorTx = api.tx.balances.setBalance(zeroBalance.address, 0, 0);
       const sudoTx = api.tx.sudo.sudo(emptyBalanceSponsorTx as any);
@@ -181,7 +350,7 @@
     });
   });
 
-  it.skip('Exceeding sponsor rate limit without having enough funds prevents scheduling a periodic transaction', async () => {
+  it('Exceeding sponsor rate limit without having enough funds prevents scheduling a periodic transaction', async () => {
     const collectionId = await createCollectionExpectSuccess();
     await setCollectionSponsorExpectSuccess(collectionId, bob.address);
     await confirmSponsorshipExpectSuccess(collectionId, '//Bob');
@@ -202,7 +371,7 @@
       };
       await expect(badTransaction()).to.be.rejectedWith('Inability to pay some fees');*/
 
-      await scheduleExpectFailure(creationTx, zeroBalance, 3, makeScheduledId(), 1, 3);
+      await expect(scheduleAfter(api, creationTx, zeroBalance, 3, makeScheduledId(), 1, 3)).to.be.rejectedWith(/Inability to pay some fees/);
 
       expect(await getFreeBalance(bob)).to.be.equal(bobBalanceBefore);
     });
addedtests/src/deprecated-helpers/helpers.tsdiffbeforeafterboth

no changes