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
35 normalizeAccountId,35 normalizeAccountId,
36 getTokenOwner,36 getTokenOwner,
37 getGenericResult,37 getGenericResult,
38 scheduleTransferFundsPeriodicExpectSuccess,38 scheduleTransferFundsExpectSuccess,
39 getFreeBalance,39 getFreeBalance,
40 confirmSponsorshipByKeyExpectSuccess,40 confirmSponsorshipByKeyExpectSuccess,
41 scheduleExpectFailure,41 scheduleExpectFailure,
42 scheduleAfter,
43 cancelScheduled,
42} from '../deprecated-helpers/helpers';44} from '../deprecated-helpers/helpers';
43import {IKeyringPair} from '@polkadot/types/types';45import {IKeyringPair} from '@polkadot/types/types';
46import {ApiPromise} from '@polkadot/api';
4447
45chai.use(chaiAsPromised);48chai.use(chaiAsPromised);
4649
47// todo:playgrounds skipped ~ postponed50const scheduledIdBase: string = '0x' + '0'.repeat(31);
51let scheduledIdSlider = 0;
52
53// Loop scheduledId around 10. Unless there are concurrent tasks with long periods/repetitions, tests' tasks' ids shouldn't ovelap.
54function makeScheduledId(): string {
55 return scheduledIdBase + ((scheduledIdSlider++) % 10);
56}
57
58// Check that there are no failing Dispatched events in the block
59function checkForFailedSchedulerEvents(api: ApiPromise, events: any[]) {
60 return new Promise((res, rej) => {
61 let schedulerEventPresent = false;
62
63 for (const {event} of events) {
64 if (api.events.scheduler.Dispatched.is(event)) {
65 schedulerEventPresent = true;
66 const result = event.data.result;
67 if (result.isErr) {
68 const decoded = api.registry.findMetaError(result.asErr.asModule);
69 const {method, section} = decoded;
70 rej(new Error(`${section}.${method}`));
71 }
72 }
73 }
74 res(schedulerEventPresent);
75 });
76}
77
78describe('Scheduling token and balance transfers', () => {
79 let alice: IKeyringPair;
80 let bob: IKeyringPair;
81
82 before(async() => {
83 await usingApi(async (_, privateKeyWrapper) => {
84 alice = privateKeyWrapper('//Alice');
85 bob = privateKeyWrapper('//Bob');
86 });
87 });
88
89
90 it('Can delay a transfer of an owned token', async () => {
91 await usingApi(async api => {
92 const collectionId = await createCollectionExpectSuccess();
93 const tokenId = await createItemExpectSuccess(alice, collectionId, 'NFT');
94
95 await scheduleTransferAndWaitExpectSuccess(api, collectionId, tokenId, alice, bob, 1, 4, makeScheduledId());
96 });
97 });
98
99 it('Can transfer funds periodically', async () => {
100 await usingApi(async api => {
101 const waitForBlocks = 1;
102 const period = 2;
103 const repetitions = 2;
104
105 await scheduleTransferFundsExpectSuccess(api, 1n * UNIQUE, alice, bob, waitForBlocks, makeScheduledId(), period, repetitions);
106 const bobsBalanceBefore = await getFreeBalance(bob);
107
108 await waitNewBlocks(waitForBlocks + 1);
109 const bobsBalanceAfterFirst = await getFreeBalance(bob);
110 expect(bobsBalanceAfterFirst > bobsBalanceBefore, '#1 Balance of the recipient did not increase').to.be.true;
111
112 await waitNewBlocks(period);
113 const bobsBalanceAfterSecond = await getFreeBalance(bob);
114 expect(bobsBalanceAfterSecond > bobsBalanceAfterFirst, '#2 Balance of the recipient did not increase').to.be.true;
115 });
116 });
117
118 it('Can cancel a scheduled operation which has not yet taken effect', async () => {
119 await usingApi(async api => {
120 const collectionId = await createCollectionExpectSuccess();
121 const tokenId = await createItemExpectSuccess(alice, collectionId, 'NFT');
122 const scheduledId = makeScheduledId();
123 const waitForBlocks = 4;
124
125 await scheduleTransferExpectSuccess(api, collectionId, tokenId, alice, bob, 1, waitForBlocks, scheduledId);
126 await expect(cancelScheduled(api, alice, scheduledId)).to.not.be.rejected;
127
128 await waitNewBlocks(waitForBlocks);
129
130 expect(await getTokenOwner(api, collectionId, tokenId)).to.be.deep.equal(normalizeAccountId(alice.address));
131 });
132 });
133
134 it('Can cancel a periodic operation (transfer of funds)', async () => {
135 await usingApi(async api => {
136 const waitForBlocks = 1;
137 const period = 3;
138 const repetitions = 2;
139
140 const scheduledId = makeScheduledId();
141
142 const bobsBalanceBefore = await getFreeBalance(bob);
143 await scheduleTransferFundsExpectSuccess(api, 1n * UNIQUE, alice, bob, waitForBlocks, scheduledId, period, repetitions);
144
145 await waitNewBlocks(waitForBlocks + 1);
146 const bobsBalanceAfterFirst = await getFreeBalance(bob);
147 expect(bobsBalanceAfterFirst > bobsBalanceBefore, '#1 Balance of the recipient did not increase').to.be.true;
148
149 await expect(cancelScheduled(api, alice, scheduledId)).to.not.be.rejected;
150
151 await waitNewBlocks(period);
152 const bobsBalanceAfterSecond = await getFreeBalance(bob);
153 expect(bobsBalanceAfterSecond == bobsBalanceAfterFirst, '#2 Balance of the recipient changed').to.be.true;
154 });
155 });
156
157 it('Can schedule a scheduled operation of canceling the scheduled operation', async () => {
158 await usingApi(async api => {
159 const scheduledId = makeScheduledId();
160
161 const waitForBlocks = 2;
162 const period = 3;
163 const repetitions = 2;
164
165 await expect(scheduleAfter(
166 api,
167 api.tx.scheduler.cancelNamed(scheduledId),
168 alice,
169 waitForBlocks,
170 scheduledId,
171 period,
172 repetitions,
173 )).to.not.be.rejected;
174
175
176 await waitNewBlocks(waitForBlocks);
177
178 // todo:scheduler debug line; doesn't work (and doesn't appear in events) when executed in the same block as the scheduled transaction
179 //await expect(executeTransaction(api, alice, api.tx.scheduler.cancelNamed(scheduledId))).to.not.be.rejected;
180
181 let schedulerEvents = 0;
182 for (let i = 0; i < period * repetitions; i++) {
183 const events = await api.query.system.events();
184 schedulerEvents += await expect(checkForFailedSchedulerEvents(api, events)).to.not.be.rejected;
185 await waitNewBlocks(1);
186 }
187 expect(schedulerEvents).to.be.equal(repetitions);
188 });
189 });
190
191 after(async () => {
192 // todo:scheduler to avoid the failed results of the previous test interfering with the next, delete after the problem is fixed
193 await waitNewBlocks(6);
194 });
195});
196
197describe('Negative Test: Scheduling', () => {
198 let alice: IKeyringPair;
199 let bob: IKeyringPair;
200
201 before(async() => {
202 await usingApi(async (_, privateKeyWrapper) => {
203 alice = privateKeyWrapper('//Alice');
204 bob = privateKeyWrapper('//Bob');
205 });
206 });
207
208 it('Can\'t overwrite a scheduled ID', async () => {
209 await usingApi(async api => {
210 const collectionId = await createCollectionExpectSuccess();
211 const tokenId = await createItemExpectSuccess(alice, collectionId, 'NFT');
212 const scheduledId = makeScheduledId();
213
214 await scheduleTransferExpectSuccess(api, collectionId, tokenId, alice, bob, 1, 4, scheduledId);
215 await expect(scheduleAfter(
216 api,
217 api.tx.balances.transfer(alice.address, 1n * UNIQUE),
218 bob,
219 2,
220 scheduledId,
221 )).to.be.rejectedWith(/scheduler\.FailedToSchedule/);
222 const bobsBalance = await getFreeBalance(bob);
223
224 await waitNewBlocks(3);
225
226 expect(await getTokenOwner(api, collectionId, tokenId)).to.be.deep.equal(normalizeAccountId(bob.address));
227 expect(bobsBalance).to.be.equal(await getFreeBalance(bob));
228 });
229 });
230
231 it('Can\'t cancel an operation which is not scheduled', async () => {
232 await usingApi(async api => {
233 const scheduledId = makeScheduledId();
234 await expect(cancelScheduled(api, alice, scheduledId)).to.be.rejectedWith(/scheduler\.NotFound/);
235 });
236 });
237
238 it('Can\'t cancel a non-owned scheduled operation', async () => {
239 await usingApi(async api => {
240 const collectionId = await createCollectionExpectSuccess();
241 const tokenId = await createItemExpectSuccess(alice, collectionId, 'NFT');
242 const scheduledId = makeScheduledId();
243 const waitForBlocks = 3;
244
245 await scheduleTransferExpectSuccess(api, collectionId, tokenId, alice, bob, 1, waitForBlocks, scheduledId);
246 await expect(cancelScheduled(api, bob, scheduledId)).to.be.rejected;
247
248 await waitNewBlocks(waitForBlocks);
249
250 expect(await getTokenOwner(api, collectionId, tokenId)).to.be.deep.equal(normalizeAccountId(alice.address));
251 });
252 });
253});
254
255// Implementation of the functionality tested here was postponed/shelved
48describe.skip('Scheduling token and balance transfers', () => {256describe.skip('Sponsoring scheduling', () => {
49 let alice: IKeyringPair;257 let alice: IKeyringPair;
50 let bob: IKeyringPair;258 let bob: IKeyringPair;
51 let scheduledIdBase: string;
52 let scheduledIdSlider: number;
53259
54 before(async() => {260 before(async() => {
55 await usingApi(async (api, privateKeyWrapper) => {261 await usingApi(async (_, privateKeyWrapper) => {
56 alice = privateKeyWrapper('//Alice');262 alice = privateKeyWrapper('//Alice');
57 bob = privateKeyWrapper('//Bob');263 bob = privateKeyWrapper('//Bob');
58 });264 });
59
60 scheduledIdBase = '0x' + '0'.repeat(31);
61 scheduledIdSlider = 0;
62 });265 });
63
64 // Loop scheduledId around 10. Unless there are concurrent tasks with long periods/repetitions, tests' tasks' ids shouldn't ovelap.
65 function makeScheduledId(): string {
66 return scheduledIdBase + ((scheduledIdSlider++) % 10);
67 }
68
69 it('Can schedule a transfer of an owned token with delay', async () => {
70 await usingApi(async () => {
71 const nftCollectionId = await createCollectionExpectSuccess();
72 const newNftTokenId = await createItemExpectSuccess(alice, nftCollectionId, 'NFT');
73 await setCollectionSponsorExpectSuccess(nftCollectionId, alice.address);
74 await confirmSponsorshipExpectSuccess(nftCollectionId);
75
76 await scheduleTransferAndWaitExpectSuccess(nftCollectionId, newNftTokenId, alice, bob, 1, 4, makeScheduledId());
77 });
78 });
79
80 it('Can transfer funds periodically', async () => {
81 await usingApi(async () => {
82 const waitForBlocks = 4;
83 const period = 2;
84 await scheduleTransferFundsPeriodicExpectSuccess(1n * UNIQUE, alice, bob, waitForBlocks, makeScheduledId(), period, 2);
85 const bobsBalanceBefore = await getFreeBalance(bob);
86
87 // discounting already waited-for operations
88 await waitNewBlocks(waitForBlocks - 2);
89 const bobsBalanceAfterFirst = await getFreeBalance(bob);
90 expect(bobsBalanceAfterFirst > bobsBalanceBefore).to.be.true;
91
92 await waitNewBlocks(period);
93 const bobsBalanceAfterSecond = await getFreeBalance(bob);
94 expect(bobsBalanceAfterSecond > bobsBalanceAfterFirst).to.be.true;
95 });
96 });
97266
98 it('Can sponsor scheduling a transaction', async () => {267 it('Can sponsor scheduling a transaction', async () => {
99 const collectionId = await createCollectionExpectSuccess();268 const collectionId = await createCollectionExpectSuccess();
100 await setCollectionSponsorExpectSuccess(collectionId, bob.address);269 await setCollectionSponsorExpectSuccess(collectionId, bob.address);
101 await confirmSponsorshipExpectSuccess(collectionId, '//Bob');270 await confirmSponsorshipExpectSuccess(collectionId, '//Bob');
102271
103 await usingApi(async () => {272 await usingApi(async api => {
104 const tokenId = await createItemExpectSuccess(alice, collectionId, 'NFT', alice.address);273 const tokenId = await createItemExpectSuccess(alice, collectionId, 'NFT', alice.address);
105274
106 const bobBalanceBefore = await getFreeBalance(bob);275 const bobBalanceBefore = await getFreeBalance(bob);
107 const waitForBlocks = 4;276 const waitForBlocks = 4;
108 // no need to wait to check, fees must be deducted on scheduling, immediately277 // no need to wait to check, fees must be deducted on scheduling, immediately
109 await scheduleTransferExpectSuccess(collectionId, tokenId, alice, bob, 0, waitForBlocks, makeScheduledId());278 await scheduleTransferExpectSuccess(api, collectionId, tokenId, alice, bob, 0, waitForBlocks, makeScheduledId());
110 const bobBalanceAfter = await getFreeBalance(bob);279 const bobBalanceAfter = await getFreeBalance(bob);
111 // expect(aliceBalanceAfter == aliceBalanceBefore).to.be.true;280 // expect(aliceBalanceAfter == aliceBalanceBefore).to.be.true;
112 expect(bobBalanceAfter < bobBalanceBefore).to.be.true;281 expect(bobBalanceAfter < bobBalanceBefore).to.be.true;
135304
136 // Schedule transfer of the NFT a few blocks ahead305 // Schedule transfer of the NFT a few blocks ahead
137 const waitForBlocks = 5;306 const waitForBlocks = 5;
138 await scheduleTransferExpectSuccess(collectionId, tokenId, zeroBalance, alice, 1, waitForBlocks, makeScheduledId());307 await scheduleTransferExpectSuccess(api, collectionId, tokenId, zeroBalance, alice, 1, waitForBlocks, makeScheduledId());
139308
140 // Get rid of the account's funds before the scheduled transaction takes place309 // Get rid of the account's funds before the scheduled transaction takes place
141 const balanceTx2 = api.tx.balances.transfer(alice.address, UNIQUE * 68n / 100n);310 const balanceTx2 = api.tx.balances.transfer(alice.address, UNIQUE * 68n / 100n);
167 const tokenId = await createItemExpectSuccess(alice, collectionId, 'NFT', alice.address);336 const tokenId = await createItemExpectSuccess(alice, collectionId, 'NFT', alice.address);
168337
169 const waitForBlocks = 5;338 const waitForBlocks = 5;
170 await scheduleTransferExpectSuccess(collectionId, tokenId, alice, zeroBalance, 1, waitForBlocks, makeScheduledId());339 await scheduleTransferExpectSuccess(api, collectionId, tokenId, alice, zeroBalance, 1, waitForBlocks, makeScheduledId());
171340
172 const emptyBalanceSponsorTx = api.tx.balances.setBalance(zeroBalance.address, 0, 0);341 const emptyBalanceSponsorTx = api.tx.balances.setBalance(zeroBalance.address, 0, 0);
173 const sudoTx = api.tx.sudo.sudo(emptyBalanceSponsorTx as any);342 const sudoTx = api.tx.sudo.sudo(emptyBalanceSponsorTx as any);
181 });350 });
182 });351 });
183352
184 it.skip('Exceeding sponsor rate limit without having enough funds prevents scheduling a periodic transaction', async () => {353 it('Exceeding sponsor rate limit without having enough funds prevents scheduling a periodic transaction', async () => {
185 const collectionId = await createCollectionExpectSuccess();354 const collectionId = await createCollectionExpectSuccess();
186 await setCollectionSponsorExpectSuccess(collectionId, bob.address);355 await setCollectionSponsorExpectSuccess(collectionId, bob.address);
187 await confirmSponsorshipExpectSuccess(collectionId, '//Bob');356 await confirmSponsorshipExpectSuccess(collectionId, '//Bob');
202 };371 };
203 await expect(badTransaction()).to.be.rejectedWith('Inability to pay some fees');*/372 await expect(badTransaction()).to.be.rejectedWith('Inability to pay some fees');*/
204373
205 await scheduleExpectFailure(creationTx, zeroBalance, 3, makeScheduledId(), 1, 3);374 await expect(scheduleAfter(api, creationTx, zeroBalance, 3, makeScheduledId(), 1, 3)).to.be.rejectedWith(/Inability to pay some fees/);
206375
207 expect(await getFreeBalance(bob)).to.be.equal(bobBalanceBefore);376 expect(await getFreeBalance(bob)).to.be.equal(bobBalanceBefore);
208 });377 });
addedtests/src/deprecated-helpers/helpers.tsdiffbeforeafterboth

no changes