hive-stream 2.0.5 → 3.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/.claude/settings.local.json +12 -0
- package/.env.example +2 -2
- package/.travis.yml +11 -11
- package/CHANGELOG.md +166 -0
- package/CLAUDE.md +75 -0
- package/LICENSE +21 -21
- package/README.md +338 -238
- package/dist/actions.d.ts +41 -10
- package/dist/actions.js +126 -23
- package/dist/actions.js.map +1 -1
- package/dist/adapters/base.adapter.d.ts +25 -25
- package/dist/adapters/base.adapter.js +63 -49
- package/dist/adapters/base.adapter.js.map +1 -1
- package/dist/adapters/mongodb.adapter.d.ts +50 -37
- package/dist/adapters/mongodb.adapter.js +363 -158
- package/dist/adapters/mongodb.adapter.js.map +1 -1
- package/dist/adapters/postgresql.adapter.d.ts +49 -0
- package/dist/adapters/postgresql.adapter.js +507 -0
- package/dist/adapters/postgresql.adapter.js.map +1 -0
- package/dist/adapters/sqlite.adapter.d.ts +40 -41
- package/dist/adapters/sqlite.adapter.js +470 -397
- package/dist/adapters/sqlite.adapter.js.map +1 -1
- package/dist/api.d.ts +6 -6
- package/dist/api.js +95 -55
- package/dist/api.js.map +1 -1
- package/dist/config.d.ts +16 -16
- package/dist/config.js +18 -18
- package/dist/config.js.map +1 -1
- package/dist/contracts/coinflip.contract.d.ts +27 -14
- package/dist/contracts/coinflip.contract.js +253 -94
- package/dist/contracts/coinflip.contract.js.map +1 -1
- package/dist/contracts/dice.contract.d.ts +37 -29
- package/dist/contracts/dice.contract.js +282 -155
- package/dist/contracts/dice.contract.js.map +1 -1
- package/dist/contracts/lotto.contract.d.ts +20 -20
- package/dist/contracts/lotto.contract.js +246 -246
- package/dist/contracts/nft.contract.d.ts +24 -0
- package/dist/contracts/nft.contract.js +533 -0
- package/dist/contracts/nft.contract.js.map +1 -0
- package/dist/contracts/token.contract.d.ts +18 -0
- package/dist/contracts/token.contract.js +263 -0
- package/dist/contracts/token.contract.js.map +1 -0
- package/dist/exchanges/bittrex.d.ts +6 -6
- package/dist/exchanges/bittrex.js +34 -34
- package/dist/exchanges/coingecko.d.ts +5 -0
- package/dist/exchanges/coingecko.js +40 -0
- package/dist/exchanges/coingecko.js.map +1 -0
- package/dist/exchanges/exchange.d.ts +9 -9
- package/dist/exchanges/exchange.js +26 -26
- package/dist/hive-rates.d.ts +9 -9
- package/dist/hive-rates.js +121 -75
- package/dist/hive-rates.js.map +1 -1
- package/dist/index.d.ts +12 -11
- package/dist/index.js +33 -32
- package/dist/index.js.map +1 -1
- package/dist/streamer.d.ts +140 -93
- package/dist/streamer.js +793 -545
- package/dist/streamer.js.map +1 -1
- package/dist/test.d.ts +1 -1
- package/dist/test.js +25 -25
- package/dist/test.js.map +1 -1
- package/dist/types/hive-stream.d.ts +35 -6
- package/dist/types/hive-stream.js +2 -2
- package/dist/utils.d.ts +27 -27
- package/dist/utils.js +271 -261
- package/dist/utils.js.map +1 -1
- package/ecosystem.config.js +17 -17
- package/jest.config.js +8 -8
- package/package.json +53 -48
- package/test-contract-block.md +18 -18
- package/tests/actions.spec.ts +252 -0
- package/tests/adapters/actions-persistence.spec.ts +144 -0
- package/tests/adapters/postgresql.adapter.spec.ts +127 -0
- package/tests/adapters/sqlite.adapter.spec.ts +180 -42
- package/tests/contracts/coinflip.contract.spec.ts +221 -131
- package/tests/contracts/dice.contract.spec.ts +202 -159
- package/tests/contracts/entrants.json +728 -728
- package/tests/contracts/lotto.contract.spec.ts +323 -323
- package/tests/contracts/nft.contract.spec.ts +948 -0
- package/tests/contracts/token.contract.spec.ts +334 -0
- package/tests/helpers/mock-adapter.ts +214 -0
- package/tests/setup.ts +29 -18
- package/tests/streamer-actions.spec.ts +263 -0
- package/tests/streamer.spec.ts +248 -151
- package/tests/utils.spec.ts +91 -94
- package/tsconfig.build.json +3 -22
- package/tslint.json +20 -20
- package/wallaby.js +26 -26
- package/.env +0 -3
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
import { PostgreSQLAdapter } from "../../src/adapters/postgresql.adapter";
|
|
2
|
+
|
|
3
|
+
describe('PostgreSQL Adapter', () => {
|
|
4
|
+
let sut: PostgreSQLAdapter;
|
|
5
|
+
|
|
6
|
+
beforeEach(() => {
|
|
7
|
+
sut = new PostgreSQLAdapter({
|
|
8
|
+
host: 'localhost',
|
|
9
|
+
port: 5432,
|
|
10
|
+
user: 'test',
|
|
11
|
+
password: 'test',
|
|
12
|
+
database: 'test_db'
|
|
13
|
+
});
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
test('constructor creates adapter with connection config', () => {
|
|
17
|
+
expect(sut).toBeInstanceOf(PostgreSQLAdapter);
|
|
18
|
+
expect(sut.getDb()).toBeDefined();
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
test('constructor works with connection string', () => {
|
|
22
|
+
const adapter = new PostgreSQLAdapter({
|
|
23
|
+
connectionString: 'postgresql://user:pass@localhost:5432/dbname',
|
|
24
|
+
ssl: true
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
expect(adapter).toBeInstanceOf(PostgreSQLAdapter);
|
|
28
|
+
expect(adapter.getDb()).toBeDefined();
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
test('constructor uses default values when not provided', () => {
|
|
32
|
+
const adapter = new PostgreSQLAdapter({
|
|
33
|
+
password: 'test'
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
expect(adapter).toBeInstanceOf(PostgreSQLAdapter);
|
|
37
|
+
expect(adapter.getDb()).toBeDefined();
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
test('processOperation sets internal state', async () => {
|
|
41
|
+
const testData = {
|
|
42
|
+
blockNumber: 12345,
|
|
43
|
+
blockId: 'test-block',
|
|
44
|
+
prevBlockId: 'prev-block',
|
|
45
|
+
trxId: 'test-tx',
|
|
46
|
+
blockTime: new Date()
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
await sut.processOperation({}, testData.blockNumber, testData.blockId, testData.prevBlockId, testData.trxId, testData.blockTime);
|
|
50
|
+
|
|
51
|
+
// Since these are private properties, we can't directly test them
|
|
52
|
+
// But we can verify the method doesn't throw
|
|
53
|
+
expect(true).toBe(true);
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
test('getDb returns knex instance', () => {
|
|
57
|
+
const db = sut.getDb();
|
|
58
|
+
expect(db).toBeDefined();
|
|
59
|
+
expect(typeof db).toBe('function'); // Knex instance is callable
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
test('destroy method is implemented', async () => {
|
|
63
|
+
// Create a mock knex instance
|
|
64
|
+
const mockKnex = {
|
|
65
|
+
destroy: jest.fn().mockResolvedValue(undefined)
|
|
66
|
+
};
|
|
67
|
+
(sut as any).db = mockKnex;
|
|
68
|
+
|
|
69
|
+
const result = await sut.destroy();
|
|
70
|
+
|
|
71
|
+
expect(result).toBe(true);
|
|
72
|
+
expect(mockKnex.destroy).toHaveBeenCalled();
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
test('query method handles raw SQL', async () => {
|
|
76
|
+
// Create a mock knex instance
|
|
77
|
+
const mockKnex = {
|
|
78
|
+
raw: jest.fn().mockResolvedValue({ rows: [{ id: 1, name: 'test' }] })
|
|
79
|
+
};
|
|
80
|
+
(sut as any).db = mockKnex;
|
|
81
|
+
|
|
82
|
+
const result = await sut.query('SELECT * FROM test WHERE id = $1', [1]);
|
|
83
|
+
|
|
84
|
+
expect(result).toEqual([{ id: 1, name: 'test' }]);
|
|
85
|
+
expect(mockKnex.raw).toHaveBeenCalledWith('SELECT * FROM test WHERE id = $1', [1]);
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
test('loadState handles missing state gracefully', async () => {
|
|
89
|
+
// Mock the query to return no results
|
|
90
|
+
const mockQuery = jest.fn().mockReturnValue({
|
|
91
|
+
select: jest.fn().mockReturnValue({
|
|
92
|
+
first: jest.fn().mockResolvedValue(null)
|
|
93
|
+
})
|
|
94
|
+
});
|
|
95
|
+
(sut as any).db = mockQuery;
|
|
96
|
+
|
|
97
|
+
const result = await sut.loadState();
|
|
98
|
+
|
|
99
|
+
expect(result).toBeNull();
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
test('saveState serializes actions correctly', async () => {
|
|
103
|
+
const testData = {
|
|
104
|
+
lastBlockNumber: 12345,
|
|
105
|
+
actions: [{ id: 'test', type: 'action' }]
|
|
106
|
+
};
|
|
107
|
+
|
|
108
|
+
// Mock the query chain
|
|
109
|
+
const mockMerge = jest.fn().mockResolvedValue(undefined);
|
|
110
|
+
const mockOnConflict = jest.fn().mockReturnValue({ merge: mockMerge });
|
|
111
|
+
const mockInsert = jest.fn().mockReturnValue({ onConflict: mockOnConflict });
|
|
112
|
+
const mockQuery = jest.fn().mockReturnValue({ insert: mockInsert });
|
|
113
|
+
(sut as any).db = mockQuery;
|
|
114
|
+
|
|
115
|
+
const result = await sut.saveState(testData);
|
|
116
|
+
|
|
117
|
+
expect(result).toBe(true);
|
|
118
|
+
expect(mockQuery).toHaveBeenCalledWith('params');
|
|
119
|
+
expect(mockInsert).toHaveBeenCalledWith({
|
|
120
|
+
id: 1,
|
|
121
|
+
actions: JSON.stringify(testData.actions),
|
|
122
|
+
lastBlockNumber: 12345
|
|
123
|
+
});
|
|
124
|
+
expect(mockOnConflict).toHaveBeenCalledWith('id');
|
|
125
|
+
expect(mockMerge).toHaveBeenCalled();
|
|
126
|
+
});
|
|
127
|
+
});
|
|
@@ -1,43 +1,181 @@
|
|
|
1
|
-
import { SqliteAdapter } from "../../src/adapters/sqlite.adapter";
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
1
|
+
import { SqliteAdapter } from "../../src/adapters/sqlite.adapter";
|
|
2
|
+
import fs from 'fs';
|
|
3
|
+
import path from 'path';
|
|
4
|
+
|
|
5
|
+
describe('SQLite Adapter', () => {
|
|
6
|
+
let sut: SqliteAdapter;
|
|
7
|
+
let testDbPath: string;
|
|
8
|
+
|
|
9
|
+
beforeEach(async () => {
|
|
10
|
+
testDbPath = path.resolve(__dirname, `../../src/adapters/hive-stream-test-basic-${Date.now()}-${Math.random()}.db`);
|
|
11
|
+
|
|
12
|
+
if (fs.existsSync(testDbPath)) {
|
|
13
|
+
fs.unlinkSync(testDbPath);
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
sut = new SqliteAdapter(testDbPath);
|
|
17
|
+
await sut.create();
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
afterEach(async () => {
|
|
21
|
+
if (sut && sut.getDb()) {
|
|
22
|
+
await sut.destroy();
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
if (fs.existsSync(testDbPath)) {
|
|
26
|
+
fs.unlinkSync(testDbPath);
|
|
27
|
+
}
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
test('find method returns values', async () => {
|
|
31
|
+
// Create a test table
|
|
32
|
+
await sut.db.schema.createTableIfNotExists('USERS', table => {
|
|
33
|
+
table.integer('id');
|
|
34
|
+
table.string('name');
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
// Insert test data
|
|
38
|
+
await sut.db('USERS').insert({ id: 1, name: 'John' });
|
|
39
|
+
|
|
40
|
+
const result = await sut.find('USERS', { id: 1, name: 'John' });
|
|
41
|
+
|
|
42
|
+
expect(result).toEqual([{ id: 1, name: 'John' }]);
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
test('findOne method returns value', async () => {
|
|
46
|
+
// Create a test table
|
|
47
|
+
await sut.db.schema.createTableIfNotExists('USERS', table => {
|
|
48
|
+
table.integer('id');
|
|
49
|
+
table.string('name');
|
|
50
|
+
table.string('email');
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
// Insert test data
|
|
54
|
+
await sut.db('USERS').insert({ id: 1, name: 'John', email: 'john@hotmail.com' });
|
|
55
|
+
|
|
56
|
+
const result = await sut.findOne('USERS', { id: 1, name: 'John', email: 'john@hotmail.com' });
|
|
57
|
+
|
|
58
|
+
expect(result).toEqual({ id: 1, name: 'John', email: 'john@hotmail.com' });
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
test('replace method replaces value', async () => {
|
|
62
|
+
// Create a test table
|
|
63
|
+
await sut.db.schema.createTableIfNotExists('USERS', table => {
|
|
64
|
+
table.integer('id').primary();
|
|
65
|
+
table.string('name');
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
// Insert initial data
|
|
69
|
+
await sut.db('USERS').insert({ id: 1, name: 'John' });
|
|
70
|
+
|
|
71
|
+
const result = await sut.replace('USERS', { id: 1 }, { id: 1, name: 'Johnny' });
|
|
72
|
+
|
|
73
|
+
expect(result).toEqual({ id: 1, name: 'Johnny' });
|
|
74
|
+
|
|
75
|
+
// Verify the data was actually replaced
|
|
76
|
+
const updatedRecord = await sut.db('USERS').where({ id: 1 }).first();
|
|
77
|
+
expect(updatedRecord).toEqual({ id: 1, name: 'Johnny' });
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
test('insert method inserts values', async () => {
|
|
81
|
+
// Create a test table
|
|
82
|
+
await sut.db.schema.createTableIfNotExists('USERS', table => {
|
|
83
|
+
table.integer('id');
|
|
84
|
+
table.string('name');
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
const result = await sut.insert('USERS', { id: 1, name: 'Alice' });
|
|
88
|
+
|
|
89
|
+
expect(result).toBe(true);
|
|
90
|
+
|
|
91
|
+
// Verify the data was actually inserted
|
|
92
|
+
const insertedRecord = await sut.db('USERS').where({ id: 1 }).first();
|
|
93
|
+
expect(insertedRecord).toEqual({ id: 1, name: 'Alice' });
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
test('loadState and saveState work correctly', async () => {
|
|
97
|
+
const testData = {
|
|
98
|
+
lastBlockNumber: 12345,
|
|
99
|
+
actions: [{ id: 'test-action', type: 'test' }]
|
|
100
|
+
};
|
|
101
|
+
|
|
102
|
+
// Save state
|
|
103
|
+
const saveResult = await sut.saveState(testData);
|
|
104
|
+
expect(saveResult).toBe(true);
|
|
105
|
+
|
|
106
|
+
// Load state
|
|
107
|
+
const loadedState = await sut.loadState();
|
|
108
|
+
expect(loadedState?.lastBlockNumber).toBe(12345);
|
|
109
|
+
expect(loadedState?.actions).toEqual([{ id: 'test-action', type: 'test' }]);
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
test('processTransfer works correctly', async () => {
|
|
113
|
+
const mockPayload = {
|
|
114
|
+
name: 'test-contract',
|
|
115
|
+
action: 'transfer',
|
|
116
|
+
payload: { amount: '100', recipient: 'bob' }
|
|
117
|
+
};
|
|
118
|
+
|
|
119
|
+
const mockMetadata = {
|
|
120
|
+
sender: 'alice',
|
|
121
|
+
amount: '100 HIVE'
|
|
122
|
+
};
|
|
123
|
+
|
|
124
|
+
// Set up transaction context
|
|
125
|
+
await sut.processOperation({}, 12345, 'block123', 'prevblock', 'tx123', new Date());
|
|
126
|
+
|
|
127
|
+
const result = await sut.processTransfer({}, mockPayload, mockMetadata);
|
|
128
|
+
expect(result).toBe(true);
|
|
129
|
+
|
|
130
|
+
// Verify the transfer was stored
|
|
131
|
+
const transfers = await sut.getTransfers();
|
|
132
|
+
expect(transfers).toHaveLength(1);
|
|
133
|
+
expect(transfers[0].sender).toBe('alice');
|
|
134
|
+
expect(transfers[0].contractName).toBe('test-contract');
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
test('processCustomJson works correctly', async () => {
|
|
138
|
+
const mockPayload = {
|
|
139
|
+
name: 'test-contract',
|
|
140
|
+
action: 'custom',
|
|
141
|
+
payload: { data: 'test' }
|
|
142
|
+
};
|
|
143
|
+
|
|
144
|
+
const mockMetadata = {
|
|
145
|
+
sender: 'alice',
|
|
146
|
+
isSignedWithActiveKey: true
|
|
147
|
+
};
|
|
148
|
+
|
|
149
|
+
// Set up transaction context
|
|
150
|
+
await sut.processOperation({}, 12345, 'block123', 'prevblock', 'tx123', new Date());
|
|
151
|
+
|
|
152
|
+
const result = await sut.processCustomJson({}, mockPayload, mockMetadata);
|
|
153
|
+
expect(result).toBe(true);
|
|
154
|
+
|
|
155
|
+
// Verify the custom JSON was stored
|
|
156
|
+
const customJson = await sut.getJson();
|
|
157
|
+
expect(customJson).toHaveLength(1);
|
|
158
|
+
expect(customJson[0].sender).toBe('alice');
|
|
159
|
+
expect(customJson[0].contractName).toBe('test-contract');
|
|
160
|
+
expect(customJson[0].isSignedWithActiveKey).toBe(1);
|
|
161
|
+
});
|
|
162
|
+
|
|
163
|
+
test('query method executes raw SQL', async () => {
|
|
164
|
+
// Create a test table
|
|
165
|
+
await sut.db.schema.createTableIfNotExists('USERS', table => {
|
|
166
|
+
table.integer('id');
|
|
167
|
+
table.string('name');
|
|
168
|
+
});
|
|
169
|
+
|
|
170
|
+
// Insert test data
|
|
171
|
+
await sut.db('USERS').insert({ id: 1, name: 'Alice' });
|
|
172
|
+
await sut.db('USERS').insert({ id: 2, name: 'Bob' });
|
|
173
|
+
|
|
174
|
+
// Test raw SQL query
|
|
175
|
+
const result = await sut.query('SELECT * FROM USERS WHERE name = ?', ['Alice']);
|
|
176
|
+
|
|
177
|
+
expect(result).toHaveLength(1);
|
|
178
|
+
expect(result[0].name).toBe('Alice');
|
|
179
|
+
expect(result[0].id).toBe(1);
|
|
180
|
+
});
|
|
43
181
|
});
|