hive-stream 3.0.0 → 3.0.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 (101) hide show
  1. package/AGENTS.md +35 -0
  2. package/DOCUMENTATION.md +380 -0
  3. package/README.md +113 -22
  4. package/dist/actions.d.ts +3 -3
  5. package/dist/actions.js +7 -7
  6. package/dist/actions.js.map +1 -1
  7. package/dist/adapters/base.adapter.d.ts +19 -1
  8. package/dist/adapters/base.adapter.js +16 -0
  9. package/dist/adapters/base.adapter.js.map +1 -1
  10. package/dist/adapters/mongodb.adapter.d.ts +5 -11
  11. package/dist/adapters/mongodb.adapter.js +10 -10
  12. package/dist/adapters/mongodb.adapter.js.map +1 -1
  13. package/dist/adapters/postgresql.adapter.d.ts +17 -0
  14. package/dist/adapters/postgresql.adapter.js +99 -8
  15. package/dist/adapters/postgresql.adapter.js.map +1 -1
  16. package/dist/adapters/sqlite.adapter.d.ts +17 -0
  17. package/dist/adapters/sqlite.adapter.js +99 -8
  18. package/dist/adapters/sqlite.adapter.js.map +1 -1
  19. package/dist/api.js +86 -0
  20. package/dist/api.js.map +1 -1
  21. package/dist/config.d.ts +3 -0
  22. package/dist/config.js +6 -3
  23. package/dist/config.js.map +1 -1
  24. package/dist/contracts/coinflip.contract.d.ts +8 -26
  25. package/dist/contracts/coinflip.contract.js +123 -144
  26. package/dist/contracts/coinflip.contract.js.map +1 -1
  27. package/dist/contracts/contract.d.ts +3 -0
  28. package/dist/contracts/contract.js +26 -0
  29. package/dist/contracts/contract.js.map +1 -0
  30. package/dist/contracts/dice.contract.d.ts +9 -36
  31. package/dist/contracts/dice.contract.js +135 -200
  32. package/dist/contracts/dice.contract.js.map +1 -1
  33. package/dist/contracts/exchange.contract.d.ts +11 -0
  34. package/dist/contracts/exchange.contract.js +492 -0
  35. package/dist/contracts/exchange.contract.js.map +1 -0
  36. package/dist/contracts/lotto.contract.d.ts +15 -19
  37. package/dist/contracts/lotto.contract.js +154 -162
  38. package/dist/contracts/lotto.contract.js.map +1 -1
  39. package/dist/contracts/nft.contract.d.ts +4 -0
  40. package/dist/contracts/nft.contract.js +65 -0
  41. package/dist/contracts/nft.contract.js.map +1 -1
  42. package/dist/contracts/poll.contract.d.ts +4 -0
  43. package/dist/contracts/poll.contract.js +105 -0
  44. package/dist/contracts/poll.contract.js.map +1 -0
  45. package/dist/contracts/rps.contract.d.ts +9 -0
  46. package/dist/contracts/rps.contract.js +217 -0
  47. package/dist/contracts/rps.contract.js.map +1 -0
  48. package/dist/contracts/tipjar.contract.d.ts +4 -0
  49. package/dist/contracts/tipjar.contract.js +60 -0
  50. package/dist/contracts/tipjar.contract.js.map +1 -0
  51. package/dist/contracts/token.contract.d.ts +3 -17
  52. package/dist/contracts/token.contract.js +128 -80
  53. package/dist/contracts/token.contract.js.map +1 -1
  54. package/dist/exchanges/coingecko.d.ts +7 -1
  55. package/dist/exchanges/coingecko.js +38 -21
  56. package/dist/exchanges/coingecko.js.map +1 -1
  57. package/dist/exchanges/exchange.d.ts +15 -8
  58. package/dist/exchanges/exchange.js +65 -11
  59. package/dist/exchanges/exchange.js.map +1 -1
  60. package/dist/hive-rates.d.ts +29 -4
  61. package/dist/hive-rates.js +179 -92
  62. package/dist/hive-rates.js.map +1 -1
  63. package/dist/index.d.ts +10 -3
  64. package/dist/index.js +18 -4
  65. package/dist/index.js.map +1 -1
  66. package/dist/streamer.d.ts +101 -8
  67. package/dist/streamer.js +410 -140
  68. package/dist/streamer.js.map +1 -1
  69. package/dist/test.js +11 -12
  70. package/dist/test.js.map +1 -1
  71. package/dist/types/hive-stream.d.ts +85 -14
  72. package/dist/types/rates.d.ts +47 -0
  73. package/dist/types/rates.js +29 -0
  74. package/dist/types/rates.js.map +1 -0
  75. package/dist/utils.d.ts +318 -11
  76. package/dist/utils.js +804 -115
  77. package/dist/utils.js.map +1 -1
  78. package/examples/contracts/README.md +8 -0
  79. package/examples/contracts/exchange.ts +38 -0
  80. package/examples/contracts/poll.ts +21 -0
  81. package/examples/contracts/rps.ts +19 -0
  82. package/examples/contracts/tipjar.ts +19 -0
  83. package/package.json +20 -19
  84. package/tests/actions.spec.ts +7 -7
  85. package/tests/adapters/actions-persistence.spec.ts +4 -4
  86. package/tests/adapters/sqlite.adapter.spec.ts +2 -2
  87. package/tests/contracts/coinflip.contract.spec.ts +26 -154
  88. package/tests/contracts/dice.contract.spec.ts +24 -140
  89. package/tests/contracts/exchange.contract.spec.ts +84 -0
  90. package/tests/contracts/lotto.contract.spec.ts +30 -295
  91. package/tests/contracts/token.contract.spec.ts +72 -316
  92. package/tests/exchanges/coingecko.exchange.spec.ts +169 -0
  93. package/tests/exchanges/exchange.base.spec.ts +246 -0
  94. package/tests/helpers/mock-fetch.ts +165 -0
  95. package/tests/hive-chain-features.spec.ts +238 -0
  96. package/tests/hive-rates.spec.ts +443 -0
  97. package/tests/integration/hive-rates.integration.spec.ts +35 -0
  98. package/tests/streamer-actions.spec.ts +29 -18
  99. package/tests/streamer.spec.ts +142 -49
  100. package/tests/types/rates.spec.ts +216 -0
  101. package/tests/utils.spec.ts +27 -6
@@ -0,0 +1,443 @@
1
+ import { HiveRates } from '../src/hive-rates';
2
+ import { CoinGeckoExchange } from '../src/exchanges/coingecko';
3
+ import { Exchange } from '../src/exchanges/exchange';
4
+ import { NetworkError, ValidationError, RatesError } from '../src/types/rates';
5
+
6
+ // Mock fetch for tests
7
+ const mockFetch = jest.fn() as jest.MockedFunction<typeof fetch>;
8
+ global.fetch = mockFetch;
9
+
10
+ class MockExchange extends Exchange {
11
+ public readonly exchangeId = 'mock-exchange';
12
+ public shouldSucceed = true;
13
+ public mockHiveRate = 0.5;
14
+ public mockHbdRate = 1.0;
15
+
16
+ constructor(config: Partial<{ cacheDuration: number; maxRetries: number; retryDelay: number }> = {}) {
17
+ // Use shorter timeouts for tests
18
+ super({
19
+ cacheDuration: config.cacheDuration ?? 100,
20
+ maxRetries: config.maxRetries ?? 1,
21
+ retryDelay: config.retryDelay ?? 10
22
+ });
23
+ }
24
+
25
+ public async fetchRates(): Promise<boolean> {
26
+ if (!this.shouldSucceed) {
27
+ throw new Error('Mock exchange error');
28
+ }
29
+
30
+ this.rateUsdHive = this.mockHiveRate;
31
+ this.rateUsdHbd = this.mockHbdRate;
32
+ return true;
33
+ }
34
+ }
35
+
36
+ describe('HiveRates', () => {
37
+ let hiveRates: HiveRates;
38
+ let mockExchange: MockExchange;
39
+
40
+ beforeEach(() => {
41
+ mockExchange = new MockExchange({ cacheDuration: 100 });
42
+ hiveRates = new HiveRates({ cacheDuration: 100 }, [mockExchange]);
43
+ mockFetch.mockClear();
44
+ jest.clearAllTimers();
45
+ jest.useFakeTimers();
46
+ });
47
+
48
+ afterEach(() => {
49
+ jest.useRealTimers();
50
+ });
51
+
52
+ describe('constructor', () => {
53
+ it('should use default CoinGecko exchange when no custom exchanges provided', () => {
54
+ const rates = new HiveRates();
55
+ expect(rates).toBeDefined();
56
+ });
57
+
58
+ it('should use custom exchanges when provided', () => {
59
+ const customExchange = new MockExchange();
60
+ const rates = new HiveRates({}, [customExchange]);
61
+ expect(rates).toBeDefined();
62
+ });
63
+
64
+ it('should use default config when none provided', () => {
65
+ const rates = new HiveRates();
66
+ expect(rates).toBeDefined();
67
+ });
68
+ });
69
+
70
+ describe('fetchRates', () => {
71
+ beforeEach(() => {
72
+ // Mock successful fiat API response
73
+ mockFetch.mockResolvedValue({
74
+ ok: true,
75
+ json: async () => ({
76
+ usd: {
77
+ eur: 0.85,
78
+ gbp: 0.73,
79
+ jpy: 110,
80
+ cad: 1.25,
81
+ aud: 1.35
82
+ }
83
+ }),
84
+ } as Response);
85
+ });
86
+
87
+ it('should fetch rates successfully', async () => {
88
+ const result = await hiveRates.fetchRates();
89
+
90
+ expect(result).toBe(true);
91
+
92
+ // Check crypto rates
93
+ const cryptoRates = hiveRates.getCryptoRates();
94
+ expect(cryptoRates.hive).toBe(0.5);
95
+ expect(cryptoRates.hbd).toBe(1.0);
96
+
97
+ // Check some cross rates
98
+ expect(hiveRates.getRate('EUR', 'HIVE')).toBeCloseTo(0.5 * 0.85);
99
+ expect(hiveRates.getRate('GBP', 'HBD')).toBeCloseTo(1.0 * 0.73);
100
+ });
101
+
102
+ it('should handle crypto fetch failure gracefully', async () => {
103
+ mockExchange.shouldSucceed = false;
104
+
105
+ const result = await hiveRates.fetchRates();
106
+
107
+ // Should still succeed if fiat rates succeed
108
+ expect(result).toBe(true);
109
+ }, 10000);
110
+
111
+ it('should handle fiat fetch failure gracefully', async () => {
112
+ mockFetch.mockRejectedValue(new Error('Fiat API error'));
113
+
114
+ // Should still succeed if crypto rates succeed
115
+ const result = await hiveRates.fetchRates();
116
+ expect(result).toBe(true);
117
+ });
118
+
119
+ it('should return false when both crypto and fiat fail', async () => {
120
+ mockExchange.shouldSucceed = false;
121
+ mockFetch.mockRejectedValue(new Error('All APIs failed'));
122
+
123
+ const result = await hiveRates.fetchRates();
124
+ expect(result).toBe(false);
125
+ }, 10000);
126
+
127
+ it('should calculate average rates from multiple exchanges', async () => {
128
+ const exchange1 = new MockExchange();
129
+ exchange1.mockHiveRate = 0.4;
130
+ exchange1.mockHbdRate = 0.9;
131
+
132
+ const exchange2 = new MockExchange();
133
+ exchange2.mockHiveRate = 0.6;
134
+ exchange2.mockHbdRate = 1.1;
135
+
136
+ const rates = new HiveRates({ cacheDuration: 100 }, [exchange1, exchange2]);
137
+
138
+ await rates.fetchRates();
139
+
140
+ const cryptoRates = rates.getCryptoRates();
141
+ expect(cryptoRates.hive).toBe(0.5); // Average of 0.4 and 0.6
142
+ expect(cryptoRates.hbd).toBe(1.0); // Average of 0.9 and 1.1
143
+ });
144
+
145
+ it('should ignore invalid crypto rates in average calculation', async () => {
146
+ const exchange1 = new MockExchange();
147
+ exchange1.mockHiveRate = 0.5;
148
+ exchange1.mockHbdRate = 1.0;
149
+
150
+ const exchange2 = new MockExchange();
151
+ exchange2.mockHiveRate = 0; // Invalid
152
+ exchange2.mockHbdRate = -1; // Invalid
153
+
154
+ const rates = new HiveRates({ cacheDuration: 100 }, [exchange1, exchange2]);
155
+
156
+ await rates.fetchRates();
157
+
158
+ const cryptoRates = rates.getCryptoRates();
159
+ expect(cryptoRates.hive).toBe(0.5); // Only valid rate used
160
+ expect(cryptoRates.hbd).toBe(1.0); // Only valid rate used
161
+ });
162
+ });
163
+
164
+ describe('rate retrieval methods', () => {
165
+ beforeEach(async () => {
166
+ mockFetch.mockResolvedValue({
167
+ ok: true,
168
+ json: async () => ({
169
+ usd: {
170
+ eur: 0.85,
171
+ gbp: 0.73,
172
+ jpy: 110
173
+ }
174
+ }),
175
+ } as Response);
176
+
177
+ await hiveRates.fetchRates();
178
+ });
179
+
180
+ it('should get specific rate correctly', () => {
181
+ const rate = hiveRates.getRate('EUR', 'HIVE');
182
+ expect(rate).toBeCloseTo(0.5 * 0.85);
183
+ });
184
+
185
+ it('should return null for non-existent rate', () => {
186
+ const rate = hiveRates.getRate('XYZ' as any, 'HIVE');
187
+ expect(rate).toBeNull();
188
+ });
189
+
190
+ it('should get all rates', () => {
191
+ const allRates = hiveRates.getAllRates();
192
+ expect(typeof allRates).toBe('object');
193
+ expect(allRates['EUR_HIVE']).toBeCloseTo(0.5 * 0.85);
194
+ expect(allRates['GBP_HBD']).toBeCloseTo(1.0 * 0.73);
195
+ });
196
+
197
+ it('should get crypto rates only', () => {
198
+ const cryptoRates = hiveRates.getCryptoRates();
199
+ expect(cryptoRates.hive).toBe(0.5);
200
+ expect(cryptoRates.hbd).toBe(1.0);
201
+ });
202
+
203
+ it('should get fiat rates only', () => {
204
+ const fiatRates = hiveRates.getFiatRates();
205
+ expect(fiatRates.EUR).toBe(0.85);
206
+ expect(fiatRates.GBP).toBe(0.73);
207
+ expect(fiatRates.JPY).toBe(110);
208
+ });
209
+
210
+ it('should support legacy method', () => {
211
+ const rate = hiveRates.fiatToHiveRate('EUR', 'HIVE');
212
+ expect(rate).toBeCloseTo(0.5 * 0.85);
213
+ });
214
+ });
215
+
216
+ describe('caching behavior', () => {
217
+ beforeEach(() => {
218
+ mockFetch.mockResolvedValue({
219
+ ok: true,
220
+ json: async () => ({
221
+ usd: { eur: 0.85 }
222
+ }),
223
+ } as Response);
224
+ });
225
+
226
+ it('should respect cache duration', async () => {
227
+ // First fetch
228
+ await hiveRates.fetchRates();
229
+
230
+ // Second fetch within cache duration should not call API
231
+ mockFetch.mockClear();
232
+ await hiveRates.fetchRates();
233
+
234
+ expect(mockFetch).not.toHaveBeenCalled();
235
+ });
236
+
237
+ it('should fetch when cache expires', async () => {
238
+ // First fetch
239
+ await hiveRates.fetchRates();
240
+
241
+ // Advance time beyond cache duration
242
+ jest.advanceTimersByTime(150);
243
+
244
+ // Second fetch should call API
245
+ mockFetch.mockClear();
246
+ await hiveRates.fetchRates();
247
+
248
+ expect(mockFetch).toHaveBeenCalled();
249
+ });
250
+
251
+ it('should track last fetch times', async () => {
252
+ const timesBefore = hiveRates.getLastFetchTimes();
253
+ expect(timesBefore.crypto).toBeUndefined();
254
+ expect(timesBefore.fiat).toBeUndefined();
255
+
256
+ await hiveRates.fetchRates();
257
+
258
+ const timesAfter = hiveRates.getLastFetchTimes();
259
+ expect(timesAfter.crypto).toBeDefined();
260
+ expect(timesAfter.fiat).toBeDefined();
261
+ });
262
+
263
+ it('should report cache validity', async () => {
264
+ const validityBefore = hiveRates.isCacheValid();
265
+ expect(validityBefore.crypto).toBe(false);
266
+ expect(validityBefore.fiat).toBe(false);
267
+
268
+ await hiveRates.fetchRates();
269
+
270
+ const validityAfter = hiveRates.isCacheValid();
271
+ expect(validityAfter.crypto).toBe(true);
272
+ expect(validityAfter.fiat).toBe(true);
273
+
274
+ jest.advanceTimersByTime(150);
275
+
276
+ const validityExpired = hiveRates.isCacheValid();
277
+ expect(validityExpired.crypto).toBe(false);
278
+ expect(validityExpired.fiat).toBe(false);
279
+ });
280
+ });
281
+
282
+ describe('fiat API handling', () => {
283
+ it('should try multiple fiat endpoints', async () => {
284
+ // First endpoint fails, second succeeds
285
+ mockFetch
286
+ .mockRejectedValueOnce(new Error('First endpoint failed'))
287
+ .mockResolvedValueOnce({
288
+ ok: true,
289
+ json: async () => ({
290
+ usd: { eur: 0.85 }
291
+ }),
292
+ } as Response);
293
+
294
+ const result = await hiveRates.fetchRates();
295
+ expect(result).toBe(true);
296
+ expect(mockFetch).toHaveBeenCalledTimes(2);
297
+ });
298
+
299
+ it('should handle all fiat endpoints failing', async () => {
300
+ mockFetch.mockRejectedValue(new Error('All endpoints failed'));
301
+
302
+ // This should still succeed because crypto fetch succeeds
303
+ const result = await hiveRates.fetchRates();
304
+ expect(result).toBe(true);
305
+ });
306
+
307
+ it('should validate fiat response format', async () => {
308
+ mockFetch.mockResolvedValue({
309
+ ok: true,
310
+ json: async () => ({ invalid: 'format' }),
311
+ } as Response);
312
+
313
+ // Should still succeed because crypto fetch succeeds, fiat just fails silently
314
+ const result = await hiveRates.fetchRates();
315
+ expect(result).toBe(true);
316
+ });
317
+
318
+ it('should filter invalid fiat rates', async () => {
319
+ mockFetch.mockResolvedValue({
320
+ ok: true,
321
+ json: async () => ({
322
+ usd: {
323
+ eur: 0.85,
324
+ invalid: 'string',
325
+ nan: NaN,
326
+ negative: -1,
327
+ zero: 0
328
+ }
329
+ }),
330
+ } as Response);
331
+
332
+ await hiveRates.fetchRates();
333
+
334
+ const fiatRates = hiveRates.getFiatRates();
335
+ expect(fiatRates.EUR).toBe(0.85);
336
+ expect(fiatRates.INVALID).toBeUndefined();
337
+ expect(fiatRates.NAN).toBeUndefined();
338
+ expect(fiatRates.NEGATIVE).toBeUndefined();
339
+ expect(fiatRates.ZERO).toBeUndefined();
340
+ });
341
+
342
+ it('should convert currency codes to uppercase', async () => {
343
+ mockFetch.mockResolvedValue({
344
+ ok: true,
345
+ json: async () => ({
346
+ usd: {
347
+ eur: 0.85,
348
+ gbp: 0.73
349
+ }
350
+ }),
351
+ } as Response);
352
+
353
+ await hiveRates.fetchRates();
354
+
355
+ const fiatRates = hiveRates.getFiatRates();
356
+ expect(fiatRates.EUR).toBe(0.85);
357
+ expect(fiatRates.GBP).toBe(0.73);
358
+ });
359
+ });
360
+
361
+ describe('error handling', () => {
362
+ it('should throw RatesError for unexpected errors', async () => {
363
+ // Mock an unexpected error in fetchCryptoRates
364
+ const originalMethod = hiveRates['fetchCryptoRates'];
365
+ hiveRates['fetchCryptoRates'] = jest.fn().mockImplementation(() => {
366
+ throw new TypeError('Unexpected error');
367
+ });
368
+
369
+ await expect(hiveRates.fetchRates()).rejects.toThrow(RatesError);
370
+ await expect(hiveRates.fetchRates()).rejects.toThrow('Unexpected error');
371
+
372
+ // Restore original method
373
+ hiveRates['fetchCryptoRates'] = originalMethod;
374
+ });
375
+
376
+ it('should preserve known error types', async () => {
377
+ mockFetch.mockResolvedValue({
378
+ ok: true,
379
+ json: async () => ({ invalid: 'format' }),
380
+ } as Response);
381
+
382
+ // Should still succeed because crypto fetch succeeds, fiat just fails silently
383
+ const result = await hiveRates.fetchRates();
384
+ expect(result).toBe(true);
385
+ });
386
+ });
387
+
388
+ describe('edge cases', () => {
389
+ it('should handle empty fiat rates response', async () => {
390
+ mockFetch.mockResolvedValue({
391
+ ok: true,
392
+ json: async () => ({
393
+ usd: {}
394
+ }),
395
+ } as Response);
396
+
397
+ // Should still succeed because crypto fetch succeeds, fiat just fails silently
398
+ const result = await hiveRates.fetchRates();
399
+ expect(result).toBe(true);
400
+ });
401
+
402
+ it('should handle no valid crypto rates', async () => {
403
+ mockExchange.mockHiveRate = 0;
404
+ mockExchange.mockHbdRate = 0;
405
+
406
+ mockFetch.mockResolvedValue({
407
+ ok: true,
408
+ json: async () => ({
409
+ usd: { eur: 0.85 }
410
+ }),
411
+ } as Response);
412
+
413
+ const result = await hiveRates.fetchRates();
414
+ expect(result).toBe(true);
415
+
416
+ const cryptoRates = hiveRates.getCryptoRates();
417
+ expect(cryptoRates.hive).toBeUndefined();
418
+ expect(cryptoRates.hbd).toBeUndefined();
419
+ });
420
+
421
+ it('should handle partial crypto rates', async () => {
422
+ mockExchange.mockHiveRate = 0.5;
423
+ mockExchange.mockHbdRate = 0; // Invalid
424
+
425
+ mockFetch.mockResolvedValue({
426
+ ok: true,
427
+ json: async () => ({
428
+ usd: { eur: 0.85 }
429
+ }),
430
+ } as Response);
431
+
432
+ await hiveRates.fetchRates();
433
+
434
+ const cryptoRates = hiveRates.getCryptoRates();
435
+ expect(cryptoRates.hive).toBe(0.5);
436
+ expect(cryptoRates.hbd).toBeUndefined();
437
+
438
+ // Should still calculate HIVE rates but not HBD rates
439
+ expect(hiveRates.getRate('EUR', 'HIVE')).toBeCloseTo(0.5 * 0.85);
440
+ expect(hiveRates.getRate('EUR', 'HBD')).toBeNull();
441
+ });
442
+ });
443
+ });
@@ -0,0 +1,35 @@
1
+ import { HiveRates } from '../../src/hive-rates';
2
+
3
+ const shouldRunIntegration = process.env.RUN_INTEGRATION === 'true';
4
+
5
+ describe('HiveRates Integration Test', () => {
6
+ (shouldRunIntegration ? it : it.skip)('should fetch real rates successfully', async () => {
7
+ const hiveRates = new HiveRates();
8
+
9
+ const result = await hiveRates.fetchRates();
10
+ expect(result).toBe(true);
11
+
12
+ // Check that we have some crypto rates
13
+ const cryptoRates = hiveRates.getCryptoRates();
14
+ expect(cryptoRates.hive).toBeGreaterThan(0);
15
+ expect(cryptoRates.hbd).toBeGreaterThan(0);
16
+
17
+ // Check that we have some fiat rates
18
+ const fiatRates = hiveRates.getFiatRates();
19
+ expect(Object.keys(fiatRates).length).toBeGreaterThan(0);
20
+ expect(fiatRates.EUR).toBeGreaterThan(0);
21
+
22
+ // Check that cross rates are calculated
23
+ const eurHiveRate = hiveRates.getRate('EUR', 'HIVE');
24
+ expect(eurHiveRate).toBeGreaterThan(0);
25
+
26
+ const gbpHbdRate = hiveRates.getRate('GBP', 'HBD');
27
+ expect(gbpHbdRate).toBeGreaterThan(0);
28
+
29
+ console.log('Sample rates:');
30
+ console.log(`HIVE: $${cryptoRates.hive?.toFixed(6)}`);
31
+ console.log(`HBD: $${cryptoRates.hbd?.toFixed(6)}`);
32
+ console.log(`EUR/HIVE: ${eurHiveRate?.toFixed(6)}`);
33
+ console.log(`GBP/HBD: ${gbpHbdRate?.toFixed(6)}`);
34
+ }, 30000); // 30 second timeout for real API calls
35
+ });
@@ -1,10 +1,13 @@
1
1
  import { TimeAction } from '../src/actions';
2
2
  import { Streamer } from '../src/streamer';
3
+ import { action, defineContract } from '../src/contracts/contract';
3
4
 
4
5
  describe('Streamer Time-based Actions', () => {
5
6
  let streamer: Streamer;
6
7
  let mockAdapter: any;
7
8
  let mockContract: any;
9
+ let testHandler: jest.Mock;
10
+ let asyncTestHandler: jest.Mock;
8
11
 
9
12
  beforeEach(async () => {
10
13
  mockAdapter = {
@@ -23,12 +26,20 @@ describe('Streamer Time-based Actions', () => {
23
26
  replace: jest.fn().mockResolvedValue(true)
24
27
  };
25
28
 
26
- mockContract = {
27
- testMethod: jest.fn(),
28
- asyncTestMethod: jest.fn().mockResolvedValue(true),
29
- create: jest.fn(),
30
- destroy: jest.fn()
31
- };
29
+ testHandler = jest.fn();
30
+ asyncTestHandler = jest.fn().mockResolvedValue(true);
31
+
32
+ mockContract = defineContract({
33
+ name: 'testcontract',
34
+ hooks: {
35
+ create: jest.fn(),
36
+ destroy: jest.fn()
37
+ },
38
+ actions: {
39
+ testMethod: action(testHandler, { trigger: 'time' }),
40
+ asyncTestMethod: action(asyncTestHandler, { trigger: 'time' })
41
+ }
42
+ });
32
43
 
33
44
  streamer = new Streamer({
34
45
  JSON_ID: 'testing',
@@ -36,7 +47,7 @@ describe('Streamer Time-based Actions', () => {
36
47
  });
37
48
 
38
49
  await streamer.registerAdapter(mockAdapter);
39
- streamer.registerContract('testcontract', mockContract);
50
+ await streamer.registerContract(mockContract);
40
51
  });
41
52
 
42
53
  afterEach(async () => {
@@ -75,11 +86,11 @@ describe('Streamer Time-based Actions', () => {
75
86
  );
76
87
  });
77
88
 
78
- test('Should throw error when registering action for non-existent method', async () => {
89
+ test('Should throw error when registering action for non-existent action', async () => {
79
90
  const action = new TimeAction('1m', 'test-action', 'testcontract', 'nonexistentMethod');
80
91
 
81
92
  await expect(streamer.registerAction(action)).rejects.toThrow(
82
- 'Method \'nonexistentMethod\' not found in contract \'testcontract\' for action \'test-action\''
93
+ 'Action \'nonexistentMethod\' not found in contract \'testcontract\' for action \'test-action\''
83
94
  );
84
95
  });
85
96
  });
@@ -166,7 +177,7 @@ describe('Streamer Time-based Actions', () => {
166
177
 
167
178
  await streamer['processActions']();
168
179
 
169
- expect(mockContract.testMethod).toHaveBeenCalledWith({ testData: 'value' });
180
+ expect(testHandler).toHaveBeenCalledWith({ testData: 'value' }, expect.any(Object));
170
181
  expect(testAction.executionCount).toBe(1);
171
182
  expect(testAction.lastExecution).toBeInstanceOf(Date);
172
183
  });
@@ -180,7 +191,7 @@ describe('Streamer Time-based Actions', () => {
180
191
 
181
192
  await streamer['processActions']();
182
193
 
183
- expect(mockContract.testMethod).not.toHaveBeenCalled();
194
+ expect(testHandler).not.toHaveBeenCalled();
184
195
  expect(testAction.executionCount).toBe(0);
185
196
  });
186
197
 
@@ -190,7 +201,7 @@ describe('Streamer Time-based Actions', () => {
190
201
 
191
202
  await streamer['processActions']();
192
203
 
193
- expect(mockContract.testMethod).not.toHaveBeenCalled();
204
+ expect(testHandler).not.toHaveBeenCalled();
194
205
  });
195
206
 
196
207
  test('Should not execute actions that have reached max executions', async () => {
@@ -200,11 +211,11 @@ describe('Streamer Time-based Actions', () => {
200
211
 
201
212
  await streamer['processActions']();
202
213
 
203
- expect(mockContract.testMethod).not.toHaveBeenCalled();
214
+ expect(testHandler).not.toHaveBeenCalled();
204
215
  });
205
216
 
206
217
  test('Should handle contract method errors gracefully', async () => {
207
- mockContract.testMethod.mockImplementation(() => {
218
+ testHandler.mockImplementation(() => {
208
219
  throw new Error('Contract method error');
209
220
  });
210
221
 
@@ -213,7 +224,7 @@ describe('Streamer Time-based Actions', () => {
213
224
  // Should not throw, but log error
214
225
  await expect(streamer['processActions']()).resolves.toBeUndefined();
215
226
 
216
- expect(mockContract.testMethod).toHaveBeenCalled();
227
+ expect(testHandler).toHaveBeenCalled();
217
228
  // Action should not increment execution count on error
218
229
  expect(testAction.executionCount).toBe(0);
219
230
  });
@@ -224,7 +235,7 @@ describe('Streamer Time-based Actions', () => {
224
235
 
225
236
  await streamer['processActions']();
226
237
 
227
- expect(mockContract.testMethod).toHaveBeenCalled();
238
+ expect(testHandler).toHaveBeenCalled();
228
239
  expect(streamer.getActions()).toHaveLength(0); // Action should be removed
229
240
  });
230
241
  });
@@ -255,9 +266,9 @@ describe('Streamer Time-based Actions', () => {
255
266
 
256
267
  await streamer['processActions']();
257
268
 
258
- expect(mockContract.testMethod).toHaveBeenCalled();
269
+ expect(testHandler).toHaveBeenCalled();
259
270
  expect(action.executionCount).toBe(1);
260
271
  });
261
272
  });
262
273
  });
263
- });
274
+ });