mirror of
https://github.com/fluencelabs/js-libp2p
synced 2025-03-17 08:00:51 +00:00
BREAKING CHANGE: dht experimental flag was removed and a dht.enabled property was added to the config
295 lines
8.4 KiB
JavaScript
295 lines
8.4 KiB
JavaScript
/* eslint-env mocha */
|
|
/* eslint max-nested-callbacks: ["error", 8] */
|
|
|
|
'use strict'
|
|
|
|
const chai = require('chai')
|
|
chai.use(require('dirty-chai'))
|
|
const expect = chai.expect
|
|
const parallel = require('async/parallel')
|
|
const _times = require('lodash.times')
|
|
const DelegatedPeerRouter = require('libp2p-delegated-peer-routing')
|
|
const sinon = require('sinon')
|
|
const nock = require('nock')
|
|
|
|
const createNode = require('./utils/create-node')
|
|
|
|
describe('.peerRouting', () => {
|
|
describe('via the dht', () => {
|
|
let nodeA
|
|
let nodeB
|
|
let nodeC
|
|
let nodeD
|
|
let nodeE
|
|
|
|
before('create the outer ring of connections', (done) => {
|
|
const tasks = _times(5, () => (cb) => {
|
|
createNode('/ip4/0.0.0.0/tcp/0', (err, node) => {
|
|
expect(err).to.not.exist()
|
|
node.start((err) => cb(err, node))
|
|
})
|
|
})
|
|
|
|
parallel(tasks, (err, nodes) => {
|
|
expect(err).to.not.exist()
|
|
nodeA = nodes[0]
|
|
nodeB = nodes[1]
|
|
nodeC = nodes[2]
|
|
nodeD = nodes[3]
|
|
nodeE = nodes[4]
|
|
|
|
parallel([
|
|
(cb) => nodeA.dial(nodeB.peerInfo, cb),
|
|
(cb) => nodeB.dial(nodeC.peerInfo, cb),
|
|
(cb) => nodeC.dial(nodeD.peerInfo, cb),
|
|
(cb) => nodeD.dial(nodeE.peerInfo, cb),
|
|
(cb) => nodeE.dial(nodeA.peerInfo, cb)
|
|
], (err) => {
|
|
expect(err).to.not.exist()
|
|
// Give the kbucket time to fill in the dht
|
|
setTimeout(done, 250)
|
|
})
|
|
})
|
|
})
|
|
|
|
after((done) => {
|
|
parallel([
|
|
(cb) => nodeA.stop(cb),
|
|
(cb) => nodeB.stop(cb),
|
|
(cb) => nodeC.stop(cb),
|
|
(cb) => nodeD.stop(cb),
|
|
(cb) => nodeE.stop(cb)
|
|
], done)
|
|
})
|
|
|
|
it('should use the nodes dht', (done) => {
|
|
const stub = sinon.stub(nodeA._dht, 'findPeer').callsFake(() => {
|
|
stub.restore()
|
|
done()
|
|
})
|
|
|
|
nodeA.peerRouting.findPeer()
|
|
})
|
|
|
|
describe('connected in an el ring', () => {
|
|
it('should be able to find a peer we are not directly connected to', (done) => {
|
|
parallel([
|
|
(cb) => nodeA.dial(nodeC.peerInfo.id, cb),
|
|
(cb) => nodeB.dial(nodeD.peerInfo.id, cb),
|
|
(cb) => nodeC.dial(nodeE.peerInfo.id, cb)
|
|
], (err) => {
|
|
if (err) throw err
|
|
expect(err).to.not.exist()
|
|
nodeB.peerRouting.findPeer(nodeE.peerInfo.id, (err, peerInfo) => {
|
|
expect(err).to.not.exist()
|
|
expect(nodeE.peerInfo.id.toB58String()).to.equal(peerInfo.id.toB58String())
|
|
done()
|
|
})
|
|
})
|
|
})
|
|
})
|
|
})
|
|
|
|
describe('via a delegate', () => {
|
|
let nodeA
|
|
let delegate
|
|
|
|
before((done) => {
|
|
parallel([
|
|
// Create the node using the delegate
|
|
(cb) => {
|
|
delegate = new DelegatedPeerRouter({
|
|
host: 'ipfs.io',
|
|
protocol: 'https',
|
|
port: '443'
|
|
})
|
|
createNode('/ip4/0.0.0.0/tcp/0', {
|
|
modules: {
|
|
peerRouting: [ delegate ]
|
|
},
|
|
config: {
|
|
dht: {
|
|
enabled: false
|
|
}
|
|
}
|
|
}, (err, node) => {
|
|
expect(err).to.not.exist()
|
|
nodeA = node
|
|
nodeA.start(cb)
|
|
})
|
|
}
|
|
], done)
|
|
})
|
|
|
|
after((done) => nodeA.stop(done))
|
|
afterEach(() => nock.cleanAll())
|
|
|
|
it('should use the delegate router to find peers', (done) => {
|
|
const stub = sinon.stub(delegate, 'findPeer').callsFake(() => {
|
|
stub.restore()
|
|
done()
|
|
})
|
|
nodeA.peerRouting.findPeer()
|
|
})
|
|
|
|
it('should be able to find a peer', (done) => {
|
|
const peerKey = 'QmTp9VkYvnHyrqKQuFPiuZkiX9gPcqj6x5LJ1rmWuSySnL'
|
|
const mockApi = nock('https://ipfs.io')
|
|
.post('/api/v0/dht/findpeer')
|
|
.query({
|
|
arg: peerKey,
|
|
timeout: '30000ms',
|
|
'stream-channels': true
|
|
})
|
|
.reply(200, `{"Extra":"","ID":"some other id","Responses":null,"Type":0}\n{"Extra":"","ID":"","Responses":[{"Addrs":["/ip4/127.0.0.1/tcp/4001"],"ID":"${peerKey}"}],"Type":2}\n`, [
|
|
'Content-Type', 'application/json',
|
|
'X-Chunked-Output', '1'
|
|
])
|
|
|
|
nodeA.peerRouting.findPeer(peerKey, (err, peerInfo) => {
|
|
expect(err).to.not.exist()
|
|
expect(peerInfo.id.toB58String()).to.equal(peerKey)
|
|
expect(mockApi.isDone()).to.equal(true)
|
|
done()
|
|
})
|
|
})
|
|
|
|
it('should error when a peer cannot be found', (done) => {
|
|
const peerKey = 'key of a peer not on the network'
|
|
const mockApi = nock('https://ipfs.io')
|
|
.post('/api/v0/dht/findpeer')
|
|
.query({
|
|
arg: peerKey,
|
|
timeout: '30000ms',
|
|
'stream-channels': true
|
|
})
|
|
.reply(200, `{"Extra":"","ID":"some other id","Responses":null,"Type":6}\n{"Extra":"","ID":"yet another id","Responses":null,"Type":0}\n{"Extra":"routing:not found","ID":"","Responses":null,"Type":3}\n`, [
|
|
'Content-Type', 'application/json',
|
|
'X-Chunked-Output', '1'
|
|
])
|
|
|
|
nodeA.peerRouting.findPeer(peerKey, (err, peerInfo) => {
|
|
expect(err).to.exist()
|
|
expect(peerInfo).to.not.exist()
|
|
expect(mockApi.isDone()).to.equal(true)
|
|
done()
|
|
})
|
|
})
|
|
|
|
it('should handle errors from the api', (done) => {
|
|
const peerKey = 'key of a peer not on the network'
|
|
const mockApi = nock('https://ipfs.io')
|
|
.post('/api/v0/dht/findpeer')
|
|
.query({
|
|
arg: peerKey,
|
|
timeout: '30000ms',
|
|
'stream-channels': true
|
|
})
|
|
.reply(502)
|
|
|
|
nodeA.peerRouting.findPeer(peerKey, (err, peerInfo) => {
|
|
expect(err).to.exist()
|
|
expect(peerInfo).to.not.exist()
|
|
expect(mockApi.isDone()).to.equal(true)
|
|
done()
|
|
})
|
|
})
|
|
})
|
|
|
|
describe('via the dht and a delegate', () => {
|
|
let nodeA
|
|
let delegate
|
|
|
|
before((done) => {
|
|
parallel([
|
|
// Create the node using the delegate
|
|
(cb) => {
|
|
delegate = new DelegatedPeerRouter({
|
|
host: 'ipfs.io',
|
|
protocol: 'https',
|
|
port: '443'
|
|
})
|
|
createNode('/ip4/0.0.0.0/tcp/0', {
|
|
modules: {
|
|
peerRouting: [ delegate ]
|
|
}
|
|
}, (err, node) => {
|
|
expect(err).to.not.exist()
|
|
nodeA = node
|
|
nodeA.start(cb)
|
|
})
|
|
}
|
|
], done)
|
|
})
|
|
|
|
after((done) => nodeA.stop(done))
|
|
|
|
describe('findPeer', () => {
|
|
it('should only use the dht if it finds the peer', (done) => {
|
|
const results = [true]
|
|
const dhtStub = sinon.stub(nodeA._dht, 'findPeer').callsArgWith(2, null, results)
|
|
const delegateStub = sinon.stub(delegate, 'findPeer').throws(() => {
|
|
return new Error('the delegate should not have been called')
|
|
})
|
|
|
|
nodeA.peerRouting.findPeer('a peer id', (err, results) => {
|
|
expect(err).to.not.exist()
|
|
expect(results).to.equal(results)
|
|
expect(dhtStub.calledOnce).to.equal(true)
|
|
expect(delegateStub.notCalled).to.equal(true)
|
|
delegateStub.restore()
|
|
dhtStub.restore()
|
|
done()
|
|
})
|
|
})
|
|
|
|
it('should use the delegate if the dht fails to find the peer', (done) => {
|
|
const results = [true]
|
|
const dhtStub = sinon.stub(nodeA._dht, 'findPeer').callsArgWith(2, null, undefined)
|
|
const delegateStub = sinon.stub(delegate, 'findPeer').callsArgWith(2, null, results)
|
|
|
|
nodeA.peerRouting.findPeer('a peer id', (err, results) => {
|
|
expect(err).to.not.exist()
|
|
expect(results).to.deep.equal(results)
|
|
expect(dhtStub.calledOnce).to.equal(true)
|
|
expect(delegateStub.calledOnce).to.equal(true)
|
|
delegateStub.restore()
|
|
dhtStub.restore()
|
|
done()
|
|
})
|
|
})
|
|
})
|
|
})
|
|
|
|
describe('no routers', () => {
|
|
let nodeA
|
|
before((done) => {
|
|
createNode('/ip4/0.0.0.0/tcp/0', {
|
|
config: {
|
|
dht: {
|
|
enabled: false
|
|
}
|
|
}
|
|
}, (err, node) => {
|
|
expect(err).to.not.exist()
|
|
nodeA = node
|
|
done()
|
|
})
|
|
})
|
|
|
|
it('.findPeer should return an error with no options', (done) => {
|
|
nodeA.peerRouting.findPeer('a cid', (err) => {
|
|
expect(err).to.exist()
|
|
done()
|
|
})
|
|
})
|
|
|
|
it('.findPeer should return an error with options', (done) => {
|
|
nodeA.peerRouting.findPeer('a cid', { maxTimeout: 5000 }, (err) => {
|
|
expect(err).to.exist()
|
|
done()
|
|
})
|
|
})
|
|
})
|
|
})
|