@rosen-bridge/abstract-extractor 0.2.0 → 0.3.1

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 (43) hide show
  1. package/CHANGELOG.md +17 -0
  2. package/dist/ergo/AbstractErgoExtractor.d.ts +3 -5
  3. package/dist/ergo/AbstractErgoExtractor.d.ts.map +1 -1
  4. package/dist/ergo/AbstractErgoExtractor.js +3 -3
  5. package/dist/ergo/AbstractErgoExtractorAction.d.ts +4 -3
  6. package/dist/ergo/AbstractErgoExtractorAction.d.ts.map +1 -1
  7. package/dist/ergo/AbstractErgoExtractorAction.js +1 -1
  8. package/dist/ergo/initializable/AbstractInitializable.d.ts +25 -20
  9. package/dist/ergo/initializable/AbstractInitializable.d.ts.map +1 -1
  10. package/dist/ergo/initializable/AbstractInitializable.js +126 -47
  11. package/dist/ergo/initializable/index.d.ts +0 -2
  12. package/dist/ergo/initializable/index.d.ts.map +1 -1
  13. package/dist/ergo/initializable/index.js +1 -3
  14. package/dist/ergo/interfaces.d.ts +5 -6
  15. package/dist/ergo/interfaces.d.ts.map +1 -1
  16. package/dist/ergo/interfaces.js +1 -1
  17. package/dist/ergo/network/ExplorerNetwork.d.ts +33 -4
  18. package/dist/ergo/network/ExplorerNetwork.d.ts.map +1 -1
  19. package/dist/ergo/network/ExplorerNetwork.js +88 -1
  20. package/dist/ergo/network/NodeNetwork.d.ts +18 -3
  21. package/dist/ergo/network/NodeNetwork.d.ts.map +1 -1
  22. package/dist/ergo/network/NodeNetwork.js +43 -1
  23. package/dist/tsconfig.tsbuildinfo +1 -1
  24. package/lib/ergo/AbstractErgoExtractor.ts +6 -15
  25. package/lib/ergo/AbstractErgoExtractorAction.ts +4 -2
  26. package/lib/ergo/initializable/AbstractInitializable.ts +163 -82
  27. package/lib/ergo/initializable/index.ts +0 -2
  28. package/lib/ergo/interfaces.ts +6 -6
  29. package/lib/ergo/network/ExplorerNetwork.ts +119 -4
  30. package/lib/ergo/network/NodeNetwork.ts +55 -2
  31. package/package.json +2 -1
  32. package/tests/AbstractExtractor.mock.ts +2 -4
  33. package/tests/AbstractExtractor.spec.ts +2 -6
  34. package/tests/initializable/AbstractInitializable.mock.ts +1 -5
  35. package/tests/initializable/AbstractInitializable.spec.ts +282 -174
  36. package/tests/initializable/testData.ts +219 -0
  37. package/tests/network/ExplorerNetwork.spec.ts +46 -2
  38. package/tests/network/NodeNetwork.spec.ts +21 -1
  39. package/tests/network/testData.ts +483 -0
  40. package/tests/testData.ts +0 -2
  41. package/tsconfig.build.tsbuildinfo +1 -1
  42. package/lib/ergo/initializable/InitializableByAddress.ts +0 -43
  43. package/lib/ergo/initializable/InitializableByToken.ts +0 -43
@@ -21,11 +21,7 @@ export class MockedInitializableErgoExtractor extends AbstractInitializableErgoE
21
21
  return Promise.resolve({ boxes: ergoBoxes, hasNextBatch: true });
22
22
  };
23
23
 
24
- extractBoxData = (
25
- box: V1.OutputInfo | OutputBox,
26
- blockId: string,
27
- height: number
28
- ) => {
24
+ extractBoxData = (box: V1.OutputInfo | OutputBox) => {
29
25
  return undefined;
30
26
  };
31
27
  }
@@ -1,247 +1,355 @@
1
- import { describe, it, expect, vitest } from 'vitest';
1
+ import { describe, expect, it, vi, vitest } from 'vitest';
2
2
 
3
+ import {
4
+ ErgoNetworkType,
5
+ NodeNetwork,
6
+ ErgoExtractedData,
7
+ ExplorerNetwork,
8
+ } from '../../lib';
3
9
  import { MockedInitializableErgoExtractor } from './AbstractInitializable.mock';
4
- import { ergoBoxes, extractedData } from './testData';
5
- import { OutputBox, ErgoExtractedData } from '../../lib';
10
+ import { transactionBatch } from './testData';
11
+ import { RETRIAL_COUNT } from '../../lib/constants';
6
12
  import { AbstractInitializableErgoExtractorAction } from '../../lib/ergo/initializable';
7
13
 
8
- describe('AbstractInitializableErgoExtractorAction', () => {
9
- describe('fetchDataWithOffsetLimit', () => {
14
+ describe('AbstractInitializableErgoExtractor', () => {
15
+ describe('getTotalTxCount', () => {
10
16
  /**
11
- * @target fetchDataWithOffsetLimit should extract unspent box data in the initial block
17
+ * @target getTotalTxCount should return the total tx count
12
18
  * @dependencies
19
+ * - explorer network
13
20
  * @scenario
14
21
  * - mock extractor
15
- * - mock `hasData` to return true for an unspent box in the init block
16
- * - spy `extractData`
17
- * - run test (call `fetchDataWithOffsetLimit`)
22
+ * - mock `getAddressTransactionsWithOffsetLimit` in node network
23
+ * - run test (call `getTotalTxCount`)
18
24
  * @expected
19
- * - to call `extractData` with unspent box information
20
- * - return one extracted element (the unspent box) and api total
25
+ * - return one total tx count
21
26
  */
22
- it('should extract unspent box data in the initial block', async () => {
23
- const extractor = new MockedInitializableErgoExtractor();
24
- const unspentBox = ergoBoxes[0];
25
- extractor.hasData = (box: OutputBox) => {
26
- if (box.boxId == unspentBox.boxId) return true;
27
- return false;
28
- };
29
- const extractSpy = vitest.fn().mockReturnValue(extractedData);
30
- extractor.extractBoxData = extractSpy;
31
- const txBlockSpy = vitest.fn();
32
- extractor.getTxBlock = txBlockSpy;
33
- const data = await extractor.fetchDataWithOffsetLimit(1252681, 0, 2);
34
- expect(extractSpy).toHaveBeenCalledWith(
35
- unspentBox,
36
- unspentBox.blockId,
37
- unspentBox.inclusionHeight
27
+ it('should return the total tx count', async () => {
28
+ const extractor = new MockedInitializableErgoExtractor(
29
+ ErgoNetworkType.Node,
30
+ 'node_url',
31
+ 'address'
38
32
  );
39
- expect(txBlockSpy).not.toHaveBeenCalled();
40
- expect(data).toEqual({
41
- extractedBoxes: [
42
- { ...extractedData, spendBlock: undefined, spendHeight: undefined },
43
- ],
44
- hasNextBatch: true,
33
+ (
34
+ extractor['network'] as NodeNetwork
35
+ ).getAddressTransactionsWithOffsetLimit = vitest.fn().mockResolvedValue({
36
+ items: [],
37
+ total: 196704,
45
38
  });
39
+ const totalTxCount = await extractor['getTotalTxCount']();
40
+ expect(totalTxCount).toEqual(196704);
46
41
  });
42
+ });
47
43
 
44
+ describe('processTransactionBatch', () => {
48
45
  /**
49
- * @target fetchDataWithOffsetLimit should filter boxes created below the initial block
46
+ * @target processTransactionBatch should group transactions and process them in correct order
50
47
  * @dependencies
51
48
  * @scenario
52
49
  * - mock extractor
53
- * - mock `hasData` to return true for all boxes
54
- * - spy `extractData`
55
- * - run test (call `fetchDataWithOffsetLimit`)
50
+ * - spy `processTransactions`
51
+ * - run test (call `processTransactionBatch`)
56
52
  * @expected
57
- * - to call `extractData` with spent box information
58
- * - return one extracted element created bellow the initial block
53
+ * - to group txs in two blocks
54
+ * - to process each blocks transaction separately
59
55
  */
60
- it('should filter boxes created below the initial block', async () => {
61
- const extractor = new MockedInitializableErgoExtractor();
62
- const spentBox = ergoBoxes[1];
63
- extractor.hasData = (box: OutputBox) => true;
64
- const extractSpy = vitest.fn().mockReturnValue(extractedData);
65
- extractor.extractBoxData = extractSpy;
66
- const data = await extractor.fetchDataWithOffsetLimit(1252678, 0, 2);
67
- expect(extractSpy).toHaveBeenCalledWith(
68
- spentBox,
69
- spentBox.blockId,
70
- spentBox.inclusionHeight
56
+ it('should group transactions and process them in correct order', async () => {
57
+ const extractor = new MockedInitializableErgoExtractor(
58
+ ErgoNetworkType.Node,
59
+ 'node_url',
60
+ 'address'
71
61
  );
72
- expect(data).toEqual({
73
- extractedBoxes: [
74
- { ...extractedData, spendBlock: undefined, spendHeight: undefined },
75
- ],
76
- hasNextBatch: true,
62
+ const processSpy = vitest.fn();
63
+ extractor.processTransactions = processSpy;
64
+ await extractor['processTransactionBatch'](transactionBatch);
65
+ expect(processSpy).toBeCalledTimes(2);
66
+ expect(processSpy).toHaveBeenCalledWith([transactionBatch[2]], {
67
+ hash: '92eaff11a4a1c29b8654473258f9d837e9ce99fc0a8a4f27c484d5871afcde64',
68
+ height: 1320698,
77
69
  });
70
+ expect(processSpy).toHaveBeenLastCalledWith(
71
+ transactionBatch.slice(0, 2),
72
+ {
73
+ hash: 'b861e2134821fcf1fcdad7e6edd56c4e4495b42b2fd72762a4c79ed1db78b44b',
74
+ height: 1320705,
75
+ }
76
+ );
78
77
  });
78
+ });
79
79
 
80
+ describe('initWithRetrial', () => {
80
81
  /**
81
- * @target fetchDataWithOffsetLimit should extract spent box data created and spent below the initial block
82
+ * @target initWithRetrial should not run the init job when its not set
82
83
  * @dependencies
84
+ * - database
83
85
  * @scenario
84
- * - mock extractor
85
- * - mock `hasData` to return true for an spent box created below the init block
86
- * - spy `extractData`
87
- * - run test (call `fetchDataWithOffsetLimit`)
86
+ * - mock extractor with false initialization
87
+ * - mock `removeAllData` in database actions
88
+ * - mock init job
89
+ * - run test (call `initWithRetrial`)
88
90
  * @expected
89
- * - to call `extractData` with spent box information
90
- * - return one extracted element with spending information
91
+ * - not to remove database old data
92
+ * - not to run the init job
91
93
  */
92
- it('should extract spent box data created and spent below the initial block', async () => {
93
- const extractor = new MockedInitializableErgoExtractor();
94
- const spentBox = ergoBoxes[1];
95
- extractor.hasData = (box: OutputBox) => {
96
- if (box.boxId == spentBox.boxId) return true;
97
- return false;
98
- };
99
- const extractSpy = vitest.fn().mockReturnValue(extractedData);
100
- extractor.extractBoxData = extractSpy;
101
- const data = await extractor.fetchDataWithOffsetLimit(1252681, 0, 2);
102
- expect(extractSpy).toHaveBeenCalledWith(
103
- spentBox,
104
- spentBox.blockId,
105
- spentBox.inclusionHeight
94
+ it('should not run the init job when its not set', async () => {
95
+ const extractor = new MockedInitializableErgoExtractor(
96
+ ErgoNetworkType.Node,
97
+ 'node_url',
98
+ 'address',
99
+ undefined,
100
+ false
106
101
  );
107
- expect(data).toEqual({
108
- extractedBoxes: [
109
- {
110
- ...extractedData,
111
- spendBlock:
112
- '9239ebca7b3b10701895e491a2213da4e07a37abc413d05434a5fab04993a19d',
113
- spendHeight: 1252680,
114
- spendTxId:
115
- 'b08fa920a6907d0e76eb0ad754439f1a26fa112b39f4de489620e4e6241930b7',
116
- spendIndex: 1,
117
- },
118
- ],
119
- hasNextBatch: true,
120
- });
102
+ const removeSpy = vitest.fn();
103
+ extractor['actions'] = {
104
+ removeAllData: removeSpy,
105
+ } as unknown as AbstractInitializableErgoExtractorAction<ErgoExtractedData>;
106
+ const initSpy = vitest.fn();
107
+ await extractor['initWithRetrial'](initSpy);
108
+ expect(removeSpy).not.toHaveBeenCalled();
109
+ expect(initSpy).not.toHaveBeenCalled();
121
110
  });
122
111
 
123
112
  /**
124
- * @target fetchDataWithOffsetLimit should extract spent box data created below the initial block but spent later
113
+ * @target initWithRetrial should call init job once
125
114
  * @dependencies
115
+ * - database
126
116
  * @scenario
127
117
  * - mock extractor
128
- * - mock `hasData` to return true for an spent box created below the init block
129
- * - spy `extractData`
130
- * - run test (call `fetchDataWithOffsetLimit`)
118
+ * - mock `removeAllData` in database actions
119
+ * - mock init job
120
+ * - run test (call `initWithRetrial`)
131
121
  * @expected
132
- * - to call `extractData` with spent box information
133
- * - return one extracted element without spending information
122
+ * - to remove database old data once
123
+ * - call init job once
134
124
  */
135
- it('should extract spent box data created below the initial block but spent later', async () => {
136
- const extractor = new MockedInitializableErgoExtractor();
137
- const spentBox = ergoBoxes[1];
138
- extractor.hasData = (box: OutputBox) => {
139
- if (box.boxId == spentBox.boxId) return true;
140
- return false;
141
- };
142
- const extractSpy = vitest.fn().mockReturnValue(extractedData);
143
- extractor.extractBoxData = extractSpy;
144
- const data = await extractor.fetchDataWithOffsetLimit(1252678, 0, 2);
145
- expect(extractSpy).toHaveBeenCalledWith(
146
- spentBox,
147
- spentBox.blockId,
148
- spentBox.inclusionHeight
125
+ it('should call init job once', async () => {
126
+ const extractor = new MockedInitializableErgoExtractor(
127
+ ErgoNetworkType.Node,
128
+ 'node_url',
129
+ 'address'
149
130
  );
150
- expect(data).toEqual({
151
- extractedBoxes: [
152
- { ...extractedData, spendBlock: undefined, spendHeight: undefined },
153
- ],
154
- hasNextBatch: true,
155
- });
131
+ const removeSpy = vitest.fn();
132
+ extractor['actions'] = {
133
+ removeAllData: removeSpy,
134
+ } as unknown as AbstractInitializableErgoExtractorAction<ErgoExtractedData>;
135
+ const initSpy = vitest.fn();
136
+ await extractor['initWithRetrial'](initSpy);
137
+ expect(removeSpy).toHaveBeenCalledOnce();
138
+ expect(initSpy).toHaveBeenCalledOnce();
156
139
  });
157
- });
158
140
 
159
- describe('initializeBoxes', () => {
160
141
  /**
161
- * @target initializeBoxes should remove all data and reinsert the newly extracted data
142
+ * @target initWithRetrial should call init job RETRIAL_COUNT number of times
162
143
  * @dependencies
144
+ * - database
163
145
  * @scenario
164
146
  * - mock extractor
165
- * - spy database functions
166
- * - mock `fetchDataWithOffsetLimit` to return one extracted data
167
- * - run test (call `initializeBoxes`)
147
+ * - mock `removeAllData` in database actions
148
+ * - mock init job to throw error
149
+ * - run test (call `initWithRetrial`)
168
150
  * @expected
169
- * - to call removeAllData once
170
- * - to call insertBoxes once with extracted data
151
+ * - to remove database old data once
152
+ * - call init job RETRIAL_COUNT number of times
153
+ * - to throw error after trials
171
154
  */
172
- it('should remove all data and reinsert the newly extracted data', async () => {
173
- const extractor = new MockedInitializableErgoExtractor();
155
+ it('should call init job RETRIAL_COUNT number of times', async () => {
156
+ const extractor = new MockedInitializableErgoExtractor(
157
+ ErgoNetworkType.Node,
158
+ 'node_url',
159
+ 'address'
160
+ );
174
161
  const removeSpy = vitest.fn();
175
- const insertSpy = vitest.fn().mockResolvedValue(true);
176
162
  extractor['actions'] = {
177
163
  removeAllData: removeSpy,
178
- insertBoxes: insertSpy,
179
164
  } as unknown as AbstractInitializableErgoExtractorAction<ErgoExtractedData>;
180
- extractor.fetchDataWithOffsetLimit = vitest.fn().mockResolvedValue({
181
- extractedBoxes: [extractedData],
182
- hasNextBatch: false,
183
- });
165
+ const initSpy = vitest.fn().mockRejectedValue(0);
166
+ await expect(
167
+ async () => await extractor['initWithRetrial'](initSpy)
168
+ ).rejects.toThrowError();
169
+ expect(removeSpy).toHaveBeenCalledOnce();
170
+ expect(initSpy).toHaveBeenCalledTimes(RETRIAL_COUNT);
171
+ });
172
+ });
184
173
 
185
- await extractor.initializeBoxes({ height: 100, hash: 'hash' });
186
- expect(removeSpy).toBeCalledTimes(1);
187
- expect(insertSpy).toBeCalledWith([extractedData], 'Test');
174
+ describe('initializeWithNode', () => {
175
+ /**
176
+ * @target initializeWithNode should process all transactions bellow the init height twice
177
+ * @dependencies
178
+ * - node network
179
+ * @scenario
180
+ * - mock extractor
181
+ * - mock `getAddressTransactionsWithOffsetLimit` in node network
182
+ * - mock `initWithRetrial` to run the job once
183
+ * - mock `getTotalTxCount` to return 3
184
+ * - run test (call `initializeWithNode`)
185
+ * @expected
186
+ * - to filter the transactions and process the txs bellow the init height
187
+ * - to process all filtered transactions twice
188
+ */
189
+ it('should process all transactions bellow the init height twice', async () => {
190
+ const extractor = new MockedInitializableErgoExtractor(
191
+ ErgoNetworkType.Node,
192
+ 'node_url',
193
+ 'address'
194
+ );
195
+ (
196
+ extractor['network'] as NodeNetwork
197
+ ).getAddressTransactionsWithOffsetLimit = vitest.fn().mockResolvedValue({
198
+ items: transactionBatch,
199
+ total: 3,
200
+ });
201
+ extractor['initWithRetrial'] = async (job: () => Promise<void>) => job();
202
+ extractor['getTotalTxCount'] = async () => 3;
203
+ const processSpy = vitest.fn();
204
+ extractor['processTransactionBatch'] = processSpy;
205
+ await extractor['initializeWithNode']({ hash: 'hash', height: 1320698 });
206
+ expect(processSpy).toBeCalledTimes(2);
207
+ expect(processSpy).toBeCalledWith([transactionBatch[2]]);
188
208
  });
189
209
 
190
210
  /**
191
- * @target initializeBoxes should iterate on api when data count is more than api limit
211
+ * @target initializeWithNode should throw error when total transaction count changes
192
212
  * @dependencies
213
+ * - node network
193
214
  * @scenario
194
215
  * - mock extractor
195
- * - spy database functions
196
- * - mock `fetchDataWithOffsetLimit` to return total number 101 (greater than API_LIMIT)
197
- * - run test (call `initializeBoxes`)
216
+ * - mock `getAddressTransactionsWithOffsetLimit` in node network
217
+ * - mock `initWithRetrial` to run the job once
218
+ * - mock `getTotalTxCount` to return 3 then 4
219
+ * - run test (call `initializeWithNode`)
198
220
  * @expected
199
- * - to call removeAllData once
200
- * - to call insertBoxes twice
221
+ * - to throw error when the number of transactions change during initialization
201
222
  */
202
- it('should iterate on api when data count is more than api limit', async () => {
203
- const extractor = new MockedInitializableErgoExtractor();
204
- const removeSpy = vitest.fn();
205
- const insertSpy = vitest.fn().mockResolvedValue(true);
206
- extractor['actions'] = {
207
- removeAllData: removeSpy,
208
- insertBoxes: insertSpy,
209
- } as unknown as AbstractInitializableErgoExtractorAction<ErgoExtractedData>;
210
- extractor.fetchDataWithOffsetLimit = vitest
223
+ it('should throw error when total transaction count changes', async () => {
224
+ const extractor = new MockedInitializableErgoExtractor(
225
+ ErgoNetworkType.Node,
226
+ 'node_url',
227
+ 'address'
228
+ );
229
+ (
230
+ extractor['network'] as NodeNetwork
231
+ ).getAddressTransactionsWithOffsetLimit = vitest.fn().mockResolvedValue({
232
+ items: transactionBatch,
233
+ total: 3,
234
+ });
235
+ extractor['initWithRetrial'] = async (job: () => Promise<void>) => job();
236
+ extractor['getTotalTxCount'] = vitest
211
237
  .fn()
212
- .mockResolvedValueOnce({
213
- extractedBoxes: [extractedData],
214
- hasNextBatch: true,
215
- })
216
- .mockResolvedValueOnce({ extractedBoxes: [], hasNextBatch: false });
217
-
218
- await extractor.initializeBoxes({ height: 100, hash: 'hash' });
219
- expect(removeSpy).toBeCalledTimes(1);
220
- expect(insertSpy).toBeCalledTimes(2);
238
+ .mockResolvedValueOnce(3)
239
+ .mockResolvedValue(4);
240
+ extractor['processTransactionBatch'] = vitest.fn();
241
+ expect(() =>
242
+ extractor['initializeWithNode']({ hash: 'hash', height: 1320698 })
243
+ ).rejects.toThrowError();
221
244
  });
245
+ });
222
246
 
247
+ describe('initializeWithExplorer', () => {
223
248
  /**
224
- * @target initializeBoxes should not run initialization when initialize flag is false
249
+ * @target initializeWithExplorer should limit the height and process
250
+ * transactions when they are less than the API_LIMIT
225
251
  * @dependencies
252
+ * - explorer network
226
253
  * @scenario
227
254
  * - mock extractor
228
- * - spy database functions
229
- * - run test (call `initializeBoxes`)
255
+ * - mock `initWithRetrial` to run the job once
256
+ * - mock `getAddressTransactionsWithHeight` in explorer network
257
+ * return API_LIMIT transactions in first call (to limit the height range)
258
+ * return 0 transactions in second call (to pass a range without process)
259
+ * return transactionBatch in last call (to process the transactions when they are less than API_LIMIT)
260
+ * - run test (call `initializeWithExplorer`)
230
261
  * @expected
231
- * - not to call any functions
262
+ * - to limit the height when the transaction count is equal to API_LIMIT
263
+ * - to cover all height ranges
264
+ * - to process transactions when they are less than API_LIMIT
232
265
  */
233
- it('should not run initialization when initialize flag is false', async () => {
234
- const extractor = new MockedInitializableErgoExtractor(false);
235
- const removeSpy = vitest.fn();
236
- const insertSpy = vitest.fn();
237
- extractor['actions'] = {
238
- removeAllData: removeSpy,
239
- insertBoxes: insertSpy,
240
- } as unknown as AbstractInitializableErgoExtractorAction<ErgoExtractedData>;
266
+ it(`should limit the height and process transactions when they are less than
267
+ the API_LIMIT`, async () => {
268
+ const extractor = new MockedInitializableErgoExtractor(
269
+ ErgoNetworkType.Explorer,
270
+ 'explorer_url',
271
+ 'address'
272
+ );
273
+ extractor['initWithRetrial'] = async (job: () => Promise<void>) => job();
274
+ const addressTxSpy = vitest
275
+ .fn()
276
+ .mockResolvedValueOnce(new Array(100).fill(transactionBatch[0]))
277
+ .mockResolvedValueOnce([])
278
+ .mockResolvedValueOnce(transactionBatch);
279
+ (
280
+ extractor['network'] as ExplorerNetwork
281
+ ).getAddressTransactionsWithHeight = addressTxSpy;
282
+ const processSpy = vitest.fn();
283
+ extractor['processTransactionBatch'] = processSpy;
284
+ await extractor['initializeWithExplorer']({
285
+ hash: 'hash',
286
+ height: 1320800,
287
+ });
288
+ expect(addressTxSpy).toHaveBeenCalledWith('address', 0, 1320800);
289
+ expect(addressTxSpy).toHaveBeenCalledWith('address', 0, 660400);
290
+ expect(addressTxSpy).toHaveBeenLastCalledWith('address', 660401, 1320800);
291
+ expect(processSpy).toHaveBeenCalledTimes(1);
292
+ expect(processSpy).toHaveBeenCalledWith(transactionBatch);
293
+ });
241
294
 
242
- await extractor.initializeBoxes({ height: 100, hash: 'hash' });
243
- expect(removeSpy).not.toBeCalled();
244
- expect(insertSpy).not.toBeCalled();
295
+ /**
296
+ * @target initializeWithExplorer should process all block transactions when
297
+ * the number of transactions in the block is more than API_LIMIT
298
+ * @dependencies
299
+ * - explorer network
300
+ * @scenario
301
+ * - mock extractor
302
+ * - mock `initWithRetrial` to run the job once
303
+ * - mock `getAddressTransactionsWithHeight` to return 100 transactions in one block
304
+ * - spy all other functions to check the calls
305
+ * - run test (call `initializeWithExplorer`)
306
+ * @expected
307
+ * - to process all transactions in block 1320000
308
+ * - not to stuck at a large block and complete the process
309
+ */
310
+ it(`should process all block transactions when the number of transactions in
311
+ the block is more than API_LIMIT`, async () => {
312
+ // mock extractor
313
+ const extractor = new MockedInitializableErgoExtractor(
314
+ ErgoNetworkType.Explorer,
315
+ 'explorer_url',
316
+ 'address'
317
+ );
318
+ // mock `initWithRetrial` to run the job once
319
+ extractor['initWithRetrial'] = async (job: () => Promise<void>) => job();
320
+ // mock `getAddressTransactionsWithHeight` to return 100 transactions in one block
321
+ const exNetwork = extractor['network'] as ExplorerNetwork;
322
+ const addressTxSpy = vitest
323
+ .fn()
324
+ .mockImplementation(
325
+ (address: string, fromHeight: number, toHeight: number) => {
326
+ if (fromHeight <= 1320000 && toHeight >= 1320000)
327
+ return new Array(100).fill(transactionBatch[0]);
328
+ return [];
329
+ }
330
+ );
331
+ exNetwork.getAddressTransactionsWithHeight = addressTxSpy;
332
+ // spy all other functions to check the calls
333
+ const blockIdSpy = vi.fn().mockResolvedValue('blockId');
334
+ const blockTxsSpy = vi.fn().mockResolvedValue([]);
335
+ exNetwork.getBlockIdAtHeight = blockIdSpy;
336
+ exNetwork.getBlockTxs = blockTxsSpy;
337
+ const processSpy = vitest.fn();
338
+ const processBatchSpy = vitest.fn();
339
+ extractor.processTransactions = processSpy;
340
+ extractor['processTransactionBatch'] = processBatchSpy;
341
+ // run test (call `initializeWithExplorer`)
342
+ await extractor['initializeWithExplorer']({
343
+ hash: 'hash',
344
+ height: 1320800,
345
+ });
346
+ expect(blockIdSpy).toHaveBeenCalledWith(1320000);
347
+ expect(blockTxsSpy).toHaveBeenCalledWith('blockId');
348
+ expect(processSpy).toHaveBeenCalledWith([], {
349
+ hash: 'blockId',
350
+ height: 1320000,
351
+ });
352
+ expect(processBatchSpy).not.toBeCalled();
245
353
  });
246
354
  });
247
355
  });