diff --git a/package.json b/package.json index 0ebefa4c..26e105bc 100644 --- a/package.json +++ b/package.json @@ -53,7 +53,7 @@ "go-ipfs-dep": "~0.4.18", "hat": "0.0.3", "ipfs": "ipfs/js-ipfs#feat/dht-part-ii", - "ipfs-api": "ipfs/js-ipfs-api#fix/dht-validate-if-receiving-stream", + "ipfs-http-client": "^28.1.0", "ipfs-unixfs": "~0.1.16", "ipfsd-ctl": "~0.40.0", "left-pad": "^1.3.0", diff --git a/test/kad-dht.js b/test/kad-dht.js index 1470b949..345229d9 100644 --- a/test/kad-dht.js +++ b/test/kad-dht.js @@ -10,13 +10,16 @@ chai.use(dirtyChai) chai.use(chaiBytes) const crypto = require('crypto') +const each = require('async/each') const parallel = require('async/parallel') +const times = require('async/times') + +const waitFor = require('./utils/wait-for') const DaemonFactory = require('ipfsd-ctl') const IPFS = require('ipfs') const goDf = DaemonFactory.create() const jsDf = DaemonFactory.create({ type: 'proc', exec: IPFS }) -// const jsDf = DaemonFactory.create({ type: 'js' }) CHANGE TO HERE once UPDATED const getConfig = (bootstrap) => ({ Bootstrap: bootstrap, @@ -40,8 +43,7 @@ const spawnGoDaemon = (bootstrap = [], callback) => { const spawnJsDaemon = (bootstrap = [], callback) => { jsDf.spawn({ initOptions: { bits: 512 }, - config: getConfig(bootstrap), - args: ['--enable-dht-experiment'] + config: getConfig(bootstrap) }, callback) } @@ -55,16 +57,16 @@ const getBootstrapAddr = (node, callback) => { } const addFileAndCat = (addDaemon, catDaemons, data, callback) => { - addDaemon.api.files.add(data, (err, res) => { + addDaemon.api.add(data, (err, res) => { expect(err).to.not.exist() - parallel(catDaemons.map((daemon) => (cb) => daemon.api.files.cat(res[0].hash, cb)), (err, res) => { - expect(err).to.not.exist() - res.forEach((resData) => { - expect(resData).to.equalBytes(data) + each(catDaemons, (daemon, cb) => { + daemon.api.cat(res[0].hash, (err, res) => { + expect(err).to.not.exist() + expect(res).to.equalBytes(data) + cb() }) - callback() - }) + }, callback) }) } @@ -94,22 +96,41 @@ describe('kad-dht', () => { // spawn daemons before(function (done) { this.timeout(70 * 1000) - const bootstrap = [bootstrapAddr] + const peersToSpawn = 3 + const bootstrap = bootstrapAddr ? [bootstrapAddr] : [] - parallel([ - (cb) => spawnJsDaemon(bootstrap, cb), - (cb) => spawnJsDaemon(bootstrap, cb), - (cb) => spawnJsDaemon(bootstrap, cb) - ], (err, spawnedNodes) => { + times(peersToSpawn, (_, next) => { + spawnJsDaemon(bootstrap, (err, node) => { + expect(err).to.not.exist() + nodes.push(node) + next(err, node) + }) + }, (err) => { expect(err).to.not.exist() - spawnedNodes.forEach((node) => nodes.push(node)) - setTimeout(done, 25000) // Wait for peers to have each other on the DHT + + const testDhtReady = (cb) => { + nodes[0].api.swarm.peers((err, peers) => { + if (err) { + return cb(err) + } + cb(null, peers.length === peersToSpawn) + }) + } + + // Wait for peers to have each other on the DHT + waitFor(testDhtReady, { + name: 'dht peers ready', + timeout: 30 * 1000, + interval: 5 * 1000 + }, done) }) }) after(function (done) { this.timeout(80 * 1000) - parallel(nodes.map((node) => (cb) => node.stop(cb)), done) + each(nodes, (node, cb) => { + node.stop(cb) + }, done) }) it('should get from the network after being added', function (done) { @@ -119,6 +140,7 @@ describe('kad-dht', () => { }) describe('a GO network', () => { + const peersToSpawn = 3 let bootstrapAddr let nodes = [] let data = crypto.randomBytes(9001) @@ -142,22 +164,40 @@ describe('kad-dht', () => { // spawn daemons before(function (done) { this.timeout(70 * 1000) - const bootstrap = [bootstrapAddr] + const bootstrap = bootstrapAddr ? [bootstrapAddr] : [] - parallel([ - (cb) => spawnGoDaemon(bootstrap, cb), - (cb) => spawnGoDaemon(bootstrap, cb), - (cb) => spawnGoDaemon(bootstrap, cb) - ], (err, spawnedNodes) => { + times(peersToSpawn, (_, next) => { + spawnGoDaemon(bootstrap, (err, node) => { + expect(err).to.not.exist() + nodes.push(node) + next(err, node) + }) + }, (err) => { expect(err).to.not.exist() - spawnedNodes.forEach((node) => nodes.push(node)) - setTimeout(done, 25000) // Wait for peers to have each other on the DHT + + const testDhtReady = (cb) => { + nodes[0].api.swarm.peers((err, peers) => { + if (err) { + return cb(err) + } + cb(null, peers.length === peersToSpawn) + }) + } + + // Wait for peers to have each other on the DHT + waitFor(testDhtReady, { + name: 'dht peers ready', + timeout: 30 * 1000, + interval: 5 * 1000 + }, done) }) }) after(function (done) { this.timeout(80 * 1000) - parallel(nodes.map((node) => (cb) => node.stop(cb)), done) + each(nodes, (node, cb) => { + node.stop(cb) + }, done) }) it('should get from the network after being added', function (done) { @@ -167,6 +207,7 @@ describe('kad-dht', () => { }) describe('a JS bootstrap node in the land of Go', () => { + const peersToSpawn = 3 let bootstrapAddr let nodes = [] let data = crypto.randomBytes(9001) @@ -190,22 +231,40 @@ describe('kad-dht', () => { // spawn daemons before(function (done) { this.timeout(70 * 1000) - const bootstrap = [bootstrapAddr] + const bootstrap = bootstrapAddr ? [bootstrapAddr] : [] - parallel([ - (cb) => spawnGoDaemon(bootstrap, cb), - (cb) => spawnGoDaemon(bootstrap, cb), - (cb) => spawnGoDaemon(bootstrap, cb) - ], (err, spawnedNodes) => { + times(peersToSpawn, (_, next) => { + spawnGoDaemon(bootstrap, (err, node) => { + expect(err).to.not.exist() + nodes.push(node) + next(err, node) + }) + }, (err) => { expect(err).to.not.exist() - spawnedNodes.forEach((node) => nodes.push(node)) - setTimeout(done, 25000) // Wait for peers to have each other on the DHT + + const testDhtReady = (cb) => { + nodes[0].api.swarm.peers((err, peers) => { + if (err) { + return cb(err) + } + cb(null, peers.length === peersToSpawn) + }) + } + + // Wait for peers to have each other on the DHT + waitFor(testDhtReady, { + name: 'dht peers ready', + timeout: 30 * 1000, + interval: 5 * 1000 + }, done) }) }) after(function (done) { this.timeout(80 * 1000) - parallel(nodes.map((node) => (cb) => node.stop(cb)), done) + each(nodes, (node, cb) => { + node.stop(cb) + }, done) }) it('should get from the network after being added', function (done) { @@ -215,6 +274,7 @@ describe('kad-dht', () => { }) describe('a Go bootstrap node in the land of JS', () => { + const peersToSpawn = 3 let bootstrapAddr let nodes = [] let data = crypto.randomBytes(9001) @@ -238,22 +298,40 @@ describe('kad-dht', () => { // spawn daemons before(function (done) { this.timeout(70 * 1000) - const bootstrap = [bootstrapAddr] + const bootstrap = bootstrapAddr ? [bootstrapAddr] : [] - parallel([ - (cb) => spawnJsDaemon(bootstrap, cb), - (cb) => spawnJsDaemon(bootstrap, cb), - (cb) => spawnJsDaemon(bootstrap, cb) - ], (err, spawnedNodes) => { + times(peersToSpawn, (_, next) => { + spawnJsDaemon(bootstrap, (err, node) => { + expect(err).to.not.exist() + nodes.push(node) + next(err, node) + }) + }, (err) => { expect(err).to.not.exist() - spawnedNodes.forEach((node) => nodes.push(node)) - setTimeout(done, 25000) // Wait for peers to have each other on the DHT + + const testDhtReady = (cb) => { + nodes[0].api.swarm.peers((err, peers) => { + if (err) { + return cb(err) + } + cb(null, peers.length === peersToSpawn) + }) + } + + // Wait for peers to have each other on the DHT + waitFor(testDhtReady, { + name: 'dht peers ready', + timeout: 30 * 1000, + interval: 5 * 1000 + }, done) }) }) after(function (done) { this.timeout(80 * 1000) - parallel(nodes.map((node) => (cb) => node.stop(cb)), done) + each(nodes, (node, cb) => { + node.stop(cb) + }, done) }) it('should get from the network after being added', function (done) { @@ -286,7 +364,7 @@ describe('kad-dht', () => { // spawn daemons before(function (done) { this.timeout(70 * 1000) - const bootstrap = [bootstrapAddr] + const bootstrap = bootstrapAddr ? [bootstrapAddr] : [] parallel([ (cb) => spawnGoDaemon(bootstrap, cb), @@ -295,13 +373,30 @@ describe('kad-dht', () => { ], (err, spawnedNodes) => { expect(err).to.not.exist() spawnedNodes.forEach((node) => nodes.push(node)) - setTimeout(done, 25000) // Wait for peers to have each other on the DHT + + const testDhtReady = (cb) => { + nodes[0].api.swarm.peers((err, peers) => { + if (err) { + return cb(err) + } + cb(null, peers.length === 3) + }) + } + + // Wait for peers to have each other on the DHT + waitFor(testDhtReady, { + name: 'dht peers ready', + timeout: 30 * 1000, + interval: 5 * 1000 + }, done) }) }) after(function (done) { this.timeout(80 * 1000) - parallel(nodes.map((node) => (cb) => node.stop(cb)), done) + each(nodes, (node, cb) => { + node.stop(cb) + }, done) }) it('should get from the network after being added', function (done) { @@ -335,7 +430,7 @@ describe('kad-dht', () => { // spawn daemons before(function (done) { this.timeout(70 * 1000) - const bootstrap = [bootstrapAddr] + const bootstrap = bootstrapAddr ? [bootstrapAddr] : [] parallel([ (cb) => spawnJsDaemon(bootstrap, cb), @@ -344,13 +439,30 @@ describe('kad-dht', () => { ], (err, spawnedNodes) => { expect(err).to.not.exist() spawnedNodes.forEach((node) => nodes.push(node)) - setTimeout(done, 25000) // Wait for peers to have each other on the DHT + + const testDhtReady = (cb) => { + nodes[0].api.swarm.peers((err, peers) => { + if (err) { + return cb(err) + } + cb(null, peers.length === 3) + }) + } + + // Wait for peers to have each other on the DHT + waitFor(testDhtReady, { + name: 'dht peers ready', + timeout: 30 * 1000, + interval: 5 * 1000 + }, done) }) }) after(function (done) { this.timeout(80 * 1000) - parallel(nodes.map((node) => (cb) => node.stop(cb)), done) + each(nodes, (node, cb) => { + node.stop(cb) + }, done) }) it('should get from the network after being added', function (done) { @@ -381,7 +493,18 @@ describe('kad-dht', () => { }) }) - // make connections + /** + * Make connections between nodes + * +-+ +-+ + * |0+-----> |1| + * +++ +++ + * ^ | + * | | + * | v + * +++ +++ + * |3| |2| + * +-+ +-+ + */ before(function (done) { this.timeout(60 * 1000) @@ -397,7 +520,9 @@ describe('kad-dht', () => { after(function (done) { this.timeout(80 * 1000) - parallel(nodes.map((node) => (cb) => node.stop(cb)), done) + each(nodes, (node, cb) => { + node.stop(cb) + }, done) }) it('one hop', function (done) { @@ -441,7 +566,18 @@ describe('kad-dht', () => { }) }) - // make connections + /** + * Make connections between nodes + * +-+ +-+ + * |0+-----> |1| + * +++ +++ + * ^ | + * | | + * | v + * +++ +++ + * |3| |2| + * +-+ +-+ + */ before(function (done) { this.timeout(60 * 1000) @@ -457,7 +593,9 @@ describe('kad-dht', () => { after(function (done) { this.timeout(80 * 1000) - parallel(nodes.map((node) => (cb) => node.stop(cb)), done) + each(nodes, (node, cb) => { + node.stop(cb) + }, done) }) it('one hop', function (done) { @@ -517,7 +655,9 @@ describe('kad-dht', () => { after(function (done) { this.timeout(80 * 1000) - parallel(nodes.map((node) => (cb) => node.stop(cb)), done) + each(nodes, (node, cb) => { + node.stop(cb) + }, done) }) it('one hop', function (done) { diff --git a/test/utils/wait-for.js b/test/utils/wait-for.js new file mode 100644 index 00000000..9e0ee39e --- /dev/null +++ b/test/utils/wait-for.js @@ -0,0 +1,37 @@ +'use strict' + +// Wait for async function `test` to callback(null, true) or timeout after +// options.timeout milliseconds. +module.exports = function waitFor (test, options, callback) { + if (typeof options === 'function') { + callback = options + options = {} + } + + options = options || {} + options.timeout = options.timeout || 5000 + options.interval = options.interval || 0 + options.name = options.name || 'event' + + const start = Date.now() + + const check = () => { + test((err, arrived) => { + if (err) { + return callback(err) + } + + if (arrived) { + return callback() + } + + if (Date.now() > start + options.timeout) { + return callback(new Error(`Timed out waiting for ${options.name}`)) + } + + setTimeout(check, options.interval) + }) + } + + check() +}