2021-05-18 22:05:28 -07:00
|
|
|
/*
|
|
|
|
|
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 sinon = require('sinon')
|
|
|
|
|
// const proxyquire = require('proxyquire').noPreserveCache()
|
|
|
|
|
// const axios = require('axios')
|
|
|
|
|
|
|
|
|
|
// Save existing environment variables.
|
|
|
|
|
// Used during transition from integration to unit tests.
|
|
|
|
|
|
|
|
|
|
// eslint-disable-next-line no-unused-vars
|
|
|
|
|
let mockServerUrl
|
|
|
|
|
const 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/'
|
2022-01-08 15:52:43 -08:00
|
|
|
// mockServerUrl = 'http://fakeurl'
|
2021-05-18 22:05:28 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Prepare the slpRoute for stubbing dependcies on slpjs.
|
2021-05-19 08:01:17 -07:00
|
|
|
const SlpRoute = require('../../src/routes/v5/slp')
|
2021-05-18 22:05:28 -07:00
|
|
|
const slpRoute = new SlpRoute()
|
|
|
|
|
|
|
|
|
|
// const pathStub = {} // Used to stub methods within slpjs.
|
2021-05-19 08:01:17 -07:00
|
|
|
// const slpRouteStub = proxyquire('../../src/routes/v5/slp', { slpjs: pathStub })
|
2021-05-18 22:05:28 -07:00
|
|
|
|
|
|
|
|
// 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
|
|
|
|
|
let sandbox
|
|
|
|
|
|
|
|
|
|
before(() => {})
|
|
|
|
|
|
|
|
|
|
// 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 = {}
|
|
|
|
|
|
|
|
|
|
sandbox = sinon.createSandbox()
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
afterEach(() => {
|
|
|
|
|
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.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('balancesForAddress()', () => {
|
|
|
|
|
const balancesForAddress = slpRoute.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 = 'slptest:qzcvpw3ah7r880d49wsqzrsl90pg0rqjjurmj3g4nk'
|
|
|
|
|
|
|
|
|
|
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 =
|
|
|
|
|
'simpleledger:qpujxqra3jmdlzzapwmmt7uspr7q0c9ff5me5fdrdn'
|
|
|
|
|
|
|
|
|
|
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'
|
|
|
|
|
)
|
|
|
|
|
})
|
2022-02-01 06:09:14 -08:00
|
|
|
|
2021-05-18 22:05:28 -07:00
|
|
|
it('returns proper error when downstream service stalls', async () => {
|
|
|
|
|
// Mock the timeout error.
|
|
|
|
|
sandbox.stub(slpRoute.axios, 'request').throws({ code: 'ECONNABORTED' })
|
|
|
|
|
|
|
|
|
|
req.params.address =
|
|
|
|
|
'simpleledger:qpujxqra3jmdlzzapwmmt7uspr7q0c9ff5me5fdrdn'
|
|
|
|
|
|
|
|
|
|
const result = await balancesForAddress(req, res)
|
|
|
|
|
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
|
|
|
|
|
|
|
|
|
assert.isAbove(res.statusCode, 499, 'HTTP status code 503 expected.')
|
|
|
|
|
assert.include(
|
|
|
|
|
result.error,
|
|
|
|
|
'Could not communicate with full node',
|
|
|
|
|
'Error message expected'
|
|
|
|
|
)
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
it('returns proper error when downstream service is down', async () => {
|
|
|
|
|
// Mock the timeout error.
|
|
|
|
|
sandbox.stub(slpRoute.axios, 'request').throws({ code: 'ECONNREFUSED' })
|
|
|
|
|
|
|
|
|
|
req.params.address =
|
|
|
|
|
'simpleledger:qpujxqra3jmdlzzapwmmt7uspr7q0c9ff5me5fdrdn'
|
|
|
|
|
|
|
|
|
|
const result = await balancesForAddress(req, res)
|
|
|
|
|
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
|
|
|
|
|
|
|
|
|
assert.isAbove(res.statusCode, 499, 'HTTP status code 503 expected.')
|
|
|
|
|
assert.include(
|
|
|
|
|
result.error,
|
|
|
|
|
'Could not communicate with full node',
|
|
|
|
|
'Error message expected'
|
|
|
|
|
)
|
|
|
|
|
})
|
|
|
|
|
|
2022-02-01 06:09:14 -08:00
|
|
|
// it('should get token balance for an address', async () => {
|
|
|
|
|
// // Mock the RPC call for unit tests.
|
|
|
|
|
// if (process.env.TEST === 'unit') {
|
|
|
|
|
// sandbox.stub(slpRoute.axios, 'request').resolves({
|
|
|
|
|
// data: mockData.mockSingleAddress
|
|
|
|
|
// })
|
|
|
|
|
// }
|
|
|
|
|
//
|
|
|
|
|
// req.params.address =
|
|
|
|
|
// 'simpleledger:qpujxqra3jmdlzzapwmmt7uspr7q0c9ff5me5fdrdn'
|
|
|
|
|
//
|
|
|
|
|
// const result = await balancesForAddress(req, res)
|
|
|
|
|
// // console.log(`result: ${util.inspect(result)}`)
|
|
|
|
|
//
|
|
|
|
|
// assert.isArray(result)
|
|
|
|
|
//
|
|
|
|
|
// assert.property(result[0], 'tokenId')
|
|
|
|
|
// assert.property(result[0], 'balanceString')
|
|
|
|
|
// assert.property(result[0], 'slpAddress')
|
|
|
|
|
// assert.property(result[0], 'balance')
|
|
|
|
|
// assert.property(result[0], 'decimalCount')
|
|
|
|
|
//
|
|
|
|
|
// assert.isNumber(result[0].balance)
|
|
|
|
|
// assert.isNumber(result[0].decimalCount)
|
|
|
|
|
// })
|
2021-05-18 22:05:28 -07:00
|
|
|
})
|
|
|
|
|
|
|
|
|
|
describe('balancesForAddressBulk()', () => {
|
|
|
|
|
const balancesForAddressBulk = slpRoute.balancesForAddressBulk
|
|
|
|
|
|
|
|
|
|
it('should throw 400 if addresses is empty', async () => {
|
|
|
|
|
const result = await balancesForAddressBulk(req, res)
|
|
|
|
|
// console.log(`result: ${util.inspect(result)}`)
|
|
|
|
|
|
|
|
|
|
assert.hasAllKeys(result, ['error'])
|
|
|
|
|
assert.include(result.error, 'addresses needs to be an array')
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
it('should throw 400 if address is invalid', async () => {
|
|
|
|
|
req.body.addresses = ['badAddress']
|
|
|
|
|
|
|
|
|
|
const result = await balancesForAddressBulk(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.body.addresses = [
|
|
|
|
|
'slptest:qzcvpw3ah7r880d49wsqzrsl90pg0rqjjurmj3g4nk'
|
|
|
|
|
]
|
|
|
|
|
|
|
|
|
|
const result = await balancesForAddressBulk(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.body.addresses = [
|
|
|
|
|
'simpleledger:qpujxqra3jmdlzzapwmmt7uspr7q0c9ff5me5fdrdn'
|
|
|
|
|
]
|
|
|
|
|
|
|
|
|
|
const result = await balancesForAddressBulk(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'
|
|
|
|
|
)
|
|
|
|
|
})
|
2022-02-01 09:06:10 -08:00
|
|
|
|
2021-05-18 22:05:28 -07:00
|
|
|
it('returns proper error when downstream service stalls', async () => {
|
|
|
|
|
// Mock the timeout error.
|
|
|
|
|
sandbox.stub(slpRoute.axios, 'request').throws({ code: 'ECONNABORTED' })
|
|
|
|
|
|
|
|
|
|
req.body.addresses = [
|
|
|
|
|
'simpleledger:qpujxqra3jmdlzzapwmmt7uspr7q0c9ff5me5fdrdn'
|
|
|
|
|
]
|
|
|
|
|
const result = await balancesForAddressBulk(req, res)
|
|
|
|
|
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
|
|
|
|
|
|
|
|
|
assert.isAbove(res.statusCode, 499, 'HTTP status code 503 expected.')
|
|
|
|
|
assert.include(
|
|
|
|
|
result.error,
|
|
|
|
|
'Could not communicate with full node',
|
|
|
|
|
'Error message expected'
|
|
|
|
|
)
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
it('returns proper error when downstream service is down', async () => {
|
|
|
|
|
// Mock the timeout error.
|
|
|
|
|
sandbox.stub(slpRoute.axios, 'request').throws({ code: 'ECONNREFUSED' })
|
|
|
|
|
|
|
|
|
|
req.body.addresses = [
|
|
|
|
|
'simpleledger:qpujxqra3jmdlzzapwmmt7uspr7q0c9ff5me5fdrdn'
|
|
|
|
|
]
|
|
|
|
|
const result = await balancesForAddressBulk(req, res)
|
|
|
|
|
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
|
|
|
|
|
|
|
|
|
assert.isAbove(res.statusCode, 499, 'HTTP status code 503 expected.')
|
|
|
|
|
assert.include(
|
|
|
|
|
result.error,
|
|
|
|
|
'Could not communicate with full node',
|
|
|
|
|
'Error message expected'
|
|
|
|
|
)
|
|
|
|
|
})
|
|
|
|
|
|
2022-02-01 09:06:10 -08:00
|
|
|
// Only run as an integration test. Too complex to stub accurately.
|
|
|
|
|
// if (process.env.TEST !== 'unit') {
|
|
|
|
|
// it('should get token balance for an address', async () => {
|
|
|
|
|
// req.body.addresses = [
|
|
|
|
|
// 'simpleledger:qpujxqra3jmdlzzapwmmt7uspr7q0c9ff5me5fdrdn'
|
|
|
|
|
// ]
|
|
|
|
|
//
|
|
|
|
|
// const result = await balancesForAddressBulk(req, res)
|
|
|
|
|
// // console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
|
|
|
|
//
|
|
|
|
|
// assert.isArray(result)
|
|
|
|
|
// assert.isArray(result[0])
|
|
|
|
|
// assert.hasAnyKeys(result[0][0], [
|
|
|
|
|
// 'tokenId',
|
|
|
|
|
// 'balance',
|
|
|
|
|
// 'balanceString',
|
|
|
|
|
// 'slpAddress',
|
|
|
|
|
// 'decimalCount'
|
|
|
|
|
// ])
|
|
|
|
|
// })
|
|
|
|
|
// }
|
2021-05-18 22:05:28 -07:00
|
|
|
})
|
|
|
|
|
|
|
|
|
|
describe('#validate2Single', () => {
|
|
|
|
|
it('should throw 400 if txid is empty', async () => {
|
|
|
|
|
req.params.txid = ''
|
|
|
|
|
const result = await slpRoute.validate2Single(req, res)
|
|
|
|
|
// console.log(`result: ${util.inspect(result)}`)
|
|
|
|
|
|
|
|
|
|
assert.hasAllKeys(result, ['error'])
|
|
|
|
|
assert.include(result.error, 'txid can not be empty')
|
|
|
|
|
})
|
|
|
|
|
|
2022-01-23 07:52:38 -08:00
|
|
|
/*
|
2021-05-18 22:05:28 -07:00
|
|
|
it('should invalidate a known invalid TXID', async () => {
|
|
|
|
|
if (process.env.TEST === 'unit') {
|
|
|
|
|
// Mock to prevent live network connection.
|
|
|
|
|
sandbox
|
|
|
|
|
.stub(slpRoute.axios, 'request')
|
|
|
|
|
.resolves({ data: { isValid: false } })
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const txid =
|
|
|
|
|
'f7e5199ef6669ad4d078093b3ad56e355b6ab84567e59ad0f08a5ad0244f783a'
|
|
|
|
|
|
|
|
|
|
req.params.txid = txid
|
|
|
|
|
const result = await slpRoute.validate2Single(req, res)
|
|
|
|
|
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
|
|
|
|
|
|
|
|
|
assert.equal(result.txid, txid)
|
|
|
|
|
assert.equal(result.isValid, false)
|
|
|
|
|
})
|
2022-01-23 07:52:38 -08:00
|
|
|
*/
|
|
|
|
|
/*
|
2021-05-18 22:05:28 -07:00
|
|
|
it('should validate a known valid TXID', async () => {
|
|
|
|
|
if (process.env.TEST === 'unit') {
|
|
|
|
|
// Mock to prevent live network connection.
|
|
|
|
|
sandbox
|
|
|
|
|
.stub(slpRoute.axios, 'request')
|
|
|
|
|
.resolves({ data: { isValid: true } })
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const txid =
|
|
|
|
|
'3a4b628cbcc183ab376d44ce5252325f042268307ffa4a53443e92b6d24fb488'
|
|
|
|
|
|
|
|
|
|
req.params.txid = txid
|
|
|
|
|
const result = await slpRoute.validate2Single(req, res)
|
|
|
|
|
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
|
|
|
|
|
|
|
|
|
assert.equal(result.txid, txid)
|
|
|
|
|
assert.equal(result.isValid, true)
|
|
|
|
|
})
|
2022-01-23 07:52:38 -08:00
|
|
|
*/
|
2021-05-18 22:05:28 -07:00
|
|
|
// This test can only be run as a mocked unit test. It's too inconsistent
|
|
|
|
|
// to run as an integration test, due to the caching built into slp-validate.
|
|
|
|
|
if (process.env.TEST === 'unit') {
|
|
|
|
|
it('should cancel if validation takes too long', async () => {
|
|
|
|
|
// Mock the timeout error.
|
|
|
|
|
sandbox.stub(slpRoute.axios, 'request').throws({
|
|
|
|
|
code: 'ECONNABORTED'
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
const txid =
|
|
|
|
|
'eacb1085dfa296fef6d4ae2c0f4529a1bef096dd2325bdcc6dcb5241b3bdb579'
|
|
|
|
|
|
|
|
|
|
req.params.txid = txid
|
|
|
|
|
const result = await slpRoute.validate2Single(req, res)
|
|
|
|
|
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
|
|
|
|
|
|
|
|
|
assert.isAbove(res.statusCode, 499, 'HTTP status code 503 expected.')
|
|
|
|
|
assert.include(
|
|
|
|
|
result.error,
|
|
|
|
|
'Could not communicate with full node',
|
|
|
|
|
'Error message expected'
|
|
|
|
|
)
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
describe('#validateSingle', () => {
|
|
|
|
|
it('should throw 400 if txid is empty', async () => {
|
|
|
|
|
req.params.txid = ''
|
|
|
|
|
const result = await slpRoute.validateSingle(req, res)
|
|
|
|
|
// console.log(`result: ${util.inspect(result)}`)
|
|
|
|
|
|
|
|
|
|
assert.hasAllKeys(result, ['error'])
|
|
|
|
|
assert.include(result.error, 'txid can not be empty')
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
it('should invalidate a known invalid TXID', async () => {
|
|
|
|
|
if (process.env.TEST === 'unit') {
|
|
|
|
|
// Mock to prevent live network connection.
|
|
|
|
|
sandbox.stub(slpRoute.axios, 'request').resolves({
|
|
|
|
|
data: {
|
|
|
|
|
c: [],
|
|
|
|
|
u: []
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const txid =
|
|
|
|
|
'f7e5199ef6669ad4d078093b3ad56e355b6ab84567e59ad0f08a5ad0244f783a'
|
|
|
|
|
|
|
|
|
|
req.params.txid = txid
|
|
|
|
|
const result = await slpRoute.validateSingle(req, res)
|
|
|
|
|
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
|
|
|
|
|
|
|
|
|
assert.equal(result.txid, txid)
|
|
|
|
|
assert.equal(result.valid, null)
|
|
|
|
|
})
|
|
|
|
|
|
2022-02-01 06:09:14 -08:00
|
|
|
// it('should validate a known valid TXID', async () => {
|
|
|
|
|
// if (process.env.TEST === 'unit') {
|
|
|
|
|
// // Mock to prevent live network connection.
|
|
|
|
|
// sandbox
|
|
|
|
|
// .stub(slpRoute.axios, 'request')
|
|
|
|
|
// .resolves({ data: mockData.mockSingleValidTxid })
|
|
|
|
|
// }
|
|
|
|
|
//
|
|
|
|
|
// const txid =
|
|
|
|
|
// '77872738b6bddee6c0cbdb9509603de20b15d4f6b26602f629417aec2f5d5e8d'
|
|
|
|
|
//
|
|
|
|
|
// req.params.txid = txid
|
|
|
|
|
// const result = await slpRoute.validateSingle(req, res)
|
|
|
|
|
// // console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
|
|
|
|
//
|
|
|
|
|
// assert.equal(result.txid, txid)
|
|
|
|
|
// assert.equal(result.valid, true)
|
|
|
|
|
// })
|
2021-05-18 22:05:28 -07:00
|
|
|
|
|
|
|
|
if (process.env.TEST === 'unit') {
|
|
|
|
|
it('should cancel if validation takes too long', async () => {
|
|
|
|
|
// Mock the timeout error.
|
|
|
|
|
sandbox.stub(slpRoute.axios, 'request').throws({
|
|
|
|
|
code: 'ECONNABORTED'
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
const txid =
|
|
|
|
|
'eacb1085dfa296fef6d4ae2c0f4529a1bef096dd2325bdcc6dcb5241b3bdb579'
|
|
|
|
|
|
|
|
|
|
req.params.txid = txid
|
|
|
|
|
const result = await slpRoute.validateSingle(req, res)
|
|
|
|
|
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
|
|
|
|
|
|
|
|
|
assert.isAbove(res.statusCode, 499, 'HTTP status code 503 expected.')
|
|
|
|
|
assert.include(
|
|
|
|
|
result.error,
|
|
|
|
|
'Could not communicate with full node',
|
|
|
|
|
'Error message expected'
|
|
|
|
|
)
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
describe('#validate3Single', () => {
|
|
|
|
|
it('should throw 400 if txid is empty', async () => {
|
|
|
|
|
req.params.txid = ''
|
|
|
|
|
const result = await slpRoute.validate3Single(req, res)
|
|
|
|
|
// console.log(`result: ${util.inspect(result)}`)
|
|
|
|
|
|
|
|
|
|
assert.hasAllKeys(result, ['error'])
|
|
|
|
|
assert.include(result.error, 'txid can not be empty')
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
it('should invalidate a known invalid TXID', async () => {
|
|
|
|
|
if (process.env.TEST === 'unit') {
|
|
|
|
|
// Mock to prevent live network connection.
|
|
|
|
|
sandbox.stub(slpRoute.axios, 'request').resolves({
|
|
|
|
|
data: {
|
|
|
|
|
c: [],
|
|
|
|
|
u: []
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const txid =
|
|
|
|
|
'f7e5199ef6669ad4d078093b3ad56e355b6ab84567e59ad0f08a5ad0244f783a'
|
|
|
|
|
|
|
|
|
|
req.params.txid = txid
|
|
|
|
|
const result = await slpRoute.validate3Single(req, res)
|
|
|
|
|
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
|
|
|
|
|
|
|
|
|
assert.equal(result.txid, txid)
|
|
|
|
|
assert.equal(result.valid, null)
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
it('should validate a known valid TXID', async () => {
|
|
|
|
|
if (process.env.TEST === 'unit') {
|
|
|
|
|
// Mock to prevent live network connection.
|
|
|
|
|
sandbox
|
|
|
|
|
.stub(slpRoute.axios, 'request')
|
|
|
|
|
.resolves({ data: mockData.mockSingleValidTxid })
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const txid =
|
|
|
|
|
'cdfed769ef7b69e09be06d2821b88598d9a5d711b8f9bd369763c78b7a578fbc'
|
|
|
|
|
|
|
|
|
|
req.params.txid = txid
|
|
|
|
|
const result = await slpRoute.validate3Single(req, res)
|
|
|
|
|
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
|
|
|
|
|
|
|
|
|
// assert.equal(result.txid, txid)
|
|
|
|
|
assert.equal(result.valid, true)
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
if (process.env.TEST === 'unit') {
|
|
|
|
|
it('should cancel if validation takes too long', async () => {
|
|
|
|
|
// Mock the timeout error.
|
|
|
|
|
sandbox.stub(slpRoute.axios, 'request').throws({
|
|
|
|
|
code: 'ECONNABORTED'
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
const txid =
|
|
|
|
|
'eacb1085dfa296fef6d4ae2c0f4529a1bef096dd2325bdcc6dcb5241b3bdb579'
|
|
|
|
|
|
|
|
|
|
req.params.txid = txid
|
|
|
|
|
const result = await slpRoute.validate3Single(req, res)
|
|
|
|
|
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
|
|
|
|
|
|
|
|
|
assert.isAbove(res.statusCode, 499, 'HTTP status code 503 expected.')
|
|
|
|
|
assert.include(
|
|
|
|
|
result.error,
|
|
|
|
|
'Could not communicate with full node',
|
|
|
|
|
'Error message expected'
|
|
|
|
|
)
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
describe('#validateBulk()', () => {
|
|
|
|
|
const validateBulk = slpRoute.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 = []
|
2022-01-08 15:52:43 -08:00
|
|
|
for (let i = 0; i < 25; i++) testArray.push('')
|
2021-05-18 22:05:28 -07:00
|
|
|
|
|
|
|
|
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('returns proper error when downstream service stalls', async () => {
|
|
|
|
|
// Mock the timeout error.
|
|
|
|
|
sandbox.stub(slpRoute.axios, 'request').throws({ code: 'ECONNABORTED' })
|
|
|
|
|
|
|
|
|
|
req.body.txids = [
|
|
|
|
|
'77872738b6bddee6c0cbdb9509603de20b15d4f6b26602f629417aec2f5d5e8d'
|
|
|
|
|
]
|
|
|
|
|
const result = await validateBulk(req, res)
|
|
|
|
|
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
|
|
|
|
|
|
|
|
|
assert.isAbove(res.statusCode, 499, 'HTTP status code 503 expected.')
|
|
|
|
|
assert.include(
|
|
|
|
|
result.error,
|
|
|
|
|
'Could not communicate with full node',
|
|
|
|
|
'Error message expected'
|
|
|
|
|
)
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
it('returns proper error when downstream service is down', async () => {
|
|
|
|
|
// Mock the timeout error.
|
|
|
|
|
sandbox.stub(slpRoute.axios, 'request').throws({ code: 'ECONNREFUSED' })
|
|
|
|
|
|
|
|
|
|
req.body.txids = [
|
|
|
|
|
'77872738b6bddee6c0cbdb9509603de20b15d4f6b26602f629417aec2f5d5e8d'
|
|
|
|
|
]
|
|
|
|
|
const result = await validateBulk(req, res)
|
|
|
|
|
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
|
|
|
|
|
|
|
|
|
assert.isAbove(res.statusCode, 499, 'HTTP status code 503 expected.')
|
|
|
|
|
assert.include(
|
|
|
|
|
result.error,
|
|
|
|
|
'Could not communicate with full node',
|
|
|
|
|
'Error message expected'
|
|
|
|
|
)
|
|
|
|
|
})
|
|
|
|
|
|
2022-02-01 09:06:10 -08:00
|
|
|
// it('should validate array with single element', async () => {
|
|
|
|
|
// // Mock the RPC call for unit tests.
|
|
|
|
|
// if (process.env.TEST === 'unit') {
|
|
|
|
|
// sandbox.stub(slpRoute.axios, 'request').resolves({
|
|
|
|
|
// data: mockData.mockSingleValidTxid
|
|
|
|
|
// })
|
|
|
|
|
// }
|
|
|
|
|
//
|
|
|
|
|
// req.body.txids = [
|
|
|
|
|
// '77872738b6bddee6c0cbdb9509603de20b15d4f6b26602f629417aec2f5d5e8d'
|
|
|
|
|
// ]
|
|
|
|
|
//
|
|
|
|
|
// const result = await validateBulk(req, res)
|
|
|
|
|
// // console.log(`result: ${util.inspect(result)}`)
|
|
|
|
|
//
|
|
|
|
|
// assert.isArray(result)
|
|
|
|
|
// assert.hasAllKeys(result[0], ['txid', 'valid'])
|
|
|
|
|
// })
|
2021-05-18 22:05:28 -07:00
|
|
|
|
2022-02-01 06:09:14 -08:00
|
|
|
// it('should validate array with two elements', async () => {
|
|
|
|
|
// // Mock the RPC call for unit tests.
|
|
|
|
|
// if (process.env.TEST === 'unit') {
|
|
|
|
|
// sandbox.stub(slpRoute.axios, 'request').resolves({
|
|
|
|
|
// data: mockData.mockTwoValidTxid
|
|
|
|
|
// })
|
|
|
|
|
// }
|
|
|
|
|
//
|
|
|
|
|
// req.body.txids = [
|
|
|
|
|
// '77872738b6bddee6c0cbdb9509603de20b15d4f6b26602f629417aec2f5d5e8d',
|
|
|
|
|
// '552112f9e458dc7d1d8b328b0a6685e8af74a64b60b6846e7c86407f27f47e42'
|
|
|
|
|
// ]
|
|
|
|
|
//
|
|
|
|
|
// 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)
|
|
|
|
|
// })
|
2021-05-18 22:05:28 -07:00
|
|
|
|
|
|
|
|
// Captures a regression bug that went out to production, captured in this
|
|
|
|
|
// GitHub Issue: https://github.com/Bitcoin-com/rest.bitcoin.com/issues/518
|
2022-02-01 06:59:15 -08:00
|
|
|
// it('should return two elements if given two elements', async () => {
|
|
|
|
|
// // Mock the RPC call for unit tests.
|
|
|
|
|
// if (process.env.TEST === 'unit') {
|
|
|
|
|
// sandbox.stub(slpRoute.axios, 'request').resolves({
|
|
|
|
|
// data: mockData.mockTwoRedundentTxid
|
|
|
|
|
// })
|
|
|
|
|
// }
|
|
|
|
|
//
|
|
|
|
|
// req.body.txids = [
|
|
|
|
|
// 'd56a2b446d8149c39ca7e06163fe8097168c3604915f631bc58777d669135a56',
|
|
|
|
|
// 'd56a2b446d8149c39ca7e06163fe8097168c3604915f631bc58777d669135a56'
|
|
|
|
|
// ]
|
|
|
|
|
//
|
|
|
|
|
// 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)
|
|
|
|
|
// })
|
2021-05-18 22:05:28 -07:00
|
|
|
|
2022-02-01 09:06:10 -08:00
|
|
|
// it('should handle a mix of valid, invalid, and non-SLP txs', async () => {
|
|
|
|
|
// // Mock the RPC call for unit tests.
|
|
|
|
|
// if (process.env.TEST === 'unit') {
|
|
|
|
|
// sandbox.stub(slpRoute.axios, 'request').resolves({
|
|
|
|
|
// data: mockData.mockValidateBulk
|
|
|
|
|
// })
|
|
|
|
|
// }
|
|
|
|
|
//
|
|
|
|
|
// const txids = [
|
|
|
|
|
// // Malformed SLP tx
|
|
|
|
|
// 'f7e5199ef6669ad4d078093b3ad56e355b6ab84567e59ad0f08a5ad0244f783a',
|
|
|
|
|
// // Normal TX (non-SLP)
|
|
|
|
|
// '01cdaec2f8b311fc2d6ecc930247bd45fa696dc204ab684596e281fe1b06c1f0',
|
|
|
|
|
// // Valid PSF SLP tx
|
|
|
|
|
// 'daf4d8b8045e7a90b7af81bfe2370178f687da0e545511bce1c9ae539eba5ffd',
|
|
|
|
|
// // Valid SLP token not in whitelist
|
|
|
|
|
// '3a4b628cbcc183ab376d44ce5252325f042268307ffa4a53443e92b6d24fb488',
|
|
|
|
|
// // Token send on BCHN network.
|
|
|
|
|
// '402c663379d9699b6e2dd38737061e5888c5a49fca77c97ab98e79e08959e019',
|
|
|
|
|
// // Token send on ABC network.
|
|
|
|
|
// '336bfe2168aac4c3303508a9e8548a0d33797a83b85b76a12d845c8d6674f79d',
|
|
|
|
|
// // Known invalid SLP token send of PSF tokens.
|
|
|
|
|
// '2bf691ad3679d928fef880b8a45b93b233f8fa0d0a92cf792313dbe77b1deb74'
|
|
|
|
|
// ]
|
|
|
|
|
//
|
|
|
|
|
// req.body.txids = txids
|
|
|
|
|
//
|
|
|
|
|
// const result = await validateBulk(req, res)
|
|
|
|
|
// // console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
|
|
|
|
//
|
|
|
|
|
// // BCHN expected results
|
|
|
|
|
// if (process.env.ISBCHN) {
|
|
|
|
|
// assert.equal(result[0].txid, txids[0])
|
|
|
|
|
// assert.equal(result[0].valid, null)
|
|
|
|
|
//
|
|
|
|
|
// assert.equal(result[1].txid, txids[1])
|
|
|
|
|
// assert.equal(result[1].valid, null)
|
|
|
|
|
//
|
|
|
|
|
// assert.equal(result[2].txid, txids[2])
|
|
|
|
|
// assert.equal(result[2].valid, true)
|
|
|
|
|
//
|
|
|
|
|
// assert.equal(result[3].txid, txids[3])
|
|
|
|
|
// assert.equal(result[3].valid, true)
|
|
|
|
|
//
|
|
|
|
|
// // Note: This should change from null to true once SLPDB finishes indexing.
|
|
|
|
|
// assert.equal(result[4].txid, txids[4])
|
|
|
|
|
// assert.equal(result[4].valid, true)
|
|
|
|
|
//
|
|
|
|
|
// assert.equal(result[5].txid, txids[5])
|
|
|
|
|
// assert.equal(result[5].valid, null)
|
|
|
|
|
//
|
|
|
|
|
// assert.equal(result[6].txid, txids[6])
|
|
|
|
|
// assert.equal(result[6].valid, false)
|
|
|
|
|
// assert.include(
|
|
|
|
|
// result[6].invalidReason,
|
|
|
|
|
// 'Token outputs are greater than valid token inputs'
|
|
|
|
|
// )
|
|
|
|
|
// } else {
|
|
|
|
|
// assert.equal(result[0].txid, txids[0])
|
|
|
|
|
// assert.equal(result[0].valid, null)
|
|
|
|
|
//
|
|
|
|
|
// assert.equal(result[1].txid, txids[1])
|
|
|
|
|
// assert.equal(result[1].valid, null)
|
|
|
|
|
//
|
|
|
|
|
// assert.equal(result[2].txid, txids[2])
|
|
|
|
|
// assert.equal(result[2].valid, true)
|
|
|
|
|
//
|
|
|
|
|
// assert.equal(result[3].txid, txids[3])
|
|
|
|
|
// assert.equal(result[3].valid, true)
|
|
|
|
|
//
|
|
|
|
|
// assert.equal(result[4].txid, txids[4])
|
|
|
|
|
// assert.equal(result[4].valid, null)
|
|
|
|
|
//
|
|
|
|
|
// assert.equal(result[5].txid, txids[5])
|
|
|
|
|
// assert.equal(result[5].valid, true)
|
|
|
|
|
//
|
|
|
|
|
// assert.equal(result[6].txid, txids[6])
|
|
|
|
|
// assert.equal(result[6].valid, false)
|
|
|
|
|
// assert.include(
|
|
|
|
|
// result[6].invalidReason,
|
|
|
|
|
// 'Token outputs are greater than valid token inputs'
|
|
|
|
|
// )
|
|
|
|
|
// }
|
|
|
|
|
// })
|
2021-05-18 22:05:28 -07:00
|
|
|
})
|
|
|
|
|
|
|
|
|
|
describe('#validate3Bulk', () => {
|
|
|
|
|
const validate3Bulk = slpRoute.validate3Bulk
|
|
|
|
|
|
|
|
|
|
it('should throw 400 if txid array is empty', async () => {
|
|
|
|
|
const result = await validate3Bulk(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 = []
|
2022-01-08 15:52:43 -08:00
|
|
|
for (let i = 0; i < 25; i++) testArray.push('')
|
2021-05-18 22:05:28 -07:00
|
|
|
|
|
|
|
|
req.body.txids = testArray
|
|
|
|
|
|
|
|
|
|
const result = await validate3Bulk(req, res)
|
|
|
|
|
// console.log(`result: ${util.inspect(result)}`)
|
|
|
|
|
|
|
|
|
|
assert.hasAllKeys(result, ['error'])
|
|
|
|
|
assert.include(result.error, 'Array too large')
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
it('returns proper error when downstream service stalls', async () => {
|
|
|
|
|
// Mock the timeout error.
|
|
|
|
|
sandbox.stub(slpRoute.axios, 'request').throws({ code: 'ECONNABORTED' })
|
|
|
|
|
|
|
|
|
|
req.body.txids = [
|
|
|
|
|
'77872738b6bddee6c0cbdb9509603de20b15d4f6b26602f629417aec2f5d5e8d'
|
|
|
|
|
]
|
|
|
|
|
const result = await validate3Bulk(req, res)
|
|
|
|
|
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
|
|
|
|
|
|
|
|
|
assert.isAbove(res.statusCode, 499, 'HTTP status code 503 expected.')
|
|
|
|
|
assert.include(
|
|
|
|
|
result.error,
|
|
|
|
|
'Could not communicate with full node',
|
|
|
|
|
'Error message expected'
|
|
|
|
|
)
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
it('returns proper error when downstream service is down', async () => {
|
|
|
|
|
// Mock the timeout error.
|
|
|
|
|
sandbox.stub(slpRoute.axios, 'request').throws({ code: 'ECONNREFUSED' })
|
|
|
|
|
|
|
|
|
|
req.body.txids = [
|
|
|
|
|
'77872738b6bddee6c0cbdb9509603de20b15d4f6b26602f629417aec2f5d5e8d'
|
|
|
|
|
]
|
|
|
|
|
const result = await validate3Bulk(req, res)
|
|
|
|
|
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
|
|
|
|
|
|
|
|
|
assert.isAbove(res.statusCode, 499, 'HTTP status code 503 expected.')
|
|
|
|
|
assert.include(
|
|
|
|
|
result.error,
|
|
|
|
|
'Could not communicate with full node',
|
|
|
|
|
'Error message expected'
|
|
|
|
|
)
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
it('should validate array with single element', async () => {
|
|
|
|
|
// Mock the RPC call for unit tests.
|
|
|
|
|
if (process.env.TEST === 'unit') {
|
|
|
|
|
sandbox.stub(slpRoute.axios, 'request').resolves({
|
|
|
|
|
data: mockData.mockPsfToken
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
req.body.txids = [
|
|
|
|
|
'cdfed769ef7b69e09be06d2821b88598d9a5d711b8f9bd369763c78b7a578fbc'
|
|
|
|
|
]
|
|
|
|
|
|
|
|
|
|
const result = await validate3Bulk(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.axios, 'request').resolves({
|
|
|
|
|
data: mockData.mockPsfToken
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
req.body.txids = [
|
|
|
|
|
'cdfed769ef7b69e09be06d2821b88598d9a5d711b8f9bd369763c78b7a578fbc',
|
|
|
|
|
'cdfed769ef7b69e09be06d2821b88598d9a5d711b8f9bd369763c78b7a578fbc'
|
|
|
|
|
]
|
|
|
|
|
|
|
|
|
|
const result = await validate3Bulk(req, res)
|
|
|
|
|
// console.log(`result: ${util.inspect(result)}`)
|
|
|
|
|
|
|
|
|
|
assert.isArray(result)
|
|
|
|
|
assert.hasAllKeys(result[0], ['txid', 'valid'])
|
|
|
|
|
assert.equal(result.length, 2)
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
// Captures a regression bug that went out to production, captured in this
|
|
|
|
|
// GitHub Issue: https://github.com/Bitcoin-com/rest.bitcoin.com/issues/518
|
|
|
|
|
it('should return two elements if given two elements', async () => {
|
|
|
|
|
// Mock the RPC call for unit tests.
|
|
|
|
|
if (process.env.TEST === 'unit') {
|
|
|
|
|
sandbox.stub(slpRoute.axios, 'request').resolves({
|
|
|
|
|
data: mockData.mockPsfToken
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
req.body.txids = [
|
|
|
|
|
'cdfed769ef7b69e09be06d2821b88598d9a5d711b8f9bd369763c78b7a578fbc',
|
|
|
|
|
'cdfed769ef7b69e09be06d2821b88598d9a5d711b8f9bd369763c78b7a578fbc'
|
|
|
|
|
]
|
|
|
|
|
|
|
|
|
|
const result = await validate3Bulk(req, res)
|
|
|
|
|
// console.log(`result: ${util.inspect(result)}`)
|
|
|
|
|
|
|
|
|
|
assert.isArray(result)
|
|
|
|
|
assert.hasAllKeys(result[0], ['txid', 'valid'])
|
|
|
|
|
assert.equal(result.length, 2)
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
if (process.env.TEST === 'unit') {
|
|
|
|
|
// This is a unit-test only test, as the results change depending on if
|
|
|
|
|
// its tested against the BCHN or ABC networks. There are integration tests
|
|
|
|
|
// for this test case in the ../integration/slp.js file.
|
|
|
|
|
it('should handle a mix of valid, invalid, and non-SLP txs', async () => {
|
|
|
|
|
// Mock the RPC call for unit tests.
|
|
|
|
|
|
|
|
|
|
sandbox.stub(slpRoute.axios, 'request').resolves({
|
|
|
|
|
data: mockData.mockValidate3Bulk
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
const txids = [
|
|
|
|
|
// Malformed SLP tx
|
|
|
|
|
'f7e5199ef6669ad4d078093b3ad56e355b6ab84567e59ad0f08a5ad0244f783a',
|
|
|
|
|
// Normal TX (non-SLP)
|
|
|
|
|
'01cdaec2f8b311fc2d6ecc930247bd45fa696dc204ab684596e281fe1b06c1f0',
|
|
|
|
|
// Valid PSF SLP tx
|
|
|
|
|
'daf4d8b8045e7a90b7af81bfe2370178f687da0e545511bce1c9ae539eba5ffd',
|
|
|
|
|
// Valid SLP token not in whitelist
|
|
|
|
|
'3a4b628cbcc183ab376d44ce5252325f042268307ffa4a53443e92b6d24fb488',
|
|
|
|
|
// Token send on BCHN network.
|
|
|
|
|
'402c663379d9699b6e2dd38737061e5888c5a49fca77c97ab98e79e08959e019',
|
|
|
|
|
// Token send on ABC network.
|
|
|
|
|
'336bfe2168aac4c3303508a9e8548a0d33797a83b85b76a12d845c8d6674f79d',
|
|
|
|
|
// Known invalid SLP token send of PSF tokens.
|
|
|
|
|
'2bf691ad3679d928fef880b8a45b93b233f8fa0d0a92cf792313dbe77b1deb74'
|
|
|
|
|
]
|
|
|
|
|
|
|
|
|
|
req.body.txids = txids
|
|
|
|
|
const result = await validate3Bulk(req, res)
|
|
|
|
|
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
|
|
|
|
|
|
|
|
|
assert.equal(result[0].txid, txids[0])
|
|
|
|
|
assert.equal(result[0].valid, null)
|
|
|
|
|
|
|
|
|
|
assert.equal(result[1].txid, txids[1])
|
|
|
|
|
assert.equal(result[1].valid, null)
|
|
|
|
|
|
|
|
|
|
assert.equal(result[2].txid, txids[2])
|
|
|
|
|
assert.equal(result[2].valid, true)
|
|
|
|
|
|
|
|
|
|
assert.equal(result[3].txid, txids[3])
|
|
|
|
|
assert.equal(result[3].valid, null)
|
|
|
|
|
|
|
|
|
|
assert.equal(result[4].txid, txids[4])
|
|
|
|
|
assert.equal(result[4].valid, null)
|
|
|
|
|
|
|
|
|
|
assert.equal(result[5].txid, txids[5])
|
|
|
|
|
assert.equal(result[5].valid, null)
|
|
|
|
|
|
|
|
|
|
assert.equal(result[6].txid, txids[6])
|
|
|
|
|
assert.equal(result[6].valid, false)
|
|
|
|
|
assert.include(
|
|
|
|
|
result[6].invalidReason,
|
|
|
|
|
'Token outputs are greater than valid token inputs'
|
|
|
|
|
)
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
describe('tokenStats()', () => {
|
|
|
|
|
it('should throw 400 if tokenID is empty', async () => {
|
|
|
|
|
req.params.tokenId = ''
|
|
|
|
|
const result = await slpRoute.tokenStats(req, res)
|
|
|
|
|
// console.log(`result: ${util.inspect(result)}`)
|
|
|
|
|
|
|
|
|
|
assert.hasAllKeys(result, ['error'])
|
|
|
|
|
assert.include(result.error, 'tokenId can not be empty')
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
it('returns proper error when downstream service stalls', async () => {
|
|
|
|
|
// Mock the timeout error.
|
|
|
|
|
sandbox
|
|
|
|
|
.stub(slpRoute.slpdb, 'getTokenStats')
|
|
|
|
|
.throws({ code: 'ECONNABORTED' })
|
|
|
|
|
|
|
|
|
|
req.params.tokenId =
|
|
|
|
|
'497291b8a1dfe69c8daea50677a3d31a5ef0e9484d8bebb610dac64bbc202fb7'
|
|
|
|
|
|
|
|
|
|
const result = await slpRoute.tokenStats(req, res)
|
|
|
|
|
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
|
|
|
|
|
|
|
|
|
assert.isAbove(res.statusCode, 499, 'HTTP status code 503 expected.')
|
|
|
|
|
assert.include(
|
|
|
|
|
result.error,
|
|
|
|
|
'Could not communicate with full node',
|
|
|
|
|
'Error message expected'
|
|
|
|
|
)
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
it('returns proper error when downstream service is down', async () => {
|
|
|
|
|
// Mock the timeout error.
|
|
|
|
|
sandbox
|
|
|
|
|
.stub(slpRoute.slpdb, 'getTokenStats')
|
|
|
|
|
.throws({ code: 'ECONNREFUSED' })
|
|
|
|
|
|
|
|
|
|
req.params.tokenId =
|
|
|
|
|
'497291b8a1dfe69c8daea50677a3d31a5ef0e9484d8bebb610dac64bbc202fb7'
|
|
|
|
|
|
|
|
|
|
const result = await slpRoute.tokenStats(req, res)
|
|
|
|
|
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
|
|
|
|
|
|
|
|
|
assert.isAbove(res.statusCode, 499, 'HTTP status code 503 expected.')
|
|
|
|
|
assert.include(
|
|
|
|
|
result.error,
|
|
|
|
|
'Could not communicate with full node',
|
|
|
|
|
'Error message expected'
|
|
|
|
|
)
|
|
|
|
|
})
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
describe('balancesForTokenSingle()', () => {
|
|
|
|
|
const balancesForTokenSingle = slpRoute.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('returns proper error when downstream service stalls', async () => {
|
|
|
|
|
// Mock the timeout error.
|
|
|
|
|
sandbox.stub(slpRoute.axios, 'request').throws({ code: 'ECONNABORTED' })
|
|
|
|
|
|
|
|
|
|
req.params.tokenId =
|
|
|
|
|
'497291b8a1dfe69c8daea50677a3d31a5ef0e9484d8bebb610dac64bbc202fb7'
|
|
|
|
|
|
|
|
|
|
const result = await balancesForTokenSingle(req, res)
|
|
|
|
|
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
|
|
|
|
|
|
|
|
|
assert.isAbove(res.statusCode, 499, 'HTTP status code 503 expected.')
|
|
|
|
|
assert.include(
|
|
|
|
|
result.error,
|
|
|
|
|
'Could not communicate with full node',
|
|
|
|
|
'Error message expected'
|
|
|
|
|
)
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
it('returns proper error when downstream service is down', async () => {
|
|
|
|
|
// Mock the timeout error.
|
|
|
|
|
sandbox.stub(slpRoute.axios, 'request').throws({ code: 'ECONNREFUSED' })
|
|
|
|
|
|
|
|
|
|
req.params.tokenId =
|
|
|
|
|
'497291b8a1dfe69c8daea50677a3d31a5ef0e9484d8bebb610dac64bbc202fb7'
|
|
|
|
|
|
|
|
|
|
const result = await balancesForTokenSingle(req, res)
|
|
|
|
|
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
|
|
|
|
|
|
|
|
|
assert.isAbove(res.statusCode, 499, 'HTTP status code 503 expected.')
|
|
|
|
|
assert.include(
|
|
|
|
|
result.error,
|
|
|
|
|
'Could not communicate with full node',
|
|
|
|
|
'Error message expected'
|
|
|
|
|
)
|
|
|
|
|
})
|
|
|
|
|
|
2022-02-01 06:09:14 -08:00
|
|
|
// it('should get balances for tokenId', async () => {
|
|
|
|
|
// // Mock the RPC call for unit tests.
|
|
|
|
|
// if (process.env.TEST === 'unit') {
|
|
|
|
|
// sandbox.stub(slpRoute.axios, 'request').resolves({
|
|
|
|
|
// data: {
|
|
|
|
|
// g: [mockData.mockBalance]
|
|
|
|
|
// }
|
|
|
|
|
// })
|
|
|
|
|
// }
|
|
|
|
|
//
|
|
|
|
|
// req.params.tokenId =
|
|
|
|
|
// '497291b8a1dfe69c8daea50677a3d31a5ef0e9484d8bebb610dac64bbc202fb7'
|
|
|
|
|
//
|
|
|
|
|
// const result = await balancesForTokenSingle(req, res)
|
|
|
|
|
// // console.log(`result: ${util.inspect(result)}`)
|
|
|
|
|
//
|
|
|
|
|
// assert.isArray(result)
|
|
|
|
|
// assert.property(result[0], 'tokenId')
|
|
|
|
|
// assert.property(result[0], 'slpAddress')
|
|
|
|
|
// assert.property(result[0], 'tokenBalanceString')
|
|
|
|
|
// })
|
2021-05-18 22:05:28 -07:00
|
|
|
})
|
|
|
|
|
|
|
|
|
|
describe('txDetails()', () => {
|
|
|
|
|
const txDetails = slpRoute.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')
|
|
|
|
|
})
|
|
|
|
|
|
2022-02-01 06:09:14 -08:00
|
|
|
// 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')
|
|
|
|
|
// }
|
|
|
|
|
// })
|
2021-05-18 22:05:28 -07:00
|
|
|
|
|
|
|
|
it('returns proper error when downstream service stalls', async () => {
|
|
|
|
|
// Mock the timeout error.
|
|
|
|
|
sandbox.stub(slpRoute.axios, 'request').throws({ code: 'ECONNABORTED' })
|
|
|
|
|
|
|
|
|
|
req.params.txid =
|
|
|
|
|
'57b3082a2bf269b3d6f40fee7fb9c664e8256a88ca5ee2697c05b94578223333'
|
|
|
|
|
|
|
|
|
|
const result = await txDetails(req, res)
|
|
|
|
|
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
|
|
|
|
assert.isAbove(res.statusCode, 499, 'HTTP status code 503 expected.')
|
|
|
|
|
assert.include(
|
|
|
|
|
result.error,
|
|
|
|
|
'Could not communicate with full node',
|
|
|
|
|
'Error message expected'
|
|
|
|
|
)
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
it('returns proper error when downstream service is down', async () => {
|
|
|
|
|
// Mock the timeout error.
|
|
|
|
|
sandbox.stub(slpRoute.axios, 'request').throws({ code: 'ECONNREFUSED' })
|
|
|
|
|
|
|
|
|
|
req.params.txid =
|
|
|
|
|
'57b3082a2bf269b3d6f40fee7fb9c664e8256a88ca5ee2697c05b94578223333'
|
|
|
|
|
|
|
|
|
|
const result = await txDetails(req, res)
|
|
|
|
|
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
|
|
|
|
|
|
|
|
|
assert.isAbove(res.statusCode, 499, 'HTTP status code 503 expected.')
|
|
|
|
|
assert.include(
|
|
|
|
|
result.error,
|
|
|
|
|
'Could not communicate with full node',
|
|
|
|
|
'Error message expected'
|
|
|
|
|
)
|
|
|
|
|
})
|
|
|
|
|
|
2022-02-01 06:59:15 -08:00
|
|
|
// if (process.env.TEST === 'integration') {
|
|
|
|
|
// it('should get tx details with token info', async () => {
|
|
|
|
|
// // TODO: add mocking for unit testing. How do I mock reponse form SLPDB
|
|
|
|
|
// // since it's not an object?
|
|
|
|
|
//
|
|
|
|
|
// // if (process.env.TEST === "unit") {
|
|
|
|
|
// // // Mock the slpjs library for unit tests.
|
|
|
|
|
// // pathStub.BitboxNetwork = slpjsMock.BitboxNetwork
|
|
|
|
|
// // txDetails = slpRouteStub.testableComponents.txDetails
|
|
|
|
|
// // }
|
|
|
|
|
//
|
|
|
|
|
// req.params.txid =
|
|
|
|
|
// '497291b8a1dfe69c8daea50677a3d31a5ef0e9484d8bebb610dac64bbc202fb7'
|
|
|
|
|
//
|
|
|
|
|
// const result = await txDetails(req, res)
|
|
|
|
|
// // console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
|
|
|
|
//
|
|
|
|
|
// assert.hasAnyKeys(result, ['tokenIsValid', 'tokenInfo'])
|
|
|
|
|
// })
|
|
|
|
|
// }
|
2021-05-18 22:05:28 -07:00
|
|
|
})
|
|
|
|
|
|
|
|
|
|
describe('txsTokenIdAddressSingle()', () => {
|
|
|
|
|
const txsTokenIdAddressSingle = slpRoute.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('returns proper error when downstream service stalls', async () => {
|
|
|
|
|
// Mock the timeout error.
|
|
|
|
|
sandbox.stub(slpRoute.axios, 'request').throws({ code: 'ECONNABORTED' })
|
|
|
|
|
|
|
|
|
|
req.params.tokenId =
|
|
|
|
|
'7ac7f4bb50b019fe0f5c81e3fc13fc0720e130282ea460768cafb49785eb2796'
|
|
|
|
|
req.params.address = 'slptest:qpwa35xq0q0cnmdu0rwzkct369hddzsqpsqdzw6h9h'
|
|
|
|
|
|
|
|
|
|
const result = await txsTokenIdAddressSingle(req, res)
|
|
|
|
|
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
|
|
|
|
assert.isAbove(res.statusCode, 499, 'HTTP status code 503 expected.')
|
|
|
|
|
assert.include(
|
|
|
|
|
result.error,
|
|
|
|
|
'Could not communicate with full node',
|
|
|
|
|
'Error message expected'
|
|
|
|
|
)
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
it('returns proper error when downstream service is down', async () => {
|
|
|
|
|
// Mock the timeout error.
|
|
|
|
|
sandbox.stub(slpRoute.axios, 'request').throws({ code: 'ECONNREFUSED' })
|
|
|
|
|
|
|
|
|
|
req.params.tokenId =
|
|
|
|
|
'7ac7f4bb50b019fe0f5c81e3fc13fc0720e130282ea460768cafb49785eb2796'
|
|
|
|
|
req.params.address = 'slptest:qpwa35xq0q0cnmdu0rwzkct369hddzsqpsqdzw6h9h'
|
|
|
|
|
|
|
|
|
|
const result = await txsTokenIdAddressSingle(req, res)
|
|
|
|
|
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
|
|
|
|
|
|
|
|
|
assert.isAbove(res.statusCode, 499, 'HTTP status code 503 expected.')
|
|
|
|
|
assert.include(
|
|
|
|
|
result.error,
|
|
|
|
|
'Could not communicate with full node',
|
|
|
|
|
'Error message expected'
|
|
|
|
|
)
|
|
|
|
|
})
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
describe('txsByAddressSingle()', () => {
|
|
|
|
|
const txsByAddressSingle = slpRoute.txsByAddressSingle
|
|
|
|
|
|
|
|
|
|
it('should throw 400 if address is missing', async () => {
|
|
|
|
|
const result = await txsByAddressSingle(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 empty', async () => {
|
|
|
|
|
req.params.address = ''
|
|
|
|
|
const result = await txsByAddressSingle(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 txsByAddressSingle(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 = 'slptest:qr83cu3p7yg9yac7qthwm0nul2ev2kukvsqmes3vl0'
|
|
|
|
|
|
|
|
|
|
const result = await txsByAddressSingle(req, res)
|
|
|
|
|
// console.log(`result: ${util.inspect(result)}`)
|
|
|
|
|
|
|
|
|
|
assert.hasAllKeys(result, ['error'])
|
|
|
|
|
assert.include(result.error, 'Invalid')
|
|
|
|
|
})
|
|
|
|
|
|
2022-02-01 06:09:14 -08:00
|
|
|
// it('should get tx history', async () => {
|
|
|
|
|
// if (process.env.TEST === 'unit') {
|
|
|
|
|
// sandbox
|
|
|
|
|
// .stub(slpRoute.slpdb, 'getHistoricalSlpTransactions')
|
|
|
|
|
// .resolves(mockData.mockTxHistory)
|
|
|
|
|
// }
|
|
|
|
|
//
|
|
|
|
|
// // req.params.address = 'simpleledger:qr5agtachyxvrwxu76vzszan5pnvuzy8duhv4lxrsk'
|
|
|
|
|
// req.params.address =
|
|
|
|
|
// 'simpleledger:qz4guf2k3p4r3t4tph0wwgyfq4p628lr2c0cvqplza'
|
|
|
|
|
//
|
|
|
|
|
// const result = await slpRoute.txsByAddressSingle(req, res)
|
|
|
|
|
// // console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
|
|
|
|
//
|
|
|
|
|
// assert.isArray(result)
|
|
|
|
|
// })
|
2021-05-18 22:05:28 -07:00
|
|
|
})
|
|
|
|
|
|
|
|
|
|
describe('#generateSendOpReturn()', () => {
|
|
|
|
|
const generateSendOpReturn = slpRoute.generateSendOpReturn
|
|
|
|
|
// Validate tokenUtxos input
|
|
|
|
|
it('should throw 400 if tokenUtxos is missing', async () => {
|
|
|
|
|
const result = await generateSendOpReturn(req, res)
|
|
|
|
|
// console.log(`result: ${util.inspect(result)}`)
|
|
|
|
|
|
|
|
|
|
assert.hasAllKeys(result, ['error'])
|
|
|
|
|
assert.include(result.error, 'tokenUtxos needs to be an array.')
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
it('should throw 400 if tokenUtxos is empty', async () => {
|
|
|
|
|
req.body.tokenUtxos = ''
|
|
|
|
|
const result = await generateSendOpReturn(req, res)
|
|
|
|
|
// console.log(`result: ${util.inspect(result)}`)
|
|
|
|
|
|
|
|
|
|
assert.hasAllKeys(result, ['error'])
|
|
|
|
|
assert.include(result.error, 'tokenUtxos needs to be an array.')
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
it('should throw 400 if tokenUtxos is not array', async () => {
|
|
|
|
|
req.body.tokenUtxos = 'tokenUtxos'
|
|
|
|
|
|
|
|
|
|
const result = await generateSendOpReturn(req, res)
|
|
|
|
|
// console.log(`result: ${util.inspect(result)}`)
|
|
|
|
|
|
|
|
|
|
assert.hasAllKeys(result, ['error'])
|
|
|
|
|
assert.include(result.error, 'tokenUtxos needs to be an array.')
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
it('should throw 400 if tokenUtxos is empty array', async () => {
|
|
|
|
|
req.body.tokenUtxos = []
|
|
|
|
|
|
|
|
|
|
const result = await generateSendOpReturn(req, res)
|
|
|
|
|
// console.log(`result: ${util.inspect(result)}`)
|
|
|
|
|
|
|
|
|
|
assert.hasAllKeys(result, ['error'])
|
|
|
|
|
assert.include(result.error, 'tokenUtxos array can not be empty.')
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
// Validate sendQty input
|
|
|
|
|
it('should throw 400 if sendQty is missing', async () => {
|
|
|
|
|
req.body.tokenUtxos = [{}, {}, {}]
|
|
|
|
|
const result = await generateSendOpReturn(req, res)
|
|
|
|
|
// console.log(`result: ${util.inspect(result)}`)
|
|
|
|
|
|
|
|
|
|
assert.hasAllKeys(result, ['error'])
|
|
|
|
|
assert.include(result.error, 'sendQty must be a number')
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
it('should throw 400 if sendQty is empty', async () => {
|
|
|
|
|
req.body.tokenUtxos = [{}, {}, {}]
|
|
|
|
|
req.body.sendQty = ''
|
|
|
|
|
|
|
|
|
|
const result = await generateSendOpReturn(req, res)
|
|
|
|
|
// console.log(`result: ${util.inspect(result)}`)
|
|
|
|
|
|
|
|
|
|
assert.hasAllKeys(result, ['error'])
|
|
|
|
|
assert.include(result.error, 'sendQty must be a number')
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
it('should throw 400 if sendQty is not a number', async () => {
|
|
|
|
|
req.body.tokenUtxos = [{}, {}, {}]
|
|
|
|
|
req.body.sendQty = 'sendQty'
|
|
|
|
|
|
|
|
|
|
const result = await generateSendOpReturn(req, res)
|
|
|
|
|
// console.log(`result: ${util.inspect(result)}`)
|
|
|
|
|
|
|
|
|
|
assert.hasAllKeys(result, ['error'])
|
|
|
|
|
assert.include(result.error, 'sendQty must be a number')
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
it('should return OP_RETURN script', async () => {
|
|
|
|
|
req.body.tokenUtxos = [
|
|
|
|
|
{
|
|
|
|
|
tokenId:
|
|
|
|
|
'38e97c5d7d3585a2cbf3f9580c82ca33985f9cb0845d4dcce220cb709f9538b0',
|
|
|
|
|
decimals: 8,
|
|
|
|
|
tokenQty: 2
|
|
|
|
|
}
|
|
|
|
|
]
|
|
|
|
|
req.body.sendQty = 1.5
|
|
|
|
|
|
|
|
|
|
if (process.env.TEST === 'unit') {
|
|
|
|
|
sandbox
|
|
|
|
|
.stub(slpRoute.bchjs.SLP.TokenType1, 'generateSendOpReturn')
|
|
|
|
|
.resolves({ script: '<Buffer ', outputs: 2 })
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const result = await generateSendOpReturn(req, res)
|
|
|
|
|
// console.log(`result: ${util.inspect(result)}`)
|
|
|
|
|
|
|
|
|
|
assert.hasAllKeys(result, ['script', 'outputs'])
|
|
|
|
|
assert.isNumber(result.outputs)
|
|
|
|
|
})
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
describe('#hydrateUtxos', () => {
|
|
|
|
|
it('should throw error if input is not an array.', async () => {
|
|
|
|
|
req.body.utxos = 'test'
|
|
|
|
|
|
|
|
|
|
const result = await slpRoute.hydrateUtxos(req, res)
|
|
|
|
|
// console.log(`result: `, result)
|
|
|
|
|
|
|
|
|
|
assert.hasAllKeys(result, ['error'])
|
|
|
|
|
assert.include(result.error, 'Input must be an array')
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
it('should throw error if Array is empty', async () => {
|
|
|
|
|
req.body.utxos = []
|
|
|
|
|
|
|
|
|
|
const result = await slpRoute.hydrateUtxos(req, res)
|
|
|
|
|
|
|
|
|
|
assert.hasAllKeys(result, ['error'])
|
|
|
|
|
assert.include(result.error, 'Array should not be empty')
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
it('should throw error if Array is too long', async () => {
|
|
|
|
|
const utxo = {
|
2022-01-08 15:52:43 -08:00
|
|
|
txid: 'bd158c564dd4ef54305b14f44f8e94c44b649f246dab14bcb42fb0d0078b8a90',
|
2021-05-18 22:05:28 -07:00
|
|
|
vout: 3,
|
|
|
|
|
amount: 0.00002015,
|
|
|
|
|
satoshis: 2015,
|
|
|
|
|
height: 594892,
|
|
|
|
|
confirmations: 5
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const utxos = []
|
|
|
|
|
|
|
|
|
|
// Populate array with 21 utxos
|
|
|
|
|
for (let i = 0; i < 21; i++) {
|
|
|
|
|
utxos.push(utxo)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
req.body.utxos = utxos
|
|
|
|
|
|
|
|
|
|
const result = await slpRoute.hydrateUtxos(req, res)
|
|
|
|
|
|
|
|
|
|
assert.hasAllKeys(result, ['error'])
|
|
|
|
|
assert.include(result.error, 'Array too long, max length is 20')
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
it('should return utxo details', async () => {
|
|
|
|
|
const utxos = [
|
|
|
|
|
{
|
|
|
|
|
utxos: [
|
|
|
|
|
{
|
2022-01-08 15:52:43 -08:00
|
|
|
txid: 'd56a2b446d8149c39ca7e06163fe8097168c3604915f631bc58777d669135a56',
|
2021-05-18 22:05:28 -07:00
|
|
|
vout: 3,
|
|
|
|
|
value: '6816',
|
|
|
|
|
height: 606848,
|
|
|
|
|
confirmations: 13,
|
|
|
|
|
satoshis: 6816
|
|
|
|
|
},
|
|
|
|
|
{
|
2022-01-08 15:52:43 -08:00
|
|
|
txid: 'd56a2b446d8149c39ca7e06163fe8097168c3604915f631bc58777d669135a56',
|
2021-05-18 22:05:28 -07:00
|
|
|
vout: 2,
|
|
|
|
|
value: '546',
|
|
|
|
|
height: 606848,
|
|
|
|
|
confirmations: 13,
|
|
|
|
|
satoshis: 546
|
|
|
|
|
}
|
|
|
|
|
]
|
|
|
|
|
}
|
|
|
|
|
]
|
|
|
|
|
|
|
|
|
|
// Mock the external network call.
|
|
|
|
|
sandbox.stub(slpRoute.bchjs.SLP.Utils, 'tokenUtxoDetails').resolves([
|
|
|
|
|
{
|
2022-01-08 15:52:43 -08:00
|
|
|
txid: 'd56a2b446d8149c39ca7e06163fe8097168c3604915f631bc58777d669135a56',
|
2021-05-18 22:05:28 -07:00
|
|
|
vout: 3,
|
|
|
|
|
value: '6816',
|
|
|
|
|
height: 606848,
|
|
|
|
|
confirmations: 13,
|
|
|
|
|
satoshis: 6816,
|
|
|
|
|
isValid: false
|
|
|
|
|
},
|
|
|
|
|
{
|
2022-01-08 15:52:43 -08:00
|
|
|
txid: 'd56a2b446d8149c39ca7e06163fe8097168c3604915f631bc58777d669135a56',
|
2021-05-18 22:05:28 -07:00
|
|
|
vout: 2,
|
|
|
|
|
value: '546',
|
|
|
|
|
height: 606848,
|
|
|
|
|
confirmations: 13,
|
|
|
|
|
satoshis: 546,
|
|
|
|
|
utxoType: 'token',
|
|
|
|
|
transactionType: 'send',
|
|
|
|
|
tokenId:
|
|
|
|
|
'dd84ca78db4d617221b58eabc6667af8fe2f7eadbfcc213d35be9f1b419beb8d',
|
|
|
|
|
tokenTicker: 'TAP',
|
|
|
|
|
tokenName: 'Thoughts and Prayers',
|
|
|
|
|
tokenDocumentUrl: '',
|
|
|
|
|
tokenDocumentHash: '',
|
|
|
|
|
decimals: 0,
|
|
|
|
|
tokenType: 1,
|
|
|
|
|
tokenQty: 5,
|
|
|
|
|
isValid: true
|
|
|
|
|
}
|
|
|
|
|
])
|
|
|
|
|
|
|
|
|
|
req.body.utxos = utxos
|
|
|
|
|
const result = await slpRoute.hydrateUtxos(req, res)
|
|
|
|
|
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
|
|
|
|
|
|
|
|
|
// Test the general structure of the output.
|
|
|
|
|
assert.isArray(result.slpUtxos)
|
|
|
|
|
assert.equal(result.slpUtxos.length, 1)
|
|
|
|
|
assert.equal(result.slpUtxos[0].utxos.length, 2)
|
|
|
|
|
|
|
|
|
|
// Test the non-slp UTXO.
|
|
|
|
|
assert.property(result.slpUtxos[0].utxos[0], 'txid')
|
|
|
|
|
assert.property(result.slpUtxos[0].utxos[0], 'vout')
|
|
|
|
|
assert.property(result.slpUtxos[0].utxos[0], 'value')
|
|
|
|
|
assert.property(result.slpUtxos[0].utxos[0], 'height')
|
|
|
|
|
assert.property(result.slpUtxos[0].utxos[0], 'confirmations')
|
|
|
|
|
assert.property(result.slpUtxos[0].utxos[0], 'satoshis')
|
|
|
|
|
assert.property(result.slpUtxos[0].utxos[0], 'isValid')
|
|
|
|
|
assert.equal(result.slpUtxos[0].utxos[0].isValid, false)
|
|
|
|
|
|
|
|
|
|
// Test the slp UTXO.
|
|
|
|
|
assert.property(result.slpUtxos[0].utxos[1], 'txid')
|
|
|
|
|
assert.property(result.slpUtxos[0].utxos[1], 'vout')
|
|
|
|
|
assert.property(result.slpUtxos[0].utxos[1], 'value')
|
|
|
|
|
assert.property(result.slpUtxos[0].utxos[1], 'height')
|
|
|
|
|
assert.property(result.slpUtxos[0].utxos[1], 'confirmations')
|
|
|
|
|
assert.property(result.slpUtxos[0].utxos[1], 'satoshis')
|
|
|
|
|
assert.property(result.slpUtxos[0].utxos[1], 'isValid')
|
|
|
|
|
assert.equal(result.slpUtxos[0].utxos[1].isValid, true)
|
|
|
|
|
assert.property(result.slpUtxos[0].utxos[1], 'transactionType')
|
|
|
|
|
assert.property(result.slpUtxos[0].utxos[1], 'tokenId')
|
|
|
|
|
assert.property(result.slpUtxos[0].utxos[1], 'tokenTicker')
|
|
|
|
|
assert.property(result.slpUtxos[0].utxos[1], 'tokenName')
|
|
|
|
|
assert.property(result.slpUtxos[0].utxos[1], 'tokenDocumentUrl')
|
|
|
|
|
assert.property(result.slpUtxos[0].utxos[1], 'tokenDocumentHash')
|
|
|
|
|
assert.property(result.slpUtxos[0].utxos[1], 'decimals')
|
|
|
|
|
assert.property(result.slpUtxos[0].utxos[1], 'tokenType')
|
|
|
|
|
assert.property(result.slpUtxos[0].utxos[1], 'tokenQty')
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
it('should throw error for missing properties', async () => {
|
|
|
|
|
const utxos = [
|
|
|
|
|
{
|
|
|
|
|
height: 639443,
|
|
|
|
|
tx_hash:
|
|
|
|
|
'30707fffb9b295a06a68d217f49c198e9e1dbe1edc3874a0928ca1905f1709df',
|
|
|
|
|
tx_pos: 0,
|
|
|
|
|
value: 6000
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
height: 639443,
|
|
|
|
|
tx_hash:
|
|
|
|
|
'8962566e413501224d178a02effc89be5ac0d8e4195f617415d443dc4c38fe50',
|
|
|
|
|
tx_pos: 1,
|
|
|
|
|
value: 546
|
|
|
|
|
}
|
|
|
|
|
]
|
|
|
|
|
|
|
|
|
|
req.body.utxos = utxos
|
|
|
|
|
const result = await slpRoute.hydrateUtxos(req, res)
|
|
|
|
|
|
|
|
|
|
assert.hasAllKeys(result, ['error'])
|
|
|
|
|
assert.include(
|
|
|
|
|
result.error,
|
|
|
|
|
'Each element in array should have a utxos property'
|
|
|
|
|
)
|
|
|
|
|
})
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
describe('#hydrateUtxosWL', () => {
|
|
|
|
|
it('should throw error if input is not an array.', async () => {
|
|
|
|
|
req.body.utxos = 'test'
|
|
|
|
|
|
|
|
|
|
const result = await slpRoute.hydrateUtxosWL(req, res)
|
|
|
|
|
// console.log(`result: `, result)
|
|
|
|
|
|
|
|
|
|
assert.hasAllKeys(result, ['error'])
|
|
|
|
|
assert.include(result.error, 'Input must be an array')
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
it('should throw error if Array is empty', async () => {
|
|
|
|
|
req.body.utxos = []
|
|
|
|
|
|
|
|
|
|
const result = await slpRoute.hydrateUtxosWL(req, res)
|
|
|
|
|
|
|
|
|
|
assert.hasAllKeys(result, ['error'])
|
|
|
|
|
assert.include(result.error, 'Array should not be empty')
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
it('should throw error if Array is too long', async () => {
|
|
|
|
|
const utxo = {
|
2022-01-08 15:52:43 -08:00
|
|
|
txid: 'bd158c564dd4ef54305b14f44f8e94c44b649f246dab14bcb42fb0d0078b8a90',
|
2021-05-18 22:05:28 -07:00
|
|
|
vout: 3,
|
|
|
|
|
amount: 0.00002015,
|
|
|
|
|
satoshis: 2015,
|
|
|
|
|
height: 594892,
|
|
|
|
|
confirmations: 5
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const utxos = []
|
|
|
|
|
|
|
|
|
|
// Populate array with 21 utxos
|
|
|
|
|
for (let i = 0; i < 21; i++) {
|
|
|
|
|
utxos.push(utxo)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
req.body.utxos = utxos
|
|
|
|
|
|
|
|
|
|
const result = await slpRoute.hydrateUtxosWL(req, res)
|
|
|
|
|
|
|
|
|
|
assert.hasAllKeys(result, ['error'])
|
|
|
|
|
assert.include(result.error, 'Array too long, max length is 20')
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
it('should return utxo details', async () => {
|
|
|
|
|
const utxos = [
|
|
|
|
|
{
|
|
|
|
|
utxos: [
|
|
|
|
|
{
|
2022-01-08 15:52:43 -08:00
|
|
|
txid: 'd56a2b446d8149c39ca7e06163fe8097168c3604915f631bc58777d669135a56',
|
2021-05-18 22:05:28 -07:00
|
|
|
vout: 3,
|
|
|
|
|
value: '6816',
|
|
|
|
|
height: 606848,
|
|
|
|
|
confirmations: 13,
|
|
|
|
|
satoshis: 6816
|
|
|
|
|
},
|
|
|
|
|
{
|
2022-01-08 15:52:43 -08:00
|
|
|
txid: 'd56a2b446d8149c39ca7e06163fe8097168c3604915f631bc58777d669135a56',
|
2021-05-18 22:05:28 -07:00
|
|
|
vout: 2,
|
|
|
|
|
value: '546',
|
|
|
|
|
height: 606848,
|
|
|
|
|
confirmations: 13,
|
|
|
|
|
satoshis: 546
|
|
|
|
|
}
|
|
|
|
|
]
|
|
|
|
|
}
|
|
|
|
|
]
|
|
|
|
|
|
|
|
|
|
// Mock the external network call.
|
|
|
|
|
sandbox.stub(slpRoute.bchjs.SLP.Utils, 'tokenUtxoDetailsWL').resolves([
|
|
|
|
|
{
|
2022-01-08 15:52:43 -08:00
|
|
|
txid: 'd56a2b446d8149c39ca7e06163fe8097168c3604915f631bc58777d669135a56',
|
2021-05-18 22:05:28 -07:00
|
|
|
vout: 3,
|
|
|
|
|
value: '6816',
|
|
|
|
|
height: 606848,
|
|
|
|
|
confirmations: 13,
|
|
|
|
|
satoshis: 6816,
|
|
|
|
|
isValid: false
|
|
|
|
|
},
|
|
|
|
|
{
|
2022-01-08 15:52:43 -08:00
|
|
|
txid: 'd56a2b446d8149c39ca7e06163fe8097168c3604915f631bc58777d669135a56',
|
2021-05-18 22:05:28 -07:00
|
|
|
vout: 2,
|
|
|
|
|
value: '546',
|
|
|
|
|
height: 606848,
|
|
|
|
|
confirmations: 13,
|
|
|
|
|
satoshis: 546,
|
|
|
|
|
utxoType: 'token',
|
|
|
|
|
transactionType: 'send',
|
|
|
|
|
tokenId:
|
|
|
|
|
'dd84ca78db4d617221b58eabc6667af8fe2f7eadbfcc213d35be9f1b419beb8d',
|
|
|
|
|
tokenTicker: 'TAP',
|
|
|
|
|
tokenName: 'Thoughts and Prayers',
|
|
|
|
|
tokenDocumentUrl: '',
|
|
|
|
|
tokenDocumentHash: '',
|
|
|
|
|
decimals: 0,
|
|
|
|
|
tokenType: 1,
|
|
|
|
|
tokenQty: 5,
|
|
|
|
|
isValid: true
|
|
|
|
|
}
|
|
|
|
|
])
|
|
|
|
|
|
|
|
|
|
req.body.utxos = utxos
|
|
|
|
|
const result = await slpRoute.hydrateUtxosWL(req, res)
|
|
|
|
|
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
|
|
|
|
|
|
|
|
|
// Test the general structure of the output.
|
|
|
|
|
assert.isArray(result.slpUtxos)
|
|
|
|
|
assert.equal(result.slpUtxos.length, 1)
|
|
|
|
|
assert.equal(result.slpUtxos[0].utxos.length, 2)
|
|
|
|
|
|
|
|
|
|
// Test the non-slp UTXO.
|
|
|
|
|
assert.property(result.slpUtxos[0].utxos[0], 'txid')
|
|
|
|
|
assert.property(result.slpUtxos[0].utxos[0], 'vout')
|
|
|
|
|
assert.property(result.slpUtxos[0].utxos[0], 'value')
|
|
|
|
|
assert.property(result.slpUtxos[0].utxos[0], 'height')
|
|
|
|
|
assert.property(result.slpUtxos[0].utxos[0], 'confirmations')
|
|
|
|
|
assert.property(result.slpUtxos[0].utxos[0], 'satoshis')
|
|
|
|
|
assert.property(result.slpUtxos[0].utxos[0], 'isValid')
|
|
|
|
|
assert.equal(result.slpUtxos[0].utxos[0].isValid, false)
|
|
|
|
|
|
|
|
|
|
// Test the slp UTXO.
|
|
|
|
|
assert.property(result.slpUtxos[0].utxos[1], 'txid')
|
|
|
|
|
assert.property(result.slpUtxos[0].utxos[1], 'vout')
|
|
|
|
|
assert.property(result.slpUtxos[0].utxos[1], 'value')
|
|
|
|
|
assert.property(result.slpUtxos[0].utxos[1], 'height')
|
|
|
|
|
assert.property(result.slpUtxos[0].utxos[1], 'confirmations')
|
|
|
|
|
assert.property(result.slpUtxos[0].utxos[1], 'satoshis')
|
|
|
|
|
assert.property(result.slpUtxos[0].utxos[1], 'isValid')
|
|
|
|
|
assert.equal(result.slpUtxos[0].utxos[1].isValid, true)
|
|
|
|
|
assert.property(result.slpUtxos[0].utxos[1], 'transactionType')
|
|
|
|
|
assert.property(result.slpUtxos[0].utxos[1], 'tokenId')
|
|
|
|
|
assert.property(result.slpUtxos[0].utxos[1], 'tokenTicker')
|
|
|
|
|
assert.property(result.slpUtxos[0].utxos[1], 'tokenName')
|
|
|
|
|
assert.property(result.slpUtxos[0].utxos[1], 'tokenDocumentUrl')
|
|
|
|
|
assert.property(result.slpUtxos[0].utxos[1], 'tokenDocumentHash')
|
|
|
|
|
assert.property(result.slpUtxos[0].utxos[1], 'decimals')
|
|
|
|
|
assert.property(result.slpUtxos[0].utxos[1], 'tokenType')
|
|
|
|
|
assert.property(result.slpUtxos[0].utxos[1], 'tokenQty')
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
it('should throw error for missing properties', async () => {
|
|
|
|
|
const utxos = [
|
|
|
|
|
{
|
|
|
|
|
height: 639443,
|
|
|
|
|
tx_hash:
|
|
|
|
|
'30707fffb9b295a06a68d217f49c198e9e1dbe1edc3874a0928ca1905f1709df',
|
|
|
|
|
tx_pos: 0,
|
|
|
|
|
value: 6000
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
height: 639443,
|
|
|
|
|
tx_hash:
|
|
|
|
|
'8962566e413501224d178a02effc89be5ac0d8e4195f617415d443dc4c38fe50',
|
|
|
|
|
tx_pos: 1,
|
|
|
|
|
value: 546
|
|
|
|
|
}
|
|
|
|
|
]
|
|
|
|
|
|
|
|
|
|
req.body.utxos = utxos
|
|
|
|
|
const result = await slpRoute.hydrateUtxosWL(req, res)
|
|
|
|
|
|
|
|
|
|
assert.hasAllKeys(result, ['error'])
|
|
|
|
|
assert.include(
|
|
|
|
|
result.error,
|
|
|
|
|
'Each element in array should have a utxos property'
|
|
|
|
|
)
|
|
|
|
|
})
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
describe('#getStatus', () => {
|
|
|
|
|
it('should get the SLPDB status', async () => {
|
|
|
|
|
if (process.env.TEST === 'unit') {
|
|
|
|
|
// Mock to prevent live network connection.
|
|
|
|
|
sandbox
|
|
|
|
|
.stub(slpRoute.axios, 'request')
|
|
|
|
|
.resolves({ data: { s: [mockData.mockStatus] } })
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const result = await slpRoute.getStatus(req, res)
|
|
|
|
|
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
|
|
|
|
|
|
|
|
|
assert.property(result, 'bchBlockHeight')
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
it('returns proper error when downstream service is down', async () => {
|
|
|
|
|
// Mock the timeout error.
|
|
|
|
|
sandbox.stub(slpRoute.axios, 'request').throws({ code: 'ECONNREFUSED' })
|
|
|
|
|
|
|
|
|
|
slpRoute.getStatus(req, res)
|
|
|
|
|
// const result = slpRoute.getStatus(req, res)
|
|
|
|
|
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
|
|
|
|
|
|
|
|
|
assert.isAbove(res.statusCode, 499, 'HTTP status code 503 expected.')
|
|
|
|
|
})
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
describe('#getNftChildren', () => {
|
|
|
|
|
it('should throw 400 if tokenID is empty', async () => {
|
|
|
|
|
req.params.tokenId = ''
|
|
|
|
|
const result = await slpRoute.getNftChildren(req, res)
|
|
|
|
|
// console.log(`result: ${util.inspect(result)}`)
|
|
|
|
|
|
|
|
|
|
assert.hasAllKeys(result, ['error'])
|
|
|
|
|
assert.include(result.error, 'tokenId can not be empty')
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
it('returns proper error when downstream service stalls', async () => {
|
|
|
|
|
// Mock the timeout error.
|
|
|
|
|
sandbox.stub(slpRoute.axios, 'request').throws({ code: 'ECONNABORTED' })
|
|
|
|
|
|
|
|
|
|
req.params.tokenId =
|
|
|
|
|
'68cd33ecd909068fbea318ae5ff1d6207cf754e53b191327d6d73b6916424c0a'
|
|
|
|
|
|
|
|
|
|
const result = await slpRoute.getNftChildren(req, res)
|
|
|
|
|
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
|
|
|
|
assert.isAbove(res.statusCode, 499, 'HTTP status code 503 expected.')
|
|
|
|
|
assert.include(
|
|
|
|
|
result.error,
|
|
|
|
|
'Could not communicate with full node',
|
|
|
|
|
'Error message expected'
|
|
|
|
|
)
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
it('returns proper error when downstream service is down', async () => {
|
|
|
|
|
// Mock the timeout error.
|
|
|
|
|
sandbox.stub(slpRoute.axios, 'request').throws({ code: 'ECONNREFUSED' })
|
|
|
|
|
|
|
|
|
|
req.params.tokenId =
|
|
|
|
|
'68cd33ecd909068fbea318ae5ff1d6207cf754e53b191327d6d73b6916424c0a'
|
|
|
|
|
|
|
|
|
|
const result = await slpRoute.getNftChildren(req, res)
|
|
|
|
|
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
|
|
|
|
|
|
|
|
|
assert.isAbove(res.statusCode, 499, 'HTTP status code 503 expected.')
|
|
|
|
|
assert.include(
|
|
|
|
|
result.error,
|
|
|
|
|
'Could not communicate with full node',
|
|
|
|
|
'Error message expected'
|
|
|
|
|
)
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
it('should return error on non-existing NFT group token', async () => {
|
|
|
|
|
if (process.env.TEST === 'unit') {
|
|
|
|
|
sandbox.stub(slpRoute, 'lookupToken').resolves({ id: 'not found' })
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
req.params.tokenId =
|
|
|
|
|
'68cd33ecd909068fbea318ae5ff1d6207cf754e53b191327d6d73b6916424c0b'
|
|
|
|
|
|
|
|
|
|
const result = await slpRoute.getNftChildren(req, res)
|
|
|
|
|
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
|
|
|
|
|
|
|
|
|
assert.include(
|
|
|
|
|
result.error,
|
|
|
|
|
'NFT group does not exists',
|
|
|
|
|
'Error message expected'
|
|
|
|
|
)
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
it('should return error on non-group NFT token', async () => {
|
|
|
|
|
if (process.env.TEST === 'unit') {
|
|
|
|
|
sandbox
|
|
|
|
|
.stub(slpRoute, 'lookupToken')
|
|
|
|
|
.resolves(mockData.mockNftChildren[0])
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
req.params.tokenId =
|
|
|
|
|
'68cd33ecd909068fbea318ae5ff1d6207cf754e53b191327d6d73b6916424c0b'
|
|
|
|
|
|
|
|
|
|
const result = await slpRoute.getNftChildren(req, res)
|
|
|
|
|
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
|
|
|
|
assert.include(
|
|
|
|
|
result.error,
|
|
|
|
|
'NFT group does not exists',
|
|
|
|
|
'Error message expected'
|
|
|
|
|
)
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
if (process.env.TEST === 'unit') {
|
|
|
|
|
it('should return error on invalid NFT group data', async () => {
|
|
|
|
|
sandbox.stub(slpRoute, 'lookupToken').resolves(mockData.mockNftGroup)
|
|
|
|
|
sandbox.stub(slpRoute.axios, 'request').resolves({
|
|
|
|
|
data: { u: 'invalid' }
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
req.params.tokenId =
|
|
|
|
|
'68cd33ecd909068fbea318ae5ff1d6207cf754e53b191327d6d73b6916424c0a'
|
|
|
|
|
|
|
|
|
|
const result = await slpRoute.getNftChildren(req, res)
|
|
|
|
|
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
|
|
|
|
assert.include(
|
|
|
|
|
result.error,
|
|
|
|
|
'No children data in the group',
|
|
|
|
|
'Error message expected'
|
|
|
|
|
)
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (process.env.ISBCHN) {
|
|
|
|
|
it('should get NFT children IDs in given NFT group', async () => {
|
|
|
|
|
if (process.env.TEST === 'unit') {
|
|
|
|
|
sandbox.stub(slpRoute, 'lookupToken').resolves(mockData.mockNftGroup)
|
|
|
|
|
sandbox.stub(slpRoute.axios, 'request').resolves({
|
|
|
|
|
data: {
|
|
|
|
|
t: [
|
|
|
|
|
{
|
|
|
|
|
tokenDetails: mockData.mockNftChildren[0],
|
|
|
|
|
nftParentId: mockData.mockNftGroup.id
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
tokenDetails: mockData.mockNftChildren[1],
|
|
|
|
|
nftParentId: mockData.mockNftGroup.id
|
|
|
|
|
}
|
|
|
|
|
]
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
req.params.tokenId =
|
|
|
|
|
'68cd33ecd909068fbea318ae5ff1d6207cf754e53b191327d6d73b6916424c0a'
|
|
|
|
|
|
|
|
|
|
const result = await slpRoute.getNftChildren(req, res)
|
|
|
|
|
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
|
|
|
|
assert.isArray(result.nftChildren)
|
|
|
|
|
assert.equal(result.nftChildren.length, 2)
|
|
|
|
|
assert.equal(
|
|
|
|
|
result.nftChildren[0],
|
|
|
|
|
'45a30085691d6ea586e3ec2aa9122e9b0e0d6c3c1fd357decccc15d8efde48a9'
|
|
|
|
|
)
|
|
|
|
|
assert.equal(
|
|
|
|
|
result.nftChildren[1],
|
|
|
|
|
'928ce61fe1006b1325a0ba0dce700bf83986a6f0691ba26e121c9ac035d12a55'
|
|
|
|
|
)
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
describe('#getNftGroup', () => {
|
|
|
|
|
it('should throw 400 if tokenID is empty', async () => {
|
|
|
|
|
req.params.tokenId = ''
|
|
|
|
|
const result = await slpRoute.getNftGroup(req, res)
|
|
|
|
|
// console.log(`result: ${util.inspect(result)}`)
|
|
|
|
|
|
|
|
|
|
assert.hasAllKeys(result, ['error'])
|
|
|
|
|
assert.include(result.error, 'tokenId can not be empty')
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
it('returns proper error when downstream service stalls', async () => {
|
|
|
|
|
// Mock the timeout error.
|
|
|
|
|
sandbox.stub(slpRoute.axios, 'request').throws({ code: 'ECONNABORTED' })
|
|
|
|
|
|
|
|
|
|
req.params.tokenId =
|
|
|
|
|
'45a30085691d6ea586e3ec2aa9122e9b0e0d6c3c1fd357decccc15d8efde48a9'
|
|
|
|
|
|
|
|
|
|
const result = await slpRoute.getNftGroup(req, res)
|
|
|
|
|
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
|
|
|
|
assert.isAbove(res.statusCode, 499, 'HTTP status code 503 expected.')
|
|
|
|
|
assert.include(
|
|
|
|
|
result.error,
|
|
|
|
|
'Could not communicate with full node',
|
|
|
|
|
'Error message expected'
|
|
|
|
|
)
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
it('returns proper error when downstream service is down', async () => {
|
|
|
|
|
// Mock the timeout error.
|
|
|
|
|
sandbox.stub(slpRoute.axios, 'request').throws({ code: 'ECONNREFUSED' })
|
|
|
|
|
|
|
|
|
|
req.params.tokenId =
|
|
|
|
|
'45a30085691d6ea586e3ec2aa9122e9b0e0d6c3c1fd357decccc15d8efde48a9'
|
|
|
|
|
|
|
|
|
|
const result = await slpRoute.getNftGroup(req, res)
|
|
|
|
|
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
|
|
|
|
|
|
|
|
|
assert.isAbove(res.statusCode, 499, 'HTTP status code 503 expected.')
|
|
|
|
|
assert.include(
|
|
|
|
|
result.error,
|
|
|
|
|
'Could not communicate with full node',
|
|
|
|
|
'Error message expected'
|
|
|
|
|
)
|
|
|
|
|
})
|
|
|
|
|
|
2022-02-01 06:59:15 -08:00
|
|
|
// it('should return error on non-existing NFT child token', async () => {
|
|
|
|
|
// if (process.env.TEST === 'unit') {
|
|
|
|
|
// sandbox.stub(slpRoute, 'lookupToken').resolves({ id: 'not found' })
|
|
|
|
|
// }
|
|
|
|
|
//
|
|
|
|
|
// req.params.tokenId =
|
|
|
|
|
// '45a30085691d6ea586e3ec2aa9122e9b0e0d6c3c1fd357decccc15d8efde48a8'
|
|
|
|
|
//
|
|
|
|
|
// const result = await slpRoute.getNftGroup(req, res)
|
|
|
|
|
// // console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
|
|
|
|
// assert.include(
|
|
|
|
|
// result.error,
|
|
|
|
|
// 'NFT child does not exists',
|
|
|
|
|
// 'Error message expected'
|
|
|
|
|
// )
|
|
|
|
|
// })
|
2021-05-18 22:05:28 -07:00
|
|
|
|
|
|
|
|
it('should return error on invalid NFT child token', async () => {
|
|
|
|
|
if (process.env.TEST === 'unit') {
|
|
|
|
|
sandbox.stub(slpRoute, 'lookupToken').resolves(mockData.mockNftGroup)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
req.params.tokenId =
|
|
|
|
|
'45a30085691d6ea586e3ec2aa9122e9b0e0d6c3c1fd357decccc15d8efde48a8'
|
|
|
|
|
|
|
|
|
|
const result = await slpRoute.getNftGroup(req, res)
|
|
|
|
|
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
|
|
|
|
assert.include(
|
|
|
|
|
result.error,
|
|
|
|
|
'NFT child does not exists',
|
|
|
|
|
'Error message expected'
|
|
|
|
|
)
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
if (process.env.TEST === 'unit') {
|
|
|
|
|
it('should return error on invalid parent', async () => {
|
|
|
|
|
req.params.tokenId =
|
|
|
|
|
'45a30085691d6ea586e3ec2aa9122e9b0e0d6c3c1fd357decccc15d8efde48a9'
|
|
|
|
|
|
|
|
|
|
const callback = sandbox.stub(slpRoute, 'lookupToken')
|
|
|
|
|
callback
|
|
|
|
|
.withArgs(req.params.tokenId)
|
|
|
|
|
.resolves(mockData.mockNftChildren[0])
|
|
|
|
|
// parent is non-valid NFT group (type != 129)
|
|
|
|
|
callback
|
|
|
|
|
.withArgs(mockData.mockNftGroup.id)
|
|
|
|
|
.resolves(mockData.mockNftChildren[0])
|
|
|
|
|
|
|
|
|
|
const result = await slpRoute.getNftGroup(req, res)
|
|
|
|
|
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
|
|
|
|
assert.include(
|
|
|
|
|
result.error,
|
|
|
|
|
'NFT group does not exists',
|
|
|
|
|
'Error message expected'
|
|
|
|
|
)
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (process.env.ISBCHN) {
|
|
|
|
|
it('should get NFT group information for tokenId', async () => {
|
|
|
|
|
req.params.tokenId =
|
|
|
|
|
'45a30085691d6ea586e3ec2aa9122e9b0e0d6c3c1fd357decccc15d8efde48a9'
|
|
|
|
|
|
|
|
|
|
if (process.env.TEST === 'unit') {
|
|
|
|
|
const callback = sandbox.stub(slpRoute, 'lookupToken')
|
|
|
|
|
callback
|
|
|
|
|
.withArgs(req.params.tokenId)
|
|
|
|
|
.resolves(mockData.mockNftChildren[0])
|
|
|
|
|
callback
|
|
|
|
|
.withArgs(mockData.mockNftGroup.id)
|
|
|
|
|
.resolves(mockData.mockNftGroup)
|
|
|
|
|
}
|
|
|
|
|
const result = await slpRoute.getNftGroup(req, res)
|
|
|
|
|
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
|
|
|
|
assert.property(result, 'nftGroup')
|
|
|
|
|
assert.property(result.nftGroup, 'id')
|
|
|
|
|
assert.equal(result.nftGroup.id, mockData.mockNftGroup.id)
|
|
|
|
|
assert.property(result.nftGroup, 'versionType')
|
|
|
|
|
assert.equal(result.nftGroup.versionType, 129)
|
|
|
|
|
assert.property(result.nftGroup, 'symbol')
|
|
|
|
|
assert.property(result.nftGroup, '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 testnet txid on mainnet", 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
|
|
|
|
|
"d284e71227ec89f713b964d8eda595be6392bebd2fac46082bc5a9ce6fb7b33e"
|
|
|
|
|
// 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 =
|
|
|
|
|
"38e97c5d7d3585a2cbf3f9580c82ca33985f9cb0845d4dcce220cb709f9538b0"
|
|
|
|
|
|
|
|
|
|
// console.log(`mockServerUrl: ${mockServerUrl}`)
|
|
|
|
|
|
|
|
|
|
// Mock the RPC call for unit tests.
|
|
|
|
|
if (process.env.TEST === "unit") {
|
|
|
|
|
nock(mockServerUrl)
|
|
|
|
|
.get(uri => uri.includes("/"))
|
|
|
|
|
.reply(200, mockData.mockSingleToken)
|
|
|
|
|
// sandbox.stub(axios, "get").resolves(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",
|
|
|
|
|
"timestamp_unix"
|
|
|
|
|
])
|
|
|
|
|
})
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
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 testnet txid on mainnet", 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
|
|
|
|
|
["d284e71227ec89f713b964d8eda595be6392bebd2fac46082bc5a9ce6fb7b33e"]
|
|
|
|
|
// 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 = [
|
|
|
|
|
"38e97c5d7d3585a2cbf3f9580c82ca33985f9cb0845d4dcce220cb709f9538b0"
|
|
|
|
|
]
|
|
|
|
|
|
|
|
|
|
const result = await listBulkToken(req, res)
|
|
|
|
|
//console.log(`result: ${util.inspect(result)}`)
|
|
|
|
|
|
|
|
|
|
assert.isArray(result)
|
|
|
|
|
assert.hasAllKeys(result[0], [
|
|
|
|
|
"id",
|
|
|
|
|
"blockCreated",
|
|
|
|
|
"blockLastActiveMint",
|
|
|
|
|
"blockLastActiveSend",
|
|
|
|
|
"circulatingSupply",
|
|
|
|
|
"containsBaton",
|
|
|
|
|
"mintingBatonStatus",
|
|
|
|
|
"txnsSinceGenesis",
|
|
|
|
|
"versionType",
|
|
|
|
|
"timestamp",
|
|
|
|
|
"symbol",
|
|
|
|
|
"name",
|
|
|
|
|
"documentUri",
|
|
|
|
|
"documentHash",
|
|
|
|
|
"decimals",
|
|
|
|
|
"initialTokenQty",
|
|
|
|
|
"totalBurned",
|
|
|
|
|
"totalMinted",
|
|
|
|
|
"validAddresses",
|
|
|
|
|
"timestamp_unix"
|
|
|
|
|
])
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
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 = [
|
|
|
|
|
"38e97c5d7d3585a2cbf3f9580c82ca33985f9cb0845d4dcce220cb709f9538b0",
|
|
|
|
|
"38e97c5d7d3585a2cbf3f9580c82ca33985f9cb0845d4dcce220cb709f9538b0"
|
|
|
|
|
]
|
|
|
|
|
|
|
|
|
|
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",
|
|
|
|
|
"timestamp_unix"
|
|
|
|
|
])
|
|
|
|
|
})
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
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 = "slptest:qzcvpw3ah7r880d49wsqzrsl90pg0rqjjurmj3g4nk"
|
|
|
|
|
|
|
|
|
|
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 =
|
|
|
|
|
"simpleledger:qpujxqra3jmdlzzapwmmt7uspr7q0c9ff5me5fdrdn"
|
|
|
|
|
req.params.tokenId =
|
|
|
|
|
"497291b8a1dfe69c8daea50677a3d31a5ef0e9484d8bebb610dac64bbc202fb7"
|
|
|
|
|
|
|
|
|
|
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 =
|
|
|
|
|
"simpleledger:qpujxqra3jmdlzzapwmmt7uspr7q0c9ff5me5fdrdn"
|
|
|
|
|
req.params.tokenId =
|
|
|
|
|
"497291b8a1dfe69c8daea50677a3d31a5ef0e9484d8bebb610dac64bbc202fb7"
|
|
|
|
|
|
|
|
|
|
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(uri => uri.includes("/"))
|
|
|
|
|
.reply(200, { result: mockData.mockConvert })
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
req.params.address =
|
|
|
|
|
"simpleledger:qpujxqra3jmdlzzapwmmt7uspr7q0c9ff5me5fdrdn"
|
|
|
|
|
|
|
|
|
|
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, "Unexpected result!")
|
|
|
|
|
} catch (err) {
|
|
|
|
|
// console.log(`err.message: ${util.inspect(err.message)}`)
|
|
|
|
|
|
|
|
|
|
assert.include(
|
|
|
|
|
err.message,
|
|
|
|
|
`Invalid BCH address. Double check your address is valid`
|
|
|
|
|
)
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
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('txsTokenIdAddressSingle()', () => {
|
|
|
|
|
const txsTokenIdAddressSingle =
|
|
|
|
|
slpRoute.txsTokenIdAddressSingle
|
|
|
|
|
|
|
|
|
|
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"])
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
*/
|