mirror of
https://github.com/Permissionless-Software-Foundation/psf-bch-api.git
synced 2026-09-21 16:52:00 -07:00
fix(tests): Updating unit tests
This commit is contained in:
@@ -0,0 +1,215 @@
|
||||
/*
|
||||
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'])
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -1,187 +0,0 @@
|
||||
/*
|
||||
Unit tests for EventRESTControllerLib.
|
||||
*/
|
||||
|
||||
// npm libraries
|
||||
import { assert } from 'chai'
|
||||
import sinon from 'sinon'
|
||||
|
||||
// Mocking data libraries
|
||||
import {
|
||||
mockKind1Event,
|
||||
mockKind0Event
|
||||
} from '../mocks/event-mocks.js'
|
||||
import {
|
||||
createMockRequestWithBody,
|
||||
createMockResponse
|
||||
} from '../mocks/controller-mocks.js'
|
||||
|
||||
// Unit under test
|
||||
import EventRESTControllerLib from '../../../src/controllers/rest-api/event/controller.js'
|
||||
|
||||
describe('#event-controller.js', () => {
|
||||
let sandbox
|
||||
let mockUseCases
|
||||
let uut
|
||||
|
||||
beforeEach(() => {
|
||||
sandbox = sinon.createSandbox()
|
||||
|
||||
// Create mock use cases
|
||||
mockUseCases = {
|
||||
publishEvent: {
|
||||
execute: sandbox.stub()
|
||||
}
|
||||
}
|
||||
|
||||
uut = new EventRESTControllerLib({
|
||||
adapters: {},
|
||||
useCases: mockUseCases
|
||||
})
|
||||
})
|
||||
|
||||
afterEach(() => {
|
||||
sandbox.restore()
|
||||
})
|
||||
|
||||
describe('#publishEvent()', () => {
|
||||
it('should successfully publish an event', async () => {
|
||||
const req = createMockRequestWithBody(mockKind1Event)
|
||||
const res = createMockResponse()
|
||||
|
||||
mockUseCases.publishEvent.execute.resolves({
|
||||
accepted: true,
|
||||
message: 'event saved',
|
||||
eventId: mockKind1Event.id
|
||||
})
|
||||
|
||||
await uut.publishEvent(req, res)
|
||||
|
||||
// Assert use case was called
|
||||
assert.isTrue(mockUseCases.publishEvent.execute.calledOnce)
|
||||
assert.deepEqual(mockUseCases.publishEvent.execute.getCall(0).args[0], mockKind1Event)
|
||||
|
||||
// Assert response
|
||||
assert.equal(res.statusValue, 200)
|
||||
assert.property(res.jsonData, 'accepted')
|
||||
assert.isTrue(res.jsonData.accepted)
|
||||
assert.equal(res.jsonData.eventId, mockKind1Event.id)
|
||||
})
|
||||
|
||||
it('should return 400 when event is rejected', async () => {
|
||||
const req = createMockRequestWithBody(mockKind1Event)
|
||||
const res = createMockResponse()
|
||||
|
||||
mockUseCases.publishEvent.execute.resolves({
|
||||
accepted: false,
|
||||
message: 'duplicate: event already exists',
|
||||
eventId: mockKind1Event.id
|
||||
})
|
||||
|
||||
await uut.publishEvent(req, res)
|
||||
|
||||
// Assert response status is 400
|
||||
assert.equal(res.statusValue, 400)
|
||||
assert.property(res.jsonData, 'accepted')
|
||||
assert.isFalse(res.jsonData.accepted)
|
||||
})
|
||||
|
||||
it('should return 400 when event data is missing', async () => {
|
||||
const req = createMockRequestWithBody(null)
|
||||
const res = createMockResponse()
|
||||
|
||||
await uut.publishEvent(req, res)
|
||||
|
||||
// Assert use case was not called
|
||||
assert.isFalse(mockUseCases.publishEvent.execute.called)
|
||||
|
||||
// Assert error response
|
||||
assert.equal(res.statusValue, 400)
|
||||
assert.property(res.jsonData, 'error')
|
||||
assert.include(res.jsonData.error, 'Event data is required')
|
||||
})
|
||||
|
||||
it('should handle use case errors', async () => {
|
||||
const req = createMockRequestWithBody(mockKind1Event)
|
||||
const res = createMockResponse()
|
||||
|
||||
mockUseCases.publishEvent.execute.rejects(new Error('Network error'))
|
||||
|
||||
await uut.publishEvent(req, res)
|
||||
|
||||
// Assert error response
|
||||
assert.equal(res.statusValue, 500)
|
||||
assert.property(res.jsonData, 'error')
|
||||
assert.include(res.jsonData.error, 'Network error')
|
||||
})
|
||||
|
||||
it('should return 400 for validation errors', async () => {
|
||||
const req = createMockRequestWithBody(mockKind1Event)
|
||||
const res = createMockResponse()
|
||||
|
||||
mockUseCases.publishEvent.execute.rejects(new Error('Invalid event structure'))
|
||||
|
||||
await uut.publishEvent(req, res)
|
||||
|
||||
// Assert validation error returns 400
|
||||
assert.equal(res.statusValue, 400)
|
||||
assert.property(res.jsonData, 'error')
|
||||
assert.include(res.jsonData.error, 'Invalid event structure')
|
||||
})
|
||||
|
||||
it('should handle errors with missing message', async () => {
|
||||
const req = createMockRequestWithBody(mockKind1Event)
|
||||
const res = createMockResponse()
|
||||
|
||||
const error = new Error()
|
||||
error.message = undefined
|
||||
mockUseCases.publishEvent.execute.rejects(error)
|
||||
|
||||
await uut.publishEvent(req, res)
|
||||
|
||||
// Assert error response with default message
|
||||
assert.equal(res.statusValue, 500)
|
||||
assert.property(res.jsonData, 'error')
|
||||
assert.equal(res.jsonData.error, 'Internal server error')
|
||||
})
|
||||
|
||||
it('should publish different event kinds', async () => {
|
||||
const req = createMockRequestWithBody(mockKind0Event)
|
||||
const res = createMockResponse()
|
||||
|
||||
mockUseCases.publishEvent.execute.resolves({
|
||||
accepted: true,
|
||||
message: 'event saved',
|
||||
eventId: mockKind0Event.id
|
||||
})
|
||||
|
||||
await uut.publishEvent(req, res)
|
||||
|
||||
assert.isTrue(mockUseCases.publishEvent.execute.calledOnce)
|
||||
assert.equal(res.statusValue, 200)
|
||||
assert.isTrue(res.jsonData.accepted)
|
||||
})
|
||||
})
|
||||
|
||||
describe('#constructor()', () => {
|
||||
it('should require adapters instance', () => {
|
||||
try {
|
||||
// eslint-disable-next-line no-new
|
||||
new EventRESTControllerLib({ useCases: mockUseCases })
|
||||
assert.equal(true, false, 'unexpected result')
|
||||
} catch (err) {
|
||||
assert.include(err.message, 'Adapters library required')
|
||||
}
|
||||
})
|
||||
|
||||
it('should require useCases instance', () => {
|
||||
try {
|
||||
// eslint-disable-next-line no-new
|
||||
new EventRESTControllerLib({ adapters: {} })
|
||||
assert.equal(true, false, 'unexpected result')
|
||||
} catch (err) {
|
||||
assert.include(err.message, 'Use Cases library required')
|
||||
}
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -1,344 +0,0 @@
|
||||
/*
|
||||
Unit tests for ReqRESTControllerLib.
|
||||
*/
|
||||
|
||||
// npm libraries
|
||||
import { assert } from 'chai'
|
||||
import sinon from 'sinon'
|
||||
|
||||
// Mocking data libraries
|
||||
import { mockEventsArray } from '../mocks/nostr-relay-mocks.js'
|
||||
import {
|
||||
createMockRequestWithParams,
|
||||
createMockResponse
|
||||
} from '../mocks/controller-mocks.js'
|
||||
|
||||
// Unit under test
|
||||
import ReqRESTControllerLib from '../../../src/controllers/rest-api/req/controller.js'
|
||||
|
||||
describe('#req-controller.js', () => {
|
||||
let sandbox
|
||||
let mockUseCases
|
||||
let uut
|
||||
|
||||
beforeEach(() => {
|
||||
sandbox = sinon.createSandbox()
|
||||
|
||||
// Create mock use cases
|
||||
mockUseCases = {
|
||||
queryEvents: {
|
||||
execute: sandbox.stub()
|
||||
},
|
||||
manageSubscription: {
|
||||
createSubscription: sandbox.stub(),
|
||||
closeSubscription: sandbox.stub()
|
||||
}
|
||||
}
|
||||
|
||||
uut = new ReqRESTControllerLib({
|
||||
adapters: {},
|
||||
useCases: mockUseCases
|
||||
})
|
||||
})
|
||||
|
||||
afterEach(() => {
|
||||
sandbox.restore()
|
||||
})
|
||||
|
||||
describe('#queryEvents()', () => {
|
||||
it('should successfully query events with filters as JSON string', async () => {
|
||||
const filters = [{ kinds: [1], limit: 10 }]
|
||||
const filtersJson = JSON.stringify(filters)
|
||||
const req = createMockRequestWithParams({ subId: 'test-sub-123' })
|
||||
req.query = { filters: filtersJson }
|
||||
const res = createMockResponse()
|
||||
|
||||
mockUseCases.queryEvents.execute.resolves(mockEventsArray)
|
||||
|
||||
await uut.queryEvents(req, res)
|
||||
|
||||
// Assert use case was called with parsed filters
|
||||
assert.isTrue(mockUseCases.queryEvents.execute.calledOnce)
|
||||
const executeArgs = mockUseCases.queryEvents.execute.getCall(0).args
|
||||
assert.deepEqual(executeArgs[0], filters)
|
||||
assert.equal(executeArgs[1], 'test-sub-123')
|
||||
|
||||
// Assert response
|
||||
assert.equal(res.statusValue, 200)
|
||||
assert.isArray(res.jsonData)
|
||||
assert.equal(res.jsonData.length, mockEventsArray.length)
|
||||
})
|
||||
|
||||
it('should successfully query events with individual query params', async () => {
|
||||
const req = createMockRequestWithParams({ subId: 'test-sub-123' })
|
||||
req.query = {
|
||||
kinds: JSON.stringify([1]),
|
||||
authors: JSON.stringify(['abc123']),
|
||||
limit: '10'
|
||||
}
|
||||
const res = createMockResponse()
|
||||
|
||||
mockUseCases.queryEvents.execute.resolves(mockEventsArray)
|
||||
|
||||
await uut.queryEvents(req, res)
|
||||
|
||||
// Assert use case was called
|
||||
assert.isTrue(mockUseCases.queryEvents.execute.calledOnce)
|
||||
const executeArgs = mockUseCases.queryEvents.execute.getCall(0).args
|
||||
assert.isArray(executeArgs[0])
|
||||
assert.equal(executeArgs[0][0].kinds[0], 1)
|
||||
assert.equal(executeArgs[0][0].authors[0], 'abc123')
|
||||
assert.equal(executeArgs[0][0].limit, 10)
|
||||
})
|
||||
|
||||
it('should handle empty filters', async () => {
|
||||
const req = createMockRequestWithParams({ subId: 'test-sub-123' })
|
||||
req.query = {}
|
||||
const res = createMockResponse()
|
||||
|
||||
mockUseCases.queryEvents.execute.resolves([])
|
||||
|
||||
await uut.queryEvents(req, res)
|
||||
|
||||
// Assert use case was called with empty filters array
|
||||
assert.isTrue(mockUseCases.queryEvents.execute.calledOnce)
|
||||
const executeArgs = mockUseCases.queryEvents.execute.getCall(0).args
|
||||
assert.deepEqual(executeArgs[0], [{}])
|
||||
})
|
||||
|
||||
it('should return 400 when subscription ID is missing', async () => {
|
||||
const req = createMockRequestWithParams({})
|
||||
const res = createMockResponse()
|
||||
|
||||
await uut.queryEvents(req, res)
|
||||
|
||||
// Assert use case was not called
|
||||
assert.isFalse(mockUseCases.queryEvents.execute.called)
|
||||
|
||||
// Assert error response
|
||||
assert.equal(res.statusValue, 400)
|
||||
assert.property(res.jsonData, 'error')
|
||||
assert.include(res.jsonData.error, 'Subscription ID is required')
|
||||
})
|
||||
|
||||
it('should return 400 when filters JSON is invalid', async () => {
|
||||
const req = createMockRequestWithParams({ subId: 'test-sub-123' })
|
||||
req.query = { filters: 'invalid-json{' }
|
||||
const res = createMockResponse()
|
||||
|
||||
await uut.queryEvents(req, res)
|
||||
|
||||
// Assert error response
|
||||
assert.equal(res.statusValue, 400)
|
||||
assert.property(res.jsonData, 'error')
|
||||
assert.include(res.jsonData.error, 'Invalid filters JSON')
|
||||
})
|
||||
|
||||
it('should handle use case errors', async () => {
|
||||
const req = createMockRequestWithParams({ subId: 'test-sub-123' })
|
||||
req.query = { filters: JSON.stringify([{ kinds: [1] }]) }
|
||||
const res = createMockResponse()
|
||||
|
||||
mockUseCases.queryEvents.execute.rejects(new Error('Query failed'))
|
||||
|
||||
await uut.queryEvents(req, res)
|
||||
|
||||
// Assert error response
|
||||
assert.equal(res.statusValue, 500)
|
||||
assert.property(res.jsonData, 'error')
|
||||
assert.include(res.jsonData.error, 'Query failed')
|
||||
})
|
||||
})
|
||||
|
||||
describe('#createSubscription()', () => {
|
||||
it('should successfully create SSE subscription', async () => {
|
||||
const req = createMockRequestWithParams({ subId: 'test-sub-123' })
|
||||
req.body = { kinds: [1] }
|
||||
const res = createMockResponse()
|
||||
|
||||
mockUseCases.manageSubscription.createSubscription.resolves()
|
||||
|
||||
await uut.createSubscription(req, res)
|
||||
|
||||
// Assert use case was called
|
||||
assert.isTrue(mockUseCases.manageSubscription.createSubscription.calledOnce)
|
||||
const createArgs = mockUseCases.manageSubscription.createSubscription.getCall(0).args
|
||||
assert.equal(createArgs[0], 'test-sub-123')
|
||||
assert.isArray(createArgs[1])
|
||||
assert.equal(createArgs[1][0].kinds[0], 1)
|
||||
assert.isFunction(createArgs[2]) // onEvent
|
||||
assert.isFunction(createArgs[3]) // onEose
|
||||
assert.isFunction(createArgs[4]) // onClosed
|
||||
|
||||
// Assert SSE headers
|
||||
assert.equal(res.headers['Content-Type'], 'text/event-stream')
|
||||
assert.equal(res.headers['Cache-Control'], 'no-cache')
|
||||
assert.equal(res.headers.Connection, 'keep-alive')
|
||||
|
||||
// Assert initial connection message was written
|
||||
assert.isTrue(res.writeData.length > 0)
|
||||
})
|
||||
|
||||
it('should return 400 when subscription ID is missing', async () => {
|
||||
const req = createMockRequestWithParams({})
|
||||
req.body = { kinds: [1] }
|
||||
const res = createMockResponse()
|
||||
|
||||
await uut.createSubscription(req, res)
|
||||
|
||||
// Assert error response
|
||||
assert.equal(res.statusValue, 400)
|
||||
assert.property(res.jsonData, 'error')
|
||||
assert.include(res.jsonData.error, 'Subscription ID is required')
|
||||
})
|
||||
|
||||
it('should return 400 when filters are missing', async () => {
|
||||
const req = createMockRequestWithParams({ subId: 'test-sub-123' })
|
||||
req.body = {}
|
||||
const res = createMockResponse()
|
||||
|
||||
await uut.createSubscription(req, res)
|
||||
|
||||
// Assert error response
|
||||
assert.equal(res.statusValue, 400)
|
||||
assert.property(res.jsonData, 'error')
|
||||
assert.include(res.jsonData.error, 'Filters are required')
|
||||
})
|
||||
|
||||
it('should handle filters as array', async () => {
|
||||
const req = createMockRequestWithParams({ subId: 'test-sub-123' })
|
||||
req.body = [{ kinds: [1] }, { kinds: [3] }]
|
||||
const res = createMockResponse()
|
||||
|
||||
mockUseCases.manageSubscription.createSubscription.resolves()
|
||||
|
||||
await uut.createSubscription(req, res)
|
||||
|
||||
// Assert filters array was passed correctly
|
||||
const createArgs = mockUseCases.manageSubscription.createSubscription.getCall(0).args
|
||||
assert.isArray(createArgs[1])
|
||||
assert.equal(createArgs[1].length, 2)
|
||||
})
|
||||
|
||||
it('should handle client disconnect', async () => {
|
||||
const req = createMockRequestWithParams({ subId: 'test-sub-123' })
|
||||
req.body = { kinds: [1] }
|
||||
req.on = sinon.stub()
|
||||
const res = createMockResponse()
|
||||
|
||||
mockUseCases.manageSubscription.createSubscription.resolves()
|
||||
mockUseCases.manageSubscription.closeSubscription.resolves()
|
||||
|
||||
await uut.createSubscription(req, res)
|
||||
|
||||
// Assert close handler was set up
|
||||
assert.isTrue(req.on.calledWith('close'))
|
||||
|
||||
// Simulate client disconnect
|
||||
const closeCallback = req.on.getCall(0).args[1]
|
||||
await closeCallback()
|
||||
|
||||
// Assert closeSubscription was called
|
||||
assert.isTrue(mockUseCases.manageSubscription.closeSubscription.calledOnce)
|
||||
assert.equal(mockUseCases.manageSubscription.closeSubscription.getCall(0).args[0], 'test-sub-123')
|
||||
})
|
||||
})
|
||||
|
||||
describe('#closeSubscription()', () => {
|
||||
it('should successfully close a subscription', async () => {
|
||||
const req = createMockRequestWithParams({ subId: 'test-sub-123' })
|
||||
const res = createMockResponse()
|
||||
|
||||
mockUseCases.manageSubscription.closeSubscription.resolves()
|
||||
|
||||
await uut.closeSubscription(req, res)
|
||||
|
||||
// Assert use case was called
|
||||
assert.isTrue(mockUseCases.manageSubscription.closeSubscription.calledOnce)
|
||||
assert.equal(mockUseCases.manageSubscription.closeSubscription.getCall(0).args[0], 'test-sub-123')
|
||||
|
||||
// Assert response
|
||||
assert.equal(res.statusValue, 200)
|
||||
assert.property(res.jsonData, 'message')
|
||||
assert.include(res.jsonData.message, 'closed successfully')
|
||||
})
|
||||
|
||||
it('should return 400 when subscription ID is missing', async () => {
|
||||
const req = createMockRequestWithParams({})
|
||||
const res = createMockResponse()
|
||||
|
||||
await uut.closeSubscription(req, res)
|
||||
|
||||
// Assert error response
|
||||
assert.equal(res.statusValue, 400)
|
||||
assert.property(res.jsonData, 'error')
|
||||
assert.include(res.jsonData.error, 'Subscription ID is required')
|
||||
})
|
||||
|
||||
it('should handle use case errors', async () => {
|
||||
const req = createMockRequestWithParams({ subId: 'test-sub-123' })
|
||||
const res = createMockResponse()
|
||||
|
||||
mockUseCases.manageSubscription.closeSubscription.rejects(new Error('Relay connection error'))
|
||||
|
||||
await uut.closeSubscription(req, res)
|
||||
|
||||
// Assert error response
|
||||
assert.equal(res.statusValue, 500)
|
||||
assert.property(res.jsonData, 'error')
|
||||
assert.include(res.jsonData.error, 'Relay connection error')
|
||||
})
|
||||
|
||||
it('should handle idempotent close (subscription already closed)', async () => {
|
||||
const req = createMockRequestWithParams({ subId: 'test-sub-123' })
|
||||
const res = createMockResponse()
|
||||
|
||||
// closeSubscription resolves successfully even if subscription doesn't exist
|
||||
mockUseCases.manageSubscription.closeSubscription.resolves()
|
||||
|
||||
await uut.closeSubscription(req, res)
|
||||
|
||||
// Assert success response even for already-closed subscription
|
||||
assert.equal(res.statusValue, 200)
|
||||
assert.property(res.jsonData, 'message')
|
||||
assert.include(res.jsonData.message, 'closed successfully')
|
||||
})
|
||||
})
|
||||
|
||||
describe('#createSubscriptionPut()', () => {
|
||||
it('should call createSubscription', async () => {
|
||||
const req = createMockRequestWithParams({ subId: 'test-sub-123' })
|
||||
req.body = { kinds: [1] }
|
||||
const res = createMockResponse()
|
||||
|
||||
mockUseCases.manageSubscription.createSubscription.resolves()
|
||||
|
||||
await uut.createSubscriptionPut(req, res)
|
||||
|
||||
// Assert createSubscription was called
|
||||
assert.isTrue(mockUseCases.manageSubscription.createSubscription.calledOnce)
|
||||
})
|
||||
})
|
||||
|
||||
describe('#constructor()', () => {
|
||||
it('should require adapters instance', () => {
|
||||
try {
|
||||
// eslint-disable-next-line no-new
|
||||
new ReqRESTControllerLib({ useCases: mockUseCases })
|
||||
assert.equal(true, false, 'unexpected result')
|
||||
} catch (err) {
|
||||
assert.include(err.message, 'Adapters library required')
|
||||
}
|
||||
})
|
||||
|
||||
it('should require useCases instance', () => {
|
||||
try {
|
||||
// eslint-disable-next-line no-new
|
||||
new ReqRESTControllerLib({ adapters: {} })
|
||||
assert.equal(true, false, 'unexpected result')
|
||||
} catch (err) {
|
||||
assert.include(err.message, 'Use Cases library required')
|
||||
}
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,85 @@
|
||||
/*
|
||||
Unit tests for RESTControllers index.
|
||||
*/
|
||||
|
||||
import { assert } from 'chai'
|
||||
import sinon from 'sinon'
|
||||
|
||||
import RESTControllers from '../../../src/controllers/rest-api/index.js'
|
||||
import BlockchainRouter from '../../../src/controllers/rest-api/full-node/blockchain/index.js'
|
||||
|
||||
describe('#controllers/rest-api/index.js', () => {
|
||||
let sandbox
|
||||
let mockAdapters
|
||||
let mockUseCases
|
||||
|
||||
const createBlockchainUseCaseStubs = () => ({
|
||||
getBestBlockHash: () => {},
|
||||
getBlockchainInfo: () => {},
|
||||
getBlockCount: () => {},
|
||||
getBlockHeader: () => {},
|
||||
getBlockHeaders: () => {},
|
||||
getChainTips: () => {},
|
||||
getDifficulty: () => {},
|
||||
getMempoolEntry: () => {},
|
||||
getMempoolEntries: () => {},
|
||||
getMempoolAncestors: () => {},
|
||||
getMempoolInfo: () => {},
|
||||
getRawMempool: () => {},
|
||||
getTxOut: () => {},
|
||||
getTxOutProof: () => {},
|
||||
getTxOutProofs: () => {},
|
||||
verifyTxOutProof: () => {},
|
||||
verifyTxOutProofs: () => {},
|
||||
getBlock: () => {},
|
||||
getBlockHash: () => {}
|
||||
})
|
||||
|
||||
beforeEach(() => {
|
||||
sandbox = sinon.createSandbox()
|
||||
mockAdapters = {
|
||||
fullNode: {
|
||||
validateArraySize: sandbox.stub().returns(true)
|
||||
}
|
||||
}
|
||||
mockUseCases = {
|
||||
blockchain: createBlockchainUseCaseStubs()
|
||||
}
|
||||
})
|
||||
|
||||
afterEach(() => {
|
||||
sandbox.restore()
|
||||
})
|
||||
|
||||
describe('#constructor()', () => {
|
||||
it('should require adapters instance', () => {
|
||||
assert.throws(() => {
|
||||
// eslint-disable-next-line no-new
|
||||
new RESTControllers({ useCases: mockUseCases })
|
||||
}, /Adapters library required/)
|
||||
})
|
||||
|
||||
it('should require useCases instance', () => {
|
||||
assert.throws(() => {
|
||||
// eslint-disable-next-line no-new
|
||||
new RESTControllers({ adapters: mockAdapters })
|
||||
}, /Use Cases library required/)
|
||||
})
|
||||
})
|
||||
|
||||
describe('#attachRESTControllers()', () => {
|
||||
it('should instantiate blockchain router and attach to app', () => {
|
||||
const attachStub = sandbox.stub(BlockchainRouter.prototype, 'attach')
|
||||
const restControllers = new RESTControllers({
|
||||
adapters: mockAdapters,
|
||||
useCases: mockUseCases
|
||||
})
|
||||
const app = {}
|
||||
|
||||
restControllers.attachRESTControllers(app)
|
||||
|
||||
assert.isTrue(attachStub.calledOnce)
|
||||
assert.equal(attachStub.getCall(0).args[0], app)
|
||||
})
|
||||
})
|
||||
})
|
||||
Reference in New Issue
Block a user