diff --git a/js/src/index.js b/js/src/index.js index 7db37eb5..dd2012e9 100644 --- a/js/src/index.js +++ b/js/src/index.js @@ -11,6 +11,7 @@ exports.swarm = require('./swarm') exports.block = require('./block') exports.dht = require('./dht') exports.dag = require('./dag') +exports.ping = require('./ping') exports.pubsub = require('./pubsub') exports.key = require('./key') exports.stats = require('./stats') diff --git a/js/src/ping.js b/js/src/ping.js new file mode 100644 index 00000000..a9bde93d --- /dev/null +++ b/js/src/ping.js @@ -0,0 +1,225 @@ +/* eslint-env mocha */ +'use strict' + +const chai = require('chai') +const dirtyChai = require('dirty-chai') +const pull = require('pull-stream') +const pump = require('pump') +const { Writable } = require('stream') +const { spawnNodesWithId } = require('./utils/spawn') + +const expect = chai.expect +chai.use(dirtyChai) + +function expectIsPingResponse (obj) { + expect(obj).to.have.a.property('success') + expect(obj).to.have.a.property('time') + expect(obj).to.have.a.property('text') + expect(obj.success).to.be.a('boolean') + expect(obj.time).to.be.a('number') + expect(obj.text).to.be.a('string') +} + +module.exports = (common) => { + describe('.ping', function () { + let ipfsdA + let ipfsdB + + before(function (done) { + this.timeout(30 * 1000) + + common.setup((err, factory) => { + if (err) return done(err) + + spawnNodesWithId(2, factory, (err, nodes) => { + if (err) return done(err) + ipfsdA = nodes[0] + ipfsdB = nodes[1] + done() + }) + }) + }) + + after((done) => common.teardown(done)) + + describe('.ping', function () { + this.timeout(15 * 1000) + + it('sends the specified number of packets', (done) => { + const count = 3 + ipfsdA.ping(ipfsdB.peerId.id, { count }, (err, responses) => { + expect(err).to.not.exist() + responses.forEach(expectIsPingResponse) + const pongs = responses.filter(r => Boolean(r.time)) + expect(pongs.length).to.equal(count) + done() + }) + }) + + it('fails when pinging an unknown peer', (done) => { + const unknownPeerId = 'QmUmaEnH1uMmvckMZbh3yShaasvELPW4ZLPWnB4entMTEn' + const count = 2 + + ipfsdA.ping(unknownPeerId, { count }, (err, responses) => { + expect(err).to.exist() + expect(responses[0].text).to.include('Looking up') + expect(responses[1].success).to.be.false() + done() + }) + }) + + it('fails when pinging an invalid peer', (done) => { + const invalidPeerId = 'not a peer ID' + const count = 2 + ipfsdA.ping(invalidPeerId, { count }, (err, responses) => { + expect(err).to.exist() + expect(err.message).to.include('failed to parse peer address') + done() + }) + }) + }) + + describe('.pingPullStream', function () { + this.timeout(15 * 1000) + + it('sends the specified number of packets', (done) => { + let packetNum = 0 + const count = 3 + pull( + ipfsdA.pingPullStream(ipfsdB.peerId.id, { count }), + pull.drain(({ success, time }) => { + expect(success).to.be.true() + // It's a pong + if (time) { + packetNum++ + } + }, (err) => { + expect(err).to.not.exist() + expect(packetNum).to.equal(count) + done() + }) + ) + }) + + it('fails when pinging an unknown peer', (done) => { + let messageNum = 0 + const unknownPeerId = 'QmUmaEnH1uMmvckMZbh3yShaasvELPW4ZLPWnB4entMTEn' + const count = 2 + pull( + ipfsdA.pingPullStream(unknownPeerId, { count }), + pull.drain((res) => { + expectIsPingResponse(res) + messageNum++ + + // First message should be "looking up" response + if (messageNum === 1) { + expect(res.text).to.include('Looking up') + } + + // Second message should be a failure response + if (messageNum === 2) { + expect(res.success).to.be.false() + } + }, (err) => { + expect(err).to.exist() + done() + }) + ) + }) + + it('fails when pinging an invalid peer', (done) => { + const invalidPeerId = 'not a peer ID' + const count = 2 + pull( + ipfsdA.pingPullStream(invalidPeerId, { count }), + pull.collect((err) => { + expect(err).to.exist() + expect(err.message).to.include('failed to parse peer address') + done() + }) + ) + }) + }) + + describe('.pingReadableStream', function () { + this.timeout(15 * 1000) + + it('sends the specified number of packets', (done) => { + let packetNum = 0 + const count = 3 + + pump( + ipfsdA.pingReadableStream(ipfsdB.peerId.id, { count }), + new Writable({ + objectMode: true, + write ({ success, time }, enc, cb) { + expect(success).to.be.true() + // It's a pong + if (time) { + packetNum++ + } + + cb() + } + }), + (err) => { + expect(err).to.not.exist() + expect(packetNum).to.equal(count) + done() + } + ) + }) + + it('fails when pinging an unknown peer', (done) => { + let messageNum = 0 + const unknownPeerId = 'QmUmaEnH1uMmvckMZbh3yShaasvELPW4ZLPWnB4entMTEn' + const count = 2 + + pump( + ipfsdA.pingReadableStream(unknownPeerId, { count }), + new Writable({ + objectMode: true, + write (res, enc, cb) { + expectIsPingResponse(res) + messageNum++ + + // First message should be "looking up" response + if (messageNum === 1) { + expect(res.text).to.include('Looking up') + } + + // Second message should be a failure response + if (messageNum === 2) { + expect(res.success).to.be.false() + } + + cb() + } + }), + (err) => { + expect(err).to.exist() + done() + } + ) + }) + + it('fails when pinging an invalid peer', (done) => { + const invalidPeerId = 'not a peer ID' + const count = 2 + + pump( + ipfsdA.pingReadableStream(invalidPeerId, { count }), + new Writable({ + objectMode: true, + write: (chunk, enc, cb) => cb() + }), + (err) => { + expect(err).to.exist() + expect(err.message).to.include('failed to parse peer address') + done() + } + ) + }) + }) + }) +} diff --git a/package.json b/package.json index bd4ec908..80d3eae3 100644 --- a/package.json +++ b/package.json @@ -52,7 +52,8 @@ "multihashing-async": "~0.4.8", "peer-id": "~0.10.7", "peer-info": "^0.14.1", - "pull-stream": "^3.6.7" + "pull-stream": "^3.6.7", + "pump": "^3.0.0" }, "devDependencies": {}, "contributors": [