difftreelog
test(scheduler) enable and add more coverage, leave sponsorship disabled
in: master
2 files changed
tests/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);
});
tests/src/deprecated-helpers/helpers.tsdiffbeforeafterbothno changes