UNPKG

blocke

Version:

Command-line tool to query the blockchain for a variety of cryptos. View blocks, transactions, and accounts (where applicable).

1,478 lines (1,442 loc) 59 kB
/* Copyright (C) 2017 BikBikBikBikBik This file is part of blocke. blocke is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. blocke is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with blocke. If not, see <http://www.gnu.org/licenses/>. */ const apiResources = require('../../../lib/api/resources'); const assert = require('../../chai-setup'); const equal = require('deep-equal'); const nock = require('nock'); const random = require('../../random-generator'); const _ = require('underscore'); //Had issues with timing of before() hook, this is part of a workaround let addAllGenericErrorTestsWasCalled = false; describe('lib/api/*', function() { function addAllGenericErrorTests() { function addGenericErrorTests(test, testFieldName, objectName) { if (test.hasOwnProperty(testFieldName)) { const mockErrorTestResponseTemplate = _.find(test[testFieldName], (data) => data.useAsErrorTestResponseTemplate === true); const testData = mockErrorTestResponseTemplate !== undefined ? mockErrorTestResponseTemplate : _.sortBy(test[testFieldName], (data) => -data.mockResponseData.length)[0]; const urlFormatters = _.pluck(testData.mockResponseData, 'urlFormatter'); const uniqueUrlFormatters = _.uniq(urlFormatters); test[testFieldName] = test[testFieldName].concat(generateGenericErrorTests(uniqueUrlFormatters, testData.mockResponseData[0].values, apiResources.generateObjectNotFoundMessage(objectName), apiResources.generateGenericObjectErrorMessage(objectName), apiResources.connectionTimeoutMessage, apiResources.readTimeoutMessage)); } } tests.forEach((test) => { addGenericErrorTests(test, 'getAccountTests', 'Account'); addGenericErrorTests(test, 'getBlockByNumberOrHashTests', 'Block'); addGenericErrorTests(test, 'getBlockByNumberTests', 'Block'); addGenericErrorTests(test, 'getNetworkInfoTests', 'Network Info'); addGenericErrorTests(test, 'getTransactionTests', 'Transaction'); }); } function generateGenericErrorTests(urlFormatters, urlFormatterValues, notFoundMessage, genericErrorMessage, connectionTimeoutMessage, readTimeoutMessage) { function generateMockResponseData(statusCode) { return _.map(urlFormatters, (urlFormatter) => ({ response: { data: {success: false}, statusCode: statusCode }, urlFormatter: urlFormatter, values: urlFormatterValues })); } function generateMockResponseError(code, connect) { return _.map(urlFormatters, (urlFormatter) => ({ response: { code: code, connect: connect }, urlFormatter: urlFormatter, values: urlFormatterValues })); } const urlFormattersArray = Array.isArray(urlFormatters) ? urlFormatters : [urlFormatters]; return [ { methodInput: random.generateRandomHashString(32), mockResponseData: generateMockResponseError('ETIMEDOUT', true), expectedError: connectionTimeoutMessage, extraTestInfo: 'Connection timeout' }, { methodInput: random.generateRandomHashString(32), mockResponseData: generateMockResponseData(400), expectedError: notFoundMessage, extraTestInfo: 'HTTP 400 response' }, { methodInput: random.generateRandomHashString(32), mockResponseData: generateMockResponseData(404), expectedError: notFoundMessage, extraTestInfo: 'HTTP 404 response' }, { methodInput: random.generateRandomHashString(32), mockResponseData: generateMockResponseData(429), expectedError: apiResources.tooManyRequestsMessage, extraTestInfo: 'HTTP 429 response' }, { methodInput: random.generateRandomHashString(32), mockResponseData: generateMockResponseData(500), expectedError: genericErrorMessage, extraTestInfo: 'HTTP 500 response' }, { methodInput: random.generateRandomHashString(32), mockResponseData: generateMockResponseError('ESOCKETTIMEDOUT', false), expectedError: readTimeoutMessage, extraTestInfo: 'Read timeout' } ]; } function prepareMockHttpResponses(test, testData, apiData, networkForApiUrl) { testData.mockResponseData.forEach((mockResponse) => { const apiBaseAddress = apiData.hasOwnProperty('apiBaseAddress') ? apiData.apiBaseAddress : test.apiBaseAddress; let urlSuffix = test.urlFormatters[mockResponse.urlFormatter]; if (Array.isArray(apiData.urlFormatterReplacements)) { apiData.urlFormatterReplacements.forEach((urlReplacement) => { urlSuffix = urlSuffix.replace(urlReplacement.old, urlReplacement.new); }); } _.each(mockResponse.values, (replacementValue, index) => { const actualValue = (() => { switch (replacementValue) { case '[input]': return Array.isArray(testData.methodInput) ? testData.methodInput[0] : testData.methodInput; case '[network]': return networkForApiUrl; default: return replacementValue; } })(); urlSuffix = urlSuffix.replace(`[${index}]`, actualValue); }); if (mockResponse.response.hasOwnProperty('statusCode')) { nock(apiBaseAddress).get(urlSuffix).reply(mockResponse.response.statusCode, mockResponse.response.data); } else { nock(apiBaseAddress).get(urlSuffix).replyWithError(mockResponse.response); } }); } function runTestForApiClientMethod(test, methodName, objectName, isStandardMethod = true) { if (addAllGenericErrorTestsWasCalled === false) { addAllGenericErrorTests(); addAllGenericErrorTestsWasCalled = true; } describe(test.api, function() { const apiArray = typeof(test.networks) === 'object' ? _.map(test.networks, (data, network) => { const apiData = { api: (self) => new self[test.api](network), isMultiNetworkApi: true, network: network }; if (data.hasOwnProperty('apiBaseAddress')) { apiData.apiBaseAddress = data.apiBaseAddress; } if (data.hasOwnProperty('networkAlias')) { apiData.networkAlias = data.networkAlias; } if (data.hasOwnProperty('urlFormatterReplacements')) { apiData.urlFormatterReplacements = data.urlFormatterReplacements; } return apiData; }) : [{api: (self) => self[test.api]}]; apiArray.forEach((apiData) => { const networkForApiUrl = apiData.hasOwnProperty('networkAlias') ? apiData.networkAlias : apiData.network; function runTests() { if (test.hasOwnProperty(`${methodName}Tests`)) { function callApiMethod(self, testData) { const apiInstance = apiData.api(self); const apiMethod = apiInstance[methodName]; return Array.isArray(testData.methodInput) ? apiMethod.apply(apiInstance, testData.methodInput) : (apiMethod.bind(apiInstance, testData.methodInput))(); } const successAndErrorTests = _.partition(test[`${methodName}Tests`], (testData) => testData.hasOwnProperty('expectedResult')); successAndErrorTests[0].forEach((testData) => { it(`should return a ${objectName} (${testData.extraTestInfo})`, function(done) { const expectedResult = testData.expectedResult.hasOwnProperty(apiData.network) ? testData.expectedResult[apiData.network] : testData.expectedResult; prepareMockHttpResponses(test, testData, apiData, networkForApiUrl); callApiMethod(this, testData).should.eventually.deep.equal(expectedResult).and.notify(done); }); }); successAndErrorTests[1].forEach((testData) => { it(`should not return a ${objectName} (${testData.extraTestInfo})`, function(done) { const expectedError = testData.expectedError.hasOwnProperty(apiData.network) ? testData.expectedError[apiData.network] : testData.expectedError; prepareMockHttpResponses(test, testData, apiData, networkForApiUrl); callApiMethod(this, testData).should.eventually.be.rejectedWith(expectedError).and.notify(done); }); }); } else if (isStandardMethod === true) { it(`should not return a ${objectName} (Operation not supported)`, function(done) { apiData.api(this)[methodName]().should.eventually.be.rejectedWith(apiResources.operationNotSupportedMessage).and.notify(done); }); } } if (apiData.isMultiNetworkApi === true) { describe(apiData.network, runTests); } else { runTests(); } }); }); } /* * * Hooks * */ beforeEach(function() { this.chainradar = require('../../../lib/api/chainradar'); this.etherchain = require('../../../lib/api/etherchain'); this.ethplorer = require('../../../lib/api/ethplorer'); this.gamecredits = require('../../../lib/api/gamecredits'); this.insight = require('../../../lib/api/insight'); this.iquidus = require('../../../lib/api/iquidus'); this.lisk = require('../../../lib/api/lisk'); this.siatech = require('../../../lib/api/siatech'); this.sochain = require('../../../lib/api/sochain'); this.wavesexplorer = require('../../../lib/api/wavesexplorer'); this.zchain = require('../../../lib/api/zchain'); }); /* * * Test Data * */ const tests = [ { api: 'chainradar', apiBaseAddress: 'http://chainradar.com', networks: { aeon: {}, bcn: {}, xdn: {networkAlias: 'duck'}, xmr: {networkAlias: 'mro'} }, urlFormatters: { block: '/api/v1/[0]/blocks/[1]/full', networkInfo: '/api/v1/[0]/status', transaction: '/api/v1/[0]/transactions/[1]/full' }, getBlockByNumberOrHashTests: [ { methodInput: random.generateRandomHashString(32, '235tgwfrvsc'), mockResponseData: [ { response: { data: {hash: random.generateRandomHashString(32, '235tgwfrvsc')}, statusCode: 200 }, urlFormatter: 'block', values: [ '[network]', '[input]' ] } ], expectedResult: {hash: random.generateRandomHashString(32, '235tgwfrvsc')}, extraTestInfo: 'Valid block id' }, { methodInput: random.generateRandomHashString(32), mockResponseData: [ { response: { data: {code: 'ApiNotAvailable'}, statusCode: 200 }, urlFormatter: 'block', values: [ '[network]', '[input]' ] } ], expectedError: apiResources.apiNotAvailableMessage, extraTestInfo: 'ApiNotAvailable response' }, { methodInput: random.generateRandomHashString(32), mockResponseData: [ { response: { data: {code: 'RequestLimitExceeded'}, statusCode: 200 }, urlFormatter: 'block', values: [ '[network]', '[input]' ] } ], expectedError: apiResources.tooManyRequestsMessage, extraTestInfo: 'RequestLimitExceeded response' } ], getNetworkInfoTests: [ { mockResponseData: [ { response: { data: {success: true}, statusCode: 200 }, urlFormatter: 'networkInfo', values: [ '[network]' ] } ], expectedResult: {success: true}, extraTestInfo: 'Valid network info request' }, { mockResponseData: [ { response: { data: {code: 'ApiNotAvailable'}, statusCode: 200 }, urlFormatter: 'networkInfo', values: [ '[network]' ] } ], expectedError: apiResources.apiNotAvailableMessage, extraTestInfo: 'ApiNotAvailable response' }, { mockResponseData: [ { response: { data: {code: 'RequestLimitExceeded'}, statusCode: 200 }, urlFormatter: 'networkInfo', values: [ '[network]' ] } ], expectedError: apiResources.tooManyRequestsMessage, extraTestInfo: 'RequestLimitExceeded response' } ], getTransactionTests: [ { methodInput: random.generateRandomHashString(32, 'aeg4rbtfgr2'), mockResponseData: [ { response: { data: {txid: random.generateRandomHashString(32, 'aeg4rbtfgr2')}, statusCode: 200 }, urlFormatter: 'transaction', values: [ '[network]', '[input]' ] } ], expectedResult: { aeon: { txid: random.generateRandomHashString(32, 'aeg4rbtfgr2'), valueDivisor: 1000000000000 }, bcn: { txid: random.generateRandomHashString(32, 'aeg4rbtfgr2'), valueDivisor: 100000000 }, xdn: { txid: random.generateRandomHashString(32, 'aeg4rbtfgr2'), valueDivisor: 100000000 }, xmr: { txid: random.generateRandomHashString(32, 'aeg4rbtfgr2'), valueDivisor: 1000000000000 }, }, extraTestInfo: 'Valid transaction hash' }, { methodInput: random.generateRandomHashString(32), mockResponseData: [ { response: { data: {code: 'ApiNotAvailable'}, statusCode: 200 }, urlFormatter: 'transaction', values: [ '[network]', '[input]' ] } ], expectedError: apiResources.apiNotAvailableMessage, extraTestInfo: 'ApiNotAvailable response' }, { methodInput: random.generateRandomHashString(32), mockResponseData: [ { response: { data: {code: 'RequestLimitExceeded'}, statusCode: 200 }, urlFormatter: 'transaction', values: [ '[network]', '[input]' ] } ], expectedError: apiResources.tooManyRequestsMessage, extraTestInfo: 'RequestLimitExceeded response' } ] }, { api: 'etherchain', apiBaseAddress: 'https://etherchain.org', urlFormatters: { block: '/api/block/[0]', networkInfo: '/api/basic_stats' }, getBlockByNumberOrHashTests: [ { methodInput: random.generateRandomHashString(32, '54teryghdf'), mockResponseData: [ { response: { data: {data: [{hash: random.generateRandomHashString(32, '54teryghdf')}]}, statusCode: 200 }, urlFormatter: 'block', values: [ '[input]' ] } ], expectedResult: {hash: random.generateRandomHashString(32, '54teryghdf')}, extraTestInfo: 'Valid block hash' }, { methodInput: `${random.generateRandomIntInclusive(1, 5000000, 'asdgfs435')}`, mockResponseData: [ { response: { data: {data: [{ hash: random.generateRandomHashString(32, '23tgfda'), number: random.generateRandomIntInclusive(1, 5000000, 'asdgfs435') }]}, statusCode: 200 }, urlFormatter: 'block', values: [ '[input]' ] } ], expectedResult: { hash: random.generateRandomHashString(32, '23tgfda'), number: random.generateRandomIntInclusive(1, 5000000, 'asdgfs435') }, extraTestInfo: 'Valid block height' }, { methodInput: random.generateRandomHashString(32, '2354ythdjg'), mockResponseData: [ { response: { data: {data: [{ hash: random.generateRandomHashString(32, 'asgfdb4'), number: random.generateRandomIntInclusive(1, 5000000, '254thgfsb') }]}, statusCode: 200 }, urlFormatter: 'block', values: [ '[input]' ] } ], expectedError: apiResources.blockNotFoundMessage, extraTestInfo: 'Default block #0 response' }, { methodInput: random.generateRandomHashString(32), mockResponseData: [ { response: { data: { data: [], success: false }, statusCode: 200 }, urlFormatter: 'block', values: [ '[input]' ] } ], expectedError: apiResources.blockNotFoundMessage, extraTestInfo: 'Generic error response' } ], getNetworkInfoTests: [ { mockResponseData: [ { response: { data: {data: {success: true}}, statusCode: 200 }, urlFormatter: 'networkInfo', values: [] } ], expectedResult: {success: true}, extraTestInfo: 'Valid network info request' } ], }, { api: 'ethplorer', apiBaseAddress: 'https://api.ethplorer.io', urlFormatters: { account: '/getAddressInfo/[0]?apiKey=freekey', accountPlusPrefix: '/getAddressInfo/0x[0]?apiKey=freekey', transaction: '/getTxInfo/[0]?apiKey=freekey' }, getAccountTests: [ { methodInput: `0x${random.generateRandomHashString(32, '245ythdgf')}`, mockResponseData: [ { response: { data: {address: `0x${random.generateRandomHashString(32, '245ythdgf')}`}, statusCode: 200 }, urlFormatter: 'account', values: [ '[input]' ] } ], expectedResult: {address: `0x${random.generateRandomHashString(32, '245ythdgf')}`}, extraTestInfo: `Valid account address with '0x' prefix` }, { methodInput: random.generateRandomHashString(32, '43565ythdd'), mockResponseData: [ { response: { data: {address: `0x${random.generateRandomHashString(32, '43565ythdd')}`}, statusCode: 200 }, urlFormatter: 'accountPlusPrefix', values: [ '[input]' ] } ], expectedResult: {address: `0x${random.generateRandomHashString(32, '43565ythdd')}`}, extraTestInfo: `Valid account address without '0x' prefix`, useAsErrorTestResponseTemplate: true }, { methodInput: `0x${random.generateRandomHashString(32, '6wegffs')}`, mockResponseData: [ { response: { data: {contractInfo: {}}, statusCode: 200 }, urlFormatter: 'account', values: [ '[input]' ] } ], expectedError: apiResources.eth.addressIsContractMessage, extraTestInfo: `Address is a contract` }, { methodInput: `0x${random.generateRandomHashString(32, '45trgfh')}`, mockResponseData: [ { response: { data: {error: 'Invalid account'}, statusCode: 200 }, urlFormatter: 'account', values: [ '[input]' ] } ], expectedError: apiResources.accountNotFoundMessage, extraTestInfo: `Generic error response` } ], getTransactionTests: [ { methodInput: random.generateRandomHashString(32, '234htgnbx'), mockResponseData: [ { response: { data: {txid: random.generateRandomHashString(32, '234htgnbx')}, statusCode: 200 }, urlFormatter: 'transaction', values: [ '[input]' ] } ], expectedResult: { txid: random.generateRandomHashString(32, '234htgnbx'), valueDivisor: 1, valueSymbol: '' }, extraTestInfo: 'ETH transfer' }, { methodInput: random.generateRandomHashString(32, '27yuerofdsa'), mockResponseData: [ { response: { data: { operations: [{ from: random.generateRandomHashString(32, 'rtgsiuodl'), intValue: random.generateRandomIntInclusive(1, 1000, '4578orthyg'), to: random.generateRandomHashString(32, '243trg'), tokenInfo: { decimals: `${random.generateRandomIntInclusive(2, 8, '324trh')}`, symbol: random.generateRandomHashString(3, 'eutiohsldjf') }, type: 'transfer' }]}, statusCode: 200 }, urlFormatter: 'transaction', values: [ '[input]' ] } ], expectedResult: { from: random.generateRandomHashString(32, 'rtgsiuodl'), operations: [{ from: random.generateRandomHashString(32, 'rtgsiuodl'), intValue: random.generateRandomIntInclusive(1, 1000, '4578orthyg'), to: random.generateRandomHashString(32, '243trg'), tokenInfo: { decimals: `${random.generateRandomIntInclusive(2, 8, '324trh')}`, symbol: random.generateRandomHashString(3, 'eutiohsldjf') }, type: 'transfer' }], to: random.generateRandomHashString(32, '243trg'), value: random.generateRandomIntInclusive(1, 1000, '4578orthyg'), valueDivisor: Math.pow(10, random.generateRandomIntInclusive(2, 8, '324trh')), valueSymbol: random.generateRandomHashString(3, 'eutiohsldjf') }, extraTestInfo: 'Token transfer' }, { methodInput: random.generateRandomHashString(32, '43tytrhgf'), mockResponseData: [ { response: { data: { operations: [{ from: random.generateRandomHashString(32, '35ythdg'), to: random.generateRandomHashString(32, '243regsf'), type: random.generateRandomHashString(32, '378teruhgilf') }]}, statusCode: 200 }, urlFormatter: 'transaction', values: [ '[input]' ] } ], expectedResult: { operations: [{ from: random.generateRandomHashString(32, '35ythdg'), to: random.generateRandomHashString(32, '243regsf'), type: random.generateRandomHashString(32, '378teruhgilf') }], valueDivisor: 1, valueSymbol: '' }, extraTestInfo: 'Non-transfer contract operation' }, { methodInput: random.generateRandomHashString(32, '234thygdf'), mockResponseData: [ { response: { data: {error: 'Invalid transaction'}, statusCode: 200 }, urlFormatter: 'transaction', values: [ '[input]' ] } ], expectedError: apiResources.transactionNotFoundMessage, extraTestInfo: `Generic error response` } ] }, { api: 'gamecredits', apiBaseAddress: 'http://blockexplorer.gamecredits.com', urlFormatters: { account: '/api/addresses/[0]/balance', block: '/api/blocks/[0]', blockHeight: '/api/blocks?height=[0]', networkInfo: '/api/network/info', transaction: '/api/transactions/[0]' }, getAccountTests: [ { methodInput: random.generateRandomHashString(32, 'asdfghbfgff'), mockResponseData: [ { response: { data: `${random.generateRandomIntInclusive(1, 5000000, '1234233')}`, statusCode: 200 }, urlFormatter: 'account', values: [ '[input]' ] } ], expectedResult: { address: random.generateRandomHashString(32, 'asdfghbfgff'), balance: random.generateRandomIntInclusive(1, 5000000, '1234233') }, extraTestInfo: 'Valid account address' } ], getBlockByNumberOrHashTests: [ { methodInput: random.generateRandomHashString(32, '234radsf'), mockResponseData: [ { response: { data: {success: false}, statusCode: 404 }, urlFormatter: 'blockHeight', values: [ '[input]' ] }, { response: { data: {hash: random.generateRandomHashString(32, '234radsf')}, statusCode: 200 }, urlFormatter: 'block', values: [ '[input]' ] } ], expectedResult: {hash: random.generateRandomHashString(32, '234radsf')}, extraTestInfo: 'Valid block hash' }, { methodInput: random.generateRandomIntInclusive(1, 5000000, '423wraedf'), mockResponseData: [ { response: { data: {height: random.generateRandomIntInclusive(1, 5000000, '423wraedf')}, statusCode: 200 }, urlFormatter: 'blockHeight', values: [ '[input]' ] } ], expectedResult: {height: random.generateRandomIntInclusive(1, 5000000, '423wraedf')}, extraTestInfo: 'Valid block height' } ], getNetworkInfoTests: [ { mockResponseData: [ { response: { data: {success: true}, statusCode: 200 }, urlFormatter: 'networkInfo', values: [] } ], expectedResult: {success: true}, extraTestInfo: 'Valid network info request' } ], getTransactionTests: [ { methodInput: random.generateRandomHashString(32, '23rtsgf'), mockResponseData: [ { response: { data: { txid: random.generateRandomHashString(32, '23rtsgf'), vin: [ { prev_txid: random.generateRandomHashString(32, 'asrhgwe5t'), vout_index: 0 }, { prev_txid: random.generateRandomHashString(32, '2radgfsg'), vout_index: 0 } ] }, statusCode: 200 }, urlFormatter: 'transaction', values: [ '[input]' ] }, { response: { data: { txid: random.generateRandomHashString(32, 'asrhgwe5t'), vout: [{ addresses: [random.generateRandomHashString(32, 'a235tgb')], value: random.generateRandomIntInclusive(1, 1000, 'fdgw3455')}] }, statusCode: 200 }, urlFormatter: 'transaction', values: [ random.generateRandomHashString(32, 'asrhgwe5t') ] }, { response: { data: { txid: random.generateRandomHashString(32, '2radgfsg'), vout: [{ addresses: [random.generateRandomHashString(32, 'asdgbg')], value: random.generateRandomIntInclusive(1, 1000, '35yjtgdhf')}] }, statusCode: 200 }, urlFormatter: 'transaction', values: [ random.generateRandomHashString(32, '2radgfsg') ] } ], expectedResult: { txid: random.generateRandomHashString(32, '23rtsgf'), vin: [ { address: random.generateRandomHashString(32, 'a235tgb'), prev_txid: random.generateRandomHashString(32, 'asrhgwe5t'), value: random.generateRandomIntInclusive(1, 1000, 'fdgw3455'), vout_index: 0 }, { address: random.generateRandomHashString(32, 'asdgbg'), prev_txid: random.generateRandomHashString(32, '2radgfsg'), value: random.generateRandomIntInclusive(1, 1000, '35yjtgdhf'), vout_index: 0 } ] }, extraTestInfo: `Valid transaction hash with 'fetchVinAddresses' true` }, { methodInput: [random.generateRandomHashString(32, '45ythdgh'), false], mockResponseData: [ { response: { data: { txid: random.generateRandomHashString(32, '45ythdgh'), vin: [{ prev_txid: random.generateRandomHashString(32, '54rhgf'), vout_index: 0 }] }, statusCode: 200 }, urlFormatter: 'transaction', values: [ '[input]' ] } ], expectedResult: { txid: random.generateRandomHashString(32, '45ythdgh'), vin: [{ prev_txid: random.generateRandomHashString(32, '54rhgf'), vout_index: 0 }] }, extraTestInfo: `Valid transaction hash with 'fetchVinAddresses' false` } ], updateTransactionInputAddressesTests: [ { methodInput: {vin: [{ prev_txid: random.generateRandomHashString(32, '54yrdhfg'), vout_index: 0 }]}, mockResponseData: [ { response: { data: { txid: random.generateRandomHashString(32, '54yrdhfg'), vout: [{ addresses: [random.generateRandomHashString(32, '5tsehg')], value: random.generateRandomIntInclusive(1, 1000, '45yrhdf') }] }, statusCode: 200 }, urlFormatter: 'transaction', values: [ random.generateRandomHashString(32, '54yrdhfg') ] } ], expectedResult: { vin: [{ address: random.generateRandomHashString(32, '5tsehg'), prev_txid: random.generateRandomHashString(32, '54yrdhfg'), value: random.generateRandomIntInclusive(1, 1000, '45yrhdf'), vout_index: 0 }] }, extraTestInfo: 'Valid transaction inputs' } ] }, { api: 'insight', networks: { bch: { apiBaseAddress: 'http://blockdozer.com', urlFormatterReplacements: [{ new: '/insight-api/', old: '/api/' }] }, dcr: {apiBaseAddress: 'https://mainnet.decred.org'}, dgb: {apiBaseAddress: 'https://digiexplorer.info'}, kmd: {apiBaseAddress: 'http://kmd.explorer.supernet.org'}, rdd: {apiBaseAddress: 'http://live.reddcoin.com'} }, urlFormatters: { account: '/api/addr/[0]', block: '/api/block/[0]', blockIndex: '/api/block-index/[0]', networkInfo: '/api/status?q=getInfo', transaction: '/api/tx/[0]' }, getAccountTests: [ { methodInput: random.generateRandomHashString(32, '2345trgfdsghf'), mockResponseData: [ { response: { data: {addrStr: random.generateRandomHashString(32, '2345trgfdsghf')}, statusCode: 200 }, urlFormatter: 'account', values: [ '[input]' ] } ], expectedResult: {addrStr: random.generateRandomHashString(32, '2345trgfdsghf')}, extraTestInfo: 'Valid account address' } ], getBlockByNumberOrHashTests: [ { methodInput: random.generateRandomHashString(32, '3425654'), mockResponseData: [ { response: { data: {hash: random.generateRandomHashString(32, '3425654')}, statusCode: 200 }, urlFormatter: 'block', values: [ '[input]' ] } ], expectedResult: {hash: random.generateRandomHashString(32, '3425654')}, extraTestInfo: 'Valid block hash' }, { methodInput: `${random.generateRandomIntInclusive(1, 5000000, '234t5ydhfg')}`, mockResponseData: [ { response: { data: {success: false}, statusCode: 404 }, urlFormatter: 'block', values: [ '[input]' ] }, { response: { data: {blockHash: random.generateRandomHashString(32, '234rasdf')}, statusCode: 200 }, urlFormatter: 'blockIndex', values: [ '[input]' ] }, { response: { data: {height: random.generateRandomIntInclusive(1, 5000000, '234t5ydhfg')}, statusCode: 200 }, urlFormatter: 'block', values: [ random.generateRandomHashString(32, '234rasdf') ] } ], expectedResult: {height: random.generateRandomIntInclusive(1, 5000000, '234t5ydhfg')}, extraTestInfo: 'Valid block height' }, { methodInput: random.generateRandomHashString(32, '453trgh'), mockResponseData: [ { response: { data: { hash: random.generateRandomHashString(32, 're4tgdfh'), height: random.generateRandomIntInclusive(1, 5000000, 'sldkjf') }, statusCode: 200 }, urlFormatter: 'block', values: [ '[input]' ] } ], expectedError: apiResources.blockNotFoundMessage, extraTestInfo: 'Incorrect block response' } ], getNetworkInfoTests: [ { mockResponseData: [ { response: { data: {success: true}, statusCode: 200 }, urlFormatter: 'networkInfo', values: [] } ], expectedResult: {success: true}, extraTestInfo: 'Valid network info request' } ], getTransactionTests: [ { methodInput: random.generateRandomHashString(32, '345456456'), mockResponseData: [ { response: { data: {txid: random.generateRandomHashString(32, '345456456')}, statusCode: 200 }, urlFormatter: 'transaction', values: [ '[input]' ] } ], expectedResult: {txid: random.generateRandomHashString(32, '345456456')}, extraTestInfo: 'Valid transaction hash' } ] }, { api: 'iquidus', networks: { sigt: {apiBaseAddress: 'http://explorer.signatum.io'}, vtc: {apiBaseAddress: 'https://explorer.vtconline.org'} }, urlFormatters: { account: '/ext/getaddress/[0]', blockHash: '/api/getblock?hash=[0]', blockHeight: '/api/getblockhash?index=[0]', networkInfo: '/ext/summary', transaction: '/api/getrawtransaction?txid=[0]&decrypt=1' }, getAccountTests: [ { methodInput: random.generateRandomHashString(32, '45terfg'), mockResponseData: [ { response: { data: {address: random.generateRandomHashString(32, '45terfg')}, statusCode: 200 }, urlFormatter: 'account', values: [ '[input]' ] } ], expectedResult: {address: random.generateRandomHashString(32, '45terfg')}, extraTestInfo: 'Valid account address' }, { methodInput: random.generateRandomHashString(32, '65yrtgd'), mockResponseData: [ { response: { data: {error: 'Invalid account'}, statusCode: 200 }, urlFormatter: 'account', values: [ '[input]' ] } ], expectedError: apiResources.accountNotFoundMessage, extraTestInfo: 'Generic error response' } ], getBlockByNumberTests: [ { methodInput: random.generateRandomIntInclusive(1, 5000000, '243thds'), mockResponseData: [ { response: { data: random.generateRandomHashString(32, '234trehgf'), statusCode: 200 }, urlFormatter: 'blockHeight', values: [ '[input]' ] }, { response: { data: {hash: random.generateRandomHashString(32, '234trehgf')}, statusCode: 200 }, urlFormatter: 'blockHash', values: [ random.generateRandomHashString(32, '234trehgf') ] } ], expectedResult: {hash: random.generateRandomHashString(32, '234trehgf')}, extraTestInfo: 'Valid block height' }, { methodInput: random.generateRandomIntInclusive(1, 5000000), mockResponseData: [ { response: { data: 'There was an error.', statusCode: 200 }, urlFormatter: 'blockHeight', values: [ '[input]' ] } ], expectedError: apiResources.blockNotFoundMessage, extraTestInfo: 'Generic error response' } ], getBlockByNumberOrHashTests: [ { methodInput: random.generateRandomHashString(32, '54trhgf'), mockResponseData: [ { response: { data: {hash: random.generateRandomHashString(32, '54trhgf')}, statusCode: 200 }, urlFormatter: 'blockHash', values: [ '[input]' ] } ], expectedResult: {hash: random.generateRandomHashString(32, '54trhgf')}, extraTestInfo: 'Valid block hash' }, { methodInput: random.generateRandomIntInclusive(1, 5000000, '35y4rhy'), mockResponseData: [ { response: { data: 'Invalid block hash', statusCode: 200 }, urlFormatter: 'blockHash', values: [ '[input]' ] }, { response: { data: random.generateRandomHashString(32, '3654yujt'), statusCode: 200 }, urlFormatter: 'blockHeight', values: [ '[input]' ] }, { response: { data: {hash: random.generateRandomHashString(32, '3654yujt')}, statusCode: 200 }, urlFormatter: 'blockHash', values: [ random.generateRandomHashString(32, '3654yujt') ] } ], expectedResult: {hash: random.generateRandomHashString(32, '3654yujt')}, extraTestInfo: 'Valid block height' }, { methodInput: random.generateRandomIntInclusive(1, 5000000), mockResponseData: [ { response: { data: 'There was an error.', statusCode: 200 }, urlFormatter: 'blockHash', values: [ '[input]' ] }, { response: { data: 'There was an error.', statusCode: 200 }, urlFormatter: 'blockHeight', values: [ '[input]' ] } ], expectedError: apiResources.blockNotFoundMessage, extraTestInfo: 'Invalid block id' } ], getNetworkInfoTests: [ { mockResponseData: [ { response: { data: {data: [{hashrate: `${random.generateRandomIntInclusive(1000, 5000000, '56y6tdhg')}`}]}, statusCode: 200 }, urlFormatter: 'networkInfo', values: [] } ], expectedResult: { sigt: { hashrate: `${random.generateRandomIntInclusive(1000, 5000000, '56y6tdhg')}`, hashRateMultiplier: 1 }, vtc: { hashrate: `${random.generateRandomIntInclusive(1000, 5000000, '56y6tdhg')}`, hashRateMultiplier: 1000000000 } }, extraTestInfo: 'Valid network info request' }, { mockResponseData: [ { response: { data: {data: [{hashrate: '-'}]}, statusCode: 200 }, urlFormatter: 'networkInfo', values: [] } ], expectedResult: { sigt: { hashrate: '0', hashRateMultiplier: 1 }, vtc: { hashrate: '0', hashRateMultiplier: 1000000000 } }, extraTestInfo: 'Non-numeric hash rate returned' } ], getTransactionTests: [ { methodInput: random.generateRandomHashString(32, '234tergsx'), mockResponseData: [ { response: { data: { txid: random.generateRandomHashString(32, '234tergsx'), vin: [{ txid: random.generateRandomHashString(32, '7rssfbsd'), vout: random.generateRandomIntInclusive(1, 10, '2ioerfhdgj')}] }, statusCode: 200 }, urlFormatter: 'transaction', values: [ '[input]' ] }, { response: { data: { txid: random.generateRandomHashString(32, '7rssfbsd'), vout: [{ n: random.generateRandomIntInclusive(1, 10, '2ioerfhdgj'), scriptPubKey: {addresses: [random.generateRandomHashString(32, '4rdsfghfdh')]}, value: random.generateRandomIntInclusive(1, 1000, '2355trsgf') }] }, statusCode: 200 }, urlFormatter: 'transaction', values: [ random.generateRandomHashString(32, '7rssfbsd') ] } ], expectedResult: { txid: random.generateRandomHashString(32, '234tergsx'), vin: [{ address: random.generateRandomHashString(32, '4rdsfghfdh'), txid: random.generateRandomHashString(32, '7rssfbsd'), value: random.generateRandomIntInclusive(1, 1000, '2355trsgf'), vout: random.generateRandomIntInclusive(1, 10, '2ioerfhdgj') }] }, extraTestInfo: `Valid transaction hash with 'fetchVinAddresses' true` }, { methodInput: [random.generateRandomHashString(32, '6545etd'), false], mockResponseData: [ { response: { data: { txid: random.generateRandomHashString(32, '6545etd'), vin: [{txid: random.generateRandomHashString(32, '234erfgsd')}] }, statusCode: 200 }, urlFormatter: 'transaction', values: [ '[input]' ] } ], expectedResult: { txid: random.generateRandomHashString(32, '6545etd'), vin: [{txid: random.generateRandomHashString(32, '234erfgsd')}] }, extraTestInfo: `Valid transaction hash with 'fetchVinAddresses' false` }, { methodInput: random.generateRandomHashString(32, '78oiuljk'), mockResponseData: [ { response: { data: 'Invalid transaction hash', statusCode: 200 }, urlFormatter: 'transaction', values: [ '[input]' ] } ], expectedError: apiResources.transactionNotFoundMessage, extraTestInfo: 'Generic error response' } ], updateTransactionInputAddressesTests: [ { methodInput: {vin: [{ txid: random.generateRandomHashString(32, '398eruigfj'), vout: random.generateRandomIntInclusive(1, 10, '324wtrregd') }]}, mockResponseData: [ { response: { data: { txid: random.generateRandomHashString(32, '398eruigfj'), vout: [{ n: random.generateRandomIntInclusive(1, 10, '324wtrregd'), scriptPubKey: {addresses: [random.generateRandomHashString(32, '2534t4s')]}, value: random.generateRandomIntInclusive(1, 1000, '2wsfgbx') }] }, statusCode: 200 }, urlFormatter: 'transaction', values: [ random.generateRandomHashString(32, '398eruigfj') ] } ], expectedResult: { vin: [{ address: random.generateRandomHashString(32, '2534t4s'), txid: random.generateRandomHashString(32, '398eruigfj'), value: random.generateRandomIntInclusive(1, 1000, '2wsfgbx'), vout: random.generateRandomIntInclusive(1, 10, '324wtrregd') }] }, extraTestInfo: 'Valid transaction inputs' } ] }, { api: 'lisk', apiBaseAddress: 'https://explorer.lisk.io', urlFormatters: { account: '/api/getAccount?address=[0]', block: '/api/getBlock?blockId=[0]', networkInfo: '/api/getLastBlocks', search: '/api/search?id=[0]', transaction: '/api/getTransaction?transactionId=[0]' }, getAccountTests: [ { methodInput: random.generateRandomHashString(32, '234resgf'), mockResponseData: [ { response: { data: {address: random.generateRandomHashString(32, '234resgf')}, statusCode: 200 }, urlFormatter: 'account', values: [ '[input]' ] } ], expectedResult: {address: random.generateRandomHashString(32, '234resgf')}, extraTestInfo: 'Valid account address' }, { methodInput: random.generateRandomHashString(32, '4tgrt'), mockResponseData: [ { response: { data: {success: false}, statusCode: 200 }, urlFormatter: 'account', values: [ '[input]' ] } ], expectedError: apiResources.accountNotFoundMessage, extraTestInfo: 'Generic error response' } ], getBlockByNumberOrHashTests: [ { methodInput: random.generateRandomHashString(32, '234resgdf'), mockResponseData: [ { response: { data: {hash: random.generateRandomHashString(32, '234resgdf')}, statusCode: 200 }, urlFormatter: 'block', values: [ '[input]' ] } ], expectedResult: {hash: random.generateRandomHashString(32, '234resgdf')}, extraTestInfo: 'Valid block hash' }, { methodInput: random.generateRandomIntInclusive(1, 5000000, 'rewgftgh'), mockResponseData: [ { response: { data: {success: false}, statusCode: 200 }, urlFormatter: 'block', values: [ '[input]' ] }, { response: { data: { id: random.generateRandomHashString(32, 'dsffghbh'), type: 'block' }, statusCode: 200 }, urlFormatter: 'search', values: [ '[input]' ] }, { response: { data: {hash: random.generateRandomHashString(32, 'dsffghbh')}, statusCode: 200 }, urlFormatter: 'block', values: [ random.generateRandomHashString(32, 'dsffghbh') ] } ], expectedResult: {hash: random.generateRandomHashString(32, 'dsffghbh')}, extraTestInfo: 'Valid block height' }, { methodInput: random.generateRandomHashString(32, 'ritsuhgd'), mockResponseData: [ { response: { data: {success: false}, statusCode: 200 }, urlFormatter: 'block', values: [ '[input]' ] }, { response: { data: {success: false}, statusCode: 200 }, urlFormatter: 'search', values: [ '[input]' ] } ], expectedError: apiResources.blockNotFoundMessage, extraTestInfo: 'Invalid block id', useAsErrorTestResponseTemplate: true } ], getNetworkInfoTests: [ { mockResponseData: [ { response: { data: {success: true}, statusCode: 200 }, urlFormatter: 'networkInfo', values: [] } ], expectedResult: {success: true}, extraTestInfo: 'Valid network info request' }, { mockResponseData: [ { response: { data: {success: false}, statusCode: 200 }, urlFormatter: 'networkInfo', values: [] } ], expectedError: apiResources.networkInfoNotFoundMessage, extraTestInfo: 'Generic error response' } ], getTransactionTests: [ { methodInput: random.generateRandomHashString(32, 'sadgh3'), mockResponseData: [ { response: { data: {txid: random.generateRandomHashString(32, 'sadgh3')}, statusCode: 200 }, urlFormatter: 'transaction', values: [ '[input]' ] } ], expectedResult: {txid: random.generateRandomHashString(32, 'sadgh3')}, extraTestInfo: 'Valid transaction hash' }, { methodInput: random.generateRandomHashString(32, '4tgrt'), mockResponseData: [ { response: { data: {success: false}, statusCode: 200 }, urlFormatter: 'transaction', values: [ '[input]' ] } ], expectedError: apiResources.transactionNotFoundMessage, extraTestInfo: 'Generic error response' } ] }, { api: 'siatech', apiBaseAddress: 'https://explore.sia.tech', urlFormatters: { block: '/explorer/blocks/[0]', networkInfo: '/explorer', hash: '/explorer/hashes/[0]', }, getBlockByNumberOrHashTests: [ { methodInput: `${random.generateRandomIntInclusive(1, 5000000, 'asdfvbn')}`, mockResponseData: [ { response: { data: {block: {height: random.generateRandomIntInclusive(1, 5000000, 'asfdghj')}}, statusCode: 200 }, urlFormatter: 'block', values: [ '[input]' ] }, { response: { data: { block: {hash: random.generateRandomHashString(32, 'mdfggsdf')}, hashtype: 'blockid' }, statusCode: 200 }, urlFormatter: 'hash', values: [ '[input]' ] } ], expectedResult: {hash: random.generateRandomHashString(32, 'mdfggsdf')}, extraTestInfo: 'Valid block hash' }, { methodInput: `${random.generateRandomIntInclusive(1, 5000000, '2435rtgf')}`, mockResponseData: [ { response: { data: {block: {height: random.generateRandomIntInclusive(1, 5000000, '2435rtgf')}}, statusCode: 200 }, urlFormatter: 'block', values: [ '[input]' ] } ], expectedResult: {height: random.generateRandomIntInclusive(1, 5000000, '2435rtgf')}, extraTestInfo: 'Valid block height' }, { methodInput: `${random.generateRandomIntInclusive(1, 5000000, 'afkjhln')}`, mockResponseData: [ { response: { data: {block: {height: random.generateRandomIntInclusive(1, 5000000, '324t5rg')}}, statusCode: 200 }, urlFormatter: 'block', values: [ '[input]' ] }, { response: { data: {success: false}, statusCode: 404 }, urlFormatter: 'hash', values: [ '[input]' ] } ], expectedError: apiResources.blockNotFoundMessage, extraTestInfo: 'Invalid block id' } ], getNetworkInfoTests: [ { mockResponseData: [ { response: { data: {success: true}, statusCode: 200 }, urlFormatter: 'networkInfo', values: [] } ], expectedResult: {success: true}, extraTestInfo: 'Valid network info request' } ], getTransactionTests: [ { methodInput: random.generateRandomHashString(32, 'fdsghfdnb'), mockResponseData: [ { response: { data: { hashtype: 'transactionid', txid: random.generateRandomHashString(32, 'fdsghfdnb') }, statusCode: 200 }, urlFormatter: 'hash', values: [ '[input]' ] } ], expectedResult: { hashtype: 'transactionid', txid: random.generateRandomHashString(32, 'fdsghfdnb') }, extraTestInfo: 'Valid transaction hash' }, { methodInput: random.generateRandomHashString(32, '43erwtdgfh'), mockResponseData: [ { response: { data: {hashtype: random.generateRandomHashString(10, '45tergfd')}, statusCode: 200 }, urlFormatter: 'hash', values: [ '[input]' ] } ], expectedError: apiResources.transactionNotFoundMessage, extraTestInfo: 'Non-transaction hash' } ] }, { api: 'sochain', apiBaseAddress: 'https://chain.so', networks: { BTC: {}, DASH: {}, DOGE: {}, LTC: {} }, urlFormatters: { account: '/api/v2/get_address_balance/[0]/[1]', block: '/api/v2/get_block/[0]/[1]', networkInfo: '/api/v2/get_info/[0]', transaction: '/api/v2/get_tx/[0]/[1]' }, getAccountTests: [ { methodInput: random.generateRandomHashString(32, '98uiojk'), mockResponseData: [ { response: { data: {data: {address: random.generateRandomHashString(32, '98uiojk')}}, statusCode: 200 }, urlFormatter: 'account', values: [ '[network]', '[input]' ] } ], expectedResult: {address: random.generateRandomHashString(32, '98uiojk')}, extraTestInfo: 'Valid account address' } ], getBlockByNumberOrHashTests: [ { methodInput: random.generateRandomHashString(32, 'kiujhg'), mockResponseData: [ { response: { data: {data: {hash: random.generateRandomHashString(32, 'kiujhg')}}, statusCode: 200 }, urlFormatter: 'block', values: [ '[network]', '[input]' ] } ], expectedResult: {hash: random.generateRandomHashString(32, 'kiujhg')}, extraTestInfo: 'Valid block id' } ], getNetworkInfoTests: [ { mockResponseData: [ { response: { data: {data: {success: true}}, statusCode: 200 }, urlFormatter: 'networkInfo', values: [ '[network]' ] } ], expectedResult: {success: true}, extraTestInfo: 'Valid network info request' } ], getTransactionTests: [ { methodInput: random.generateRandomHashString(32, '76uyjhg'), mockResponseData: [ { response: { data: {data: {txid: random.generateRandomHashString(32, '76uyjhg')}}, statusCode: 200 }, urlFormatter: 'transaction', values: [ '[network]', '[input]' ] } ], expectedResult: {txid: random.generateRandomHashString(32, '76uyjhg')}, extraTestInfo: 'Valid transaction hash' } ] }, { api: 'wavesexplorer', apiBaseAddress: 'https://nodes.wavesnodes.com', urlFormatters: { account: '/addresses/balance/[0]', blockAt: '/blocks/at/[0]', blockSignature: '/blocks/signature/[0]', networkInfo: '/blocks/last', transaction: '/transactions/info/[0]' }, getAccountTests: [ { methodInput: `1W${random.generateRandomHashString(32, '3regfnvb')}`, mockResponseData: [ { response: { data: {address: random.generateRandomHashString(32, '3regfnvb')}, statusCode: 200 }, urlFormatter: 'account', values: [ random.generateRandomHashString(32, '3regfnvb') ] } ], expectedResult: {address: random.generateRandomHashString(32, '3regfnvb')}, extraTestInfo: `Valid account address with '1W' prefix` }, { methodInput: random.generateRandomHashString(32, '678tuyhgf'), mockResponseData: [ { response: { data: {address: random.generateRandomHashString(32, '678tuyhgf')}, statusCode: 200 }, urlFormatter: 'account', values: [ '[input]' ] } ], expectedResult: {address: random.generateRandomHashString(32, '678tuyhgf')}, extraTestInfo: `Valid account address without '1W' prefix`, useAsErrorTestResponseTemplate: true } ], getBlockByNumberOrHashTests: [ { methodInput: random.generateRandomHashString(32, 'bncfy34'), mockResponseData: [ { response: { data: {hash: random.generateRandomHashString(32, 'bncfy34')}, statusCode: 200 }, urlFormatter: 'blockSignature', values: [ '[input]' ] } ], expectedResult: {hash: random.generateRandomHashString(32, 'bncfy34')}, extraTestInfo: 'Valid block hash' }, { methodInput: random.generateRandomIntInclusive(1, 5000000, '56tryhfg'), mockResponseData: [ { response: { data: {success: f