@rosen-bridge/abstract-extractor 0.3.1 → 1.0.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.
- package/CHANGELOG.md +13 -0
- package/dist/ergo/AbstractErgoExtractor.d.ts +30 -5
- package/dist/ergo/AbstractErgoExtractor.d.ts.map +1 -1
- package/dist/ergo/AbstractErgoExtractor.js +72 -7
- package/dist/ergo/AbstractErgoExtractorAction.d.ts +37 -10
- package/dist/ergo/AbstractErgoExtractorAction.d.ts.map +1 -1
- package/dist/ergo/AbstractErgoExtractorAction.js +134 -1
- package/dist/ergo/AbstractErgoExtractorEntity.d.ts +11 -0
- package/dist/ergo/AbstractErgoExtractorEntity.d.ts.map +1 -0
- package/dist/ergo/AbstractErgoExtractorEntity.js +57 -0
- package/dist/ergo/index.d.ts +1 -0
- package/dist/ergo/index.d.ts.map +1 -1
- package/dist/ergo/index.js +2 -1
- package/dist/ergo/initializable/AbstractInitializable.d.ts +4 -3
- package/dist/ergo/initializable/AbstractInitializable.d.ts.map +1 -1
- package/dist/ergo/initializable/AbstractInitializable.js +9 -5
- package/dist/ergo/initializable/AbstractInitializableAction.d.ts +7 -2
- package/dist/ergo/initializable/AbstractInitializableAction.d.ts.map +1 -1
- package/dist/ergo/initializable/AbstractInitializableAction.js +11 -1
- package/dist/ergo/interfaces.d.ts +20 -1
- package/dist/ergo/interfaces.d.ts.map +1 -1
- package/dist/ergo/interfaces.js +8 -1
- package/lib/ergo/AbstractErgoExtractor.ts +105 -12
- package/lib/ergo/AbstractErgoExtractorAction.ts +185 -18
- package/lib/ergo/AbstractErgoExtractorEntity.ts +28 -0
- package/lib/ergo/index.ts +1 -0
- package/lib/ergo/initializable/AbstractInitializable.ts +22 -9
- package/lib/ergo/initializable/AbstractInitializableAction.ts +19 -3
- package/lib/ergo/interfaces.ts +24 -1
- package/package.json +6 -2
- package/tests/{AbstractExtractor.mock.ts → AbstractErgoExtractor.mock.ts} +11 -4
- package/tests/AbstractErgoExtractor.spec.ts +281 -0
- package/tests/AbstractErgoExtractorAction.mock.ts +45 -0
- package/tests/AbstractErgoExtractorAction.spec.ts +269 -0
- package/tests/initializable/AbstractInitializable.mock.ts +14 -3
- package/tests/initializable/AbstractInitializable.spec.ts +37 -5
- package/tests/initializable/AbstractInitializableAction.mock.ts +45 -0
- package/tests/initializable/AbstractInitializableAction.spec.ts +65 -0
- package/tests/testData.ts +38 -0
- package/tests/testUtils.ts +22 -0
- package/tsconfig.build.tsbuildinfo +1 -1
- package/dist/ergo/initializable/InitializableByAddress.d.ts +0 -19
- package/dist/ergo/initializable/InitializableByAddress.d.ts.map +0 -1
- package/dist/ergo/initializable/InitializableByAddress.js +0 -30
- package/dist/ergo/initializable/InitializableByToken.d.ts +0 -19
- package/dist/ergo/initializable/InitializableByToken.d.ts.map +0 -1
- package/dist/ergo/initializable/InitializableByToken.js +0 -30
- package/dist/tsconfig.tsbuildinfo +0 -1
- package/tests/AbstractExtractor.spec.ts +0 -102
|
@@ -0,0 +1,269 @@
|
|
|
1
|
+
import { DataSource, Repository } from 'typeorm';
|
|
2
|
+
import { describe, it, expect, beforeEach } from 'vitest';
|
|
3
|
+
import { pick } from 'lodash-es';
|
|
4
|
+
|
|
5
|
+
import { TestErgoExtractorAction } from './AbstractErgoExtractorAction.mock';
|
|
6
|
+
import { createDatabase, TestEntity } from './testUtils';
|
|
7
|
+
import { block, block2, sampleEntities } from './testData';
|
|
8
|
+
import { SpendInfo } from '../lib';
|
|
9
|
+
|
|
10
|
+
describe('AbstractErgoExtractorAction', () => {
|
|
11
|
+
let dataSource: DataSource;
|
|
12
|
+
let action: TestErgoExtractorAction;
|
|
13
|
+
let repository: Repository<TestEntity>;
|
|
14
|
+
beforeEach(async () => {
|
|
15
|
+
dataSource = await createDatabase();
|
|
16
|
+
action = new TestErgoExtractorAction(dataSource);
|
|
17
|
+
repository = dataSource.getRepository(TestEntity);
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
describe('storeBoxes', () => {
|
|
21
|
+
/**
|
|
22
|
+
* @target storeBoxes should save the passed box entities to database
|
|
23
|
+
* @dependencies
|
|
24
|
+
* @scenario
|
|
25
|
+
* - run test (call `storeBoxes` with 2 new boxes)
|
|
26
|
+
* @expected
|
|
27
|
+
* - to save 2 new entities
|
|
28
|
+
* - to return 2 inserted entity data
|
|
29
|
+
*/
|
|
30
|
+
it(`should save the passed box entities to database`, async () => {
|
|
31
|
+
const result = await action.storeBoxes(
|
|
32
|
+
sampleEntities.slice(0, 2),
|
|
33
|
+
block,
|
|
34
|
+
'extractor1'
|
|
35
|
+
);
|
|
36
|
+
|
|
37
|
+
const [rows, rowsCount] = await repository.findAndCount();
|
|
38
|
+
|
|
39
|
+
expect(rowsCount).toEqual(2);
|
|
40
|
+
expect(rows[0]).toMatchObject({
|
|
41
|
+
...sampleEntities[0],
|
|
42
|
+
extractor: 'extractor1',
|
|
43
|
+
block: block.hash,
|
|
44
|
+
height: block.height,
|
|
45
|
+
spendBlock: null,
|
|
46
|
+
spendHeight: null,
|
|
47
|
+
});
|
|
48
|
+
expect(rows[1]).toMatchObject({
|
|
49
|
+
...sampleEntities[1],
|
|
50
|
+
extractor: 'extractor1',
|
|
51
|
+
block: block.hash,
|
|
52
|
+
height: block.height,
|
|
53
|
+
spendBlock: null,
|
|
54
|
+
spendHeight: null,
|
|
55
|
+
});
|
|
56
|
+
expect(result).toEqual(true);
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* @target storeBoxes should correctly save boxes with different extractors
|
|
61
|
+
* @dependencies
|
|
62
|
+
* @scenario
|
|
63
|
+
* - insert 2 boxes belonging to first-extractor
|
|
64
|
+
* - run test (call `storeBoxes` with same boxes for the second-extractor)
|
|
65
|
+
* @expected
|
|
66
|
+
* - to save 2 new entities belonging to second-extractor
|
|
67
|
+
* - to return 2 new inserted entity data
|
|
68
|
+
*/
|
|
69
|
+
it(`should correctly save boxes with different extractors`, async () => {
|
|
70
|
+
await repository.insert([
|
|
71
|
+
{
|
|
72
|
+
...sampleEntities[0],
|
|
73
|
+
extractor: 'first-extractor',
|
|
74
|
+
block: '1',
|
|
75
|
+
height: 1,
|
|
76
|
+
},
|
|
77
|
+
{
|
|
78
|
+
...sampleEntities[1],
|
|
79
|
+
extractor: 'first-extractor',
|
|
80
|
+
block: '1',
|
|
81
|
+
height: 1,
|
|
82
|
+
},
|
|
83
|
+
]);
|
|
84
|
+
|
|
85
|
+
const result = await action.storeBoxes(
|
|
86
|
+
[sampleEntities[0], sampleEntities[1]],
|
|
87
|
+
block,
|
|
88
|
+
'second-extractor'
|
|
89
|
+
);
|
|
90
|
+
|
|
91
|
+
const [insertedRows] = await repository.findAndCount();
|
|
92
|
+
expect(insertedRows[2]).toMatchObject({
|
|
93
|
+
...sampleEntities[0],
|
|
94
|
+
extractor: 'second-extractor',
|
|
95
|
+
block: 'hash',
|
|
96
|
+
height: block.height,
|
|
97
|
+
spendBlock: null,
|
|
98
|
+
spendHeight: null,
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
expect(insertedRows[3]).toMatchObject({
|
|
102
|
+
...sampleEntities[1],
|
|
103
|
+
extractor: 'second-extractor',
|
|
104
|
+
block: 'hash',
|
|
105
|
+
height: block.height,
|
|
106
|
+
spendBlock: null,
|
|
107
|
+
spendHeight: null,
|
|
108
|
+
});
|
|
109
|
+
expect(result).toEqual(true);
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* @target storeBoxes should update boxes correctly
|
|
114
|
+
* @dependencies
|
|
115
|
+
* @scenario
|
|
116
|
+
* - insert 2 boxes
|
|
117
|
+
* - run test (call `storeBoxes` with the same boxes and updated info)
|
|
118
|
+
* @expected
|
|
119
|
+
* - to update the existing box in database
|
|
120
|
+
* - to return updated box id
|
|
121
|
+
*/
|
|
122
|
+
it(`storeBoxes should update boxes correctly`, async () => {
|
|
123
|
+
await repository.insert([
|
|
124
|
+
{
|
|
125
|
+
...sampleEntities[0],
|
|
126
|
+
extractor: 'extractor',
|
|
127
|
+
block: '1',
|
|
128
|
+
height: 1,
|
|
129
|
+
},
|
|
130
|
+
{
|
|
131
|
+
...sampleEntities[1],
|
|
132
|
+
extractor: 'extractor',
|
|
133
|
+
block: '1',
|
|
134
|
+
height: 1,
|
|
135
|
+
},
|
|
136
|
+
]);
|
|
137
|
+
|
|
138
|
+
const result = await action.storeBoxes(
|
|
139
|
+
[
|
|
140
|
+
{
|
|
141
|
+
...sampleEntities[0],
|
|
142
|
+
serialized: 'updatedBoxSerialized',
|
|
143
|
+
},
|
|
144
|
+
],
|
|
145
|
+
block,
|
|
146
|
+
'extractor'
|
|
147
|
+
);
|
|
148
|
+
const [secondInsertRows, secondInsertRowsCount] =
|
|
149
|
+
await repository.findAndCount();
|
|
150
|
+
expect(secondInsertRowsCount).toEqual(2);
|
|
151
|
+
expect(secondInsertRows[0]).toMatchObject({
|
|
152
|
+
...sampleEntities[0],
|
|
153
|
+
extractor: 'extractor',
|
|
154
|
+
serialized: 'updatedBoxSerialized',
|
|
155
|
+
block: block.hash,
|
|
156
|
+
height: block.height,
|
|
157
|
+
spendBlock: null,
|
|
158
|
+
spendHeight: null,
|
|
159
|
+
});
|
|
160
|
+
expect(result).toEqual(true);
|
|
161
|
+
});
|
|
162
|
+
});
|
|
163
|
+
|
|
164
|
+
describe('spendBoxes', () => {
|
|
165
|
+
/**
|
|
166
|
+
* @target spendBoxes should set spendBlock and spendHeight for a set of boxes
|
|
167
|
+
* @dependencies
|
|
168
|
+
* @scenario
|
|
169
|
+
* - insert two boxes
|
|
170
|
+
* - mock spending information for the first box
|
|
171
|
+
* - run test (call `spendBoxes`)
|
|
172
|
+
* @expected
|
|
173
|
+
* - spend the first box
|
|
174
|
+
* - return boxId and serialized of spent box
|
|
175
|
+
*/
|
|
176
|
+
it(`should set spendBlock and spendHeight for a set of boxes`, async () => {
|
|
177
|
+
await action.storeBoxes(sampleEntities.slice(0, 2), block, 'extractor1');
|
|
178
|
+
|
|
179
|
+
const spendBlock = { ...block, hash: 'spendHash', height: 10006016 };
|
|
180
|
+
const spendInfos: Array<SpendInfo> = [
|
|
181
|
+
{ txId: 'txId', boxId: sampleEntities[0].boxId, index: 0 },
|
|
182
|
+
];
|
|
183
|
+
|
|
184
|
+
const spentBoxIds = await action.spendBoxes(
|
|
185
|
+
spendInfos,
|
|
186
|
+
spendBlock,
|
|
187
|
+
'extractor1'
|
|
188
|
+
);
|
|
189
|
+
|
|
190
|
+
const spentBoxes = await repository.findOneBy({
|
|
191
|
+
boxId: sampleEntities[0].boxId,
|
|
192
|
+
extractor: 'extractor1',
|
|
193
|
+
});
|
|
194
|
+
|
|
195
|
+
expect(spentBoxes).toMatchObject({
|
|
196
|
+
...sampleEntities[0],
|
|
197
|
+
block: block.hash,
|
|
198
|
+
height: block.height,
|
|
199
|
+
extractor: 'extractor1',
|
|
200
|
+
spendBlock: spendBlock.hash,
|
|
201
|
+
spendHeight: spendBlock.height,
|
|
202
|
+
});
|
|
203
|
+
expect(spentBoxIds).toEqual([pick(sampleEntities[0], ['boxId'])]);
|
|
204
|
+
});
|
|
205
|
+
});
|
|
206
|
+
|
|
207
|
+
describe('deleteBlockBoxes', () => {
|
|
208
|
+
/**
|
|
209
|
+
* @target deleteBlockBoxes should delete the boxes created in the specified block
|
|
210
|
+
* @dependencies
|
|
211
|
+
* @scenario
|
|
212
|
+
* - insert four boxes created in two different blocks
|
|
213
|
+
* - run test(call `deleteBlockBoxes` to delete block2)
|
|
214
|
+
* @expected
|
|
215
|
+
* - to delete the two boxes created in block2
|
|
216
|
+
* - to return the deleted entity data
|
|
217
|
+
*/
|
|
218
|
+
it(`should delete the boxes created in the specified block`, async () => {
|
|
219
|
+
await action.storeBoxes(sampleEntities.slice(0, 2), block, 'extractor1');
|
|
220
|
+
await action.storeBoxes(sampleEntities.slice(2), block2, 'extractor1');
|
|
221
|
+
|
|
222
|
+
const result = await action.deleteBlockBoxes(block2.hash, 'extractor1');
|
|
223
|
+
|
|
224
|
+
const [rows, rowsCount] = await repository.findAndCount();
|
|
225
|
+
|
|
226
|
+
expect(rowsCount).toEqual(2);
|
|
227
|
+
expect(rows.map((row) => row.boxId)).not.toContain(
|
|
228
|
+
sampleEntities.slice(2).map((box) => box.boxId)
|
|
229
|
+
);
|
|
230
|
+
expect(result).toEqual({
|
|
231
|
+
deletedData: action.convertEntityToData(
|
|
232
|
+
sampleEntities.slice(2) as TestEntity[]
|
|
233
|
+
),
|
|
234
|
+
updatedData: [],
|
|
235
|
+
});
|
|
236
|
+
});
|
|
237
|
+
|
|
238
|
+
/**
|
|
239
|
+
* @target deleteBlockBoxes should update the boxes spent in the specified block
|
|
240
|
+
* @dependencies
|
|
241
|
+
* @scenario
|
|
242
|
+
* - insert four boxes created in a block
|
|
243
|
+
* - spend one of the in the block2
|
|
244
|
+
* - run test(call `deleteBlockBoxes` to delete block2)
|
|
245
|
+
* @expected
|
|
246
|
+
* - to update the box spent in block2
|
|
247
|
+
* - to return the updated entity boxId and serialized
|
|
248
|
+
*/
|
|
249
|
+
it(`should update the boxes spent in the specified block`, async () => {
|
|
250
|
+
await action.storeBoxes(sampleEntities, block, 'extractor1');
|
|
251
|
+
const spendInfos: Array<SpendInfo> = [
|
|
252
|
+
{ txId: 'txId', boxId: sampleEntities[0].boxId, index: 0 },
|
|
253
|
+
];
|
|
254
|
+
await action.spendBoxes(spendInfos, block2, 'extractor1');
|
|
255
|
+
const result = await action.deleteBlockBoxes(block2.hash, 'extractor1');
|
|
256
|
+
|
|
257
|
+
const [rows, rowsCount] = await repository.findAndCount();
|
|
258
|
+
|
|
259
|
+
expect(rowsCount).toEqual(4);
|
|
260
|
+
expect(rows.map((row) => row.boxId)).not.toContain(
|
|
261
|
+
sampleEntities.slice(2).map((box) => box.boxId)
|
|
262
|
+
);
|
|
263
|
+
expect(result).toEqual({
|
|
264
|
+
deletedData: [],
|
|
265
|
+
updatedData: [pick(sampleEntities[0], ['boxId'])],
|
|
266
|
+
});
|
|
267
|
+
});
|
|
268
|
+
});
|
|
269
|
+
});
|
|
@@ -3,11 +3,22 @@ import {
|
|
|
3
3
|
AbstractInitializableErgoExtractor,
|
|
4
4
|
AbstractInitializableErgoExtractorAction,
|
|
5
5
|
} from '../../lib/ergo/initializable';
|
|
6
|
-
import {
|
|
6
|
+
import {
|
|
7
|
+
OutputBox,
|
|
8
|
+
AbstractBoxData,
|
|
9
|
+
BlockInfo,
|
|
10
|
+
AbstractErgoExtractorEntity,
|
|
11
|
+
} from '../../lib';
|
|
7
12
|
import { ergoBoxes } from './testData';
|
|
8
13
|
|
|
9
|
-
export class MockedInitializableErgoExtractor extends AbstractInitializableErgoExtractor<
|
|
10
|
-
|
|
14
|
+
export class MockedInitializableErgoExtractor extends AbstractInitializableErgoExtractor<
|
|
15
|
+
AbstractBoxData,
|
|
16
|
+
AbstractErgoExtractorEntity
|
|
17
|
+
> {
|
|
18
|
+
actions: AbstractInitializableErgoExtractorAction<
|
|
19
|
+
AbstractBoxData,
|
|
20
|
+
AbstractErgoExtractorEntity
|
|
21
|
+
>;
|
|
11
22
|
|
|
12
23
|
getId = () => 'Test';
|
|
13
24
|
|
|
@@ -3,8 +3,9 @@ import { describe, expect, it, vi, vitest } from 'vitest';
|
|
|
3
3
|
import {
|
|
4
4
|
ErgoNetworkType,
|
|
5
5
|
NodeNetwork,
|
|
6
|
-
|
|
6
|
+
AbstractBoxData,
|
|
7
7
|
ExplorerNetwork,
|
|
8
|
+
AbstractErgoExtractorEntity,
|
|
8
9
|
} from '../../lib';
|
|
9
10
|
import { MockedInitializableErgoExtractor } from './AbstractInitializable.mock';
|
|
10
11
|
import { transactionBatch } from './testData';
|
|
@@ -59,7 +60,7 @@ describe('AbstractInitializableErgoExtractor', () => {
|
|
|
59
60
|
'node_url',
|
|
60
61
|
'address'
|
|
61
62
|
);
|
|
62
|
-
const processSpy = vitest.fn();
|
|
63
|
+
const processSpy = vitest.fn().mockResolvedValue(true);
|
|
63
64
|
extractor.processTransactions = processSpy;
|
|
64
65
|
await extractor['processTransactionBatch'](transactionBatch);
|
|
65
66
|
expect(processSpy).toBeCalledTimes(2);
|
|
@@ -75,6 +76,28 @@ describe('AbstractInitializableErgoExtractor', () => {
|
|
|
75
76
|
}
|
|
76
77
|
);
|
|
77
78
|
});
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* @target processTransactionBatch should throw error when processing transactions fails for a block
|
|
82
|
+
* @dependencies
|
|
83
|
+
* @scenario
|
|
84
|
+
* - mock extractor
|
|
85
|
+
* - spy `processTransactions` to return false (always fails)
|
|
86
|
+
* - run test (call `processTransactionBatch`)
|
|
87
|
+
* @expected
|
|
88
|
+
* - to throw error
|
|
89
|
+
*/
|
|
90
|
+
it('should throw error when processing transactions fails for a block', async () => {
|
|
91
|
+
const extractor = new MockedInitializableErgoExtractor(
|
|
92
|
+
ErgoNetworkType.Node,
|
|
93
|
+
'node_url',
|
|
94
|
+
'address'
|
|
95
|
+
);
|
|
96
|
+
extractor.processTransactions = vitest.fn().mockResolvedValue(false);
|
|
97
|
+
await expect(() =>
|
|
98
|
+
extractor['processTransactionBatch'](transactionBatch)
|
|
99
|
+
).rejects.toThrowError();
|
|
100
|
+
});
|
|
78
101
|
});
|
|
79
102
|
|
|
80
103
|
describe('initWithRetrial', () => {
|
|
@@ -102,7 +125,10 @@ describe('AbstractInitializableErgoExtractor', () => {
|
|
|
102
125
|
const removeSpy = vitest.fn();
|
|
103
126
|
extractor['actions'] = {
|
|
104
127
|
removeAllData: removeSpy,
|
|
105
|
-
} as unknown as AbstractInitializableErgoExtractorAction<
|
|
128
|
+
} as unknown as AbstractInitializableErgoExtractorAction<
|
|
129
|
+
AbstractBoxData,
|
|
130
|
+
AbstractErgoExtractorEntity
|
|
131
|
+
>;
|
|
106
132
|
const initSpy = vitest.fn();
|
|
107
133
|
await extractor['initWithRetrial'](initSpy);
|
|
108
134
|
expect(removeSpy).not.toHaveBeenCalled();
|
|
@@ -131,7 +157,10 @@ describe('AbstractInitializableErgoExtractor', () => {
|
|
|
131
157
|
const removeSpy = vitest.fn();
|
|
132
158
|
extractor['actions'] = {
|
|
133
159
|
removeAllData: removeSpy,
|
|
134
|
-
} as unknown as AbstractInitializableErgoExtractorAction<
|
|
160
|
+
} as unknown as AbstractInitializableErgoExtractorAction<
|
|
161
|
+
AbstractBoxData,
|
|
162
|
+
AbstractErgoExtractorEntity
|
|
163
|
+
>;
|
|
135
164
|
const initSpy = vitest.fn();
|
|
136
165
|
await extractor['initWithRetrial'](initSpy);
|
|
137
166
|
expect(removeSpy).toHaveBeenCalledOnce();
|
|
@@ -161,7 +190,10 @@ describe('AbstractInitializableErgoExtractor', () => {
|
|
|
161
190
|
const removeSpy = vitest.fn();
|
|
162
191
|
extractor['actions'] = {
|
|
163
192
|
removeAllData: removeSpy,
|
|
164
|
-
} as unknown as AbstractInitializableErgoExtractorAction<
|
|
193
|
+
} as unknown as AbstractInitializableErgoExtractorAction<
|
|
194
|
+
AbstractBoxData,
|
|
195
|
+
AbstractErgoExtractorEntity
|
|
196
|
+
>;
|
|
165
197
|
const initSpy = vitest.fn().mockRejectedValue(0);
|
|
166
198
|
await expect(
|
|
167
199
|
async () => await extractor['initWithRetrial'](initSpy)
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { DataSource } from 'typeorm';
|
|
2
|
+
import { pick } from 'lodash-es';
|
|
3
|
+
|
|
4
|
+
import {
|
|
5
|
+
AbstractErgoExtractorEntity,
|
|
6
|
+
BlockInfo,
|
|
7
|
+
AbstractBoxData,
|
|
8
|
+
AbstractInitializableErgoExtractorAction,
|
|
9
|
+
} from '../../lib';
|
|
10
|
+
import { TestEntity } from '../testUtils';
|
|
11
|
+
|
|
12
|
+
export class TestInitializableErgoExtractorAction extends AbstractInitializableErgoExtractorAction<
|
|
13
|
+
AbstractBoxData,
|
|
14
|
+
AbstractErgoExtractorEntity
|
|
15
|
+
> {
|
|
16
|
+
constructor(dataSource: DataSource) {
|
|
17
|
+
super(dataSource, TestEntity);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* create the test database entity from data and block information
|
|
22
|
+
*/
|
|
23
|
+
createEntity = (
|
|
24
|
+
boxes: AbstractBoxData[],
|
|
25
|
+
block: BlockInfo,
|
|
26
|
+
extractor: string
|
|
27
|
+
): Omit<AbstractErgoExtractorEntity, 'id'>[] => {
|
|
28
|
+
return boxes.map((box) => ({
|
|
29
|
+
boxId: box.boxId,
|
|
30
|
+
block: block.hash,
|
|
31
|
+
height: block.height,
|
|
32
|
+
serialized: box.serialized,
|
|
33
|
+
extractor: extractor,
|
|
34
|
+
}));
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* convert the database entity back to raw data
|
|
39
|
+
*/
|
|
40
|
+
convertEntityToData = (
|
|
41
|
+
entities: AbstractErgoExtractorEntity[]
|
|
42
|
+
): AbstractBoxData[] => {
|
|
43
|
+
return entities.map((data) => pick(data, ['boxId', 'serialized']));
|
|
44
|
+
};
|
|
45
|
+
}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { DataSource, Repository } from 'typeorm';
|
|
2
|
+
import { describe, it, expect, beforeEach } from 'vitest';
|
|
3
|
+
|
|
4
|
+
import { TestInitializableErgoExtractorAction } from './AbstractInitializableAction.mock';
|
|
5
|
+
import { createDatabase, TestEntity } from '../testUtils';
|
|
6
|
+
import { sampleEntities } from '../testData';
|
|
7
|
+
|
|
8
|
+
describe('AbstractErgoExtractorAction', () => {
|
|
9
|
+
let dataSource: DataSource;
|
|
10
|
+
let action: TestInitializableErgoExtractorAction;
|
|
11
|
+
let repository: Repository<TestEntity>;
|
|
12
|
+
beforeEach(async () => {
|
|
13
|
+
dataSource = await createDatabase();
|
|
14
|
+
action = new TestInitializableErgoExtractorAction(dataSource);
|
|
15
|
+
repository = dataSource.getRepository(TestEntity);
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
describe('removeAllData', () => {
|
|
19
|
+
/**
|
|
20
|
+
* @target removeAllData should remove all available data related to this extractor
|
|
21
|
+
* @dependencies
|
|
22
|
+
* @scenario
|
|
23
|
+
* - insert 4 entities related to this extractor
|
|
24
|
+
* - run test (call `removeAllData`)
|
|
25
|
+
* @expected
|
|
26
|
+
* - to have 4 entities before removing
|
|
27
|
+
* - to have no remaining entities after remove
|
|
28
|
+
*/
|
|
29
|
+
it(`should remove all available data related to this extractor`, async () => {
|
|
30
|
+
await dataSource.getRepository(TestEntity).insert(sampleEntities);
|
|
31
|
+
const countBefore = await repository.count();
|
|
32
|
+
|
|
33
|
+
await action.removeAllData('extractor');
|
|
34
|
+
const countAfter = await repository.count();
|
|
35
|
+
|
|
36
|
+
expect(countBefore).toEqual(4);
|
|
37
|
+
expect(countAfter).toEqual(0);
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* @target removeAllData should not remove any data related to another extractor
|
|
42
|
+
* @dependencies
|
|
43
|
+
* @scenario
|
|
44
|
+
* - insert 4 entities related to another extractor
|
|
45
|
+
* - run test (call `removeAllData`)
|
|
46
|
+
* @expected
|
|
47
|
+
* - to have 4 entities after removing
|
|
48
|
+
*/
|
|
49
|
+
it(`should not remove any data related to another extractor`, async () => {
|
|
50
|
+
await dataSource.getRepository(TestEntity).insert(
|
|
51
|
+
sampleEntities.map((entity) => ({
|
|
52
|
+
...entity,
|
|
53
|
+
extractor: 'extractor-new',
|
|
54
|
+
}))
|
|
55
|
+
);
|
|
56
|
+
const countBefore = await repository.count();
|
|
57
|
+
|
|
58
|
+
await action.removeAllData('extractor');
|
|
59
|
+
const countAfter = await repository.count();
|
|
60
|
+
|
|
61
|
+
expect(countBefore).toEqual(4);
|
|
62
|
+
expect(countAfter).toEqual(4);
|
|
63
|
+
});
|
|
64
|
+
});
|
|
65
|
+
});
|
package/tests/testData.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { Block } from '../lib';
|
|
2
|
+
import { TestEntity } from './testUtils';
|
|
2
3
|
|
|
3
4
|
export const tx = {
|
|
4
5
|
id: '3b91fbd2b6f4f3f971098655ffa320841001b071908de057cdf8c425cd3b3e61',
|
|
@@ -107,3 +108,40 @@ export const block: Block = {
|
|
|
107
108
|
height: 100,
|
|
108
109
|
parentHash: 'parentHash',
|
|
109
110
|
} as Block;
|
|
111
|
+
|
|
112
|
+
export const block2: Block = {
|
|
113
|
+
hash: 'hash2',
|
|
114
|
+
height: 101,
|
|
115
|
+
parentHash: 'parentHash2',
|
|
116
|
+
} as Block;
|
|
117
|
+
|
|
118
|
+
export const sampleEntities: Omit<TestEntity, 'id'>[] = [
|
|
119
|
+
{
|
|
120
|
+
extractor: 'extractor',
|
|
121
|
+
boxId: '1',
|
|
122
|
+
serialized: 'serialized1',
|
|
123
|
+
block: 'blockId1',
|
|
124
|
+
height: 100,
|
|
125
|
+
},
|
|
126
|
+
{
|
|
127
|
+
extractor: 'extractor',
|
|
128
|
+
boxId: '2',
|
|
129
|
+
serialized: 'serialized2',
|
|
130
|
+
block: 'blockId2',
|
|
131
|
+
height: 200,
|
|
132
|
+
},
|
|
133
|
+
{
|
|
134
|
+
extractor: 'extractor',
|
|
135
|
+
boxId: '3',
|
|
136
|
+
serialized: 'serialized3',
|
|
137
|
+
block: 'blockId3',
|
|
138
|
+
height: 300,
|
|
139
|
+
},
|
|
140
|
+
{
|
|
141
|
+
extractor: 'extractor',
|
|
142
|
+
boxId: '4',
|
|
143
|
+
serialized: 'serialized4',
|
|
144
|
+
block: 'blockId4',
|
|
145
|
+
height: 400,
|
|
146
|
+
},
|
|
147
|
+
];
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { DataSource, Entity } from 'typeorm';
|
|
2
|
+
import { AbstractErgoExtractorEntity } from '../lib';
|
|
3
|
+
|
|
4
|
+
@Entity('test_entity')
|
|
5
|
+
export class TestEntity extends AbstractErgoExtractorEntity {}
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* generates a dataSource with in memory database for testing
|
|
9
|
+
*/
|
|
10
|
+
export const createDatabase = async (): Promise<DataSource> => {
|
|
11
|
+
const dataSource = new DataSource({
|
|
12
|
+
type: 'sqlite',
|
|
13
|
+
database: `:memory:`,
|
|
14
|
+
entities: [TestEntity],
|
|
15
|
+
migrations: [],
|
|
16
|
+
synchronize: true,
|
|
17
|
+
logging: false,
|
|
18
|
+
});
|
|
19
|
+
await dataSource.initialize();
|
|
20
|
+
await dataSource.runMigrations();
|
|
21
|
+
return dataSource;
|
|
22
|
+
};
|