@human-protocol/sdk 0.0.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,817 @@
1
+ import { getPublicURL } from './../src/storage';
2
+ import { EscrowStatus, Job } from '../src';
3
+ import { upload } from '../src/storage';
4
+ import { toFullDigit } from '../src/utils';
5
+ import {
6
+ DEFAULT_GAS_PAYER_ADDR,
7
+ DEFAULT_GAS_PAYER_PRIVKEY,
8
+ DEFAULT_HMTOKEN_ADDR,
9
+ DEFAULT_STAKING_ADDR,
10
+ NOT_TRUSTED_OPERATOR_PRIVKEY,
11
+ REPUTATION_ORACLE_PRIVKEY,
12
+ TRUSTED_OPERATOR1_ADDR,
13
+ TRUSTED_OPERATOR1_PRIVKEY,
14
+ TRUSTED_OPERATOR2_ADDR,
15
+ WORKER1_ADDR,
16
+ WORKER2_ADDR,
17
+ WORKER3_ADDR,
18
+ } from './utils/constants';
19
+ import { manifest } from './utils/manifest';
20
+
21
+ jest.mock('../src/storage', () => ({
22
+ ...jest.requireActual('../src/storage'),
23
+ upload: jest.fn().mockResolvedValue({
24
+ key: 'uploaded-key',
25
+ hash: 'uploaded-hash',
26
+ }),
27
+ download: jest.fn().mockResolvedValue({
28
+ results: 0,
29
+ }),
30
+ getPublicURL: jest.fn().mockResolvedValue('public-url'),
31
+ }));
32
+
33
+ const setupJob = async (job: Job) => {
34
+ await job.initialize();
35
+ await job.launch();
36
+ await job.setup();
37
+ };
38
+
39
+ describe('Test Job', () => {
40
+ describe('New job', () => {
41
+ let job: Job;
42
+
43
+ beforeEach(() => {
44
+ job = new Job({
45
+ gasPayer: DEFAULT_GAS_PAYER_PRIVKEY,
46
+ reputationOracle: REPUTATION_ORACLE_PRIVKEY,
47
+ manifest: manifest,
48
+ hmTokenAddr: DEFAULT_HMTOKEN_ADDR,
49
+ stakingAddr: DEFAULT_STAKING_ADDR,
50
+ logLevel: 'error',
51
+ });
52
+ });
53
+
54
+ afterEach(() => {
55
+ jest.clearAllMocks();
56
+ });
57
+
58
+ it('Should be able to initializes the job by deploying escrow factory', async () => {
59
+ const initialized = await job.initialize();
60
+ expect(initialized).toBe(true);
61
+
62
+ expect(await job.contractData?.factory?.address).not.toBeNull();
63
+ });
64
+
65
+ it('Should be able to launch the job after staking', async () => {
66
+ expect(await job.initialize()).toBe(true);
67
+ expect(await job.launch()).toBe(false);
68
+
69
+ await job.stake(1);
70
+
71
+ expect(await job.launch()).toBe(true);
72
+ expect(await job.status()).toBe(EscrowStatus.Launched);
73
+ });
74
+
75
+ it('Should be able to setup the job', async () => {
76
+ // Fail to setup the job before launch
77
+ expect(await job.setup()).toBe(false);
78
+
79
+ await job.initialize();
80
+ await job.launch();
81
+
82
+ expect(await job.setup()).toBe(true);
83
+ });
84
+
85
+ it('Should be able to add trusted handlers', async () => {
86
+ await job.initialize();
87
+ await job.launch();
88
+
89
+ expect(await job.isTrustedHandler(DEFAULT_GAS_PAYER_ADDR)).toBe(true);
90
+
91
+ expect(
92
+ await job.addTrustedHandlers([
93
+ TRUSTED_OPERATOR1_ADDR,
94
+ TRUSTED_OPERATOR2_ADDR,
95
+ ])
96
+ ).toBe(true);
97
+
98
+ expect(await job.isTrustedHandler(TRUSTED_OPERATOR1_ADDR)).toBe(true);
99
+ expect(await job.isTrustedHandler(TRUSTED_OPERATOR2_ADDR)).toBe(true);
100
+ });
101
+
102
+ it('Should be able to bulk payout workers', async () => {
103
+ await setupJob(job);
104
+
105
+ expect(
106
+ await job.bulkPayout(
107
+ [
108
+ {
109
+ address: WORKER1_ADDR,
110
+ amount: 20,
111
+ },
112
+ {
113
+ address: WORKER2_ADDR,
114
+ amount: 50,
115
+ },
116
+ ],
117
+ {}
118
+ )
119
+ ).toBe(true);
120
+
121
+ // The escrow contract is still in Partial state as there's still balance left.
122
+ expect((await job.balance())?.toString()).toBe(
123
+ toFullDigit(30).toString()
124
+ );
125
+ expect(await job.status()).toBe(EscrowStatus.Partial);
126
+
127
+ // Trying to pay more than the contract balance results in failure.
128
+ expect(
129
+ await job.bulkPayout(
130
+ [
131
+ {
132
+ address: WORKER3_ADDR,
133
+ amount: 50,
134
+ },
135
+ ],
136
+ {}
137
+ )
138
+ ).toBe(false);
139
+
140
+ // Paying the remaining amount empties the escrow and updates the status correctly.
141
+ expect(
142
+ await job.bulkPayout(
143
+ [
144
+ {
145
+ address: WORKER3_ADDR,
146
+ amount: 30,
147
+ },
148
+ ],
149
+ {}
150
+ )
151
+ ).toBe(true);
152
+ expect((await job.balance())?.toString()).toBe(toFullDigit(0).toString());
153
+ expect(await job.status()).toBe(EscrowStatus.Paid);
154
+ });
155
+
156
+ it('Should encrypt result, when bulk paying out workers', async () => {
157
+ await setupJob(job);
158
+
159
+ jest.clearAllMocks();
160
+ const finalResults = { results: 0 };
161
+ await job.bulkPayout(
162
+ [
163
+ {
164
+ address: WORKER1_ADDR,
165
+ amount: 100,
166
+ },
167
+ ],
168
+ finalResults,
169
+ true
170
+ );
171
+
172
+ expect(upload).toHaveBeenCalledWith(
173
+ job.storageAccessData,
174
+ finalResults,
175
+ job.providerData?.reputationOracle?.publicKey,
176
+ true,
177
+ false
178
+ );
179
+ expect(upload).toHaveBeenCalledTimes(1);
180
+ });
181
+
182
+ it('Should not encrypt result, when bulk paying out workers', async () => {
183
+ await setupJob(job);
184
+
185
+ jest.clearAllMocks();
186
+ const finalResults = { results: 0 };
187
+ await job.bulkPayout(
188
+ [
189
+ {
190
+ address: WORKER1_ADDR,
191
+ amount: 100,
192
+ },
193
+ ],
194
+ finalResults,
195
+ false
196
+ );
197
+
198
+ expect(upload).toHaveBeenCalledWith(
199
+ job.storageAccessData,
200
+ finalResults,
201
+ job.providerData?.reputationOracle?.publicKey,
202
+ false,
203
+ false
204
+ );
205
+ expect(upload).toHaveBeenCalledTimes(1);
206
+ });
207
+
208
+ it('Should store result in private storage, when bulk paying out workers', async () => {
209
+ await setupJob(job);
210
+
211
+ jest.clearAllMocks();
212
+ const finalResults = { results: 0 };
213
+ await job.bulkPayout(
214
+ [
215
+ {
216
+ address: WORKER1_ADDR,
217
+ amount: 100,
218
+ },
219
+ ],
220
+ finalResults,
221
+ false,
222
+ false
223
+ );
224
+
225
+ expect(upload).toHaveBeenCalledWith(
226
+ job.storageAccessData,
227
+ finalResults,
228
+ job.providerData?.reputationOracle?.publicKey,
229
+ false,
230
+ false
231
+ );
232
+ expect(upload).toHaveBeenCalledTimes(1);
233
+ });
234
+
235
+ it('Should store result in public storage, when bulk paying out workers', async () => {
236
+ await setupJob(job);
237
+
238
+ jest.clearAllMocks();
239
+ const finalResults = { results: 0 };
240
+ await job.bulkPayout(
241
+ [
242
+ {
243
+ address: WORKER1_ADDR,
244
+ amount: 50,
245
+ },
246
+ ],
247
+ finalResults,
248
+ false,
249
+ true
250
+ );
251
+
252
+ expect(upload).toHaveBeenCalledWith(
253
+ job.storageAccessData,
254
+ finalResults,
255
+ job.providerData?.reputationOracle?.publicKey,
256
+ false,
257
+ true
258
+ );
259
+ expect(upload).toHaveBeenCalledTimes(1);
260
+ expect(getPublicURL).toHaveBeenCalledTimes(1);
261
+ });
262
+
263
+ it('Should return final result', async () => {
264
+ await setupJob(job);
265
+
266
+ const finalResults = { results: 0 };
267
+ await job.bulkPayout(
268
+ [
269
+ {
270
+ address: WORKER1_ADDR,
271
+ amount: 100,
272
+ },
273
+ ],
274
+ finalResults,
275
+ true
276
+ );
277
+
278
+ expect(JSON.stringify(await job.finalResults())).toBe(
279
+ JSON.stringify(finalResults)
280
+ );
281
+ });
282
+
283
+ it('Should be able to abort the job', async () => {
284
+ await setupJob(job);
285
+
286
+ expect(await job.abort()).toBe(true);
287
+ });
288
+
289
+ it('Should be able to abort partially paid job', async () => {
290
+ await setupJob(job);
291
+
292
+ const finalResults = { results: 0 };
293
+ await job.bulkPayout(
294
+ [
295
+ {
296
+ address: WORKER1_ADDR,
297
+ amount: 50,
298
+ },
299
+ ],
300
+ finalResults,
301
+ true
302
+ );
303
+
304
+ expect(await job.abort()).toBe(true);
305
+ });
306
+
307
+ it('Should not be able to abort fully paid job', async () => {
308
+ await setupJob(job);
309
+
310
+ const finalResults = { results: 0 };
311
+ await job.bulkPayout(
312
+ [
313
+ {
314
+ address: WORKER1_ADDR,
315
+ amount: 100,
316
+ },
317
+ ],
318
+ finalResults,
319
+ true
320
+ );
321
+
322
+ expect(await job.abort()).toBe(false);
323
+ });
324
+
325
+ it('Should be able to cancel the job', async () => {
326
+ await setupJob(job);
327
+
328
+ expect(await job.cancel()).toBe(true);
329
+ expect((await job.balance())?.toString()).toBe(toFullDigit(0).toString());
330
+ });
331
+
332
+ it('Should be able to cancel partially paid job', async () => {
333
+ await setupJob(job);
334
+
335
+ const finalResults = { results: 0 };
336
+ await job.bulkPayout(
337
+ [
338
+ {
339
+ address: WORKER1_ADDR,
340
+ amount: 50,
341
+ },
342
+ ],
343
+ finalResults,
344
+ true
345
+ );
346
+
347
+ expect(await job.cancel()).toBe(true);
348
+ expect((await job.balance())?.toString()).toBe(toFullDigit(0).toString());
349
+ });
350
+
351
+ it('Should not be able to cancel paid job', async () => {
352
+ await setupJob(job);
353
+
354
+ const finalResults = { results: 0 };
355
+ await job.bulkPayout(
356
+ [
357
+ {
358
+ address: WORKER1_ADDR,
359
+ amount: 100,
360
+ },
361
+ ],
362
+ finalResults,
363
+ true
364
+ );
365
+
366
+ expect(await job.cancel()).toBe(false);
367
+ });
368
+
369
+ it('Should be able to allocate token to the job', async () => {
370
+ await job.initialize();
371
+
372
+ expect(await job.launch()).toBe(true);
373
+ expect(await job.status()).toBe(EscrowStatus.Launched);
374
+
375
+ expect(await job.allocate(1)).toBe(true);
376
+ });
377
+
378
+ it('Should be able to launch another job after allocating portion of the stake', async () => {
379
+ await job.initialize();
380
+ await job.stake(2);
381
+
382
+ expect(await job.launch()).toBe(true);
383
+ expect(await job.status()).toBe(EscrowStatus.Launched);
384
+
385
+ expect(await job.allocate(1)).toBe(true);
386
+
387
+ const newJob = new Job({
388
+ gasPayer: DEFAULT_GAS_PAYER_PRIVKEY,
389
+ reputationOracle: REPUTATION_ORACLE_PRIVKEY,
390
+ manifest: manifest,
391
+ hmTokenAddr: DEFAULT_HMTOKEN_ADDR,
392
+ stakingAddr: DEFAULT_STAKING_ADDR,
393
+ logLevel: 'error',
394
+ });
395
+
396
+ await newJob.initialize();
397
+ expect(await newJob.launch()).toBe(true);
398
+ });
399
+
400
+ it('Should not be able to launch another job after allocating all of the stake', async () => {
401
+ await job.initialize();
402
+
403
+ expect(await job.launch()).toBe(true);
404
+ expect(await job.status()).toBe(EscrowStatus.Launched);
405
+
406
+ expect(await job.allocate(1)).toBe(true);
407
+
408
+ const newJob = new Job({
409
+ gasPayer: DEFAULT_GAS_PAYER_PRIVKEY,
410
+ reputationOracle: REPUTATION_ORACLE_PRIVKEY,
411
+ manifest: manifest,
412
+ hmTokenAddr: DEFAULT_HMTOKEN_ADDR,
413
+ stakingAddr: DEFAULT_STAKING_ADDR,
414
+ logLevel: 'error',
415
+ });
416
+
417
+ await newJob.initialize();
418
+ expect(await newJob.launch()).toBe(false);
419
+ });
420
+
421
+ it('Should be able to launch another job after staking more tokens', async () => {
422
+ await job.initialize();
423
+ await job.stake(1);
424
+
425
+ expect(await job.launch()).toBe(true);
426
+ expect(await job.status()).toBe(EscrowStatus.Launched);
427
+
428
+ expect(await job.allocate(1)).toBe(true);
429
+
430
+ const newJob = new Job({
431
+ gasPayer: DEFAULT_GAS_PAYER_PRIVKEY,
432
+ reputationOracle: REPUTATION_ORACLE_PRIVKEY,
433
+ manifest: manifest,
434
+ hmTokenAddr: DEFAULT_HMTOKEN_ADDR,
435
+ stakingAddr: DEFAULT_STAKING_ADDR,
436
+ logLevel: 'error',
437
+ });
438
+
439
+ await newJob.initialize();
440
+ await newJob.stake(1);
441
+ expect(await newJob.launch()).toBe(true);
442
+ });
443
+ });
444
+
445
+ describe('Access existing job from trusted handler', () => {
446
+ let job: Job;
447
+
448
+ beforeEach(async () => {
449
+ const originalJob = new Job({
450
+ gasPayer: DEFAULT_GAS_PAYER_PRIVKEY,
451
+ reputationOracle: REPUTATION_ORACLE_PRIVKEY,
452
+ manifest: manifest,
453
+ hmTokenAddr: DEFAULT_HMTOKEN_ADDR,
454
+ stakingAddr: DEFAULT_STAKING_ADDR,
455
+ trustedHandlers: [TRUSTED_OPERATOR1_PRIVKEY],
456
+ logLevel: 'error',
457
+ });
458
+
459
+ await originalJob.initialize();
460
+ await originalJob.launch();
461
+ await originalJob.stake(1);
462
+ await originalJob.setup();
463
+
464
+ job = new Job({
465
+ gasPayer: NOT_TRUSTED_OPERATOR_PRIVKEY,
466
+ hmTokenAddr: DEFAULT_HMTOKEN_ADDR,
467
+ reputationOracle: REPUTATION_ORACLE_PRIVKEY,
468
+ escrowAddr: originalJob.contractData?.escrowAddr,
469
+ factoryAddr: originalJob.contractData?.factoryAddr,
470
+ trustedHandlers: [TRUSTED_OPERATOR1_PRIVKEY],
471
+ logLevel: 'error',
472
+ });
473
+ });
474
+
475
+ afterEach(() => {
476
+ jest.clearAllMocks();
477
+ });
478
+
479
+ it('Should be able to initializes the job by accessing existing escrow', async () => {
480
+ expect(await job.initialize()).toBe(true);
481
+
482
+ expect(await job.manifestData?.manifestlink?.url).toBe('uploaded-key');
483
+ expect(await job.manifestData?.manifestlink?.hash).toBe('uploaded-hash');
484
+ });
485
+
486
+ it('Should not be able to launch the job again', async () => {
487
+ await job.initialize();
488
+
489
+ expect(await job.launch()).toBe(false);
490
+ expect(await job.status()).toBe(EscrowStatus.Pending);
491
+ });
492
+
493
+ it('Should not be able to setup the job again', async () => {
494
+ await job.initialize();
495
+
496
+ expect(await job.setup()).toBe(false);
497
+
498
+ expect((await job.balance())?.toString()).toBe(
499
+ toFullDigit(100).toString()
500
+ );
501
+ expect(await job.manifestData?.manifestlink?.url).toBe('uploaded-key');
502
+ expect(await job.manifestData?.manifestlink?.hash).toBe('uploaded-hash');
503
+ });
504
+
505
+ it('Should be able to add trusted handlers', async () => {
506
+ await job.initialize();
507
+
508
+ expect(await job.isTrustedHandler(DEFAULT_GAS_PAYER_ADDR)).toBe(true);
509
+
510
+ expect(
511
+ await job.addTrustedHandlers([
512
+ TRUSTED_OPERATOR1_ADDR,
513
+ TRUSTED_OPERATOR2_ADDR,
514
+ ])
515
+ ).toBe(true);
516
+
517
+ expect(await job.isTrustedHandler(TRUSTED_OPERATOR1_ADDR)).toBe(true);
518
+ expect(await job.isTrustedHandler(TRUSTED_OPERATOR2_ADDR)).toBe(true);
519
+ });
520
+
521
+ it('Should be able to bulk payout workers', async () => {
522
+ await job.initialize();
523
+
524
+ expect(
525
+ await job.bulkPayout(
526
+ [
527
+ {
528
+ address: WORKER1_ADDR,
529
+ amount: 20,
530
+ },
531
+ {
532
+ address: WORKER2_ADDR,
533
+ amount: 50,
534
+ },
535
+ ],
536
+ {}
537
+ )
538
+ ).toBe(true);
539
+
540
+ // The escrow contract is still in Partial state as there's still balance left.
541
+ expect((await job.balance())?.toString()).toBe(
542
+ toFullDigit(30).toString()
543
+ );
544
+ expect(await job.status()).toBe(EscrowStatus.Partial);
545
+
546
+ // Trying to pay more than the contract balance results in failure.
547
+ expect(
548
+ await job.bulkPayout(
549
+ [
550
+ {
551
+ address: WORKER3_ADDR,
552
+ amount: 50,
553
+ },
554
+ ],
555
+ {}
556
+ )
557
+ ).toBe(false);
558
+
559
+ // Paying the remaining amount empties the escrow and updates the status correctly.
560
+ expect(
561
+ await job.bulkPayout(
562
+ [
563
+ {
564
+ address: WORKER3_ADDR,
565
+ amount: 30,
566
+ },
567
+ ],
568
+ {}
569
+ )
570
+ ).toBe(true);
571
+ expect((await job.balance())?.toString()).toBe(toFullDigit(0).toString());
572
+ expect(await job.status()).toBe(EscrowStatus.Paid);
573
+ });
574
+
575
+ it('Should encrypt result, when bulk paying out workers', async () => {
576
+ await job.initialize();
577
+
578
+ jest.clearAllMocks();
579
+ const finalResults = { results: 0 };
580
+ await job.bulkPayout(
581
+ [
582
+ {
583
+ address: WORKER1_ADDR,
584
+ amount: 100,
585
+ },
586
+ ],
587
+ finalResults,
588
+ true
589
+ );
590
+
591
+ expect(upload).toHaveBeenCalledWith(
592
+ job.storageAccessData,
593
+ finalResults,
594
+ job.providerData?.reputationOracle?.publicKey,
595
+ true,
596
+ false
597
+ );
598
+ expect(upload).toHaveBeenCalledTimes(1);
599
+ });
600
+
601
+ it('Should not encrypt result, when bulk paying out workers', async () => {
602
+ await job.initialize();
603
+
604
+ jest.clearAllMocks();
605
+ const finalResults = { results: 0 };
606
+ await job.bulkPayout(
607
+ [
608
+ {
609
+ address: WORKER1_ADDR,
610
+ amount: 100,
611
+ },
612
+ ],
613
+ finalResults,
614
+ false
615
+ );
616
+
617
+ expect(upload).toHaveBeenCalledWith(
618
+ job.storageAccessData,
619
+ finalResults,
620
+ job.providerData?.reputationOracle?.publicKey,
621
+ false,
622
+ false
623
+ );
624
+ expect(upload).toHaveBeenCalledTimes(1);
625
+ });
626
+
627
+ it('Should store result in private storage, when bulk paying out workers', async () => {
628
+ await job.initialize();
629
+
630
+ jest.clearAllMocks();
631
+ const finalResults = { results: 0 };
632
+ await job.bulkPayout(
633
+ [
634
+ {
635
+ address: WORKER1_ADDR,
636
+ amount: 100,
637
+ },
638
+ ],
639
+ finalResults,
640
+ false,
641
+ false
642
+ );
643
+
644
+ expect(upload).toHaveBeenCalledWith(
645
+ job.storageAccessData,
646
+ finalResults,
647
+ job.providerData?.reputationOracle?.publicKey,
648
+ false,
649
+ false
650
+ );
651
+ expect(upload).toHaveBeenCalledTimes(1);
652
+ });
653
+
654
+ it('Should store result in public storage, when bulk paying out workers', async () => {
655
+ await job.initialize();
656
+
657
+ jest.clearAllMocks();
658
+ const finalResults = { results: 0 };
659
+ await job.bulkPayout(
660
+ [
661
+ {
662
+ address: WORKER1_ADDR,
663
+ amount: 50,
664
+ },
665
+ ],
666
+ finalResults,
667
+ false,
668
+ true
669
+ );
670
+
671
+ expect(upload).toHaveBeenCalledWith(
672
+ job.storageAccessData,
673
+ finalResults,
674
+ job.providerData?.reputationOracle?.publicKey,
675
+ false,
676
+ true
677
+ );
678
+ expect(upload).toHaveBeenCalledTimes(1);
679
+ expect(getPublicURL).toHaveBeenCalledTimes(1);
680
+ });
681
+
682
+ it('Should return final result', async () => {
683
+ await job.initialize();
684
+
685
+ const finalResults = { results: 0 };
686
+ await job.bulkPayout(
687
+ [
688
+ {
689
+ address: WORKER1_ADDR,
690
+ amount: 100,
691
+ },
692
+ ],
693
+ finalResults,
694
+ true
695
+ );
696
+
697
+ expect(JSON.stringify(await job.finalResults())).toBe(
698
+ JSON.stringify(finalResults)
699
+ );
700
+ });
701
+
702
+ it('Should be able to abort the job', async () => {
703
+ await job.initialize();
704
+
705
+ expect(await job.abort()).toBe(true);
706
+ });
707
+
708
+ it('Should be able to abort partially paid job', async () => {
709
+ await job.initialize();
710
+
711
+ const finalResults = { results: 0 };
712
+ await job.bulkPayout(
713
+ [
714
+ {
715
+ address: WORKER1_ADDR,
716
+ amount: 50,
717
+ },
718
+ ],
719
+ finalResults,
720
+ true
721
+ );
722
+
723
+ expect(await job.abort()).toBe(true);
724
+ });
725
+
726
+ it('Should not be able to abort fully paid job', async () => {
727
+ await job.initialize();
728
+
729
+ const finalResults = { results: 0 };
730
+ await job.bulkPayout(
731
+ [
732
+ {
733
+ address: WORKER1_ADDR,
734
+ amount: 100,
735
+ },
736
+ ],
737
+ finalResults,
738
+ true
739
+ );
740
+
741
+ expect(await job.abort()).toBe(false);
742
+ });
743
+
744
+ it('Should be able to cancel the job', async () => {
745
+ await job.initialize();
746
+
747
+ expect(await job.cancel()).toBe(true);
748
+ expect((await job.balance())?.toString()).toBe(toFullDigit(0).toString());
749
+ });
750
+
751
+ it('Should be able to cancel partially paid job', async () => {
752
+ await job.initialize();
753
+
754
+ const finalResults = { results: 0 };
755
+ await job.bulkPayout(
756
+ [
757
+ {
758
+ address: WORKER1_ADDR,
759
+ amount: 50,
760
+ },
761
+ ],
762
+ finalResults,
763
+ true
764
+ );
765
+
766
+ expect(await job.cancel()).toBe(true);
767
+ expect((await job.balance())?.toString()).toBe(toFullDigit(0).toString());
768
+ });
769
+
770
+ it('Should not be able to cancel paid job', async () => {
771
+ await job.initialize();
772
+
773
+ const finalResults = { results: 0 };
774
+ await job.bulkPayout(
775
+ [
776
+ {
777
+ address: WORKER1_ADDR,
778
+ amount: 100,
779
+ },
780
+ ],
781
+ finalResults,
782
+ true
783
+ );
784
+
785
+ expect(await job.cancel()).toBe(false);
786
+ });
787
+
788
+ it('Should not be able to allocate to job without staking', async () => {
789
+ await job.initialize();
790
+ expect(await job.allocate(1, TRUSTED_OPERATOR1_ADDR)).toBe(false);
791
+ });
792
+
793
+ it('Should be able to allocate to job after staking', async () => {
794
+ await job.initialize();
795
+ await job.stake(1, TRUSTED_OPERATOR1_ADDR);
796
+
797
+ expect(await job.allocate(1, TRUSTED_OPERATOR1_ADDR)).toBe(true);
798
+ });
799
+
800
+ it('Should be able to launch another job after staking', async () => {
801
+ await job.initialize();
802
+ await job.stake(1, TRUSTED_OPERATOR1_ADDR);
803
+
804
+ const newJob = new Job({
805
+ gasPayer: TRUSTED_OPERATOR1_PRIVKEY,
806
+ reputationOracle: REPUTATION_ORACLE_PRIVKEY,
807
+ manifest: manifest,
808
+ hmTokenAddr: DEFAULT_HMTOKEN_ADDR,
809
+ stakingAddr: DEFAULT_STAKING_ADDR,
810
+ logLevel: 'error',
811
+ });
812
+
813
+ await newJob.initialize();
814
+ expect(await newJob.launch()).toBe(true);
815
+ });
816
+ });
817
+ });