@rosen-bridge/tx-pot 0.1.0

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.
Files changed (49) hide show
  1. package/.eslintignore +1 -0
  2. package/README.md +36 -0
  3. package/dist/db/entities/TransactionEntity.d.ts +15 -0
  4. package/dist/db/entities/TransactionEntity.d.ts.map +1 -0
  5. package/dist/db/entities/TransactionEntity.js +81 -0
  6. package/dist/db/migrations/index.d.ts +7 -0
  7. package/dist/db/migrations/index.d.ts.map +1 -0
  8. package/dist/db/migrations/index.js +7 -0
  9. package/dist/db/migrations/postgres/1706350644686-migration.d.ts +7 -0
  10. package/dist/db/migrations/postgres/1706350644686-migration.d.ts.map +1 -0
  11. package/dist/db/migrations/postgres/1706350644686-migration.js +28 -0
  12. package/dist/db/migrations/sqlite/1706007154531-migration.d.ts +7 -0
  13. package/dist/db/migrations/sqlite/1706007154531-migration.d.ts.map +1 -0
  14. package/dist/db/migrations/sqlite/1706007154531-migration.js +28 -0
  15. package/dist/index.d.ts +6 -0
  16. package/dist/index.d.ts.map +1 -0
  17. package/dist/index.js +6 -0
  18. package/dist/network/AbstractPotChainManager.d.ts +36 -0
  19. package/dist/network/AbstractPotChainManager.d.ts.map +1 -0
  20. package/dist/network/AbstractPotChainManager.js +3 -0
  21. package/dist/transaction/TxPot.d.ts +164 -0
  22. package/dist/transaction/TxPot.d.ts.map +1 -0
  23. package/dist/transaction/TxPot.js +386 -0
  24. package/dist/transaction/types.d.ts +35 -0
  25. package/dist/transaction/types.d.ts.map +1 -0
  26. package/dist/transaction/types.js +21 -0
  27. package/dist/transaction/utils.d.ts +8 -0
  28. package/dist/transaction/utils.d.ts.map +1 -0
  29. package/dist/transaction/utils.js +56 -0
  30. package/lib/db/entities/TransactionEntity.ts +44 -0
  31. package/lib/db/migrations/index.ts +7 -0
  32. package/lib/db/migrations/postgres/1706350644686-migration.ts +31 -0
  33. package/lib/db/migrations/sqlite/1706007154531-migration.ts +31 -0
  34. package/lib/index.ts +5 -0
  35. package/lib/network/AbstractPotChainManager.ts +44 -0
  36. package/lib/transaction/TxPot.ts +519 -0
  37. package/lib/transaction/types.ts +46 -0
  38. package/lib/transaction/utils.ts +59 -0
  39. package/package.json +39 -0
  40. package/tests/.gitkeep +0 -0
  41. package/tests/db/dataSource.mock.ts +18 -0
  42. package/tests/network/TestPotChainManager.ts +23 -0
  43. package/tests/transaction/TestTxPot.ts +32 -0
  44. package/tests/transaction/TxPot.spec.ts +1517 -0
  45. package/tests/transaction/testData.ts +84 -0
  46. package/tsconfig.build.json +8 -0
  47. package/tsconfig.build.tsbuildinfo +1 -0
  48. package/tsconfig.json +9 -0
  49. package/vitest.config.ts +13 -0
@@ -0,0 +1,1517 @@
1
+ import { Repository } from 'typeorm';
2
+ import { mockDataSource } from '../db/dataSource.mock';
3
+ import { TransactionEntity, TransactionStatus } from '../../lib';
4
+ import { TestTxPot } from './TestTxPot';
5
+ import * as testData from './testData';
6
+ import { TestPotChainManager } from '../network/TestPotChainManager';
7
+
8
+ describe('TxPot', () => {
9
+ let txRepository: Repository<TransactionEntity>;
10
+ let txPot: TestTxPot;
11
+
12
+ beforeAll(() => {
13
+ vi.useFakeTimers();
14
+ vi.setSystemTime(new Date(testData.currentTimeStamp));
15
+ });
16
+
17
+ beforeEach(async () => {
18
+ // init TxPot
19
+ const dataSource = await mockDataSource();
20
+ txPot = TestTxPot.setup(dataSource);
21
+ txRepository = dataSource.getRepository(TransactionEntity);
22
+ });
23
+
24
+ afterAll(() => {
25
+ vi.useRealTimers();
26
+ });
27
+
28
+ describe('setTransactionAsInvalid', () => {
29
+ /**
30
+ * @target TxPot.setTransactionAsInvalid should update status when enough blocks is passed
31
+ * @dependencies
32
+ * - Date
33
+ * - database
34
+ * @scenario
35
+ * - insert tx
36
+ * - mock PotChainManager and register to TxPot
37
+ * - mock `getHeight`
38
+ * - mock `getTxRequiredConfirmation` to return -1
39
+ * - run test
40
+ * - check db records
41
+ * @expected
42
+ * - columns of the tx should be updated
43
+ * - status should be updated to invalid
44
+ * - lastStatusUpdate should be updated to currentTimeStamp in seconds
45
+ */
46
+ it('should update status when enough blocks is passed', async () => {
47
+ // insert tx
48
+ await txRepository.insert(testData.tx1);
49
+
50
+ // mock PotChainManager and register to TxPot
51
+ const mockedManager = new TestPotChainManager();
52
+ txPot.registerChain(testData.tx1.chain, mockedManager);
53
+ // mock `getHeight`
54
+ const requiredConfirmation = 5;
55
+ const currentHeight = testData.tx1.lastCheck + requiredConfirmation;
56
+ vi.spyOn(mockedManager, 'getHeight').mockResolvedValue(currentHeight);
57
+ // mock `getTxRequiredConfirmation`
58
+ vi.spyOn(mockedManager, 'getTxRequiredConfirmation').mockReturnValue(
59
+ requiredConfirmation
60
+ );
61
+
62
+ // run test
63
+ await txPot.callSetTransactionAsInvalid(testData.tx1);
64
+
65
+ // check db records
66
+ const txs = (await txRepository.find()).map((tx) => [
67
+ tx.txId,
68
+ tx.status,
69
+ tx.lastStatusUpdate,
70
+ ]);
71
+ expect(txs).toEqual([
72
+ [
73
+ testData.tx1.txId,
74
+ TransactionStatus.INVALID,
75
+ String(testData.currentTimeStampAsSeconds),
76
+ ],
77
+ ]);
78
+ });
79
+
80
+ /**
81
+ * @target TxPot.setTransactionAsInvalid should NOT update when enough blocks is NOT passed
82
+ * @dependencies
83
+ * - Date
84
+ * - database
85
+ * @scenario
86
+ * - insert tx
87
+ * - mock PotChainManager and register to TxPot
88
+ * - mock `getHeight`
89
+ * - mock `getTxRequiredConfirmation` to return -1
90
+ * - run test
91
+ * - check db records
92
+ * @expected
93
+ * - columns of the tx should remain unchanged
94
+ */
95
+ it('should NOT update when enough blocks is NOT passed', async () => {
96
+ // insert tx
97
+ await txRepository.insert(testData.tx1);
98
+
99
+ // mock PotChainManager and register to TxPot
100
+ const mockedManager = new TestPotChainManager();
101
+ txPot.registerChain(testData.tx1.chain, mockedManager);
102
+ // mock `getHeight`
103
+ const requiredConfirmation = 5;
104
+ const currentHeight = testData.tx1.lastCheck + requiredConfirmation - 1;
105
+ vi.spyOn(mockedManager, 'getHeight').mockResolvedValue(currentHeight);
106
+ // mock `getTxRequiredConfirmation`
107
+ vi.spyOn(mockedManager, 'getTxRequiredConfirmation').mockReturnValue(
108
+ requiredConfirmation
109
+ );
110
+
111
+ // run test
112
+ await txPot.callSetTransactionAsInvalid(testData.tx1);
113
+
114
+ // check db records
115
+ const txs = (await txRepository.find()).map((tx) => [
116
+ tx.txId,
117
+ tx.status,
118
+ tx.lastStatusUpdate,
119
+ ]);
120
+ expect(txs).toEqual([
121
+ [testData.tx1.txId, testData.tx1.status, testData.tx1.lastStatusUpdate],
122
+ ]);
123
+ });
124
+ });
125
+
126
+ describe('validateTx', () => {
127
+ /**
128
+ * @target TxPot.validateTx should return true when no validator function is set
129
+ * @dependencies
130
+ * @scenario
131
+ * - run test
132
+ * - check returned value
133
+ * @expected
134
+ * - should return true
135
+ */
136
+ it('should return true when no validator function is set', async () => {
137
+ const res = await txPot.callValidateTx(testData.tx1);
138
+ expect(res).toEqual(true);
139
+ });
140
+
141
+ /**
142
+ * @target TxPot.validateTx should return true when tx is valid
143
+ * @dependencies
144
+ * @scenario
145
+ * - register a validator function to return true
146
+ * - run test
147
+ * - check returned value
148
+ * @expected
149
+ * - should return true
150
+ */
151
+ it('should return true when tx is valid', async () => {
152
+ const mockedValidator = async (tx: TransactionEntity) => true;
153
+ txPot.registerValidator(
154
+ testData.tx1.chain,
155
+ testData.tx1.txType,
156
+ mockedValidator
157
+ );
158
+
159
+ const res = await txPot.callValidateTx(testData.tx1);
160
+ expect(res).toEqual(true);
161
+ });
162
+
163
+ /**
164
+ * @target TxPot.validateTx should return false and set tx as invalid
165
+ * @dependencies
166
+ * - database
167
+ * @scenario
168
+ * - insert tx into db
169
+ * - register a validator function to return false
170
+ * - mock TxPot.setTransactionAsInvalid
171
+ * - run test
172
+ * - check returned value
173
+ * - check if function got called
174
+ * @expected
175
+ * - should return false
176
+ * - `setTransactionAsInvalid` should got called
177
+ */
178
+ it('should return false and set tx as invalid', async () => {
179
+ await txRepository.insert(testData.tx1);
180
+
181
+ const mockedValidator = async (tx: TransactionEntity) => false;
182
+ txPot.registerValidator(
183
+ testData.tx1.chain,
184
+ testData.tx1.txType,
185
+ mockedValidator
186
+ );
187
+
188
+ const mockedSetTransactionAsInvalid = vi.fn();
189
+ vi.spyOn(txPot as any, 'setTransactionAsInvalid').mockImplementation(
190
+ mockedSetTransactionAsInvalid
191
+ );
192
+
193
+ const res = await txPot.callValidateTx(testData.tx1);
194
+ expect(res).toEqual(false);
195
+ expect(mockedSetTransactionAsInvalid).toHaveBeenCalled();
196
+ });
197
+ });
198
+
199
+ describe('setTxStatus', () => {
200
+ /**
201
+ * @target TxPot.setTxStatus should update status and lastStatusUpdate successfully
202
+ * @dependencies
203
+ * - Date
204
+ * - database
205
+ * @scenario
206
+ * - insert 2 txs
207
+ * - register a callback function
208
+ * - run test
209
+ * - check db records
210
+ * @expected
211
+ * - columns of target tx should be updated as expected
212
+ * - status should be updated to new status
213
+ * - lastStatusUpdate should be updated to currentTimeStamp in seconds
214
+ * - columns of other txs should remain unchanged
215
+ */
216
+ it('should update status and lastStatusUpdate successfully', async () => {
217
+ await txRepository.insert(testData.tx1);
218
+ await txRepository.insert(testData.tx5);
219
+
220
+ const newStatus = TransactionStatus.IN_SIGN;
221
+ await txPot.callSetTxStatus(testData.tx1, newStatus);
222
+
223
+ const txs = (await txRepository.find()).map((tx) => [
224
+ tx.txId,
225
+ tx.status,
226
+ tx.lastStatusUpdate,
227
+ ]);
228
+ expect(txs).toEqual([
229
+ [
230
+ testData.tx1.txId,
231
+ newStatus,
232
+ String(testData.currentTimeStampAsSeconds),
233
+ ],
234
+ [testData.tx5.txId, testData.tx5.status, testData.tx5.lastStatusUpdate],
235
+ ]);
236
+ });
237
+
238
+ /**
239
+ * @target TxPot.setTxStatus should also call tx status callback if is provided
240
+ * @dependencies
241
+ * - Date
242
+ * - database
243
+ * @scenario
244
+ * - insert 2 txs
245
+ * - register a callback function for new status
246
+ * - run test
247
+ * - check db records
248
+ * - check if function got called
249
+ * @expected
250
+ * - columns of target tx should be updated as expected
251
+ * - status should be updated to new status
252
+ * - lastStatusUpdate should be updated to currentTimeStamp in seconds
253
+ * - columns of other txs should remain unchanged
254
+ * - mocked callback should got called
255
+ */
256
+ it('should also call tx status callback if is provided', async () => {
257
+ // insert 2 txs
258
+ await txRepository.insert(testData.tx1);
259
+ await txRepository.insert(testData.tx5);
260
+
261
+ // register a callback function
262
+ const newStatus = TransactionStatus.IN_SIGN;
263
+ const mockedCallback = vi.fn();
264
+ mockedCallback.mockResolvedValue(undefined);
265
+ txPot.registerCallback(testData.tx1.txType, newStatus, mockedCallback);
266
+
267
+ // run test
268
+ await txPot.callSetTxStatus(testData.tx1, newStatus);
269
+
270
+ // check db records
271
+ const txs = (await txRepository.find()).map((tx) => [
272
+ tx.txId,
273
+ tx.status,
274
+ tx.lastStatusUpdate,
275
+ ]);
276
+ expect(txs).toEqual([
277
+ [
278
+ testData.tx1.txId,
279
+ newStatus,
280
+ String(testData.currentTimeStampAsSeconds),
281
+ ],
282
+ [testData.tx5.txId, testData.tx5.status, testData.tx5.lastStatusUpdate],
283
+ ]);
284
+
285
+ // check if function got called
286
+ expect(mockedCallback).toHaveBeenCalled();
287
+ });
288
+ });
289
+
290
+ describe('processSignedTx', () => {
291
+ /**
292
+ * @target TxPot.processSignedTx should submit the tx and update the status
293
+ * @dependencies
294
+ * - database
295
+ * @scenario
296
+ * - insert tx with signed status
297
+ * - mock PotChainManager and register to TxPot
298
+ * - mock `submitTransaction`
299
+ * - run test (call `update`)
300
+ * - check if function got called
301
+ * - check db records
302
+ * @expected
303
+ * - `submitTransaction` should got called
304
+ * - columns of the tx should be updated
305
+ * - status should be updated to sent
306
+ * - lastStatusUpdate should be updated to currentTimeStamp in seconds
307
+ */
308
+ it('should submit the tx and update the status', async () => {
309
+ // insert tx with signed status
310
+ await txRepository.insert(testData.tx4);
311
+
312
+ // mock PotChainManager and register to TxPot
313
+ const mockedManager = new TestPotChainManager();
314
+ txPot.registerChain(testData.tx4.chain, mockedManager);
315
+ // mock `submitTransaction`
316
+ const mockedSubmitTransaction = vi.fn();
317
+ mockedSubmitTransaction.mockResolvedValue(undefined);
318
+ vi.spyOn(mockedManager, 'submitTransaction').mockImplementation(
319
+ mockedSubmitTransaction
320
+ );
321
+
322
+ // run test
323
+ await txPot.update();
324
+
325
+ // check if function got called
326
+ expect(mockedSubmitTransaction).toHaveBeenCalled();
327
+
328
+ // check db records
329
+ const txs = (await txRepository.find()).map((tx) => [
330
+ tx.txId,
331
+ tx.status,
332
+ tx.lastStatusUpdate,
333
+ ]);
334
+ expect(txs).toEqual([
335
+ [
336
+ testData.tx4.txId,
337
+ TransactionStatus.SENT,
338
+ String(testData.currentTimeStampAsSeconds),
339
+ ],
340
+ ]);
341
+ });
342
+
343
+ /**
344
+ * @target TxPot.processSignedTx should update the status regardless of submit response
345
+ * @dependencies
346
+ * - database
347
+ * @scenario
348
+ * - insert tx with signed status
349
+ * - mock PotChainManager and register to TxPot
350
+ * - mock `submitTransaction` to throw error
351
+ * - run test (call `update`)
352
+ * - check if function got called
353
+ * - check db records
354
+ * @expected
355
+ * - `submitTransaction` should got called
356
+ * - columns of the tx should be updated
357
+ * - status should be updated to sent
358
+ * - lastStatusUpdate should be updated to currentTimeStamp in seconds
359
+ */
360
+ it('should update the status regardless of submit response', async () => {
361
+ // insert tx with signed status
362
+ await txRepository.insert(testData.tx4);
363
+
364
+ // mock PotChainManager and register to TxPot
365
+ const mockedManager = new TestPotChainManager();
366
+ txPot.registerChain(testData.tx4.chain, mockedManager);
367
+ // mock `submitTransaction`
368
+ const mockedSubmitTransaction = vi.fn();
369
+ mockedSubmitTransaction.mockRejectedValue(
370
+ Error(`TestError: submit failed`)
371
+ );
372
+ vi.spyOn(mockedManager, 'submitTransaction').mockImplementation(
373
+ mockedSubmitTransaction
374
+ );
375
+
376
+ // run test
377
+ await txPot.update();
378
+
379
+ // check if function got called
380
+ expect(mockedSubmitTransaction).toHaveBeenCalled();
381
+
382
+ // check db records
383
+ const txs = (await txRepository.find()).map((tx) => [
384
+ tx.txId,
385
+ tx.status,
386
+ tx.lastStatusUpdate,
387
+ ]);
388
+ expect(txs).toEqual([
389
+ [
390
+ testData.tx4.txId,
391
+ TransactionStatus.SENT,
392
+ String(testData.currentTimeStampAsSeconds),
393
+ ],
394
+ ]);
395
+ });
396
+ });
397
+
398
+ describe('processesSentTx', () => {
399
+ /**
400
+ * @target TxPot.processesSentTx should update tx status to completed when
401
+ * transaction is confirmed enough
402
+ * @dependencies
403
+ * - Date
404
+ * - database
405
+ * @scenario
406
+ * - insert tx with sent status
407
+ * - mock PotChainManager and register to TxPot
408
+ * - mock `getTxConfirmation`
409
+ * - mock `getTxRequiredConfirmation`
410
+ * - run test (call `update`)
411
+ * - check db records
412
+ * @expected
413
+ * - columns of the tx should be updated
414
+ * - status should be updated to completed
415
+ * - lastStatusUpdate should be updated to currentTimeStamp in seconds
416
+ */
417
+ it('should update tx status to completed when transaction is confirmed enough', async () => {
418
+ // insert tx with sent status
419
+ await txRepository.insert(testData.tx6);
420
+
421
+ // mock PotChainManager and register to TxPot
422
+ const mockedManager = new TestPotChainManager();
423
+ txPot.registerChain(testData.tx6.chain, mockedManager);
424
+ // mock `getTxConfirmation`
425
+ vi.spyOn(mockedManager, 'getTxConfirmation').mockResolvedValue(5);
426
+ // mock `getTxRequiredConfirmation`
427
+ vi.spyOn(mockedManager, 'getTxRequiredConfirmation').mockReturnValue(5);
428
+
429
+ // run test
430
+ await txPot.update();
431
+
432
+ // check db records
433
+ const txs = (await txRepository.find()).flatMap((tx) => [
434
+ tx.txId,
435
+ tx.status,
436
+ tx.lastStatusUpdate,
437
+ ]);
438
+ expect(txs).toEqual([
439
+ testData.tx6.txId,
440
+ TransactionStatus.COMPLETED,
441
+ String(testData.currentTimeStampAsSeconds),
442
+ ]);
443
+ });
444
+
445
+ /**
446
+ * @target TxPot.processesSentTx should only update tx lastCheck when transaction
447
+ * is NOT confirmed enough
448
+ * @dependencies
449
+ * - database
450
+ * @scenario
451
+ * - insert tx with sent status
452
+ * - mock PotChainManager and register to TxPot
453
+ * - mock `getTxConfirmation`
454
+ * - mock `getTxRequiredConfirmation`
455
+ * - mock `getHeight`
456
+ * - run test (call `update`)
457
+ * - check db records
458
+ * @expected
459
+ * - columns of the tx should be as expected
460
+ * - lastCheck should be updated to current height
461
+ * - status and lastStatusUpdate should remain unchanged
462
+ */
463
+ it('should only update tx lastCheck when transaction is NOT confirmed enough', async () => {
464
+ // insert tx with sent status
465
+ await txRepository.insert(testData.tx6);
466
+
467
+ // mock PotChainManager and register to TxPot
468
+ const mockedManager = new TestPotChainManager();
469
+ txPot.registerChain(testData.tx6.chain, mockedManager);
470
+ // mock `getTxConfirmation`
471
+ vi.spyOn(mockedManager, 'getTxConfirmation').mockResolvedValue(5);
472
+ // mock `getTxRequiredConfirmation`
473
+ vi.spyOn(mockedManager, 'getTxRequiredConfirmation').mockReturnValue(10);
474
+ // mock `getHeight`
475
+ const mockedHeight = 100110;
476
+ vi.spyOn(mockedManager, 'getHeight').mockResolvedValue(mockedHeight);
477
+
478
+ // run test
479
+ await txPot.update();
480
+
481
+ // check db records
482
+ const txs = (await txRepository.find()).flatMap((tx) => [
483
+ tx.txId,
484
+ tx.status,
485
+ tx.lastCheck,
486
+ tx.lastStatusUpdate,
487
+ ]);
488
+ expect(txs).toEqual([
489
+ testData.tx6.txId,
490
+ testData.tx6.status,
491
+ mockedHeight,
492
+ testData.tx6.lastStatusUpdate,
493
+ ]);
494
+ });
495
+
496
+ /**
497
+ * @target TxPot.processesSentTx should update tx lastCheck when transaction
498
+ * is found in mempool
499
+ * @dependencies
500
+ * - database
501
+ * @scenario
502
+ * - insert tx with sent status
503
+ * - mock PotChainManager and register to TxPot
504
+ * - mock `getTxConfirmation`
505
+ * - mock `getTxRequiredConfirmation` to return -1
506
+ * - mock `isTxInMempool` to return true
507
+ * - mock `getHeight`
508
+ * - run test (call `update`)
509
+ * - check db records
510
+ * @expected
511
+ * - columns of the tx should be as expected
512
+ * - lastCheck should be updated to current height
513
+ * - status and lastStatusUpdate should remain unchanged
514
+ */
515
+ it('should update tx lastCheck when transaction is found in mempool', async () => {
516
+ // insert tx with sent status
517
+ await txRepository.insert(testData.tx6);
518
+
519
+ // mock PotChainManager and register to TxPot
520
+ const mockedManager = new TestPotChainManager();
521
+ txPot.registerChain(testData.tx6.chain, mockedManager);
522
+ // mock `getTxConfirmation`
523
+ vi.spyOn(mockedManager, 'getTxConfirmation').mockResolvedValue(-1);
524
+ // mock `getTxRequiredConfirmation`
525
+ vi.spyOn(mockedManager, 'getTxRequiredConfirmation').mockReturnValue(10);
526
+ // mock `isTxInMempool`
527
+ vi.spyOn(mockedManager, 'isTxInMempool').mockResolvedValue(true);
528
+ // mock `getHeight`
529
+ const mockedHeight = 100110;
530
+ vi.spyOn(mockedManager, 'getHeight').mockResolvedValue(mockedHeight);
531
+
532
+ // run test
533
+ await txPot.update();
534
+
535
+ // check db records
536
+ const txs = (await txRepository.find()).flatMap((tx) => [
537
+ tx.txId,
538
+ tx.status,
539
+ tx.lastCheck,
540
+ tx.lastStatusUpdate,
541
+ ]);
542
+ expect(txs).toEqual([
543
+ testData.tx6.txId,
544
+ testData.tx6.status,
545
+ mockedHeight,
546
+ testData.tx6.lastStatusUpdate,
547
+ ]);
548
+ });
549
+
550
+ /**
551
+ * @target TxPot.processesSentTx should resubmit if tx is not found but still valid
552
+ * @dependencies
553
+ * - database
554
+ * @scenario
555
+ * - insert tx with sent status
556
+ * - mock PotChainManager and register to TxPot
557
+ * - mock `getTxConfirmation`
558
+ * - mock `getTxRequiredConfirmation` to return -1
559
+ * - mock `isTxInMempool` to return false
560
+ * - mock `isTxValid` to return true
561
+ * - mock `submitTransaction`
562
+ * - run test (call `update`)
563
+ * - check if function got called
564
+ * @expected
565
+ * - `submitTransaction` should got called
566
+ */
567
+ it('should resubmit if tx is not found but still valid', async () => {
568
+ // insert tx with sent status
569
+ await txRepository.insert(testData.tx6);
570
+
571
+ // mock PotChainManager and register to TxPot
572
+ const mockedManager = new TestPotChainManager();
573
+ txPot.registerChain(testData.tx6.chain, mockedManager);
574
+ // mock `getTxConfirmation`
575
+ vi.spyOn(mockedManager, 'getTxConfirmation').mockResolvedValue(-1);
576
+ // mock `getTxRequiredConfirmation`
577
+ vi.spyOn(mockedManager, 'getTxRequiredConfirmation').mockReturnValue(10);
578
+ // mock `isTxInMempool`
579
+ vi.spyOn(mockedManager, 'isTxInMempool').mockResolvedValue(false);
580
+ // mock `isTxValid`
581
+ vi.spyOn(mockedManager, 'isTxValid').mockResolvedValue(true);
582
+ // mock `submitTransaction`
583
+ const mockedSubmitTransaction = vi.fn();
584
+ mockedSubmitTransaction.mockResolvedValue(undefined);
585
+ vi.spyOn(mockedManager, 'submitTransaction').mockImplementation(
586
+ mockedSubmitTransaction
587
+ );
588
+
589
+ // run test
590
+ await txPot.update();
591
+
592
+ // check if function got called
593
+ expect(mockedSubmitTransaction).toHaveBeenCalled();
594
+ });
595
+
596
+ /**
597
+ * @target TxPot.processesSentTx should set transaction as invalid if tx is not valid anymore
598
+ * @dependencies
599
+ * - database
600
+ * @scenario
601
+ * - insert tx with sent status
602
+ * - mock PotChainManager and register to TxPot
603
+ * - mock `getTxConfirmation`
604
+ * - mock `getTxRequiredConfirmation` to return -1
605
+ * - mock `isTxInMempool` to return false
606
+ * - mock `isTxValid` to return false
607
+ * - mock `getHeight`
608
+ * - mock TxPot.setTransactionAsInvalid
609
+ * - run test (call `update`)
610
+ * - check if function got called
611
+ * @expected
612
+ * - `setTransactionAsInvalid` should got called
613
+ */
614
+ it('should set transaction as invalid if tx is not valid anymore', async () => {
615
+ // insert tx with sent status
616
+ await txRepository.insert(testData.tx6);
617
+
618
+ // mock PotChainManager and register to TxPot
619
+ const mockedManager = new TestPotChainManager();
620
+ txPot.registerChain(testData.tx6.chain, mockedManager);
621
+ // mock `getTxConfirmation`
622
+ vi.spyOn(mockedManager, 'getTxConfirmation').mockResolvedValue(-1);
623
+ // mock `getTxRequiredConfirmation`
624
+ vi.spyOn(mockedManager, 'getTxRequiredConfirmation').mockReturnValue(10);
625
+ // mock `isTxInMempool`
626
+ vi.spyOn(mockedManager, 'isTxInMempool').mockResolvedValue(false);
627
+ // mock `isTxValid`
628
+ vi.spyOn(mockedManager, 'isTxValid').mockResolvedValue(false);
629
+
630
+ // mock TxPot.setTransactionAsInvalid
631
+ const mockedSetTransactionAsInvalid = vi.fn();
632
+ vi.spyOn(txPot as any, 'setTransactionAsInvalid').mockImplementation(
633
+ mockedSetTransactionAsInvalid
634
+ );
635
+
636
+ // run test
637
+ await txPot.update();
638
+
639
+ // check if function got called
640
+ expect(mockedSetTransactionAsInvalid).toHaveBeenCalled();
641
+ });
642
+
643
+ /**
644
+ * @target TxPot.processesSentTx should set transaction as invalid when registered
645
+ * validator recognizes the tx as invalid
646
+ * @dependencies
647
+ * - database
648
+ * @scenario
649
+ * - insert tx with sent status
650
+ * - register a validator function to return false
651
+ * - mock PotChainManager and register to TxPot
652
+ * - mock `getTxConfirmation`
653
+ * - mock `getTxRequiredConfirmation` to return -1
654
+ * - mock `isTxInMempool` to return false
655
+ * - mock `isTxValid` to return true
656
+ * - mock `getHeight`
657
+ * - mock TxPot.setTransactionAsInvalid
658
+ * - run test (call `update`)
659
+ * - check if function got called
660
+ * @expected
661
+ * - `setTransactionAsInvalid` should got called
662
+ */
663
+ it('should set transaction as invalid when registered validator recognizes the tx as invalid', async () => {
664
+ // insert tx with sent status
665
+ await txRepository.insert(testData.tx6);
666
+
667
+ // register a validator function
668
+ const mockedValidator = async (tx: TransactionEntity) => false;
669
+ txPot.registerValidator(
670
+ testData.tx6.chain,
671
+ testData.tx6.txType,
672
+ mockedValidator
673
+ );
674
+
675
+ // mock PotChainManager and register to TxPot
676
+ const mockedManager = new TestPotChainManager();
677
+ txPot.registerChain(testData.tx6.chain, mockedManager);
678
+ // mock `getTxConfirmation`
679
+ vi.spyOn(mockedManager, 'getTxConfirmation').mockResolvedValue(-1);
680
+ // mock `getTxRequiredConfirmation`
681
+ vi.spyOn(mockedManager, 'getTxRequiredConfirmation').mockReturnValue(10);
682
+ // mock `isTxInMempool`
683
+ vi.spyOn(mockedManager, 'isTxInMempool').mockResolvedValue(false);
684
+ // mock `isTxValid`
685
+ vi.spyOn(mockedManager, 'isTxValid').mockResolvedValue(true);
686
+
687
+ // mock TxPot.setTransactionAsInvalid
688
+ const mockedSetTransactionAsInvalid = vi.fn();
689
+ vi.spyOn(txPot as any, 'setTransactionAsInvalid').mockImplementation(
690
+ mockedSetTransactionAsInvalid
691
+ );
692
+
693
+ // run test
694
+ await txPot.update();
695
+
696
+ // check if function got called
697
+ expect(mockedSetTransactionAsInvalid).toHaveBeenCalled();
698
+ });
699
+ });
700
+
701
+ describe('getTxsByStatus', () => {
702
+ /**
703
+ * @target TxPot.getTxsByStatus should return txs filtered by status
704
+ * @dependencies
705
+ * - database
706
+ * @scenario
707
+ * - insert 3 txs with different status
708
+ * - run test
709
+ * - check returned value
710
+ * @expected
711
+ * - should return 2 filtered txs
712
+ */
713
+ it('should return txs filtered by status', async () => {
714
+ await txRepository.insert(testData.tx1); // different status
715
+ await txRepository.insert(testData.tx3);
716
+ await txRepository.insert(testData.tx5);
717
+
718
+ const txs = await txPot.getTxsByStatus(
719
+ testData.tx3.status as TransactionStatus
720
+ );
721
+ expect(txs.length).toEqual(2);
722
+ expect(txs[0].txId).toEqual(testData.tx3.txId);
723
+ expect(txs[1].txId).toEqual(testData.tx5.txId);
724
+ });
725
+
726
+ /**
727
+ * @target TxPot.getTxsByStatus should only return valid txs when
728
+ * validate key is passed
729
+ * @dependencies
730
+ * - database
731
+ * @scenario
732
+ * - insert 2 txs with same status
733
+ * - register a validator function
734
+ * - should return true for first tx
735
+ * - should return false for second tx
736
+ * - mock TxPot.setTransactionAsInvalid
737
+ * - run test
738
+ * - check returned value
739
+ * @expected
740
+ * - should return the valid tx
741
+ */
742
+ it('should only return valid txs when validate key is passed', async () => {
743
+ // insert 2 txs with same status
744
+ await txRepository.insert(testData.tx3);
745
+ await txRepository.insert(testData.tx5);
746
+
747
+ // register a validator function
748
+ const mockedValidator = async (tx: TransactionEntity) => {
749
+ if (tx.txId === testData.tx3.txId) return true;
750
+ return false;
751
+ };
752
+ txPot.registerValidator(
753
+ testData.tx3.chain,
754
+ testData.tx3.txType,
755
+ mockedValidator
756
+ );
757
+ txPot.registerValidator(
758
+ testData.tx5.chain,
759
+ testData.tx5.txType,
760
+ mockedValidator
761
+ );
762
+
763
+ // mock TxPot.setTransactionAsInvalid
764
+ vi.spyOn(txPot as any, 'setTransactionAsInvalid').mockResolvedValue(
765
+ undefined
766
+ );
767
+
768
+ // run test
769
+ const txs = await txPot.getTxsByStatus(
770
+ testData.tx3.status as TransactionStatus,
771
+ true
772
+ );
773
+ expect(txs.length).toEqual(1);
774
+ expect(txs[0].txId).toEqual(testData.tx3.txId);
775
+ });
776
+ });
777
+
778
+ describe('addTx', () => {
779
+ /**
780
+ * @target TxPot.addTx should insert new tx successfully
781
+ * @dependencies
782
+ * - Date
783
+ * - database
784
+ * @scenario
785
+ * - run test
786
+ * - check db records
787
+ * @expected
788
+ * - new tx should be inserted
789
+ */
790
+ it('should insert new tx successfully', async () => {
791
+ await txPot.addTx(
792
+ testData.tx1.txId,
793
+ testData.tx1.chain,
794
+ testData.tx1.txType,
795
+ testData.tx1.requiredSign,
796
+ testData.tx1.serializedTx,
797
+ testData.tx1.status as TransactionStatus,
798
+ testData.tx1.lastCheck
799
+ );
800
+
801
+ const txs = await txRepository.find();
802
+ expect(txs.length).toEqual(1);
803
+ expect(txs[0]).toEqual({
804
+ ...testData.tx1,
805
+ lastStatusUpdate: String(testData.currentTimeStampAsSeconds),
806
+ extra: null,
807
+ extra2: null,
808
+ });
809
+ });
810
+ });
811
+
812
+ describe('setTxStatusById', () => {
813
+ /**
814
+ * @target TxPot.setTxStatusById should throw error when tx is not found
815
+ * @dependencies
816
+ * - Date
817
+ * - database
818
+ * @scenario
819
+ * - run test & expect exception thrown
820
+ * @expected
821
+ * - it should throw Error
822
+ */
823
+ it('should throw error when tx is not found', async () => {
824
+ await expect(async () => {
825
+ await txPot.setTxStatusById(
826
+ testData.tx1.txId,
827
+ testData.tx1.chain,
828
+ TransactionStatus.IN_SIGN
829
+ );
830
+ }).rejects.toThrow(Error);
831
+ });
832
+
833
+ /**
834
+ * @target TxPot.setTxStatusById should update status and lastStatusUpdate successfully
835
+ * @dependencies
836
+ * - Date
837
+ * - database
838
+ * @scenario
839
+ * - insert 2 txs
840
+ * - run test
841
+ * - check db records
842
+ * @expected
843
+ * - columns of target tx should be updated
844
+ * - status should be updated to new status
845
+ * - lastStatusUpdate should be updated to currentTimeStamp in seconds
846
+ * - columns of other txs should remain unchanged
847
+ */
848
+ it('should update status and lastStatusUpdate successfully', async () => {
849
+ await txRepository.insert(testData.tx1);
850
+ await txRepository.insert(testData.tx2);
851
+
852
+ const newStatus = TransactionStatus.IN_SIGN;
853
+ await txPot.setTxStatusById(
854
+ testData.tx1.txId,
855
+ testData.tx1.chain,
856
+ newStatus
857
+ );
858
+
859
+ const txs = (await txRepository.find()).map((tx) => [
860
+ tx.txId,
861
+ tx.status,
862
+ tx.lastStatusUpdate,
863
+ ]);
864
+ expect(txs).toEqual([
865
+ [
866
+ testData.tx1.txId,
867
+ newStatus,
868
+ String(testData.currentTimeStampAsSeconds),
869
+ ],
870
+ [testData.tx2.txId, testData.tx2.status, testData.tx2.lastStatusUpdate],
871
+ ]);
872
+ });
873
+ });
874
+
875
+ describe('setTxAsSignFailed', () => {
876
+ /**
877
+ * @target TxPot.setTxAsSignFailed should set tx as sign-failed and update required fields successfully
878
+ * @dependencies
879
+ * - Date
880
+ * - database
881
+ * @scenario
882
+ * - insert 2 txs
883
+ * - run test
884
+ * - check db records
885
+ * @expected
886
+ * - columns of target tx should be updated as expected
887
+ * - status should be sign-failed
888
+ * - lastStatusUpdate should be updated to currentTimeStamp in seconds
889
+ * - failedInSign should be true
890
+ * - signFailedCount should be incremented
891
+ * - columns of other txs should remain unchanged
892
+ */
893
+ it('should set tx as sign-failed and update required fields successfully', async () => {
894
+ await txRepository.insert(testData.tx3);
895
+ await txRepository.insert(testData.tx5);
896
+
897
+ await txPot.setTxAsSignFailed(testData.tx5.txId, testData.tx5.chain);
898
+
899
+ const txs = (await txRepository.find()).map((tx) => [
900
+ tx.txId,
901
+ tx.status,
902
+ tx.lastStatusUpdate,
903
+ tx.failedInSign,
904
+ tx.signFailedCount,
905
+ ]);
906
+ expect(txs).toEqual([
907
+ [
908
+ testData.tx3.txId,
909
+ testData.tx3.status,
910
+ testData.tx3.lastStatusUpdate,
911
+ testData.tx3.failedInSign,
912
+ testData.tx3.signFailedCount,
913
+ ],
914
+ [
915
+ testData.tx5.txId,
916
+ TransactionStatus.SIGN_FAILED,
917
+ String(testData.currentTimeStampAsSeconds),
918
+ true,
919
+ testData.tx5.signFailedCount + 1,
920
+ ],
921
+ ]);
922
+ });
923
+ });
924
+
925
+ describe('setTxAsSigned', () => {
926
+ /**
927
+ * @target TxPot.setTxAsSigned should set tx as signed and update required fields successfully
928
+ * @dependencies
929
+ * - Date
930
+ * - database
931
+ * @scenario
932
+ * - insert 2 txs
933
+ * - run test
934
+ * - check db records
935
+ * @expected
936
+ * - columns of target tx should be updated as expected
937
+ * - status should be signed
938
+ * - lastCheck should be updated to currentHeight
939
+ * - lastStatusUpdate should be updated to currentTimeStamp in seconds
940
+ * - serializedTx should be updated
941
+ * - columns of other txs should remain unchanged
942
+ */
943
+ it('should set tx as signed and update required fields successfully', async () => {
944
+ await txRepository.insert(testData.tx3);
945
+ await txRepository.insert(testData.tx5);
946
+
947
+ const currentHeight = testData.tx3.lastCheck + 10;
948
+ const serializedSignedTx = 'serialized-signed-tx';
949
+ await txPot.setTxAsSigned(
950
+ testData.tx3.txId,
951
+ testData.tx3.chain,
952
+ serializedSignedTx,
953
+ currentHeight
954
+ );
955
+
956
+ const txs = (await txRepository.find()).map((tx) => [
957
+ tx.txId,
958
+ tx.status,
959
+ tx.lastCheck,
960
+ tx.lastStatusUpdate,
961
+ tx.serializedTx,
962
+ ]);
963
+ expect(txs).toEqual([
964
+ [
965
+ testData.tx3.txId,
966
+ TransactionStatus.SIGNED,
967
+ currentHeight,
968
+ String(testData.currentTimeStampAsSeconds),
969
+ serializedSignedTx,
970
+ ],
971
+ [
972
+ testData.tx5.txId,
973
+ testData.tx5.status,
974
+ testData.tx5.lastCheck,
975
+ testData.tx5.lastStatusUpdate,
976
+ testData.tx5.serializedTx,
977
+ ],
978
+ ]);
979
+ });
980
+
981
+ /**
982
+ * @target TxPot.setTxAsSigned should also update extra fields if are provided
983
+ * @dependencies
984
+ * - Date
985
+ * - database
986
+ * @scenario
987
+ * - insert 2 txs
988
+ * - run test
989
+ * - check db records
990
+ * @expected
991
+ * - columns of target tx should be updated as expected
992
+ * - status should be signed
993
+ * - lastCheck should be updated to currentHeight
994
+ * - lastStatusUpdate should be updated to currentTimeStamp in seconds
995
+ * - serializedTx should be updated
996
+ * - extra should be updated
997
+ * - extra2 should be updated
998
+ * - columns of other txs should remain unchanged
999
+ */
1000
+ it('should also update extra fields if are provided', async () => {
1001
+ await txRepository.insert(testData.tx3);
1002
+ await txRepository.insert(testData.tx5);
1003
+
1004
+ const currentHeight = testData.tx3.lastCheck + 10;
1005
+ const serializedSignedTx = 'serialized-signed-tx';
1006
+ const updatedExtra = 'updated-extra';
1007
+ const updatedExtra2 = 'updated-extra-2';
1008
+ await txPot.setTxAsSigned(
1009
+ testData.tx3.txId,
1010
+ testData.tx3.chain,
1011
+ serializedSignedTx,
1012
+ currentHeight,
1013
+ updatedExtra,
1014
+ updatedExtra2
1015
+ );
1016
+
1017
+ const txs = (await txRepository.find()).map((tx) => [
1018
+ tx.txId,
1019
+ tx.status,
1020
+ tx.lastCheck,
1021
+ tx.lastStatusUpdate,
1022
+ tx.serializedTx,
1023
+ tx.extra,
1024
+ tx.extra2,
1025
+ ]);
1026
+ expect(txs).toEqual([
1027
+ [
1028
+ testData.tx3.txId,
1029
+ TransactionStatus.SIGNED,
1030
+ currentHeight,
1031
+ String(testData.currentTimeStampAsSeconds),
1032
+ serializedSignedTx,
1033
+ updatedExtra,
1034
+ updatedExtra2,
1035
+ ],
1036
+ [
1037
+ testData.tx5.txId,
1038
+ testData.tx5.status,
1039
+ testData.tx5.lastCheck,
1040
+ testData.tx5.lastStatusUpdate,
1041
+ testData.tx5.serializedTx,
1042
+ testData.tx5.extra ?? null,
1043
+ testData.tx5.extra2 ?? null,
1044
+ ],
1045
+ ]);
1046
+ });
1047
+ });
1048
+
1049
+ describe('updateTxLastCheck', () => {
1050
+ /**
1051
+ * @target TxPot.updateTxLastCheck should update lastCheck column successfully
1052
+ * @dependencies
1053
+ * - database
1054
+ * @scenario
1055
+ * - insert 2 txs
1056
+ * - run test
1057
+ * - check db records
1058
+ * @expected
1059
+ * - lastCheck of target tx should be updated
1060
+ * - lastCheck of other txs should remain unchanged
1061
+ */
1062
+ it('should update lastCheck column successfully', async () => {
1063
+ await txRepository.insert(testData.tx1);
1064
+ await txRepository.insert(testData.tx2);
1065
+
1066
+ const updatedLastCheck = testData.tx1.lastCheck + 100;
1067
+ await txPot.updateTxLastCheck(
1068
+ testData.tx1.txId,
1069
+ testData.tx1.chain,
1070
+ updatedLastCheck
1071
+ );
1072
+
1073
+ const txs = (await txRepository.find()).map((tx) => [
1074
+ tx.txId,
1075
+ tx.lastCheck,
1076
+ ]);
1077
+ expect(txs).toEqual([
1078
+ [testData.tx1.txId, updatedLastCheck],
1079
+ [testData.tx2.txId, testData.tx2.lastCheck],
1080
+ ]);
1081
+ });
1082
+ });
1083
+
1084
+ describe('resetFailedInSign', () => {
1085
+ /**
1086
+ * @target TxPot.resetFailedInSign should set failedInSign column to false successfully
1087
+ * @dependencies
1088
+ * - database
1089
+ * @scenario
1090
+ * - insert 3 txs
1091
+ * - 2 with failedInSign column as true
1092
+ * - 1 with failedInSign column as false
1093
+ * - run test
1094
+ * - check db records
1095
+ * @expected
1096
+ * - failedInSign of target tx should be false
1097
+ * - failedInSign of other txs should remain unchanged
1098
+ */
1099
+ it('should set failedInSign column to false successfully', async () => {
1100
+ await txRepository.insert(testData.tx1); // failedInSign is false
1101
+ await txRepository.insert(testData.tx3);
1102
+ await txRepository.insert(testData.tx4);
1103
+
1104
+ await txPot.resetFailedInSign(testData.tx3.txId, testData.tx3.chain);
1105
+
1106
+ const txs = (await txRepository.find()).map((tx) => [
1107
+ tx.txId,
1108
+ tx.failedInSign,
1109
+ ]);
1110
+ expect(txs).toEqual([
1111
+ [testData.tx1.txId, testData.tx1.failedInSign],
1112
+ [testData.tx3.txId, false],
1113
+ [testData.tx4.txId, testData.tx4.failedInSign],
1114
+ ]);
1115
+ });
1116
+ });
1117
+
1118
+ describe('updateRequiredSign', () => {
1119
+ /**
1120
+ * @target TxPot.updateRequiredSign should update requiredSign column successfully
1121
+ * @dependencies
1122
+ * - database
1123
+ * @scenario
1124
+ * - insert 2 txs
1125
+ * - run test
1126
+ * - check db records
1127
+ * @expected
1128
+ * - requiredSign of target tx should be updated
1129
+ * - requiredSign of other txs should remain unchanged
1130
+ */
1131
+ it('should update requiredSign column successfully', async () => {
1132
+ await txRepository.insert(testData.tx1);
1133
+ await txRepository.insert(testData.tx2);
1134
+
1135
+ const updatedRequiredSign = testData.tx1.requiredSign + 2;
1136
+ await txPot.updateRequiredSign(
1137
+ testData.tx1.txId,
1138
+ testData.tx1.chain,
1139
+ updatedRequiredSign
1140
+ );
1141
+
1142
+ const txs = (await txRepository.find()).map((tx) => [
1143
+ tx.txId,
1144
+ tx.requiredSign,
1145
+ ]);
1146
+ expect(txs).toEqual([
1147
+ [testData.tx1.txId, updatedRequiredSign],
1148
+ [testData.tx2.txId, testData.tx2.requiredSign],
1149
+ ]);
1150
+ });
1151
+ });
1152
+
1153
+ describe('getTxByKey', () => {
1154
+ /**
1155
+ * @target TxPot.getTxByKey should return the tx successfully
1156
+ * @dependencies
1157
+ * - database
1158
+ * @scenario
1159
+ * - insert 2 txs
1160
+ * - run test
1161
+ * - check returned value
1162
+ * @expected
1163
+ * - should return the expected tx
1164
+ */
1165
+ it('should return the tx successfully', async () => {
1166
+ await txRepository.insert(testData.tx1);
1167
+ await txRepository.insert(testData.tx2);
1168
+
1169
+ const tx = await txPot.getTxByKey(testData.tx1.txId, testData.tx1.chain);
1170
+ expect(tx?.txId).toEqual(testData.tx1.txId);
1171
+ });
1172
+
1173
+ /**
1174
+ * @target TxPot.getTxByKey should return null when tx is not found
1175
+ * @dependencies
1176
+ * - database
1177
+ * @scenario
1178
+ * - insert 2 txs
1179
+ * - run test
1180
+ * - check returned value
1181
+ * @expected
1182
+ * - should return null
1183
+ */
1184
+ it('should return null when tx is not found', async () => {
1185
+ await txRepository.insert(testData.tx1);
1186
+ await txRepository.insert(testData.tx2);
1187
+
1188
+ const tx = await txPot.getTxByKey(testData.tx3.txId, testData.tx3.chain);
1189
+ expect(tx).toBeNull();
1190
+ });
1191
+ });
1192
+
1193
+ describe('getTxsQuery', () => {
1194
+ /**
1195
+ * @target TxPot.getTxsQuery should return all txs when no option is passed
1196
+ * @dependencies
1197
+ * - database
1198
+ * @scenario
1199
+ * - insert 2 txs
1200
+ * - run test
1201
+ * - check returned value
1202
+ * @expected
1203
+ * - should return 2 txs
1204
+ */
1205
+ it('should return all txs when no option is passed', async () => {
1206
+ await txRepository.insert(testData.tx1);
1207
+ await txRepository.insert(testData.tx2);
1208
+
1209
+ const txs = await txPot.getTxsQuery();
1210
+ expect(txs.length).toEqual(2);
1211
+ });
1212
+
1213
+ /**
1214
+ * @target TxPot.getTxsQuery should return txs filtered by txId
1215
+ * @dependencies
1216
+ * - database
1217
+ * @scenario
1218
+ * - insert 2 txs with different txIds
1219
+ * - run test
1220
+ * - check returned value
1221
+ * @expected
1222
+ * - should return filtered tx
1223
+ */
1224
+ it('should return txs filtered by txId', async () => {
1225
+ await txRepository.insert(testData.tx1);
1226
+ await txRepository.insert(testData.tx2);
1227
+
1228
+ const txs = await txPot.getTxsQuery([{ txId: testData.tx1.txId }]);
1229
+ expect(txs.length).toEqual(1);
1230
+ expect(txs[0].txId).toEqual(testData.tx1.txId);
1231
+ });
1232
+
1233
+ /**
1234
+ * @target TxPot.getTxsQuery should return txs filtered by list of txId
1235
+ * @dependencies
1236
+ * - database
1237
+ * @scenario
1238
+ * - insert 3 txs with different txIds
1239
+ * - run test
1240
+ * - check returned value
1241
+ * @expected
1242
+ * - should return 2 filtered txs
1243
+ */
1244
+ it('should return txs filtered by list of txId', async () => {
1245
+ await txRepository.insert(testData.tx1);
1246
+ await txRepository.insert(testData.tx2);
1247
+ await txRepository.insert(testData.tx3); // txId not on the list
1248
+
1249
+ const txs = await txPot.getTxsQuery([
1250
+ { txId: [testData.tx1.txId, testData.tx2.txId] },
1251
+ ]);
1252
+ expect(txs.length).toEqual(2);
1253
+ expect(txs[0].txId).toEqual(testData.tx1.txId);
1254
+ expect(txs[1].txId).toEqual(testData.tx2.txId);
1255
+ });
1256
+
1257
+ /**
1258
+ * @target TxPot.getTxsQuery should return txs filtered by chain
1259
+ * @dependencies
1260
+ * - database
1261
+ * @scenario
1262
+ * - insert 3 txs with different chains
1263
+ * - run test
1264
+ * - check returned value
1265
+ * @expected
1266
+ * - should return 2 filtered txs
1267
+ */
1268
+ it('should return txs filtered by chain', async () => {
1269
+ await txRepository.insert(testData.tx1);
1270
+ await txRepository.insert(testData.tx2);
1271
+ await txRepository.insert(testData.tx3); // different chain
1272
+
1273
+ const txs = await txPot.getTxsQuery([{ chain: testData.tx1.chain }]);
1274
+ expect(txs.length).toEqual(2);
1275
+ expect(txs[0].txId).toEqual(testData.tx1.txId);
1276
+ expect(txs[1].txId).toEqual(testData.tx2.txId);
1277
+ });
1278
+
1279
+ /**
1280
+ * @target TxPot.getTxsQuery should return txs filtered by txType
1281
+ * @dependencies
1282
+ * - database
1283
+ * @scenario
1284
+ * - insert 3 txs with different txTypes
1285
+ * - run test
1286
+ * - check returned value
1287
+ * @expected
1288
+ * - should return 2 filtered txs
1289
+ */
1290
+ it('should return txs filtered by txType', async () => {
1291
+ await txRepository.insert(testData.tx1);
1292
+ await txRepository.insert(testData.tx3);
1293
+ await txRepository.insert(testData.tx4); // different txType
1294
+
1295
+ const txs = await txPot.getTxsQuery([{ txType: testData.tx1.txType }]);
1296
+ expect(txs.length).toEqual(2);
1297
+ expect(txs[0].txId).toEqual(testData.tx1.txId);
1298
+ expect(txs[1].txId).toEqual(testData.tx3.txId);
1299
+ });
1300
+
1301
+ /**
1302
+ * @target TxPot.getTxsQuery should return txs filtered by status
1303
+ * @dependencies
1304
+ * - database
1305
+ * @scenario
1306
+ * - insert 3 txs with different status
1307
+ * - run test
1308
+ * - check returned value
1309
+ * @expected
1310
+ * - should return 2 filtered txs
1311
+ */
1312
+ it('should return txs filtered by status', async () => {
1313
+ await txRepository.insert(testData.tx1); // different status
1314
+ await txRepository.insert(testData.tx3);
1315
+ await txRepository.insert(testData.tx5);
1316
+
1317
+ const txs = await txPot.getTxsQuery([
1318
+ {
1319
+ status: {
1320
+ not: false,
1321
+ value: testData.tx3.status as TransactionStatus,
1322
+ },
1323
+ },
1324
+ ]);
1325
+ expect(txs.length).toEqual(2);
1326
+ expect(txs[0].txId).toEqual(testData.tx3.txId);
1327
+ expect(txs[1].txId).toEqual(testData.tx5.txId);
1328
+ });
1329
+
1330
+ /**
1331
+ * @target TxPot.getTxsQuery should return txs filtered by list of statuses
1332
+ * @dependencies
1333
+ * - database
1334
+ * @scenario
1335
+ * - insert 3 txs with different status
1336
+ * - run test
1337
+ * - check returned value
1338
+ * @expected
1339
+ * - should return 2 filtered txs
1340
+ */
1341
+ it('should return txs filtered by list of statuses', async () => {
1342
+ await txRepository.insert(testData.tx1);
1343
+ await txRepository.insert(testData.tx2);
1344
+ await txRepository.insert(testData.tx3); // status not on the list
1345
+
1346
+ const txs = await txPot.getTxsQuery([
1347
+ {
1348
+ status: {
1349
+ not: false,
1350
+ value: [
1351
+ testData.tx1.status as TransactionStatus,
1352
+ testData.tx2.status as TransactionStatus,
1353
+ ],
1354
+ },
1355
+ },
1356
+ ]);
1357
+ expect(txs.length).toEqual(2);
1358
+ expect(txs[0].txId).toEqual(testData.tx1.txId);
1359
+ expect(txs[1].txId).toEqual(testData.tx2.txId);
1360
+ });
1361
+
1362
+ /**
1363
+ * @target TxPot.getTxsQuery should return txs that their status are not
1364
+ * equal to given status
1365
+ * @dependencies
1366
+ * - database
1367
+ * @scenario
1368
+ * - insert 3 txs with different status
1369
+ * - run test
1370
+ * - check returned value
1371
+ * @expected
1372
+ * - should return 2 filtered txs
1373
+ */
1374
+ it('should return txs that their status are not equal to given status', async () => {
1375
+ await txRepository.insert(testData.tx1);
1376
+ await txRepository.insert(testData.tx2);
1377
+ await txRepository.insert(testData.tx3);
1378
+
1379
+ const txs = await txPot.getTxsQuery([
1380
+ {
1381
+ status: {
1382
+ not: true,
1383
+ value: testData.tx1.status as TransactionStatus,
1384
+ },
1385
+ },
1386
+ ]);
1387
+ expect(txs.length).toEqual(2);
1388
+ expect(txs[0].txId).toEqual(testData.tx2.txId);
1389
+ expect(txs[1].txId).toEqual(testData.tx3.txId);
1390
+ });
1391
+
1392
+ /**
1393
+ * @target TxPot.getTxsQuery should return txs that their status are not
1394
+ * on given list of statuses
1395
+ * @dependencies
1396
+ * - database
1397
+ * @scenario
1398
+ * - insert 3 txs with different status
1399
+ * - run test
1400
+ * - check returned value
1401
+ * @expected
1402
+ * - should return filtered tx
1403
+ */
1404
+ it('should return txs that their status are not on given list of statuses', async () => {
1405
+ await txRepository.insert(testData.tx1);
1406
+ await txRepository.insert(testData.tx2);
1407
+ await txRepository.insert(testData.tx3); // status not on the list
1408
+
1409
+ const txs = await txPot.getTxsQuery([
1410
+ {
1411
+ status: {
1412
+ not: true,
1413
+ value: [
1414
+ testData.tx1.status as TransactionStatus,
1415
+ testData.tx2.status as TransactionStatus,
1416
+ ],
1417
+ },
1418
+ },
1419
+ ]);
1420
+ expect(txs.length).toEqual(1);
1421
+ expect(txs[0].txId).toEqual(testData.tx3.txId);
1422
+ });
1423
+
1424
+ /**
1425
+ * @target TxPot.getTxsQuery should return txs filtered by failedInSign
1426
+ * @dependencies
1427
+ * - database
1428
+ * @scenario
1429
+ * - insert 3 txs with different failedInSign
1430
+ * - run test
1431
+ * - check returned value
1432
+ * @expected
1433
+ * - should return 2 filtered txs
1434
+ */
1435
+ it('should return txs filtered by failedInSign', async () => {
1436
+ await txRepository.insert(testData.tx1);
1437
+ await txRepository.insert(testData.tx2);
1438
+ await txRepository.insert(testData.tx3); // different failedInSign
1439
+
1440
+ const txs = await txPot.getTxsQuery([
1441
+ { failedInSign: testData.tx1.failedInSign },
1442
+ ]);
1443
+ expect(txs.length).toEqual(2);
1444
+ expect(txs[0].txId).toEqual(testData.tx1.txId);
1445
+ expect(txs[1].txId).toEqual(testData.tx2.txId);
1446
+ });
1447
+
1448
+ /**
1449
+ * @target TxPot.getTxsQuery should return txs filtered by extra
1450
+ * @dependencies
1451
+ * - database
1452
+ * @scenario
1453
+ * - insert 2 txs with different extra
1454
+ * - run test
1455
+ * - check returned value
1456
+ * @expected
1457
+ * - should return filtered tx
1458
+ */
1459
+ it('should return txs filtered by extra', async () => {
1460
+ await txRepository.insert(testData.tx1); // different extra
1461
+ await txRepository.insert(testData.tx3);
1462
+
1463
+ const txs = await txPot.getTxsQuery([{ extra: testData.tx3.extra }]);
1464
+ expect(txs.length).toEqual(1);
1465
+ expect(txs[0].txId).toEqual(testData.tx3.txId);
1466
+ });
1467
+
1468
+ /**
1469
+ * @target TxPot.getTxsQuery should return txs filtered by list of extra
1470
+ * @dependencies
1471
+ * - database
1472
+ * @scenario
1473
+ * - insert 3 txs with different list of extra
1474
+ * - run test
1475
+ * - check returned value
1476
+ * @expected
1477
+ * - should return 2 filtered txs
1478
+ */
1479
+ it('should return txs filtered by list of extra', async () => {
1480
+ await txRepository.insert(testData.tx1); // extra not on the list
1481
+ await txRepository.insert(testData.tx3);
1482
+ await txRepository.insert(testData.tx4);
1483
+
1484
+ const txs = await txPot.getTxsQuery([
1485
+ { extra: [testData.tx3.extra!, testData.tx4.extra!] },
1486
+ ]);
1487
+ expect(txs.length).toEqual(2);
1488
+ expect(txs[0].txId).toEqual(testData.tx3.txId);
1489
+ expect(txs[1].txId).toEqual(testData.tx4.txId);
1490
+ });
1491
+
1492
+ /**
1493
+ * @target TxPot.getTxsQuery should combine two filter options successfully
1494
+ * @dependencies
1495
+ * - database
1496
+ * @scenario
1497
+ * - insert 3 txs with different txIds and chains
1498
+ * - run test
1499
+ * - check returned value
1500
+ * @expected
1501
+ * - should return 2 filtered txs
1502
+ */
1503
+ it('should combine two filter options successfully', async () => {
1504
+ await txRepository.insert(testData.tx1);
1505
+ await txRepository.insert(testData.tx2);
1506
+ await txRepository.insert(testData.tx3);
1507
+
1508
+ const txs = await txPot.getTxsQuery([
1509
+ { txId: testData.tx1.txId },
1510
+ { chain: testData.tx1.chain },
1511
+ ]);
1512
+ expect(txs.length).toEqual(2);
1513
+ expect(txs[0].txId).toEqual(testData.tx1.txId);
1514
+ expect(txs[1].txId).toEqual(testData.tx2.txId);
1515
+ });
1516
+ });
1517
+ });