feat(v3): Forked the v2 code and tests into v3

This commit is contained in:
Chris Troutner
2019-05-31 08:42:57 -07:00
parent 6ed45b07a9
commit 6f615644d6
45 changed files with 12746 additions and 6 deletions
+1373
View File
File diff suppressed because it is too large Load Diff
+610
View File
@@ -0,0 +1,610 @@
"use strict"
const blockRoute = require("../../src/routes/v3/block")
const chai = require("chai")
const assert = chai.assert
const nock = require("nock") // HTTP mocking
let originalEnvVars // Used during transition from integration to unit tests.
// Mocking data.
const { mockReq, mockRes } = require("./mocks/express-mocks")
const mockData = require("./mocks/block-mock")
// Used for debugging.
const util = require("util")
util.inspect.defaultOptions = { depth: 1 }
describe("#Block", () => {
let req, res
before(() => {
// Save existing environment variables.
originalEnvVars = {
BITCOINCOM_BASEURL: process.env.BITCOINCOM_BASEURL,
RPC_BASEURL: process.env.RPC_BASEURL,
RPC_USERNAME: process.env.RPC_USERNAME,
RPC_PASSWORD: process.env.RPC_PASSWORD
}
// Set default environment variables for unit tests.
if (!process.env.TEST) process.env.TEST = "unit"
if (process.env.TEST === "unit") {
process.env.BITCOINCOM_BASEURL = "http://fakeurl/api/"
process.env.RPC_BASEURL = "http://fakeurl/api"
process.env.RPC_USERNAME = "fakeusername"
process.env.RPC_PASSWORD = "fakepassword"
}
})
// Setup the mocks before each test.
beforeEach(() => {
// Mock the req and res objects used by Express routes.
req = mockReq
res = mockRes
// Explicitly reset the parmas and body.
req.params = {}
req.body = {}
// Activate nock if it's inactive.
if (!nock.isActive()) nock.activate()
})
afterEach(() => {
// Clean up HTTP mocks.
nock.cleanAll() // clear interceptor list.
nock.restore()
})
after(() => {
// Restore any pre-existing environment variables.
process.env.BITCOINCOM_BASEURL = originalEnvVars.BITCOINCOM_BASEURL
process.env.RPC_BASEURL = originalEnvVars.RPC_BASEURL
process.env.RPC_USERNAME = originalEnvVars.RPC_USERNAME
process.env.RPC_PASSWORD = originalEnvVars.RPC_PASSWORD
})
describe("#root", () => {
// root route handler.
const root = blockRoute.testableComponents.root
it("should respond to GET for base route", async () => {
const result = root(req, res)
assert.equal(result.status, "block", "Returns static string")
})
})
describe("#detailsByHashSingle", () => {
const detailsByHash = blockRoute.testableComponents.detailsByHashSingle
it("should throw an error for an empty hash", async () => {
req.params.hash = ""
const result = await detailsByHash(req, res)
assert.equal(res.statusCode, 400, "HTTP status code 400 expected.")
assert.include(
result.error,
"hash must not be empty",
"Proper error message"
)
})
it("should throw 50X when network issues", async () => {
// Save the existing RPC URL.
const savedUrl = process.env.BITCOINCOM_BASEURL
// Manipulate the URL to cause a 500 network error.
process.env.BITCOINCOM_BASEURL = "http://fakeurl/api/"
req.params.hash = "abc123"
const result = await detailsByHash(req, res)
//console.log(`result: ${util.inspect(result)}`)
// Restore the saved URL.
process.env.BITCOINCOM_BASEURL = savedUrl
assert.isAbove(res.statusCode, 499, "HTTP status code 50X expected.")
//assert.include(result.error, "ENOTFOUND", "Error message expected")
})
it("should throw an error for invalid hash", async () => {
req.params.hash = "abc123"
// Mock the Insight URL for unit tests.
if (process.env.TEST === "unit") {
nock(`${process.env.BITCOINCOM_BASEURL}`)
.get(`/block/${req.params.hash}`)
.reply(404, "Not found")
}
const result = await detailsByHash(req, res)
//console.log(`result: ${util.inspect(result)}`)
assert.include(result.error, "Not found", "Proper error message")
})
it("should GET /detailsByHash/:hash", async () => {
req.params.hash =
"00000000000000645dec6503d3f5eafb0d2537a7a28f181d721dec7c44154c79"
// Mock the Insight URL for unit tests.
if (process.env.TEST === "unit") {
nock(`${process.env.BITCOINCOM_BASEURL}`)
.get(`/block/${req.params.hash}`)
.reply(200, mockData.mockBlockDetails)
}
const result = await detailsByHash(req, res)
//console.log(`result: ${util.inspect(result)}`)
assert.hasAnyKeys(result, [
"hash",
"size",
"height",
"version",
"merkleroot",
"tx",
"time",
"nonce",
"bits",
"difficulty",
"chainwork",
"confirmations",
"previousblockhash",
"nextblockhash",
"reward",
"isMainChain",
"poolInfo"
])
assert.isArray(result.tx)
})
})
describe("#detailsByHashBulk", () => {
// details route handler.
const detailsByHashBulk = blockRoute.testableComponents.detailsByHashBulk
it("should throw an error for an empty body", async () => {
req.body = {}
const result = await detailsByHashBulk(req, res)
assert.equal(res.statusCode, 400, "HTTP status code 400 expected.")
assert.include(
result.error,
"hashes needs to be an array",
"Proper error message"
)
})
it("should error on non-array single address", async () => {
req.body = {
hashes:
"00000000000000645dec6503d3f5eafb0d2537a7a28f181d721dec7c44154c79"
}
const result = await detailsByHashBulk(req, res)
assert.equal(res.statusCode, 400, "HTTP status code 400 expected.")
assert.include(
result.error,
"hashes needs to be an array",
"Proper error message"
)
})
it("should throw 400 error if addresses array is too large", async () => {
const testArray = []
for (var i = 0; i < 25; i++) testArray.push("")
req.body.hashes = testArray
const result = await detailsByHashBulk(req, res)
//console.log(`result: ${util.inspect(result)}`)
assert.hasAllKeys(result, ["error"])
assert.include(result.error, "Array too large")
})
it("should throw an error for an invalid hash", async () => {
req.body = {
hashes: [`abc123`]
}
const result = await detailsByHashBulk(req, res)
//console.log(`result: ${util.inspect(result)}`)
assert.equal(res.statusCode, 400, "HTTP status code 400 expected.")
assert.include(result.error, "Invalid hash", "Proper error message")
})
it("should throw 500 when network issues", async () => {
const savedUrl = process.env.BITCOINCOM_BASEURL
try {
req.body = {
hashes: [
"00000000000000645dec6503d3f5eafb0d2537a7a28f181d721dec7c44154c79"
]
}
// Switch the Insight URL to something that will error out.
process.env.BITCOINCOM_BASEURL = "http://fakeurl/api/"
const result = await detailsByHashBulk(req, res)
// Restore the saved URL.
process.env.BITCOINCOM_BASEURL = savedUrl
assert.equal(res.statusCode, 500, "HTTP status code 500 expected.")
assert.include(result.error, "ENOTFOUND", "Error message expected")
} catch (err) {
// Restore the saved URL.
process.env.BITCOINCOM_BASEURL = savedUrl
}
})
it("should get details for a single hash", async () => {
req.body = {
hashes: [
"00000000000000645dec6503d3f5eafb0d2537a7a28f181d721dec7c44154c79"
]
}
// Mock the Insight URL for unit tests.
if (process.env.TEST === "unit") {
nock(`${process.env.BITCOINCOM_BASEURL}`)
.get(`/block/${req.body.hashes[0]}`)
.reply(200, mockData.mockBlockDetails)
}
// Call the details API.
const result = await detailsByHashBulk(req, res)
//console.log(`result: ${util.inspect(result)}`)
// Assert that required fields exist in the returned object.
assert.equal(result.length, 1, "Array with one entry")
assert.hasAllKeys(result[0], [
"bits",
"chainwork",
"confirmations",
"difficulty",
"hash",
"height",
"isMainChain",
"merkleroot",
"nextblockhash",
"nonce",
"poolInfo",
"previousblockhash",
"reward",
"size",
"time",
"tx",
"version"
])
})
it("should get details for multiple hashes", async () => {
req.body = {
hashes: [
`00000000000000645dec6503d3f5eafb0d2537a7a28f181d721dec7c44154c79`,
`00000000c2b2c19cf499f57d5b0f724c6df753330d7acc7d4a8ebe412d427bd0`
]
}
// Mock the Insight URL for unit tests.
if (process.env.TEST === "unit") {
nock(`${process.env.BITCOINCOM_BASEURL}`)
.get(`/block/${req.body.hashes[0]}`)
.reply(200, mockData.mockBlockDetails)
nock(`${process.env.BITCOINCOM_BASEURL}`)
.get(`/block/${req.body.hashes[1]}`)
.reply(200, mockData.mockBlockDetails)
}
// Call the details API.
const result = await detailsByHashBulk(req, res)
//console.log(`result: ${util.inspect(result)}`)
assert.isArray(result)
assert.equal(result.length, 2, "2 outputs for 2 inputs")
})
it("should throw an error if hash not found", async () => {
req.body = {
hashes: [
`00000000000000645dec6503d3f5eafb0d2537a7a28f181d721dec7c44abcdef`
]
}
// Mock the Insight URL for unit tests.
if (process.env.TEST === "unit") {
nock(`${process.env.BITCOINCOM_BASEURL}`)
.get(`/block/${req.body.hashes[0]}`)
//.reply(404, { error: { message: "Not Found" } })
.reply(404, "Not found")
}
const result = await detailsByHashBulk(req, res)
//console.log(`result: ${util.inspect(result)}`)
assert.equal(res.statusCode, 404, "HTTP status code 404 expected.")
assert.include(result.error, "Not found", "Proper error message")
})
})
describe("Block Details By Height", () => {
// block route handler.
const detailsByHeight = blockRoute.testableComponents.detailsByHeightSingle
it("should throw an error for an empty height", async () => {
req.params.height = ""
const result = await detailsByHeight(req, res)
assert.equal(res.statusCode, 400, "HTTP status code 400 expected.")
assert.include(
result.error,
"height must not be empty",
"Proper error message"
)
})
it("should throw 500 when network issues", async () => {
// Save the existing RPC URL.
const savedUrl = process.env.BITCOINCOM_BASEURL
const savedUrl2 = process.env.RPC_BASEURL
// Manipulate the URL to cause a 500 network error.
process.env.BITCOINCOM_BASEURL = "http://fakeurl/api/"
process.env.RPC_BASEURL = "http://fakeurl/api/"
req.params.height = "abc123"
const result = await detailsByHeight(req, res)
//console.log(`result: ${util.inspect(result)}`)
// Restore the saved URL.
process.env.BITCOINCOM_BASEURL = savedUrl
process.env.RPC_BASEURL = savedUrl2
assert.isAbove(
res.statusCode,
499,
"HTTP status code 500 or great expected."
)
//assert.include(result.error, "ENOTFOUND", "Error message expected")
})
it("should throw an error for invalid height", async () => {
req.params.height = "abc123"
// Mock the RPC call for unit tests.
if (process.env.TEST === "unit") {
nock(`${process.env.RPC_BASEURL}`)
.post(``)
.reply(500, {
error: {
code: -1,
message: "JSON value is not an integer as expected"
}
})
}
const result = await detailsByHeight(req, res)
//console.log(`result: ${util.inspect(result)}`)
assert.equal(res.statusCode, 400, "HTTP status code 400 expected.")
assert.include(
result.error,
"JSON value is not an integer as expected",
"Proper error message"
)
})
it("should GET /detailsByHeight/:height", async () => {
// Mock the RPC call for unit tests.
if (process.env.TEST === "unit") {
nock(`${process.env.RPC_BASEURL}`)
.post(``)
.reply(200, { result: mockData.mockBlockHash })
}
// Mock the Insight URL for unit tests.
if (process.env.TEST === "unit") {
nock(`${process.env.BITCOINCOM_BASEURL}`)
.get(
`/block/00000000000000645dec6503d3f5eafb0d2537a7a28f181d721dec7c44154c79`
)
.reply(200, mockData.mockBlockDetails)
}
req.params.height = 500000
const result = await detailsByHeight(req, res)
//console.log(`result: ${util.inspect(result)}`)
assert.hasAnyKeys(result, [
"hash",
"size",
"height",
"version",
"merkleroot",
"tx",
"time",
"nonce",
"bits",
"difficulty",
"chainwork",
"confirmations",
"previousblockhash",
"nextblockhash",
"reward",
"isMainChain",
"poolInfo"
])
assert.isArray(result.tx)
})
})
describe("#detailsByHeightBulk", () => {
// details route handler.
const detailsByHeightBulk =
blockRoute.testableComponents.detailsByHeightBulk
it("should throw an error for an empty body", async () => {
req.body = {}
const result = await detailsByHeightBulk(req, res)
assert.equal(res.statusCode, 400, "HTTP status code 400 expected.")
assert.include(
result.error,
"heights needs to be an array",
"Proper error message"
)
})
it("should error on non-array single height", async () => {
req.body = {
heights: 500000
}
const result = await detailsByHeightBulk(req, res)
assert.equal(res.statusCode, 400, "HTTP status code 400 expected.")
assert.include(
result.error,
"heights needs to be an array",
"Proper error message"
)
})
it("should throw 400 error if addresses array is too large", async () => {
const testArray = []
for (var i = 0; i < 25; i++) testArray.push("")
req.body.heights = testArray
const result = await detailsByHeightBulk(req, res)
//console.log(`result: ${util.inspect(result)}`)
assert.hasAllKeys(result, ["error"])
assert.include(result.error, "Array too large")
})
it("should throw error for an invalid height", async () => {
// Mock the RPC call for unit tests.
if (process.env.TEST === "unit") {
nock(`${process.env.RPC_BASEURL}`)
.post(``)
.reply(500, {
error: {
code: -1,
message: "JSON value is not an integer as expected"
}
})
}
req.body.heights = [`abc123`]
const result = await detailsByHeightBulk(req, res)
// console.log(`result: ${util.inspect(result)}`)
assert.equal(res.statusCode, 400, "HTTP status code 400 expected.")
})
it("should throw 500 when network issues", async () => {
const savedUrl = process.env.BITCOINCOM_BASEURL
try {
req.body.heights = [`500000`]
// Switch the Insight URL to something that will error out.
process.env.BITCOINCOM_BASEURL = "http://fakeurl/api/"
const result = await detailsByHeightBulk(req, res)
// Restore the saved URL.
process.env.BITCOINCOM_BASEURL = savedUrl
assert.equal(res.statusCode, 500, "HTTP status code 500 expected.")
assert.include(result.error, "ENOTFOUND", "Error message expected")
} catch (err) {
// Restore the saved URL.
process.env.BITCOINCOM_BASEURL = savedUrl
}
})
it("should get details for a single height", async () => {
req.body.heights = [`500000`]
// Mock the Insight URL for unit tests.
if (process.env.TEST === "unit") {
nock(`${process.env.RPC_BASEURL}`)
.post(``)
.reply(200, { result: mockData.mockBlockHash })
nock(`${process.env.BITCOINCOM_BASEURL}`)
.get(`/block/${mockData.mockBlockHash}`)
.reply(200, mockData.mockBlockDetails)
}
// Call the details API.
const result = await detailsByHeightBulk(req, res)
//console.log(`result: ${util.inspect(result)}`)
// Assert that required fields exist in the returned object.
assert.equal(result.length, 1, "Array with one entry")
assert.hasAllKeys(result[0], [
"bits",
"chainwork",
"confirmations",
"difficulty",
"hash",
"height",
"isMainChain",
"merkleroot",
"nextblockhash",
"nonce",
"poolInfo",
"previousblockhash",
"reward",
"size",
"time",
"tx",
"version"
])
})
it("should get details for multiple block heights", async () => {
req.body = {
heights: [`500000`, `500001`]
}
// Mock the Insight URL for unit tests.
if (process.env.TEST === "unit") {
nock(`${process.env.RPC_BASEURL}`)
.post(``)
.times(2)
.reply(200, { result: mockData.mockBlockHash })
nock(`${process.env.BITCOINCOM_BASEURL}`)
.get(`/block/${mockData.mockBlockHash}`)
.times(2)
.reply(200, mockData.mockBlockDetails)
}
// Call the details API.
const result = await detailsByHeightBulk(req, res)
//console.log(`result: ${util.inspect(result)}`)
assert.isArray(result)
assert.equal(result.length, 2, "2 outputs for 2 inputs")
})
})
})
File diff suppressed because it is too large Load Diff
+140
View File
@@ -0,0 +1,140 @@
/*
TESTS FOR THE CONTROL.JS LIBRARY
This test file uses the environment variable TEST to switch between unit
and integration tests. By default, TEST is set to 'unit'. Set this variable
to 'integration' to run the tests against BCH mainnet.
*/
"use strict"
const chai = require("chai")
const assert = chai.assert
const controlRoute = require("../../src/routes/v3/control")
const nock = require("nock") // HTTP mocking
let originalEnvVars // Used during transition from integration to unit tests.
// Mocking data.
const { mockReq, mockRes } = require("./mocks/express-mocks")
const mockData = require("./mocks/control-mock")
// Used for debugging.
const util = require("util")
util.inspect.defaultOptions = { depth: 1 }
describe("#ControlRouter", () => {
let req, res
before(() => {
// Save existing environment variables.
originalEnvVars = {
BITCOINCOM_BASEURL: process.env.BITCOINCOM_BASEURL,
RPC_BASEURL: process.env.RPC_BASEURL,
RPC_USERNAME: process.env.RPC_USERNAME,
RPC_PASSWORD: process.env.RPC_PASSWORD
}
// Set default environment variables for unit tests.
if (!process.env.TEST) process.env.TEST = "unit"
if (process.env.TEST === "unit") {
process.env.BITCOINCOM_BASEURL = "http://fakeurl/api/"
process.env.RPC_BASEURL = "http://fakeurl/api"
process.env.RPC_USERNAME = "fakeusername"
process.env.RPC_PASSWORD = "fakepassword"
}
})
// Setup the mocks before each test.
beforeEach(() => {
// Mock the req and res objects used by Express routes.
req = mockReq
res = mockRes
// Explicitly reset the parmas and body.
req.params = {}
req.body = {}
req.query = {}
// Activate nock if it's inactive.
if (!nock.isActive()) nock.activate()
})
afterEach(() => {
// Clean up HTTP mocks.
nock.cleanAll() // clear interceptor list.
nock.restore()
})
after(() => {
// Restore any pre-existing environment variables.
process.env.BITCOINCOM_BASEURL = originalEnvVars.BITCOINCOM_BASEURL
process.env.RPC_BASEURL = originalEnvVars.RPC_BASEURL
process.env.RPC_USERNAME = originalEnvVars.RPC_USERNAME
process.env.RPC_PASSWORD = originalEnvVars.RPC_PASSWORD
})
describe("#root", async () => {
// root route handler.
const root = controlRoute.testableComponents.root
it("should respond to GET for base route", async () => {
const result = root(req, res)
//console.log(`result: ${util.inspect(result)}`)
assert.equal(result.status, "control", "Returns static string")
})
})
describe("#GetInfo", () => {
const getInfo = controlRoute.testableComponents.getInfo
it("should throw 500 when network issues", async () => {
// Save the existing RPC URL.
const savedUrl = process.env.RPC_BASEURL
// Manipulate the URL to cause a 500 network error.
process.env.RPC_BASEURL = "http://fakeurl/api/"
const result = await getInfo(req, res)
//console.log(`result: ${util.inspect(result)}`)
// Restore the saved URL.
process.env.RPC_BASEURL = savedUrl
assert.isAbove(
res.statusCode,
499,
"HTTP status code 500 or greater expected."
)
//assert.include(result.error, "ENOTFOUND", "Error message expected")
})
it("should get info on the full node", async () => {
// Mock the RPC call for unit tests.
if (process.env.TEST === "unit") {
nock(`${process.env.RPC_BASEURL}`)
.post(``)
.reply(200, { result: mockData.mockGetInfo })
}
const result = await getInfo(req, res)
//console.log(`result: ${util.inspect(result)}`)
assert.hasAnyKeys(result, [
"version",
"protocolversion",
"blocks",
"timeoffset",
"connections",
"proxy",
"difficulty",
"testnet",
"paytxfee",
"relayfee",
"errors"
])
})
})
})
+60
View File
@@ -0,0 +1,60 @@
/*
End-to-end tests for the Address endpoint.
These tests assume that the repo is running locally and pointed at TESTNET
*/
"use strict"
const rp = require("request-promise")
const rawtransactions = require("../../../dist/routes/v2/address")
/*
This unconfirmed utxo test needs to be expanded in the future to automatically
create a transaction. For now, that is done manually and the address provided
as a constant.
1. Start rest running locally, pointed at TESTNET.
2. Fund each address with a small amount of tBCH
3. Run this program with `node address.js`
4. If successful, it will return the unconfirmed UTXOs.
*/
const addr1 = "bchtest:qpdcp5pv7qphu5tsjfgwezld9n9aq9ue6swgqpf45c"
const addr2 = "bchtest:qzpvx999fagau0xqmvu3xvll9evapge7u5hcgeknzv"
async function testSingleUnconfirmed() {
try {
const options = {
method: "GET",
uri: `http://localhost:3000/v2/address/unconfirmed/${addr1}`,
resolveWithFullResponse: true,
json: true
}
const result = await rp(options)
// console.log(`result.body: ${JSON.stringify(result.body, null, 2)}`)
} catch (err) {
// console.log(`Error: `, err)
}
}
testSingleUnconfirmed()
async function testDoubleUnconfirmed() {
try {
const options = {
method: "POST",
uri: `http://localhost:3000/v2/address/unconfirmed`,
resolveWithFullResponse: true,
json: true,
body: {
addresses: [addr1, addr2]
}
}
const result = await rp(options)
// console.log(`result.body: ${JSON.stringify(result.body, null, 2)}`)
} catch (err) {
// console.log(`Error: `, err)
}
}
testDoubleUnconfirmed()
+29
View File
@@ -0,0 +1,29 @@
"use strict"
/**
* Read more about panda here: https://panda-suite.github.io/
*/
const panda = require("pandacash-core")
const runLocalNode = done => {
const server = panda.server({
// always the same mnemonic
// mnemonic: "cigar magnet ocean purchase travel damp snack alone theme budget wagon wrong",
seedAccounts: true,
enableLogs: false,
debug: false
})
server.listen({
port: 48332,
walletPort: 48333,
}, (err, pandaCashCore) => {
if (err) return console.error(err)
done()
})
}
module.exports = {
runLocalNode
}
+7
View File
@@ -0,0 +1,7 @@
This directory contains integration tests. To run them requires that
a local copy of rest.bitcoin.com is running. The tests in this directory are
exactly the same calls that end users would use for their app to interact
directly with rest.bitcoin.com.
At present, this directory focuses on raw-transations. But more integration tests
will be added.
+94
View File
@@ -0,0 +1,94 @@
/*
*/
"use strict"
const chai = require("chai")
const assert = chai.assert
const axios = require("axios")
// Used for debugging.
const util = require("util")
util.inspect.defaultOptions = { depth: 1 }
const SERVER = `https://rest.btctest.net/v2/`
//const SERVER = `http://localhost:3000/v2/`
describe("#rate limits", () => {
it("should get control/getInfo() with no auth", async () => {
const options = {
method: "GET",
url: `${SERVER}control/getInfo`
}
const result = await axios(options)
//console.log(`result.status: ${result.status}`)
//console.log(`result.data: ${util.inspect(result.data)}`)
assert.equal(result.status, 200)
assert.hasAnyKeys(result.data, ["version"])
})
it("should trigger rate-limit handler if rate limits exceeds 60 request per minute", async () => {
try {
// Actual rate limit is 60 per minute X 4 nodes = 240 rpm.
const options = {
method: "GET",
url: `${SERVER}control/getInfo`
}
const promises = []
for (let i = 0; i < 250; i++) {
const promise = axios(options)
promises.push(promise)
}
await Promise.all(promises)
assert.equal(true, false, "Unexpected result!")
} catch (err) {
//console.log(`err.response: ${util.inspect(err.response)}`)
assert.equal(err.response.status, 429)
assert.include(err.response.data.error, "Too many requests")
}
})
it("should not trigger rate-limit handler if correct pro-tier password is used", async () => {
try {
const username = "BITBOX"
// Pro-tier is accessed by using the right password.
const password = "BITBOX"
//const password = "something"
const combined = `${username}:${password}`
const base64Credential = Buffer.from(combined).toString("base64")
const readyCredential = `Basic ${base64Credential}`
const options = {
method: "GET",
url: `${SERVER}control/getInfo`,
headers: { Authorization: readyCredential }
}
const promises = []
for (let i = 0; i < 250; i++) {
const promise = axios(options)
promises.push(promise)
}
await Promise.all(promises)
assert.equal(true, true, "Not throwing an error is a pass!")
} catch (err) {
// console.log(`err.response: ${util.inspect(err.response)}`)
assert.equal(
true,
false,
"This error handler should not have been triggered. Is the password correct?"
)
}
})
})
+191
View File
@@ -0,0 +1,191 @@
/*
These integration tests are intended to be run against a live local copy of
rest.bitcoin.com. They exercise the endpoints in the same way the SDK or
end-user application would. These tests were created to replace the parts
removed from the swagger UI, that otherwise would have excersiced these endpoints.
TODO:
-/rawtransactions/sendRawTransaction is more appropropriate for an e2e test,
so it is omitted here.
*/
"use strict"
const chai = require("chai")
const assert = chai.assert
const rawtransactions = require("../../../dist/routes/v2/rawtransactions")
const rp = require("request-promise")
// Used for debugging.
const util = require("util")
util.inspect.defaultOptions = { depth: 1 }
const mockData = require("../mocks/raw-transactions-mocks")
describe("#Raw-Transactions", () => {
describe("#root", () => {
it(`should return root`, async () => {
const options = {
method: "GET",
uri: `http://localhost:3000/v2/rawtransactions/`,
resolveWithFullResponse: true,
json: true
}
const result = await rp(options)
//console.log(`result: ${JSON.stringify(result, null, 0)}`)
assert.equal(result.body.status, "rawtransactions")
})
})
describe("#whCreateTx", () => {
it(`should return tx hex`, async () => {
const minIn = {
txid:
"f7ed9cf23dee85910f6269c9a101a75fcfd2f3c6fc81f17fad824ff7aaf99ab2",
vout: 1
}
const options = {
method: "PUT",
uri: `http://localhost:3000/v2/rawtransactions/create`,
resolveWithFullResponse: true,
json: true,
body: {
//inputs: [mockData.mockWHCreateInput],
inputs: [minIn],
outputs: {}
}
}
const result = await rp(options)
//console.log(`result.body: ${JSON.stringify(result.body, null, 0)}`)
assert.equal(
result.body,
"0200000001b29af9aaf74f82ad7ff181fcc6f3d2cf5fa701a1c969620f9185ee3df29cedf70100000000ffffffff0000000000"
)
})
})
describe("#whOpReturn", () => {
it(`should return tx hex`, async () => {
const options = {
method: "PUT",
uri: `http://localhost:3000/v2/rawtransactions/opreturn`,
resolveWithFullResponse: true,
json: true,
body: {
rawtx: "01000000000000000000",
payload: "00000000000000020000000006dac2c0"
}
}
const result = await rp(options)
//console.log(`result.body: ${JSON.stringify(result.body, null, 0)}`)
assert.equal(
result.body,
"0100000000010000000000000000166a140877686300000000000000020000000006dac2c000000000"
)
})
})
describe("#whReference", () => {
it(`should return tx hex`, async () => {
const options = {
method: "PUT",
uri: `http://localhost:3000/v2/rawtransactions/reference`,
resolveWithFullResponse: true,
json: true,
body: {
rawtx:
"0100000001a7a9402ecd77f3c9f745793c9ec805bfa2e14b89877581c734c774864247e6f50400000000ffffffff03aa0a0000000000001976a9146d18edfe073d53f84dd491dae1379f8fb0dfe5d488ac5c0d0000000000004751210252ce4bdd3ce38b4ebbc5a6e1343608230da508ff12d23d85b58c964204c4cef3210294cc195fc096f87d0f813a337ae7e5f961b1c8a18f1f8604a909b3a5121f065b52aeaa0a0000000000001976a914946cb2e08075bcbaf157e47bcb67eb2b2339d24288ac00000000",
destination: "bitcoincash:qrn60nerx5zug4u4hal06atep3lzhtecvy4pxk75lf",
amount: 0.005
}
}
const result = await rp(options)
//console.log(`result.body: ${JSON.stringify(result.body, null, 0)}`)
assert.isString(result.body)
})
})
describe("#whChangeOutput", () => {
it(`should return tx hex`, async () => {
const options = {
method: "PUT",
uri: `http://localhost:3000/v2/rawtransactions/change`,
resolveWithFullResponse: true,
json: true,
body: {
rawtx:
"0100000001b15ee60431ef57ec682790dec5a3c0d83a0c360633ea8308fbf6d5fc10a779670400000000ffffffff025c0d00000000000047512102f3e471222bb57a7d416c82bf81c627bfcd2bdc47f36e763ae69935bba4601ece21021580b888ff56feb27f17f08802ebed26258c23697d6a462d43fc13b565fda2dd52aeaa0a0000000000001976a914946cb2e08075bcbaf157e47bcb67eb2b2339d24288ac00000000",
destination: "bitcoincash:qrn60nerx5zug4u4hal06atep3lzhtecvy4pxk75lf",
prevtxs: [
{
txid:
"6779a710fcd5f6fb0883ea3306360c3ad8c0a3c5de902768ec57ef3104e65eb1",
vout: 4,
scriptPubKey:
"76a9147b25205fd98d462880a3e5b0541235831ae959e588ac",
value: 0.00068257
}
],
fee: 0.000035
}
}
const result = await rp(options)
//console.log(`result.body: ${JSON.stringify(result.body, null, 0)}`)
assert.isString(result.body)
})
})
describe("#whInput", () => {
it(`should return tx hex`, async () => {
const options = {
method: "PUT",
uri: `http://localhost:3000/v2/rawtransactions/input`,
resolveWithFullResponse: true,
json: true,
body: {
txid:
"b006729017df05eda586df9ad3f8ccfee5be340aadf88155b784d1fc0e8342ee",
n: 0
}
}
const result = await rp(options)
//console.log(`result.body: ${JSON.stringify(result.body, null, 0)}`)
assert.isString(result.body)
})
})
describe("#getRawTransaction", () => {
it(`should return tx hex`, async () => {
const options = {
method: "POST",
uri: `http://localhost:3000/v2/rawtransactions/getRawTransaction`,
resolveWithFullResponse: true,
json: true,
body: {
txids: [
"0e3e2357e806b6cdb1f70b54c3a3a17b6714ee1f0e68bebb44a74b1efd512098"
],
verbose: true
}
}
const result = await rp(options)
// console.log(`result.body: ${util.inspect(result.body)}`)
assert.isArray(result.body)
})
})
})
+176
View File
@@ -0,0 +1,176 @@
/*
TESTS FOR THE MINING.TS LIBRARY
This test file uses the environment variable TEST to switch between unit
and integration tests. By default, TEST is set to 'unit'. Set this variable
to 'integration' to run the tests against BCH mainnet.
*/
"use strict"
const chai = require("chai")
const assert = chai.assert
const miningRoute = require("../../src/routes/v3/mining")
const nock = require("nock") // HTTP mocking
let originalEnvVars // Used during transition from integration to unit tests.
// Mocking data.
const { mockReq, mockRes } = require("./mocks/express-mocks")
const mockData = require("./mocks/mining-mocks")
// Used for debugging.
const util = require("util")
util.inspect.defaultOptions = { depth: 1 }
describe("#Mining", () => {
let req, res
before(() => {
// Save existing environment variables.
originalEnvVars = {
BITCOINCOM_BASEURL: process.env.BITCOINCOM_BASEURL,
RPC_BASEURL: process.env.RPC_BASEURL,
RPC_USERNAME: process.env.RPC_USERNAME,
RPC_PASSWORD: process.env.RPC_PASSWORD
}
// Set default environment variables for unit tests.
if (!process.env.TEST) process.env.TEST = "unit"
if (process.env.TEST === "unit") {
process.env.BITCOINCOM_BASEURL = "http://fakeurl/api/"
process.env.RPC_BASEURL = "http://fakeurl/api"
process.env.RPC_USERNAME = "fakeusername"
process.env.RPC_PASSWORD = "fakepassword"
}
})
// Setup the mocks before each test.
beforeEach(() => {
// Mock the req and res objects used by Express routes.
req = mockReq
res = mockRes
// Explicitly reset the parmas and body.
req.params = {}
req.body = {}
req.query = {}
// Activate nock if it's inactive.
if (!nock.isActive()) nock.activate()
})
afterEach(() => {
// Clean up HTTP mocks.
nock.cleanAll() // clear interceptor list.
nock.restore()
})
after(() => {
// Restore any pre-existing environment variables.
process.env.BITCOINCOM_BASEURL = originalEnvVars.BITCOINCOM_BASEURL
process.env.RPC_BASEURL = originalEnvVars.RPC_BASEURL
process.env.RPC_USERNAME = originalEnvVars.RPC_USERNAME
process.env.RPC_PASSWORD = originalEnvVars.RPC_PASSWORD
})
describe("#root", async () => {
// root route handler.
const root = miningRoute.testableComponents.root
it("should respond to GET for base route", async () => {
const result = root(req, res)
//console.log(`result: ${util.inspect(result)}`)
assert.equal(result.status, "mining", "Returns static string")
})
})
describe("#getMiningInfo", async () => {
const getMiningInfo = miningRoute.testableComponents.getMiningInfo
it("should throw 503 when network issues", async () => {
// Save the existing RPC URL.
const savedUrl2 = process.env.RPC_BASEURL
// Manipulate the URL to cause a 500 network error.
process.env.RPC_BASEURL = "http://fakeurl/api/"
const result = await getMiningInfo(req, res)
//console.log(`result: ${util.inspect(result)}`)
// Restore the saved URL.
process.env.RPC_BASEURL = savedUrl2
assert.isAbove(
res.statusCode,
499,
"HTTP status code 500 or greater expected."
)
//assert.include(result.error,"Network error: Could not communicate with full node","Error message expected")
})
it("should GET mining information", async () => {
// Mock the RPC call for unit tests.
if (process.env.TEST === "unit") {
nock(`${process.env.RPC_BASEURL}`)
.post(``)
.reply(200, { result: mockData.mockMiningInfo })
}
const result = await getMiningInfo(req, res)
//console.log(`result: ${util.inspect(result)}`)
assert.hasAllKeys(result, [
"blocks",
"currentblocksize",
"currentblocktx",
"difficulty",
"blockprioritypercentage",
"errors",
"networkhashps",
"pooledtx",
"chain"
])
})
})
describe("#getNetworkHashPS", async () => {
const getNetworkHashPS = miningRoute.testableComponents.getNetworkHashPS
it("should throw 503 when network issues", async () => {
// Save the existing RPC URL.
const savedUrl2 = process.env.RPC_BASEURL
// Manipulate the URL to cause a 500 network error.
process.env.RPC_BASEURL = "http://fakeurl/api/"
const result = await getNetworkHashPS(req, res)
//console.log(`result: ${util.inspect(result)}`)
// Restore the saved URL.
process.env.RPC_BASEURL = savedUrl2
assert.isAbove(
res.statusCode,
499,
"HTTP status code 500 or greater expected."
)
//assert.include(result.error,"Network error: Could not communicate with full node","Error message expected")
})
it("should GET Network Hash per second", async () => {
// Mock the RPC call for unit tests.
if (process.env.TEST === "unit") {
nock(`${process.env.RPC_BASEURL}`)
.post(``)
.reply(200, { result: 517604755.6648782 })
}
const result = await getNetworkHashPS(req, res)
//console.log(`result: ${util.inspect(result)}`)
assert.isNumber(result)
})
})
})
+160
View File
@@ -0,0 +1,160 @@
/*
This library contains mocking data for running unit tests on the address route.
*/
"use strict"
const mockAddressDetails = {
addrStr: "1Fg4r9iDrEkCcDmHTy2T79EusNfhyQpu7W",
balance: 0.00126419,
balanceSat: 126419,
totalReceived: 0.02175868,
totalReceivedSat: 2175868,
totalSent: 0.02049449,
totalSentSat: 2049449,
unconfirmedBalance: 0,
unconfirmedBalanceSat: 0,
unconfirmedTxApperances: 0,
txApperances: 3,
transactions: [
"2dc053f55a666a3d2a08b1c680b704d62a55506d14ad884add87edcc56b9277d",
"544c15ce35c0f2e808d28f29d6587f1ec9276233e29856b7f2938cf0daef0026",
"81039b1d7b855b133f359f9dc65f776bd105650153a941675fedc504228ddbd3"
],
legacyAddress: "1Fg4r9iDrEkCcDmHTy2T79EusNfhyQpu7W",
cashAddress: "bitcoincash:qzs02v05l7qs5s24srqju498qu55dwuj0cx5ehjm2c"
}
const mockUtxoDetails = [
{
txid: "15f6a584080b04911121fbaca7bfcf3dd64ef2bfa5a01daf31e05a296c3e5e9e",
vout: 286,
amount: 0.00001,
satoshis: 1000,
height: 546083,
confirmations: 3490
},
{
txid: "15f6a584080b04911121fbaca7bfcf3dd64ef2bfa5a01daf31e05a296c3e5e9e",
vout: 287,
amount: 0.00001,
satoshis: 1000,
height: 546083,
confirmations: 3490
},
{
txid: "15f6a584080b04911121fbaca7bfcf3dd64ef2bfa5a01daf31e05a296c3e5e9e",
vout: 288,
amount: 0.00001,
satoshis: 1000,
height: 546083,
confirmations: 3490
}
]
const mockUnconfirmed = [
{
address: "1EzdL6TBbkNhnB2fYiBaKmcs5fxaoqwdAp",
txid: "000c00a90fb5031da6e02f7625df2f8b35a4c16e6feb9fc72293e67e5ff75786",
vout: 0,
scriptPubKey: "76a914997fabcd94a1e2aaa13a7664362e5e7b96c169a988ac",
amount: 0.00999626,
satoshis: 999626,
confirmations: 0,
ts: 1537989425
}
]
const mockTransactions = {
pagesTotal: 1,
txs: [
{
txid: "000c00a90fb5031da6e02f7625df2f8b35a4c16e6feb9fc72293e67e5ff75786",
version: 2,
locktime: 549565,
vin: [
{
txid:
"45c891c6d44619fc85716ba1b593aa83ebc1e500fe611b1ab98531ea203a0f21",
vout: 209,
sequence: 4294967294,
n: 0,
scriptSig: {
hex:
"47304402205b136f348bedae61a87c0500979d47ab02c76f0e4aba866b94c4931fccb5d7dc0220757a141660475b0cd4cb8b1f54b1ace95105f5bea775e817b115fb589fe4477541210246daec651c506f353daa7468714399c773df42ac4d663022d0e446a3331ac64a",
asm:
"304402205b136f348bedae61a87c0500979d47ab02c76f0e4aba866b94c4931fccb5d7dc0220757a141660475b0cd4cb8b1f54b1ace95105f5bea775e817b115fb589fe44775[ALL|FORKID] 0246daec651c506f353daa7468714399c773df42ac4d663022d0e446a3331ac64a"
},
addr: "14TdovsQUL69xL5g3zSzhLpmDb93d9rU9m",
valueSat: 1979534,
value: 0.01979534,
doubleSpentTxID: null
},
{
txid:
"6e5fa79547112a912adf6082c975dc80e282045193512ea369adb6bac7420674",
vout: 147,
sequence: 4294967294,
n: 1,
scriptSig: {
hex:
"473044022056674ac83a37d54ffec2c6d33cfc2ae2256f821b8bd818e1b6783356b49f3d28022046d3dd147c8fc51f2da5d16996a3cea1d7314e327aa1ae1017418737ed20136a4121038f23af565f68d8455e07e5c33c5cfc5114de2f478f035db23eb02f8ff5fe7f6e",
asm:
"3044022056674ac83a37d54ffec2c6d33cfc2ae2256f821b8bd818e1b6783356b49f3d28022046d3dd147c8fc51f2da5d16996a3cea1d7314e327aa1ae1017418737ed20136a[ALL|FORKID] 038f23af565f68d8455e07e5c33c5cfc5114de2f478f035db23eb02f8ff5fe7f6e"
},
addr: "1CEtC2fjELvmAzTd6MWuHb1gvuSq8x3Xpb",
valueSat: 10466,
value: 0.00010466,
doubleSpentTxID: null
}
],
vout: [
{
value: "0.00999626",
n: 0,
scriptPubKey: {
hex: "76a914997fabcd94a1e2aaa13a7664362e5e7b96c169a988ac",
asm:
"OP_DUP OP_HASH160 997fabcd94a1e2aaa13a7664362e5e7b96c169a9 OP_EQUALVERIFY OP_CHECKSIG",
addresses: ["1EzdL6TBbkNhnB2fYiBaKmcs5fxaoqwdAp"],
type: "pubkeyhash"
},
spentTxId: null,
spentIndex: null,
spentHeight: null
},
{
value: "0.00990000",
n: 1,
scriptPubKey: {
hex: "76a914e3335e4d6babe61ea58311293c1c6bfe802801cb88ac",
asm:
"OP_DUP OP_HASH160 e3335e4d6babe61ea58311293c1c6bfe802801cb OP_EQUALVERIFY OP_CHECKSIG",
addresses: ["1MiKvgaQTFCTEZbSDMZgw9ahaB52Cr4ofb"],
type: "pubkeyhash"
},
spentTxId: null,
spentIndex: null,
spentHeight: null
}
],
blockhash:
"00000000000000000158b1a2883688873ec5ea076e3b0f576bcdfe1fd277880f",
blockheight: 549601,
confirmations: 5,
time: 1537990026,
blocktime: 1537990026,
valueOut: 0.01989626,
size: 372,
valueIn: 0.0199,
fees: 0.00000374
}
]
}
module.exports = {
mockAddressDetails,
mockUtxoDetails,
mockUnconfirmed,
mockTransactions
}
+42
View File
@@ -0,0 +1,42 @@
/*
This library contains mocking data for running unit tests on the address route.
*/
"use strict"
const mockBlockDetails = {
hash: "00000000000000645dec6503d3f5eafb0d2537a7a28f181d721dec7c44154c79",
size: 1319,
height: 1267544,
version: 536870912,
merkleroot:
"c2cd01ec2cf149acc7631385f89ae103d1a2ad212ab810c862d9680a811618ce",
tx: [
"52bfa89d449fef8070ec33e7a61f5ec8b5417ff62b050cd6a144ebce9557e0fa",
"8988e8742d2523f667c2d1374861919e3a82903e059fec75c10795da2303b93b",
"41def004f22b196d675566a6983ecf97611e72c48375b8ba779983d5ccd369d7",
"8ca8efb06abb94395f8331f87e101504dcf66aee828f2111922f056971fa7502",
"9ab409cbb203be7cf22bd6598d787fcdd5b417d885b72207ecc7253470770649"
],
time: 1542048973,
nonce: 3936284753,
bits: "1a03c148",
difficulty: 4467892.99177529,
chainwork: "00000000000000000000000000000000000000000000003ee27b503a064045bd",
confirmations: 3,
previousblockhash:
"00000000000001891202cbe18729a02a8763cf04da0ca7aded6e6c7d9b500785",
nextblockhash:
"000000000000039b93b3f3403a0eb21150904b15229d998cf3788fd8bf192cc2",
reward: 0.78125,
isMainChain: true,
poolInfo: {}
}
const mockBlockHash =
"00000000000000645dec6503d3f5eafb0d2537a7a28f181d721dec7c44154c79"
module.exports = {
mockBlockDetails,
mockBlockHash
}
+271
View File
@@ -0,0 +1,271 @@
/*
This library contains mocking data for running unit tests on the address route.
*/
"use strict"
const mockBlockHash =
"00000000000000645dec6503d3f5eafb0d2537a7a28f181d721dec7c44154c79"
const mockBlockchainInfo = {
chain: "test",
blocks: 1267694,
headers: 1267694,
bestblockhash:
"000000000000013eaf80d1e157e32804c36a58ad0bb26ca59833880c80298780",
difficulty: 4105763.969035785,
mediantime: 1542131305,
verificationprogress: 0.9999968911571566,
chainwork: "00000000000000000000000000000000000000000000003f0443b0b5f02ce255",
pruned: false,
softforks: [
{ id: "bip34", version: 2, reject: { status: true } },
{ id: "bip66", version: 3, reject: { status: true } },
{ id: "bip65", version: 4, reject: { status: true } }
],
bip9_softforks: {
csv: {
status: "active",
startTime: 1456790400,
timeout: 1493596800,
since: 770112
}
}
}
const mockChainTips = [
{
height: 1267696,
hash: "000000000000035bfc43a642ebbff8cfc1e88b1d564de8b0a6c7e2797eeafb21",
branchlen: 0,
status: "active"
},
{
height: 1267581,
hash: "000000000001bd12e4124207682563135b21353ca3087bc6b0c409f7f9e1da91",
branchlen: 1,
status: "valid-fork"
},
{
height: 1267375,
hash: "00000000702036979df70236bcc45dfc72d43d5d0e6834007afa1fa627e49587",
branchlen: 1036,
status: "headers-only"
},
{
height: 1266979,
hash: "00000000e851abbde2174ccdc5c2508b909f81f23c4b7abeac864eee1a4891c7",
branchlen: 1,
status: "valid-fork"
},
{
height: 1266973,
hash: "000000007e1324d2900ed70947f89ab8fd4a267e87c9244c333215659a7370d5",
branchlen: 1,
status: "valid-fork"
},
{
height: 1266967,
hash: "000000000f37f12843d4800f50ec4de44dce0432e4d448242d43ff04a7a2948d",
branchlen: 1,
status: "valid-fork"
},
{
height: 1266963,
hash: "00000000ae11ec99e88898cbd72567cb4cb79e7fc13243357bef79632769deb4",
branchlen: 1,
status: "valid-fork"
},
{
height: 1266962,
hash: "000000004f97851259b4460a049e44fa5bd4beadee04a85bf81bd2e90c4f24f9",
branchlen: 1,
status: "valid-fork"
},
{
height: 1266950,
hash: "00000000d12a0e4a827f73ecaadb563df7e99817294939f2be8e943079dc60b2",
branchlen: 1,
status: "valid-fork"
},
{
height: 1266944,
hash: "00000000612d16af273217818e8e7ac5014a19f68ed04e1bc897b1be0a9c744f",
branchlen: 1,
status: "valid-fork"
},
{
height: 1266941,
hash: "0000000000002b91cdb15cacf3368da2c9ffce511d64092193b507dad75f4a27",
branchlen: 1,
status: "valid-fork"
},
{
height: 1266896,
hash: "000000000002745f67a66617b1f9f65c2dfb4c4b8a9ed9b1320b98d3ca46cbe5",
branchlen: 1,
status: "valid-fork"
},
{
height: 1266481,
hash: "0000000000036babee3ba7654cddb29a9fe5e85c9fbeb44bb53d5ffb694b9670",
branchlen: 1,
status: "valid-fork"
},
{
height: 1266452,
hash: "00000000502c3ab1490e0839780a4b441bbe21507c454545941e6adcde73c2ff",
branchlen: 1,
status: "valid-fork"
},
{
height: 1266336,
hash: "000000001c9a0b7cd9eb2210f69d5a9a3f4546ba3707deb40993d729190582d7",
branchlen: 10,
status: "headers-only"
},
{
height: 1266097,
hash: "000000000028ac0fbd18afe6f001f70cb7549bd2b3082d754dde88ee236368a2",
branchlen: 1,
status: "valid-fork"
},
{
height: 1265522,
hash: "00000000000000c6df7a2063030e6ce51399e470cc46e4bf6d67aa3f0feca98d",
branchlen: 1,
status: "valid-fork"
},
{
height: 1265470,
hash: "0000000000168e6442a595a02340fccf8dc2b0e8912c632d39e1f870685f6d7c",
branchlen: 2,
status: "valid-fork"
},
{
height: 1265275,
hash: "000000008bc2b37ae6af1b886b52e7e1c1122c12badc4ae6c13df64f789267cf",
branchlen: 11027,
status: "headers-only"
},
{
height: 1265257,
hash: "00000000005bc3e9973d7273211eab02ae02549c7ebae48764c0bd648d4d7bbe",
branchlen: 114,
status: "valid-fork"
},
{
height: 1265254,
hash: "0000000000e5b909d3541857315e9ee103b042d9ba661f25cc3e8fb25a96c8ce",
branchlen: 111,
status: "valid-fork"
},
{
height: 1265245,
hash: "000000000066eb8832f6f990baceb379536f437216e22475a6c1b9133d250cb8",
branchlen: 102,
status: "valid-fork"
},
{
height: 1264455,
hash: "00000000000001befc1bb17a23208d0b77b2449e37093ce8fbb5e346b19cafcc",
branchlen: 1,
status: "valid-headers"
},
{
height: 1264373,
hash: "00000000000001ed3747e04fb95054cac0358fb4d8009d1999fc3ea34413e84a",
branchlen: 175,
status: "valid-fork"
},
{
height: 1263912,
hash: "00000000000004635aecbcb97edcd6f5396483338838cb40da804eb354c68bde",
branchlen: 1,
status: "valid-fork"
},
{
height: 1262906,
hash: "00000000b31643d92a3e7c38e9755844fc8607e3f1055580a8ff28854e8a8ece",
branchlen: 1,
status: "valid-fork"
},
{
height: 1255749,
hash: "000000001dde5b015a99137f4cba87871370f4b6fcfbcc8b126547e6c33177da",
branchlen: 129,
status: "headers-only"
},
{
height: 1188789,
hash: "00000000af942ce4eb60b3213cbcb7c98a7330f1f8f1adb4b6376f5e822e15b2",
branchlen: 92,
status: "headers-only"
}
]
const mockMempoolInfo = {
size: 87,
bytes: 16816,
usage: 66408,
maxmempool: 300000000,
mempoolminfee: 0
}
const mockRawMempool = [
"db045bc3bd1088fa91f5ebb05c35cb9e2a91a22377f79b465cc6920b9893123c",
"6b3df7febf2b9834f1409155f88b866dd516b36376eae00e2b455df82e290405"
]
const mockBlockHeaderConcise =
"0000ff7f7d217c9b7845ea8b50d620c59a1bf7c276566406e9b7bc7e463e0000000000006d70322c0b697c1c81d2744f87f09f1e9780ba5d30338952e2cdc64e60456f8423bb0a5ceafa091a3e843526"
const mockBlockHeader = {
hash: "00000000000008c3679777df34f1a09565f98b2400a05b7c8da72525fdca3900",
confirmations: 7,
height: 1272859,
version: 2147418112,
versionHex: "7fff0000",
merkleroot:
"846f45604ec6cde2528933305dba80971e9ff0874f74d2811c7c690b2c32706d",
time: 1544207139,
mediantime: 1544202884,
nonce: 641041470,
bits: "1a09faea",
difficulty: 1681035.704111868,
chainwork: "00000000000000000000000000000000000000000000003fc4752e608403be04",
previousblockhash:
"0000000000003e467ebcb7e906645676c2f71b9ac520d6508bea45789b7c217d",
nextblockhash:
"00000000000006899041cdfd6c0b73a97730c362346dde479b77414ad7f25ace"
}
const mockTxOut = {
bestblock: "00000000003a7f19730dd9f172d7466658a5c8833dd03ed8f4ba36e3d857b5e7",
confirmations: 10,
value: 0.0001,
scriptPubKey: {
asm:
"OP_DUP OP_HASH160 0ee020c07f39526ac5505c54fa1ab98490979b83 OP_EQUALVERIFY OP_CHECKSIG",
hex: "76a9140ee020c07f39526ac5505c54fa1ab98490979b8388ac",
reqSigs: 1,
type: "pubkeyhash",
addresses: ["bchtest:qq8wqgxq0uu4y6k92pw9f7s6hxzfp9umsvtg39pzqf"]
},
coinbase: false
}
const mockTxOutProof =
"000000200798039affceb7a381e15dc62443c286efe7cae852393b656807000000000000cf9a7d6c654fd1c4558229a619a550ff749b373fa0f0027eed68b1abf6175d5c07c50f5cffff001d0452403b34000000070d0765da76980d542b5670d27ccda9e717073270f8921ad017deb7ee83fe6c4c22830a48cc8392197f1742a3d81805bd2aa4eb7469f38c74f674429d682cb87efcff1713fd0c388a50f0d11adf05363397ef79d16cdb786d47c941c2cb89f2588f42aced9c05f4203b4440616a9e4266ec47909b9580a643f402b5d4e82cc3cddd73df05a33abf6af3975d973d3033d10c4168a73d58d01f685a96d5a2519cd0de9c77faf3f8725ddf155cbdc8ab8102f173e2d0a0d74767f3bff22f588158d6d5bc71c079c7659cc864819c43877635007650502eb4060fcc9feec3280a41d602ad0a"
module.exports = {
mockBlockHash,
mockBlockchainInfo,
mockChainTips,
mockMempoolInfo,
mockRawMempool,
mockBlockHeaderConcise,
mockBlockHeader,
mockTxOut,
mockTxOutProof
}
+27
View File
@@ -0,0 +1,27 @@
/*
This library contains mocking data for running unit tests on the address route.
*/
"use strict"
const mockGetInfo = {
version: 170200,
protocolversion: 70015,
walletversion: 160300,
balance: 0,
blocks: 1266726,
timeoffset: 0,
connections: 8,
proxy: "",
difficulty: 1,
testnet: true,
keypoololdest: 1536331195,
keypoolsize: 2000,
paytxfee: 0,
relayfee: 0.00001,
errors: "Warning: unknown new rules activated (versionbit 28)"
}
module.exports = {
mockGetInfo
}
+88
View File
@@ -0,0 +1,88 @@
/*
Contains mocks of Express req and res objects.
*/
"use strict"
const sinon = require("sinon")
// Inspect JS Objects.
const util = require("util")
util.inspect.defaultOptions = {
showHidden: true,
colors: true
}
// mock for res.send()
function fakeSend(arg) {
//console.log(`res.send: ${util.inspect(arg)}`);
mockRes.output = arg
return arg
}
// mock for res.json()
function fakeJson(arg) {
//console.log(`res.json: ${util.inspect(arg)}`);
mockRes.output = arg
return arg
}
// mock for res.setStatus(num)
const setStatusCode = arg => {
mockRes.statusCode = arg
}
const mockReq = {
accepts: sinon.stub().returns({}),
acceptsCharsets: sinon.stub().returns({}),
acceptsEncodings: sinon.stub().returns({}),
acceptsLanguages: sinon.stub().returns({}),
body: {},
flash: sinon.stub().returns({}),
get: sinon.stub().returns({}),
is: sinon.stub().returns({}),
params: {},
query: {},
session: {},
locals: {},
headers: {}
}
const mockRes = {
append: sinon.stub().returns({}),
attachement: sinon.stub().returns({}),
clearCookie: sinon.stub().returns({}),
cookie: sinon.stub().returns({}),
download: sinon.stub().returns({}),
end: sinon.stub().returns({}),
format: {},
get: sinon.stub().returns({}),
headersSent: sinon.stub().returns({}),
json: sinon.stub().callsFake(fakeJson),
jsonp: sinon.stub().returns({}),
links: sinon.stub().returns({}),
locals: {},
location: sinon.stub().returns({}),
output: null, // Used for retrieving output data.
redirect: sinon.stub().returns({}),
render: sinon.stub().returns({}),
send: sinon.stub().callsFake(fakeSend),
sendFile: sinon.stub().returns({}),
sendStatus: sinon.stub().returns({}),
set: sinon.stub().returns({}),
status: sinon.stub().callsFake(setStatusCode),
statusCode: null, // Default value before calling stats();
type: sinon.stub().returns({}),
vary: sinon.stub().returns({}),
write: sinon.stub().returns({}),
setHeader: sinon.stub().returns({}),
format: sinon.stub().returns({})
}
const mockNext = sinon.stub().returns()
module.exports = {
mockReq,
mockRes,
mockNext
}
+22
View File
@@ -0,0 +1,22 @@
/*
This library contains mocking data for running unit tests on the address route.
*/
"use strict"
const mockMiningInfo = {
blocks: 1270185,
currentblocksize: 0,
currentblocktx: 0,
difficulty: 1,
blockprioritypercentage: 5,
errors:
"Warning: Unknown block versions being mined! It's possible unknown rules are in effect",
networkhashps: 517410290.9365583,
pooledtx: 5,
chain: "test"
}
module.exports = {
mockMiningInfo
}
+157
View File
@@ -0,0 +1,157 @@
/*
This library contains mocking data for running unit tests.
*/
"use strict"
const mockDecodeRawTransaction = {
txid: "a332237d82a2543af1b0e1ae3c8cea1610c290ebcaf084a7e9894a61de0be988",
hash: "a332237d82a2543af1b0e1ae3c8cea1610c290ebcaf084a7e9894a61de0be988",
size: 226,
version: 2,
locktime: 0,
vin: [
{
txid: "21cced645eab150585ed7ca7c96edebab5793cc0a3b3b286c42fd7d6d798b5b9",
vout: 1,
scriptSig: {
asm:
"3045022100a7b1b08956abb8d6f322aa709d8583c8ea492ba0585f1a6f4f9983520af74a5a0220411aee4a9a54effab617b0508c504c31681b15f9b187179b4874257badd41390[ALL|FORKID] 0360cfc66fdacb650bc4c83b4e351805181ee696b7d5ab4667c57b2786f51c413d",
hex:
"483045022100a7b1b08956abb8d6f322aa709d8583c8ea492ba0585f1a6f4f9983520af74a5a0220411aee4a9a54effab617b0508c504c31681b15f9b187179b4874257badd4139041210360cfc66fdacb650bc4c83b4e351805181ee696b7d5ab4667c57b2786f51c413d"
},
sequence: 4294967295
}
],
vout: [
{
value: 0.0001,
n: 0,
scriptPubKey: {
asm:
"OP_DUP OP_HASH160 eb4b180def88e3f5625b2d8ae2c098ff7d85f664 OP_EQUALVERIFY OP_CHECKSIG",
hex: "76a914eb4b180def88e3f5625b2d8ae2c098ff7d85f66488ac",
reqSigs: 1,
type: "pubkeyhash",
addresses: [Array]
}
},
{
value: 0.09989752,
n: 1,
scriptPubKey: {
asm:
"OP_DUP OP_HASH160 eb4b180def88e3f5625b2d8ae2c098ff7d85f664 OP_EQUALVERIFY OP_CHECKSIG",
hex: "76a914eb4b180def88e3f5625b2d8ae2c098ff7d85f66488ac",
reqSigs: 1,
type: "pubkeyhash",
addresses: [Array]
}
}
]
}
const mockDecodeScript = {
asm:
"0 0 -57 OP_NOP6 OP_LSHIFT OP_UNKNOWN OP_UNKNOWN OP_UNKNOWN c486b2b3a3c03c79b5bade6ec9a77ced850515ab5e64edcc21010000006b483045022100a7b1b08956abb8d6f322aa OP_2OVER OP_NUMEQUALVERIFY OP_OR OP_INVERT OP_UNKNOWN OP_UNKNOWN 2ba0585f1a6f4f9983520af74a5a0220411aee4a9a54effab617b0508c504c31681b15f9b187179b4874257badd4139041210360cfc66fdacb650bc4c83b4e351805181ee696b7d5ab 67c57b2786f51c413dffffffff0210270000000000001976a914eb4b180def88e3f5625b2d8ae2c098ff7d85f66488ac786e9800000000001976a914eb4b180def88e3f5625b [error]",
type: "nonstandard",
p2sh: "bchtest:pzy6dwfy6yf373w0dr05a6flfqksurjhwcl3awhdvm"
}
const mockRawTransactionConcise =
"02000000014e6b52500110b1c30315b85805fb274f0f4afceffc1589f889b27709e59e987d000000006a473044022052762770baa71c1a0b9544ad0f1ea343d32c22aa87c5f8397b6852f464c15b1e02201f4390745cb470e21e0e3c14229f39fef55ea0643a4c997f99d9f3501eae09b7412103c346eee77a77a8d3e073dacc0532ca7a5b9747bc06d88bf091cac9f4bc8bb792ffffffff02d1778f950a0000001976a91436d2f27bbd826a86db1e93618ce3de89ef33169388ac80969800000000001976a914152ea3cd65f18cb8fa9146c84ea1a97af8f051de88ac00000000"
const mockRawTransactionVerbose = {
hex:
"02000000014e6b52500110b1c30315b85805fb274f0f4afceffc1589f889b27709e59e987d000000006a473044022052762770baa71c1a0b9544ad0f1ea343d32c22aa87c5f8397b6852f464c15b1e02201f4390745cb470e21e0e3c14229f39fef55ea0643a4c997f99d9f3501eae09b7412103c346eee77a77a8d3e073dacc0532ca7a5b9747bc06d88bf091cac9f4bc8bb792ffffffff02d1778f950a0000001976a91436d2f27bbd826a86db1e93618ce3de89ef33169388ac80969800000000001976a914152ea3cd65f18cb8fa9146c84ea1a97af8f051de88ac00000000",
txid: "bd320377db7026a3dd5c7ec444596c0ee18fc25c4f34ee944adc03e432ce1971",
hash: "bd320377db7026a3dd5c7ec444596c0ee18fc25c4f34ee944adc03e432ce1971",
size: 225,
version: 2,
locktime: 0,
vin: [
{
txid: "7d989ee50977b289f88915fceffc4a0f4f27fb0558b81503c3b1100150526b4e",
vout: 0,
scriptSig: {
asm:
"3044022052762770baa71c1a0b9544ad0f1ea343d32c22aa87c5f8397b6852f464c15b1e02201f4390745cb470e21e0e3c14229f39fef55ea0643a4c997f99d9f3501eae09b7[ALL|FORKID] 03c346eee77a77a8d3e073dacc0532ca7a5b9747bc06d88bf091cac9f4bc8bb792",
hex:
"473044022052762770baa71c1a0b9544ad0f1ea343d32c22aa87c5f8397b6852f464c15b1e02201f4390745cb470e21e0e3c14229f39fef55ea0643a4c997f99d9f3501eae09b7412103c346eee77a77a8d3e073dacc0532ca7a5b9747bc06d88bf091cac9f4bc8bb792"
},
sequence: 4294967295
}
],
vout: [
{
value: 454.58880465,
n: 0,
scriptPubKey: {
asm:
"OP_DUP OP_HASH160 36d2f27bbd826a86db1e93618ce3de89ef331693 OP_EQUALVERIFY OP_CHECKSIG",
hex: "76a91436d2f27bbd826a86db1e93618ce3de89ef33169388ac",
reqSigs: 1,
type: "pubkeyhash",
addresses: ["bchtest:qqmd9unmhkpx4pkmr6fkrr8rm6y77vckjvqe8aey35"]
}
},
{
value: 0.1,
n: 1,
scriptPubKey: {
asm:
"OP_DUP OP_HASH160 152ea3cd65f18cb8fa9146c84ea1a97af8f051de OP_EQUALVERIFY OP_CHECKSIG",
hex: "76a914152ea3cd65f18cb8fa9146c84ea1a97af8f051de88ac",
reqSigs: 1,
type: "pubkeyhash",
addresses: ["bchtest:qq2jag7dvhccew86j9rvsn4p49a03uz3mcpw3d6aca"]
}
}
],
blockhash: "000000000000026fa244de975ca89ea08008aa566564ce2e8ebb3144361b601b",
confirmations: 125,
time: 1542646373,
blocktime: 1542646373
}
const mockWHDecode = {
txid: "f8a9857fe3b8a288b5fcafb1b0fc196731f433add6d962f77acd7c10b970ff89",
fee: "500",
sendingaddress: "bchtest:qzjtnzcvzxx7s0na88yrg3zl28wwvfp97538sgrrmr",
referenceaddress: "bchtest:qzjtnzcvzxx7s0na88yrg3zl28wwvfp97538sgrrmr",
ismine: false,
version: 0,
type_int: 0,
type: "Simple Send",
propertyid: 368,
precision: "8",
amount: "10.00000000",
valid: true,
blockhash: "0000000046ba0bcef78caaa4492622176bbc563cf249ab52340a0449bc8e26f6",
blocktime: 1542814183,
positioninblock: 57,
block: 1269008,
confirmations: 2
}
const mockWHCreateInput = {
txid: "f7ed9cf23dee85910f6269c9a101a75fcfd2f3c6fc81f17fad824ff7aaf99ab2",
vout: 1,
scriptPubKey: "76a914a4b98b0c118de83e7d39c834445f51dce62425f588ac",
amount: 0.09984138,
satoshis: 9984138,
height: 1269011,
confirmations: 4,
legacyAddress: "mvXwPH74hW2yVTWwDwzsjGoaUAqJvWk7ZJ",
cashAddress: "bchtest:qzjtnzcvzxx7s0na88yrg3zl28wwvfp97538sgrrmr",
value: 0.09984138
}
module.exports = {
mockDecodeRawTransaction,
mockDecodeScript,
mockRawTransactionConcise,
mockRawTransactionVerbose,
mockWHDecode,
mockWHCreateInput
}
+327
View File
@@ -0,0 +1,327 @@
/*
This library contains mocking data for running unit tests.
*/
"use strict"
const mockList = {
t: [
{
tokenDetails: {
tokenIdHex:
"df808a41672a0a0ae6475b44f272a107bc9961b90f29dc918d71301f24fe92fb",
documentUri: "",
documentSha256: "",
symbol: "NAKAMOTO",
name: "NAKAMOTO",
decimals: 8
},
tokenStats: {
qty_valid_txns_since_genesis: 241,
qty_valid_token_utxos: 151,
qty_valid_token_addresses: 113,
qty_token_circulating_supply: "20995990",
qty_token_burned: "4010",
qty_token_minted: "21000000",
qty_satoshis_locked_up: 81900
}
}
]
}
const mockSingleToken = {
t: [
{
tokenDetails: {
decimals: 8,
tokenIdHex:
"650dea14c77f4d749608e36e375450c9ac91deb8b1b53e50cb0de2059a52d19a",
timestamp: "2018-08-26 07:06",
transactionType: "GENESIS",
versionType: 1,
documentUri: "",
documentSha256Hex: null,
symbol: "",
name: "TESTYCOIN",
batonVout: null,
containsBaton: false,
genesisOrMintQuantity: "9999",
sendOutputs: null
},
tokenStats: {
block_created: 1253802,
block_last_active_send: 1253802,
block_last_active_mint: null,
qty_valid_txns_since_genesis: 2,
qty_valid_token_utxos: 0,
qty_valid_token_addresses: 0,
qty_token_minted: "9999",
qty_token_burned: "9999",
qty_token_circulating_supply: "0",
qty_satoshis_locked_up: 0,
minting_baton_status: "NEVER_CREATED"
}
}
]
}
const mockSingleTokenError = {
t: []
}
const mockSingleAddress = {
a: [
{
_id: "5c93ed62a19119333d1595bc",
tokenDetails: {
tokenIdHex:
"6b081fcd1f78b187be1464313dac8ff257251b727a42b613552a4040870aeb29"
},
address: "slptest:pz0qcslrqn7hr44hsszwl4lw5r6udkg6zqv7sq3kk7",
satoshis_balance: 546,
token_balance: "4616984"
}
],
t: [
{
tokenDetails: {
decimals: 8,
tokenIdHex:
"6b081fcd1f78b187be1464313dac8ff257251b727a42b613552a4040870aeb29",
timestamp: "2019-02-25 10:05",
transactionType: "GENESIS",
versionType: 1,
documentUri: "https://developer.bitcoin.com",
documentSha256Hex: "",
symbol: "DEV",
name: "DEVCOIN",
batonVout: 2,
containsBaton: true,
genesisOrMintQuantity: "500000000",
sendOutputs: null
},
tokenStats: {
block_created: 571272,
block_last_active_send: 574758,
block_last_active_mint: 571272,
qty_valid_txns_since_genesis: 172,
qty_valid_token_utxos: 170,
qty_valid_token_addresses: 170,
qty_token_minted: "1000000000",
qty_token_burned: "4.99999995",
qty_token_circulating_supply: "999999995.00000005",
qty_satoshis_locked_up: 92820,
minting_baton_status: "ALIVE"
}
}
]
}
const mockTx = {
txid: "57b3082a2bf269b3d6f40fee7fb9c664e8256a88ca5ee2697c05b9457822d446",
version: 2,
locktime: 0,
vin: [
{
txid: "61e71554a3dc18158f30d9e8f5c9b6641a789690b32302899f81cbea9fe3bb49",
vout: 2,
sequence: 4294967295,
n: 0,
scriptSig: {
hex:
"4730440220409e79fec552f01203f41d3d621ae3db89c720af261c8268ce5f0453de009f5d022001e7ffefeba7b0716d32ea55cb6ace267b6ee9cbcc8a017bb9c3b6acf7889418412103c87f0ec048a0771bdb60533d45cac88c6974afeb055a65edd663c2f947335585",
asm:
"30440220409e79fec552f01203f41d3d621ae3db89c720af261c8268ce5f0453de009f5d022001e7ffefeba7b0716d32ea55cb6ace267b6ee9cbcc8a017bb9c3b6acf7889418[ALL|FORKID] 03c87f0ec048a0771bdb60533d45cac88c6974afeb055a65edd663c2f947335585"
},
value: 546,
legacyAddress: "mw22g57T9YA7MZQQu5eBDKj3PKTdt99oDL",
cashAddress: "bchtest:qz4qnxcxwvmacgye8wlakhz0835x0w3vtvaga95c09"
},
{
txid: "61e71554a3dc18158f30d9e8f5c9b6641a789690b32302899f81cbea9fe3bb49",
vout: 1,
sequence: 4294967295,
n: 1,
scriptSig: {
hex:
"483045022100a743bee56c99bd103be48a78fa4c7342100815d9d2448dbe6e1d338c3a13b241022066728b5279fc22eef5cd019582ff34771e29175835fc98aa3168a1548fd78ac8412103c87f0ec048a0771bdb60533d45cac88c6974afeb055a65edd663c2f947335585",
asm:
"3045022100a743bee56c99bd103be48a78fa4c7342100815d9d2448dbe6e1d338c3a13b241022066728b5279fc22eef5cd019582ff34771e29175835fc98aa3168a1548fd78ac8[ALL|FORKID] 03c87f0ec048a0771bdb60533d45cac88c6974afeb055a65edd663c2f947335585"
},
value: 546,
legacyAddress: "mw22g57T9YA7MZQQu5eBDKj3PKTdt99oDL",
cashAddress: "bchtest:qz4qnxcxwvmacgye8wlakhz0835x0w3vtvaga95c09"
},
{
txid: "61e71554a3dc18158f30d9e8f5c9b6641a789690b32302899f81cbea9fe3bb49",
vout: 3,
sequence: 4294967295,
n: 2,
scriptSig: {
hex:
"483045022100821473902eec5f1ce7d43b1ba7f9ec453bfe8b8dfc3de3e0723c883ab109922f02206162960e80618531fab2c16aee260fddd7979bab62471c8686af7de75f8732ec412103c87f0ec048a0771bdb60533d45cac88c6974afeb055a65edd663c2f947335585",
asm:
"3045022100821473902eec5f1ce7d43b1ba7f9ec453bfe8b8dfc3de3e0723c883ab109922f02206162960e80618531fab2c16aee260fddd7979bab62471c8686af7de75f8732ec[ALL|FORKID] 03c87f0ec048a0771bdb60533d45cac88c6974afeb055a65edd663c2f947335585"
},
value: 9997521,
legacyAddress: "mw22g57T9YA7MZQQu5eBDKj3PKTdt99oDL",
cashAddress: "bchtest:qz4qnxcxwvmacgye8wlakhz0835x0w3vtvaga95c09"
}
],
vout: [
{
value: "0.00000000",
n: 0,
scriptPubKey: {
hex:
"6a04534c500001010453454e44207ac7f4bb50b019fe0f5c81e3fc13fc0720e130282ea460768cafb49785eb2796080000000049504f80080000001c71e64280",
asm:
"OP_RETURN 5262419 1 1145980243 7ac7f4bb50b019fe0f5c81e3fc13fc0720e130282ea460768cafb49785eb2796 0000000049504f80 0000001c71e64280"
},
spentTxId: null,
spentIndex: null,
spentHeight: null
},
{
value: "0.00000546",
n: 1,
scriptPubKey: {
hex: "76a914396b8e57ad0cb58d30e2992f22047b3c20377aa688ac",
asm:
"OP_DUP OP_HASH160 396b8e57ad0cb58d30e2992f22047b3c20377aa6 OP_EQUALVERIFY OP_CHECKSIG",
addresses: ["mkkZf7T3fU3vHSzNPy51HBmM46ghN1gnN9"],
type: "pubkeyhash"
},
spentTxId: null,
spentIndex: null,
spentHeight: null
},
{
value: "0.00000546",
n: 2,
scriptPubKey: {
hex: "76a914aa099b067337dc20993bbfdb5c4f3c6867ba2c5b88ac",
asm:
"OP_DUP OP_HASH160 aa099b067337dc20993bbfdb5c4f3c6867ba2c5b OP_EQUALVERIFY OP_CHECKSIG",
addresses: ["mw22g57T9YA7MZQQu5eBDKj3PKTdt99oDL"],
type: "pubkeyhash"
},
spentTxId: null,
spentIndex: null,
spentHeight: null
},
{
value: "0.09996891",
n: 3,
scriptPubKey: {
hex: "76a914aa099b067337dc20993bbfdb5c4f3c6867ba2c5b88ac",
asm:
"OP_DUP OP_HASH160 aa099b067337dc20993bbfdb5c4f3c6867ba2c5b OP_EQUALVERIFY OP_CHECKSIG",
addresses: ["mw22g57T9YA7MZQQu5eBDKj3PKTdt99oDL"],
type: "pubkeyhash"
},
spentTxId: null,
spentIndex: null,
spentHeight: null
}
],
blockhash: "000000000000ce978accc64a6bb567acf0c653c202309a0f8e220149bf0c6968",
blockheight: 1287490,
confirmations: 746,
time: 1550855104,
blocktime: 1550855104,
valueOut: 0.09997983,
size: 628,
valueIn: 0.09998613,
fees: 0.0000063,
tokenInfo: {
versionType: 1,
transactionType: "SEND",
tokenIdHex:
"7ac7f4bb50b019fe0f5c81e3fc13fc0720e130282ea460768cafb49785eb2796",
sendOutputs: ["0", "1230000000", "122170000000"]
},
tokenIsValid: true
}
const mockConvert = {
slpAddress: "slptest:qz35h5mfa8w2pqma2jq06lp7dnv5fxkp2shlcycvd5",
cashAddress: "bchtest:qz35h5mfa8w2pqma2jq06lp7dnv5fxkp2svtllzmlf",
legacyAddress: "mvQPGnzRT6gMWASZBMg7NcT3vmvsSKSQtf"
}
const mockTokenDetails = {
tokenIdHex:
"df808a41672a0a0ae6475b44f272a107bc9961b90f29dc918d71301f24fe92fb",
documentUri: "",
symbol: "NAKAMOTO",
name: "NAKAMOTO",
decimals: 8,
timestamp: "",
containsBaton: true,
versionType: 1
}
const mockTokenStats = {
qty_valid_txns_since_genesis: 241,
qty_valid_token_utxos: 151,
qty_valid_token_addresses: 113,
qty_token_circulating_supply: "20995990",
qty_token_burned: "4010",
qty_token_minted: "21000000",
qty_satoshis_locked_up: 81900
}
const mockBalance = {
slpAddress: "simpleledger:qp9d8mn8ypryfvea2mev0ggc3wg6plpn4suuaeuss3",
satoshis_balance: 546,
token_balance: "1000"
}
const mockTransactions = [
{
txid: "a302f045be8efa1cd982833a7f187ff4fac8baac36da0c887eb2787d8b45e2af",
tokenDetails: {
valid: true,
detail: {
decimals: null,
tokenIdHex:
"495322b37d6b2eae81f045eda612b95870a0c2b6069c58f70cf8ef4e6a9fd43a",
timestamp: null,
transactionType: "MINT",
versionType: 1,
documentUri: null,
documentSha256Hex: null,
symbol: null,
name: null,
batonVout: 2,
containsBaton: true,
genesisOrMintQuantity: {
$numberDecimal: "1000"
},
sendOutputs: null
},
invalidReason: null,
schema_version: 30
}
}
]
const mockFoobar = {
c: [],
u: []
}
module.exports = {
mockList,
mockSingleToken,
mockConvert,
mockTokenDetails,
mockTokenStats,
mockTx,
mockBalance,
mockTransactions,
mockSingleTokenError,
mockSingleAddress,
mockFoobar
}
+71
View File
@@ -0,0 +1,71 @@
/*
Mocks used for unit tests that interact with slpjs.
*/
"use strict"
const sinon = require("sinon")
const proxyquire = require("proxyquire")
const BigNumber = require("bignumber.js")
const slpMocks = require("./slp-mocks")
// Mock the BitboxNetwork class.
class BitboxNetwork {
constructor() {}
async getAllSlpBalancesAndUtxos(address) {
return {
satoshis_available_bch: 9996891,
satoshis_in_slp_baton: 546,
satoshis_in_slp_token: 546,
satoshis_in_invalid_token_dag: 0,
satoshis_in_invalid_baton_dag: 0,
slpTokenBalances: {
"7ac7f4bb50b019fe0f5c81e3fc13fc0720e130282ea460768cafb49785eb2796": new BigNumber(
123400000000
)
},
slpTokenUtxos: {
"7ac7f4bb50b019fe0f5c81e3fc13fc0720e130282ea460768cafb49785eb2796": []
},
slpBatonUtxos: {
"7ac7f4bb50b019fe0f5c81e3fc13fc0720e130282ea460768cafb49785eb2796": []
},
nonSlpUtxos: [{}],
invalidTokenUtxos: [],
invalidBatonUtxos: []
}
}
async getTokenInformation(txid) {
BigNumber.set({ DECIMAL_PLACES: 8, ROUNDING_MODE: 4 })
const obj = {
versionType: 1,
transactionType: 0,
symbol: "SLPSDK",
name: "SLP SDK example using BITBOX",
documentUri: "developer.bitcoin.com",
documentSha256: null,
decimals: 8,
batonVout: 2,
containsBaton: true,
genesisOrMintQuantity: new BigNumber(123400000000)
}
return obj
}
async getTransactionDetails(txid) {
return slpMocks.mockTx
}
}
// Mock the slpjs library.
const slpjs = {
BitboxNetwork,
slp: {},
validator: {}
}
module.exports = slpjs
+74
View File
@@ -0,0 +1,74 @@
/*
This library contains mocking data for running unit tests on the address route.
*/
"use strict"
/*
const mockDetails = {
txid: "6f235bd3a689f03c11969cd649ccad592462ca958bc519a30194e7a67b349a40",
version: 2,
locktime: 0,
vin: [Array],
vout: [Array],
blockhash: "00000000e7232ff12462dedf9c11985f5b54202515277c337ccc59812758f28b",
blockheight: 1270188,
confirmations: 2,
time: 1543436253,
blocktime: 1543436253,
valueOut: 450.78867333,
size: 226,
valueIn: 450.78867559,
fees: 0.00000226
}
*/
const mockDetails = {
txid: "6f235bd3a689f03c11969cd649ccad592462ca958bc519a30194e7a67b349a40",
version: 2,
locktime: 0,
vin: [
{
txid: "273d616d1c48f4b075c497f36ffdc79da5c8d6ed75485808b3599aac504f8525",
vout: 0,
sequence: 4294967295,
n: 0,
scriptSig: [{}],
addr: "bchtest:qqmd9unmhkpx4pkmr6fkrr8rm6y77vckjvqe8aey35",
valueSat: 45078867559,
value: 450.78867559,
doubleSpentTxID: null
}
],
vout: [
{
value: "450.68867333",
n: 0,
scriptPubKey: [{}],
spentTxId: null,
spentIndex: null,
spentHeight: null
},
{
value: "0.10000000",
n: 1,
scriptPubKey: [{}],
spentTxId: null,
spentIndex: null,
spentHeight: null
}
],
blockhash: "00000000e7232ff12462dedf9c11985f5b54202515277c337ccc59812758f28b",
blockheight: 1270188,
confirmations: 3,
time: 1543436253,
blocktime: 1543436253,
valueOut: 450.78867333,
size: 226,
valueIn: 450.78867559,
fees: 0.00000226
}
module.exports = {
mockDetails
}
+18
View File
@@ -0,0 +1,18 @@
/*
This library contains mocking data for running unit tests on the address route.
*/
"use strict"
const mockAddress = {
isvalid: true,
address: "bchtest:qqqk4y6lsl5da64sg5qc3xezmplyu5kmpyz2ysaa5y",
scriptPubKey: "76a914016a935f87e8deeab04501889b22d87e4e52db0988ac",
ismine: false,
iswatchonly: false,
isscript: false
}
module.exports = {
mockAddress
}
+166
View File
@@ -0,0 +1,166 @@
"use strict"
const chai = require("chai")
const assert = chai.assert
const nock = require("nock") // HTTP mocking
// Used for debugging.
const util = require("util")
util.inspect.defaultOptions = { depth: 1 }
// Mocking data.
const { mockReq, mockRes, mockNext } = require("./mocks/express-mocks")
// Libraries under test
const rateLimitMiddleware = require("../../src/middleware/route-ratelimit")
const controlRoute = require("../../src/routes/v3/control")
let req, res, next
let originalEnvVars // Used during transition from integration to unit tests.
describe("#route-ratelimits", () => {
before(() => {
// Save existing environment variables.
originalEnvVars = {
BITCOINCOM_BASEURL: process.env.BITCOINCOM_BASEURL,
RPC_BASEURL: process.env.RPC_BASEURL,
RPC_USERNAME: process.env.RPC_USERNAME,
RPC_PASSWORD: process.env.RPC_PASSWORD
}
})
// Setup the mocks before each test.
beforeEach(() => {
// Mock the req and res objects used by Express routes.
req = mockReq
res = mockRes
next = mockNext
// Explicitly reset the parmas and body.
req.params = {}
req.body = {}
req.query = {}
})
describe("#routeRateLimit", () => {
const routeRateLimit = rateLimitMiddleware.routeRateLimit
const getInfo = controlRoute.testableComponents.getInfo
/*
it("should pass through rate-limit middleware", async () => {
req.baseUrl = "/v2"
req.path = "/control/getInfo"
req.method = "GET"
await routeRateLimit(req, res, next)
// next() will be called if rate-limit is not triggered
assert.equal(next.called, true)
})
it("should trigger rate-limit handler if rate limits exceeds 60 request per minute", async () => {
req.baseUrl = "/v2"
req.path = "/control/getInfo"
req.method = "GET"
for (let i = 0; i < 65; i++) {
next.reset() // reset the stubbed next() function.
await routeRateLimit(req, res, next)
//console.log(`next() called: ${next.called}`)
}
// Note: next() will be called unless the rate-limit kicks in.
assert.equal(
next.called,
false,
`next should not be called if rate limit was triggered.`
)
})
*/
/*
it("should NOT trigger rate-limit handler for pro-tier at 65 RPM", async () => {
// Clear the require cache before running this test.
delete require.cache[
require.resolve("../../dist/middleware/route-ratelimit")
]
rateLimitMiddleware = require("../../dist/middleware/route-ratelimit")
routeRateLimit = rateLimitMiddleware.routeRateLimit
req.baseUrl = "/v2"
req.path = "/control/getInfo"
req.method = "GET"
req.locals.proLimit = true
//console.log(`req.locals before test: ${util.inspect(req.locals)}`)
// Prepare the authorization header
//req.headers.authorization = generateAuthHeader("BITBOX")
for (let i = 0; i < 65; i++) {
next.reset() // reset the stubbed next() function.
await routeRateLimit(req, res, next)
//console.log(`next() called: ${next.called}`)
}
//console.log(`req.locals after test: ${util.inspect(req.locals)}`)
// Note: next() will be called unless the rate-limit kicks in.
assert.equal(
next.called,
true,
`next should be called if rate limit was not triggered.`
)
})
it("rate-limiting should still kick in at a higher RPM for pro-tier", async () => {
// Clear the require cache before running this test.
delete require.cache[
require.resolve("../../dist/middleware/route-ratelimit")
]
rateLimitMiddleware = require("../../dist/middleware/route-ratelimit")
routeRateLimit = rateLimitMiddleware.routeRateLimit
req.baseUrl = "/v2"
req.path = "/control/getInfo"
req.method = "GET"
req.locals.proLimit = true
//console.log(`req.locals before test: ${util.inspect(req.locals)}`)
// Prepare the authorization header
//req.headers.authorization = generateAuthHeader("BITBOX")
for (let i = 0; i < 650; i++) {
next.reset() // reset the stubbed next() function.
await routeRateLimit(req, res, next)
//console.log(`next() called: ${next.called}`)
}
//console.log(`req.locals after test: ${util.inspect(req.locals)}`)
// Note: next() will be called unless the rate-limit kicks in.
assert.equal(
next.called,
false,
`next should NOT be called if rate limit was triggered.`
)
})
*/
})
})
// Generates a Basic authorization header.
function generateAuthHeader(pass) {
// https://en.wikipedia.org/wiki/Basic_access_authentication
const username = "BITBOX"
const combined = `${username}:${pass}`
var base64Credential = Buffer.from(combined).toString("base64")
var readyCredential = `Basic ${base64Credential}`
return readyCredential
}
+807
View File
@@ -0,0 +1,807 @@
/*
TESTS FOR THE RAWTRANSACTIONS.TS LIBRARY
This test file uses the environment variable TEST to switch between unit
and integration tests. By default, TEST is set to 'unit'. Set this variable
to 'integration' to run the tests against BCH mainnet.
TODO:
-Create e2e test for sendRawTransaction.
*/
"use strict"
const chai = require("chai")
const assert = chai.assert
const rawtransactions = require("../../src/routes/v3/rawtransactions")
const nock = require("nock") // HTTP mocking
let originalEnvVars // Used during transition from integration to unit tests.
// Mocking data.
//delete require.cache[require.resolve("./mocks/express-mocks")] // Fixes bug
const { mockReq, mockRes, mockNext } = require("./mocks/express-mocks")
const mockData = require("./mocks/raw-transactions-mocks")
// Used for debugging.
const util = require("util")
util.inspect.defaultOptions = { depth: 5 }
describe("#Raw-Transactions", () => {
let req, res, next
before(() => {
// Save existing environment variables.
originalEnvVars = {
BITCOINCOM_BASEURL: process.env.BITCOINCOM_BASEURL,
RPC_BASEURL: process.env.RPC_BASEURL,
RPC_USERNAME: process.env.RPC_USERNAME,
RPC_PASSWORD: process.env.RPC_PASSWORD
}
// Set default environment variables for unit tests.
if (!process.env.TEST) process.env.TEST = "unit"
if (process.env.TEST === "unit") {
process.env.BITCOINCOM_BASEURL = "http://fakeurl/api/"
process.env.RPC_BASEURL = "http://fakeurl/api"
process.env.RPC_USERNAME = "fakeusername"
process.env.RPC_PASSWORD = "fakepassword"
}
})
// Setup the mocks before each test.
beforeEach(() => {
// Mock the req and res objects used by Express routes.
req = mockReq
res = mockRes
next = mockNext
// Explicitly reset the parmas and body.
req.params = {}
req.body = {}
req.query = {}
req.locals = {}
// Activate nock if it's inactive.
if (!nock.isActive()) nock.activate()
})
afterEach(() => {
// Clean up HTTP mocks.
nock.cleanAll() // clear interceptor list.
nock.restore()
})
after(() => {
// Restore any pre-existing environment variables.
process.env.BITCOINCOM_BASEURL = originalEnvVars.BITCOINCOM_BASEURL
process.env.RPC_BASEURL = originalEnvVars.RPC_BASEURL
process.env.RPC_USERNAME = originalEnvVars.RPC_USERNAME
process.env.RPC_PASSWORD = originalEnvVars.RPC_PASSWORD
})
describe("#root", async () => {
// root route handler.
const root = rawtransactions.testableComponents.root
it("should respond to GET for base route", async () => {
const result = root(req, res)
//console.log(`result: ${util.inspect(result)}`)
assert.equal(result.status, "rawtransactions", "Returns static string")
})
})
describe("decodeRawTransactionSingle()", () => {
// block route handler.
const decodeRawTransaction =
rawtransactions.testableComponents.decodeRawTransactionSingle
it("should throw error if hex is missing", async () => {
const result = await decodeRawTransaction(req, res)
//console.log(`result: ${util.inspect(result)}`)
assert.hasAllKeys(result, ["error"])
assert.include(result.error, "hex can not be empty")
})
it("should throw 503 when network issues", async () => {
// Save the existing RPC URL.
const savedUrl2 = process.env.RPC_BASEURL
// Manipulate the URL to cause a 500 network error.
process.env.RPC_BASEURL = "http://fakeurl/api/"
req.params.hex =
"0200000001b9b598d7d6d72fc486b2b3a3c03c79b5bade6ec9a77ced850515ab5e64edcc21010000006b483045022100a7b1b08956abb8d6f322aa709d8583c8ea492ba0585f1a6f4f9983520af74a5a0220411aee4a9a54effab617b0508c504c31681b15f9b187179b4874257badd4139041210360cfc66fdacb650bc4c83b4e351805181ee696b7d5ab4667c57b2786f51c413dffffffff0210270000000000001976a914eb4b180def88e3f5625b2d8ae2c098ff7d85f66488ac786e9800000000001976a914eb4b180def88e3f5625b2d8ae2c098ff7d85f66488ac00000000"
const result = await decodeRawTransaction(req, res)
//console.log(`result: ${util.inspect(result)}`)
// Restore the saved URL.
process.env.RPC_BASEURL = savedUrl2
assert.isAbove(
res.statusCode,
499,
"HTTP status code 500 or greater expected."
)
//assert.include(result.error,"Network error: Could not communicate with full node","Error message expected")
})
it("should GET /decodeRawTransaction", async () => {
// Mock the RPC call for unit tests.
if (process.env.TEST === "unit") {
nock(`${process.env.RPC_BASEURL}`)
.post(``)
.reply(200, { result: mockData.mockDecodeRawTransaction })
}
req.params.hex =
"0200000001b9b598d7d6d72fc486b2b3a3c03c79b5bade6ec9a77ced850515ab5e64edcc21010000006b483045022100a7b1b08956abb8d6f322aa709d8583c8ea492ba0585f1a6f4f9983520af74a5a0220411aee4a9a54effab617b0508c504c31681b15f9b187179b4874257badd4139041210360cfc66fdacb650bc4c83b4e351805181ee696b7d5ab4667c57b2786f51c413dffffffff0210270000000000001976a914eb4b180def88e3f5625b2d8ae2c098ff7d85f66488ac786e9800000000001976a914eb4b180def88e3f5625b2d8ae2c098ff7d85f66488ac00000000"
const result = await decodeRawTransaction(req, res)
//console.log(`result: ${util.inspect(result)}`)
assert.hasAnyKeys(result, [
"txid",
"hash",
"size",
"version",
"locktime",
"vin",
"vout"
])
assert.isArray(result.vin)
assert.isArray(result.vout)
})
})
describe("decodeRawTransactionBulk()", () => {
const decodeRawTransactionBulk =
rawtransactions.testableComponents.decodeRawTransactionBulk
it("should throw 400 error if hexes array is missing", async () => {
const result = await decodeRawTransactionBulk(req, res)
//console.log(`result: ${util.inspect(result)}`)
assert.hasAllKeys(result, ["error"])
assert.include(result.error, "hexes must be an array")
})
it("should throw 400 error if hexes array is too large", async () => {
const testArray = []
for (var i = 0; i < 25; i++) testArray.push("")
req.body.hexes = testArray
const result = await decodeRawTransactionBulk(req, res)
//console.log(`result: ${util.inspect(result)}`)
assert.hasAllKeys(result, ["error"])
assert.include(result.error, "Array too large")
})
it("should throw 400 error if hexes is empty", async () => {
req.body.hexes = [""]
const result = await decodeRawTransactionBulk(req, res)
//console.log(`result: ${util.inspect(result)}`)
assert.hasAllKeys(result, ["error"])
assert.include(result.error, "Encountered empty hex")
})
it("should error on non-array single hex", async () => {
req.body.hexes =
"0200000001b9b598d7d6d72fc486b2b3a3c03c79b5bade6ec9a77ced850515ab5e64edcc21010000006b483045022100a7b1b08956abb8d6f322aa709d8583c8ea492ba0585f1a6f4f9983520af74a5a0220411aee4a9a54effab617b0508c504c31681b15f9b187179b4874257badd4139041210360cfc66fdacb650bc4c83b4e351805181ee696b7d5ab4667c57b2786f51c413dffffffff0210270000000000001976a914eb4b180def88e3f5625b2d8ae2c098ff7d85f66488ac786e9800000000001976a914eb4b180def88e3f5625b2d8ae2c098ff7d85f66488ac00000000"
const result = await decodeRawTransactionBulk(req, res)
assert.equal(res.statusCode, 400, "HTTP status code 400 expected.")
assert.include(
result.error,
"hexes must be an array",
"Proper error message"
)
})
it("should decode an array with a single hex", async () => {
// Mock the RPC call for unit tests.
if (process.env.TEST === "unit") {
nock(`${process.env.RPC_BASEURL}`)
.post(``)
.reply(200, { result: mockData.mockDecodeRawTransaction })
}
req.body.hexes = [
"0200000001b9b598d7d6d72fc486b2b3a3c03c79b5bade6ec9a77ced850515ab5e64edcc21010000006b483045022100a7b1b08956abb8d6f322aa709d8583c8ea492ba0585f1a6f4f9983520af74a5a0220411aee4a9a54effab617b0508c504c31681b15f9b187179b4874257badd4139041210360cfc66fdacb650bc4c83b4e351805181ee696b7d5ab4667c57b2786f51c413dffffffff0210270000000000001976a914eb4b180def88e3f5625b2d8ae2c098ff7d85f66488ac786e9800000000001976a914eb4b180def88e3f5625b2d8ae2c098ff7d85f66488ac00000000"
]
const result = await decodeRawTransactionBulk(req, res)
//console.log(`result: ${util.inspect(result)}`)
assert.isArray(result)
assert.hasAnyKeys(result[0], [
"txid",
"hash",
"size",
"version",
"locktime",
"vin",
"vout"
])
assert.isArray(result[0].vin)
assert.isArray(result[0].vout)
})
it("should decode an array with multiple hexes", async () => {
// Mock the RPC call for unit tests.
if (process.env.TEST === "unit") {
nock(`${process.env.RPC_BASEURL}`)
.post(``)
.times(2)
.reply(200, { result: mockData.mockDecodeRawTransaction })
}
req.body.hexes = [
"0200000001b9b598d7d6d72fc486b2b3a3c03c79b5bade6ec9a77ced850515ab5e64edcc21010000006b483045022100a7b1b08956abb8d6f322aa709d8583c8ea492ba0585f1a6f4f9983520af74a5a0220411aee4a9a54effab617b0508c504c31681b15f9b187179b4874257badd4139041210360cfc66fdacb650bc4c83b4e351805181ee696b7d5ab4667c57b2786f51c413dffffffff0210270000000000001976a914eb4b180def88e3f5625b2d8ae2c098ff7d85f66488ac786e9800000000001976a914eb4b180def88e3f5625b2d8ae2c098ff7d85f66488ac00000000",
"0200000001b9b598d7d6d72fc486b2b3a3c03c79b5bade6ec9a77ced850515ab5e64edcc21010000006b483045022100a7b1b08956abb8d6f322aa709d8583c8ea492ba0585f1a6f4f9983520af74a5a0220411aee4a9a54effab617b0508c504c31681b15f9b187179b4874257badd4139041210360cfc66fdacb650bc4c83b4e351805181ee696b7d5ab4667c57b2786f51c413dffffffff0210270000000000001976a914eb4b180def88e3f5625b2d8ae2c098ff7d85f66488ac786e9800000000001976a914eb4b180def88e3f5625b2d8ae2c098ff7d85f66488ac00000000"
]
const result = await decodeRawTransactionBulk(req, res)
//console.log(`result: ${util.inspect(result)}`)
assert.isArray(result)
assert.hasAnyKeys(result[0], [
"txid",
"hash",
"size",
"version",
"locktime",
"vin",
"vout"
])
assert.isArray(result[0].vin)
assert.isArray(result[0].vout)
})
})
describe("decodeScriptSingle()", () => {
// block route handler.
const decodeScriptSingle =
rawtransactions.testableComponents.decodeScriptSingle
it("should throw error if hex is missing", async () => {
const result = await decodeScriptSingle(req, res)
//console.log(`result: ${util.inspect(result)}`)
assert.hasAllKeys(result, ["error"])
assert.include(result.error, "hex can not be empty")
})
it("should throw 503 when network issues", async () => {
// Save the existing RPC URL.
const savedUrl2 = process.env.RPC_BASEURL
// Manipulate the URL to cause a 500 network error.
process.env.RPC_BASEURL = "http://fakeurl/api/"
req.params.hex =
"0200000001b9b598d7d6d72fc486b2b3a3c03c79b5bade6ec9a77ced850515ab5e64edcc21010000006b483045022100a7b1b08956abb8d6f322aa709d8583c8ea492ba0585f1a6f4f9983520af74a5a0220411aee4a9a54effab617b0508c504c31681b15f9b187179b4874257badd4139041210360cfc66fdacb650bc4c83b4e351805181ee696b7d5ab4667c57b2786f51c413dffffffff0210270000000000001976a914eb4b180def88e3f5625b2d8ae2c098ff7d85f66488ac786e9800000000001976a914eb4b180def88e3f5625b2d8ae2c098ff7d85f66488ac00000000"
const result = await decodeScriptSingle(req, res)
//console.log(`result: ${util.inspect(result)}`)
// Restore the saved URL.
process.env.RPC_BASEURL = savedUrl2
assert.isAbove(
res.statusCode,
499,
"HTTP status code 500 or greater expected."
)
//assert.include(result.error,"Network error: Could not communicate with full node","Error message expected")
})
it("should GET /decodeScriptSingle", async () => {
// Mock the RPC call for unit tests.
if (process.env.TEST === "unit") {
nock(`${process.env.RPC_BASEURL}`)
.post(``)
.reply(200, { result: mockData.mockDecodeScript })
}
req.params.hex =
"0200000001b9b598d7d6d72fc486b2b3a3c03c79b5bade6ec9a77ced850515ab5e64edcc21010000006b483045022100a7b1b08956abb8d6f322aa709d8583c8ea492ba0585f1a6f4f9983520af74a5a0220411aee4a9a54effab617b0508c504c31681b15f9b187179b4874257badd4139041210360cfc66fdacb650bc4c83b4e351805181ee696b7d5ab4667c57b2786f51c413dffffffff0210270000000000001976a914eb4b180def88e3f5625b2d8ae2c098ff7d85f66488ac786e9800000000001976a914eb4b180def88e3f5625b2d8ae2c098ff7d85f66488ac00000000"
const result = await decodeScriptSingle(req, res)
//console.log(`result: ${util.inspect(result)}`)
assert.hasAllKeys(result, ["asm", "type", "p2sh"])
})
})
describe("decodeScriptBulk()", () => {
const decodeScriptBulk = rawtransactions.testableComponents.decodeScriptBulk
it("should throw 400 error if hexes array is missing", async () => {
const result = await decodeScriptBulk(req, res)
//console.log(`result: ${util.inspect(result)}`)
assert.hasAllKeys(result, ["error"])
assert.include(result.error, "hexes must be an array")
})
it("should throw 400 error if hexes array is too large", async () => {
const testArray = []
for (var i = 0; i < 25; i++) testArray.push("")
req.body.hexes = testArray
const result = await decodeScriptBulk(req, res)
//console.log(`result: ${util.inspect(result)}`)
assert.hasAllKeys(result, ["error"])
assert.include(result.error, "Array too large")
})
it("should throw 400 error if hexes is empty", async () => {
req.body.hexes = [""]
const result = await decodeScriptBulk(req, res)
//console.log(`result: ${util.inspect(result)}`)
assert.hasAllKeys(result, ["error"])
assert.include(result.error, "Encountered empty hex")
})
it("should error on non-array single hex", async () => {
req.body.hexes =
"0200000001b9b598d7d6d72fc486b2b3a3c03c79b5bade6ec9a77ced850515ab5e64edcc21010000006b483045022100a7b1b08956abb8d6f322aa709d8583c8ea492ba0585f1a6f4f9983520af74a5a0220411aee4a9a54effab617b0508c504c31681b15f9b187179b4874257badd4139041210360cfc66fdacb650bc4c83b4e351805181ee696b7d5ab4667c57b2786f51c413dffffffff0210270000000000001976a914eb4b180def88e3f5625b2d8ae2c098ff7d85f66488ac786e9800000000001976a914eb4b180def88e3f5625b2d8ae2c098ff7d85f66488ac00000000"
const result = await decodeScriptBulk(req, res)
assert.equal(res.statusCode, 400, "HTTP status code 400 expected.")
assert.include(
result.error,
"hexes must be an array",
"Proper error message"
)
})
it("should decode an array with a single hex", async () => {
// Mock the RPC call for unit tests.
if (process.env.TEST === "unit") {
nock(`${process.env.RPC_BASEURL}`)
.post(``)
.reply(200, { result: mockData.mockDecodeScript })
}
req.body.hexes = [
"4830450221009a51e00ec3524a7389592bc27bea4af5104a59510f5f0cfafa64bbd5c164ca2e02206c2a8bbb47eabdeed52f17d7df668d521600286406930426e3a9415fe10ed592012102e6e1423f7abde8b70bca3e78a7d030e5efabd3eb35c19302542b5fe7879c1a16"
]
const result = await decodeScriptBulk(req, res)
//console.log(`result: ${util.inspect(result)}`)
assert.isArray(result)
assert.hasAllKeys(result[0], ["asm", "type", "p2sh"])
})
it("should decode an array with a multiple hexes", async () => {
// Mock the RPC call for unit tests.
if (process.env.TEST === "unit") {
nock(`${process.env.RPC_BASEURL}`)
.post(``)
.times(2)
.reply(200, { result: mockData.mockDecodeScript })
}
req.body.hexes = [
"4830450221009a51e00ec3524a7389592bc27bea4af5104a59510f5f0cfafa64bbd5c164ca2e02206c2a8bbb47eabdeed52f17d7df668d521600286406930426e3a9415fe10ed592012102e6e1423f7abde8b70bca3e78a7d030e5efabd3eb35c19302542b5fe7879c1a16",
"4830450221009a51e00ec3524a7389592bc27bea4af5104a59510f5f0cfafa64bbd5c164ca2e02206c2a8bbb47eabdeed52f17d7df668d521600286406930426e3a9415fe10ed592012102e6e1423f7abde8b70bca3e78a7d030e5efabd3eb35c19302542b5fe7879c1a16"
]
const result = await decodeScriptBulk(req, res)
//console.log(`result: ${util.inspect(result)}`)
assert.isArray(result)
assert.equal(result.length, 2)
assert.hasAllKeys(result[0], ["asm", "type", "p2sh"])
})
})
describe("getRawTransactionBulk()", () => {
// block route handler.
const getRawTransactionBulk =
rawtransactions.testableComponents.getRawTransactionBulk
it("should throw 400 error if txids array is missing", async () => {
const result = await getRawTransactionBulk(req, res)
//console.log(`result: ${util.inspect(result)}`)
assert.hasAllKeys(result, ["error"])
assert.include(result.error, "txids must be an array")
})
it("should throw 400 error if txids array is too large", async () => {
const testArray = []
for (var i = 0; i < 25; i++) testArray.push("")
req.body.txids = testArray
const result = await getRawTransactionBulk(req, res)
//console.log(`result: ${util.inspect(result)}`)
assert.hasAllKeys(result, ["error"])
assert.include(result.error, "Array too large")
})
it("should throw 400 error if txid is empty", async () => {
req.body.txids = [""]
const result = await getRawTransactionBulk(req, res)
//console.log(`result: ${util.inspect(result)}`)
assert.hasAllKeys(result, ["error"])
assert.include(result.error, "Encountered empty TXID")
})
it("should throw 400 error if txid is invalid", async () => {
// Mock the RPC call for unit tests.
if (process.env.TEST === "unit") {
nock(`${process.env.RPC_BASEURL}`)
.post(``)
.reply(500, {
error: { message: "parameter 1 must be of length 64 (not 6)" }
})
}
req.body.txids = ["abc123"]
const result = await getRawTransactionBulk(req, res)
//console.log(`result: ${util.inspect(result)}`)
assert.hasAllKeys(result, ["error"])
assert.equal(res.statusCode, 400, "HTTP status code 400 expected.")
assert.include(result.error, "parameter 1 must be of length 64 (not 6)")
})
it("should get concise transaction data", async () => {
// Mock the RPC call for unit tests.
if (process.env.TEST === "unit") {
nock(`${process.env.RPC_BASEURL}`)
.post(``)
.reply(200, { result: mockData.mockRawTransactionConcise })
}
req.body.txids = [
"bd320377db7026a3dd5c7ec444596c0ee18fc25c4f34ee944adc03e432ce1971"
]
const result = await getRawTransactionBulk(req, res)
//console.log(`result: ${util.inspect(result)}`)
assert.isArray(result)
assert.isString(result[0])
})
it("should get verbose transaction data", async () => {
// Mock the RPC call for unit tests.
if (process.env.TEST === "unit") {
nock(`${process.env.RPC_BASEURL}`)
.post(``)
.reply(200, { result: mockData.mockRawTransactionVerbose })
}
req.body.txids = [
"bd320377db7026a3dd5c7ec444596c0ee18fc25c4f34ee944adc03e432ce1971"
]
req.body.verbose = true
const result = await getRawTransactionBulk(req, res)
//console.log(`result: ${util.inspect(result)}`)
assert.isArray(result)
assert.hasAnyKeys(result[0], [
"hex",
"txid",
"hash",
"size",
"version",
"locktime",
"vin",
"vout",
"blockhash",
"confirmations",
"time",
"blocktime"
])
assert.isArray(result[0].vin)
assert.isArray(result[0].vout)
})
})
describe("getRawTransactionSingle()", () => {
// block route handler.
const getRawTransactionSingle =
rawtransactions.testableComponents.getRawTransactionSingle
it("should throw 400 error if txid is missing", async () => {
const result = await getRawTransactionSingle(req, res)
//console.log(`result: ${util.inspect(result)}`)
assert.hasAllKeys(result, ["error"])
assert.include(result.error, "txid can not be empty")
})
it("should throw 400 error if txid is invalid", async () => {
// Mock the RPC call for unit tests.
if (process.env.TEST === "unit") {
nock(`${process.env.RPC_BASEURL}`)
.post(``)
.reply(500, {
error: { message: "parameter 1 must be of length 64 (not 6)" }
})
}
req.params.txid = "abc123"
const result = await getRawTransactionSingle(req, res)
//console.log(`result: ${util.inspect(result)}`)
assert.hasAllKeys(result, ["error"])
assert.equal(res.statusCode, 400, "HTTP status code 400 expected.")
assert.include(result.error, "parameter 1 must be of length 64 (not 6)")
})
it("should get concise transaction data", async () => {
// Mock the RPC call for unit tests.
if (process.env.TEST === "unit") {
nock(`${process.env.RPC_BASEURL}`)
.post(``)
.reply(200, { result: mockData.mockRawTransactionConcise })
}
req.params.txid =
"bd320377db7026a3dd5c7ec444596c0ee18fc25c4f34ee944adc03e432ce1971"
const result = await getRawTransactionSingle(req, res)
//console.log(`result: ${util.inspect(result)}`)
assert.isString(result)
})
it("should get verbose transaction data", async () => {
// Mock the RPC call for unit tests.
if (process.env.TEST === "unit") {
nock(`${process.env.RPC_BASEURL}`)
.post(``)
.reply(200, { result: mockData.mockRawTransactionVerbose })
}
req.params.txid =
"bd320377db7026a3dd5c7ec444596c0ee18fc25c4f34ee944adc03e432ce1971"
req.query.verbose = "true"
const result = await getRawTransactionSingle(req, res)
//console.log(`result: ${util.inspect(result)}`)
assert.hasAnyKeys(result, [
"hex",
"txid",
"hash",
"size",
"version",
"locktime",
"vin",
"vout",
"blockhash",
"confirmations",
"time",
"blocktime"
])
assert.isArray(result.vin)
assert.isArray(result.vout)
})
})
describe("sendRawTransactionBulk()", () => {
const sendRawTransaction =
rawtransactions.testableComponents.sendRawTransactionBulk
it("should throw 400 error if hexs array is missing", async () => {
const result = await sendRawTransaction(req, res)
//console.log(`result: ${util.inspect(result)}`)
assert.hasAllKeys(result, ["error"])
assert.include(result.error, "hex must be an array")
})
it("should throw 400 error if hexs array is too large", async () => {
const testArray = []
for (var i = 0; i < 25; i++) testArray.push("")
req.body.hexes = testArray
const result = await sendRawTransaction(req, res)
//console.log(`result: ${util.inspect(result)}`)
assert.hasAllKeys(result, ["error"])
assert.include(result.error, "Array too large")
})
it("should throw 400 error if hex array element is empty", async () => {
req.body.hexes = [""]
const result = await sendRawTransaction(req, res)
//console.log(`result: ${util.inspect(result)}`)
assert.hasAllKeys(result, ["error"])
assert.include(result.error, "Encountered empty hex")
})
it("should throw 500 error if hex is invalid", async () => {
// Mock the RPC call for unit tests.
if (process.env.TEST === "unit") {
nock(`${process.env.RPC_BASEURL}`)
.post(``)
.reply(500, {
error: { message: "TX decode failed" }
})
}
req.body.hexes = ["abc123"]
const result = await sendRawTransaction(req, res)
//console.log(`result: ${util.inspect(result)}`)
assert.hasAllKeys(result, ["error"])
assert.equal(res.statusCode, 400, "HTTP status code 400 expected.")
assert.include(result.error, "TX decode failed")
})
it("should submit hex encoded transaction", async () => {
// This is a difficult test to run as transaction hex is invalid after a
// block confirmation. So the unit tests simulates what the output 'should'
// be, but the integration asserts an expected failure.
// Mock the RPC call for unit tests.
if (process.env.TEST === "unit") {
nock(`${process.env.RPC_BASEURL}`)
.post(``)
.reply(200, {
result:
"aef8848396e67532b42008b9d75b5a5a3459a6717740f31f0553b74102b4b118"
})
}
req.body.hexes = [
"0200000001189f7cf4303e2e0bcc5af4be323b9b397dd4104ca2de09528eb90a1450b8a999010000006a4730440220212ec2ffce136a30cec1bc86a40b08a2afdeb6f8dbd652d7bcb07b1aad6dfa8c022041f59585273b89d88879a9a531ba3272dc953f48ff57dad955b2dee70e76c0624121030143ffd18f1c4add75c86b2f930d9551d51f7a6bd786314247022b7afc45d231ffffffff0230d39700000000001976a914af64a026e06910c59463b000d18c3d125d7e951a88ac58c20000000000001976a914af64a026e06910c59463b000d18c3d125d7e951a88ac00000000"
]
const result = await sendRawTransaction(req, res)
//console.log(`result: ${util.inspect(result)}`)
if (process.env.TEST === "unit") {
assert.isArray(result)
assert.isString(result[0])
// Integration test
} else {
assert.hasAllKeys(result, ["error"])
assert.include(result.error, "transaction already in block chain")
}
})
})
describe("sendRawTransactionSingle()", () => {
// block route handler.
const sendRawTransaction =
rawtransactions.testableComponents.sendRawTransactionSingle
it("should throw an error for an empty hex", async () => {
req.params.hex = ""
const result = await sendRawTransaction(req, res)
assert.equal(res.statusCode, 400, "HTTP status code 400 expected.")
assert.include(
result.error,
"Encountered empty hex",
"Proper error message"
)
})
it("should throw an error for a non-string", async () => {
req.params.hex = 456
const result = await sendRawTransaction(req, res)
assert.equal(res.statusCode, 400, "HTTP status code 400 expected.")
assert.include(
result.error,
"hex must be a string",
"Proper error message"
)
})
it("should throw 500 when network issues", async () => {
// Save the existing RPC URL.
const savedUrl = process.env.BITCOINCOM_BASEURL
const savedUrl2 = process.env.RPC_BASEURL
// Manipulate the URL to cause a 500 network error.
process.env.BITCOINCOM_BASEURL = "http://fakeurl/api/"
process.env.RPC_BASEURL = "http://fakeurl/api/"
req.params.hex =
"0200000001189f7cf4303e2e0bcc5af4be323b9b397dd4104ca2de09528eb90a1450b8a999010000006a4730440220212ec2ffce136a30cec1bc86a40b08a2afdeb6f8dbd652d7bcb07b1aad6dfa8c022041f59585273b89d88879a9a531ba3272dc953f48ff57dad955b2dee70e76c0624121030143ffd18f1c4add75c86b2f930d9551d51f7a6bd786314247022b7afc45d231ffffffff0230d39700000000001976a914af64a026e06910c59463b000d18c3d125d7e951a88ac58c20000000000001976a914af64a026e06910c59463b000d18c3d125d7e951a88ac00000000"
const result = await sendRawTransaction(req, res)
//console.log(`result: ${util.inspect(result)}`)
// Restore the saved URL.
process.env.BITCOINCOM_BASEURL = savedUrl
process.env.RPC_BASEURL = savedUrl2
assert.isAbove(
res.statusCode,
499,
"HTTP status code 500 or great expected."
)
})
it("should throw an error for invalid hex", async () => {
req.params.hex = "abc123"
// Mock the RPC call for unit tests.
if (process.env.TEST === "unit") {
nock(`${process.env.RPC_BASEURL}`)
.post(``)
.reply(500, {
error: { message: "TX decode failed" }
})
}
const result = await sendRawTransaction(req, res)
//console.log(`result: ${util.inspect(result)}`)
assert.hasAllKeys(result, ["error"])
assert.equal(res.statusCode, 400, "HTTP status code 400 expected.")
assert.include(result.error, "TX decode failed")
})
it("should GET /sendRawTransaction/:hex", async () => {
// This is a difficult test to run as transaction hex is invalid after a
// block confirmation. So the unit tests simulates what the output 'should'
// be, but the integration asserts an expected failure.
// Mock the RPC call for unit tests.
if (process.env.TEST === "unit") {
nock(`${process.env.RPC_BASEURL}`)
.post(``)
.reply(200, {
result:
"aef8848396e67532b42008b9d75b5a5a3459a6717740f31f0553b74102b4b118"
})
}
req.params.hex =
"0200000001189f7cf4303e2e0bcc5af4be323b9b397dd4104ca2de09528eb90a1450b8a999010000006a4730440220212ec2ffce136a30cec1bc86a40b08a2afdeb6f8dbd652d7bcb07b1aad6dfa8c022041f59585273b89d88879a9a531ba3272dc953f48ff57dad955b2dee70e76c0624121030143ffd18f1c4add75c86b2f930d9551d51f7a6bd786314247022b7afc45d231ffffffff0230d39700000000001976a914af64a026e06910c59463b000d18c3d125d7e951a88ac58c20000000000001976a914af64a026e06910c59463b000d18c3d125d7e951a88ac00000000"
const result = await sendRawTransaction(req, res)
//console.log(`result: ${util.inspect(result)}`)
if (process.env.TEST === "unit") {
assert.isString(result)
// Integration test
} else {
assert.hasAllKeys(result, ["error"])
assert.include(result.error, "transaction already in block chain")
}
})
})
})
+971
View File
@@ -0,0 +1,971 @@
/*
TESTS FOR THE SLP.TS LIBRARY
This test file uses the environment variable TEST to switch between unit
and integration tests. By default, TEST is set to 'unit'. Set this variable
to 'integration' to run the tests against BCH mainnet.
TODO:
-See listSingleToken() tests.
*/
"use strict"
const chai = require("chai")
const assert = chai.assert
const nock = require("nock") // HTTP mocking
const sinon = require("sinon")
const proxyquire = require("proxyquire").noPreserveCache()
// Prepare the slpRoute for stubbing dependcies on slpjs.
const slpRoute = require("../../src/routes/v3/slp")
const pathStub = {} // Used to stub methods within slpjs.
const slpRouteStub = proxyquire("../../src/routes/v3/slp", { slpjs: pathStub })
let originalEnvVars // Used during transition from integration to unit tests.
// Mocking data.
const { mockReq, mockRes } = require("./mocks/express-mocks")
const mockData = require("./mocks/slp-mocks")
const slpjsMock = require("./mocks/slpjs-mocks")
// Used for debugging.
const util = require("util")
util.inspect.defaultOptions = { depth: 1 }
describe("#SLP", () => {
let req, res, mockServerUrl
let sandbox
before(() => {
// Save existing environment variables.
originalEnvVars = {
BITDB_URL: process.env.BITDB_URL,
BITCOINCOM_BASEURL: process.env.BITCOINCOM_BASEURL,
SLPDB_URL: process.env.SLPDB_URL
}
// Set default environment variables for unit tests.
if (!process.env.TEST) process.env.TEST = "unit"
// Block network connections for unit tests.
if (process.env.TEST === "unit") {
process.env.BITDB_URL = "http://fakeurl/"
process.env.BITCOINCOM_BASEURL = "http://fakeurl/"
process.env.SLPDB_URL = "http://fakeurl/"
mockServerUrl = `http://fakeurl`
}
})
// Setup the mocks before each test.
beforeEach(() => {
// Mock the req and res objects used by Express routes.
req = mockReq
res = mockRes
// Explicitly reset the parmas and body.
//req.params = {}
req.body = {}
req.query = {}
req.locals = {}
// 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(() => {
// Restore any pre-existing environment variables.
process.env.BITDB_URL = originalEnvVars.BITDB_URL
process.env.BITCOINCOM_BASEURL = originalEnvVars.BITCOINCOM_BASEURL
process.env.SLPDB_URL = originalEnvVars.SLPDB_URL
})
describe("#root", async () => {
// root route handler.
const root = slpRoute.testableComponents.root
it("should respond to GET for base route", async () => {
const result = root(req, res)
//console.log(`result: ${util.inspect(result)}`)
assert.equal(result.status, "slp", "Returns static string")
})
})
describe("list()", () => {
// list route handler
const list = slpRoute.testableComponents.list
it("should throw 500 when network issues", async () => {
// Save the existing SLPDB_URL.
const savedUrl2 = process.env.SLPDB_URL
// Manipulate the URL to cause a 500 network error.
process.env.SLPDB_URL = "http://fakeurl/api/"
const result = await list(req, res)
// console.log(`result: ${util.inspect(result)}`)
// Restore the saved URL.
process.env.SLPDB_URL = savedUrl2
assert.isAbove(
res.statusCode,
499,
"HTTP status code 500 or greater expected."
)
//assert.include(result.error,"Network error: Could not communicate with full node","Error message expected")
})
it("should GET list", async () => {
// Mock the RPC call for unit tests.
if (process.env.TEST === "unit") {
const b64 = `eyJ2IjozLCJxIjp7ImRiIjpbInQiXSwiZmluZCI6eyIkcXVlcnkiOnt9fSwicHJvamVjdCI6eyJ0b2tlbkRldGFpbHMiOjEsInRva2VuU3RhdHMiOjEsIl9pZCI6MH0sImxpbWl0IjoxMDB9fQ==`
nock(process.env.SLPDB_URL)
.get(uri => uri.includes("/"))
.reply(200, mockData.mockList)
}
const result = await list(req, res)
// console.log(`test result: ${util.inspect(result)}`)
assert.isArray(result)
assert.hasAnyKeys(result[0], [
"id",
"timestamp",
"symbol",
"name",
"documentUri",
"documentHash",
"decimals",
"initialTokenQty"
])
})
})
describe("listSingleToken()", () => {
const listSingleToken = slpRoute.testableComponents.listSingleToken
it("should throw 400 if tokenId is empty", async () => {
const result = await listSingleToken(req, res)
//console.log(`result: ${util.inspect(result)}`)
assert.hasAllKeys(result, ["error"])
assert.include(result.error, "tokenId can not be empty")
})
it("should throw 503 when network issues", async () => {
// Save the existing BITDB_URL.
const savedUrl2 = process.env.SLPDB_URL
// Manipulate the URL to cause a 500 network error.
process.env.SLPDB_URL = "http://fakeurl/api/"
req.params.tokenId =
"650dea14c77f4d749608e36e375450c9ac91deb8b1b53e50cb0de2059a52d19a"
const result = await listSingleToken(req, res)
// console.log(`result: ${util.inspect(result)}`)
// Restore the saved URL.
process.env.SLPDB_URL = savedUrl2
assert.isAbove(
res.statusCode,
499,
"HTTP status code 500 or greater expected."
)
//assert.include(result.error,"Network error: Could not communicate with full node","Error message expected")
})
it("should return 'not found' for mainnet txid on testnet", async () => {
// Mock the RPC call for unit tests.
if (process.env.TEST === "unit") {
nock(mockServerUrl)
.get(uri => uri.includes("/"))
.reply(200, mockData.mockSingleToken)
}
req.params.tokenId =
// testnet
//"650dea14c77f4d749608e36e375450c9ac91deb8b1b53e50cb0de2059a52d19a"
// mainnet
"259908ae44f46ef585edef4bcc1e50dc06e4c391ac4be929fae27235b8158cf1"
const result = await listSingleToken(req, res)
// console.log(`result: ${util.inspect(result)}`)
assert.hasAllKeys(result, ["id"])
assert.include(result.id, "not found")
})
it("should get token information", async () => {
// testnet
const tokenIdToTest =
"650dea14c77f4d749608e36e375450c9ac91deb8b1b53e50cb0de2059a52d19a"
// Mock the RPC call for unit tests.
if (process.env.TEST === "unit") {
nock(mockServerUrl)
.get(uri => uri.includes("/"))
.reply(200, mockData.mockSingleToken)
}
req.params.tokenId = tokenIdToTest
const result = await listSingleToken(req, res)
//console.log(`result: ${util.inspect(result)}`)
assert.hasAllKeys(result, [
"id",
"blockCreated",
"blockLastActiveMint",
"blockLastActiveSend",
"circulatingSupply",
"containsBaton",
"mintingBatonStatus",
"txnsSinceGenesis",
"versionType",
"timestamp",
"symbol",
"name",
"documentUri",
"documentHash",
"decimals",
"initialTokenQty",
"totalBurned",
"totalMinted",
"validAddresses"
])
})
})
describe("listBulkToken()", () => {
const listBulkToken = slpRoute.testableComponents.listBulkToken
it("should throw 400 if tokenIds array is empty", async () => {
const result = await listBulkToken(req, res)
// console.log(`result: ${util.inspect(result)}`)
assert.hasAllKeys(result, ["error"])
assert.include(result.error, "tokenIds needs to be an array")
assert.equal(res.statusCode, 400)
})
it("should throw 400 error if array is too large", async () => {
const testArray = []
for (var i = 0; i < 25; i++) testArray.push("")
req.body.tokenIds = testArray
const result = await listBulkToken(req, res)
//console.log(`result: ${util.inspect(result)}`)
assert.hasAllKeys(result, ["error"])
assert.include(result.error, "Array too large")
})
it("should throw 400 if tokenId is empty", async () => {
// Mock the RPC call for unit tests.
if (process.env.TEST === "unit") {
nock(mockServerUrl)
.get(uri => uri.includes("/"))
.reply(200, mockData.mockEmptyTokenId)
}
req.body.tokenIds = ""
const result = await listBulkToken(req, res)
// console.log(`result: ${util.inspect(result)}`)
assert.hasAllKeys(result, ["error"])
assert.include(
result.error,
"tokenIds needs to be an array. Use GET for single tokenId."
)
})
it("should throw 503 when network issues", async () => {
// Save the existing BITDB_URL.
const savedUrl2 = process.env.SLPDB_URL
// Manipulate the URL to cause a 500 network error.
process.env.SLPDB_URL = "http://fakeurl/api/"
req.body.tokenIds = [
"650dea14c77f4d749608e36e375450c9ac91deb8b1b53e50cb0de2059a52d19a"
]
const result = await listBulkToken(req, res)
// console.log(`result: ${util.inspect(result)}`)
// Restore the saved URL.
process.env.SLPDB_URL = savedUrl2
assert.isAbove(
res.statusCode,
499,
"HTTP status code 500 or greater expected."
)
//assert.include(result.error,"Network error: Could not communicate with full node","Error message expected")
})
it("should return 'not found' for mainnet txid on testnet", async () => {
// Mock the RPC call for unit tests.
if (process.env.TEST === "unit") {
nock(mockServerUrl)
.get(uri => uri.includes("/"))
.reply(200, mockData.mockSingleTokenError)
}
req.body.tokenIds =
// testnet
//"650dea14c77f4d749608e36e375450c9ac91deb8b1b53e50cb0de2059a52d19a"
// mainnet
["0b314bc2b2905b8844222871c6b665ae3494117c83b11302824561bb904efb6b"]
const result = await listBulkToken(req, res)
// console.log(`result: ${util.inspect(result)}`)
assert.isArray(result)
assert.hasAllKeys(result[0], ["id", "valid"])
assert.strictEqual(result[0].valid, false)
})
it("should get token information for single token ID", async () => {
// Mock the RPC call for unit tests.
if (process.env.TEST === "unit") {
nock(mockServerUrl)
.get(uri => uri.includes("/"))
.reply(200, mockData.mockSingleToken)
}
req.body.tokenIds =
// testnet
["650dea14c77f4d749608e36e375450c9ac91deb8b1b53e50cb0de2059a52d19a"]
const result = await listBulkToken(req, res)
//console.log(`result: ${util.inspect(result)}`)
assert.isArray(result)
assert.hasAllKeys(result[0], [
"blockCreated",
"blockLastActiveMint",
"blockLastActiveSend",
"circulatingSupply",
"containsBaton",
"mintingBatonStatus",
"txnsSinceGenesis",
"versionType",
"timestamp",
"symbol",
"name",
"documentUri",
"documentHash",
"decimals",
"initialTokenQty",
"id",
"totalBurned",
"totalMinted",
"validAddresses"
])
})
it("should get token information for multiple token IDs", async () => {
// Mock the RPC call for unit tests.
if (process.env.TEST === "unit") {
nock(mockServerUrl)
.get(uri => uri.includes("/"))
.times(2)
.reply(200, mockData.mockSingleToken)
}
req.body.tokenIds =
// testnet
[
"650dea14c77f4d749608e36e375450c9ac91deb8b1b53e50cb0de2059a52d19a",
"c35a87afad11c8d086c1449ffd8b0a84324e72b15b1bcfdf166a493551b4eea6"
]
const result = await listBulkToken(req, res)
//console.log(`result: ${util.inspect(result)}`)
assert.isArray(result)
assert.hasAllKeys(result[0], [
"blockCreated",
"blockLastActiveMint",
"blockLastActiveSend",
"circulatingSupply",
"containsBaton",
"mintingBatonStatus",
"txnsSinceGenesis",
"versionType",
"timestamp",
"symbol",
"name",
"documentUri",
"documentHash",
"decimals",
"initialTokenQty",
"id",
"totalBurned",
"totalMinted",
"validAddresses"
])
})
})
describe("balancesForAddress()", () => {
const balancesForAddress = slpRoute.testableComponents.balancesForAddress
it("should throw 400 if address is empty", async () => {
const result = await balancesForAddress(req, res)
//console.log(`result: ${util.inspect(result)}`)
assert.hasAllKeys(result, ["error"])
assert.include(result.error, "address can not be empty")
})
it("should throw 400 if address is invalid", async () => {
req.params.address = "badAddress"
const result = await balancesForAddress(req, res)
//console.log(`result: ${util.inspect(result)}`)
assert.hasAllKeys(result, ["error"])
assert.include(result.error, "Invalid BCH address.")
})
it("should throw 400 if address network mismatch", async () => {
req.params.address =
"simpleledger:qr5agtachyxvrwxu76vzszan5pnvuzy8duhv4lxrsk"
const result = await balancesForAddress(req, res)
// console.log(`result: ${util.inspect(result)}`)
assert.hasAllKeys(result, ["error"])
assert.include(result.error, "Invalid")
})
it("should throw 5XX error when network issues", async () => {
// Save the existing SLPDB_URL.
const savedUrl2 = process.env.SLPDB_URL
// Manipulate the URL to cause a 500 network error.
process.env.SLPDB_URL = "http://fakeurl/api/"
req.params.address = "slptest:qz35h5mfa8w2pqma2jq06lp7dnv5fxkp2shlcycvd5"
const result = await balancesForAddress(req, res)
// console.log(`result: ${util.inspect(result)}`)
// Restore the saved URL.
process.env.SLPDB_URL = savedUrl2
assert.isAbove(
res.statusCode,
499,
"HTTP status code 500 or greater expected."
)
assert.include(
result.error,
"Network error: Could not communicate",
"Error message expected"
)
})
it("should get token balance for an address", async () => {
// Mock the RPC call for unit tests.
if (process.env.TEST === "unit") {
nock(mockServerUrl)
.get(uri => uri.includes("/"))
.times(2)
.reply(200, mockData.mockSingleAddress)
}
req.params.address = "slptest:pz0qcslrqn7hr44hsszwl4lw5r6udkg6zqv7sq3kk7"
const result = await balancesForAddress(req, res)
// console.log(`result: ${util.inspect(result)}`)
assert.isArray(result)
assert.hasAllKeys(result[0], [
"tokenId",
"balance",
"slpAddress",
"decimalCount"
])
})
})
describe("balancesForAddressByTokenID()", () => {
const balancesForAddressByTokenID =
slpRoute.testableComponents.balancesForAddressByTokenID
it("should throw 400 if address is empty", async () => {
req.params.address = ""
req.params.tokenId =
"650dea14c77f4d749608e36e375450c9ac91deb8b1b53e50cb0de2059a52d19a"
const result = await balancesForAddressByTokenID(req, res)
// console.log(`result: ${util.inspect(result)}`)
assert.hasAllKeys(result, ["error"])
assert.include(result.error, "address can not be empty")
})
it("should throw 400 if tokenId is empty", async () => {
req.params.address =
"simpleledger:qr5agtachyxvrwxu76vzszan5pnvuzy8duhv4lxrsk"
req.params.tokenId = ""
const result = await balancesForAddressByTokenID(req, res)
// console.log(`result: ${util.inspect(result)}`)
assert.hasAllKeys(result, ["error"])
assert.include(result.error, "tokenId can not be empty")
})
it("should throw 400 if address is invalid", async () => {
req.params.address = "badAddress"
req.params.tokenId =
"650dea14c77f4d749608e36e375450c9ac91deb8b1b53e50cb0de2059a52d19a"
const result = await balancesForAddressByTokenID(req, res)
//console.log(`result: ${util.inspect(result)}`)
assert.hasAllKeys(result, ["error"])
assert.include(result.error, "Invalid BCH address.")
})
it("should throw 400 if address network mismatch", async () => {
req.params.address =
"simpleledger:qr5agtachyxvrwxu76vzszan5pnvuzy8duhv4lxrsk"
const result = await balancesForAddressByTokenID(req, res)
//console.log(`result: ${util.inspect(result)}`)
assert.hasAllKeys(result, ["error"])
assert.include(result.error, "Invalid")
})
it("should throw 5XX error when network issues", async () => {
// Save the existing SLPDB_URL.
const savedUrl2 = process.env.SLPDB_URL
// Manipulate the URL to cause a 500 network error.
process.env.SLPDB_URL = "http://fakeurl/api/"
req.params.address = "slptest:qz4qnxcxwvmacgye8wlakhz0835x0w3vtvxu67w0ac"
req.params.tokenId =
"7ac7f4bb50b019fe0f5c81e3fc13fc0720e130282ea460768cafb49785eb2796"
const result = await balancesForAddressByTokenID(req, res)
// console.log(`result: ${util.inspect(result)}`)
// Restore the saved URL.
process.env.SLPDB_URL = savedUrl2
assert.isAbove(
res.statusCode,
499,
"HTTP status code 500 or greater expected."
)
assert.include(
result.error,
"Network error: Could not communicate",
"Error message expected"
)
})
it("should get token information", async () => {
if (process.env.TEST === "unit") {
nock(mockServerUrl)
.get(uri => uri.includes("/"))
.times(2)
.reply(200, mockData.mockSingleAddress)
}
req.params.address = "slptest:pz0qcslrqn7hr44hsszwl4lw5r6udkg6zqv7sq3kk7"
req.params.tokenId =
"6b081fcd1f78b187be1464313dac8ff257251b727a42b613552a4040870aeb29"
const result = await balancesForAddressByTokenID(req, res)
// console.log(`result: ${util.inspect(result)}`)
// TODO - add decimalCount
// assert.hasAllKeys(result, ["tokenId", "balance", "decimalCount"])
assert.hasAllKeys(result, ["tokenId", "balance"])
})
})
describe("convertAddressSingle()", () => {
const convertAddressSingle =
slpRoute.testableComponents.convertAddressSingle
it("should throw 400 if address is empty", async () => {
req.params.address = ""
const result = await convertAddressSingle(req, res)
//console.log(`result: ${util.inspect(result)}`)
assert.hasAllKeys(result, ["error"])
assert.include(result.error, "address can not be empty")
})
//
it("should convert address", async () => {
// Mock the RPC call for unit tests.
if (process.env.TEST === "unit") {
nock(`${process.env.SLPDB_URL}`)
.post(``)
.reply(200, { result: mockData.mockConvert })
}
req.params.address = "slptest:qz35h5mfa8w2pqma2jq06lp7dnv5fxkp2shlcycvd5"
const result = await convertAddressSingle(req, res)
// console.log(`result: ${util.inspect(result)}`)
assert.hasAllKeys(result, ["cashAddress", "legacyAddress", "slpAddress"])
})
})
describe("convertAddressBulk()", () => {
const convertAddressBulk = slpRoute.testableComponents.convertAddressBulk
it("should throw 400 if addresses array is empty", async () => {
const result = await convertAddressBulk(req, res)
// console.log(`result: ${util.inspect(result)}`)
assert.hasAllKeys(result, ["error"])
assert.include(result.error, "addresses needs to be an array")
assert.equal(res.statusCode, 400)
})
it("should throw 400 error if array is too large", async () => {
const testArray = []
for (var i = 0; i < 25; i++) testArray.push("")
req.body.addresses = testArray
const result = await convertAddressBulk(req, res)
//console.log(`result: ${util.inspect(result)}`)
assert.hasAllKeys(result, ["error"])
assert.include(result.error, "Array too large")
})
it("should error on malformed address", async () => {
try {
req.body.addresses = ["bitcoincash:qzs02v05l7qs5s5dwuj0cx5ehjm2c"]
await convertAddressBulk(req, res)
assert.equal(true, false, "Unsupported address format")
} catch (err) {
// console.log(`err.message: ${util.inspect(err.message)}`)
assert.include(err.message, `Unsupported address format`)
}
})
it("should validate array with single element", async () => {
req.body.addresses = [
"bitcoincash:qzs02v05l7qs5s24srqju498qu55dwuj0cx5ehjm2c"
]
const result = await convertAddressBulk(req, res)
//console.log(`result: ${util.inspect(result)}`)
assert.isArray(result)
assert.hasAllKeys(result[0], [
"slpAddress",
"cashAddress",
"legacyAddress"
])
})
it("should validate array with multiple elements", async () => {
req.body.addresses = [
"bitcoincash:qzs02v05l7qs5s24srqju498qu55dwuj0cx5ehjm2c",
"bitcoincash:qrehqueqhw629p6e57994436w730t4rzasnly00ht0"
]
const result = await convertAddressBulk(req, res)
//console.log(`result: ${util.inspect(result)}`)
assert.isArray(result)
assert.hasAllKeys(result[0], [
"slpAddress",
"cashAddress",
"legacyAddress"
])
})
})
describe("validateBulk()", () => {
const validateBulk = slpRoute.testableComponents.validateBulk
it("should throw 400 if txid array is empty", async () => {
const result = await validateBulk(req, res)
// console.log(`result: ${util.inspect(result)}`)
assert.hasAllKeys(result, ["error"])
assert.include(result.error, "txids needs to be an array")
assert.equal(res.statusCode, 400)
})
it("should throw 400 error if array is too large", async () => {
const testArray = []
for (var i = 0; i < 25; i++) testArray.push("")
req.body.txids = testArray
const result = await validateBulk(req, res)
//console.log(`result: ${util.inspect(result)}`)
assert.hasAllKeys(result, ["error"])
assert.include(result.error, "Array too large")
})
it("should validate array with single element", async () => {
// Mock the RPC call for unit tests.
if (process.env.TEST === "unit") {
sandbox
.stub(slpRoute.testableComponents, "isValidSlpTxid")
.resolves(true)
}
req.body.txids = [
"78d57a82a0dd9930cc17843d9d06677f267777dd6b25055bad0ae43f1b884091"
]
const result = await validateBulk(req, res)
// console.log(`result: ${util.inspect(result)}`)
assert.isArray(result)
assert.hasAllKeys(result[0], ["txid", "valid"])
})
it("should validate array with two elements", async () => {
// Mock the RPC call for unit tests.
if (process.env.TEST === "unit") {
sandbox
.stub(slpRoute.testableComponents, "isValidSlpTxid")
.resolves(true)
}
req.body.txids = [
"78d57a82a0dd9930cc17843d9d06677f267777dd6b25055bad0ae43f1b884091",
"82d996847a861b08b1601284ef7d40a1777d019154a6c4ed11571609dd3555ac"
]
const result = await validateBulk(req, res)
// console.log(`result: ${util.inspect(result)}`)
assert.isArray(result)
assert.hasAllKeys(result[0], ["txid", "valid"])
assert.equal(result.length, 2)
})
})
describe("tokenStatsSingle()", () => {
const tokenStatsSingle = slpRoute.testableComponents.tokenStats
it("should throw 400 if tokenID is empty", async () => {
req.params.tokenId = ""
const result = await tokenStatsSingle(req, res)
// console.log(`result: ${util.inspect(result)}`)
assert.hasAllKeys(result, ["error"])
assert.include(result.error, "tokenId can not be empty")
})
//
it("should get token stats for tokenId", async () => {
// Mock the RPC call for unit tests.
if (process.env.TEST === "unit") {
nock(`${process.env.SLPDB_URL}`)
.get(uri => uri.includes("/"))
.reply(200, {
t: [
{
tokenDetails: mockData.mockTokenDetails,
tokenStats: mockData.mockTokenStats
}
]
})
}
req.params.tokenId =
"37279c7dc81ceb34d12f03344b601c582e931e05d0e552c29c428bfa39d39af3"
const result = await tokenStatsSingle(req, res)
// console.log(`result: ${util.inspect(result)}`)
assert.hasAllKeys(result, [
"blockCreated",
"blockLastActiveMint",
"blockLastActiveSend",
"containsBaton",
"initialTokenQty",
"mintingBatonStatus",
"circulatingSupply",
"decimals",
"documentHash",
"versionType",
"timestamp",
"documentUri",
"name",
"symbol",
"id",
"totalBurned",
"totalMinted",
"txnsSinceGenesis",
"validAddresses"
])
})
})
describe("balancesForTokenSingle()", () => {
const balancesForTokenSingle =
slpRoute.testableComponents.balancesForTokenSingle
it("should throw 400 if tokenID is empty", async () => {
req.params.tokenId = ""
const result = await balancesForTokenSingle(req, res)
//console.log(`result: ${util.inspect(result)}`)
assert.hasAllKeys(result, ["error"])
assert.include(result.error, "tokenId can not be empty")
})
//
it("should get balances for tokenId", async () => {
// Mock the RPC call for unit tests.
if (process.env.TEST === "unit") {
nock(`${process.env.SLPDB_URL}`)
.get(uri => uri.includes("/"))
.reply(200, {
a: [mockData.mockBalance]
})
}
req.params.tokenId =
"37279c7dc81ceb34d12f03344b601c582e931e05d0e552c29c428bfa39d39af3"
const result = await balancesForTokenSingle(req, res)
// console.log(`result: ${util.inspect(result)}`)
assert.hasAllKeys(result[0], ["tokenId", "slpAddress", "tokenBalance"])
})
})
describe("txDetails()", () => {
let txDetails = slpRoute.testableComponents.txDetails
it("should throw 400 if txid is empty", async () => {
const result = await txDetails(req, res)
//console.log(`result: ${util.inspect(result)}`)
assert.hasAllKeys(result, ["error"])
assert.include(result.error, "txid can not be empty")
})
it("should throw 400 for malformed txid", async () => {
req.params.txid =
"57b3082a2bf269b3d6f40fee7fb9c664e8256a88ca5ee2697c05b9457"
const result = await txDetails(req, res)
//console.log(`result: ${util.inspect(result)}`)
assert.hasAllKeys(result, ["error"])
assert.include(result.error, "This is not a txid")
})
it("should throw 400 for non-existant txid", async () => {
// Integration test
if (process.env.TEST !== "unit") {
req.params.txid =
"57b3082a2bf269b3d6f40fee7fb9c664e8256a88ca5ee2697c05b94578223333"
const result = await txDetails(req, res)
//console.log(`result: ${util.inspect(result)}`)
assert.hasAllKeys(result, ["error"])
assert.include(result.error, "TXID not found")
}
})
it("should get tx details with token info", async () => {
if (process.env.TEST === "unit") {
// Mock the slpjs library for unit tests.
pathStub.BitboxNetwork = slpjsMock.BitboxNetwork
txDetails = slpRouteStub.testableComponents.txDetails
}
req.params.txid =
"57b3082a2bf269b3d6f40fee7fb9c664e8256a88ca5ee2697c05b9457822d446"
const result = await txDetails(req, res)
//console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.hasAnyKeys(result, ["tokenIsValid", "tokenInfo"])
})
})
describe("txsTokenIdAddressSingle()", () => {
const txsTokenIdAddressSingle =
slpRoute.testableComponents.txsTokenIdAddressSingle
it("should throw 400 if tokenId is empty", async () => {
req.params.tokenId = ""
const result = await txsTokenIdAddressSingle(req, res)
//console.log(`result: ${util.inspect(result)}`)
assert.hasAllKeys(result, ["error"])
assert.include(result.error, "tokenId can not be empty")
})
it("should throw 400 if address is empty", async () => {
req.params.tokenId =
"495322b37d6b2eae81f045eda612b95870a0c2b6069c58f70cf8ef4e6a9fd43a"
req.params.address = ""
const result = await txsTokenIdAddressSingle(req, res)
//console.log(`result: ${util.inspect(result)}`)
assert.hasAllKeys(result, ["error"])
assert.include(result.error, "address can not be empty")
})
/*
it("should get tx details with tokenId and address", async () => {
if (process.env.TEST === "unit") {
nock(`${process.env.SLPDB_URL}`)
.get(uri => uri.includes("/"))
.reply(200, {
c: mockData.mockTransactions
})
}
//req.params.tokenId =
// "37279c7dc81ceb34d12f03344b601c582e931e05d0e552c29c428bfa39d39af3"
//req.params.address = "slptest:qr83cu3p7yg9yac7qthwm0nul2ev2kukvsqmes3vl0"
req.params.tokenId =
"7ac7f4bb50b019fe0f5c81e3fc13fc0720e130282ea460768cafb49785eb2796"
req.params.address = "slptest:qpwa35xq0q0cnmdu0rwzkct369hddzsqpsqdzw6h9h"
const result = await txsTokenIdAddressSingle(req, res)
console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.hasAnyKeys(result[0], ["txid", "tokenDetails"])
})
*/
})
})
+333
View File
@@ -0,0 +1,333 @@
/*
TESTS FOR THE TRANSACTION.TS LIBRARY
This test file uses the environment variable TEST to switch between unit
and integration tests. By default, TEST is set to 'unit'. Set this variable
to 'integration' to run the tests against BCH mainnet.
TODO:
-See "should throw an error for an invalid txid" for detailsSingle:
--The error handler should be refactored to return an intelligent error message,
instead of the 503 error it is returning now.
*/
"use strict"
const chai = require("chai")
const assert = chai.assert
const transactionRoute = require("../../src/routes/v3/transaction")
const nock = require("nock") // HTTP mocking
let originalEnvVars // Used during transition from integration to unit tests.
// Mocking data.
const { mockReq, mockRes } = require("./mocks/express-mocks")
const mockData = require("./mocks/transaction-mocks")
// Used for debugging.
const util = require("util")
util.inspect.defaultOptions = { depth: 1 }
describe("#Transactions", () => {
let req, res
before(() => {
// Save existing environment variables.
originalEnvVars = {
BITCOINCOM_BASEURL: process.env.BITCOINCOM_BASEURL,
RPC_BASEURL: process.env.RPC_BASEURL,
RPC_USERNAME: process.env.RPC_USERNAME,
RPC_PASSWORD: process.env.RPC_PASSWORD
}
// Set default environment variables for unit tests.
if (!process.env.TEST) process.env.TEST = "unit"
if (process.env.TEST === "unit") {
process.env.BITCOINCOM_BASEURL = "http://fakeurl/api/"
process.env.RPC_BASEURL = "http://fakeurl/api"
process.env.RPC_USERNAME = "fakeusername"
process.env.RPC_PASSWORD = "fakepassword"
}
})
// Setup the mocks before each test.
beforeEach(() => {
// Mock the req and res objects used by Express routes.
req = mockReq
res = mockRes
// Explicitly reset the parmas and body.
req.params = {}
req.body = {}
req.query = {}
// Activate nock if it's inactive.
if (!nock.isActive()) nock.activate()
})
afterEach(() => {
// Clean up HTTP mocks.
nock.cleanAll() // clear interceptor list.
nock.restore()
})
after(() => {
// Restore any pre-existing environment variables.
process.env.BITCOINCOM_BASEURL = originalEnvVars.BITCOINCOM_BASEURL
process.env.RPC_BASEURL = originalEnvVars.RPC_BASEURL
process.env.RPC_USERNAME = originalEnvVars.RPC_USERNAME
process.env.RPC_PASSWORD = originalEnvVars.RPC_PASSWORD
})
describe("#root", async () => {
// root route handler.
const root = transactionRoute.testableComponents.root
it("should respond to GET for base route", async () => {
const result = root(req, res)
//console.log(`result: ${util.inspect(result)}`)
assert.equal(result.status, "transaction", "Returns static string")
})
})
describe("#detailsBulk", async () => {
const detailsBulk = transactionRoute.testableComponents.detailsBulk
it("should throw an error for an empty body", async () => {
req.body = {}
const result = await detailsBulk(req, res)
assert.equal(res.statusCode, 400, "HTTP status code 400 expected.")
assert.include(
result.error,
"txids needs to be an array",
"Proper error message"
)
})
it("should error on non-array single txid", async () => {
req.body = {
txids: `6f235bd3a689f03c11969cd649ccad592462ca958bc519a30194e7a67b349a40`
}
const result = await detailsBulk(req, res)
assert.equal(res.statusCode, 400, "HTTP status code 400 expected.")
assert.include(
result.error,
"txids needs to be an array",
"Proper error message"
)
})
it("should throw an error for an invalid txid", async () => {
const fakeTXID = `02v05l7qs5s24srqju498qu55dwuj0cx5ehjm2c`
// Mock the Insight URL for unit tests.
if (process.env.TEST === "unit") {
nock(`${process.env.BITCOINCOM_BASEURL}`)
.get(`/tx/${fakeTXID}`)
.reply(400, {
result: { error: "parameter 1 must be hexadecimal string" }
})
}
req.body = {
txids: [fakeTXID]
}
const result = await detailsBulk(req, res)
//console.log(`result: ${util.inspect(result)}`)
assert.equal(res.statusCode, 400, "HTTP status code 400 expected.")
})
it("should process a single txid", async () => {
const txid = `6f235bd3a689f03c11969cd649ccad592462ca958bc519a30194e7a67b349a40`
// Mock the Insight URL for unit tests.
if (process.env.TEST === "unit") {
nock(`${process.env.BITCOINCOM_BASEURL}`)
.get(`/tx/${txid}`)
.reply(200, mockData.mockDetails)
}
req.body = {
txids: [txid]
}
const result = await detailsBulk(req, res)
//console.log(`result: ${util.inspect(result)}`)
assert.isArray(result)
assert.hasAnyKeys(result[0], [
"txid",
"version",
"locktime",
"vin",
"vout",
"blockhash",
"blockheight",
"confirmations",
"time",
"blocktime",
"valueOut",
"size",
"valueIn",
"fees"
])
})
it("should process a multiple txids", async () => {
const txid1 = `6f235bd3a689f03c11969cd649ccad592462ca958bc519a30194e7a67b349a40`
const txid2 = `8d4fd4dcaa9d8051dc7d862dc23d8aa23e20b77b9c928c49380685459caa7043`
// Mock the Insight URL for unit tests.
if (process.env.TEST === "unit") {
nock(`${process.env.BITCOINCOM_BASEURL}`)
.get(`/tx/${txid1}`)
.reply(200, mockData.mockDetails)
}
// Mock the Insight URL for unit tests.
if (process.env.TEST === "unit") {
nock(`${process.env.BITCOINCOM_BASEURL}`)
.get(`/tx/${txid2}`)
.reply(200, mockData.mockDetails)
}
req.body = {
txids: [txid1, txid2]
}
const result = await detailsBulk(req, res)
//console.log(`result: ${util.inspect(result)}`)
assert.isArray(result)
assert.hasAnyKeys(result[0], [
"txid",
"version",
"locktime",
"vin",
"vout",
"blockhash",
"blockheight",
"confirmations",
"time",
"blocktime",
"valueOut",
"size",
"valueIn",
"fees"
])
})
})
describe("#detailsSingle", () => {
// details route handler.
const detailsSingle = transactionRoute.testableComponents.detailsSingle
it("should throw 400 if txid is empty", async () => {
const result = await detailsSingle(req, res)
//console.log(`result: ${util.inspect(result)}`)
assert.hasAllKeys(result, ["error"])
assert.include(result.error, "txid can not be empty")
})
it("should error on an array", async () => {
req.params.txid = [`qzs02v05l7qs5s24srqju498qu55dwuj0cx5ehjm2c`]
const result = await detailsSingle(req, res)
assert.equal(res.statusCode, 400, "HTTP status code 400 expected.")
assert.include(
result.error,
"txid can not be an array",
"Proper error message"
)
})
it("should throw an error for an invalid txid", async () => {
if (process.env.TEST !== "unit") {
req.params.txid = `02v05l7qs5s24srqju498qu55dwuj0cx5ehjm2c`
const result = await detailsSingle(req, res)
//console.log(`result: ${util.inspect(result)}`)
// The error handling code should probably be updated to respond with a better
// error message.
assert.equal(res.statusCode, 400, "HTTP status code 400 expected.")
assert.include(
result.error,
"parameter 1 must be hexadecimal string",
"Proper error message"
)
}
})
it("should throw 500 when network issues", async () => {
const savedUrl = process.env.BITCOINCOM_BASEURL
try {
req.params.txid = `6f235bd3a689f03c11969cd649ccad592462ca958bc519a30194e7a67b349a40`
// Switch the Insight URL to something that will error out.
process.env.BITCOINCOM_BASEURL = "http://fakeurl/api/"
const result = await detailsSingle(req, res)
// Restore the saved URL.
process.env.BITCOINCOM_BASEURL = savedUrl
assert.isAbove(
res.statusCode,
499,
"HTTP status code 500 or greater expected."
)
//assert.include(result.error,"Network error: Could not communicate with full node","Error message expected")
} catch (err) {
// Restore the saved URL.
process.env.BITCOINCOM_BASEURL = savedUrl
}
})
it("should get details for a single address", async () => {
const txid = `6f235bd3a689f03c11969cd649ccad592462ca958bc519a30194e7a67b349a40`
req.params.txid = txid
// Mock the Insight URL for unit tests.
if (process.env.TEST === "unit") {
nock(`${process.env.BITCOINCOM_BASEURL}`)
.get(`/tx/${txid}`)
.reply(200, mockData.mockDetails)
}
// Call the details API.
const result = await detailsSingle(req, res)
//console.log(`result: ${util.inspect(result)}`)
// Assert that required fields exist in the returned object.
assert.hasAllKeys(result, [
"txid",
"version",
"locktime",
"vin",
"vout",
"blockhash",
"blockheight",
"confirmations",
"time",
"blocktime",
"valueOut",
"size",
"valueIn",
"fees"
])
assert.isArray(result.vin)
assert.isArray(result.vout)
})
})
})
+300
View File
@@ -0,0 +1,300 @@
/*
TESTS FOR THE UTIL.TS LIBRARY
This test file uses the environment variable TEST to switch between unit
and integration tests. By default, TEST is set to 'unit'. Set this variable
to 'integration' to run the tests against BCH mainnet.
*/
"use strict"
const chai = require("chai")
const assert = chai.assert
const utilRoute = require("../../src/routes/v3/util")
const nock = require("nock") // HTTP mocking
let originalEnvVars // Used during transition from integration to unit tests.
// Mocking data.
const { mockReq, mockRes } = require("./mocks/express-mocks")
const mockData = require("./mocks/util-mocks")
// Used for debugging.
const util = require("util")
util.inspect.defaultOptions = { depth: 1 }
describe("#Util", () => {
let req, res
before(() => {
// Save existing environment variables.
originalEnvVars = {
BITCOINCOM_BASEURL: process.env.BITCOINCOM_BASEURL,
RPC_BASEURL: process.env.RPC_BASEURL,
RPC_USERNAME: process.env.RPC_USERNAME,
RPC_PASSWORD: process.env.RPC_PASSWORD
}
// Set default environment variables for unit tests.
if (!process.env.TEST) process.env.TEST = "unit"
if (process.env.TEST === "unit") {
process.env.BITCOINCOM_BASEURL = "http://fakeurl/api/"
process.env.RPC_BASEURL = "http://fakeurl/api"
process.env.RPC_USERNAME = "fakeusername"
process.env.RPC_PASSWORD = "fakepassword"
}
})
// Setup the mocks before each test.
beforeEach(() => {
// Mock the req and res objects used by Express routes.
req = mockReq
res = mockRes
// Explicitly reset the parmas and body.
req.params = {}
req.body = {}
req.query = {}
// Activate nock if it's inactive.
if (!nock.isActive()) nock.activate()
})
afterEach(() => {
// Clean up HTTP mocks.
nock.cleanAll() // clear interceptor list.
nock.restore()
})
after(() => {
// Restore any pre-existing environment variables.
process.env.BITCOINCOM_BASEURL = originalEnvVars.BITCOINCOM_BASEURL
process.env.RPC_BASEURL = originalEnvVars.RPC_BASEURL
process.env.RPC_USERNAME = originalEnvVars.RPC_USERNAME
process.env.RPC_PASSWORD = originalEnvVars.RPC_PASSWORD
})
describe("#root", async () => {
// root route handler.
const root = utilRoute.testableComponents.root
it("should respond to GET for base route", async () => {
const result = root(req, res)
//console.log(`result: ${util.inspect(result)}`)
assert.equal(result.status, "util", "Returns static string")
})
})
describe("#validateAddressSingle", async () => {
const validateAddress = utilRoute.testableComponents.validateAddressSingle
it("should throw an error for an empty address", async () => {
const result = await validateAddress(req, res)
assert.equal(res.statusCode, 400, "HTTP status code 400 expected.")
assert.include(
result.error,
"address can not be empty",
"Proper error message"
)
})
it("should throw 503 when network issues", async () => {
// Save the existing RPC URL.
const savedUrl2 = process.env.RPC_BASEURL
// Manipulate the URL to cause a 500 network error.
process.env.RPC_BASEURL = "http://fakeurl/api/"
req.params.address = `bchtest:qqqk4y6lsl5da64sg5qc3xezmplyu5kmpyz2ysaa5y`
const result = await validateAddress(req, res)
//console.log(`result: ${util.inspect(result)}`)
// Restore the saved URL.
process.env.RPC_BASEURL = savedUrl2
assert.isAbove(
res.statusCode,
499,
"HTTP status code 500 or greater expected."
)
//assert.include(result.error,"Network error: Could not communicate with full node","Error message expected")
})
it("should validate address", async () => {
// Mock the RPC call for unit tests.
if (process.env.TEST === "unit") {
nock(`${process.env.RPC_BASEURL}`)
.post(``)
.reply(200, { result: mockData.mockAddress })
}
req.params.address = `bchtest:qqqk4y6lsl5da64sg5qc3xezmplyu5kmpyz2ysaa5y`
const result = await validateAddress(req, res)
//console.log(`result: ${util.inspect(result)}`)
assert.hasAllKeys(result, [
"isvalid",
"address",
"scriptPubKey",
"ismine",
"iswatchonly",
"isscript"
])
})
})
describe("#validateAddressBulk", async () => {
const validateAddressBulk = utilRoute.testableComponents.validateAddressBulk
it("should throw an error for an empty body", async () => {
const result = await validateAddressBulk(req, res)
assert.equal(res.statusCode, 400, "HTTP status code 400 expected.")
assert.include(
result.error,
"addresses needs to be an array. Use GET for single address.",
"Proper error message"
)
})
it("should error on non-array single address", async () => {
req.body = {
addresses: `bchtest:qqqk4y6lsl5da64sg5qc3xezmplyu5kmpyz2ysaa5y`
}
const result = await validateAddressBulk(req, res)
assert.equal(res.statusCode, 400, "HTTP status code 400 expected.")
assert.include(
result.error,
"addresses needs to be an array. Use GET for single address.",
"Proper error message"
)
})
it("should throw 400 error if addresses array is too large", async () => {
const testArray = []
for (var i = 0; i < 25; i++) testArray.push("")
req.body.addresses = testArray
const result = await validateAddressBulk(req, res)
//console.log(`result: ${util.inspect(result)}`)
assert.hasAllKeys(result, ["error"])
assert.include(result.error, "Array too large")
})
it("should error on invalid address", async () => {
req.body = {
addresses: [`bchtest:qqqk4y6lsl5da64sg5qc3xezmpl`]
}
const result = await validateAddressBulk(req, res)
assert.equal(res.statusCode, 400, "HTTP status code 400 expected.")
assert.include(
result.error,
"Invalid BCH address. Double check your address is valid",
"Proper error message"
)
})
it("should error on mainnet address when using testnet", async () => {
req.body = {
addresses: [`bitcoincash:qrcc3jsqpgwqcdru70sk54sd0g3l04q7c53ycm6ucj`]
}
const result = await validateAddressBulk(req, res)
assert.equal(res.statusCode, 400, "HTTP status code 400 expected.")
assert.include(
result.error,
"Invalid network. Trying to use a testnet address on mainnet, or vice versa.",
"Proper error message"
)
})
it("should throw 503 when network issues", async () => {
// Save the existing RPC URL.
const savedUrl2 = process.env.RPC_BASEURL
// Manipulate the URL to cause a 500 network error.
process.env.RPC_BASEURL = "http://fakeurl/api/"
req.body.addresses = [
`bchtest:qqqk4y6lsl5da64sg5qc3xezmplyu5kmpyz2ysaa5y`
]
const result = await validateAddressBulk(req, res)
//console.log(`result: ${util.inspect(result)}`)
// Restore the saved URL.
process.env.RPC_BASEURL = savedUrl2
assert.isAbove(
res.statusCode,
499,
"HTTP status code 500 or greater expected."
)
})
it("should validate a single address", async () => {
// Mock the RPC call for unit tests.
if (process.env.TEST === "unit") {
nock(`${process.env.RPC_BASEURL}`)
.post(``)
.reply(200, { result: mockData.mockAddress })
}
req.body.addresses = [
`bchtest:qqqk4y6lsl5da64sg5qc3xezmplyu5kmpyz2ysaa5y`
]
const result = await validateAddressBulk(req, res)
//console.log(`result: ${util.inspect(result)}`)
assert.isArray(result)
assert.hasAllKeys(result[0], [
"isvalid",
"address",
"scriptPubKey",
"ismine",
"iswatchonly",
"isscript"
])
})
it("should validate a multiple addresses", async () => {
// Mock the RPC call for unit tests.
if (process.env.TEST === "unit") {
nock(`${process.env.RPC_BASEURL}`)
.post(``)
.times(2)
.reply(200, { result: mockData.mockAddress })
}
req.body.addresses = [
`bchtest:qqqk4y6lsl5da64sg5qc3xezmplyu5kmpyz2ysaa5y`,
`bchtest:qqqk4y6lsl5da64sg5qc3xezmplyu5kmpyz2ysaa5y`
]
const result = await validateAddressBulk(req, res)
//console.log(`result: ${util.inspect(result)}`)
assert.isArray(result)
assert.hasAllKeys(result[0], [
"isvalid",
"address",
"scriptPubKey",
"ismine",
"iswatchonly",
"isscript"
])
})
})
})