Improved adapters unit tests

This commit is contained in:
Daniel Gonzalez
2025-09-23 16:34:09 -04:00
parent c0e0e42fc6
commit 0e51dc7f15
10 changed files with 979 additions and 35 deletions
+3
View File
@@ -110,6 +110,9 @@ class BchAdapter {
// Choose the BCH wallet service to use. // Choose the BCH wallet service to use.
async selectProvider (providerId) { async selectProvider (providerId) {
try { try {
if (!providerId || typeof providerId !== 'string') {
throw new Error('providerId must be a string!')
}
this.ipfs.ipfsCoordAdapter.config.preferredProvider = providerId this.ipfs.ipfsCoordAdapter.config.preferredProvider = providerId
return true return true
+8
View File
@@ -119,6 +119,10 @@ class IpfsFilesAdapter {
// Choose the ipfs-file-pin-service to use. // Choose the ipfs-file-pin-service to use.
async selectProvider (providerId) { async selectProvider (providerId) {
try { try {
if (!providerId || typeof providerId !== 'string') {
throw new Error('providerId must be a string!')
}
this.ipfs.ipfsCoordAdapter.config.selectedIpfsFileProvider = providerId this.ipfs.ipfsCoordAdapter.config.selectedIpfsFileProvider = providerId
return true return true
@@ -228,6 +232,10 @@ class IpfsFilesAdapter {
// Returns a promise that resolves to data when the RPC response is recieved. // Returns a promise that resolves to data when the RPC response is recieved.
async waitForRPCResponse (rpcId) { async waitForRPCResponse (rpcId) {
try { try {
if (!rpcId) {
throw new Error('rpcId can not be false or undefined')
}
// Initialize variables for tracking the return data. // Initialize variables for tracking the return data.
let dataFound = false let dataFound = false
let cnt = 0 let cnt = 0
+7 -3
View File
@@ -219,9 +219,10 @@ class IpfsCoordAdapter {
} }
} }
} catch (err) { } catch (err) {
console.log('Error in pollForP2wdbServices()', err)
// catch and handle known failure mode. // catch and handle known failure mode.
if ( if (
err.message.includes("Cannot read property 'peerList' of undefined") err.message.includes('Cannot read')
) { ) {
return return
} }
@@ -286,6 +287,9 @@ class IpfsCoordAdapter {
_this.config.preferredProvider && _this.config.preferredProvider &&
thisPeer === _this.config.preferredProvider thisPeer === _this.config.preferredProvider
) { ) {
console.log('preferredProvider: ', _this.config.preferredProvider)
console.log('thisPeer: ', thisPeer)
console.log('selectedServiceProvider: ', _this.state.selectedServiceProvider)
if (_this.state.selectedServiceProvider !== thisPeer) { if (_this.state.selectedServiceProvider !== thisPeer) {
console.log(`---->BCH wallet service switched to preferred peer: ${thisPeer}`) console.log(`---->BCH wallet service switched to preferred peer: ${thisPeer}`)
} }
@@ -310,7 +314,7 @@ class IpfsCoordAdapter {
} catch (err) { } catch (err) {
// catch and handle known failure mode. // catch and handle known failure mode.
if ( if (
err.message.includes("Cannot read property 'peerList' of undefined") err.message.includes('Cannot read')
) { ) {
return return
} }
@@ -400,7 +404,7 @@ class IpfsCoordAdapter {
} catch (err) { } catch (err) {
// catch and handle known failure mode. // catch and handle known failure mode.
if ( if (
err.message.includes("Cannot read property 'peerList' of undefined") err.message.includes('Cannot read')
) { ) {
return return
} }
+7
View File
@@ -109,6 +109,9 @@ class P2wdbAdapter {
// Choose the BCH wallet service to use. // Choose the BCH wallet service to use.
async selectProvider (providerId) { async selectProvider (providerId) {
try { try {
if (!providerId || typeof providerId !== 'string') {
throw new Error('providerId must be a string!')
}
this.ipfs.ipfsCoordAdapter.config.preferredP2wdbProvider = providerId this.ipfs.ipfsCoordAdapter.config.preferredP2wdbProvider = providerId
return true return true
@@ -215,6 +218,10 @@ class P2wdbAdapter {
// Returns a promise that resolves to data when the RPC response is recieved. // Returns a promise that resolves to data when the RPC response is recieved.
async waitForRPCResponse (rpcId) { async waitForRPCResponse (rpcId) {
try { try {
if (!rpcId) {
throw new Error('rpcId can not be false or undefined')
}
// Initialize variables for tracking the return data. // Initialize variables for tracking the return data.
let dataFound = false let dataFound = false
let cnt = 0 let cnt = 0
+186
View File
@@ -17,6 +17,7 @@ import cloneDeep from 'lodash.clonedeep'
import BchAdapter from '../../../src/adapters/bch/index.js' import BchAdapter from '../../../src/adapters/bch/index.js'
import adapters from '../mocks/adapters/index.js' import adapters from '../mocks/adapters/index.js'
import mockDataLib from '../mocks/use-cases/rest-api-mocks.js' import mockDataLib from '../mocks/use-cases/rest-api-mocks.js'
import ipfsCoordMocks from '../mocks/adapters/ipfs-coord-mocks.js'
const eventEmitter = new EventEmitter() const eventEmitter = new EventEmitter()
@@ -91,7 +92,30 @@ describe('#bch-use-case', () => {
assert.property(result, 'balances') assert.property(result, 'balances')
assert.isArray(result.balances) assert.isArray(result.balances)
}) })
it('should return false if data is not found', async () => {
// Mock dependencies
uut.ipfs.ipfsCoordAdapter.wallet = {
bchjs: {
Util: {
sleep: () => {}
}
}
}
// Mock data.
const rpcId = '123'
// Fill the queue with false values.
for (let i = 0; i < 10; i++) {
uut.rpcDataQueue.push({ payload: { id: '1234567890' } })
}
sandbox.stub(uut, 'sleep').resolves()
const result = await uut.waitForRPCResponse(rpcId)
// console.log('result: ', result)
assert.property(result, 'success')
assert.isFalse(result.success)
})
it('should catch and throw an error', async () => { it('should catch and throw an error', async () => {
try { try {
await uut.waitForRPCResponse() await uut.waitForRPCResponse()
@@ -114,12 +138,46 @@ describe('#bch-use-case', () => {
uut.rpcHandler(data) uut.rpcHandler(data)
}) })
it('should skip errors', async () => {
try {
sandbox.stub(uut, 'rpcDataQueue').throws(new Error('test error'))
await uut.rpcHandler()
} catch (err) {
assert.fail('Unexpected code path')
}
})
}) })
describe('#getStatus', () => { describe('#getStatus', () => {
it('should get the status from ipfs-coord', async () => { it('should get the status from ipfs-coord', async () => {
uut.ipfs.ipfsCoordAdapter.state.serviceProviders = ipfsCoordMocks.peers
uut.ipfs.ipfsCoordAdapter.ipfsCoord.thisNode.peerData = ipfsCoordMocks.peerData
const result = await uut.getStatus() const result = await uut.getStatus()
console.log('result: ', result) console.log('result: ', result)
assert.isObject(result)
})
it('should handle error', async () => {
try {
uut.ipfs.ipfsCoordAdapter.ipfsCoord.thisNode = null
await uut.getStatus()
assert.fail('Unexpected code path')
} catch (err) {
assert.include(err.message, 'Cannot read')
}
})
})
describe('#selectProvider', () => {
it('should select provider', async () => {
await uut.selectProvider('providerId to select')
assert.equal(uut.ipfs.ipfsCoordAdapter.config.preferredProvider, 'providerId to select')
})
it('should handle error', async () => {
try {
await uut.selectProvider()
assert.fail('Unexpected code path')
} catch (err) {
assert.include(err.message, 'providerId must be a string!')
}
}) })
}) })
@@ -154,6 +212,18 @@ describe('#bch-use-case', () => {
assert.equal(err.message, 'test error') assert.equal(err.message, 'test error')
} }
}) })
it('should throw an error if selected provider is not defined', async () => {
try {
uut.ipfs.ipfsCoordAdapter.state.selectedServiceProvider = null
await uut.getBalances()
assert.fail('Unexpected code path')
} catch (err) {
// console.log(err)
assert.equal(err.message, 'No BCH Wallet Service provider available yet.')
}
})
}) })
describe('#getUtxos', () => { describe('#getUtxos', () => {
@@ -184,6 +254,19 @@ describe('#bch-use-case', () => {
assert.equal(err.message, 'addr required when calling getUtxos') assert.equal(err.message, 'addr required when calling getUtxos')
} }
}) })
it('should throw an error if selected provider is not defined', async () => {
try {
uut.ipfs.ipfsCoordAdapter.state.selectedServiceProvider = null
const addr = 'addr'
await uut.getUtxos(addr)
assert.fail('Unexpected code path')
} catch (err) {
// console.log(err)
assert.equal(err.message, 'No BCH Wallet Service provider available yet.')
}
})
}) })
describe('#getUtxosBulk', () => { describe('#getUtxosBulk', () => {
@@ -230,6 +313,19 @@ describe('#bch-use-case', () => {
assert.equal(err.message, 'addresses parameter must not exceed 20 elements') assert.equal(err.message, 'addresses parameter must not exceed 20 elements')
} }
}) })
it('should throw an error if selected provider is not defined', async () => {
try {
uut.ipfs.ipfsCoordAdapter.state.selectedServiceProvider = null
const addrs = ['addr']
await uut.getUtxosBulk(addrs)
assert.fail('Unexpected code path')
} catch (err) {
// console.log(err)
assert.equal(err.message, 'No BCH Wallet Service provider available yet.')
}
})
}) })
describe('#broadcast', () => { describe('#broadcast', () => {
@@ -260,6 +356,19 @@ describe('#bch-use-case', () => {
assert.equal(err.message, 'hex required when calling broadcast') assert.equal(err.message, 'hex required when calling broadcast')
} }
}) })
it('should throw an error if selected provider is not defined', async () => {
try {
uut.ipfs.ipfsCoordAdapter.state.selectedServiceProvider = null
const hex = 'abc123'
await uut.broadcast(hex)
assert.fail('Unexpected code path')
} catch (err) {
// console.log(err)
assert.equal(err.message, 'No BCH Wallet Service provider available yet.')
}
})
}) })
describe('#getTransactions', () => { describe('#getTransactions', () => {
@@ -290,6 +399,19 @@ describe('#bch-use-case', () => {
assert.equal(err.message, 'address required when calling getTransactions()') assert.equal(err.message, 'address required when calling getTransactions()')
} }
}) })
it('should throw an error if selected provider is not defined', async () => {
try {
uut.ipfs.ipfsCoordAdapter.state.selectedServiceProvider = null
const addr = 'abc123'
await uut.getTransactions(addr)
assert.fail('Unexpected code path')
} catch (err) {
// console.log(err)
assert.equal(err.message, 'No BCH Wallet Service provider available yet.')
}
})
}) })
describe('#getTransaction', () => { describe('#getTransaction', () => {
@@ -320,6 +442,19 @@ describe('#bch-use-case', () => {
assert.equal(err.message, 'txids required when calling getTransaction()') assert.equal(err.message, 'txids required when calling getTransaction()')
} }
}) })
it('should throw an error if selected provider is not defined', async () => {
try {
uut.ipfs.ipfsCoordAdapter.state.selectedServiceProvider = null
const txid = 'abc123'
await uut.getTransaction(txid)
assert.fail('Unexpected code path')
} catch (err) {
// console.log(err)
assert.equal(err.message, 'No BCH Wallet Service provider available yet.')
}
})
}) })
describe('#getPubKey', () => { describe('#getPubKey', () => {
@@ -350,6 +485,19 @@ describe('#bch-use-case', () => {
assert.equal(err.message, 'address required when calling getPubKey()') assert.equal(err.message, 'address required when calling getPubKey()')
} }
}) })
it('should throw an error if selected provider is not defined', async () => {
try {
uut.ipfs.ipfsCoordAdapter.state.selectedServiceProvider = null
const addr = 'abc123'
await uut.getPubKey(addr)
assert.fail('Unexpected code path')
} catch (err) {
// console.log(err)
assert.equal(err.message, 'No BCH Wallet Service provider available yet.')
}
})
}) })
describe('#utxoIsValid', () => { describe('#utxoIsValid', () => {
@@ -380,6 +528,18 @@ describe('#bch-use-case', () => {
assert.equal(err.message, 'utxo required when calling utxoIsValid()') assert.equal(err.message, 'utxo required when calling utxoIsValid()')
} }
}) })
it('should throw an error if selected provider is not defined', async () => {
try {
uut.ipfs.ipfsCoordAdapter.state.selectedServiceProvider = null
const utxo = { a: 'b' }
await uut.utxoIsValid(utxo)
assert.fail('Unexpected code path')
} catch (err) {
// console.log(err)
assert.equal(err.message, 'No BCH Wallet Service provider available yet.')
}
})
}) })
describe('#getTokenData', () => { describe('#getTokenData', () => {
@@ -410,6 +570,19 @@ describe('#bch-use-case', () => {
assert.equal(err.message, 'tokenId required when calling getTokenData()') assert.equal(err.message, 'tokenId required when calling getTokenData()')
} }
}) })
it('should throw an error if selected provider is not defined', async () => {
try {
uut.ipfs.ipfsCoordAdapter.state.selectedServiceProvider = null
const tokenId = 'blah'
await uut.getTokenData(tokenId)
assert.fail('Unexpected code path')
} catch (err) {
// console.log(err)
assert.equal(err.message, 'No BCH Wallet Service provider available yet.')
}
})
}) })
describe('#getTokenData2', () => { describe('#getTokenData2', () => {
@@ -440,5 +613,18 @@ describe('#bch-use-case', () => {
assert.equal(err.message, 'tokenId required when calling getTokenData2()') assert.equal(err.message, 'tokenId required when calling getTokenData2()')
} }
}) })
it('should throw an error if selected provider is not defined', async () => {
try {
uut.ipfs.ipfsCoordAdapter.state.selectedServiceProvider = null
const tokenId = 'blah'
await uut.getTokenData2(tokenId)
assert.fail('Unexpected code path')
} catch (err) {
// console.log(err)
assert.equal(err.message, 'No BCH Wallet Service provider available yet.')
}
})
}) })
}) })
+191 -29
View File
@@ -151,7 +151,21 @@ describe('#IPFS', () => {
} }
}) })
}) })
describe('#subscribeToChat', () => {
it('should subscribe to the chat channel', async () => {
// Mock dependencies
uut.ipfsCoord = {
adapters: {
pubsub: {
subscribeToPubsubChannel: async () => {
}
}
}
}
await uut.subscribeToChat()
})
})
describe('#pollForBchServices', () => { describe('#pollForBchServices', () => {
it('should find and select the wallet service', () => { it('should find and select the wallet service', () => {
uut.ipfsCoord = { uut.ipfsCoord = {
@@ -169,46 +183,194 @@ describe('#IPFS', () => {
'QmWkjYRRTaxVEuGK8ip2X3trVyJShFs6U9g1h9x6fK5mZ2' 'QmWkjYRRTaxVEuGK8ip2X3trVyJShFs6U9g1h9x6fK5mZ2'
) )
}) })
it('should use preferredProvider if set', async () => {
// Mock dependencies
uut.ipfsCoord = {
thisNode: {
peerList: mockData.peers,
peerData: mockData.peerData
}
}
// Define already selected service provider.
uut.state.serviceProviders = [mockData.peers[2]]
uut.state.selectedServiceProvider = mockData.peers[2]
// Define preferred provider.
uut.config.preferredProvider = mockData.peers[0]
sandbox.stub(uut.semver, 'gt').returns(true)
it('should catch and report errors', () => { await uut.pollForBchServices()
uut.pollForBchServices()
assert.equal(uut.state.selectedServiceProvider, uut.config.preferredProvider)
})
it('should log unknown errors', async () => {
// Mock dependencies
uut.ipfsCoord = {
thisNode: {
peerList: mockData.peers,
peerData: mockData.peerData
}
}
sandbox.stub(uut.semver, 'gt').throws(new Error('test error'))
await uut.pollForBchServices()
assert.equal(uut.state.selectedServiceProvider, '')
assert.isOk(true, 'Not throwing an error is a success.')
})
it('should handle ipfsCoord errors', async () => {
uut.ipfsCoord = {
}
await uut.pollForBchServices()
assert.equal(uut.state.selectedServiceProvider, '')
assert.isOk(true, 'Not throwing an error is a success.') assert.isOk(true, 'Not throwing an error is a success.')
}) })
}) })
// describe('#peerInputHandler', () => { describe('#pollForP2wdbServices', () => {
// it('should emit an event trigger', () => { it('should poll for P2WDB services', async () => {
// const data = 'some data'
//
// uut.peerInputHandler(data)
//
// assert.isOk(true, 'Not throwing an error is a success')
// })
//
// it('should catch and report errors', () => {
// // Force an error
// sandbox.stub(uut.eventEmitter, 'emit').throws(new Error('test error'))
//
// uut.peerInputHandler()
//
// assert.isOk(true, 'Not throwing an error is a success.')
// })
// })
describe('#subscribeToChat', () => {
it('should subscribe to the chat channel', async () => {
// Mock dependencies // Mock dependencies
uut.ipfsCoord = { uut.ipfsCoord = {
adapters: { thisNode: {
pubsub: { peerList: mockData.peers,
subscribeToPubsubChannel: async () => { peerData: mockData.peerData
} }
} }
sandbox.stub(uut.semver, 'gt').returns(true)
await uut.pollForP2wdbServices()
assert.equal(uut.state.selectedP2wdbProvider, mockData.peers[2])
})
it('should use preferredP2wdbProvider if set', async () => {
// Mock dependencies
uut.ipfsCoord = {
thisNode: {
peerList: mockData.peers,
peerData: mockData.peerData
} }
} }
await uut.subscribeToChat() uut.config.preferredP2wdbProvider = mockData.peers[2]
sandbox.stub(uut.semver, 'gt').returns(true)
await uut.pollForP2wdbServices()
assert.equal(uut.state.selectedP2wdbProvider, uut.config.preferredP2wdbProvider)
})
it('should handle a peer that does not match the protocol', async () => {
// Mock dependencies
uut.ipfsCoord = {
thisNode: {
peerList: mockData.peers,
peerData: mockData.peerData
}
}
sandbox.stub(uut.semver, 'gt').returns(false)
await uut.pollForP2wdbServices()
assert.equal(uut.state.selectedP2wdbProvider, '')
})
it('should log unknown errors', async () => {
// Mock dependencies
uut.ipfsCoord = {
thisNode: {
peerList: mockData.peers,
peerData: mockData.peerData
}
}
sandbox.stub(uut.semver, 'gt').throws(new Error('test error'))
await uut.pollForP2wdbServices()
assert.equal(uut.state.selectedP2wdbProvider, '')
assert.isOk(true, 'Not throwing an error is a success.')
})
it('should handle ipfsCoord errors', async () => {
uut.ipfsCoord = {
}
await uut.pollForP2wdbServices()
assert.equal(uut.state.selectedP2wdbProvider, '')
assert.isOk(true, 'Not throwing an error is a success.')
})
})
describe('#pollForIpfsFileServices', () => {
it('should poll for IPFS File services', async () => {
// Mock dependencies
uut.ipfsCoord = {
thisNode: {
peerList: mockData.peers,
peerData: mockData.peerData
}
}
sandbox.stub(uut.semver, 'gt').returns(true)
await uut.pollForIpfsFileServices()
assert.equal(uut.state.selectedIpfsFileProvider, mockData.peers[3])
})
it('should use preferredIpfsFileProvider if set', async () => {
// Mock dependencies
uut.ipfsCoord = {
thisNode: {
peerList: mockData.peers,
peerData: mockData.peerData
}
}
// Define already selected service provider.
uut.state.ipfsFileProviders = [mockData.peers[0]]
uut.state.selectedIpfsFileProvider = mockData.peers[0]
uut.config.preferredIpfsFileProvider = mockData.peers[3]
sandbox.stub(uut.semver, 'satisfies').returns(true)
await uut.pollForIpfsFileServices()
assert.equal(uut.state.selectedIpfsFileProvider, uut.config.preferredIpfsFileProvider)
})
it('should handle a peer that does not match the protocol', async () => {
// Mock dependencies
uut.ipfsCoord = {
thisNode: {
peerList: mockData.peers,
peerData: mockData.peerData
}
}
sandbox.stub(uut.semver, 'satisfies').returns(false)
await uut.pollForIpfsFileServices()
assert.equal(uut.state.selectedIpfsFileProvider, '')
})
it('should log unknown errors', async () => {
// Mock dependencies
uut.ipfsCoord = {
thisNode: {
peerList: mockData.peers,
peerData: mockData.peerData
}
}
sandbox.stub(uut.semver, 'satisfies').throws(new Error('test error'))
await uut.pollForIpfsFileServices()
assert.equal(uut.state.selectedIpfsFileProvider, '')
assert.isOk(true, 'Not throwing an error is a success.')
})
it('should handle ipfsCoord errors', async () => {
uut.ipfsCoord = {
}
await uut.pollForIpfsFileServices()
assert.equal(uut.state.selectedIpfsFileProvider, '')
assert.isOk(true, 'Not throwing an error is a success.')
}) })
}) })
}) })
@@ -0,0 +1,243 @@
/*
Unit tests for the IPFS Files Adapter.
*/
import { assert } from 'chai'
import sinon from 'sinon'
import cloneDeep from 'lodash.clonedeep'
import adapters from '../mocks/adapters/index.js'
import IPFSFilesAdapter from '../../../src/adapters/ipfs-files/index.js'
// import IPFSMock from '../mocks/ipfs-mock.js'
import mockDataLib from '../mocks/adapters/ipfs-coord-mocks.js'
import mockDataRPC from '../mocks/use-cases/rest-api-mocks.js'
describe('#IPFS-Files', () => {
let uut
let sandbox
let mockData
beforeEach(() => {
const ipfs = adapters.ipfs
uut = new IPFSFilesAdapter({ ipfs })
sandbox = sinon.createSandbox()
mockData = cloneDeep(mockDataLib)
})
afterEach(() => {
sandbox.restore()
clearInterval(uut.pollBchServiceInterval)
clearInterval(uut.pollP2wdbServiceInterval)
})
describe('#constructor', () => {
it('should throw an error if ipfs instance is not included', () => {
try {
uut = new IPFSFilesAdapter()
assert.fail('Unexpected code path')
} catch (err) {
assert.include(
err.message,
'An instance of IPFS must be passed when instantiating the IPFS Files Adapter library.'
)
}
})
})
describe('#rpcHandler', () => {
it('should add RPC data to queue', () => {
const data = {
payload: {
id: '123'
}
}
uut.rpcHandler(data)
})
it('should skip errors', async () => {
try {
sandbox.stub(uut.rpcDataQueue, 'push').throws(new Error('test error'))
await uut.rpcHandler()
} catch (err) {
assert.fail('Unexpected code path')
}
})
})
describe('#getStatus', () => {
it('should get the status from ipfs-coord', async () => {
uut.ipfs.ipfsCoordAdapter.state.ipfsFileProviders = mockData.peers
uut.ipfs.ipfsCoordAdapter.ipfsCoord.thisNode = { peerData: mockData.peerData }
await uut.getStatus()
})
it('should handle error', async () => {
try {
uut.ipfs.ipfsCoordAdapter.ipfsCoord.thisNode = null
await uut.getStatus()
assert.fail('Unexpected code path')
} catch (err) {
assert.include(err.message, 'Cannot read')
}
})
})
describe('#selectProvider', () => {
it('should select provider', async () => {
await uut.selectProvider('providerId to select')
assert.equal(uut.ipfs.ipfsCoordAdapter.config.selectedIpfsFileProvider, 'providerId to select')
})
it('should handle error', async () => {
try {
await uut.selectProvider()
assert.fail('Unexpected code path')
} catch (err) {
assert.include(err.message, 'providerId must be a string!')
}
})
})
describe('#getFileMetadata', () => {
it('should get file metadata', async () => {
uut.ipfs.ipfsCoordAdapter.state.selectedIpfsFileProvider = mockData.peers[0]
sandbox.stub(uut, 'waitForRPCResponse').resolves({
success: true
})
const result = await uut.getFileMetadata({ cid: 'cid to get' })
assert.equal(result.success, true)
})
it('should handle error if no cid is provided', async () => {
try {
await uut.getFileMetadata()
assert.fail('Unexpected code path')
} catch (err) {
assert.include(err.message, 'getFileMetadata() cid input hash must be a string.')
}
})
it('should handle error if no provider is selected', async () => {
try {
uut.ipfs.ipfsCoordAdapter.state.selectedIpfsFileProvider = null
await uut.getFileMetadata({ cid: 'cid to get' })
assert.fail('Unexpected code path')
} catch (err) {
assert.include(err.message, 'No IPFS File Pin Service provider available yet.')
}
})
})
describe('#getPins', () => {
it('should get pins', async () => {
uut.ipfs.ipfsCoordAdapter.state.selectedIpfsFileProvider = mockData.peers[0]
sandbox.stub(uut, 'waitForRPCResponse').resolves({
success: true
})
const result = await uut.getPins()
assert.equal(result.success, true)
})
it('should handle error if no provider is selected', async () => {
try {
uut.ipfs.ipfsCoordAdapter.state.selectedIpfsFileProvider = null
await uut.getPins()
assert.fail('Unexpected code path')
} catch (err) {
assert.include(err.message, 'No IPFS File Pin Service provider available yet.')
}
})
})
describe('#waitForRPCResponse', () => {
it('should resolve when data is received', async () => {
// Mock dependencies
uut.ipfs.ipfsCoordAdapter.wallet = {
bchjs: {
Util: {
sleep: () => {}
}
}
}
// Mock data.
const rpcId = '123'
uut.rpcDataQueue.push(mockDataRPC.rpcData)
const result = await uut.waitForRPCResponse(rpcId)
// console.log('result: ', result)
assert.property(result, 'success')
assert.equal(result.success, true)
assert.property(result, 'balances')
assert.isArray(result.balances)
})
it('should return false if data is not found', async () => {
// Mock dependencies
uut.ipfs.ipfsCoordAdapter.wallet = {
bchjs: {
Util: {
sleep: () => {}
}
}
}
// Mock data.
const rpcId = '123'
// Fill the queue with false values.
for (let i = 0; i < 10; i++) {
uut.rpcDataQueue.push({ payload: { id: '1234567890' } })
}
sandbox.stub(uut.ipfs.ipfsCoordAdapter.bchjs.Util, 'sleep').resolves()
const result = await uut.waitForRPCResponse(rpcId)
// console.log('result: ', result)
assert.property(result, 'success')
assert.isFalse(result.success)
})
it('should catch and throw an error', async () => {
try {
uut.ipfs.ipfsCoordAdapter.wallet = {
bchjs: {
Util: {
sleep: () => {}
}
}
}
await uut.waitForRPCResponse()
assert.fail('Unexpected code path')
} catch (err) {
// console.log('error: ', err)
assert.include(err.message, 'rpcId can not be false or undefined')
}
})
})
describe('#pinClaim', () => {
it('should process pinClaim', async () => {
uut.ipfs.ipfsCoordAdapter.state.selectedIpfsFileProvider = mockData.peers[0]
sandbox.stub(uut, 'waitForRPCResponse').resolves({
success: true
})
const result = await uut.pinClaim({
proofOfBurnTxid: 'proofOfBurnTxid to pin',
cid: 'cid to pin',
claimTxid: 'claimTxid to pin',
filename: 'filename to pin',
address: 'address to pin'
})
assert.equal(result.success, true)
})
it('should handle error if no provider is selected', async () => {
try {
uut.ipfs.ipfsCoordAdapter.state.selectedIpfsFileProvider = null
await uut.pinClaim({
proofOfBurnTxid: 'proofOfBurnTxid to pin',
cid: 'cid to pin',
claimTxid: 'claimTxid to pin',
filename: 'filename to pin',
address: 'address to pin'
})
assert.fail('Unexpected code path')
} catch (err) {
assert.include(err.message, 'No IPFS File Provider Service is available yet. Try again in a few seconds.')
}
})
})
})
+223
View File
@@ -0,0 +1,223 @@
/*
Unit tests for P2WDB Adapter.
*/
import { assert } from 'chai'
import sinon from 'sinon'
import cloneDeep from 'lodash.clonedeep'
import adapters from '../mocks/adapters/index.js'
import P2WDBAdapter from '../../../src/adapters/p2wdb/index.js'
// import IPFSMock from '../mocks/ipfs-mock.js'
import mockDataLib from '../mocks/adapters/ipfs-coord-mocks.js'
import mockDataRPC from '../mocks/use-cases/rest-api-mocks.js'
describe('#P2WDB Adapter', () => {
let uut
let sandbox
let mockData
beforeEach(() => {
const ipfs = adapters.ipfs
uut = new P2WDBAdapter({ ipfs })
sandbox = sinon.createSandbox()
mockData = cloneDeep(mockDataLib)
})
afterEach(() => {
sandbox.restore()
clearInterval(uut.pollBchServiceInterval)
clearInterval(uut.pollP2wdbServiceInterval)
})
describe('#constructor', () => {
it('should throw an error if ipfs instance is not included', () => {
try {
uut = new P2WDBAdapter()
assert.fail('Unexpected code path')
} catch (err) {
assert.include(
err.message,
'An instance of IPFS must be passed when instantiating the P2WDB Adapter library.'
)
}
})
})
describe('#rpcHandler', () => {
it('should add RPC data to queue', () => {
const data = {
payload: {
id: '123'
}
}
uut.rpcHandler(data)
})
it('should skip errors', async () => {
try {
sandbox.stub(uut.rpcDataQueue, 'push').throws(new Error('test error'))
await uut.rpcHandler()
} catch (err) {
assert.fail('Unexpected code path')
}
})
})
describe('#getStatus', () => {
it('should get the status from ipfs-coord', async () => {
uut.ipfs.ipfsCoordAdapter.state.p2wdbProviders = mockData.peers
uut.ipfs.ipfsCoordAdapter.ipfsCoord.thisNode = { peerData: mockData.peerData }
await uut.getStatus()
})
it('should handle error', async () => {
try {
uut.ipfs.ipfsCoordAdapter.ipfsCoord.thisNode = null
await uut.getStatus()
assert.fail('Unexpected code path')
} catch (err) {
assert.include(err.message, 'Cannot read')
}
})
})
describe('#selectProvider', () => {
it('should select provider', async () => {
await uut.selectProvider('providerId to select')
assert.equal(uut.ipfs.ipfsCoordAdapter.config.preferredP2wdbProvider, 'providerId to select')
})
it('should handle error', async () => {
try {
await uut.selectProvider()
assert.fail('Unexpected code path')
} catch (err) {
assert.include(err.message, 'providerId must be a string!')
}
})
})
describe('#waitForRPCResponse', () => {
it('should resolve when data is received', async () => {
// Mock dependencies
uut.ipfs.ipfsCoordAdapter.wallet = {
bchjs: {
Util: {
sleep: () => {}
}
}
}
// Mock data.
const rpcId = '123'
uut.rpcDataQueue.push(mockDataRPC.rpcData)
const result = await uut.waitForRPCResponse(rpcId)
// console.log('result: ', result)
assert.property(result, 'success')
assert.equal(result.success, true)
assert.property(result, 'balances')
assert.isArray(result.balances)
})
it('should return false if data is not found', async () => {
// Mock dependencies
uut.ipfs.ipfsCoordAdapter.wallet = {
bchjs: {
Util: {
sleep: () => {}
}
}
}
// Mock data.
const rpcId = '123'
// Fill the queue with false values.
for (let i = 0; i < 10; i++) {
uut.rpcDataQueue.push({ payload: { id: '1234567890' } })
}
sandbox.stub(uut.ipfs.ipfsCoordAdapter.bchjs.Util, 'sleep').resolves()
const result = await uut.waitForRPCResponse(rpcId)
// console.log('result: ', result)
assert.property(result, 'success')
assert.isFalse(result.success)
})
it('should catch and throw an error', async () => {
try {
uut.ipfs.ipfsCoordAdapter.wallet = {
bchjs: {
Util: {
sleep: () => {}
}
}
}
await uut.waitForRPCResponse()
assert.fail('Unexpected code path')
} catch (err) {
// console.log('error: ', err)
assert.include(err.message, 'rpcId can not be false or undefined')
}
})
})
describe('#writeEntry', () => {
it('should handle error if no provider is selected', async () => {
try {
uut.ipfs.ipfsCoordAdapter.state.selectedP2wdbProvider = null
await uut.writeEntry({
txid: 'txid to write',
signature: 'signature to write',
message: 'message to write',
data: 'data to write'
})
assert.fail('Unexpected code path')
} catch (err) {
assert.include(err.message, 'No P2WDB Service provider available yet.')
}
})
it('should process writeEntry', async () => {
uut.ipfs.ipfsCoordAdapter.state.selectedP2wdbProvider = mockData.peers[0]
sandbox.stub(uut, 'waitForRPCResponse').resolves({
success: true
})
const result = await uut.writeEntry({
txid: 'txid to write',
signature: 'signature to write',
message: 'message to write',
data: 'data to write'
})
assert.equal(result.success, true)
})
})
describe('#getEntryByHash', () => {
it('should handle error if hash is not provided', async () => {
try {
await uut.getEntryByHash()
assert.fail('Unexpected code path')
} catch (err) {
assert.include(err.message, 'getEntry() input hash must be a string.')
}
})
it('should handle error if no provider is selected', async () => {
try {
uut.ipfs.ipfsCoordAdapter.state.selectedP2wdbProvider = null
await uut.getEntryByHash('hash to get')
assert.fail('Unexpected code path')
} catch (err) {
assert.include(err.message, 'No P2WDB Service provider available yet.')
}
})
it('should process getEntryByHash', async () => {
uut.ipfs.ipfsCoordAdapter.state.selectedP2wdbProvider = mockData.peers[0]
sandbox.stub(uut, 'waitForRPCResponse').resolves({
success: true
})
const result = await uut.getEntryByHash('hash to get')
assert.equal(result.success, true)
})
})
})
+1 -1
View File
@@ -30,7 +30,7 @@ class IpfsAdapter {
class IpfsCoordAdapter { class IpfsCoordAdapter {
constructor () { constructor () {
this.config = {}
this.ipfsCoord = { this.ipfsCoord = {
adapters: { adapters: {
ipfs: { ipfs: {
+110 -2
View File
@@ -4,7 +4,9 @@
const peers = [ const peers = [
'QmWkjYRRTaxVEuGK8ip2X3trVyJShFs6U9g1h9x6fK5mZ2', 'QmWkjYRRTaxVEuGK8ip2X3trVyJShFs6U9g1h9x6fK5mZ2',
'QmQqjVVD3CS6zaJmoSdt6GwX1c1tD5T9FZas4qxLbru8xN' 'QmQqjVVD3CS6zaJmoSdt6GwX1c1tD5T9FZas4qxLbru8xN',
'QmWkjYRRTaxVEuGK8ip2X3trVyJShFs6U9g1h9x6fK5mT3',
'QmWkjYRRTaxVEuGK8ip2X3trVyJShFs6U9g1h9x6fK5mD4'
] ]
const peerData = [ const peerData = [
@@ -110,7 +112,113 @@ const peerData = [
}, },
updatedAt: '2021-08-24T23:13:20.411Z' updatedAt: '2021-08-24T23:13:20.411Z'
} }
} },
{
from: 'QmWkjYRRTaxVEuGK8ip2X3trVyJShFs6U9g1h9x6fK5mT3',
channel: 'psf-ipfs-coordination-001',
data: {
apiName: 'ipfs-coord-announce',
apiVersion: '1.3.2',
apiInfo:
'You should put an IPFS hash or web URL here to your documentation.',
ipfsId: 'QmWkjYRRTaxVEuGK8ip2X3trVyJShFs6U9g1h9x6fK5mZ2',
type: 'node.js',
ipfsMultiaddrs: [
'/ip4/127.0.0.1/tcp/5701/p2p/QmWkjYRRTaxVEuGK8ip2X3trVyJShFs6U9g1h9x6fK5mZ2',
'/ip4/127.0.0.1/tcp/5702/ws/p2p/QmWkjYRRTaxVEuGK8ip2X3trVyJShFs6U9g1h9x6fK5mZ2',
'/ip4/192.168.0.2/tcp/5701/p2p/QmWkjYRRTaxVEuGK8ip2X3trVyJShFs6U9g1h9x6fK5mZ2',
'/ip4/192.168.0.2/tcp/5702/ws/p2p/QmWkjYRRTaxVEuGK8ip2X3trVyJShFs6U9g1h9x6fK5mZ2'
],
orbitdb:
'/orbitdb/zdpuAntCMCcmZuFy2hq5F7kpcti3yHa34LLZHzyVqorkoSpAJ/QmWkjYRRTaxVEuGK8ip2X3trVyJShFs6U9g1h9x6fK5mZ221082417',
circuitRelays: [],
isCircuitRelay: false,
cryptoAddresses: [
{
blockchain: 'BCH',
type: 'cashAddr',
address: 'bitcoincash:qru6kq3p4tv6z2lmy0n560lyhh3z2feay5gjzggc37'
},
{
blockchain: 'BCH',
type: 'slpAddr',
address: 'simpleledger:qru6kq3p4tv6z2lmy0n560lyhh3z2feay5yffnac0q'
}
],
encryptPubKey:
'021db6e97650659653ba61dd493dc348a7429cdcbafd4fb73b08b1223bf5bd98df',
jsonLd: {
'@context': 'https://schema.org/',
'@type': 'WebAPI',
name: 'trout-bch-wallet-service-dev',
version: '1.11.12',
protocol: 'p2wdb',
description:
'IPFS service providing BCH blockchain access needed by a wallet.',
documentation: 'https://ipfs-bch-wallet-service.fullstack.cash/',
provider: {
'@type': 'Organization',
name: 'Permissionless Software Foundation',
url: 'https://PSFoundation.cash'
},
identifier: 'QmWkjYRRTaxVEuGK8ip2X3trVyJShFs6U9g1h9x6fK5mZ2'
},
updatedAt: '2021-08-24T23:13:19.306Z'
}
},
{
from: 'QmWkjYRRTaxVEuGK8ip2X3trVyJShFs6U9g1h9x6fK5mD4',
channel: 'psf-ipfs-coordination-001',
data: {
apiName: 'ipfs-coord-announce',
apiVersion: '1.3.2',
apiInfo:
'You should put an IPFS hash or web URL here to your documentation.',
ipfsId: 'QmWkjYRRTaxVEuGK8ip2X3trVyJShFs6U9g1h9x6fK5mZ2',
type: 'node.js',
ipfsMultiaddrs: [
'/ip4/127.0.0.1/tcp/5701/p2p/QmWkjYRRTaxVEuGK8ip2X3trVyJShFs6U9g1h9x6fK5mZ2',
'/ip4/127.0.0.1/tcp/5702/ws/p2p/QmWkjYRRTaxVEuGK8ip2X3trVyJShFs6U9g1h9x6fK5mZ2',
'/ip4/192.168.0.2/tcp/5701/p2p/QmWkjYRRTaxVEuGK8ip2X3trVyJShFs6U9g1h9x6fK5mZ2',
'/ip4/192.168.0.2/tcp/5702/ws/p2p/QmWkjYRRTaxVEuGK8ip2X3trVyJShFs6U9g1h9x6fK5mZ2'
],
orbitdb:
'/orbitdb/zdpuAntCMCcmZuFy2hq5F7kpcti3yHa34LLZHzyVqorkoSpAJ/QmWkjYRRTaxVEuGK8ip2X3trVyJShFs6U9g1h9x6fK5mZ221082417',
circuitRelays: [],
isCircuitRelay: false,
cryptoAddresses: [
{
blockchain: 'BCH',
type: 'cashAddr',
address: 'bitcoincash:qru6kq3p4tv6z2lmy0n560lyhh3z2feay5gjzggc37'
},
{
blockchain: 'BCH',
type: 'slpAddr',
address: 'simpleledger:qru6kq3p4tv6z2lmy0n560lyhh3z2feay5yffnac0q'
}
],
encryptPubKey:
'021db6e97650659653ba61dd493dc348a7429cdcbafd4fb73b08b1223bf5bd98df',
jsonLd: {
'@context': 'https://schema.org/',
'@type': 'WebAPI',
name: 'trout-bch-wallet-service-dev',
version: '1.11.12',
protocol: 'ipfs-file-pin-service',
description:
'IPFS service providing BCH blockchain access needed by a wallet.',
documentation: 'https://ipfs-bch-wallet-service.fullstack.cash/',
provider: {
'@type': 'Organization',
name: 'Permissionless Software Foundation',
url: 'https://PSFoundation.cash'
},
identifier: 'QmWkjYRRTaxVEuGK8ip2X3trVyJShFs6U9g1h9x6fK5mZ2'
},
updatedAt: '2021-08-24T23:13:19.306Z'
}
},
] ]
// module.exports = { // module.exports = {