UNPKG

foundation-server

Version:

An extremely efficient, highly scalable, all-in-one, easy to setup cryptocurrency mining pool

858 lines (818 loc) 64.9 kB
/* * * API (Updated) * */ const redis = require('redis-mock'); jest.mock('redis', () => jest.requireActual('redis-mock')); const events = require('events'); const poolConfig = require('../../configs/pools/example.js'); const portalConfig = require('../../configs/main/example.js'); const PoolApi = require('../main/api'); const client = redis.createClient({ 'port': portalConfig.redis.port, 'host': portalConfig.redis.host, }); client._maxListeners = 0; client._redisMock._maxListeners = 0; const poolConfigs = { Pool1: poolConfig }; //////////////////////////////////////////////////////////////////////////////// function mockRequest(pool, endpoint, method) { return { params: { pool: pool, endpoint: endpoint }, query: { method: method } }; } function mockResponse() { const response = new events.EventEmitter(); response.writeHead = (code, headers) => { response.emit('header', [code, headers]); }; response.end = (payload) => response.emit('end', payload); return response; } function mockBuildBlock(height, hash, reward, transaction, difficulty, worker, solo) { return JSON.stringify({ height: height, hash: hash, reward: reward, transaction: transaction, difficulty: difficulty, worker: worker, solo: solo, }); } function mockSetupClient(client, commands, pool, callback) { client.multi(commands).exec(() => callback()); } //////////////////////////////////////////////////////////////////////////////// describe('Test API functionality', () => { beforeEach((done) => { client.flushall(() => done()); }); test('Test initialization of API', () => { const poolApi = new PoolApi(client, poolConfigs, portalConfig); expect(typeof poolApi.handleBlocksConfirmed).toBe('function'); expect(typeof poolApi.handleMinersActive).toBe('function'); expect(typeof poolApi.handleApiV1).toBe('function'); }); test('Test unknownPool API endpoint', (done) => { const response = mockResponse(); response.on('end', (payload) => { const processed = JSON.parse(payload); expect(processed.statusCode).toBe(404); expect(processed.body).toBe('The requested pool was not found. Verify your input and try again'); done(); }); mockSetupClient(client, [], 'Pool1', () => { const request = mockRequest(); const poolApi = new PoolApi(client, poolConfigs, portalConfig); poolApi.handleApiV1(request, (code, message) => { poolApi.buildResponse(code, message, response); }); }); }); test('Test unknownMethod API endpoint [1]', (done) => { const response = mockResponse(); response.on('end', (payload) => { const processed = JSON.parse(payload); expect(processed.statusCode).toBe(405); expect(processed.body).toBe('The requested method is not currently supported. Verify your input and try again'); done(); }); mockSetupClient(client, [], 'Pool1', () => { const request = mockRequest('Pool1', 'unknown', 'unknown'); const poolApi = new PoolApi(client, poolConfigs, portalConfig); poolApi.handleApiV1(request, (code, message) => { poolApi.buildResponse(code, message, response); }); }); }); test('Test unknownMethod API endpoint [2]', (done) => { const response = mockResponse(); response.on('end', (payload) => { const processed = JSON.parse(payload); expect(processed.statusCode).toBe(405); expect(processed.body).toBe('The requested method is not currently supported. Verify your input and try again'); done(); }); mockSetupClient(client, [], 'Pool1', () => { const request = mockRequest('Pool1', 'unknown', 'unknown'); const poolConfigsCopy = JSON.parse(JSON.stringify(poolConfigs)); const poolApi = new PoolApi(client, poolConfigsCopy, portalConfig); poolApi.handleApiV1(request, (code, message) => { poolApi.buildResponse(code, message, response); }); }); }); test('Test request without parameters', (done) => { const response = mockResponse(); response.on('end', (payload) => { const processed = JSON.parse(payload); expect(processed.statusCode).toBe(404); expect(processed.body).toBe('The requested pool was not found. Verify your input and try again'); done(); }); mockSetupClient(client, [], 'Pool1', () => { const request = {}; const poolApi = new PoolApi(client, poolConfigs, portalConfig); poolApi.handleApiV1(request, (code, message) => { poolApi.buildResponse(code, message, response); }); }); }); test('Test handleBlocksConfirmed API endpoint', (done) => { const commands = [ ['sadd', 'Pool1:blocks:primary:confirmed', mockBuildBlock(180, 'hash', 12.5, 'txid', 8, 'worker', false)], ['sadd', 'Pool1:blocks:primary:confirmed', mockBuildBlock(181, 'hash', 12.5, 'txid', 8, 'worker', false)], ['sadd', 'Pool1:blocks:primary:confirmed', mockBuildBlock(182, 'hash', 12.5, 'txid', 8, 'worker', false)], ['sadd', 'Pool1:blocks:primary:confirmed', mockBuildBlock(183, 'hash', 12.5, 'txid', 8, 'worker', false)], ['sadd', 'Pool1:blocks:auxiliary:confirmed', mockBuildBlock(180, 'hash', 12.5, 'txid', 8, 'worker', false)], ['sadd', 'Pool1:blocks:auxiliary:confirmed', mockBuildBlock(181, 'hash', 12.5, 'txid', 8, 'worker', false)]]; const response = mockResponse(); response.on('end', (payload) => { const processed = JSON.parse(payload); expect(processed.statusCode).toBe(200); expect(Object.keys(processed.body).length).toBe(2); expect(processed.body.primary.length).toBe(4); expect(processed.body.auxiliary.length).toBe(2); expect(processed.body.primary[0].height).toBe(183); done(); }); mockSetupClient(client, commands, 'Pool1', () => { const request = mockRequest('Pool1', 'blocks', 'confirmed'); const poolApi = new PoolApi(client, poolConfigs, portalConfig); poolApi.handleApiV1(request, (code, message) => { poolApi.buildResponse(code, message, response); }); }); }); test('Test handleBlocksKicked API endpoint', (done) => { const commands = [ ['sadd', 'Pool1:blocks:primary:kicked', mockBuildBlock(180, 'hash', 12.5, 'txid', 8, 'worker', false)], ['sadd', 'Pool1:blocks:primary:kicked', mockBuildBlock(181, 'hash', 12.5, 'txid', 8, 'worker', false)], ['sadd', 'Pool1:blocks:primary:kicked', mockBuildBlock(182, 'hash', 12.5, 'txid', 8, 'worker', false)], ['sadd', 'Pool1:blocks:primary:kicked', mockBuildBlock(183, 'hash', 12.5, 'txid', 8, 'worker', false)], ['sadd', 'Pool1:blocks:auxiliary:kicked', mockBuildBlock(180, 'hash', 12.5, 'txid', 8, 'worker', false)], ['sadd', 'Pool1:blocks:auxiliary:kicked', mockBuildBlock(181, 'hash', 12.5, 'txid', 8, 'worker', false)]]; const response = mockResponse(); response.on('end', (payload) => { const processed = JSON.parse(payload); expect(processed.statusCode).toBe(200); expect(Object.keys(processed.body).length).toBe(2); expect(processed.body.primary.length).toBe(4); expect(processed.body.auxiliary.length).toBe(2); expect(processed.body.primary[0].height).toBe(183); done(); }); mockSetupClient(client, commands, 'Pool1', () => { const request = mockRequest('Pool1', 'blocks', 'kicked'); const poolApi = new PoolApi(client, poolConfigs, portalConfig); poolApi.handleApiV1(request, (code, message) => { poolApi.buildResponse(code, message, response); }); }); }); test('Test handleBlocksPending API endpoint', (done) => { const commands = [ ['sadd', 'Pool1:blocks:primary:pending', mockBuildBlock(180, 'hash', 12.5, 'txid', 8, 'worker', false)], ['sadd', 'Pool1:blocks:primary:pending', mockBuildBlock(181, 'hash', 12.5, 'txid', 8, 'worker', false)], ['sadd', 'Pool1:blocks:primary:pending', mockBuildBlock(182, 'hash', 12.5, 'txid', 8, 'worker', false)], ['sadd', 'Pool1:blocks:primary:pending', mockBuildBlock(183, 'hash', 12.5, 'txid', 8, 'worker', false)], ['sadd', 'Pool1:blocks:auxiliary:pending', mockBuildBlock(180, 'hash', 12.5, 'txid', 8, 'worker', false)], ['sadd', 'Pool1:blocks:auxiliary:pending', mockBuildBlock(181, 'hash', 12.5, 'txid', 8, 'worker', false)]]; const response = mockResponse(); response.on('end', (payload) => { const processed = JSON.parse(payload); expect(processed.statusCode).toBe(200); expect(Object.keys(processed.body).length).toBe(2); expect(processed.body.primary.length).toBe(4); expect(processed.body.auxiliary.length).toBe(2); expect(processed.body.primary[0].height).toBe(183); done(); }); mockSetupClient(client, commands, 'Pool1', () => { const request = mockRequest('Pool1', 'blocks', 'pending'); const poolApi = new PoolApi(client, poolConfigs, portalConfig); poolApi.handleApiV1(request, (code, message) => { poolApi.buildResponse(code, message, response); }); }); }); test('Test handleBlocks API endpoint', (done) => { const commands = [ ['sadd', 'Pool1:blocks:primary:confirmed', mockBuildBlock(180, 'hash', 12.5, 'txid', 8, 'worker', false)], ['sadd', 'Pool1:blocks:primary:kicked', mockBuildBlock(181, 'hash', 12.5, 'txid', 8, 'worker', false)], ['sadd', 'Pool1:blocks:primary:pending', mockBuildBlock(182, 'hash', 12.5, 'txid', 8, 'worker', false)], ['sadd', 'Pool1:blocks:primary:pending', mockBuildBlock(183, 'hash', 12.5, 'txid', 8, 'worker', false)], ['sadd', 'Pool1:blocks:auxiliary:pending', mockBuildBlock(183, 'hash', 12.5, 'txid', 8, 'worker', false)], ['sadd', 'Pool1:blocks:auxiliary:pending', mockBuildBlock(184, 'hash', 12.5, 'txid', 8, 'worker', false)]]; const response = mockResponse(); response.on('end', (payload) => { const processed = JSON.parse(payload); expect(processed.statusCode).toBe(200); expect(typeof processed.body).toBe('object'); expect(Object.keys(processed.body).length).toBe(2); expect(Object.keys(processed.body.primary).length).toBe(3); expect(Object.keys(processed.body.auxiliary).length).toBe(3); expect(processed.body.primary.confirmed.length).toBe(1); expect(processed.body.primary.confirmed[0].height).toBe(180); expect(processed.body.primary.kicked.length).toBe(1); expect(processed.body.primary.pending.length).toBe(2); expect(processed.body.primary.pending[0].height).toBe(183); expect(processed.body.auxiliary.pending.length).toBe(2); expect(processed.body.auxiliary.pending[0].height).toBe(184); done(); }); mockSetupClient(client, commands, 'Pool1', () => { const request = mockRequest('Pool1', 'blocks'); const poolApi = new PoolApi(client, poolConfigs, portalConfig); poolApi.handleApiV1(request, (code, message) => { poolApi.buildResponse(code, message, response); }); }); }); test('Test handleMinerBlocks API endpoint', (done) => { const commands = [ ['sadd', 'Pool1:blocks:primary:confirmed', mockBuildBlock(180, 'hash', 12.5, 'txid', 8, 'worker1', false)], ['sadd', 'Pool1:blocks:primary:confirmed', mockBuildBlock(181, 'hash', 12.5, 'txid', 8, 'worker2', false)], ['sadd', 'Pool1:blocks:primary:pending', mockBuildBlock(182, 'hash', 12.5, 'txid', 8, 'worker1', false)], ['sadd', 'Pool1:blocks:primary:pending', mockBuildBlock(183, 'hash', 12.5, 'txid', 8, 'worker2', false)], ['sadd', 'Pool1:blocks:primary:kicked', mockBuildBlock(184, 'hash', 12.5, 'txid', 8, 'worker1', false)], ['sadd', 'Pool1:blocks:primary:kicked', mockBuildBlock(185, 'hash', 12.5, 'txid', 8, 'worker2', false)], ['sadd', 'Pool1:blocks:auxiliary:confirmed', mockBuildBlock(186, 'hash', 12.5, 'txid', 8, 'worker1', false)], ['sadd', 'Pool1:blocks:auxiliary:confirmed', mockBuildBlock(187, 'hash', 12.5, 'txid', 8, 'worker2', false)], ['sadd', 'Pool1:blocks:auxiliary:pending', mockBuildBlock(188, 'hash', 12.5, 'txid', 8, 'worker1', false)], ['sadd', 'Pool1:blocks:auxiliary:pending', mockBuildBlock(189, 'hash', 12.5, 'txid', 8, 'worker2', false)], ['sadd', 'Pool1:blocks:auxiliary:kicked', mockBuildBlock(190, 'hash', 12.5, 'txid', 8, 'worker1', false)], ['sadd', 'Pool1:blocks:auxiliary:kicked', mockBuildBlock(191, 'hash', 12.5, 'txid', 8, 'worker2', false)]]; const response = mockResponse(); response.on('end', (payload) => { const processed = JSON.parse(payload); expect(processed.statusCode).toBe(200); expect(typeof processed.body).toBe('object'); expect(Object.keys(processed.body).length).toBe(2); expect(Object.keys(processed.body.primary).length).toBe(3); expect(Object.keys(processed.body.auxiliary).length).toBe(3); expect(processed.body.primary.confirmed.length).toBe(1); expect(processed.body.primary.confirmed[0].height).toBe(180); expect(processed.body.primary.pending.length).toBe(1); expect(processed.body.primary.pending[0].height).toBe(182); expect(processed.body.primary.kicked.length).toBe(1); expect(processed.body.primary.kicked[0].height).toBe(184); expect(processed.body.auxiliary.confirmed.length).toBe(1); expect(processed.body.auxiliary.confirmed[0].height).toBe(186); expect(processed.body.auxiliary.pending.length).toBe(1); expect(processed.body.auxiliary.pending[0].height).toBe(188); expect(processed.body.auxiliary.kicked.length).toBe(1); expect(processed.body.auxiliary.kicked[0].height).toBe(190); done(); }); mockSetupClient(client, commands, 'Pool1', () => { const request = mockRequest('Pool1', 'blocks', 'worker1'); const poolApi = new PoolApi(client, poolConfigs, portalConfig); poolApi.handleApiV1(request, (code, message) => { poolApi.buildResponse(code, message, response); }); }); }); test('Test handlePools API endpoint', (done) => { const response = mockResponse(); response.on('end', (payload) => { const processed = JSON.parse(payload); expect(processed.statusCode).toBe(200); expect(typeof processed.body).toBe('object'); expect(processed.body.length).toBe(1); expect(processed.body[0]).toBe('Pool1'); done(); }); mockSetupClient(client, [], 'Pool1', () => { const request = mockRequest('pools'); const poolApi = new PoolApi(client, poolConfigs, portalConfig); poolApi.handleApiV1(request, (code, message) => { poolApi.buildResponse(code, message, response); }); }); }); test('Test handleHistorical API endpoint', (done) => { const commands = [ ['zadd', 'Pool1:statistics:primary:historical', Date.now() / 1000, '{"time":163787808585313,"hashrate":{"shared":0,"solo":0},"network":{"difficulty":"0.001978989105730653","hashrate":"52007.68563030699"},"status":{"miners":0,"workers":0}}'], ['zadd', 'Pool1:statistics:primary:historical', Date.now() / 1000, '{"time":163787808089135,"hashrate":{"shared":0,"solo":0},"network":{"difficulty":"0.001978989105730653","hashrate":"52007.68563030699"},"status":{"miners":0,"workers":0}}'], ['zadd', 'Pool1:statistics:auxiliary:historical', Date.now() / 1000, '{"time":1637878095133,"hashrate":{"shared":0,"solo":0},"network":{"difficulty":"0.001978989105730653","hashrate":"52007.68563030699"},"status":{"miners":0,"workers":0}}'], ['zadd', 'Pool1:statistics:auxiliary:historical', Date.now() / 1000, '{"time":1637878099113,"hashrate":{"shared":0,"solo":0},"network":{"difficulty":"0.001978989105730653","hashrate":"52007.68563030699"},"status":{"miners":0,"workers":0}}']]; const response = mockResponse(); const expected = { 'auxiliary': [{'hashrate': {'shared': 0, 'solo': 0}, 'network': {'difficulty': '0.001978989105730653', 'hashrate': '52007.68563030699'}, 'status': {'miners': 0, 'workers': 0}, 'time': 1637878095133}, {'hashrate': {'shared': 0, 'solo': 0}, 'network': {'difficulty': '0.001978989105730653', 'hashrate': '52007.68563030699'}, 'status': {'miners': 0, 'workers': 0}, 'time': 1637878099113}], 'primary': [{'hashrate': {'shared': 0, 'solo': 0}, 'network': {'difficulty': '0.001978989105730653', 'hashrate': '52007.68563030699'}, 'status': {'miners': 0, 'workers': 0}, 'time': 163787808089135}, {'hashrate': {'shared': 0, 'solo': 0}, 'network': {'difficulty': '0.001978989105730653', 'hashrate': '52007.68563030699'}, 'status': {'miners': 0, 'workers': 0}, 'time': 163787808585313}]}; response.on('end', (payload) => { const processed = JSON.parse(payload); expect(processed.statusCode).toBe(200); expect(typeof processed.body).toBe('object'); expect(processed.body).toStrictEqual(expected); done(); }); mockSetupClient(client, commands, 'Pool1', () => { const request = mockRequest('Pool1', 'historical'); const poolApi = new PoolApi(client, poolConfigs, portalConfig); poolApi.handleApiV1(request, (code, message) => { poolApi.buildResponse(code, message, response); }); }); }); test('Test handleMinersActive API endpoint', (done) => { const commands = [ ['hset', 'Pool1:rounds:primary:current:shared:shares', 'worker2.w1', JSON.stringify({ time: 0, work: 64, times: 20, worker: 'worker2.w1' })], ['hset', 'Pool1:rounds:primary:current:shared:shares', 'worker2.w2', JSON.stringify({ time: 0, work: 44, times: 31, worker: 'worker2.w2' })], ['hset', 'Pool1:rounds:primary:current:shared:shares', 'worker3', JSON.stringify({ time: 0, work: 8, times: 50, worker: 'worker3' })], ['hset', 'Pool1:rounds:primary:current:solo:shares', 'worker1', JSON.stringify({ time: 0, work: 64, times: 43, types: { valid: 2, invalid: 1, stale: 1 }, worker: 'worker1' })], ['zadd', 'Pool1:rounds:primary:current:shared:hashrate', Date.now() / 1000, JSON.stringify({ time: 0, worker: 'worker2.w1', times: 20, work: 64 })], ['zadd', 'Pool1:rounds:primary:current:shared:hashrate', Date.now() / 1000, JSON.stringify({ time: 1, worker: 'worker2.w2', times: 31, work: 44 })], ['zadd', 'Pool1:rounds:primary:current:solo:hashrate', Date.now() / 1000, JSON.stringify({ time: 0, worker: 'worker1', times: 10, work: 32 })], ['zadd', 'Pool1:rounds:primary:current:solo:hashrate', Date.now() / 1000, JSON.stringify({ time: 1, worker: 'worker1', times: 14, work: 32 })], ['zadd', 'Pool1:rounds:primary:current:solo:hashrate', Date.now() / 1000, JSON.stringify({ time: 1, worker: 'worker1', times: 14, work: 32 })], ['zadd', 'Pool1:rounds:primary:current:solo:hashrate', Date.now() / 1000, JSON.stringify({ time: 1, worker: 'worker1', times: 14, work: 32 })]]; const response = mockResponse(); const expected = { 'auxiliary': {'shared': [], 'solo': []}, 'primary': { 'shared': [{'time': 0, 'effort': null, 'hashrate': 1546188226.56, 'miner': 'worker2', 'shares': {'invalid': 0, 'stale': 0, 'valid': 0}, 'times': 31, 'work': 108}], 'solo': [{'time': 0, 'effort': null, 'hashrate': 916259689.8133334, 'miner': 'worker1', 'shares': {'invalid': 1, 'stale': 1, 'valid': 2}, 'times': 43, 'work': 64}]}}; response.on('end', (payload) => { const processed = JSON.parse(payload); expect(processed.statusCode).toBe(200); expect(typeof processed.body).toBe('object'); expect(processed.body).toStrictEqual(expected); done(); }); mockSetupClient(client, commands, 'Pool1', () => { const request = mockRequest('Pool1', 'miners', 'active'); const poolApi = new PoolApi(client, poolConfigs, portalConfig); poolApi.handleApiV1(request, (code, message) => { poolApi.buildResponse(code, message, response); }); }); }); test('Test handleMinersSpecific API endpoint [1]', (done) => { const commands = [ ['hset', 'Pool1:payments:primary:balances', 'worker2', 37.43], ['hset', 'Pool1:payments:primary:generate', 'worker1', 134.3], ['hset', 'Pool1:payments:primary:generate', 'worker2', 255.17], ['hset', 'Pool1:payments:primary:generate', 'worker3', 0], ['hset', 'Pool1:payments:primary:immature', 'worker1', 76.23], ['hset', 'Pool1:payments:primary:immature', 'worker2', 12.17], ['hset', 'Pool1:payments:primary:immature', 'worker3', 76.4], ['hset', 'Pool1:payments:primary:paid', 'worker1', 0], ['hset', 'Pool1:payments:primary:paid', 'worker2', 123.5], ['hset', 'Pool1:payments:primary:paid', 'worker3', 45.66], ['hset', 'Pool1:rounds:primary:current:shared:shares', 'worker2.w1', JSON.stringify({ time: 0, work: 64, times: 20, worker: 'worker2.w1' })], ['hset', 'Pool1:rounds:primary:current:shared:shares', 'worker2.w2', JSON.stringify({ time: 0, work: 44, times: 31, worker: 'worker2.w2' })], ['hset', 'Pool1:rounds:primary:current:shared:shares', 'worker3', JSON.stringify({ time: 0, work: 8, times: 50, worker: 'worker3' })], ['hset', 'Pool1:rounds:primary:current:solo:shares', 'worker1', JSON.stringify({ time: 0, work: 64, times: 43, types: { valid: 2, invalid: 1, stale: 1 }, worker: 'worker1' })], ['zadd', 'Pool1:rounds:primary:current:shared:hashrate', Date.now() / 1000, JSON.stringify({ time: 0, worker: 'worker2.w1', work: 64 })], ['zadd', 'Pool1:rounds:primary:current:shared:hashrate', Date.now() / 1000, JSON.stringify({ time: 0, worker: 'worker2.w1', work: 64 })], ['zadd', 'Pool1:rounds:primary:current:shared:hashrate', Date.now() / 1000, JSON.stringify({ time: 0, worker: 'worker2.w1', work: 64 })], ['zadd', 'Pool1:rounds:primary:current:shared:hashrate', Date.now() / 1000, JSON.stringify({ time: 1, worker: 'worker3', work: 8 })], ['zadd', 'Pool1:rounds:primary:current:shared:hashrate', Date.now() / 1000, JSON.stringify({ time: 1, worker: 'worker2.w2', work: 44 })], ['zadd', 'Pool1:rounds:primary:current:solo:hashrate', Date.now() / 1000, JSON.stringify({ time: 0, worker: 'worker1', work: 32 })], ['zadd', 'Pool1:rounds:primary:current:solo:hashrate', Date.now() / 1000, JSON.stringify({ time: 1, worker: 'worker1', work: 32 })]]; const response = mockResponse(); const expected = { 'auxiliary': {'hashrate': {'shared': 0, 'solo': 0}, 'payments': {'balances': 0, 'generate': 0, 'immature': 0, 'paid': 0}, 'shares': {'shared': {}, 'solo': {}}, 'times': {'shared': 0}, 'work': {'shared': 0, 'solo': 0}, 'workers': {'shared': [], 'solo': []}}, 'primary': {'hashrate': {'shared': 1546188226.56, 'solo': 0}, 'payments': {'balances': 37.43, 'generate': 255.17, 'immature': 12.17, 'paid': 123.5}, 'shares': {'shared': {'invalid': 0, 'stale': 0, 'valid': 0}, 'solo': {}}, 'times': {'shared': 31}, 'work': {'shared': 108, 'solo': 0}, 'workers': {'shared': ['worker2.w1', 'worker2.w2'], 'solo': []}}}; response.on('end', (payload) => { const processed = JSON.parse(payload); expect(processed.statusCode).toBe(200); expect(typeof processed.body).toBe('object'); expect(processed.body).toStrictEqual(expected); done(); }); mockSetupClient(client, commands, 'Pool1', () => { const request = mockRequest('Pool1', 'miners', 'worker2'); const poolApi = new PoolApi(client, poolConfigs, portalConfig); poolApi.handleApiV1(request, (code, message) => { poolApi.buildResponse(code, message, response); }); }); }); test('Test handleMinersSpecific API endpoint [2]', (done) => { const commands = [ ['hset', 'Pool1:payments:auxiliary:generate', 'worker2', 255.17], ['hset', 'Pool1:payments:auxiliary:generate', 'worker3', 0], ['hset', 'Pool1:payments:auxiliary:immature', 'worker2', 12.17], ['hset', 'Pool1:payments:auxiliary:immature', 'worker3', 76.4], ['hset', 'Pool1:payments:auxiliary:paid', 'worker1', 0], ['hset', 'Pool1:payments:auxiliary:paid', 'worker2', 123.5], ['hset', 'Pool1:payments:auxiliary:paid', 'worker3', 45.66], ['hset', 'Pool1:rounds:auxiliary:current:shared:shares', 'worker2.w1', JSON.stringify({ time: 0, work: 64, times: 20, worker: 'worker2.w1' })], ['hset', 'Pool1:rounds:auxiliary:current:shared:shares', 'worker2.w2', JSON.stringify({ time: 0, work: 44, times: 31, worker: 'worker2.w2' })], ['hset', 'Pool1:rounds:auxiliary:current:shared:shares', 'worker3', JSON.stringify({ time: 0, work: 8, times: 50, worker: 'worker3' })], ['hset', 'Pool1:rounds:auxiliary:current:solo:shares', 'worker1', JSON.stringify({ time: 0, work: 64, times: 43, types: { valid: 2, invalid: 1, stale: 1 }, worker: 'worker1' })], ['zadd', 'Pool1:rounds:auxiliary:current:shared:hashrate', Date.now() / 1000, JSON.stringify({ time: 0, worker: 'worker2.w1', work: 64 })], ['zadd', 'Pool1:rounds:auxiliary:current:shared:hashrate', Date.now() / 1000, JSON.stringify({ time: 0, worker: 'worker2.w1', work: 64 })], ['zadd', 'Pool1:rounds:auxiliary:current:shared:hashrate', Date.now() / 1000, JSON.stringify({ time: 0, worker: 'worker2.w1', work: 64 })], ['zadd', 'Pool1:rounds:auxiliary:current:shared:hashrate', Date.now() / 1000, JSON.stringify({ time: 1, worker: 'worker3', work: 8 })], ['zadd', 'Pool1:rounds:auxiliary:current:shared:hashrate', Date.now() / 1000, JSON.stringify({ time: 1, worker: 'worker2.w2', work: 44 })], ['zadd', 'Pool1:rounds:auxiliary:current:solo:hashrate', Date.now() / 1000, JSON.stringify({ time: 0, worker: 'worker1', work: 32 })], ['zadd', 'Pool1:rounds:auxiliary:current:solo:hashrate', Date.now() / 1000, JSON.stringify({ time: 1, worker: 'worker1', work: 32 })]]; const response = mockResponse(); const expected = { 'auxiliary': {'hashrate': {'shared': 0, 'solo': 916259689.8133334}, 'payments': {'balances': 0, 'generate': 0, 'immature': 0, 'paid': 0}, 'shares': {'shared': {}, 'solo': {'invalid': 1, 'stale': 1, 'valid': 2}}, 'times': {'shared': 0}, 'work': {'shared': 0, 'solo': 64}, 'workers': {'shared': [], 'solo': ['worker1']}}, 'primary': {'hashrate': {'shared': 0, 'solo': 0}, 'payments': {'balances': 0, 'generate': 0, 'immature': 0, 'paid': 0}, 'shares': {'shared': {}, 'solo': {}}, 'times': {'shared': 0}, 'work': {'shared': 0, 'solo': 0}, 'workers': {'shared': [], 'solo': []}}}; response.on('end', (payload) => { const processed = JSON.parse(payload); expect(processed.statusCode).toBe(200); expect(typeof processed.body).toBe('object'); expect(processed.body).toStrictEqual(expected); done(); }); mockSetupClient(client, commands, 'Pool1', () => { const request = mockRequest('Pool1', 'miners', 'worker1'); const poolApi = new PoolApi(client, poolConfigs, portalConfig); poolApi.handleApiV1(request, (code, message) => { poolApi.buildResponse(code, message, response); }); }); }); test('Test handleMiners API endpoint', (done) => { const commands = [ ['hset', 'Pool1:rounds:primary:current:shared:shares', 'worker2.w1', JSON.stringify({ time: 0, work: 64, times: 20, worker: 'worker2.w1' })], ['hset', 'Pool1:rounds:primary:current:shared:shares', 'worker2.w2', JSON.stringify({ time: 0, work: 44, times: 31, worker: 'worker2.w2' })], ['hset', 'Pool1:rounds:primary:current:shared:shares', 'worker3', JSON.stringify({ time: 0, work: 8, times: 50, worker: 'worker3' })], ['hset', 'Pool1:rounds:primary:current:solo:shares', 'worker1', JSON.stringify({ time: 0, work: 64, times: 43, types: { valid: 2, invalid: 1, stale: 1 }, worker: 'worker1' })], ['zadd', 'Pool1:rounds:primary:current:shared:hashrate', Date.now() / 1000, JSON.stringify({ time: 0, worker: 'worker2.w1', work: 64 })], ['zadd', 'Pool1:rounds:primary:current:shared:hashrate', Date.now() / 1000, JSON.stringify({ time: 0, worker: 'worker2.w1', work: 64 })], ['zadd', 'Pool1:rounds:primary:current:shared:hashrate', Date.now() / 1000, JSON.stringify({ time: 0, worker: 'worker2.w1', work: 64 })], ['zadd', 'Pool1:rounds:primary:current:shared:hashrate', Date.now() / 1000, JSON.stringify({ time: 1, worker: 'worker3', work: 8 })], ['zadd', 'Pool1:rounds:primary:current:shared:hashrate', Date.now() / 1000, JSON.stringify({ time: 1, worker: 'worker2.w2', work: 44 })], ['zadd', 'Pool1:rounds:primary:current:solo:hashrate', Date.now() / 1000, JSON.stringify({ time: 0, worker: 'worker1', work: 32 })], ['zadd', 'Pool1:rounds:primary:current:solo:hashrate', Date.now() / 1000, JSON.stringify({ time: 1, worker: 'worker1', work: 32 })]]; const response = mockResponse(); const expected = { 'auxiliary': {'shared': [], 'solo': []}, 'primary': {'shared': [{'time': 0, 'effort': null, 'hashrate': 1546188226.56, 'miner': 'worker2', 'shares': {'invalid': 0, 'stale': 0, 'valid': 0}, 'times': 31, 'work': 108}, {'time': 0, 'effort': null, 'hashrate': 114532461.22666667, 'miner': 'worker3', 'shares': {'invalid': 0, 'stale': 0, 'valid': 0}, 'times': 50, 'work': 8}], 'solo': [{'time': 0, 'effort': null, 'hashrate': 916259689.8133334, 'miner': 'worker1', 'shares': {'invalid': 1, 'stale': 1, 'valid': 2}, 'times': 43, 'work': 64}]}}; response.on('end', (payload) => { const processed = JSON.parse(payload); expect(processed.statusCode).toBe(200); expect(typeof processed.body).toBe('object'); expect(processed.body).toStrictEqual(expected); done(); }); mockSetupClient(client, commands, 'Pool1', () => { const request = mockRequest('Pool1', 'miners'); const poolApi = new PoolApi(client, poolConfigs, portalConfig); poolApi.handleApiV1(request, (code, message) => { poolApi.buildResponse(code, message, response); }); }); }); test('Test handlePaymentsBalances API endpoint', (done) => { const commands = [ ['hset', 'Pool1:payments:primary:balances', 'worker1', 134.3], ['hset', 'Pool1:payments:primary:balances', 'worker2', 255.17], ['hset', 'Pool1:payments:primary:balances', 'worker3', 0], ['hset', 'Pool1:payments:auxiliary:balances', 'worker2', 1255.17], ['hset', 'Pool1:payments:auxiliary:balances', 'worker3', 135]]; const response = mockResponse(); response.on('end', (payload) => { const processed = JSON.parse(payload); expect(processed.statusCode).toBe(200); expect(typeof processed.body).toBe('object'); expect(Object.keys(processed.body.primary).length).toBe(2); expect(Object.keys(processed.body.auxiliary).length).toBe(2); expect(processed.body.primary.worker1).toBe(134.3); expect(processed.body.primary.worker2).toBe(255.17); expect(processed.body.auxiliary.worker2).toBe(1255.17); expect(processed.body.auxiliary.worker3).toBe(135); done(); }); mockSetupClient(client, commands, 'Pool1', () => { const request = mockRequest('Pool1', 'payments', 'balances'); const poolApi = new PoolApi(client, poolConfigs, portalConfig); poolApi.handleApiV1(request, (code, message) => { poolApi.buildResponse(code, message, response); }); }); }); test('Test handlePaymentsGenerate API endpoint', (done) => { const commands = [ ['hset', 'Pool1:payments:primary:generate', 'worker1', 134.3], ['hset', 'Pool1:payments:primary:generate', 'worker2', 255.17], ['hset', 'Pool1:payments:primary:generate', 'worker3', 0], ['hset', 'Pool1:payments:auxiliary:generate', 'worker2', 1255.17], ['hset', 'Pool1:payments:auxiliary:generate', 'worker3', 135]]; const response = mockResponse(); response.on('end', (payload) => { const processed = JSON.parse(payload); expect(processed.statusCode).toBe(200); expect(typeof processed.body).toBe('object'); expect(Object.keys(processed.body.primary).length).toBe(2); expect(Object.keys(processed.body.auxiliary).length).toBe(2); expect(processed.body.primary.worker1).toBe(134.3); expect(processed.body.primary.worker2).toBe(255.17); expect(processed.body.auxiliary.worker2).toBe(1255.17); expect(processed.body.auxiliary.worker3).toBe(135); done(); }); mockSetupClient(client, commands, 'Pool1', () => { const request = mockRequest('Pool1', 'payments', 'generate'); const poolApi = new PoolApi(client, poolConfigs, portalConfig); poolApi.handleApiV1(request, (code, message) => { poolApi.buildResponse(code, message, response); }); }); }); test('Test handlePaymentsImmature API endpoint', (done) => { const commands = [ ['hset', 'Pool1:payments:primary:immature', 'worker1', 134.3], ['hset', 'Pool1:payments:primary:immature', 'worker2', 255.17], ['hset', 'Pool1:payments:primary:immature', 'worker3', 0], ['hset', 'Pool1:payments:auxiliary:immature', 'worker2', 1255.17], ['hset', 'Pool1:payments:auxiliary:immature', 'worker3', 135]]; const response = mockResponse(); response.on('end', (payload) => { const processed = JSON.parse(payload); expect(processed.statusCode).toBe(200); expect(typeof processed.body).toBe('object'); expect(Object.keys(processed.body.primary).length).toBe(2); expect(Object.keys(processed.body.auxiliary).length).toBe(2); expect(processed.body.primary.worker1).toBe(134.3); expect(processed.body.primary.worker2).toBe(255.17); expect(processed.body.auxiliary.worker2).toBe(1255.17); expect(processed.body.auxiliary.worker3).toBe(135); done(); }); mockSetupClient(client, commands, 'Pool1', () => { const request = mockRequest('Pool1', 'payments', 'immature'); const poolApi = new PoolApi(client, poolConfigs, portalConfig); poolApi.handleApiV1(request, (code, message) => { poolApi.buildResponse(code, message, response); }); }); }); test('Test handlePaymentsPaid API endpoint', (done) => { const commands = [ ['hset', 'Pool1:payments:primary:paid', 'worker1', 134.3], ['hset', 'Pool1:payments:primary:paid', 'worker2', 255.17], ['hset', 'Pool1:payments:primary:paid', 'worker3', 0], ['hset', 'Pool1:payments:auxiliary:paid', 'worker2', 1255.17], ['hset', 'Pool1:payments:auxiliary:paid', 'worker3', 135]]; const response = mockResponse(); response.on('end', (payload) => { const processed = JSON.parse(payload); expect(processed.statusCode).toBe(200); expect(typeof processed.body).toBe('object'); expect(Object.keys(processed.body.primary).length).toBe(2); expect(Object.keys(processed.body.auxiliary).length).toBe(2); expect(processed.body.primary.worker1).toBe(134.3); expect(processed.body.primary.worker2).toBe(255.17); expect(processed.body.auxiliary.worker2).toBe(1255.17); expect(processed.body.auxiliary.worker3).toBe(135); done(); }); mockSetupClient(client, commands, 'Pool1', () => { const request = mockRequest('Pool1', 'payments', 'paid'); const poolApi = new PoolApi(client, poolConfigs, portalConfig); poolApi.handleApiV1(request, (code, message) => { poolApi.buildResponse(code, message, response); }); }); }); test('Test handlePorts API endpoint', (done) => { const response = mockResponse(); response.on('end', (payload) => { const processed = JSON.parse(payload); expect(processed.statusCode).toBe(200); expect(typeof processed.body).toBe('object'); expect(typeof processed.body.ports[0]).toBe('object'); expect(processed.body.ports[0].difficulty.initial).toBe(32); expect(processed.body.ports[0].difficulty.maximum).toBe(512); expect(processed.body.ports[0].difficulty.minimum).toBe(8); expect(processed.body.ports[0].enabled).toBe(true); expect(processed.body.ports[0].port).toBe(3002); expect(processed.body.ports[1].port).toBe(3003); done(); }); mockSetupClient(client, [], 'Pool1', () => { const request = mockRequest('Pool1', 'ports'); const poolApi = new PoolApi(client, poolConfigs, portalConfig); poolApi.handleApiV1(request, (code, message) => { poolApi.buildResponse(code, message, response); }); }); }); test('Test handlePaymentsRecords API endpoint', (done) => { const commands = [ ['zadd', 'Pool1:payments:primary:records', 0, JSON.stringify({ time: 0, paid: 200.15, transaction: 'hash1', records: 'record1' })], ['zadd', 'Pool1:payments:primary:records', 0, JSON.stringify({ time: 1, paid: 84.23, transaction: 'hash2', records: 'record2' })], ['zadd', 'Pool1:payments:auxiliary:records', 0, JSON.stringify({ time: 2, paid: 760.133, transaction: 'hash3', records: 'record3' })]]; const response = mockResponse(); response.on('end', (payload) => { const processed = JSON.parse(payload); expect(processed.statusCode).toBe(200); expect(typeof processed.body).toBe('object'); expect(processed.body.primary.length).toBe(2); expect(processed.body.auxiliary.length).toBe(1); expect(processed.body.primary[0].paid).toBe(84.23); expect(processed.body.primary[0].transaction).toBe('hash2'); expect(processed.body.primary[0].records).toBe('record2'); expect(processed.body.primary[1].paid).toBe(200.15); expect(processed.body.primary[1].transaction).toBe('hash1'); expect(processed.body.primary[1].records).toBe('record1'); expect(processed.body.auxiliary[0].paid).toBe(760.133); expect(processed.body.auxiliary[0].transaction).toBe('hash3'); expect(processed.body.auxiliary[0].records).toBe('record3'); done(); }); mockSetupClient(client, commands, 'Pool1', () => { const request = mockRequest('Pool1', 'payments', 'records'); const poolApi = new PoolApi(client, poolConfigs, portalConfig); poolApi.handleApiV1(request, (code, message) => { poolApi.buildResponse(code, message, response); }); }); }); test('Test handlePayments API endpoint', (done) => { const commands = [ ['hset', 'Pool1:payments:primary:generate', 'worker1', 134.3], ['hset', 'Pool1:payments:primary:generate', 'worker2', 255.17], ['hset', 'Pool1:payments:primary:generate', 'worker3', 0], ['hset', 'Pool1:payments:primary:immature', 'worker1', 76.23], ['hset', 'Pool1:payments:primary:immature', 'worker2', 12.17], ['hset', 'Pool1:payments:primary:immature', 'worker3', 76.4], ['hset', 'Pool1:payments:primary:paid', 'worker1', 0], ['hset', 'Pool1:payments:primary:paid', 'worker2', 123.5], ['hset', 'Pool1:payments:primary:paid', 'worker3', 45.66], ['hset', 'Pool1:payments:auxiliary:generate', 'worker1', 134.3], ['hset', 'Pool1:payments:auxiliary:generate', 'worker2', 255.17], ['hset', 'Pool1:payments:auxiliary:immature', 'worker1', 76.23], ['hset', 'Pool1:payments:auxiliary:paid', 'worker2', 123.5]]; const response = mockResponse(); response.on('end', (payload) => { const processed = JSON.parse(payload); expect(processed.statusCode).toBe(200); expect(typeof processed.body).toBe('object'); expect(Object.keys(processed.body.primary).length).toBe(4); expect(Object.keys(processed.body.auxiliary).length).toBe(4); expect(processed.body.primary.generate.worker1).toBe(134.3); expect(processed.body.primary.generate.worker2).toBe(255.17); expect(processed.body.primary.immature.worker1).toBe(76.23); expect(processed.body.primary.immature.worker2).toBe(12.17); expect(processed.body.primary.immature.worker3).toBe(76.4); expect(processed.body.primary.paid.worker2).toBe(123.5); expect(processed.body.primary.paid.worker3).toBe(45.66); expect(processed.body.auxiliary.generate.worker1).toBe(134.3); expect(processed.body.auxiliary.generate.worker2).toBe(255.17); expect(processed.body.auxiliary.immature.worker1).toBe(76.23); expect(processed.body.auxiliary.paid.worker2).toBe(123.5); done(); }); mockSetupClient(client, commands, 'Pool1', () => { const request = mockRequest('Pool1', 'payments'); const poolApi = new PoolApi(client, poolConfigs, portalConfig); poolApi.handleApiV1(request, (code, message) => { poolApi.buildResponse(code, message, response); }); }); }); test('Test handleRoundsCurrent API endpoint', (done) => { const commands = [ ['hset', 'Pool1:rounds:primary:current:shared:shares', 'worker2.w1', JSON.stringify({ time: 0, work: 64, times: 20, worker: 'worker2.w1' })], ['hset', 'Pool1:rounds:primary:current:shared:shares', 'worker2.w2', JSON.stringify({ time: 0, work: 44, times: 31, worker: 'worker2.w2' })], ['hset', 'Pool1:rounds:primary:current:shared:shares', 'worker3', JSON.stringify({ time: 0, work: 8, times: 50, worker: 'worker3' })], ['hset', 'Pool1:rounds:primary:current:solo:shares', 'worker1', JSON.stringify({ time: 0, work: 64, times: 43, types: { valid: 2, invalid: 1, stale: 1 }, worker: 'worker1' })]]; const response = mockResponse(); const expected = { 'auxiliary': {'round': 'current', 'shared': {}, 'solo': {}, 'times': {}}, 'primary': {'round': 'current', 'shared': {'worker2': 108, 'worker3': 8}, 'solo': {'worker1': 64}, 'times': {'worker2': 31, 'worker3': 50}}}; response.on('end', (payload) => { const processed = JSON.parse(payload); expect(processed.statusCode).toBe(200); expect(typeof processed.body).toBe('object'); expect(processed.body).toStrictEqual(expected); done(); }); mockSetupClient(client, commands, 'Pool1', () => { const request = mockRequest('Pool1', 'rounds', 'current'); const poolApi = new PoolApi(client, poolConfigs, portalConfig); poolApi.handleApiV1(request, (code, message) => { poolApi.buildResponse(code, message, response); }); }); }); test('Test handleRoundsHeight API endpoint', (done) => { const commands = [ ['hset', 'Pool1:rounds:primary:round-180:shares', 'worker2.w1', JSON.stringify({ time: 0, work: 64, times: 20, worker: 'worker2.w1' })], ['hset', 'Pool1:rounds:primary:round-180:shares', 'worker2.w2', JSON.stringify({ time: 0, work: 44, times: 31, worker: 'worker2.w2' })], ['hset', 'Pool1:rounds:auxiliary:round-180:shares', 'worker3', JSON.stringify({ time: 0, work: 8, times: 50, worker: 'worker3' })]]; const response = mockResponse(); const expected = { 'auxiliary': {'round': 180, 'work': {'worker3': 8}, 'times': {'worker3': 50}}, 'primary': {'round': 180, 'work': {'worker2': 108}, 'times': {'worker2': 31}}}; response.on('end', (payload) => { const processed = JSON.parse(payload); expect(processed.statusCode).toBe(200); expect(typeof processed.body).toBe('object'); expect(processed.body).toStrictEqual(expected); done(); }); mockSetupClient(client, commands, 'Pool1', () => { const request = mockRequest('Pool1', 'rounds', '180'); const poolApi = new PoolApi(client, poolConfigs, portalConfig); poolApi.handleApiV1(request, (code, message) => { poolApi.buildResponse(code, message, response); }); }); }); test('Test handleRounds API endpoint [1]', (done) => { const commands = [ ['hset', 'Pool1:rounds:primary:round-180:shares', 'worker2.w1', JSON.stringify({ time: 0, work: 64, times: 20, worker: 'worker2.w1' })], ['hset', 'Pool1:rounds:primary:round-180:shares', 'worker2.w2', JSON.stringify({ time: 0, work: 44, times: 31, worker: 'worker2.w2' })], ['hset', 'Pool1:rounds:auxiliary:round-180:shares', 'worker3', JSON.stringify({ time: 0, work: 8, times: 50, worker: 'worker3' })], ['hset', 'Pool1:rounds:primary:round-181:shares', 'worker2.w1', JSON.stringify({ time: 0, work: 84, times: 50, worker: 'worker2.w1' })], ['hset', 'Pool1:rounds:primary:round-181:shares', 'worker2.w2', JSON.stringify({ time: 0, work: 34, times: 46, worker: 'worker2.w2' })], ['hset', 'Pool1:rounds:auxiliary:round-181:shares', 'worker3', JSON.stringify({ time: 0, work: 100, times: 105, worker: 'worker3' })]]; const response = mockResponse(); const expected = { 'auxiliary': [{'round': 180, 'work': {'worker3': 8}, 'times': {'worker3': 50}}, {'round': 181, 'work': {'worker3': 100}, 'times': {'worker3': 105}}], 'primary': [{'round': 180, 'work': {'worker2': 108}, 'times': {'worker2': 31}}, {'round': 181, 'work': {'worker2': 118}, 'times': {'worker2': 50}}]}; response.on('end', (payload) => { const processed = JSON.parse(payload); expect(processed.statusCode).toBe(200); expect(typeof processed.body).toBe('object'); expect(processed.body).toStrictEqual(expected); done(); }); mockSetupClient(client, commands, 'Pool1', () => { const request = mockRequest('Pool1', 'rounds'); const poolApi = new PoolApi(client, poolConfigs, portalConfig); poolApi.handleApiV1(request, (code, message) => { poolApi.buildResponse(code, message, response); }); }); }); test('Test handleRounds API endpoint [2]', (done) => { const response = mockResponse(); response.on('end', (payload) => { const processed = JSON.parse(payload); expect(processed.statusCode).toBe(200); expect(typeof processed.body).toBe('object'); expect(Object.keys(processed.body).length).toBe(2); expect(Object.keys(processed.body.primary).length).toBe(0); expect(Object.keys(processed.body.auxiliary).length).toBe(0); done(); }); mockSetupClient(client, [], 'Pool1', () => { const request = mockRequest('Pool1', 'rounds'); const poolApi = new PoolApi(client, poolConfigs, portalConfig); poolApi.handleApiV1(request, (code, message) => { poolApi.buildResponse(code, message, response); }); }); }); test('Test handleStatistics API endpoint [1]', (done) => { const commands = [ ['hset', 'Pool1:blocks:primary:counts', 'valid', 500], ['hset', 'Pool1:blocks:primary:counts', 'invalid', 2], ['hset', 'Pool1:payments:primary:counts', 'total', 200.5], ['hset', 'Pool1:payments:primary:counts', 'last', 0], ['hset', 'Pool1:payments:primary:counts', 'next', 1], ['hset', 'Pool1:rounds:primary:current:shared:counts', 'valid', 3190], ['hset', 'Pool1:rounds:primary:current:shared:counts', 'stale', 123], ['hset', 'Pool1:rounds:primary:current:shared:counts', 'invalid', 465], ['zadd', 'Pool1:rounds:primary:current:shared:hashrate', Date.now() / 1000, JSON.stringify({ time: 0, worker: 'worker2.w1', work: 64 })], ['zadd', 'Pool1:rounds:primary:current:shared:hashrate', Date.now() / 1000, JSON.stringify({ time: 0, worker: 'worker2.w1', work: 64 })], ['zadd', 'Pool1:rounds:primary:current:shared:hashrate', Date.now() / 1000, JSON.stringify({ time: 0, worker: 'worker2.w1', work: 64 })], ['zadd', 'Pool1:rounds:primary:current:shared:hashrate', Date.now() / 1000, JSON.stringify({ time: 1, worker: 'worker3', work: 8 })], ['zadd', 'Pool1:rounds:primary:current:shared:hashrate', Date.now() / 1000, JSON.stringify({ time: 1, worker: 'worker2.w2', work: 44 })], ['zadd', 'Pool1:rounds:primary:current:solo:hashrate', Date.now() / 1000, JSON.stringify({ time: 0, worker: 'worker1', work: 32 })], ['zadd', 'Pool1:rounds:primary:current:solo:hashrate', Date.now() / 1000, JSON.stringify({ time: 1, worker: 'worker1', work: 32 })], ['hset', 'Pool1:blocks:auxiliary:counts', 'valid', 500], ['hset', 'Pool1:blocks:auxiliary:counts', 'invalid', 2]]; const response = mockResponse(); const expected = { 'auxiliary': {'blocks': {'invalid': 2, 'valid': 500}, 'config': {'algorithm': '', 'coin': '', 'minPayment': 0, 'paymentInterval': 0, 'recipientFee': 0, 'symbol': ''}, 'hashrate': {'shared': 0, 'solo': 0}, 'network': {'difficulty': 0, 'hashrate': 0, 'height': 0}, 'payments': {'last': 0, 'next': 0, 'total': 0}, 'shares': {'invalid': 0, 'stale': 0, 'valid': 0}, 'status': {'effort': 0, 'luck': {'luck1': 0, 'luck10': 0, 'luck100': 0}, 'miners': 0, 'workers': 0}}, 'primary': {'blocks': {'invalid': 2, 'valid': 500}, 'config': {'algorithm': '', 'coin': '', 'minPayment': 0, 'paymentInterval': 0, 'recipientFee': 0, 'symbol': ''}, 'hashrate': {'shared': 1660720687.7866666, 'solo': 916259689.8133334}, 'network': {'difficulty': 0, 'hashrate': 0, 'height': 0}, 'payments': {'last': 0, 'next': 1, 'total': 200.5}, 'shares': {'invalid': 465, 'stale': 123, 'valid': 3190}, 'status': {'effort': 0, 'luck': {'luck1': 0, 'luck10': 0, 'luck100': 0}, 'miners': 3, 'workers': 4}}}; response.on('end', (payload) => { const processed = JSON.parse(payload); expect(processed.statusCode).toBe(200); expect(typeof processed.body).toBe('object'); expect(processed.body).toStrictEqual(expected); done(); }); mockSetupClient(client, commands, 'Pool1', () => { const request = mockRequest('Pool1', 'statistics'); const poolApi = new PoolApi(client, poolConfigs, portalConfig); poolApi.handleApiV1(request, (code, message) => { poolApi.buildResponse(code, message, response); }); }); }); test('Test handleStatistics API endpoint [2]', (done) => { const commands = [ ['hset', 'Pool1:blocks:primary:counts', 'valid', 500], ['hset', 'Pool1:blocks:primary:counts', 'invalid', 2], ['hset', 'Pool1:payments:primary:counts', 'total', 200.5], ['hset', 'Pool1:payments:primary:counts', 'last', 0], ['hset', 'Pool1:payments:primary:counts', 'next', 1], ['hset', 'Pool1:rounds:primary:current:shared:counts', 'valid', 3190], ['hset', 'Pool1:rounds:primary:current:shared:counts', 'stale', 123], ['hset', 'Pool1:rounds:primary:current:shared:co