mirror of
https://github.com/Permissionless-Software-Foundation/psf-bch-api.git
synced 2026-09-21 16:52:00 -07:00
216 lines
6.3 KiB
JavaScript
216 lines
6.3 KiB
JavaScript
/*
|
|||
|
|
Unit tests for BlockchainRESTController.
|
||
|
|
*/
|
||
|
|
|
||
|
|
import { assert } from 'chai'
|
||
|
|
import sinon from 'sinon'
|
||
|
|
|
||
|
|
import BlockchainRESTController from '../../../src/controllers/rest-api/full-node/blockchain/controller.js'
|
||
|
|
import {
|
||
|
|
createMockRequest,
|
||
|
|
createMockResponse
|
||
|
|
} from '../mocks/controller-mocks.js'
|
||
|
|
|
||
|
|
describe('#blockchain-controller.js', () => {
|
||
|
|
let sandbox
|
||
|
|
let mockUseCases
|
||
|
|
let mockAdapters
|
||
|
|
let uut
|
||
|
|
|
||
|
|
const createBlockchainUseCaseStubs = () => ({
|
||
|
|
getBestBlockHash: sandbox.stub().resolves('hash'),
|
||
|
|
getBlockchainInfo: sandbox.stub().resolves({}),
|
||
|
|
getBlockCount: sandbox.stub().resolves(123),
|
||
|
|
getBlockHeader: sandbox.stub().resolves({ header: true }),
|
||
|
|
getBlockHeaders: sandbox.stub().resolves(['header']),
|
||
|
|
getChainTips: sandbox.stub().resolves(['tip']),
|
||
|
|
getDifficulty: sandbox.stub().resolves(1),
|
||
|
|
getMempoolEntry: sandbox.stub().resolves({}),
|
||
|
|
getMempoolEntries: sandbox.stub().resolves([]),
|
||
|
|
getMempoolAncestors: sandbox.stub().resolves([]),
|
||
|
|
getMempoolInfo: sandbox.stub().resolves({ size: 1 }),
|
||
|
|
getRawMempool: sandbox.stub().resolves(['tx']),
|
||
|
|
getTxOut: sandbox.stub().resolves({ value: 1 }),
|
||
|
|
getTxOutProof: sandbox.stub().resolves('proof'),
|
||
|
|
getTxOutProofs: sandbox.stub().resolves(['proof']),
|
||
|
|
verifyTxOutProof: sandbox.stub().resolves(['txid']),
|
||
|
|
verifyTxOutProofs: sandbox.stub().resolves([['txid']]),
|
||
|
|
getBlock: sandbox.stub().resolves({ hash: 'abc' }),
|
||
|
|
getBlockHash: sandbox.stub().resolves('blockhash')
|
||
|
|
})
|
||
|
|
|
||
|
|
beforeEach(() => {
|
||
|
|
sandbox = sinon.createSandbox()
|
||
|
|
mockAdapters = {
|
||
|
|
fullNode: {
|
||
|
|
validateArraySize: sandbox.stub().returns(true)
|
||
|
|
}
|
||
|
|
}
|
||
|
|
mockUseCases = {
|
||
|
|
blockchain: createBlockchainUseCaseStubs()
|
||
|
|
}
|
||
|
|
uut = new BlockchainRESTController({
|
||
|
|
adapters: mockAdapters,
|
||
|
|
useCases: mockUseCases
|
||
|
|
})
|
||
|
|
})
|
||
|
|
|
||
|
|
afterEach(() => {
|
||
|
|
sandbox.restore()
|
||
|
|
})
|
||
|
|
|
||
|
|
describe('#constructor()', () => {
|
||
|
|
it('should require adapters', () => {
|
||
|
|
assert.throws(() => {
|
||
|
|
// eslint-disable-next-line no-new
|
||
|
|
new BlockchainRESTController({ useCases: mockUseCases })
|
||
|
|
}, /Adapters library required/)
|
||
|
|
})
|
||
|
|
|
||
|
|
it('should require blockchain use cases', () => {
|
||
|
|
assert.throws(() => {
|
||
|
|
// eslint-disable-next-line no-new
|
||
|
|
new BlockchainRESTController({ adapters: mockAdapters, useCases: {} })
|
||
|
|
}, /Blockchain use cases required/)
|
||
|
|
})
|
||
|
|
})
|
||
|
|
|
||
|
|
describe('#root()', () => {
|
||
|
|
it('should return service status', async () => {
|
||
|
|
const req = createMockRequest()
|
||
|
|
const res = createMockResponse()
|
||
|
|
|
||
|
|
await uut.root(req, res)
|
||
|
|
|
||
|
|
assert.equal(res.statusValue, 200)
|
||
|
|
assert.deepEqual(res.jsonData, { status: 'blockchain' })
|
||
|
|
})
|
||
|
|
})
|
||
|
|
|
||
|
|
describe('#getBestBlockHash()', () => {
|
||
|
|
it('should return hash on success', async () => {
|
||
|
|
const req = createMockRequest()
|
||
|
|
const res = createMockResponse()
|
||
|
|
|
||
|
|
await uut.getBestBlockHash(req, res)
|
||
|
|
|
||
|
|
assert.equal(res.statusValue, 200)
|
||
|
|
assert.equal(res.jsonData, 'hash')
|
||
|
|
assert.isTrue(mockUseCases.blockchain.getBestBlockHash.calledOnce)
|
||
|
|
})
|
||
|
|
|
||
|
|
it('should handle errors via handleError()', async () => {
|
||
|
|
const error = new Error('failure')
|
||
|
|
error.status = 422
|
||
|
|
mockUseCases.blockchain.getBestBlockHash.rejects(error)
|
||
|
|
const req = createMockRequest()
|
||
|
|
const res = createMockResponse()
|
||
|
|
|
||
|
|
await uut.getBestBlockHash(req, res)
|
||
|
|
|
||
|
|
assert.equal(res.statusValue, 422)
|
||
|
|
assert.deepEqual(res.jsonData, { error: 'failure' })
|
||
|
|
})
|
||
|
|
})
|
||
|
|
|
||
|
|
describe('#getBlockHeaderSingle()', () => {
|
||
|
|
it('should return 400 if hash is missing', async () => {
|
||
|
|
const req = createMockRequest()
|
||
|
|
const res = createMockResponse()
|
||
|
|
|
||
|
|
await uut.getBlockHeaderSingle(req, res)
|
||
|
|
|
||
|
|
assert.equal(res.statusValue, 400)
|
||
|
|
assert.property(res.jsonData, 'error')
|
||
|
|
})
|
||
|
|
|
||
|
|
it('should call use case with verbose flag', async () => {
|
||
|
|
const hash = 'a'.repeat(64)
|
||
|
|
const req = createMockRequest({
|
||
|
|
params: { hash },
|
||
|
|
query: { verbose: 'true' }
|
||
|
|
})
|
||
|
|
const res = createMockResponse()
|
||
|
|
|
||
|
|
await uut.getBlockHeaderSingle(req, res)
|
||
|
|
|
||
|
|
assert.equal(res.statusValue, 200)
|
||
|
|
assert.isTrue(
|
||
|
|
mockUseCases.blockchain.getBlockHeader.calledOnceWithExactly({
|
||
|
|
hash,
|
||
|
|
verbose: true
|
||
|
|
})
|
||
|
|
)
|
||
|
|
})
|
||
|
|
})
|
||
|
|
|
||
|
|
describe('#getBlockHeaderBulk()', () => {
|
||
|
|
it('should return error if hashes is not array', async () => {
|
||
|
|
const req = createMockRequest({
|
||
|
|
body: { hashes: 'not-an-array' },
|
||
|
|
locals: {}
|
||
|
|
})
|
||
|
|
const res = createMockResponse()
|
||
|
|
|
||
|
|
await uut.getBlockHeaderBulk(req, res)
|
||
|
|
|
||
|
|
assert.equal(res.statusValue, 400)
|
||
|
|
assert.include(res.jsonData.error, 'hashes needs to be an array')
|
||
|
|
})
|
||
|
|
|
||
|
|
it('should validate array size and call use case', async () => {
|
||
|
|
const hash = 'a'.repeat(64)
|
||
|
|
const req = createMockRequest({
|
||
|
|
body: { hashes: [hash], verbose: true },
|
||
|
|
locals: { proLimit: false }
|
||
|
|
})
|
||
|
|
const res = createMockResponse()
|
||
|
|
mockUseCases.blockchain.getBlockHeaders.resolves(['result'])
|
||
|
|
|
||
|
|
await uut.getBlockHeaderBulk(req, res)
|
||
|
|
|
||
|
|
assert.equal(res.statusValue, 200)
|
||
|
|
assert.deepEqual(res.jsonData, ['result'])
|
||
|
|
assert.isTrue(
|
||
|
|
mockAdapters.fullNode.validateArraySize.calledOnceWithExactly(1, { isProUser: false })
|
||
|
|
)
|
||
|
|
assert.isTrue(
|
||
|
|
mockUseCases.blockchain.getBlockHeaders.calledOnceWithExactly({
|
||
|
|
hashes: [hash],
|
||
|
|
verbose: true
|
||
|
|
})
|
||
|
|
)
|
||
|
|
})
|
||
|
|
|
||
|
|
it('should return error if array size invalid', async () => {
|
||
|
|
mockAdapters.fullNode.validateArraySize.returns(false)
|
||
|
|
const req = createMockRequest({
|
||
|
|
body: { hashes: ['a'.repeat(64)] },
|
||
|
|
locals: {}
|
||
|
|
})
|
||
|
|
const res = createMockResponse()
|
||
|
|
|
||
|
|
await uut.getBlockHeaderBulk(req, res)
|
||
|
|
|
||
|
|
assert.equal(res.statusValue, 400)
|
||
|
|
assert.equal(res.jsonData.error, 'Array too large.')
|
||
|
|
})
|
||
|
|
})
|
||
|
|
|
||
|
|
describe('#verifyTxOutProofBulk()', () => {
|
||
|
|
it('should flatten proof responses', async () => {
|
||
|
|
mockUseCases.blockchain.verifyTxOutProofs.resolves([['txid-a'], ['txid-b']])
|
||
|
|
const req = createMockRequest({
|
||
|
|
body: { proofs: ['proof-a', 'proof-b'] },
|
||
|
|
locals: {}
|
||
|
|
})
|
||
|
|
const res = createMockResponse()
|
||
|
|
|
||
|
|
await uut.verifyTxOutProofBulk(req, res)
|
||
|
|
|
||
|
|
assert.equal(res.statusValue, 200)
|
||
|
|
assert.deepEqual(res.jsonData, ['txid-a', 'txid-b'])
|
||
|
|
})
|
||
|
|
})
|
||
|
|
})
|