mirror of
https://github.com/fullstack-cash/bch-api.git
synced 2026-09-21 16:52:04 -07:00
Adding old tests back in
This commit is contained in:
+1373
File diff suppressed because it is too large
Load Diff
@@ -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
@@ -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"
|
||||
])
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -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)
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -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
|
||||
}
|
||||
@@ -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
@@ -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"])
|
||||
})
|
||||
*/
|
||||
})
|
||||
})
|
||||
@@ -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
@@ -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"
|
||||
])
|
||||
})
|
||||
})
|
||||
})
|
||||
Reference in New Issue
Block a user