Merge pull request #115 from christroutner/dh-blockbook-refactor

refactor(Blockbook): Refactored Blockbook route library
This commit is contained in:
Chris Troutner
2020-03-14 11:29:39 -07:00
committed by GitHub
6 changed files with 903 additions and 645 deletions
+120 -72
View File
@@ -855,9 +855,9 @@
}
},
"acorn": {
"version": "7.1.0",
"resolved": "https://registry.npmjs.org/acorn/-/acorn-7.1.0.tgz",
"integrity": "sha512-kL5CuoXA/dgxlBbVrflsflzQ3PAas7RYZB52NOm/6839iVYJgKMJ3cQJD+t2i5+qFa8h3MDpEOJiS64E8JLnSQ==",
"version": "7.1.1",
"resolved": "https://registry.npmjs.org/acorn/-/acorn-7.1.1.tgz",
"integrity": "sha512-add7dgA5ppRPxCFJoAGfMDi7PIBXq1RtGo7BhbLaxwrXPOmw8gq48Y9ozT01hUKy9byMjlR20EJhu5zlkErEkg==",
"dev": true
},
"acorn-jsx": {
@@ -8288,9 +8288,9 @@
"dev": true
},
"npm": {
"version": "6.13.6",
"resolved": "https://registry.npmjs.org/npm/-/npm-6.13.6.tgz",
"integrity": "sha512-NomC08kv7HIl1FOyLOe9Hp89kYsOsvx52huVIJ7i8hFW8Xp65lDwe/8wTIrh9q9SaQhA8hTrfXPh3BEL3TmMpw==",
"version": "6.14.2",
"resolved": "https://registry.npmjs.org/npm/-/npm-6.14.2.tgz",
"integrity": "sha512-eBVjzvGJ9v2/jRJZFtIkvUVKmJ0sCJNNwc9Z1gI6llwaT7EBYWJe5o61Ipc1QR0FaDCKM3l1GizI09Ro3STJEw==",
"dev": true,
"requires": {
"JSONStream": "^1.3.5",
@@ -8299,12 +8299,12 @@
"ansistyles": "~0.1.3",
"aproba": "^2.0.0",
"archy": "~1.0.0",
"bin-links": "^1.1.6",
"bin-links": "^1.1.7",
"bluebird": "^3.5.5",
"byte-size": "^5.0.1",
"cacache": "^12.0.3",
"call-limit": "^1.1.1",
"chownr": "^1.1.3",
"chownr": "^1.1.4",
"ci-info": "^2.0.0",
"cli-columns": "^3.1.2",
"cli-table3": "^0.5.1",
@@ -8321,10 +8321,10 @@
"fs-vacuum": "~1.2.10",
"fs-write-stream-atomic": "~1.0.10",
"gentle-fs": "^2.3.0",
"glob": "^7.1.4",
"glob": "^7.1.6",
"graceful-fs": "^4.2.3",
"has-unicode": "~2.0.1",
"hosted-git-info": "^2.8.5",
"hosted-git-info": "^2.8.8",
"iferr": "^1.0.2",
"imurmurhash": "*",
"infer-owner": "^1.0.4",
@@ -8342,7 +8342,7 @@
"libnpmorg": "^1.0.1",
"libnpmsearch": "^2.0.2",
"libnpmteam": "^1.0.2",
"libnpx": "^10.2.0",
"libnpx": "^10.2.2",
"lock-verify": "^2.1.0",
"lockfile": "^1.0.4",
"lodash._baseindexof": "*",
@@ -8361,7 +8361,7 @@
"mississippi": "^3.0.0",
"mkdirp": "~0.5.1",
"move-concurrently": "^1.0.1",
"node-gyp": "^5.0.5",
"node-gyp": "^5.1.0",
"nopt": "~4.0.1",
"normalize-package-data": "^2.5.0",
"npm-audit-report": "^1.3.2",
@@ -8369,10 +8369,10 @@
"npm-install-checks": "^3.0.2",
"npm-lifecycle": "^3.1.4",
"npm-package-arg": "^6.1.1",
"npm-packlist": "^1.4.7",
"npm-packlist": "^1.4.8",
"npm-pick-manifest": "^3.0.2",
"npm-profile": "^4.0.2",
"npm-registry-fetch": "^4.0.2",
"npm-profile": "^4.0.4",
"npm-registry-fetch": "^4.0.3",
"npm-user-validate": "~1.0.0",
"npmlog": "~4.1.2",
"once": "~1.4.0",
@@ -8389,7 +8389,7 @@
"read-installed": "~4.0.3",
"read-package-json": "^2.1.1",
"read-package-tree": "^5.3.1",
"readable-stream": "^3.4.0",
"readable-stream": "^3.6.0",
"readdir-scoped-modules": "^1.1.0",
"request": "^2.88.0",
"retry": "^0.12.0",
@@ -8581,7 +8581,7 @@
}
},
"bin-links": {
"version": "1.1.6",
"version": "1.1.7",
"bundled": true,
"dev": true,
"requires": {
@@ -8694,7 +8694,7 @@
}
},
"chownr": {
"version": "1.1.3",
"version": "1.1.4",
"bundled": true,
"dev": true
},
@@ -9134,7 +9134,7 @@
}
},
"env-paths": {
"version": "1.0.0",
"version": "2.2.0",
"bundled": true,
"dev": true
},
@@ -9478,7 +9478,7 @@
}
},
"get-caller-file": {
"version": "1.0.2",
"version": "1.0.3",
"bundled": true,
"dev": true
},
@@ -9499,7 +9499,7 @@
}
},
"glob": {
"version": "7.1.4",
"version": "7.1.6",
"bundled": true,
"dev": true,
"requires": {
@@ -9587,7 +9587,7 @@
"dev": true
},
"hosted-git-info": {
"version": "2.8.5",
"version": "2.8.8",
"bundled": true,
"dev": true
},
@@ -9703,7 +9703,7 @@
}
},
"invert-kv": {
"version": "1.0.0",
"version": "2.0.0",
"bundled": true,
"dev": true
},
@@ -9892,11 +9892,11 @@
"dev": true
},
"lcid": {
"version": "1.0.0",
"version": "2.0.0",
"bundled": true,
"dev": true,
"requires": {
"invert-kv": "^1.0.0"
"invert-kv": "^2.0.0"
}
},
"libcipm": {
@@ -10069,7 +10069,7 @@
}
},
"libnpx": {
"version": "10.2.0",
"version": "10.2.2",
"bundled": true,
"dev": true,
"requires": {
@@ -10220,17 +10220,34 @@
"ssri": "^6.0.0"
}
},
"map-age-cleaner": {
"version": "0.1.3",
"bundled": true,
"dev": true,
"requires": {
"p-defer": "^1.0.0"
}
},
"meant": {
"version": "1.0.1",
"bundled": true,
"dev": true
},
"mem": {
"version": "1.1.0",
"version": "4.3.0",
"bundled": true,
"dev": true,
"requires": {
"mimic-fn": "^1.0.0"
"map-age-cleaner": "^0.1.1",
"mimic-fn": "^2.0.0",
"p-is-promise": "^2.0.0"
},
"dependencies": {
"mimic-fn": {
"version": "2.1.0",
"bundled": true,
"dev": true
}
}
},
"mime-db": {
@@ -10246,11 +10263,6 @@
"mime-db": "~1.35.0"
}
},
"mimic-fn": {
"version": "1.2.0",
"bundled": true,
"dev": true
},
"minimatch": {
"version": "3.0.4",
"bundled": true,
@@ -10338,6 +10350,11 @@
"bundled": true,
"dev": true
},
"nice-try": {
"version": "1.0.5",
"bundled": true,
"dev": true
},
"node-fetch-npm": {
"version": "2.0.2",
"bundled": true,
@@ -10349,36 +10366,21 @@
}
},
"node-gyp": {
"version": "5.0.5",
"version": "5.1.0",
"bundled": true,
"dev": true,
"requires": {
"env-paths": "^1.0.0",
"glob": "^7.0.3",
"graceful-fs": "^4.1.2",
"mkdirp": "^0.5.0",
"nopt": "2 || 3",
"npmlog": "0 || 1 || 2 || 3 || 4",
"request": "^2.87.0",
"rimraf": "2",
"semver": "~5.3.0",
"env-paths": "^2.2.0",
"glob": "^7.1.4",
"graceful-fs": "^4.2.2",
"mkdirp": "^0.5.1",
"nopt": "^4.0.1",
"npmlog": "^4.1.2",
"request": "^2.88.0",
"rimraf": "^2.6.3",
"semver": "^5.7.1",
"tar": "^4.4.12",
"which": "1"
},
"dependencies": {
"nopt": {
"version": "3.0.6",
"bundled": true,
"dev": true,
"requires": {
"abbrev": "1"
}
},
"semver": {
"version": "5.3.0",
"bundled": true,
"dev": true
}
"which": "^1.3.1"
}
},
"nopt": {
@@ -10478,12 +10480,13 @@
}
},
"npm-packlist": {
"version": "1.4.7",
"version": "1.4.8",
"bundled": true,
"dev": true,
"requires": {
"ignore-walk": "^3.0.1",
"npm-bundled": "^1.0.1"
"npm-bundled": "^1.0.1",
"npm-normalize-package-bin": "^1.0.1"
}
},
"npm-pick-manifest": {
@@ -10497,7 +10500,7 @@
}
},
"npm-profile": {
"version": "4.0.2",
"version": "4.0.4",
"bundled": true,
"dev": true,
"requires": {
@@ -10507,7 +10510,7 @@
}
},
"npm-registry-fetch": {
"version": "4.0.2",
"version": "4.0.3",
"bundled": true,
"dev": true,
"requires": {
@@ -10599,13 +10602,41 @@
"dev": true
},
"os-locale": {
"version": "2.1.0",
"version": "3.1.0",
"bundled": true,
"dev": true,
"requires": {
"execa": "^0.7.0",
"lcid": "^1.0.0",
"mem": "^1.1.0"
"execa": "^1.0.0",
"lcid": "^2.0.0",
"mem": "^4.0.0"
},
"dependencies": {
"cross-spawn": {
"version": "6.0.5",
"bundled": true,
"dev": true,
"requires": {
"nice-try": "^1.0.4",
"path-key": "^2.0.1",
"semver": "^5.5.0",
"shebang-command": "^1.2.0",
"which": "^1.2.9"
}
},
"execa": {
"version": "1.0.0",
"bundled": true,
"dev": true,
"requires": {
"cross-spawn": "^6.0.0",
"get-stream": "^4.0.0",
"is-stream": "^1.1.0",
"npm-run-path": "^2.0.0",
"p-finally": "^1.0.0",
"signal-exit": "^3.0.0",
"strip-eof": "^1.0.0"
}
}
}
},
"os-tmpdir": {
@@ -10622,11 +10653,21 @@
"os-tmpdir": "^1.0.0"
}
},
"p-defer": {
"version": "1.0.0",
"bundled": true,
"dev": true
},
"p-finally": {
"version": "1.0.0",
"bundled": true,
"dev": true
},
"p-is-promise": {
"version": "2.1.0",
"bundled": true,
"dev": true
},
"p-limit": {
"version": "1.2.0",
"bundled": true,
@@ -10974,7 +11015,7 @@
}
},
"readable-stream": {
"version": "3.4.0",
"version": "3.6.0",
"bundled": true,
"dev": true,
"requires": {
@@ -11358,11 +11399,18 @@
}
},
"string_decoder": {
"version": "1.2.0",
"version": "1.3.0",
"bundled": true,
"dev": true,
"requires": {
"safe-buffer": "~5.1.0"
"safe-buffer": "~5.2.0"
},
"dependencies": {
"safe-buffer": {
"version": "5.2.0",
"bundled": true,
"dev": true
}
}
},
"stringify-package": {
@@ -11743,7 +11791,7 @@
"dev": true
},
"yargs": {
"version": "11.0.0",
"version": "11.1.1",
"bundled": true,
"dev": true,
"requires": {
@@ -11751,7 +11799,7 @@
"decamelize": "^1.1.1",
"find-up": "^2.1.0",
"get-caller-file": "^1.0.1",
"os-locale": "^2.0.0",
"os-locale": "^3.1.0",
"require-directory": "^2.1.1",
"require-main-filename": "^1.0.1",
"set-blocking": "^2.0.0",
+2 -1
View File
@@ -32,7 +32,7 @@ const RawtransactionsV3 = require('./routes/v3/full-node/rawtransactions')
const utilV3 = require('./routes/v3/util')
const slpV3 = require('./routes/v3/slp')
const xpubV3 = require('./routes/v3/xpub')
const blockbookV3 = require('./routes/v3/blockbook')
const BlockbookV3 = require('./routes/v3/blockbook')
const Ninsight = require('./routes/v3/ninsight')
require('dotenv').config()
@@ -42,6 +42,7 @@ const blockchainV3 = new BlockchainV3()
const controlV3 = new ControlV3()
const miningV3 = new MiningV3()
const rawtransactionsV3 = new RawtransactionsV3()
const blockbookV3 = new BlockbookV3()
const app = express()
+475 -495
View File
File diff suppressed because it is too large Load Diff
+4 -7
View File
@@ -6,7 +6,8 @@ const axios = require('axios')
const routeUtils = require('./route-utils')
const wlogger = require('../../util/winston-logging')
const blockbook = require('./blockbook')
const Blockbook = require('./blockbook')
const blockbook = new Blockbook()
const util = require('util')
util.inspect.defaultOptions = { depth: 1 }
@@ -242,9 +243,7 @@ class UtilRoute {
const fromAddr = bchjs.ECPair.toCashAddress(ecPair)
// Get a balance on the public address
const balances = await _this.blockbook.testableComponents.balanceFromBlockbook(
fromAddr
)
const balances = await _this.blockbook.balanceFromBlockbook(fromAddr)
// console.log(`balances: ${JSON.stringify(balances, null, 2)}`)
// Total balance is the sum of the confirmed and unconfirmed balance.
@@ -264,9 +263,7 @@ class UtilRoute {
}
// Get all UTXOs help by the address.
const utxos = await _this.blockbook.testableComponents.utxosFromBlockbook(
fromAddr
)
const utxos = await _this.blockbook.utxosFromBlockbook(fromAddr)
// console.log(`utxos: ${JSON.stringify(utxos, null, 2)}`)
const tokenUtxos = []
+293 -61
View File
@@ -12,20 +12,20 @@
const chai = require('chai')
const assert = chai.assert
const nock = require('nock') // HTTP mocking
let originalUrl, mockServerUrl // Used during transition from integration to unit tests.
// originalUrl = process.env.BLOCKBOOK_URL
const sinon = require('sinon')
let originalUrl // Used during transition from integration to unit tests.
// Set default environment variables for unit tests.
if (!process.env.TEST) process.env.TEST = 'unit'
if (process.env.TEST === 'unit') {
process.env.BLOCKBOOK_URL = 'http://fakeurl/api/'
mockServerUrl = 'http://fakeurl'
}
// Only load blockbook library after setting BLOCKBOOK_URL env var.
const blockbookRoute = require('../../src/routes/v3/blockbook')
const BlockbookRoute = require('../../src/routes/v3/blockbook')
const blockbookRoute = new BlockbookRoute()
// Mocking data.
const { mockReq, mockRes } = require('./mocks/express-mocks')
@@ -37,7 +37,7 @@ util.inspect.defaultOptions = { depth: 1 }
describe('#Blockbook Router', () => {
let req, res
let sandbox
before(() => {
// console.log(`Testing type is: ${process.env.TEST}`)
@@ -55,14 +55,11 @@ describe('#Blockbook Router', () => {
req.body = {}
req.query = {}
// Activate nock if it's inactive.
if (!nock.isActive()) nock.activate()
sandbox = sinon.createSandbox()
})
afterEach(() => {
// Clean up HTTP mocks.
nock.cleanAll() // clear interceptor list.
nock.restore()
sandbox.restore()
})
after(() => {
@@ -71,7 +68,7 @@ describe('#Blockbook Router', () => {
describe('#root', () => {
// root route handler.
const root = blockbookRoute.testableComponents.root
const root = blockbookRoute.root
it('should respond to GET for base route', async () => {
const result = root(req, res)
@@ -82,10 +79,10 @@ describe('#Blockbook Router', () => {
describe('#Balance Single', () => {
// details route handler.
const balanceSingle = blockbookRoute.testableComponents.balanceSingle
// const balanceSingle = blockbookRoute.balanceSingle
it('should throw 400 if address is empty', async () => {
const result = await balanceSingle(req, res)
const result = await blockbookRoute.balanceSingle(req, res)
// console.log(`result: ${util.inspect(result)}`)
assert.hasAllKeys(result, ['error'])
@@ -95,7 +92,7 @@ describe('#Blockbook Router', () => {
it('should error on an array', async () => {
req.params.address = ['qzs02v05l7qs5s24srqju498qu55dwuj0cx5ehjm2c']
const result = await balanceSingle(req, res)
const result = await blockbookRoute.balanceSingle(req, res)
assert.equal(res.statusCode, 400, 'HTTP status code 400 expected.')
assert.include(
@@ -106,9 +103,10 @@ describe('#Blockbook Router', () => {
})
it('should throw an error for an invalid address', async () => {
req.params.address = '02v05l7qs5s24srqju498qu55dwuj0cx5ehjm2c'
req.params.address =
'02v05l7qs5s24srqju498qu55dwuj0cx5ehjm2c'
const result = await balanceSingle(req, res)
const result = await blockbookRoute.balanceSingle(req, res)
assert.equal(res.statusCode, 400, 'HTTP status code 400 expected.')
assert.include(
@@ -119,9 +117,10 @@ describe('#Blockbook Router', () => {
})
it('should detect a network mismatch', async () => {
req.params.address = 'bchtest:qq89kjkeqz9mngp8kl3dpmu43y2wztdjqu500gn4c4'
req.params.address =
'bchtest:qq89kjkeqz9mngp8kl3dpmu43y2wztdjqu500gn4c4'
const result = await balanceSingle(req, res)
const result = await blockbookRoute.balanceSingle(req, res)
assert.equal(res.statusCode, 400, 'HTTP status code 400 expected.')
assert.include(result.error, 'Invalid network', 'Proper error message')
@@ -136,7 +135,7 @@ describe('#Blockbook Router', () => {
// Switch the Insight URL to something that will error out.
process.env.BLOCKBOOK_URL = 'http://fakeurl/api/'
const result = await balanceSingle(req, res)
const result = await blockbookRoute.balanceSingle(req, res)
// Restore the saved URL.
process.env.BLOCKBOOK_URL = savedUrl
@@ -148,21 +147,60 @@ describe('#Blockbook Router', () => {
process.env.BLOCKBOOK_URL = savedUrl
}
})
it('returns proper error when downstream service stalls', async () => {
req.params.address =
'bitcoincash:qrdka2205f4hyukutc2g0s6lykperc8nsu5u2ddpqf'
// Mock the timeout error.
sandbox.stub(blockbookRoute.axios, 'request').throws({
code: 'ECONNABORTED'
})
const result = await blockbookRoute.balanceSingle(req, res)
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.isAbove(res.statusCode, 499, 'HTTP status code 503 expected.')
assert.include(
result.error,
'Could not communicate with full node',
'Error message expected'
)
})
it('returns proper error when downstream service is down', async () => {
req.params.address =
'bitcoincash:qrdka2205f4hyukutc2g0s6lykperc8nsu5u2ddpqf'
// Mock the timeout error.
sandbox.stub(blockbookRoute.axios, 'request').throws({
code: 'ECONNREFUSED'
})
const result = await blockbookRoute.balanceSingle(req, res)
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.isAbove(res.statusCode, 499, 'HTTP status code 503 expected.')
assert.include(
result.error,
'Could not communicate with full node',
'Error message expected'
)
})
it('should get balance for a single address', async () => {
req.params.address = 'bitcoincash:qrdka2205f4hyukutc2g0s6lykperc8nsu5u2ddpqf'
req.params.address =
'bitcoincash:qrdka2205f4hyukutc2g0s6lykperc8nsu5u2ddpqf'
// console.log(`process.env.BLOCKBOOK_URL: ${process.env.BLOCKBOOK_URL}`)
// Mock the Insight URL for unit tests.
if (process.env.TEST === 'unit') {
nock(`${process.env.BLOCKBOOK_URL}`)
.get(uri => uri.includes('/'))
.reply(200, mockData.mockBalance)
sandbox.stub(blockbookRoute.axios, 'request').resolves({
data: mockData.mockBalance
})
}
// Call the details API.
const result = await balanceSingle(req, res)
const result = await blockbookRoute.balanceSingle(req, res)
// console.log(`result: ${util.inspect(result)}`)
assert.hasAnyKeys(result, [
@@ -184,7 +222,7 @@ describe('#Blockbook Router', () => {
describe('#Balance Bulk', () => {
// details route handler.
const balanceBulk = blockbookRoute.testableComponents.balanceBulk
const balanceBulk = blockbookRoute.balanceBulk
it('should throw an error for an empty body', async () => {
req.body = {}
@@ -284,7 +322,44 @@ describe('#Blockbook Router', () => {
process.env.BLOCKBOOK_URL = savedUrl
}
})
it('returns proper error when downstream service stalls', async () => {
req.body = {
addresses: ['bitcoincash:qrdka2205f4hyukutc2g0s6lykperc8nsu5u2ddpqf']
}
// Mock the timeout error.
sandbox.stub(blockbookRoute.axios, 'request').throws({
code: 'ECONNABORTED'
})
const result = await balanceBulk(req, res)
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.isAbove(res.statusCode, 499, 'HTTP status code 503 expected.')
assert.include(
result.error,
'Could not communicate with full node',
'Error message expected'
)
})
it('returns proper error when downstream service is down', async () => {
req.body = {
addresses: ['bitcoincash:qrdka2205f4hyukutc2g0s6lykperc8nsu5u2ddpqf']
}
// Mock the timeout error.
sandbox.stub(blockbookRoute.axios, 'request').throws({
code: 'ECONNREFUSED'
})
const result = await balanceBulk(req, res)
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.isAbove(res.statusCode, 499, 'HTTP status code 503 expected.')
assert.include(
result.error,
'Could not communicate with full node',
'Error message expected'
)
})
it('should get details for a single address', async () => {
req.body = {
addresses: ['bitcoincash:qrdka2205f4hyukutc2g0s6lykperc8nsu5u2ddpqf']
@@ -292,9 +367,9 @@ describe('#Blockbook Router', () => {
// Mock the Insight URL for unit tests.
if (process.env.TEST === 'unit') {
nock(mockServerUrl)
.get(uri => uri.includes('/'))
.reply(200, mockData.mockBalance)
sandbox.stub(blockbookRoute.axios, 'request').resolves({
data: mockData.mockBalance
})
}
// Call the details API.
@@ -328,10 +403,9 @@ describe('#Blockbook Router', () => {
// Mock the Insight URL for unit tests.
if (process.env.TEST === 'unit') {
nock(mockServerUrl)
.get(uri => uri.includes('/'))
.times(2)
.reply(200, mockData.mockBalance)
sandbox.stub(blockbookRoute.axios, 'request').resolves({
data: mockData.mockBalance
})
}
// Call the details API.
@@ -345,7 +419,7 @@ describe('#Blockbook Router', () => {
describe('#UTXOs Single', () => {
// details route handler.
const utxosSingle = blockbookRoute.testableComponents.utxosSingle
const utxosSingle = blockbookRoute.utxosSingle
it('should throw 400 if address is empty', async () => {
const result = await utxosSingle(req, res)
@@ -382,7 +456,8 @@ describe('#Blockbook Router', () => {
})
it('should detect a network mismatch', async () => {
req.params.address = 'bchtest:qq89kjkeqz9mngp8kl3dpmu43y2wztdjqu500gn4c4'
req.params.address =
'bchtest:qq89kjkeqz9mngp8kl3dpmu43y2wztdjqu500gn4c4'
const result = await utxosSingle(req, res)
@@ -411,21 +486,58 @@ describe('#Blockbook Router', () => {
process.env.BLOCKBOOK_URL = savedUrl
}
})
it('returns proper error when downstream service stalls', async () => {
req.params.address =
'bitcoincash:qp3sn6vlwz28ntmf3wmyra7jqttfx7z6zgtkygjhc7'
// Mock the timeout error.
sandbox.stub(blockbookRoute.axios, 'request').throws({
code: 'ECONNABORTED'
})
const result = await utxosSingle(req, res)
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.isAbove(res.statusCode, 499, 'HTTP status code 503 expected.')
assert.include(
result.error,
'Could not communicate with full node',
'Error message expected'
)
})
it('returns proper error when downstream service is down', async () => {
req.params.address =
'bitcoincash:qp3sn6vlwz28ntmf3wmyra7jqttfx7z6zgtkygjhc7'
// Mock the timeout error.
sandbox.stub(blockbookRoute.axios, 'request').throws({
code: 'ECONNREFUSED'
})
const result = await utxosSingle(req, res)
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.isAbove(res.statusCode, 499, 'HTTP status code 503 expected.')
assert.include(
result.error,
'Could not communicate with full node',
'Error message expected'
)
})
it('should get utxos for a single address', async () => {
req.params.address =
'bitcoincash:qp3sn6vlwz28ntmf3wmyra7jqttfx7z6zgtkygjhc7'
// Mock the Insight URL for unit tests.
if (process.env.TEST === 'unit') {
nock(`${process.env.BLOCKBOOK_URL}`)
.get(uri => uri.includes('/'))
.reply(200, mockData.mockUtxos)
sandbox.stub(blockbookRoute.axios, 'request').resolves({
data: mockData.mockUtxos
})
}
// Call the details API.
const result = await utxosSingle(req, res)
// console.log(`result: ${util.inspect(result)}`)
// console.log(`result utxosSingle: ${util.inspect(result)}`)
assert.isArray(result)
assert.hasAnyKeys(result[0], [
@@ -440,7 +552,7 @@ describe('#Blockbook Router', () => {
describe('#UTXO Bulk', () => {
// details route handler.
const utxosBulk = blockbookRoute.testableComponents.utxosBulk
const utxosBulk = blockbookRoute.utxosBulk
it('should throw an error for an empty body', async () => {
req.body = {}
@@ -540,7 +652,46 @@ describe('#Blockbook Router', () => {
process.env.BLOCKBOOK_URL = savedUrl
}
})
it('returns proper error when downstream service stalls', async () => {
req.body = {
addresses: ['bitcoincash:qp3sn6vlwz28ntmf3wmyra7jqttfx7z6zgtkygjhc7']
}
// Mock the timeout error.
sandbox.stub(blockbookRoute.axios, 'request').throws({
code: 'ECONNABORTED'
})
const result = await utxosBulk(req, res)
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.isAbove(res.statusCode, 499, 'HTTP status code 503 expected.')
assert.include(
result.error,
'Could not communicate with full node',
'Error message expected'
)
})
it('returns proper error when downstream service is down', async () => {
req.body = {
addresses: ['bitcoincash:qp3sn6vlwz28ntmf3wmyra7jqttfx7z6zgtkygjhc7']
}
// Mock the timeout error.
sandbox.stub(blockbookRoute.axios, 'request').throws({
code: 'ECONNREFUSED'
})
const result = await utxosBulk(req, res)
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.isAbove(res.statusCode, 499, 'HTTP status code 503 expected.')
assert.include(
result.error,
'Could not communicate with full node',
'Error message expected'
)
})
it('should get details for a single address', async () => {
req.body = {
addresses: ['bitcoincash:qp3sn6vlwz28ntmf3wmyra7jqttfx7z6zgtkygjhc7']
@@ -548,9 +699,9 @@ describe('#Blockbook Router', () => {
// Mock the Insight URL for unit tests.
if (process.env.TEST === 'unit') {
nock(mockServerUrl)
.get(uri => uri.includes('/'))
.reply(200, mockData.mockUtxos)
sandbox.stub(blockbookRoute.axios, 'request').resolves({
data: mockData.mockUtxos
})
}
// Call the details API.
@@ -578,10 +729,9 @@ describe('#Blockbook Router', () => {
// Mock the Insight URL for unit tests.
if (process.env.TEST === 'unit') {
nock(mockServerUrl)
.get(uri => uri.includes('/'))
.times(2)
.reply(200, mockData.mockUtxos)
sandbox.stub(blockbookRoute.axios, 'request').resolves({
data: mockData.mockUtxos
})
}
// Call the details API.
@@ -596,7 +746,7 @@ describe('#Blockbook Router', () => {
describe('#txSingle', () => {
// route handler
const txSingle = blockbookRoute.testableComponents.txSingle
const txSingle = blockbookRoute.txSingle
it('should throw 400 if txid is empty', async () => {
const result = await txSingle(req, res)
@@ -652,15 +802,54 @@ describe('#Blockbook Router', () => {
process.env.BLOCKBOOK_URL = savedUrl
}
})
it('returns proper error when downstream service stalls', async () => {
req.params.txid =
'6181c669614fa18039a19b23eb06806bfece1f7514ab457c3bb82a40fe171a6d'
// Mock the timeout error.
sandbox.stub(blockbookRoute.axios, 'request').throws({
code: 'ECONNABORTED'
})
const result = await txSingle(req, res)
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.isAbove(res.statusCode, 499, 'HTTP status code 503 expected.')
assert.include(
result.error,
'Could not communicate with full node',
'Error message expected'
)
})
it('returns proper error when downstream service is down', async () => {
req.params.txid =
'6181c669614fa18039a19b23eb06806bfece1f7514ab457c3bb82a40fe171a6d'
// Mock the timeout error.
sandbox.stub(blockbookRoute.axios, 'request').throws({
code: 'ECONNREFUSED'
})
const result = await txSingle(req, res)
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.isAbove(res.statusCode, 499, 'HTTP status code 503 expected.')
assert.include(
result.error,
'Could not communicate with full node',
'Error message expected'
)
})
it('should get tx details for a single txid', async () => {
req.params.txid = '6181c669614fa18039a19b23eb06806bfece1f7514ab457c3bb82a40fe171a6d'
req.params.txid =
'6181c669614fa18039a19b23eb06806bfece1f7514ab457c3bb82a40fe171a6d'
// Mock the Insight URL for unit tests.
if (process.env.TEST === 'unit') {
nock(`${process.env.BLOCKBOOK_URL}`)
.get(uri => uri.includes('/'))
.reply(200, mockData.mockTx)
sandbox.stub(blockbookRoute.axios, 'request').resolves({
data: mockData.mockTx
})
}
// process.env.BLOCKBOOK_URL = `https://157.230.178.198:19131/`
@@ -710,7 +899,7 @@ describe('#Blockbook Router', () => {
describe('#txBulk', () => {
// route handler
const txBulk = blockbookRoute.testableComponents.txBulk
const txBulk = blockbookRoute.txBulk
it('should throw an error for an empty body', async () => {
req.body = {}
@@ -727,7 +916,8 @@ describe('#Blockbook Router', () => {
})
it('should error on non-array single address', async () => {
req.body.txids = '6181c669614fa18039a19b23eb06806bfece1f7514ab457c3bb82a40fe171a6d'
req.body.txids =
'6181c669614fa18039a19b23eb06806bfece1f7514ab457c3bb82a40fe171a6d'
const result = await txBulk(req, res)
@@ -801,7 +991,50 @@ describe('#Blockbook Router', () => {
process.env.BLOCKBOOK_URL = savedUrl
}
})
it('returns proper error when downstream service stalls', async () => {
req.body = {
txids: [
'6181c669614fa18039a19b23eb06806bfece1f7514ab457c3bb82a40fe171a6d'
]
}
// Mock the timeout error.
sandbox.stub(blockbookRoute.axios, 'request').throws({
code: 'ECONNABORTED'
})
const result = await txBulk(req, res)
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.isAbove(res.statusCode, 499, 'HTTP status code 503 expected.')
assert.include(
result.error,
'Could not communicate with full node',
'Error message expected'
)
})
it('returns proper error when downstream service is down', async () => {
req.body = {
txids: [
'6181c669614fa18039a19b23eb06806bfece1f7514ab457c3bb82a40fe171a6d'
]
}
// Mock the timeout error.
sandbox.stub(blockbookRoute.axios, 'request').throws({
code: 'ECONNREFUSED'
})
const result = await txBulk(req, res)
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.isAbove(res.statusCode, 499, 'HTTP status code 503 expected.')
assert.include(
result.error,
'Could not communicate with full node',
'Error message expected'
)
})
it('should get details for a single txid', async () => {
req.body = {
txids: [
@@ -811,9 +1044,9 @@ describe('#Blockbook Router', () => {
// Mock the Insight URL for unit tests.
if (process.env.TEST === 'unit') {
nock(mockServerUrl)
.get(uri => uri.includes('/'))
.reply(200, mockData.mockTx)
sandbox.stub(blockbookRoute.axios, 'request').resolves({
data: mockData.mockTx
})
}
// Call the details API.
@@ -868,10 +1101,9 @@ describe('#Blockbook Router', () => {
// Mock the Insight URL for unit tests.
if (process.env.TEST === 'unit') {
nock(mockServerUrl)
.get(uri => uri.includes('/'))
.times(2)
.reply(200, mockData.mockTx)
sandbox.stub(blockbookRoute.axios, 'request').resolves({
data: mockData.mockTx
})
}
// Call the details API.
+9 -9
View File
@@ -357,14 +357,14 @@ describe('#Util', () => {
sandbox
.stub(
utilRouteInst.blockbook.testableComponents,
utilRouteInst.blockbook,
'balanceFromBlockbook'
)
.resolves(mockData.mockBalance)
sandbox
.stub(
utilRouteInst.blockbook.testableComponents,
utilRouteInst.blockbook,
'utxosFromBlockbook'
)
.resolves(mockData.mockUtxos)
@@ -396,7 +396,7 @@ describe('#Util', () => {
if (process.env.TEST === 'unit') {
sandbox
.stub(
utilRouteInst.blockbook.testableComponents,
utilRouteInst.blockbook,
'balanceFromBlockbook'
)
.resolves(mockData.mockBalance)
@@ -424,14 +424,14 @@ describe('#Util', () => {
it('should generate transaction for valid BCH-only sweep', async () => {
sandbox
.stub(
utilRouteInst.blockbook.testableComponents,
utilRouteInst.blockbook,
'balanceFromBlockbook'
)
.resolves(mockData.mockBalance)
sandbox
.stub(
utilRouteInst.blockbook.testableComponents,
utilRouteInst.blockbook,
'utxosFromBlockbook'
)
.resolves(mockData.mockUtxos)
@@ -461,14 +461,14 @@ describe('#Util', () => {
it('should throw 422 error if no non-token UTXOs', async () => {
sandbox
.stub(
utilRouteInst.blockbook.testableComponents,
utilRouteInst.blockbook,
'balanceFromBlockbook'
)
.resolves(mockData.mockBalance)
sandbox
.stub(
utilRouteInst.blockbook.testableComponents,
utilRouteInst.blockbook,
'utxosFromBlockbook'
)
.resolves(mockData.mockUtxos)
@@ -503,14 +503,14 @@ describe('#Util', () => {
it('should detect and throw error for multiple token classes', async () => {
sandbox
.stub(
utilRouteInst.blockbook.testableComponents,
utilRouteInst.blockbook,
'balanceFromBlockbook'
)
.resolves(mockData.mockBalance)
sandbox
.stub(
utilRouteInst.blockbook.testableComponents,
utilRouteInst.blockbook,
'utxosFromBlockbook'
)
.resolves(mockData.mockThreeUtxos)