diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..18ddbab
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,42 @@
+**/node_modules/
+**/*.log
+test/repo-tests*
+**/bundle.js
+docs
+# Logs
+logs
+*.log
+
+coverage
+
+# Runtime data
+pids
+*.pid
+*.seed
+
+# Directory for instrumented libs generated by jscoverage/JSCover
+lib-cov
+
+# Coverage directory used by tools like istanbul
+coverage
+
+# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
+.grunt
+
+# node-waf configuration
+.lock-wscript
+
+build
+
+# Dependency directory
+# https://www.npmjs.org/doc/misc/npm-faq.html#should-i-check-my-node_modules-folder-into-git
+node_modules
+
+lib
+dist
+test/test-data/go-ipfs-repo/LOCK
+test/test-data/go-ipfs-repo/LOG
+test/test-data/go-ipfs-repo/LOG.old
+
+# while testing npm5
+package-lock.json
diff --git a/.travis.yml b/.travis.yml
new file mode 100644
index 0000000..93c315b
--- /dev/null
+++ b/.travis.yml
@@ -0,0 +1,33 @@
+language: node_js
+cache: npm
+stages:
+ - check
+ - test
+ - cov
+
+node_js:
+ - '10'
+
+os:
+ - linux
+ - osx
+ - windows
+
+matrix:
+ fast_finish: true
+ allow_failures:
+ - os: windows
+
+script: npx nyc -s npm run test:node -- --bail
+after_success: npx nyc report --reporter=text-lcov > coverage.lcov && npx codecov
+
+jobs:
+ include:
+ - stage: check
+ script:
+ - npx aegir commitlint --travis
+ - npx aegir dep-check
+ - npm run lint
+
+notifications:
+ email: false
diff --git a/README.md b/README.md
index 89db6c4..da59d8a 100644
--- a/README.md
+++ b/README.md
@@ -1,33 +1,40 @@
-# Interoperability Compliance Test Stories for libp2p
+# Interoperability Tests for libp2p
-
-
-
-
+[![](https://img.shields.io/badge/made%20by-Protocol%20Labs-blue.svg?style=flat-square)](https://protocol.ai/)
+[![](https://img.shields.io/badge/project-libp2p-yellow.svg?style=flat-square)](http://libp2p.io/)
+[![](https://img.shields.io/badge/freenode-%23ipfs-blue.svg?style=flat-square)](http://webchat.freenode.net/?channels=%23libp2p)
-> In this repo you will find the [PDD (Protocol Driven Development)](https://github.com/ipfs/pdd) stories that enable you to test a libp2p implementation against other implementations.
+> Interoperability tests for libp2p Implementations
-## ❗️ Important
+This repository will be used for interop tests.
-**The setup for these tests is still in flux, please make sure to read the latest update at: https://github.com/libp2p/interop/issues/1**
+## Lead Maintainer
-## PDD Compliance Test Stories
+[Vasco Santos](https://github.com/vasco-santos)
-Each Test Story has actors and a dramatization.
+## Usage
-- **Qualitative** - Test Stories that check feature interoperability and compliance.
- - [Transports](./PDD-TRANSPORTS.md)
- - [Protocol and Stream Multiplexing](./PDD-PROTOCOL-AND-STREAM-MULTIPLEXING.md)
- - [Identify](./PDD-IDENTIFY.md)
- - [Encrypted Communications](./PDD-ENCRYPTED-COMMUNICATIONS.md)
- - [Peer Routing](./PDD-PEER-ROUTING.md)
- - [Content Routing](./PDD-CONTENT-ROUTING.md)
- - [PubSub](./PDD-PUBSUB.md)
- - [Circuit Relay](./PDD-CIRCUIT-RELAY.md)
- - [The IPFS bundle](./PDD-THE-IPFS-BUNDLE.md)
-- **Quantitative** - Test Stories focused on stress testing the implementations against each other.
- - [Thousands of Muxed Streams](./PDD-THOUSANDS-OF-MUXED-STREAMS.md)
- - [Hundreds of Peer Connections](./PDD-HUNDREDS-OF-PEER-CONNECTIONS.md)
+### Install
+
+```
+> git clone git@github.com:libp2p/interop.git
+> cd interop
+> npm install
+```
+
+### Run the tests
+
+```
+> npm test
+```
+
+### Test with a non yet released version of js-ipfs
+
+TODO
+
+### Test with a non yet released version of go-ipfs
+
+TODO
## Contribute
diff --git a/package.json b/package.json
index 657d88e..1ff36fe 100644
--- a/package.json
+++ b/package.json
@@ -1,19 +1,44 @@
{
- "name": "libp2p-interop",
+ "name": "interop-libp2p",
"version": "0.0.0",
- "description": "",
- "main": " ",
+ "description": "Interoperability Tests for libp2p",
+ "leadMaintainer": "Vasco Santos ",
+ "main": "",
+ "engines": {
+ "node": ">=10.0.0",
+ "npm": ">6.0.0"
+ },
"scripts": {
- "test": "echo \"Error: no test specified\" && exit 1"
+ "lint": "aegir lint",
+ "test": "aegir test -t node -f test/node.js",
+ "test:node": "aegir test -t node -f test/node.js",
+ "test:browser": "aegir test -t browser --no-cors -f test/browser.js"
},
"repository": {
"type": "git",
"url": "git+https://github.com/libp2p/interop.git"
},
- "author": "David Dias ",
+ "keywords": [
+ "libp2p"
+ ],
"license": "MIT",
"bugs": {
"url": "https://github.com/libp2p/interop/issues"
},
- "homepage": "https://github.com/libp2p/interop#readme"
+ "homepage": "https://github.com/libp2p/interop#readme",
+ "devDependencies": {
+ "aegir": "^18.1.0",
+ "chai": "^4.2.0",
+ "chai-checkmark": "^1.0.1",
+ "cross-env": "^5.2.0",
+ "dirty-chai": "^2.0.1",
+ "go-libp2p-dep": "^6.0.30",
+ "libp2p-daemon": "~0.1.2",
+ "libp2p-daemon-client": "~0.0.3",
+ "rimraf": "^2.6.3"
+ },
+ "contributors": [],
+ "dependencies": {
+ "execa": "^1.0.0"
+ }
}
diff --git a/PDD-CIRCUIT-RELAY.md b/pdd/PDD-CIRCUIT-RELAY.md
similarity index 100%
rename from PDD-CIRCUIT-RELAY.md
rename to pdd/PDD-CIRCUIT-RELAY.md
diff --git a/PDD-CONTENT-ROUTING.md b/pdd/PDD-CONTENT-ROUTING.md
similarity index 100%
rename from PDD-CONTENT-ROUTING.md
rename to pdd/PDD-CONTENT-ROUTING.md
diff --git a/PDD-ENCRYPTED-COMMUNICATIONS.md b/pdd/PDD-ENCRYPTED-COMMUNICATIONS.md
similarity index 100%
rename from PDD-ENCRYPTED-COMMUNICATIONS.md
rename to pdd/PDD-ENCRYPTED-COMMUNICATIONS.md
diff --git a/PDD-HUNDREDS-OF-PEER-CONNECTIONS.md b/pdd/PDD-HUNDREDS-OF-PEER-CONNECTIONS.md
similarity index 100%
rename from PDD-HUNDREDS-OF-PEER-CONNECTIONS.md
rename to pdd/PDD-HUNDREDS-OF-PEER-CONNECTIONS.md
diff --git a/PDD-IDENTIFY.md b/pdd/PDD-IDENTIFY.md
similarity index 100%
rename from PDD-IDENTIFY.md
rename to pdd/PDD-IDENTIFY.md
diff --git a/PDD-PEER-ROUTING.md b/pdd/PDD-PEER-ROUTING.md
similarity index 100%
rename from PDD-PEER-ROUTING.md
rename to pdd/PDD-PEER-ROUTING.md
diff --git a/PDD-PROTOCOL-AND-STREAM-MULTIPLEXING.md b/pdd/PDD-PROTOCOL-AND-STREAM-MULTIPLEXING.md
similarity index 100%
rename from PDD-PROTOCOL-AND-STREAM-MULTIPLEXING.md
rename to pdd/PDD-PROTOCOL-AND-STREAM-MULTIPLEXING.md
diff --git a/PDD-PUBSUB.md b/pdd/PDD-PUBSUB.md
similarity index 100%
rename from PDD-PUBSUB.md
rename to pdd/PDD-PUBSUB.md
diff --git a/PDD-THE-IPFS-BUNDLE.md b/pdd/PDD-THE-IPFS-BUNDLE.md
similarity index 100%
rename from PDD-THE-IPFS-BUNDLE.md
rename to pdd/PDD-THE-IPFS-BUNDLE.md
diff --git a/PDD-THOUSANDS-OF-MUXED-STREAMS.md b/pdd/PDD-THOUSANDS-OF-MUXED-STREAMS.md
similarity index 100%
rename from PDD-THOUSANDS-OF-MUXED-STREAMS.md
rename to pdd/PDD-THOUSANDS-OF-MUXED-STREAMS.md
diff --git a/PDD-TRANSPORT.md b/pdd/PDD-TRANSPORT.md
similarity index 100%
rename from PDD-TRANSPORT.md
rename to pdd/PDD-TRANSPORT.md
diff --git a/peer-a.json b/pdd/peer-a.json
similarity index 100%
rename from peer-a.json
rename to pdd/peer-a.json
diff --git a/peer-b.json b/pdd/peer-b.json
similarity index 100%
rename from peer-b.json
rename to pdd/peer-b.json
diff --git a/peer-c.json b/pdd/peer-c.json
similarity index 100%
rename from peer-c.json
rename to pdd/peer-c.json
diff --git a/peer-d.json b/pdd/peer-d.json
similarity index 100%
rename from peer-d.json
rename to pdd/peer-d.json
diff --git a/peer-e.json b/pdd/peer-e.json
similarity index 100%
rename from peer-e.json
rename to pdd/peer-e.json
diff --git a/src/daemon.js b/src/daemon.js
new file mode 100644
index 0000000..408c008
--- /dev/null
+++ b/src/daemon.js
@@ -0,0 +1,142 @@
+'use strict'
+
+const assert = require('assert')
+const execa = require('execa')
+const fs = require('fs')
+const path = require('path')
+const rimraf = require('rimraf')
+
+const Client = require('libp2p-daemon-client')
+const { getSockPath, isWindows } = require('./utils')
+
+// process path
+const processPath = process.cwd()
+
+// go-libp2p defaults
+const goDaemon = {
+ defaultSock: getSockPath('/tmp/p2pd-go.sock'),
+ bin: path.join('go-libp2p-dep', 'go-libp2p', isWindows ? 'p2pd.exe' : 'p2pd')
+}
+
+// js-libp2p defaults
+const jsDaemon = {
+ defaultSock: getSockPath('/tmp/p2pd-js.sock'),
+ bin: path.join('libp2p-daemon', 'src', 'cli', 'bin.js')
+}
+
+class Daemon {
+ /**
+ * @constructor
+ * @param {String} type daemon implementation type ("go" or "js")
+ * @param {String} sock unix socket path
+ */
+ constructor (type, sock) {
+ assert(type === 'go' || type === 'js', 'invalid type received. Should be "go" or "js"')
+
+ this._client = undefined
+ this._type = type
+ this._binPath = this._getBinPath(type)
+ this._sock = sock && getSockPath(sock)
+
+ if (!this._sock) {
+ this._sock = type === 'go' ? goDaemon.defaultSock : jsDaemon.defaultSock
+ }
+ }
+
+ /**
+ * Get binary path according to implementation and OS
+ * @private
+ * @param {String} type daemon implementation type ("go" or "js")
+ * @returns {String}
+ */
+ _getBinPath (type) {
+ const depPath = type === 'go' ? goDaemon.bin : jsDaemon.bin
+ let npmPath = path.join(processPath, '../', depPath)
+
+ if (fs.existsSync(npmPath)) {
+ return npmPath
+ }
+
+ npmPath = path.join(processPath, 'node_modules', depPath)
+
+ if (fs.existsSync(npmPath)) {
+ return npmPath
+ }
+
+ throw new Error('Cannot find the libp2p executable')
+ }
+
+ /**
+ * @async
+ * Starts a daemon and a client associated with it.
+ * @returns {void}
+ */
+ async start () {
+ if (this._client) {
+ throw new Error('Daemon has already started')
+ }
+
+ // start daemon
+ await this._startDaemon()
+
+ // start client
+ this._client = new Client(this._sock)
+
+ await this._client.attach()
+ }
+
+ /**
+ * Starts the specifiec daemon and wait for its start.
+ * @private
+ * @returns {Promise}
+ */
+ _startDaemon () {
+ return new Promise((resolve, reject) => {
+ const options = this._type === 'go' ? ['-listen', `/unix${this._sock}`] : ['--sock', this._sock]
+ const daemon = execa(this._binPath, options)
+
+ daemon.stdout.once('data', () => {
+ return resolve()
+ })
+ daemon.stderr.once('data', (data) => {
+ return reject(data.toString())
+ })
+ })
+ }
+
+ /**
+ * @async
+ * Stops the daemon client and cleans the unix socket.
+ * @returns {void}
+ */
+ async stop () {
+ await this._client && this._client.close()
+ await this._cleanUnixSocket()
+ }
+
+ /**
+ * Cleans the unix socket.
+ * @private
+ * @returns {Promise}
+ */
+ _cleanUnixSocket () {
+ return new Promise((resolve, reject) => {
+ rimraf(this._sock, (err) => {
+ if (err) {
+ return reject(err)
+ }
+ resolve()
+ })
+ })
+ }
+
+ /**
+ * libp2p client instance
+ * @type {Client}
+ */
+ get client () {
+ return this._client
+ }
+}
+
+exports = module.exports = Daemon
diff --git a/src/utils.js b/src/utils.js
new file mode 100644
index 0000000..c8d7a72
--- /dev/null
+++ b/src/utils.js
@@ -0,0 +1,11 @@
+'use strict'
+
+const os = require('os')
+const path = require('path')
+const isWindows = os.platform() === 'win32'
+
+exports.isWindows = isWindows
+
+exports.getSockPath = (sockPath) => isWindows
+ ? path.join('\\\\?\\pipe', sockPath)
+ : path.resolve(os.tmpdir(), sockPath)
diff --git a/test/connect/README.md b/test/connect/README.md
new file mode 100644
index 0000000..ae14148
--- /dev/null
+++ b/test/connect/README.md
@@ -0,0 +1,3 @@
+# Connect
+
+In this set of tests, we intend to guarantee that nodes implemented in a specific language are able to connect with other nodes, regardless of their implementation language.
\ No newline at end of file
diff --git a/test/connect/go2go.js b/test/connect/go2go.js
new file mode 100644
index 0000000..1a9cacf
--- /dev/null
+++ b/test/connect/go2go.js
@@ -0,0 +1,61 @@
+/* eslint-env mocha */
+'use strict'
+
+const chai = require('chai')
+chai.use(require('dirty-chai'))
+chai.use(require('chai-checkmark'))
+const expect = chai.expect
+
+const Daemon = require('../../src/daemon')
+
+describe('connect', () => {
+ let goDaemon1
+ let goDaemon2
+
+ // Start Daemons
+ before(async function () {
+ this.timeout(20 * 1000)
+
+ goDaemon1 = new Daemon('go')
+ goDaemon2 = new Daemon('go', '/tmp/p2pd-go2.sock')
+
+ await Promise.all([
+ goDaemon1.start(),
+ goDaemon2.start()
+ ])
+ })
+
+ // Stop daemons
+ after(async function () {
+ await Promise.all([
+ goDaemon1.stop(),
+ goDaemon2.stop()
+ ])
+ })
+
+ it('go peer to go peer', async function () {
+ this.timeout(10 * 1000)
+
+ const identify1 = await goDaemon1.client.identify()
+ const identify2 = await goDaemon2.client.identify()
+
+ // verify connected peers
+ const knownPeersBeforeConnect1 = await goDaemon1.client.listPeers()
+ expect(knownPeersBeforeConnect1).to.have.lengthOf(0)
+
+ const knownPeersBeforeConnect2 = await goDaemon2.client.listPeers()
+ expect(knownPeersBeforeConnect2).to.have.lengthOf(0)
+
+ // connect peers
+ await goDaemon2.client.connect(identify1.peerId, identify1.addrs)
+
+ // verify connected peers
+ const knownPeersAfterConnect1 = await goDaemon1.client.listPeers()
+ expect(knownPeersAfterConnect1).to.have.lengthOf(1)
+ expect(knownPeersAfterConnect1[0].toB58String()).to.equal(identify2.peerId.toB58String())
+
+ const knownPeersAfterConnect2 = await goDaemon2.client.listPeers()
+ expect(knownPeersAfterConnect2).to.have.lengthOf(1)
+ expect(knownPeersAfterConnect2[0].toB58String()).to.equal(identify1.peerId.toB58String())
+ })
+})
diff --git a/test/connect/go2js.js b/test/connect/go2js.js
new file mode 100644
index 0000000..f60fde6
--- /dev/null
+++ b/test/connect/go2js.js
@@ -0,0 +1,61 @@
+/* eslint-env mocha */
+'use strict'
+
+const chai = require('chai')
+chai.use(require('dirty-chai'))
+chai.use(require('chai-checkmark'))
+const expect = chai.expect
+
+const Daemon = require('../../src/daemon')
+
+describe('connect', () => {
+ let goDaemon
+ let jsDaemon
+
+ // Start Daemons
+ before(async function () {
+ this.timeout(20 * 1000)
+
+ goDaemon = new Daemon('go')
+ jsDaemon = new Daemon('js')
+
+ await Promise.all([
+ goDaemon.start(),
+ jsDaemon.start()
+ ])
+ })
+
+ // Stop daemons
+ after(async function () {
+ await Promise.all([
+ goDaemon.stop(),
+ jsDaemon.stop()
+ ])
+ })
+
+ it('go peer to js peer', async function () {
+ this.timeout(10 * 1000)
+
+ const identifyJs = await jsDaemon.client.identify()
+ const identifyGo = await goDaemon.client.identify()
+
+ // verify connected peers
+ const knownPeersBeforeConnectJs = await jsDaemon.client.listPeers()
+ expect(knownPeersBeforeConnectJs).to.have.lengthOf(0)
+
+ const knownPeersBeforeConnectGo = await goDaemon.client.listPeers()
+ expect(knownPeersBeforeConnectGo).to.have.lengthOf(0)
+
+ // connect peers
+ await goDaemon.client.connect(identifyJs.peerId, identifyJs.addrs)
+
+ // verify connected peers
+ const knownPeersAfterConnectGo = await goDaemon.client.listPeers()
+ expect(knownPeersAfterConnectGo).to.have.lengthOf(1)
+ expect(knownPeersAfterConnectGo[0].toB58String()).to.equal(identifyJs.peerId.toB58String())
+
+ const knownPeersAfterConnectJs = await jsDaemon.client.listPeers()
+ expect(knownPeersAfterConnectJs).to.have.lengthOf(1)
+ expect(knownPeersAfterConnectJs[0].toB58String()).to.equal(identifyGo.peerId.toB58String())
+ })
+})
diff --git a/test/connect/index.js b/test/connect/index.js
new file mode 100644
index 0000000..dbe06c2
--- /dev/null
+++ b/test/connect/index.js
@@ -0,0 +1,6 @@
+'use strict'
+
+require('./go2go')
+require('./go2js')
+require('./js2go')
+require('./js2js')
diff --git a/test/connect/js2go.js b/test/connect/js2go.js
new file mode 100644
index 0000000..a4a885a
--- /dev/null
+++ b/test/connect/js2go.js
@@ -0,0 +1,61 @@
+/* eslint-env mocha */
+'use strict'
+
+const chai = require('chai')
+chai.use(require('dirty-chai'))
+chai.use(require('chai-checkmark'))
+const expect = chai.expect
+
+const Daemon = require('../../src/daemon')
+
+describe('connect', () => {
+ let jsDaemon
+ let goDaemon
+
+ // Start Daemons
+ before(async function () {
+ this.timeout(20 * 1000)
+
+ jsDaemon = new Daemon('js')
+ goDaemon = new Daemon('go')
+
+ await Promise.all([
+ jsDaemon.start(),
+ goDaemon.start()
+ ])
+ })
+
+ // Stop daemons
+ after(async function () {
+ await Promise.all([
+ jsDaemon.stop(),
+ goDaemon.stop()
+ ])
+ })
+
+ it('js peer to go peer', async function () {
+ this.timeout(10 * 1000)
+
+ const identifyJs = await jsDaemon.client.identify()
+ const identifyGo = await goDaemon.client.identify()
+
+ // verify connected peers
+ const knownPeersBeforeConnectJs = await jsDaemon.client.listPeers()
+ expect(knownPeersBeforeConnectJs).to.have.lengthOf(0)
+
+ const knownPeersBeforeConnectGo = await goDaemon.client.listPeers()
+ expect(knownPeersBeforeConnectGo).to.have.lengthOf(0)
+
+ // connect peers
+ await jsDaemon.client.connect(identifyGo.peerId, identifyGo.addrs)
+
+ // verify connected peers
+ const knownPeersAfterConnectJs = await jsDaemon.client.listPeers()
+ expect(knownPeersAfterConnectJs).to.have.lengthOf(1)
+ expect(knownPeersAfterConnectJs[0].toB58String()).to.equal(identifyGo.peerId.toB58String())
+
+ const knownPeersAfterConnectGo = await goDaemon.client.listPeers()
+ expect(knownPeersAfterConnectGo).to.have.lengthOf(1)
+ expect(knownPeersAfterConnectGo[0].toB58String()).to.equal(identifyJs.peerId.toB58String())
+ })
+})
diff --git a/test/connect/js2js.js b/test/connect/js2js.js
new file mode 100644
index 0000000..0eca213
--- /dev/null
+++ b/test/connect/js2js.js
@@ -0,0 +1,64 @@
+/* eslint-env mocha */
+'use strict'
+
+const chai = require('chai')
+chai.use(require('dirty-chai'))
+chai.use(require('chai-checkmark'))
+const expect = chai.expect
+
+const Daemon = require('../../src/daemon')
+
+describe('connect', () => {
+ let jsDaemon1
+ let jsDaemon2
+
+ // Start Daemons
+ before(async function () {
+ this.timeout(20 * 1000)
+
+ jsDaemon1 = new Daemon('js')
+ jsDaemon2 = new Daemon('js', '/tmp/p2pd-js2.sock')
+
+ await Promise.all([
+ jsDaemon1.start(),
+ jsDaemon2.start()
+ ])
+ })
+
+ // Stop daemons
+ after(async function () {
+ await Promise.all([
+ jsDaemon1.stop(),
+ jsDaemon2.stop()
+ ])
+ })
+
+ it('js peer to js peer', async function () {
+ this.timeout(10 * 1000)
+
+ const identify1 = await jsDaemon1.client.identify()
+ const identify2 = await jsDaemon2.client.identify()
+
+ // verify connected peers
+ const knownPeersBeforeConnect1 = await jsDaemon1.client.listPeers()
+ expect(knownPeersBeforeConnect1).to.have.lengthOf(0)
+
+ const knownPeersBeforeConnect2 = await jsDaemon2.client.listPeers()
+ expect(knownPeersBeforeConnect2).to.have.lengthOf(0)
+
+ // connect peers
+ await jsDaemon2.client.connect(identify1.peerId, identify1.addrs)
+
+ // jsDaemon1 will take some time to get the peers
+ await new Promise(resolve => setTimeout(resolve, 1000))
+
+ // verify connected peers
+ const knownPeersAfterConnect1 = await jsDaemon1.client.listPeers()
+ expect(knownPeersAfterConnect1).to.have.lengthOf(1)
+ expect(knownPeersAfterConnect1[0].toB58String()).to.equal(identify2.peerId.toB58String())
+
+ const knownPeersAfterConnect2 = await jsDaemon2.client.listPeers()
+ expect(knownPeersAfterConnect2).to.have.lengthOf(1)
+ expect(knownPeersAfterConnect2[0].toB58String()).to.equal(identify1.peerId.toB58String())
+ })
+})
diff --git a/test/node.js b/test/node.js
new file mode 100644
index 0000000..6d036c4
--- /dev/null
+++ b/test/node.js
@@ -0,0 +1,3 @@
+'use strict'
+
+require('./connect')