diff --git a/.aegir.js b/.aegir.js index f11e51ff..c40c7a1f 100644 --- a/.aegir.js +++ b/.aegir.js @@ -1,6 +1,10 @@ 'use strict' const createServer = require('ipfsd-ctl').createServer +const parallel = require('async/parallel') +const rendezvous = require('libp2p-websocket-star-rendezvous') + +let rzserver const server = createServer() module.exports = { @@ -16,8 +20,28 @@ module.exports = { }, hooks: { browser: { - pre: server.start.bind(server), - post: server.stop.bind(server) + pre: (done) => { + parallel([ + (cb) => server.start(cb), + (cb) => { + rendezvous.start({ + port: 24642 + }, (err, _rzserver) => { + if (err) { + return done(err) + } + rzserver = _rzserver + cb() + }) + } + ], done) + }, + post: (done) => { + parallel([ + (cb) => server.stop(cb), + (cb) => rzserver.stop(cb) + ], done) + } } } } diff --git a/package.json b/package.json index 469cd6de..4d068b0d 100644 --- a/package.json +++ b/package.json @@ -17,9 +17,9 @@ }, "scripts": { "lint": "aegir lint", - "test": "aegir test -t node -t browser --no-cors", - "test:node": "aegir test -t node -f test/node.js", - "test:browser": "aegir test -t browser --no-cors -f test/browser.js" + "test": "cross-env IPFS_REUSEPORT=false aegir test -t node -t browser --no-cors", + "test:node": "cross-env IPFS_REUSEPORT=false aegir test -t node -f test/node.js", + "test:browser": "cross-env IPFS_REUSEPORT=false aegir test -t browser --no-cors -f test/browser.js" }, "pre-push": [ "lint" @@ -44,7 +44,8 @@ "bs58": "^4.0.1", "buffer-loader": "0.0.1", "chai": "^4.1.2", - "cids": "^0.5.3", + "cross-env": "^5.1.3", + "cids": "~0.5.3", "detect-node": "^2.0.3", "dir-compare": "^1.4.0", "dirty-chai": "^2.0.1", @@ -53,12 +54,13 @@ "form-data": "^2.3.2", "go-ipfs-dep": "^0.4.15", "hat": "0.0.3", - "ipfs": "~0.28.2", - "ipfs-api": "^21.0.0", - "ipfsd-ctl": "~0.36.0", - "left-pad": "^1.3.0", - "lodash": "^4.17.10", - "mocha": "^5.1.1", + "ipfs": "~0.28.0", + "ipfs-api": "^18.1.1", + "ipfsd-ctl": "~0.30.3", + "left-pad": "^1.2.0", + "libp2p-websocket-star-rendezvous": "~0.2.2", + "lodash": "^4.17.4", + "mocha": "^4.0.1", "ncp": "^2.0.0", "pretty-bytes": "^5.0.0", "random-fs": "^1.0.3", diff --git a/test/browser.js b/test/browser.js index e1398b8c..beca3f4b 100644 --- a/test/browser.js +++ b/test/browser.js @@ -4,4 +4,5 @@ // require('./exchange-files') // require('./pubsub') require('./kad-dht') -require('./repo') +require('./circuit') +// require('./repo') diff --git a/test/circuit.js b/test/circuit.js new file mode 100644 index 00000000..86ced5df --- /dev/null +++ b/test/circuit.js @@ -0,0 +1,66 @@ +/* eslint max-nested-callbacks: ["error", 8] */ +/* eslint-env mocha */ +'use strict' + +const chai = require('chai') +const dirtyChai = require('dirty-chai') +const expect = chai.expect +chai.use(dirtyChai) + +const parallel = require('async/parallel') + +const all = require('./circuit/all') +const browser = require('./circuit/browser') + +const isNode = require('detect-node') +const send = require('./utils/circuit').send +const connect = require('./utils/circuit').connect + +const timeout = 80 * 1000 +const baseTest = { + connect, + send, + timeout +} + +// TODO: unskip once go-ipfs 0.4.15 is out +describe.skip('circuit', () => { + const tests = all + if (!isNode) { + Object.assign(tests, browser) + } + + Object.keys(tests).forEach((test) => { + let nodes + let nodeA + let relay + let nodeB + + tests[test] = Object.assign({}, baseTest, tests[test]) + const dsc = tests[test].skip && tests[test].skip() ? describe.skip : describe + dsc(test, function () { + this.timeout(tests[test].timeout) + + before((done) => { + tests[test].create((err, _nodes) => { + expect(err).to.not.exist() + nodes = _nodes.map((n) => n.ipfsd) + nodeA = _nodes[0] + relay = _nodes[1] + nodeB = _nodes[2] + done() + }) + }) + + after((done) => parallel(nodes.map((ipfsd) => (cb) => ipfsd.stop(cb)), done)) + + it('connect', (done) => { + tests[test].connect(nodeA, nodeB, relay, done) + }) + + it('send', (done) => { + tests[test].send(nodeA.ipfsd.api, nodeB.ipfsd.api, done) + }) + }) + }) +}) diff --git a/test/circuit/all.js b/test/circuit/all.js new file mode 100644 index 00000000..1fcbbe0f --- /dev/null +++ b/test/circuit/all.js @@ -0,0 +1,71 @@ +/* eslint max-nested-callbacks: ["error", 8] */ +/* eslint-env mocha */ +'use strict' + +const series = require('async/series') + +const utils = require('../utils/circuit') + +const createJs = utils.createJsNode +const createGo = utils.createGoNode + +const base = '/ip4/127.0.0.1/tcp/0' + +module.exports = { + 'go-go-go': { + create: (callback) => series([ + (cb) => createGo([`${base}/ws`], cb), + (cb) => createGo([`${base}/ws`], cb), + (cb) => createGo([`${base}/ws`], cb) + ], callback) + }, + 'js-go-go': { + create: (callback) => series([ + (cb) => createJs([`${base}/ws`], cb), + (cb) => createGo([`${base}/ws`], cb), + (cb) => createGo([`${base}/ws`], cb) + ], callback) + }, + 'go-go-js': { + create: (callback) => series([ + (cb) => createGo([`${base}/ws`], cb), + (cb) => createGo([`${base}/ws`], cb), + (cb) => createJs([`${base}/ws`], cb) + ], callback) + }, + 'js-go-js': { + create: (callback) => series([ + (cb) => createJs([`${base}/ws`], cb), + (cb) => createGo([`${base}/ws`], cb), + (cb) => createJs([`${base}/ws`], cb) + ], callback) + }, + 'go-js-go': { + create: (callback) => series([ + (cb) => createGo([`${base}/ws`], cb), + (cb) => createJs([`${base}/ws`], cb), + (cb) => createGo([`${base}/ws`], cb) + ], callback) + }, + 'js-js-go': { + create: (callback) => series([ + (cb) => createJs([`${base}/ws`], cb), + (cb) => createJs([`${base}/ws`], cb), + (cb) => createGo([`${base}/ws`], cb) + ], callback) + }, + 'go-js-js': { + create: (callback) => series([ + (cb) => createGo([`${base}/ws`], cb), + (cb) => createJs([`${base}/ws`], cb), + (cb) => createJs([`${base}/ws`], cb) + ], callback) + }, + 'js-js-js': { + create: (callback) => series([ + (cb) => createJs([`${base}/ws`], cb), + (cb) => createJs([`${base}/ws`], cb), + (cb) => createJs([`${base}/ws`], cb) + ], callback) + } +} diff --git a/test/circuit/browser.js b/test/circuit/browser.js new file mode 100644 index 00000000..4b7afd26 --- /dev/null +++ b/test/circuit/browser.js @@ -0,0 +1,155 @@ +/* eslint max-nested-callbacks: ["error", 8] */ +/* eslint-env mocha */ +'use strict' + +const series = require('async/series') + +const utils = require('../utils/circuit') + +const createJs = utils.createJsNode +const createProc = utils.createProcNode +const createGo = utils.createGoNode +const connWithTimeout = utils.connWithTimeout + +const getWsAddr = utils.getWsAddr +const getWsStarAddr = utils.getWsStarAddr +const getCircuitAddr = utils.getCircuitAddr + +const base = '/ip4/127.0.0.1/tcp/0' + +module.exports = { + 'browser-go-js': { + create: + (callback) => series([ + (cb) => createProc([], cb), + (cb) => createGo([`${base}/ws`], cb), + (cb) => createJs([`${base}/ws`], cb) + ], callback), + connect: connWithTimeout(1500) + }, + 'browser-go-go': { + create: (callback) => series([ + (cb) => createProc([], cb), + (cb) => createGo([`${base}/ws`], cb), + (cb) => createGo([`${base}/ws`], cb) + ], callback), + connect: connWithTimeout(1500) + }, + 'browser-js-js': { + create: (callback) => series([ + (cb) => createProc([], cb), + (cb) => createJs([`${base}/ws`], cb), + (cb) => createJs([`${base}/ws`], cb) + ], callback), + connect: connWithTimeout(1500) + }, + 'browser-js-go': { + create: (callback) => series([ + (cb) => createProc([], cb), + (cb) => createJs([`${base}/ws`], cb), + (cb) => createJs([`${base}/ws`], cb) + ], callback), + connect: connWithTimeout(1500) + }, + 'js-go-browser': { + create: (callback) => series([ + (cb) => createJs([`${base}/ws`], cb), + (cb) => createGo([`${base}/ws`], cb), + (cb) => createProc([], cb) + ], callback), + connect: connWithTimeout(1500) + }, + 'go-go-browser': { + create: (callback) => series([ + (cb) => createGo([`${base}/ws`], cb), + (cb) => createGo([`${base}/ws`], cb), + (cb) => createProc([], cb) + ], callback), + connect: connWithTimeout(1500) + }, + 'js-js-browser': { + create: (callback) => series([ + (cb) => createJs([`${base}/ws`], cb), + (cb) => createJs([`${base}/ws`], cb), + (cb) => createProc([], cb) + ], callback), + connect: connWithTimeout(1500) + }, + 'go-js-browser': { + create: (callback) => series([ + (cb) => createGo([`${base}/ws`], cb), + (cb) => createJs([`${base}/ws`], cb), + (cb) => createProc([], cb) + ], callback), + connect: connWithTimeout(1500) + }, + 'go-browser-browser': { + create: (callback) => series([ + (cb) => createGo([`${base}/ws`], cb), + (cb) => createProc([`/ip4/127.0.0.1/tcp/24642/ws/p2p-websocket-star`], cb), + (cb) => createProc([`/ip4/127.0.0.1/tcp/24642/ws/p2p-websocket-star`], cb) + ], callback), + connect: (nodeA, nodeB, relay, callback) => { + series([ + (cb) => relay.ipfsd.api.swarm.connect(getWsAddr(nodeA.addrs), cb), + (cb) => relay.ipfsd.api.swarm.connect(getWsStarAddr(nodeB.addrs), cb), + // TODO: needed until https://github.com/ipfs/interop/issues/17 is resolved + (cb) => setTimeout(cb, 5000), + (cb) => nodeA.ipfsd.api.swarm.connect(getCircuitAddr(nodeB.addrs), cb) + ], callback) + }, + timeout: 100 * 1000, + skip: () => false + }, + 'js-browser-browser': { + create: (callback) => series([ + (cb) => createJs([`${base}/ws`], cb), + (cb) => createProc([`/ip4/127.0.0.1/tcp/24642/ws/p2p-websocket-star`], cb), + (cb) => createProc([`/ip4/127.0.0.1/tcp/24642/ws/p2p-websocket-star`], cb) + ], callback), + connect: (nodeA, nodeB, relay, callback) => { + series([ + (cb) => relay.ipfsd.api.swarm.connect(getWsAddr(nodeA.addrs), cb), + (cb) => relay.ipfsd.api.swarm.connect(getWsStarAddr(nodeB.addrs), cb), + // TODO: needed until https://github.com/ipfs/interop/issues/17 is resolved + (cb) => setTimeout(cb, 3000), + (cb) => nodeA.ipfsd.api.swarm.connect(getCircuitAddr(nodeB.addrs), cb) + ], callback) + }, + timeout: 100 * 1000 + }, + 'browser-browser-go': { + create: (callback) => series([ + (cb) => createProc([`/ip4/127.0.0.1/tcp/24642/ws/p2p-websocket-star`], cb), + (cb) => createProc([`/ip4/127.0.0.1/tcp/24642/ws/p2p-websocket-star`], cb), + (cb) => createGo([`${base}/ws`], cb) + ], callback), + connect: (nodeA, nodeB, relay, callback) => { + series([ + (cb) => relay.ipfsd.api.swarm.connect(getWsStarAddr(nodeA.addrs), cb), + (cb) => relay.ipfsd.api.swarm.connect(getWsAddr(nodeB.addrs), cb), + // TODO: needed until https://github.com/ipfs/interop/issues/17 is resolved + (cb) => setTimeout(cb, 5000), + (cb) => nodeA.ipfsd.api.swarm.connect(getCircuitAddr(nodeB.addrs), cb) + ], callback) + }, + timeout: 100 * 1000, + skip: () => true + }, + 'browser-browser-js': { + create: (callback) => series([ + (cb) => createProc([`/ip4/127.0.0.1/tcp/24642/ws/p2p-websocket-star`], cb), + (cb) => createProc([`/ip4/127.0.0.1/tcp/24642/ws/p2p-websocket-star`], cb), + (cb) => createJs([`${base}/ws`], cb) + ], callback), + connect: (nodeA, nodeB, relay, callback) => { + series([ + (cb) => relay.ipfsd.api.swarm.connect(getWsStarAddr(nodeA.addrs), cb), + (cb) => relay.ipfsd.api.swarm.connect(getWsAddr(nodeB.addrs), cb), + // TODO: needed until https://github.com/ipfs/interop/issues/17 is resolved + (cb) => setTimeout(cb, 3000), + (cb) => nodeA.ipfsd.api.swarm.connect(getCircuitAddr(nodeB.addrs), cb) + ], callback) + } + } +} diff --git a/test/exchange-files.js b/test/exchange-files.js index 163e0c58..39a78c2e 100644 --- a/test/exchange-files.js +++ b/test/exchange-files.js @@ -18,6 +18,8 @@ const join = require('path').join const os = require('os') const hat = require('hat') +const isWindows = os.platform() === 'win32' + const rmDir = promisify(rimraf) const DaemonFactory = require('ipfsd-ctl') @@ -60,8 +62,8 @@ describe('exchange files', () => { parallel([ (cb) => goDf.spawn({ initOptions: { bits: 1024 } }, cb), - (cb) => jsDf.spawn({ initOptions: { bits: 512 } }, cb), - (cb) => jsDf.spawn({ initOptions: { bits: 512 } }, cb) + (cb) => jsDf.spawn({ type: 'js', initOptions: { bits: 512 } }, cb), + (cb) => jsDf.spawn({ type: 'js', initOptions: { bits: 512 } }, cb) ], (err, n) => { expect(err).to.not.exist() nodes = n @@ -177,6 +179,11 @@ describe('exchange files', () => { // TODO these tests are not fetching the full dir?? describe('get directory', () => dirs.forEach((num) => { + // skipping until https://github.com/ipfs/interop/issues/9 is addressed + if (isWindows) { + return + } + it(`go -> js: depth: 5, num: ${num}`, function () { this.timeout(50 * 1000) const dir = tmpDir() diff --git a/test/node.js b/test/node.js index f42c5a11..ac49e854 100644 --- a/test/node.js +++ b/test/node.js @@ -1,6 +1,7 @@ /* eslint-env mocha */ 'use strict' +require('./circuit') require('./repo') require('./exchange-files') require('./kad-dht') diff --git a/test/repo.js b/test/repo.js index 9e3cd075..a318bdc7 100644 --- a/test/repo.js +++ b/test/repo.js @@ -12,6 +12,8 @@ const os = require('os') const path = require('path') const hat = require('hat') +const isWindows = os.platform() === 'win32' + const DaemonFactory = require('ipfsd-ctl') const goDf = DaemonFactory.create() const jsDf = DaemonFactory.create({ type: 'js' }) @@ -25,6 +27,11 @@ function catAndCheck (api, hash, data, callback) { } describe('repo', () => { + // skipping until https://github.com/ipfs/interop/issues/8 is addressed + if (isWindows) { + return + } + it('read repo: go -> js', function (done) { this.timeout(50 * 1000) diff --git a/test/utils/circuit.js b/test/utils/circuit.js new file mode 100644 index 00000000..2ae7d5d0 --- /dev/null +++ b/test/utils/circuit.js @@ -0,0 +1,168 @@ +'use strict' + +const chai = require('chai') +const dirtyChai = require('dirty-chai') +const expect = chai.expect +chai.use(dirtyChai) + +const waterfall = require('async/waterfall') +const series = require('async/series') + +const crypto = require('crypto') + +const IPFS = require('ipfs') + +const DaemonFactory = require('ipfsd-ctl') +const jsDf = DaemonFactory.create({ type: 'js' }) +const goDf = DaemonFactory.create({ type: 'go' }) +const procDf = DaemonFactory.create({ type: 'proc', exec: IPFS }) + +const baseConf = { + Bootstrap: [], + Addresses: { + API: '/ip4/0.0.0.0/tcp/0', + Gateway: '/ip4/0.0.0.0/tcp/0' + }, + Discovery: { + MDNS: { + Enabled: + false + } + } +} + +exports.createProcNode = (addrs, callback) => { + procDf.spawn({ + initOptions: { bits: 512 }, + config: Object.assign({}, baseConf, { + Addresses: { + Swarm: addrs + }, + EXPERIMENTAL: { + relay: { + enabled: true, + hop: { + enabled: true + } + } + } + }) + }, (err, ipfsd) => { + expect(err).to.not.exist() + ipfsd.api.id((err, id) => { + callback(err, { ipfsd, addrs: id.addresses }) + }) + }) +} + +exports.createJsNode = (addrs, callback) => { + jsDf.spawn({ + initOptions: { bits: 512 }, + config: Object.assign({}, baseConf, { + Addresses: { + Swarm: addrs + }, + EXPERIMENTAL: { + relay: { + enabled: true, + hop: { + enabled: true + } + } + } + }) + }, (err, ipfsd) => { + expect(err).to.not.exist() + ipfsd.api.id((err, id) => { + callback(err, { ipfsd, addrs: id.addresses }) + }) + }) +} + +exports.createGoNode = (addrs, callback) => { + goDf.spawn({ + initOptions: { bits: 1024 }, + config: Object.assign({}, baseConf, { + Addresses: { + Swarm: addrs + }, + Swarm: { + DisableRelay: false, + EnableRelayHop: true + } + }) + }, (err, ipfsd) => { + expect(err).to.not.exist() + ipfsd.api.id((err, id) => { + const addrs = [].concat(id.addresses, [`/p2p-circuit/ipfs/${id.id}`]) + callback(err, { ipfsd, addrs: addrs }) + }) + }) +} + +const data = crypto.randomBytes(128) +exports.send = (nodeA, nodeB, callback) => { + waterfall([ + (cb) => nodeA.files.add(data, cb), + (res, cb) => nodeB.files.cat(res[0].hash, cb), + (buffer, cb) => { + expect(buffer).to.deep.equal(data) + cb() + } + ], callback) +} + +const getWsAddr = (addrs) => addrs + .map((a) => a.toString()) + .find((a) => { + return a.includes('/ws') && !a.includes('/p2p-websocket-star') + }) + +exports.getWsAddr = getWsAddr + +const getWsStarAddr = (addrs) => addrs + .map((a) => a.toString()) + .find((a) => a.includes('/p2p-websocket-star')) + +exports.getWsStarAddr = getWsStarAddr + +const getWrtcStarAddr = (addrs) => addrs + .map((a) => a.toString()) + .find((a) => a.includes('/p2p-webrtc-star')) + +exports.getWrtcStarAddr = getWrtcStarAddr + +const getTcpAddr = (addrs) => addrs + .map((a) => a.toString()) + .find((a) => !a.includes('/ws') && !a.includes('/p2p-websocket-star')) + +exports.getTcpAddr = getTcpAddr + +const getCircuitAddr = (addrs) => addrs + .map((a) => a.toString()) + .find((a) => a.includes('/p2p-circuit/ipfs')) + +exports.getCircuitAddr = getCircuitAddr + +const connect = (nodeA, nodeB, relay, timeout, callback) => { + if (typeof timeout === 'function') { + callback = timeout + timeout = 1000 + } + + series([ + (cb) => nodeA.ipfsd.api.swarm.connect(getWsAddr(relay.addrs), cb), + (cb) => nodeB.ipfsd.api.swarm.connect(getWsAddr(relay.addrs), cb), + // TODO: needed until https://github.com/ipfs/interop/issues/17 is resolved + (cb) => setTimeout(cb, timeout), + (cb) => nodeA.ipfsd.api.swarm.connect(getCircuitAddr(nodeB.addrs), cb) + ], callback) +} + +exports.connect = connect + +exports.connWithTimeout = (timeout) => { + return (nodeA, nodeB, relay, callback) => { + connect(nodeA, nodeB, relay, timeout, callback) + } +}