hive-stream 3.0.0 → 3.0.2
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/AGENTS.md +35 -0
- package/DOCUMENTATION.md +396 -0
- package/README.md +160 -39
- package/dist/actions.d.ts +3 -3
- package/dist/actions.js +7 -7
- package/dist/actions.js.map +1 -1
- package/dist/adapters/base.adapter.d.ts +19 -1
- package/dist/adapters/base.adapter.js +16 -0
- package/dist/adapters/base.adapter.js.map +1 -1
- package/dist/adapters/mongodb.adapter.d.ts +5 -11
- package/dist/adapters/mongodb.adapter.js +10 -10
- package/dist/adapters/mongodb.adapter.js.map +1 -1
- package/dist/adapters/postgresql.adapter.d.ts +17 -0
- package/dist/adapters/postgresql.adapter.js +99 -8
- package/dist/adapters/postgresql.adapter.js.map +1 -1
- package/dist/adapters/sqlite.adapter.d.ts +17 -0
- package/dist/adapters/sqlite.adapter.js +99 -8
- package/dist/adapters/sqlite.adapter.js.map +1 -1
- package/dist/api.js +86 -0
- package/dist/api.js.map +1 -1
- package/dist/config.d.ts +26 -0
- package/dist/config.js +76 -4
- package/dist/config.js.map +1 -1
- package/dist/contracts/coinflip.contract.d.ts +8 -26
- package/dist/contracts/coinflip.contract.js +123 -144
- package/dist/contracts/coinflip.contract.js.map +1 -1
- package/dist/contracts/contract.d.ts +3 -0
- package/dist/contracts/contract.js +26 -0
- package/dist/contracts/contract.js.map +1 -0
- package/dist/contracts/dice.contract.d.ts +9 -36
- package/dist/contracts/dice.contract.js +135 -200
- package/dist/contracts/dice.contract.js.map +1 -1
- package/dist/contracts/exchange.contract.d.ts +11 -0
- package/dist/contracts/exchange.contract.js +492 -0
- package/dist/contracts/exchange.contract.js.map +1 -0
- package/dist/contracts/lotto.contract.d.ts +15 -19
- package/dist/contracts/lotto.contract.js +154 -162
- package/dist/contracts/lotto.contract.js.map +1 -1
- package/dist/contracts/nft.contract.d.ts +4 -0
- package/dist/contracts/nft.contract.js +65 -0
- package/dist/contracts/nft.contract.js.map +1 -1
- package/dist/contracts/poll.contract.d.ts +4 -0
- package/dist/contracts/poll.contract.js +105 -0
- package/dist/contracts/poll.contract.js.map +1 -0
- package/dist/contracts/rps.contract.d.ts +9 -0
- package/dist/contracts/rps.contract.js +217 -0
- package/dist/contracts/rps.contract.js.map +1 -0
- package/dist/contracts/tipjar.contract.d.ts +4 -0
- package/dist/contracts/tipjar.contract.js +60 -0
- package/dist/contracts/tipjar.contract.js.map +1 -0
- package/dist/contracts/token.contract.d.ts +3 -17
- package/dist/contracts/token.contract.js +128 -80
- package/dist/contracts/token.contract.js.map +1 -1
- package/dist/exchanges/coingecko.d.ts +7 -1
- package/dist/exchanges/coingecko.js +38 -21
- package/dist/exchanges/coingecko.js.map +1 -1
- package/dist/exchanges/exchange.d.ts +15 -8
- package/dist/exchanges/exchange.js +65 -11
- package/dist/exchanges/exchange.js.map +1 -1
- package/dist/hive-rates.d.ts +29 -4
- package/dist/hive-rates.js +179 -92
- package/dist/hive-rates.js.map +1 -1
- package/dist/index.d.ts +11 -3
- package/dist/index.js +19 -4
- package/dist/index.js.map +1 -1
- package/dist/metadata.d.ts +63 -0
- package/dist/metadata.js +407 -0
- package/dist/metadata.js.map +1 -0
- package/dist/streamer.d.ts +104 -11
- package/dist/streamer.js +415 -143
- package/dist/streamer.js.map +1 -1
- package/dist/test.js +11 -12
- package/dist/test.js.map +1 -1
- package/dist/types/hive-stream.d.ts +85 -14
- package/dist/types/rates.d.ts +47 -0
- package/dist/types/rates.js +29 -0
- package/dist/types/rates.js.map +1 -0
- package/dist/utils.d.ts +318 -11
- package/dist/utils.js +804 -115
- package/dist/utils.js.map +1 -1
- package/examples/contracts/README.md +8 -0
- package/examples/contracts/exchange.ts +38 -0
- package/examples/contracts/poll.ts +21 -0
- package/examples/contracts/rps.ts +19 -0
- package/examples/contracts/tipjar.ts +19 -0
- package/package.json +20 -19
- package/test-contract-block.md +3 -3
- package/tests/actions.spec.ts +7 -7
- package/tests/adapters/actions-persistence.spec.ts +4 -4
- package/tests/adapters/sqlite.adapter.spec.ts +2 -2
- package/tests/config-input.spec.ts +90 -0
- package/tests/contracts/coinflip.contract.spec.ts +26 -154
- package/tests/contracts/dice.contract.spec.ts +24 -140
- package/tests/contracts/exchange.contract.spec.ts +84 -0
- package/tests/contracts/lotto.contract.spec.ts +30 -295
- package/tests/contracts/token.contract.spec.ts +72 -316
- package/tests/exchanges/coingecko.exchange.spec.ts +169 -0
- package/tests/exchanges/exchange.base.spec.ts +246 -0
- package/tests/helpers/mock-fetch.ts +165 -0
- package/tests/hive-chain-features.spec.ts +319 -0
- package/tests/hive-rates.spec.ts +443 -0
- package/tests/integration/hive-rates.integration.spec.ts +35 -0
- package/tests/metadata.spec.ts +63 -0
- package/tests/streamer-actions.spec.ts +29 -18
- package/tests/streamer.spec.ts +142 -49
- package/tests/types/rates.spec.ts +216 -0
- package/tests/utils.spec.ts +27 -6
package/tests/streamer.spec.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { TimeAction } from '../src/actions';
|
|
2
2
|
import { Streamer } from '../src/streamer';
|
|
3
|
+
import { action as contractAction, defineContract } from '../src/contracts/contract';
|
|
3
4
|
import { createMockAdapter } from './helpers/mock-adapter';
|
|
4
5
|
|
|
5
6
|
describe('Streamer', () => {
|
|
@@ -40,17 +41,20 @@ describe('Streamer', () => {
|
|
|
40
41
|
|
|
41
42
|
await sut.registerAdapter(adapter);
|
|
42
43
|
|
|
43
|
-
expect(adapter.create).
|
|
44
|
+
expect(adapter.create).toHaveBeenCalled();
|
|
44
45
|
});
|
|
45
46
|
});
|
|
46
47
|
|
|
47
48
|
describe('Actions', () => {
|
|
48
49
|
test('Registers a new action', async () => {
|
|
49
|
-
const mockContract = {
|
|
50
|
-
|
|
51
|
-
|
|
50
|
+
const mockContract = defineContract({
|
|
51
|
+
name: 'testcontract',
|
|
52
|
+
actions: {
|
|
53
|
+
testmethod: contractAction(jest.fn(), { trigger: 'time' })
|
|
54
|
+
}
|
|
55
|
+
});
|
|
52
56
|
|
|
53
|
-
sut.registerContract(
|
|
57
|
+
await sut.registerContract(mockContract);
|
|
54
58
|
|
|
55
59
|
const adapter = {
|
|
56
60
|
create: jest.fn().mockResolvedValue(true),
|
|
@@ -70,7 +74,7 @@ describe('Streamer', () => {
|
|
|
70
74
|
db: null
|
|
71
75
|
} as any;
|
|
72
76
|
|
|
73
|
-
sut.registerAdapter(adapter);
|
|
77
|
+
await sut.registerAdapter(adapter);
|
|
74
78
|
|
|
75
79
|
const action = new TimeAction('1m', 'testoneminute', 'testcontract', 'testmethod');
|
|
76
80
|
|
|
@@ -82,11 +86,14 @@ describe('Streamer', () => {
|
|
|
82
86
|
});
|
|
83
87
|
|
|
84
88
|
test('Does not allow duplicate actions of the same id', async () => {
|
|
85
|
-
const mockContract = {
|
|
86
|
-
|
|
87
|
-
|
|
89
|
+
const mockContract = defineContract({
|
|
90
|
+
name: 'testcontract',
|
|
91
|
+
actions: {
|
|
92
|
+
testmethod: contractAction(jest.fn(), { trigger: 'time' })
|
|
93
|
+
}
|
|
94
|
+
});
|
|
88
95
|
|
|
89
|
-
sut.registerContract(
|
|
96
|
+
await sut.registerContract(mockContract);
|
|
90
97
|
|
|
91
98
|
const adapter = {
|
|
92
99
|
create: jest.fn().mockResolvedValue(true),
|
|
@@ -106,7 +113,7 @@ describe('Streamer', () => {
|
|
|
106
113
|
db: null
|
|
107
114
|
} as any;
|
|
108
115
|
|
|
109
|
-
sut.registerAdapter(adapter);
|
|
116
|
+
await sut.registerAdapter(adapter);
|
|
110
117
|
|
|
111
118
|
const action = new TimeAction('1m', 'testoneminute', 'testcontract', 'testmethod');
|
|
112
119
|
const action2 = new TimeAction('1m', 'testoneminute', 'testcontract', 'testmethod');
|
|
@@ -118,11 +125,14 @@ describe('Streamer', () => {
|
|
|
118
125
|
});
|
|
119
126
|
|
|
120
127
|
test('Registers actions loaded from adapter loadActions call', async () => {
|
|
121
|
-
const mockContract = {
|
|
122
|
-
|
|
123
|
-
|
|
128
|
+
const mockContract = defineContract({
|
|
129
|
+
name: 'testcontract',
|
|
130
|
+
actions: {
|
|
131
|
+
testmethod: contractAction(jest.fn(), { trigger: 'time' })
|
|
132
|
+
}
|
|
133
|
+
});
|
|
124
134
|
|
|
125
|
-
sut.registerContract(
|
|
135
|
+
await sut.registerContract(mockContract);
|
|
126
136
|
|
|
127
137
|
const adapter = {
|
|
128
138
|
create: jest.fn().mockResolvedValue(true),
|
|
@@ -131,7 +141,7 @@ describe('Streamer', () => {
|
|
|
131
141
|
timeValue: '1m',
|
|
132
142
|
id: 'testoneminute',
|
|
133
143
|
contractName: 'testcontract',
|
|
134
|
-
|
|
144
|
+
contractAction: 'testmethod',
|
|
135
145
|
payload: {},
|
|
136
146
|
date: new Date().toISOString(),
|
|
137
147
|
enabled: true,
|
|
@@ -164,51 +174,67 @@ describe('Streamer', () => {
|
|
|
164
174
|
});
|
|
165
175
|
|
|
166
176
|
describe('Contracts', () => {
|
|
167
|
-
test('Should register a new contract', () => {
|
|
168
|
-
const contract = {
|
|
169
|
-
|
|
170
|
-
|
|
177
|
+
test('Should register a new contract', async () => {
|
|
178
|
+
const contract = defineContract({
|
|
179
|
+
name: 'testcontract',
|
|
180
|
+
actions: {
|
|
181
|
+
myMethod: contractAction(jest.fn())
|
|
182
|
+
}
|
|
183
|
+
});
|
|
171
184
|
|
|
172
|
-
sut.registerContract(
|
|
185
|
+
await sut.registerContract(contract);
|
|
173
186
|
|
|
174
|
-
expect(contract['_instance']).toBeInstanceOf(Streamer);
|
|
175
187
|
expect(sut['contracts'].length).toStrictEqual(1);
|
|
176
188
|
});
|
|
177
189
|
|
|
178
|
-
test('Should register a new contract and call its create
|
|
179
|
-
const
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
190
|
+
test('Should register a new contract and call its create hook', async () => {
|
|
191
|
+
const createHook = jest.fn();
|
|
192
|
+
const contract = defineContract({
|
|
193
|
+
name: 'testcontract',
|
|
194
|
+
hooks: {
|
|
195
|
+
create: createHook
|
|
196
|
+
},
|
|
197
|
+
actions: {
|
|
198
|
+
myMethod: contractAction(jest.fn())
|
|
199
|
+
}
|
|
200
|
+
});
|
|
201
|
+
|
|
202
|
+
await sut.registerContract(contract);
|
|
203
|
+
|
|
204
|
+
expect(createHook).toHaveBeenCalled();
|
|
188
205
|
expect(sut['contracts'].length).toStrictEqual(1);
|
|
189
206
|
});
|
|
190
207
|
|
|
191
|
-
test('Should unregister a registered contract', () => {
|
|
192
|
-
const contract = {
|
|
193
|
-
|
|
194
|
-
|
|
208
|
+
test('Should unregister a registered contract', async () => {
|
|
209
|
+
const contract = defineContract({
|
|
210
|
+
name: 'testcontract',
|
|
211
|
+
actions: {
|
|
212
|
+
myMethod: contractAction(jest.fn())
|
|
213
|
+
}
|
|
214
|
+
});
|
|
195
215
|
|
|
196
|
-
sut.registerContract(
|
|
197
|
-
sut.unregisterContract('testcontract');
|
|
216
|
+
await sut.registerContract(contract);
|
|
217
|
+
await sut.unregisterContract('testcontract');
|
|
198
218
|
|
|
199
219
|
expect(sut['contracts'].length).toStrictEqual(0);
|
|
200
220
|
});
|
|
201
221
|
|
|
202
|
-
test('Should unregister a registered contract and call its destroy
|
|
203
|
-
const
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
222
|
+
test('Should unregister a registered contract and call its destroy hook', async () => {
|
|
223
|
+
const destroyHook = jest.fn();
|
|
224
|
+
const contract = defineContract({
|
|
225
|
+
name: 'testcontract',
|
|
226
|
+
hooks: {
|
|
227
|
+
destroy: destroyHook
|
|
228
|
+
},
|
|
229
|
+
actions: {
|
|
230
|
+
myMethod: contractAction(jest.fn())
|
|
231
|
+
}
|
|
232
|
+
});
|
|
233
|
+
|
|
234
|
+
await sut.registerContract(contract);
|
|
235
|
+
await sut.unregisterContract('testcontract');
|
|
236
|
+
|
|
237
|
+
expect(destroyHook).toHaveBeenCalled();
|
|
212
238
|
expect(sut['contracts'].length).toStrictEqual(0);
|
|
213
239
|
});
|
|
214
240
|
});
|
|
@@ -246,4 +272,71 @@ describe('Streamer', () => {
|
|
|
246
272
|
|
|
247
273
|
sut.stop();
|
|
248
274
|
});
|
|
249
|
-
|
|
275
|
+
|
|
276
|
+
test('Start method should respect RESUME_FROM_STATE false', async () => {
|
|
277
|
+
sut.setConfig({ LAST_BLOCK_NUMBER: 123, RESUME_FROM_STATE: false });
|
|
278
|
+
|
|
279
|
+
const adapter = {
|
|
280
|
+
create: jest.fn().mockResolvedValue(true),
|
|
281
|
+
destroy: jest.fn(),
|
|
282
|
+
loadActions: jest.fn().mockResolvedValue([]),
|
|
283
|
+
loadState: jest.fn().mockResolvedValue({ lastBlockNumber: 999 }),
|
|
284
|
+
saveState: jest.fn().mockResolvedValue(true),
|
|
285
|
+
processBlock: jest.fn(),
|
|
286
|
+
processOperation: jest.fn(),
|
|
287
|
+
processTransfer: jest.fn(),
|
|
288
|
+
processCustomJson: jest.fn(),
|
|
289
|
+
find: jest.fn(),
|
|
290
|
+
findOne: jest.fn(),
|
|
291
|
+
insert: jest.fn(),
|
|
292
|
+
replace: jest.fn(),
|
|
293
|
+
client: null,
|
|
294
|
+
db: null
|
|
295
|
+
} as any;
|
|
296
|
+
|
|
297
|
+
await sut.registerAdapter(adapter);
|
|
298
|
+
|
|
299
|
+
jest.spyOn(sut as any, 'getBlock').mockImplementation(() => true);
|
|
300
|
+
jest.spyOn(sut as any, 'getLatestBlock').mockImplementation(() => true);
|
|
301
|
+
|
|
302
|
+
await sut.start();
|
|
303
|
+
|
|
304
|
+
expect(sut['lastBlockNumber']).toStrictEqual(123);
|
|
305
|
+
|
|
306
|
+
sut.stop();
|
|
307
|
+
});
|
|
308
|
+
|
|
309
|
+
test('getBlock should process multiple blocks per batch when behind', async () => {
|
|
310
|
+
const adapter = createMockAdapter();
|
|
311
|
+
await sut.registerAdapter(adapter);
|
|
312
|
+
|
|
313
|
+
sut.setConfig({ CATCH_UP_BATCH_SIZE: 3, BLOCK_CHECK_INTERVAL: 1, DEBUG_MODE: false });
|
|
314
|
+
sut['lastBlockNumber'] = 10;
|
|
315
|
+
|
|
316
|
+
const mockBlock = {
|
|
317
|
+
block_id: 'block-id',
|
|
318
|
+
previous: 'prev-id',
|
|
319
|
+
transaction_ids: ['trx-1'],
|
|
320
|
+
timestamp: '2023-01-01T00:00:00',
|
|
321
|
+
transactions: []
|
|
322
|
+
};
|
|
323
|
+
|
|
324
|
+
sut['client'] = {
|
|
325
|
+
database: {
|
|
326
|
+
getDynamicGlobalProperties: jest.fn().mockResolvedValue({
|
|
327
|
+
head_block_number: 20,
|
|
328
|
+
time: '2023-01-01T00:00:00'
|
|
329
|
+
}),
|
|
330
|
+
getBlock: jest.fn().mockResolvedValue(mockBlock)
|
|
331
|
+
}
|
|
332
|
+
} as any;
|
|
333
|
+
|
|
334
|
+
const loadBlockSpy = jest.spyOn(sut as any, 'loadBlock');
|
|
335
|
+
|
|
336
|
+
await (sut as any).getBlock();
|
|
337
|
+
clearTimeout(sut['blockNumberTimeout']);
|
|
338
|
+
|
|
339
|
+
expect(loadBlockSpy).toHaveBeenCalledTimes(3);
|
|
340
|
+
expect(sut['lastBlockNumber']).toStrictEqual(13);
|
|
341
|
+
});
|
|
342
|
+
});
|
|
@@ -0,0 +1,216 @@
|
|
|
1
|
+
import {
|
|
2
|
+
RatesError,
|
|
3
|
+
NetworkError,
|
|
4
|
+
ValidationError,
|
|
5
|
+
ExchangeRates,
|
|
6
|
+
HiveRates,
|
|
7
|
+
CryptoRates,
|
|
8
|
+
ExchangeResponse,
|
|
9
|
+
FiatResponse,
|
|
10
|
+
RateConfig,
|
|
11
|
+
CurrencyPair,
|
|
12
|
+
SupportedCrypto,
|
|
13
|
+
SupportedFiat
|
|
14
|
+
} from '../../src/types/rates';
|
|
15
|
+
|
|
16
|
+
describe('Types and Error Classes', () => {
|
|
17
|
+
describe('RatesError', () => {
|
|
18
|
+
it('should create basic RatesError', () => {
|
|
19
|
+
const error = new RatesError('Test message', 'TEST_CODE');
|
|
20
|
+
|
|
21
|
+
expect(error.message).toBe('Test message');
|
|
22
|
+
expect(error.code).toBe('TEST_CODE');
|
|
23
|
+
expect(error.name).toBe('RatesError');
|
|
24
|
+
expect(error.source).toBeUndefined();
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
it('should create RatesError with source', () => {
|
|
28
|
+
const error = new RatesError('Test message', 'TEST_CODE', 'test-source');
|
|
29
|
+
|
|
30
|
+
expect(error.source).toBe('test-source');
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
it('should be instance of Error', () => {
|
|
34
|
+
const error = new RatesError('Test', 'CODE');
|
|
35
|
+
expect(error).toBeInstanceOf(Error);
|
|
36
|
+
expect(error).toBeInstanceOf(RatesError);
|
|
37
|
+
});
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
describe('NetworkError', () => {
|
|
41
|
+
it('should create NetworkError', () => {
|
|
42
|
+
const error = new NetworkError('Network failed');
|
|
43
|
+
|
|
44
|
+
expect(error.message).toBe('Network failed');
|
|
45
|
+
expect(error.code).toBe('NETWORK_ERROR');
|
|
46
|
+
expect(error.name).toBe('NetworkError');
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
it('should create NetworkError with source', () => {
|
|
50
|
+
const error = new NetworkError('Network failed', 'api-endpoint');
|
|
51
|
+
|
|
52
|
+
expect(error.source).toBe('api-endpoint');
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
it('should be instance of RatesError', () => {
|
|
56
|
+
const error = new NetworkError('Test');
|
|
57
|
+
expect(error).toBeInstanceOf(Error);
|
|
58
|
+
expect(error).toBeInstanceOf(RatesError);
|
|
59
|
+
expect(error).toBeInstanceOf(NetworkError);
|
|
60
|
+
});
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
describe('ValidationError', () => {
|
|
64
|
+
it('should create ValidationError', () => {
|
|
65
|
+
const error = new ValidationError('Invalid data');
|
|
66
|
+
|
|
67
|
+
expect(error.message).toBe('Invalid data');
|
|
68
|
+
expect(error.code).toBe('VALIDATION_ERROR');
|
|
69
|
+
expect(error.name).toBe('ValidationError');
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
it('should create ValidationError with source', () => {
|
|
73
|
+
const error = new ValidationError('Invalid data', 'response-parser');
|
|
74
|
+
|
|
75
|
+
expect(error.source).toBe('response-parser');
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
it('should be instance of RatesError', () => {
|
|
79
|
+
const error = new ValidationError('Test');
|
|
80
|
+
expect(error).toBeInstanceOf(Error);
|
|
81
|
+
expect(error).toBeInstanceOf(RatesError);
|
|
82
|
+
expect(error).toBeInstanceOf(ValidationError);
|
|
83
|
+
});
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
describe('Type Definitions', () => {
|
|
87
|
+
it('should accept valid ExchangeRates', () => {
|
|
88
|
+
const rates: ExchangeRates = {
|
|
89
|
+
EUR: 0.85,
|
|
90
|
+
GBP: 0.73,
|
|
91
|
+
JPY: 110
|
|
92
|
+
};
|
|
93
|
+
|
|
94
|
+
expect(typeof rates.EUR).toBe('number');
|
|
95
|
+
expect(typeof rates.GBP).toBe('number');
|
|
96
|
+
expect(typeof rates.JPY).toBe('number');
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
it('should accept valid HiveRates', () => {
|
|
100
|
+
const rates: HiveRates = {
|
|
101
|
+
'USD_EUR': 0.85,
|
|
102
|
+
'EUR_HIVE': 0.425,
|
|
103
|
+
'GBP_HBD': 0.73
|
|
104
|
+
};
|
|
105
|
+
|
|
106
|
+
expect(typeof rates['USD_EUR']).toBe('number');
|
|
107
|
+
expect(typeof rates['EUR_HIVE']).toBe('number');
|
|
108
|
+
expect(typeof rates['GBP_HBD']).toBe('number');
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
it('should accept valid CryptoRates', () => {
|
|
112
|
+
const rates: CryptoRates = {
|
|
113
|
+
usdHive: 0.5,
|
|
114
|
+
usdHbd: 1.0
|
|
115
|
+
};
|
|
116
|
+
|
|
117
|
+
expect(rates.usdHive).toBe(0.5);
|
|
118
|
+
expect(rates.usdHbd).toBe(1.0);
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
it('should accept valid ExchangeResponse', () => {
|
|
122
|
+
const successResponse: ExchangeResponse = {
|
|
123
|
+
success: true,
|
|
124
|
+
rates: { usdHive: 0.5, usdHbd: 1.0 }
|
|
125
|
+
};
|
|
126
|
+
|
|
127
|
+
const errorResponse: ExchangeResponse = {
|
|
128
|
+
success: false,
|
|
129
|
+
error: 'API failed'
|
|
130
|
+
};
|
|
131
|
+
|
|
132
|
+
expect(successResponse.success).toBe(true);
|
|
133
|
+
expect(successResponse.rates).toBeDefined();
|
|
134
|
+
expect(errorResponse.success).toBe(false);
|
|
135
|
+
expect(errorResponse.error).toBe('API failed');
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
it('should accept valid FiatResponse', () => {
|
|
139
|
+
const successResponse: FiatResponse = {
|
|
140
|
+
success: true,
|
|
141
|
+
rates: { EUR: 0.85, GBP: 0.73 }
|
|
142
|
+
};
|
|
143
|
+
|
|
144
|
+
const errorResponse: FiatResponse = {
|
|
145
|
+
success: false,
|
|
146
|
+
error: 'API failed'
|
|
147
|
+
};
|
|
148
|
+
|
|
149
|
+
expect(successResponse.success).toBe(true);
|
|
150
|
+
expect(successResponse.rates).toBeDefined();
|
|
151
|
+
expect(errorResponse.success).toBe(false);
|
|
152
|
+
expect(errorResponse.error).toBe('API failed');
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
it('should accept valid RateConfig', () => {
|
|
156
|
+
const config: RateConfig = {
|
|
157
|
+
cacheDuration: 3600000,
|
|
158
|
+
maxRetries: 3,
|
|
159
|
+
retryDelay: 1000,
|
|
160
|
+
timeout: 10000
|
|
161
|
+
};
|
|
162
|
+
|
|
163
|
+
expect(config.cacheDuration).toBe(3600000);
|
|
164
|
+
expect(config.maxRetries).toBe(3);
|
|
165
|
+
expect(config.retryDelay).toBe(1000);
|
|
166
|
+
expect(config.timeout).toBe(10000);
|
|
167
|
+
});
|
|
168
|
+
|
|
169
|
+
it('should accept partial RateConfig', () => {
|
|
170
|
+
const config: RateConfig = {
|
|
171
|
+
cacheDuration: 5000
|
|
172
|
+
};
|
|
173
|
+
|
|
174
|
+
expect(config.cacheDuration).toBe(5000);
|
|
175
|
+
expect(config.maxRetries).toBeUndefined();
|
|
176
|
+
});
|
|
177
|
+
|
|
178
|
+
it('should work with CurrencyPair type', () => {
|
|
179
|
+
const pair1: CurrencyPair = 'USD_EUR';
|
|
180
|
+
const pair2: CurrencyPair = 'EUR_HIVE';
|
|
181
|
+
const pair3: CurrencyPair = 'GBP_HBD';
|
|
182
|
+
|
|
183
|
+
expect(pair1).toBe('USD_EUR');
|
|
184
|
+
expect(pair2).toBe('EUR_HIVE');
|
|
185
|
+
expect(pair3).toBe('GBP_HBD');
|
|
186
|
+
});
|
|
187
|
+
|
|
188
|
+
it('should work with SupportedCrypto type', () => {
|
|
189
|
+
const crypto1: SupportedCrypto = 'HIVE';
|
|
190
|
+
const crypto2: SupportedCrypto = 'HBD';
|
|
191
|
+
|
|
192
|
+
expect(crypto1).toBe('HIVE');
|
|
193
|
+
expect(crypto2).toBe('HBD');
|
|
194
|
+
});
|
|
195
|
+
|
|
196
|
+
it('should work with SupportedFiat type', () => {
|
|
197
|
+
const fiat1: SupportedFiat = 'USD';
|
|
198
|
+
const fiat2: SupportedFiat = 'EUR';
|
|
199
|
+
const fiat3: SupportedFiat = 'GBP';
|
|
200
|
+
const fiat4: SupportedFiat = 'JPY';
|
|
201
|
+
const fiat5: SupportedFiat = 'CAD';
|
|
202
|
+
const fiat6: SupportedFiat = 'AUD';
|
|
203
|
+
const fiat7: SupportedFiat = 'CHF';
|
|
204
|
+
const fiat8: SupportedFiat = 'CNY';
|
|
205
|
+
|
|
206
|
+
expect(fiat1).toBe('USD');
|
|
207
|
+
expect(fiat2).toBe('EUR');
|
|
208
|
+
expect(fiat3).toBe('GBP');
|
|
209
|
+
expect(fiat4).toBe('JPY');
|
|
210
|
+
expect(fiat5).toBe('CAD');
|
|
211
|
+
expect(fiat6).toBe('AUD');
|
|
212
|
+
expect(fiat7).toBe('CHF');
|
|
213
|
+
expect(fiat8).toBe('CNY');
|
|
214
|
+
});
|
|
215
|
+
});
|
|
216
|
+
});
|
package/tests/utils.spec.ts
CHANGED
|
@@ -16,7 +16,7 @@ describe('Utils', () => {
|
|
|
16
16
|
});
|
|
17
17
|
|
|
18
18
|
test('Invalid numeric values passed', () => {
|
|
19
|
-
expect(Utils.roundPrecision('dasd', 3)).toBeNaN();
|
|
19
|
+
expect(Utils.roundPrecision('dasd' as any, 3)).toBeNaN();
|
|
20
20
|
});
|
|
21
21
|
});
|
|
22
22
|
|
|
@@ -76,17 +76,38 @@ describe('Utils', () => {
|
|
|
76
76
|
|
|
77
77
|
const value = await Utils.convertHiveAmount(amount, fiatSymbol, hiveSymbol);
|
|
78
78
|
|
|
79
|
-
expect(fetch).
|
|
80
|
-
|
|
79
|
+
expect(fetch).toHaveBeenNthCalledWith(
|
|
80
|
+
1,
|
|
81
|
+
'https://api.coingecko.com/api/v3/simple/price?ids=hive,hive_dollar&vs_currencies=usd',
|
|
82
|
+
expect.objectContaining({
|
|
83
|
+
headers: expect.objectContaining({
|
|
84
|
+
'User-Agent': 'hive-stream/3.0.0',
|
|
85
|
+
'Accept': 'application/json'
|
|
86
|
+
})
|
|
87
|
+
})
|
|
88
|
+
);
|
|
89
|
+
expect(fetch).toHaveBeenNthCalledWith(
|
|
90
|
+
2,
|
|
91
|
+
'https://cdn.jsdelivr.net/npm/@fawazahmed0/currency-api@latest/v1/currencies/usd.json',
|
|
92
|
+
expect.objectContaining({
|
|
93
|
+
headers: expect.objectContaining({
|
|
94
|
+
'User-Agent': 'hive-stream/3.0.0',
|
|
95
|
+
'Accept': 'application/json',
|
|
96
|
+
'Cache-Control': 'no-cache'
|
|
97
|
+
})
|
|
98
|
+
})
|
|
99
|
+
);
|
|
81
100
|
|
|
82
101
|
expect(value).toStrictEqual(Number((amount / 0.229951).toFixed(3))); // amount / HIVE price from CoinGecko (fiat to HIVE conversion)
|
|
83
102
|
});
|
|
84
103
|
});
|
|
85
104
|
|
|
86
105
|
describe('Get transfer URL', () => {
|
|
87
|
-
test('Gets a transfer URL string', () => {
|
|
88
|
-
|
|
106
|
+
test('Gets a transfer URL string with proper URL encoding', () => {
|
|
107
|
+
const result = Utils.getTransferUrl('beggars', 'TEST123', '10.000 HIVE', 'http://localhost:5001');
|
|
108
|
+
const expected = 'https://hivesigner.com/sign/transfer?to=beggars&memo=TEST123&amount=10.000%20HIVE&redirect_uri=http%3A%2F%2Flocalhost%3A5001';
|
|
109
|
+
expect(result).toStrictEqual(expected);
|
|
89
110
|
});
|
|
90
111
|
});
|
|
91
112
|
|
|
92
|
-
});
|
|
113
|
+
});
|