Skip to content

Commit 28d479e

Browse files
authored
feat: pool config stats (#32)
* feat: pool config stats * var name typo fix
1 parent cb37c12 commit 28d479e

18 files changed

Lines changed: 356 additions & 467 deletions

tests/integration/api.poolManager.test.js

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,16 @@ test('Pool Manager API', { timeout: 90000 }, async (main) => {
131131
await worker.start()
132132
worker.worker.net_r0.jRequest = (publicKey, method, params) => {
133133
if (method === 'listThings') {
134+
if (params?.query?.id) {
135+
const thing = mockMiners.find(m => m.id === params.query.id)
136+
if (!thing) return Promise.resolve([])
137+
const withRackAndInfo = {
138+
...thing,
139+
rack: thing.id.startsWith('miner-') ? 'miner-am-s19xp' : 'container-unit-a',
140+
info: { ...thing.info, container: thing.tags?.unit || 'unit-A', poolConfig: thing.snap?.config?.pool_config ? 'stratum+tcp://btc.f2pool.com:3333' : null }
141+
}
142+
return Promise.resolve([withRackAndInfo])
143+
}
134144
return Promise.resolve(mockMiners)
135145
}
136146
if (method === 'getWrkExtData') {
@@ -344,4 +354,45 @@ test('Pool Manager API', { timeout: 90000 }, async (main) => {
344354
})
345355
})
346356

357+
await main.test('Api: auth/pools/config/:id', async (n) => {
358+
const thingId = 'miner-001'
359+
const api = `${appNodeBaseUrl}/auth/pools/config/${thingId}`
360+
361+
await n.test('api should fail for missing auth token', async (t) => {
362+
try {
363+
await httpClient.get(api, { encoding })
364+
t.fail()
365+
} catch (e) {
366+
t.is(e.response.message.includes('ERR_AUTH_FAIL'), true)
367+
}
368+
})
369+
370+
await n.test('api should succeed and return pool config for thing', async (t) => {
371+
const token = await getTestToken(testUser)
372+
const headers = { Authorization: `Bearer ${token}` }
373+
try {
374+
const res = await httpClient.get(api, { headers, encoding })
375+
t.ok(res.body)
376+
t.ok('poolConfig' in res.body)
377+
t.ok(typeof res.body.overriddenConfig === 'number')
378+
t.pass()
379+
} catch (e) {
380+
console.error('Pool thing config error:', e)
381+
t.fail()
382+
}
383+
})
384+
385+
await n.test('api should return 404 for unknown thing id', async (t) => {
386+
const token = await getTestToken(testUser)
387+
const headers = { Authorization: `Bearer ${token}` }
388+
const unknownApi = `${appNodeBaseUrl}/auth/pools/config/nonexistent-thing-id`
389+
try {
390+
await httpClient.get(unknownApi, { headers, encoding })
391+
t.fail()
392+
} catch (e) {
393+
t.ok(e.response?.message?.includes('ERR_THING_NOT_FOUND') || e.code === 'ERR_HTTP_REQUEST_FAILED' || e.statusCode === 404)
394+
t.pass()
395+
}
396+
})
397+
})
347398
})

tests/unit/handlers/auth.handlers.test.js

Lines changed: 9 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
const test = require('brittle')
44
const { getSiteName, extDataRoute, getUserInfo, newAuthToken, getUserPermissions } = require('../../../workers/lib/server/handlers/auth.handlers')
5+
const { createMockCtxWithOrks } = require('../helpers/mockHelpers')
56

67
test('getSiteName - returns site from context', (t) => {
78
const mockCtx = {
@@ -16,17 +17,10 @@ test('getSiteName - returns site from context', (t) => {
1617
})
1718

1819
test('extDataRoute - with query param', async (t) => {
19-
const mockCtx = {
20-
conf: {
21-
orks: [
22-
{ rpcPublicKey: 'key1' },
23-
{ rpcPublicKey: 'key2' }
24-
]
25-
},
26-
net_r0: {
27-
jRequest: async () => ({ data: 'test' })
28-
}
29-
}
20+
const mockCtx = createMockCtxWithOrks(
21+
[{ rpcPublicKey: 'key1' }, { rpcPublicKey: 'key2' }],
22+
async () => ({ data: 'test' })
23+
)
3024

3125
const mockReq = {
3226
query: {
@@ -42,16 +36,10 @@ test('extDataRoute - with query param', async (t) => {
4236
})
4337

4438
test('extDataRoute - without query param', async (t) => {
45-
const mockCtx = {
46-
conf: {
47-
orks: [
48-
{ rpcPublicKey: 'key1' }
49-
]
50-
},
51-
net_r0: {
52-
jRequest: async () => ({ data: 'test' })
53-
}
54-
}
39+
const mockCtx = createMockCtxWithOrks(
40+
[{ rpcPublicKey: 'key1' }],
41+
async () => ({ data: 'test' })
42+
)
5543

5644
const mockReq = {
5745
query: {

tests/unit/handlers/configs.handlers.test.js

Lines changed: 54 additions & 83 deletions
Original file line numberDiff line numberDiff line change
@@ -2,24 +2,23 @@
22

33
const test = require('brittle')
44
const { getConfigs } = require('../../../workers/lib/server/handlers/configs.handlers')
5+
const { RPC_METHODS } = require('../../../workers/lib/constants')
6+
const { createMockCtxWithOrks } = require('../helpers/mockHelpers')
57

68
test('getConfigs - happy path', async (t) => {
7-
const mockCtx = {
8-
conf: {
9-
orks: [{ rpcPublicKey: 'key1' }]
10-
},
11-
net_r0: {
12-
jRequest: async (key, method, payload) => {
13-
if (method === 'getConfigs') {
14-
return [
15-
{ id: 'config1', name: 'Pool Config 1', url: 'stratum://pool1.example.com' },
16-
{ id: 'config2', name: 'Pool Config 2', url: 'stratum://pool2.example.com' }
17-
]
18-
}
19-
return []
9+
const mockCtx = createMockCtxWithOrks(
10+
[{ rpcPublicKey: 'key1' }],
11+
async (key, method, payload) => {
12+
if (method === 'getConfigs') {
13+
return [
14+
{ id: 'config1', name: 'Pool Config 1', url: 'stratum://pool1.example.com' },
15+
{ id: 'config2', name: 'Pool Config 2', url: 'stratum://pool2.example.com' }
16+
]
2017
}
18+
if (method === RPC_METHODS.LIST_THINGS) return []
19+
return []
2120
}
22-
}
21+
)
2322

2423
const mockReq = {
2524
params: { type: 'pool' },
@@ -35,17 +34,15 @@ test('getConfigs - happy path', async (t) => {
3534

3635
test('getConfigs - with query filter', async (t) => {
3736
let capturedPayload = null
38-
const mockCtx = {
39-
conf: {
40-
orks: [{ rpcPublicKey: 'key1' }]
41-
},
42-
net_r0: {
43-
jRequest: async (key, method, payload) => {
44-
capturedPayload = payload
45-
return [{ id: 'config1', active: true }]
46-
}
37+
const mockCtx = createMockCtxWithOrks(
38+
[{ rpcPublicKey: 'key1' }],
39+
async (key, method, payload) => {
40+
if (method === 'getConfigs') capturedPayload = payload
41+
if (method === 'getConfigs') return [{ id: 'config1', active: true }]
42+
if (method === RPC_METHODS.LIST_THINGS) return []
43+
return []
4744
}
48-
}
45+
)
4946

5047
const mockReq = {
5148
params: { type: 'pool' },
@@ -60,17 +57,15 @@ test('getConfigs - with query filter', async (t) => {
6057

6158
test('getConfigs - with fields projection', async (t) => {
6259
let capturedPayload = null
63-
const mockCtx = {
64-
conf: {
65-
orks: [{ rpcPublicKey: 'key1' }]
66-
},
67-
net_r0: {
68-
jRequest: async (key, method, payload) => {
69-
capturedPayload = payload
70-
return [{ name: 'Config 1' }]
71-
}
60+
const mockCtx = createMockCtxWithOrks(
61+
[{ rpcPublicKey: 'key1' }],
62+
async (key, method, payload) => {
63+
if (method === 'getConfigs') capturedPayload = payload
64+
if (method === 'getConfigs') return [{ name: 'Config 1' }]
65+
if (method === RPC_METHODS.LIST_THINGS) return []
66+
return []
7267
}
73-
}
68+
)
7469

7570
const mockReq = {
7671
params: { type: 'pool' },
@@ -86,17 +81,15 @@ test('getConfigs - with fields projection', async (t) => {
8681

8782
test('getConfigs - with both query and fields', async (t) => {
8883
let capturedPayload = null
89-
const mockCtx = {
90-
conf: {
91-
orks: [{ rpcPublicKey: 'key1' }]
92-
},
93-
net_r0: {
94-
jRequest: async (key, method, payload) => {
95-
capturedPayload = payload
96-
return [{ name: 'Config 1' }]
97-
}
84+
const mockCtx = createMockCtxWithOrks(
85+
[{ rpcPublicKey: 'key1' }],
86+
async (key, method, payload) => {
87+
if (method === 'getConfigs') capturedPayload = payload
88+
if (method === 'getConfigs') return [{ name: 'Config 1' }]
89+
if (method === RPC_METHODS.LIST_THINGS) return []
90+
return []
9891
}
99-
}
92+
)
10093

10194
const mockReq = {
10295
params: { type: 'pool' },
@@ -114,10 +107,7 @@ test('getConfigs - with both query and fields', async (t) => {
114107
})
115108

116109
test('getConfigs - invalid config type throws', async (t) => {
117-
const mockCtx = {
118-
conf: { orks: [] },
119-
net_r0: { jRequest: async () => ([]) }
120-
}
110+
const mockCtx = createMockCtxWithOrks([], async () => ([]))
121111

122112
const mockReq = {
123113
params: { type: 'invalid_type' },
@@ -134,10 +124,7 @@ test('getConfigs - invalid config type throws', async (t) => {
134124
})
135125

136126
test('getConfigs - missing config type throws', async (t) => {
137-
const mockCtx = {
138-
conf: { orks: [] },
139-
net_r0: { jRequest: async () => ([]) }
140-
}
127+
const mockCtx = createMockCtxWithOrks([], async () => ([]))
141128

142129
const mockReq = {
143130
params: {},
@@ -154,10 +141,7 @@ test('getConfigs - missing config type throws', async (t) => {
154141
})
155142

156143
test('getConfigs - invalid query JSON throws', async (t) => {
157-
const mockCtx = {
158-
conf: { orks: [{ rpcPublicKey: 'key1' }] },
159-
net_r0: { jRequest: async () => ([]) }
160-
}
144+
const mockCtx = createMockCtxWithOrks([{ rpcPublicKey: 'key1' }], async () => ([]))
161145

162146
const mockReq = {
163147
params: { type: 'pool' },
@@ -174,10 +158,7 @@ test('getConfigs - invalid query JSON throws', async (t) => {
174158
})
175159

176160
test('getConfigs - invalid fields JSON throws', async (t) => {
177-
const mockCtx = {
178-
conf: { orks: [{ rpcPublicKey: 'key1' }] },
179-
net_r0: { jRequest: async () => ([]) }
180-
}
161+
const mockCtx = createMockCtxWithOrks([{ rpcPublicKey: 'key1' }], async () => ([]))
181162

182163
const mockReq = {
183164
params: { type: 'pool' },
@@ -194,10 +175,7 @@ test('getConfigs - invalid fields JSON throws', async (t) => {
194175
})
195176

196177
test('getConfigs - empty ork results', async (t) => {
197-
const mockCtx = {
198-
conf: { orks: [{ rpcPublicKey: 'key1' }] },
199-
net_r0: { jRequest: async () => ([]) }
200-
}
178+
const mockCtx = createMockCtxWithOrks([{ rpcPublicKey: 'key1' }], async () => ([]))
201179

202180
const mockReq = {
203181
params: { type: 'pool' },
@@ -211,12 +189,10 @@ test('getConfigs - empty ork results', async (t) => {
211189
})
212190

213191
test('getConfigs - handles error results from orks', async (t) => {
214-
const mockCtx = {
215-
conf: { orks: [{ rpcPublicKey: 'key1' }] },
216-
net_r0: {
217-
jRequest: async () => ({ error: 'timeout' })
218-
}
219-
}
192+
const mockCtx = createMockCtxWithOrks(
193+
[{ rpcPublicKey: 'key1' }],
194+
async () => ({ error: 'timeout' })
195+
)
220196

221197
const mockReq = {
222198
params: { type: 'pool' },
@@ -231,20 +207,15 @@ test('getConfigs - handles error results from orks', async (t) => {
231207

232208
test('getConfigs - aggregates results from multiple orks', async (t) => {
233209
let callCount = 0
234-
const mockCtx = {
235-
conf: {
236-
orks: [
237-
{ rpcPublicKey: 'key1' },
238-
{ rpcPublicKey: 'key2' }
239-
]
240-
},
241-
net_r0: {
242-
jRequest: async () => {
243-
callCount++
244-
return [{ id: `config${callCount}`, name: `Config ${callCount}` }]
245-
}
210+
const mockCtx = createMockCtxWithOrks(
211+
[{ rpcPublicKey: 'key1' }, { rpcPublicKey: 'key2' }],
212+
async (key, method) => {
213+
callCount++
214+
if (method === 'getConfigs') return [{ id: `config${callCount}`, name: `Config ${callCount}` }]
215+
if (method === RPC_METHODS.LIST_THINGS) return []
216+
return []
246217
}
247-
}
218+
)
248219

249220
const mockReq = {
250221
params: { type: 'pool' },

0 commit comments

Comments
 (0)