diff --git a/.aegir.js b/.aegir.js index 6f258da0..111a903b 100644 --- a/.aegir.js +++ b/.aegir.js @@ -15,18 +15,20 @@ module.exports = { served: true, included: false }], - singleRun: true, + singleRun: false, captureTimeout: 5000 * 1000, // browserDisconnectTolerance: 3, //this one helps browserDisconnectTimeout: 5000 * 1000, - browserNoActivityTimeout: 5000 * 1000, + browserNoActivityTimeout: 5000 * 1000 }, hooks: { pre: (done) => { parallel([ (cb) => server.start(cb), (cb) => { - rendezvous.start({port: 24642}, (err, _rzserver) => { + rendezvous.start({ + port: 24642 + }, (err, _rzserver) => { if (err) { return done(err) } diff --git a/package.json b/package.json index 0062337b..5f0ac4be 100644 --- a/package.json +++ b/package.json @@ -17,7 +17,7 @@ }, "scripts": { "lint": "aegir lint", - "test": "cross-env IPFS_REUSEPORT=false aegir test -t node -t browser --no-cors --timeout 1000000", + "test": "cross-env IPFS_REUSEPORT=false aegir test -t node -t browser --no-cors --timeout 50000", "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" }, diff --git a/test/browser.js b/test/browser.js index 21715fe8..beca3f4b 100644 --- a/test/browser.js +++ b/test/browser.js @@ -4,5 +4,5 @@ // require('./exchange-files') // require('./pubsub') require('./kad-dht') -require('./circuit-relay') +require('./circuit') // require('./repo') diff --git a/test/circuit-relay.js b/test/circuit-relay.js deleted file mode 100644 index af149988..00000000 --- a/test/circuit-relay.js +++ /dev/null @@ -1,859 +0,0 @@ -/* 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 series = require('async/series') -const waterfall = require('async/waterfall') -const multiaddr = require('multiaddr') -const crypto = require('crypto') -const IPFS = require('ipfs') - -const isNode = require('detect-node') - -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 - } - } -} - -const base = '/ip4/127.0.0.1/tcp' - -const setUpInProcNode = (addrs, hop, callback) => { - if (typeof hop === 'function') { - callback = hop - hop = false - } - - procDf.spawn({ - initOptions: { bits: 512 }, - config: Object.assign({}, baseConf, { - Addresses: { - Swarm: addrs - }, - EXPERIMENTAL: { - relay: { - enabled: true, - hop: { - enabled: hop - } - } - } - }) - }, (err, ipfsd) => { - expect(err).to.not.exist() - ipfsd.api.id((err, id) => { - callback(err, { ipfsd, addrs: id.addresses }) - }) - }) -} - -const setUpJsNode = (addrs, hop, callback) => { - if (typeof hop === 'function') { - callback = hop - hop = false - } - - jsDf.spawn({ - initOptions: { bits: 512 }, - config: Object.assign({}, baseConf, { - Addresses: { - Swarm: addrs - }, - EXPERIMENTAL: { - relay: { - enabled: true, - hop: { - enabled: hop - } - } - } - }) - }, (err, ipfsd) => { - expect(err).to.not.exist() - ipfsd.api.id((err, id) => { - callback(err, { ipfsd, addrs: id.addresses }) - }) - }) -} - -const setUpGoNode = (addrs, hop, callback) => { - if (typeof hop === 'function') { - callback = hop - hop = false - } - - goDf.spawn({ - initOptions: { bits: 1024 }, - config: Object.assign({}, baseConf, { - Addresses: { - Swarm: addrs - }, - Swarm: { - DisableRelay: false, - EnableRelayHop: hop - } - }) - }, (err, ipfsd) => { - expect(err).to.not.exist() - ipfsd.api.id((err, id) => { - callback(err, { ipfsd, addrs: id.addresses }) - }) - }) -} - -const wsAddr = (addrs) => addrs.map((a) => a.toString()) - .find((a) => a.includes('/ws') && !a.includes('/p2p-websocket-star')) -const wsStarAddr = (addrs) => addrs.map((a) => a.toString()).find((a) => a.includes('/p2p-websocket-star')) -const tcpAddr = (addrs) => addrs.map((a) => a.toString()).find((a) => !a.includes('/ws')) - -function reusableTests (relay, parseAddrA, parseAddrB) { - describe(`js <-> ${relay} relay <-> go`, function () { - // this.timeout(80 * 1000) - - let nodeA - let nodeAAddr - let nodeB - let nodeBAddr - let nodeBCircuitAddr - - let nodes - before(function (done) { - parallel([ - (cb) => setUpGoNode([this.addrA], cb), - (cb) => setUpJsNode([this.addrB], cb) - ], function (err, res) { - expect(err).to.not.exist() - nodes = res.map((node) => node.ipfsd) - - nodeAAddr = parseAddrA(res[0].addrs) - nodeA = res[0].ipfsd.api - - nodeBAddr = parseAddrB(res[1].addrs) - - nodeB = res[1].ipfsd.api - nodeBCircuitAddr = `/p2p-circuit/ipfs/${multiaddr(nodeBAddr).getPeerId()}` - - done() - }) - }) - - after((done) => parallel(nodes.map((node) => (cb) => node.stop(cb)), done)) - - it('should connect', function (done) { - series([ - (cb) => this.relay.api.swarm.connect(nodeAAddr, cb), - (cb) => setTimeout(cb, 1000), - (cb) => this.relay.api.swarm.connect(nodeBAddr, cb), - (cb) => setTimeout(cb, 1000), - (cb) => nodeA.swarm.connect(nodeBCircuitAddr, cb) - ], done) - }) - - it('should transfer', function (done) { - const data = crypto.randomBytes(128) - 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() - } - ], done) - }) - }) - - describe(`js <-> ${relay} relay <-> js`, function () { - // this.timeout(80 * 1000) - let nodeA - let nodeAAddr - - let nodeB - let nodeBAddr - let nodeBCircuitAddr - - let nodes - before(function (done) { - parallel([ - (cb) => setUpJsNode([this.addrA], cb), - (cb) => setUpJsNode([this.addrB], cb) - ], (err, res) => { - expect(err).to.not.exist() - nodes = res.map((node) => node.ipfsd) - - nodeA = res[0].ipfsd.api - nodeAAddr = parseAddrA(res[0].addrs) - nodeBCircuitAddr = `/p2p-circuit/ipfs/${multiaddr(nodeAAddr).getPeerId()}` - - nodeB = res[1].ipfsd.api - nodeBAddr = parseAddrB(res[1].addrs) - - done() - }) - }) - - after((done) => parallel(nodes.map((node) => (cb) => node.stop(cb)), done)) - - it('should connect', function (done) { - series([ - (cb) => this.relay.api.swarm.connect(nodeAAddr, cb), - (cb) => setTimeout(cb, 1000), - (cb) => this.relay.api.swarm.connect(nodeBAddr, cb), - (cb) => setTimeout(cb, 1000), - (cb) => nodeB.swarm.connect(nodeBCircuitAddr, cb) - ], done) - }) - - it('should transfer', function (done) { - const data = crypto.randomBytes(128) - 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() - } - ], done) - }) - }) - - describe(`go <-> ${relay} relay <-> go`, function () { - // this.timeout(80 * 1000) - let nodeA - let nodeAAddr - let nodeACircuitAddr - - let nodeB - let nodeBAddr - - let nodes - before(function (done) { - parallel([ - (cb) => setUpGoNode([this.addrA], cb), - (cb) => setUpGoNode([this.addrB], cb) - ], (err, res) => { - expect(err).to.not.exist() - nodes = res.map((node) => node.ipfsd) - - nodeA = res[0].ipfsd.api - nodeAAddr = parseAddrA(res[0].addrs) - nodeACircuitAddr = `/p2p-circuit/ipfs/${multiaddr(nodeAAddr).getPeerId()}` - - nodeB = res[1].ipfsd.api - nodeBAddr = parseAddrB(res[1].addrs) - - done() - }) - }) - - after((done) => parallel(nodes.map((node) => (cb) => node.stop(cb)), done)) - - it('should connect', function (done) { - series([ - (cb) => this.relay.api.swarm.connect(nodeAAddr, cb), - (cb) => setTimeout(cb, 1000), - (cb) => this.relay.api.swarm.connect(nodeBAddr, cb), - (cb) => setTimeout(cb, 1000), - (cb) => nodeB.swarm.connect(nodeACircuitAddr, cb) - ], done) - }) - - it('should transfer', function (done) { - const data = crypto.randomBytes(128) - 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() - } - ], done) - }) - }) - - describe(`go <-> ${relay} relay <-> js`, function () { - // this.timeout(80 * 1000) - let nodeA - let nodeAAddr - let nodeACircuitAddr - - let nodeB - let nodeBAddr - - let nodes - before(function (done) { - parallel([ - (cb) => setUpGoNode([this.addrA], cb), - (cb) => setUpJsNode([this.addrB], cb) - ], (err, res) => { - expect(err).to.not.exist() - nodes = res.map((node) => node.ipfsd) - - nodeA = res[0].ipfsd.api - nodeAAddr = parseAddrA(res[0].addrs) - nodeACircuitAddr = `/p2p-circuit/ipfs/${multiaddr(nodeAAddr).getPeerId()}` - - nodeB = res[1].ipfsd.api - nodeBAddr = parseAddrB(res[1].addrs) - - done() - }) - }) - - after((done) => parallel(nodes.map((node) => (cb) => node.stop(cb)), done)) - - it('should connect', function (done) { - series([ - (cb) => this.relay.api.swarm.connect(nodeAAddr, cb), - (cb) => setTimeout(cb, 1000), - (cb) => this.relay.api.swarm.connect(nodeBAddr, cb), - (cb) => setTimeout(cb, 1000), - (cb) => nodeB.swarm.connect(nodeACircuitAddr, cb) - ], done) - }) - - it('should transfer', function (done) { - const data = crypto.randomBytes(128) - 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() - } - ], done) - }) - }) -} - -describe('circuit', () => { - describe('js relay', function () { - this.relay = null - this.relayAddrs = null - - before(function (done) { - // this.timeout(50 * 1000) - - this.addrA = `${base}/0` - this.addrB = `${base}/0/ws` - - setUpJsNode([ - `${base}/0/ws`, - `${base}/0`, - '/ip4/127.0.0.1/tcp/24642/ws/p2p-websocket-star' - ], true, (err, res) => { - expect(err).to.not.exist() - this.relay = res.ipfsd - this.relayAddrs = res.addrs - done() - }) - }) - - after(function (done) { - this.relay.stop(done) - }) - - reusableTests('js', tcpAddr, wsAddr) - - describe(`js browser`, function () { - if (isNode) { - return - } - - describe(`js <-> js relay <-> browser`, function () { - // this.timeout(100 * 1000) - - let nodeA - let nodeAAddr - - let nodeB - let nodeBAddr - let nodeBCircuitAddr - - let nodes - before(function (done) { - parallel([ - (cb) => setUpJsNode([this.addrA], cb), - (cb) => setUpInProcNode(['/ip4/127.0.0.1/tcp/24642/ws/p2p-websocket-star'], cb) - ], (err, res) => { - expect(err).to.not.exist() - nodes = res.map((node) => node.ipfsd) - - nodeA = res[0].ipfsd.api - nodeAAddr = tcpAddr(res[0].addrs) - nodeBCircuitAddr = `/p2p-circuit/ipfs/${multiaddr(nodeAAddr).getPeerId()}` - - nodeB = res[1].ipfsd.api - nodeBAddr = wsStarAddr(res[1].addrs) - - done() - }) - }) - - after((done) => parallel(nodes.map((node) => (cb) => node.stop(cb)), done)) - - it('should connect', function (done) { - series([ - (cb) => this.relay.api.swarm.connect(nodeAAddr, cb), - (cb) => setTimeout(cb, 1000), - (cb) => this.relay.api.swarm.connect(nodeBAddr, cb), - (cb) => setTimeout(cb, 1000), - (cb) => nodeB.swarm.connect(nodeBCircuitAddr, cb) - ], done) - }) - - it('should transfer', function (done) { - const data = crypto.randomBytes(128) - 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() - } - ], done) - }) - }) - - // skipped untill https://github.com/ipfs/js-ipfs/issues/1203 is resolved - describe.skip(`go <-> js relay <-> browser`, function () { - // this.timeout(100 * 1000) - - let nodeA - // let nodeAAddr - - let nodeB - let nodeBAddr - let nodeBCircuitAddr - - let nodes - before(function (done) { - parallel([ - (cb) => setUpGoNode([this.addrA], cb), - (cb) => setUpInProcNode(['/ip4/127.0.0.1/tcp/24642/ws/p2p-websocket-star'], cb) - ], (err, res) => { - expect(err).to.not.exist() - nodes = res.map((node) => node.ipfsd) - - nodeA = res[0].ipfsd.api - // nodeAAddr = tcpAddr(res[0].addrs) - - nodeB = res[1].ipfsd.api - nodeBAddr = wsStarAddr(res[1].addrs) - nodeBCircuitAddr = `/p2p-circuit/ipfs/${multiaddr(nodeBAddr).getPeerId()}` - - done() - }) - }) - - after((done) => parallel(nodes.map((node) => (cb) => node.stop(cb)), done)) - - it('should connect', function (done) { - series([ - (cb) => nodeA.swarm.connect(tcpAddr(this.relayAddrs), cb), - (cb) => setTimeout(cb, 1000), - (cb) => nodeB.swarm.connect(wsAddr(this.relayAddrs), cb), - (cb) => setTimeout(cb, 1000), - (cb) => nodeA.swarm.connect(nodeBCircuitAddr, cb) - ], done) - }) - - it('should transfer', function (done) { - const data = crypto.randomBytes(128) - 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() - } - ], done) - }) - }) - - describe(`browser <-> js relay <-> browser`, function () { - // this.timeout(100 * 1000) - - let nodeA - let nodeB - - // let nodeAId1 - let nodeBAddrs - - before(function (done) { - parallel([ - (cb) => setUpInProcNode([], false, cb), - (cb) => setUpInProcNode([], false, cb) - ], (err, nodes) => { - expect(err).to.not.exist() - nodeA = nodes[0].ipfsd.api - nodeB = nodes[1].ipfsd.api - - nodeBAddrs = nodes[1].addrs - done() - }) - }) - - it('should connect', function (done) { - console.dir(`wsAddr: ${wsAddr(this.relayAddrs)}`) - series([ - (cb) => nodeA.swarm.connect(wsAddr(this.relayAddrs), cb), - (cb) => setTimeout(cb, 1000), - (cb) => nodeB.swarm.connect(wsAddr(this.relayAddrs), cb), - (cb) => setTimeout(cb, 1000), - (cb) => nodeA.swarm.connect(nodeBAddrs[0], cb) - ], done) - }) - - it('should transfer', function (done) { - const data = crypto.randomBytes(128) - 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() - } - ], done) - }) - }) - }) - }) - - describe('go relay', function () { - this.relay = null - this.relayAddrs = null - - before(function (done) { - // this.timeout(50 * 1000) - - this.addrA = `${base}/0` - this.addrB = `${base}/0/ws` - - setUpGoNode([ - `${base}/0/ws`, - `${base}/0` - ], true, (err, res) => { - expect(err).to.not.exist() - this.relay = res.ipfsd - this.relayAddrs = res.addrs - done() - }) - }) - - after(function (done) { - this.relay.stop(done) - }) - - reusableTests('go', tcpAddr, wsAddr) - - describe(`go browser`, function () { - if (isNode) { - return - } - - describe(`js <-> go relay <-> browser`, function () { - // this.timeout(100 * 1000) - - let nodeA - - let nodeB - let nodeBCircuitAddr - - let nodes - before(function (done) { - parallel([ - (cb) => setUpJsNode([this.addrA], cb), - (cb) => setUpInProcNode([], cb) - ], (err, res) => { - expect(err).to.not.exist() - nodes = res.map((node) => node.ipfsd) - - nodeA = res[0].ipfsd.api - nodeB = res[1].ipfsd.api - nodeBCircuitAddr = `/p2p-circuit/ipfs/${multiaddr(res[1].addrs[0]).getPeerId()}` - - done() - }) - }) - - after((done) => parallel(nodes.map((node) => (cb) => node.stop(cb)), done)) - - it('should connect', function (done) { - series([ - (cb) => nodeA.swarm.connect(tcpAddr(this.relayAddrs), cb), - (cb) => setTimeout(cb, 1000), - (cb) => nodeB.swarm.connect(wsAddr(this.relayAddrs), cb), - (cb) => setTimeout(cb, 1000), - (cb) => nodeA.swarm.connect(nodeBCircuitAddr, cb) - ], done) - }) - - it('should transfer', function (done) { - const data = crypto.randomBytes(128) - 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() - } - ], done) - }) - }) - - describe(`go <-> go relay <-> browser`, function () { - // this.timeout(100 * 1000) - - let nodeA - - let nodeB - let nodeBCircuitAddr - - let nodes - before(function (done) { - parallel([ - (cb) => setUpGoNode([this.addrA], cb), - (cb) => setUpInProcNode([], cb) - ], (err, res) => { - expect(err).to.not.exist() - nodes = res.map((node) => node.ipfsd) - - nodeA = res[0].ipfsd.api - nodeB = res[1].ipfsd.api - nodeBCircuitAddr = `/p2p-circuit/ipfs/${multiaddr(res[1].addrs[0]).getPeerId()}` - - done() - }) - }) - - after((done) => parallel(nodes.map((node) => (cb) => node.stop(cb)), done)) - - it('should connect', function (done) { - series([ - (cb) => nodeA.swarm.connect(tcpAddr(this.relayAddrs), cb), - (cb) => setTimeout(cb, 1000), - (cb) => nodeB.swarm.connect(wsAddr(this.relayAddrs), cb), - (cb) => setTimeout(cb, 1000), - (cb) => nodeA.swarm.connect(nodeBCircuitAddr, cb) - ], done) - }) - - it('should transfer', function (done) { - const data = crypto.randomBytes(128) - 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() - } - ], done) - }) - }) - - describe(`browser <-> go relay <-> browser`, function () { - if (isNode) { - return - } - - // this.timeout(100 * 1000) - - let nodeA - let nodeB - - // let nodeAId1 - let nodeBAddrs - - before(function (done) { - parallel([ - (cb) => setUpInProcNode([], false, cb), - (cb) => setUpInProcNode([], false, cb) - ], (err, nodes) => { - expect(err).to.not.exist() - nodeA = nodes[0].ipfsd.api - nodeB = nodes[1].ipfsd.api - - nodeBAddrs = nodes[1].addrs - done() - }) - }) - - it('should connect', function (done) { - series([ - (cb) => nodeA.swarm.connect(wsAddr(this.relayAddrs), cb), - (cb) => setTimeout(cb, 1000), - (cb) => nodeB.swarm.connect(wsAddr(this.relayAddrs), cb), - (cb) => setTimeout(cb, 1000), - (cb) => nodeA.swarm.connect(nodeBAddrs[0], cb) - ], done) - }) - - it('should transfer', function (done) { - const data = crypto.randomBytes(128) - 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() - } - ], done) - }) - }) - }) - }) - - describe('browser relay', function () { - if (isNode) { return } - - this.relay = null - this.relayAddrs = null - - before(function (done) { - // this.timeout(50 * 1000) - - this.addrA = `${base}/0/ws` - this.addrB = `${base}/0/ws` - - setUpInProcNode(['/ip4/127.0.0.1/tcp/24642/ws/p2p-websocket-star'], true, (err, res) => { - expect(err).to.not.exist() - this.relay = res.ipfsd - this.relayAddrs = res.addrs - done() - }) - }) - - after(function (done) { - this.relay.stop(done) - }) - - reusableTests('browser', wsAddr, wsAddr) - - describe(`browser`, function () { - describe(`js <-> browser relay <-> browser`, function () { - // this.timeout(100 * 1000) - - let nodeA - let nodeAAddr - - let nodeB - let nodeBCircuitAddr - - let nodes - before(function (done) { - parallel([ - (cb) => setUpJsNode([this.addrA], cb), - (cb) => setUpInProcNode(['/ip4/127.0.0.1/tcp/24642/ws/p2p-websocket-star'], cb) - ], (err, res) => { - expect(err).to.not.exist() - nodes = res.map((node) => node.ipfsd) - - nodeA = res[0].ipfsd.api - nodeAAddr = wsAddr(res[0].addrs) - - nodeB = res[1].ipfsd.api - nodeBCircuitAddr = `/p2p-circuit/ipfs/${multiaddr(res[1].addrs[0]).getPeerId()}` - - done() - }) - }) - - after((done) => parallel(nodes.map((node) => (cb) => node.stop(cb)), done)) - - it('should connect', function (done) { - series([ - (cb) => this.relay.api.swarm.connect(nodeAAddr, cb), - (cb) => setTimeout(cb, 1000), - (cb) => nodeB.swarm.connect(wsStarAddr(this.relayAddrs), cb), - (cb) => setTimeout(cb, 1000), - (cb) => nodeA.swarm.connect(nodeBCircuitAddr, cb) - ], done) - }) - - it('should transfer', function (done) { - const data = crypto.randomBytes(128) - 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() - } - ], done) - }) - }) - - describe(`go <-> browser relay <-> browser`, function () { - // this.timeout(100 * 1000) - - let nodeA - let nodeAAddr - - let nodeB - let nodeBCircuitAddr - - let nodes - before(function (done) { - parallel([ - (cb) => setUpGoNode([this.addrA], cb), - (cb) => setUpInProcNode(['/ip4/127.0.0.1/tcp/24642/ws/p2p-websocket-star'], cb) - ], (err, res) => { - expect(err).to.not.exist() - nodes = res.map((node) => node.ipfsd) - - nodeA = res[0].ipfsd.api - nodeAAddr = wsAddr(res[0].addrs) - - nodeB = res[1].ipfsd.api - nodeBCircuitAddr = `/p2p-circuit/ipfs/${multiaddr(res[1].addrs[0]).getPeerId()}` - - done() - }) - }) - - after((done) => parallel(nodes.map((node) => (cb) => node.stop(cb)), done)) - - it('should connect', function (done) { - series([ - (cb) => this.relay.api.swarm.connect(nodeAAddr, cb), - (cb) => setTimeout(cb, 1000), - (cb) => nodeB.swarm.connect(wsStarAddr(this.relayAddrs), cb), - (cb) => setTimeout(cb, 1000), - (cb) => nodeA.swarm.connect(nodeBCircuitAddr, cb) - ], done) - }) - - it('should transfer', function (done) { - const data = crypto.randomBytes(128) - 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() - } - ], done) - }) - }) - }) - }) -}) diff --git a/test/circuit.js b/test/circuit.js new file mode 100644 index 00000000..ff4450e6 --- /dev/null +++ b/test/circuit.js @@ -0,0 +1,456 @@ +/* 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 isNode = require('detect-node') + +const utils = require('./utils/circuit') + +const proc = utils.setUpProcNode +const js = utils.setUpJsNode +const go = utils.setUpGoNode + +const ws = utils.wsAddr +const star = utils.wsStarAddr +const tcp = utils.tcpAddr +const circuit = utils.circuitAddr + +const create = utils.create +const connect = utils.connect +const send = utils.send + +const base = '/ip4/127.0.0.1/tcp' + +// TODO: (dryajov) circuit tests ended up being +// more complex than expected, the majority +// of the complexity comes from spawning and +// connecting the nodes. +// +// I've come up with this little DSL to avoid +// duplicating code all over the place. Some +// notable quirks that lead to this: +// +// - ipfs-api connect, doesn't support peer ids, +// only plain addresses, hence we end up filtering +// addresses (clunky) +// - not all connect sequences work in all cases +// - i.e. cant connect to browser relays since +// go doesn't support the star protos, so the +// sequence has to be changed to connect the +// browser relay to the nodes instead +// +// that breaks the flow and also any attempt to +// generalize and abstract things out + +const tests = [ + { + name: 'go-go-go', + nodes: { + node1: { + exec: go, + addrs: [`${base}/0/ws`] + }, + relay: { + exec: go, + addrs: [`${base}/0/ws`] + }, + node2: { + exec: go, + addrs: [`${base}/0/ws`] + } + }, + connect: [ + [{ name: 'node1', parser: ws }, { name: 'relay' }], + [{ name: 'node2', parser: ws }, { name: 'relay' }], + [{ name: 'node1', parser: circuit }, { name: 'node2' }] + ], + send: ['node1', 'node2'] + }, + { + name: 'js-go-go', + nodes: { + node1: { + exec: js, + addrs: [`${base}/0/ws`] + }, + relay: { + exec: go, + addrs: [`${base}/0/ws`] + }, + node2: { + exec: go, + addrs: [`${base}/0/ws`] + } + }, + connect: [ + [{ name: 'node1', parser: ws }, { name: 'relay' }], + [{ name: 'node2', parser: ws }, { name: 'relay' }], + [{ name: 'node1', parser: circuit }, { name: 'node2' }] + ], + send: ['node1', 'node2'], + skip: () => true + }, + { + name: 'js-go-js', + nodes: { + node1: { + exec: js, + addrs: [`${base}/0/ws`] + }, + relay: { + exec: go, + addrs: [`${base}/0/ws`] + }, + node2: { + exec: js, + addrs: [`${base}/0/ws`] + } + }, + connect: [ + [{ name: 'node1', parser: ws }, { name: 'relay' }], + [{ name: 'node2', parser: ws }, { name: 'relay' }], + [{ name: 'node1', parser: circuit }, { name: 'node2' }] + ], + send: ['node1', 'node2'] + }, + { + name: 'js-js-js', + nodes: { + node1: { + exec: js, + addrs: [`${base}/0/ws`] + }, + relay: { + exec: js, + addrs: [`${base}/0/ws`] + }, + node2: { + exec: js, + addrs: [`${base}/0/ws`] + } + }, + connect: [ + [{ name: 'node1', parser: ws }, { name: 'relay' }], + [{ name: 'node2', parser: ws }, { name: 'relay' }], + [{ name: 'node1', parser: circuit }, { name: 'node2' }] + ], + send: ['node1', 'node2'] + }, + { + name: 'js-js-go', + nodes: { + node1: { + exec: js, + addrs: [`${base}/0/ws`] + }, + relay: { + exec: js, + addrs: [`${base}/0/ws`] + }, + node2: { + exec: go, + addrs: [`${base}/0/ws`] + } + }, + connect: [ + [{ name: 'node1', parser: ws }, { name: 'relay' }], + [{ name: 'node2', parser: ws }, { name: 'relay' }], + [{ name: 'node1', parser: circuit }, { name: 'node2' }] + ], + send: ['node1', 'node2'] + }, + { + name: 'go-js-go', + nodes: { + node1: { + exec: go, + addrs: [`${base}/0/ws`] + }, + relay: { + exec: js, + addrs: [`${base}/0/ws`] + }, + node2: { + exec: go, + addrs: [`${base}/0/ws`] + } + }, + connect: [ + [{ name: 'node1', parser: ws }, { name: 'relay' }], + [{ name: 'node2', parser: ws }, { name: 'relay' }], + [{ name: 'node1', parser: circuit }, { name: 'node2' }] + ], + send: ['node1', 'node2'] + }, + // Browser only tests + { + name: 'browser-js-go', + nodes: { + node1: { + exec: proc, + addrs: [] + }, + relay: { + exec: js, + addrs: [`${base}/0/ws`] + }, + node2: { + exec: go, + addrs: [`${base}/0/ws`] + } + }, + connect: [ + [{ name: 'node1', parser: ws }, { name: 'relay' }], + [{ name: 'node2', parser: ws }, { name: 'relay' }], + [{ name: 'node1', parser: circuit }, { name: 'node2' }] + ], + send: ['node1', 'node2'], + skip: () => isNode + }, + { + name: 'browser-js-js', + nodes: { + node1: { + exec: proc, + addrs: [] + }, + relay: { + exec: js, + addrs: [`${base}/0/ws`] + }, + node2: { + exec: js, + addrs: [`${base}/0/ws`] + } + }, + connect: [ + [{ name: 'node1', parser: ws }, { name: 'relay' }], + [{ name: 'node2', parser: ws }, { name: 'relay' }], + [{ name: 'node1', parser: circuit }, { name: 'node2' }] + ], + send: ['node1', 'node2'], + skip: () => isNode + }, + { + name: 'js-js-browser', + nodes: { + node1: { + exec: js, + addrs: [`${base}/0/ws`] + }, + relay: { + exec: js, + addrs: [`${base}/0/ws`] + }, + node2: { + exec: proc, + addrs: [] + } + }, + connect: [ + [{ name: 'node1', parser: ws }, { name: 'relay' }], + [{ name: 'node2', parser: ws }, { name: 'relay' }], + [{ name: 'node1', parser: circuit }, { name: 'node2' }] + ], + send: ['node1', 'node2'], + skip: () => isNode + }, + { + name: 'go-js-browser', + nodes: { + node1: { + exec: go, + addrs: [`${base}/0/ws`] + }, + relay: { + exec: js, + addrs: [`${base}/0/ws`] + }, + node2: { + exec: proc, + addrs: [] + } + }, + connect: [ + [{ name: 'node1', parser: ws }, { name: 'relay' }], + [{ name: 'node2', parser: ws }, { name: 'relay' }], + [{ name: 'node1', parser: circuit }, { name: 'node2' }] + ], + send: ['node1', 'node2'], + skip: () => isNode + }, + { + name: 'browser-js-browser', + nodes: { + node1: { + exec: proc, + addrs: [] + }, + relay: { + exec: js, + addrs: [`${base}/0/ws`] + }, + node2: { + exec: proc, + addrs: [] + } + }, + connect: [ + [{ name: 'node1', parser: ws }, { name: 'relay' }], + [{ name: 'node2', parser: ws }, { name: 'relay' }], + [{ name: 'node1', parser: circuit }, { name: 'node2' }] + ], + send: ['node1', 'node2'], + skip: () => isNode + }, + { + name: 'browser-go-go', + nodes: { + node1: { + exec: proc, + addrs: [] + }, + relay: { + exec: go, + addrs: [`${base}/0/ws`] + }, + node2: { + exec: go, + addrs: [`${base}/0/ws`] + } + }, + connect: [ + [{ name: 'node1', parser: ws }, { name: 'relay' }], + [{ name: 'node2', parser: ws }, { name: 'relay' }], + [{ name: 'node1', parser: circuit }, { name: 'node2' }] + ], + send: ['node1', 'node2'], + skip: () => isNode + }, + { + name: 'browser-go-js', + nodes: { + node1: { + exec: proc, + addrs: [] + }, + relay: { + exec: go, + addrs: [`${base}/0/ws`] + }, + node2: { + exec: js, + addrs: [`${base}/0/ws`] + } + }, + connect: [ + [{ name: 'node1', parser: ws }, { name: 'relay' }], + [{ name: 'node2', parser: ws }, { name: 'relay' }], + [{ name: 'node1', parser: circuit }, { name: 'node2' }] + ], + send: ['node1', 'node2'], + skip: () => isNode + }, + { + name: 'go-go-browser', + nodes: { + node1: { + exec: js, + addrs: [`${base}/0/ws`] + }, + relay: { + exec: go, + addrs: [`${base}/0/ws`] + }, + node2: { + exec: proc, + addrs: [] + } + }, + connect: [ + [{ name: 'node1', parser: ws }, { name: 'relay' }], + [{ name: 'node2', parser: ws }, { name: 'relay' }], + [{ name: 'node1', parser: circuit }, { name: 'node2' }] + ], + send: ['node1', 'node2'], + skip: () => isNode + }, + { + name: 'go-go-browser', + nodes: { + node1: { + exec: go, + addrs: [`${base}/0/ws`] + }, + relay: { + exec: go, + addrs: [`${base}/0/ws`] + }, + node2: { + exec: proc, + addrs: [] + } + }, + connect: [ + [{ name: 'node1', parser: ws }, { name: 'relay' }], + [{ name: 'node2', parser: ws }, { name: 'relay' }], + [{ name: 'node1', parser: circuit }, { name: 'node2' }] + ], + send: ['node1', 'node2'], + skip: () => isNode + }, + { + name: 'browser-go-browser', + nodes: { + node1: { + exec: proc, + addrs: [] + }, + relay: { + exec: go, + addrs: [`${base}/0/ws`] + }, + node2: { + exec: proc, + addrs: [] + } + }, + connect: [ + [{ name: 'node1', parser: ws }, { name: 'relay' }], + [{ name: 'node2', parser: ws }, { name: 'relay' }], + [{ name: 'node1', parser: circuit }, { name: 'node2' }] + ], + send: ['node1', 'node2'], + skip: () => isNode + } +] + +describe.only('circuit', () => { + tests.forEach((test) => { + let nodes + + const dsc = test.skip && test.skip() ? describe.skip : describe + dsc(test.name, function () { + before((done) => { + create(test.nodes, (err, _nodes) => { + expect(err).to.not.exist() + nodes = _nodes + done() + }) + }) + + it('connect', (done) => { + connect(test.connect, nodes, done) + }) + + it('send', (done) => { + send(test.send, nodes, done) + }) + }) + }) +}) diff --git a/test/node.js b/test/node.js index d14777e4..ac49e854 100644 --- a/test/node.js +++ b/test/node.js @@ -1,7 +1,7 @@ /* eslint-env mocha */ 'use strict' -require('./circuit-relay') +require('./circuit') require('./repo') require('./exchange-files') require('./kad-dht') diff --git a/test/utils/circuit.js b/test/utils/circuit.js new file mode 100644 index 00000000..26833723 --- /dev/null +++ b/test/utils/circuit.js @@ -0,0 +1,180 @@ +'use strict' + +const chai = require('chai') +const dirtyChai = require('dirty-chai') +const expect = chai.expect +chai.use(dirtyChai) + +const parallel = require('async/parallel') +const series = require('async/series') +const waterfall = require('async/waterfall') +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.setUpProcNode = (addrs, hop, callback) => { + if (typeof hop === 'function') { + callback = hop + hop = false + } + + procDf.spawn({ + initOptions: { bits: 512 }, + config: Object.assign({}, baseConf, { + Addresses: { + Swarm: addrs + }, + EXPERIMENTAL: { + relay: { + enabled: true, + hop: { + enabled: hop + } + } + } + }) + }, (err, ipfsd) => { + expect(err).to.not.exist() + ipfsd.api.id((err, id) => { + callback(err, { ipfsd, addrs: id.addresses }) + }) + }) +} + +exports.setUpJsNode = (addrs, hop, callback) => { + if (typeof hop === 'function') { + callback = hop + hop = false + } + + jsDf.spawn({ + initOptions: { bits: 512 }, + config: Object.assign({}, baseConf, { + Addresses: { + Swarm: addrs + }, + EXPERIMENTAL: { + relay: { + enabled: true, + hop: { + enabled: hop + } + } + } + }) + }, (err, ipfsd) => { + expect(err).to.not.exist() + ipfsd.api.id((err, id) => { + callback(err, { ipfsd, addrs: id.addresses }) + }) + }) +} + +exports.setUpGoNode = (addrs, hop, callback) => { + if (typeof hop === 'function') { + callback = hop + hop = false + } + + goDf.spawn({ + initOptions: { bits: 1024 }, + config: Object.assign({}, baseConf, { + Addresses: { + Swarm: addrs + }, + Swarm: { + DisableRelay: false, + EnableRelayHop: hop + } + }) + }, (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 }) + }) + }) +} + +exports.create = (nodes, callback) => { + parallel( + Object.keys(nodes).map((key) => (cb) => { + const node = nodes[key] + return node.exec(node.addrs, true, (err, res) => { + expect(err).to.not.exist() + cb(null, { + name: key, + node: res + }) + }) + }), (err, res) => { + if (err) { return callback(err) } + callback(null, res) + }) +} + +exports.connect = (connect, nodes, callback) => { + const seq = connect.map((step) => { + const nodeA = nodes.find((node) => node.name === step[0].name) + const nodeB = nodes.find((node) => node.name === step[1].name) + + return (cb) => { + const addr = step[0].parser(nodeB.node.addrs) + nodeA.node.ipfsd.api.swarm.connect(addr, cb) + } + }) + + series(seq.map((func) => (cb) => func(cb)), callback) +} + +exports.send = (send, nodes, callback) => { + const data = crypto.randomBytes(128) + const nodeA = nodes.find((node) => node.name === send[0]).node.ipfsd.api + const nodeB = nodes.find((node) => node.name === send[1]).node.ipfsd.api + 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) +} + +exports.wsAddr = (addrs) => addrs + .map((a) => a.toString()) + .find((a) => { + return a.includes('/ws') && !a.includes('/p2p-websocket-star') + }) + +exports.wsStarAddr = (addrs) => addrs + .map((a) => a.toString()) + .find((a) => a.includes('/p2p-websocket-star')) + +exports.tcpAddr = (addrs) => addrs + .map((a) => a.toString()) + .find((a) => !a.includes('/ws') && !a.includes('/p2p-websocket-star')) + +exports.circuitAddr = (addrs) => addrs + .map((a) => a.toString()) + .find((a) => a.includes('/p2p-circuit/ipfs')) +