From 1301fe92ab89f90ce8e0be2f6dffb13d628e44d6 Mon Sep 17 00:00:00 2001 From: achingbrain Date: Wed, 3 Jul 2019 16:13:17 +0100 Subject: [PATCH] chore: refactor to async/await --- .aegir.js | 8 +- README.md | 93 +++---- package.json | 25 +- src/endpoint/routes.js | 159 ++++++------ src/endpoint/server.js | 24 +- src/factory-client.js | 81 ++---- src/factory-daemon.js | 47 ++-- src/factory-in-proc.js | 81 +++--- src/ipfsd-client.js | 154 +++++------- src/ipfsd-daemon.js | 316 +++++++++++------------- src/ipfsd-in-proc.js | 146 ++++------- src/utils/configure-node.js | 10 - src/utils/exec.js | 13 +- src/utils/find-ipfs-executable.js | 1 + src/utils/parse-config.js | 13 +- src/utils/repo/browser.js | 18 +- src/utils/repo/nodejs.js | 30 ++- src/utils/run.js | 4 +- src/utils/set-config-value.js | 7 +- test/add-retrieve.spec.js | 41 ++-- test/api.spec.js | 121 ++++----- test/custom-api.spec.js | 15 +- test/endpoint/client.js | 237 +++++++++--------- test/endpoint/routes.js | 254 +++++++++---------- test/endpoint/server.js | 19 +- test/exec.node.js | 139 +++++------ test/npm-install.node.js | 44 ++-- test/spawn-options.spec.js | 396 ++++++++++++------------------ test/start-stop.node.js | 381 +++++++++++++--------------- 29 files changed, 1215 insertions(+), 1662 deletions(-) delete mode 100644 src/utils/configure-node.js diff --git a/.aegir.js b/.aegir.js index a58ff22a..6ac37631 100644 --- a/.aegir.js +++ b/.aegir.js @@ -1,10 +1,10 @@ 'use strict' -const path = require('path') + const createServer = require('./src').createServer const server = createServer() // using defaults module.exports = { - bundlesize: { maxSize: '291kB' }, + bundlesize: { maxSize: '270kB' }, karma: { files: [{ pattern: 'test/fixtures/**/*', @@ -15,8 +15,8 @@ module.exports = { }, hooks: { browser: { - pre: server.start.bind(server), - post: server.stop.bind(server) + pre: () => server.start(), + post: () => server.stop() } } } diff --git a/README.md b/README.md index 2d779903..c4520c81 100644 --- a/README.md +++ b/README.md @@ -40,16 +40,12 @@ npm install --save ipfsd-ctl const IPFSFactory = require('ipfsd-ctl') const f = IPFSFactory.create() -f.spawn(function (err, ipfsd) { - if (err) { throw err } +const ipfs = await f.spawn() +const id = await ipfsd.api.id() - ipfsd.api.id(function (err, id) { - if (err) { throw err } +console.log(id) - console.log(id) - ipfsd.stop() - }) -}) +await ipfsd.stop() ``` **Spawn an IPFS daemon from the Browser using the provided remote endpoint** @@ -64,20 +60,14 @@ const port = 9090 const server = IPFSFactory.createServer(port) const f = IPFSFactory.create({ remote: true, port: port }) -server.start((err) => { - if (err) { throw err } +await server.start() +const ipfsd = await f.spawn() +const id = await ipfsd.api.id() - f.spawn((err, ipfsd) => { - if (err) { throw err } +console.log(id) - ipfsd.api.id(function (err, id) { - if (err) { throw err } - - console.log(id) - ipfsd.stop(server.stop) - }) - }) -}) +await ipfsd.stop() +await server.stop() ``` ## Disposable vs non Disposable nodes @@ -112,7 +102,7 @@ Install one or both of the following modules: **example:** See [Usage](#usage) -#### Spawn a daemon with `f.spawn([options], callback)` +#### Spawn a daemon with `f.spawn([options]) : Promise` Spawn the daemon @@ -126,14 +116,14 @@ Spawn the daemon - `args` - array of cmd line arguments to be passed to ipfs daemon - `config` - ipfs configuration options -- `callback` - is a function with the signature `function (err, ipfsd)` where: - - `err` - is the error set if spawning the node is unsuccessful - - `ipfsd` - is the daemon controller instance: - - `api` - a property of `ipfsd`, an instance of [ipfs-http-client](https://github.com/ipfs/js-ipfs-http-client) attached to the newly created ipfs node +Returns a promise that resolves to: + +- `ipfsd` - is the daemon controller instance: + - `api` - a property of `ipfsd`, an instance of [ipfs-http-client](https://github.com/ipfs/js-ipfs-http-client) attached to the newly created ipfs node **example:** See [Usage](#usage) -#### Get daemon version with `f.version(callback)` +#### Get daemon version with `f.version() : Promise` Get the version without spawning a daemon @@ -158,17 +148,13 @@ const IPFSFactory = require('ipfsd-ctl') const server = IPFSFactory.createServer({ port: 12345 }) -server.start((err) => { - if (err) { throw err } +await server.start() - console.log('endpoint is running') +console.log('endpoint is running') - server.stop((err) => { - if (err) { throw err } +await server.stop() - console.log('endpoint has stopped') - }) -}) +console.log('endpoint has stopped') ``` ### IPFS Daemon Controller - `ipfsd` @@ -191,7 +177,7 @@ Get the current repo path. Returns string. Is the node started. Returns a boolean. -#### `init([initOpts], callback)` +#### `init([initOpts]) : Promise` Initialize a repo. @@ -200,30 +186,31 @@ Initialize a repo. - `directory` (default IPFS_PATH if defined, or ~/.ipfs for go-ipfs and ~/.jsipfs for js-ipfs) - The location of the repo. - `pass` (optional) - The passphrase of the key chain. -`callback` is a function with the signature `function (err, ipfsd)` where `err` is an Error in case something goes wrong and `ipfsd` is the daemon controller instance. +Returns a promise that resolves to a daemon controller instance. -#### `ipfsd.cleanup(callback)` +#### `ipfsd.cleanup() : Promise` Delete the repo that was being used. If the node was marked as `disposable` this will be called automatically when the process is exited. -`callback` is a function with the signature `function(err)`. +Returns a promise that resolves when the cleanup is complete. -#### `ipfsd.start(flags, callback)` +#### `ipfsd.start(flags) : Promise` Start the daemon. `flags` - Flags array to be passed to the `ipfs daemon` command. -`callback` is a function with the signature `function(err, ipfsClient)` that receives an instance of `Error` on failure or an instance of `ipfs-http-client` on success. +Returns a promiset hat resolves to an instance of `ipfs-http-client`. - -#### `ipfsd.stop([timeout, callback])` +#### `ipfsd.stop([timeout]) : Promise` Stop the daemon. -`callback` is a function with the signature `function(err)` callback - function that receives an instance of `Error` on failure. Use timeout to specify the grace period in ms before hard stopping the daemon. Otherwise, a grace period of `10500` ms will be used for disposable nodes and `10500 * 3` ms for non disposable nodes. +Use `timeout` to specify the grace period in ms before hard stopping the daemon. Otherwise, a grace period of `10500` ms will be used for disposable nodes and `10500 * 3` ms for non disposable nodes. + +Returns a promise that resolves when the daemon has stopped. -#### `ipfsd.killProcess([timeout, callback])` +#### `ipfsd.killProcess([timeout]) : Promise` Kill the `ipfs daemon` process. Use timeout to specify the grace period in ms before hard stopping the daemon. Otherwise, a grace period of `10500` ms will be used for disposable nodes and `10500 * 3` ms for non disposable nodes. @@ -231,23 +218,23 @@ Note: timeout is ignored for `proc` nodes First a `SIGTERM` is sent, after 10.5 seconds `SIGKILL` is sent if the process hasn't exited yet. -`callback` is a function with the signature `function()` called once the process is killed +Returns a promise that resolves once the process is killed -#### `ipfsd.pid(callback)` +#### `ipfsd.pid() : Promise` Get the pid of the `ipfs daemon` process. Returns the pid number -`callback` is a function with the signature `function(err, pid)` that receives the `pid` of the running daemon or an `Error` instance on failure +Returns a promiset that resolves to the `pid` of the running daemon. -#### `ipfsd.getConfig([key], callback)` +#### `ipfsd.getConfig([key]) : Promise` Returns the output of an `ipfs config` command. If no `key` is passed, the whole config is returned as an object. `key` (optional) - A specific config to retrieve. -`callback` is a function with the signature `function(err, (Object|string))` that receives an object or string on success or an `Error` instance on failure +Returns a promise that resolves to `Object|string` on success. -#### `ipfsd.setConfig(key, value, callback)` +#### `ipfsd.setConfig(key, value) : Promise` Set a config value. @@ -255,13 +242,13 @@ Set a config value. `value` - the config value to change/set -`callback` is a function with the signature `function(err)` callback - function that receives an `Error` instance on failure +Returns a promise that resolves on success. -#### `ipfsd.version(callback)` +#### `ipfsd.version() : Promise` Get the version of ipfs -`callback` is a function with the signature `function(err, version)` +Returns a promise that resolves to the `version` ### IPFS HTTP Client - `ipfsd.api` diff --git a/package.json b/package.json index 2cb769ac..70bd434d 100644 --- a/package.json +++ b/package.json @@ -20,9 +20,10 @@ "coverage": "aegir coverage --timeout 50000" }, "browser": { - "hapi": false, + "@hapi/hapi": false, "glob": false, "fs": false, + "fs-extra": false, "joi": false, "./src/utils/repo/nodejs.js": "./src/utils/repo/browser.js", "./src/utils/exec.js": false, @@ -61,40 +62,34 @@ "daemon" ], "dependencies": { - "async": "^2.6.2", - "base-x": "^3.0.5", + "@hapi/hapi": "^18.3.1", "boom": "^7.3.0", "debug": "^4.1.1", "detect-node": "^2.0.4", "dexie": "^2.0.4", - "execa": "^1.0.0", - "hapi": "^16.6.2", + "execa": "^2.0.2", + "fs-extra": "^8.1.0", "hat": "~0.0.3", - "ipfs-http-client": "ipfs/js-ipfs-http-client#master", + "ipfs-http-client": "^32.0.1", "joi": "^14.3.1", - "libp2p-crypto": "~0.16.1", "lodash.clone": "^4.5.0", "lodash.defaults": "^4.2.0", "lodash.defaultsdeep": "^4.6.0", "multiaddr": "^6.1.0", - "once": "^1.4.0", - "protons": "^1.0.1", - "rimraf": "^2.6.3", - "safe-json-parse": "^4.0.0", "safe-json-stringify": "^1.2.0", "superagent": "^5.0.5" }, "devDependencies": { - "aegir": "^19.0.3", + "aegir": "^19.0.5", "chai": "^4.2.0", + "delay": "^4.3.0", "detect-port": "^1.3.0", "dirty-chai": "^2.0.1", "go-ipfs-dep": "~0.4.21", - "husky": "^2.4.1", + "husky": "^3.0.0", "ipfs": "~0.36.3", "is-running": "^2.1.0", - "lint-staged": "^8.1.7", - "mkdirp": "~0.5.1", + "lint-staged": "^9.0.2", "proxyquire": "^2.1.0", "superagent-mocker": "~0.5.2" }, diff --git a/src/endpoint/routes.js b/src/endpoint/routes.js index bf964b98..ab297872 100644 --- a/src/endpoint/routes.js +++ b/src/endpoint/routes.js @@ -1,8 +1,8 @@ 'use strict' const hat = require('hat') -const boom = require('boom') const Joi = require('joi') +const boom = require('boom') const defaults = require('lodash.defaultsdeep') const FactoryDaemon = require('../factory-daemon') const tmpDir = require('../utils/tmp-dir') @@ -27,28 +27,30 @@ module.exports = (server) => { server.route({ method: 'GET', path: '/util/tmp-dir', - handler: (request, reply) => { + handler: (request) => { const type = request.query.type || 'go' const path = tmpDir(type === 'js') - reply({ tmpDir: path }) + return { tmpDir: path } } }) server.route({ method: 'GET', path: '/version', - handler: (request, reply) => { + handler: async (request) => { const type = request.query.type || 'go' // TODO: use the ../src/index.js so that the right Factory is picked const f = new FactoryDaemon({ type: type }) - f.version((err, version) => { - if (err) { - return reply(boom.badRequest(err)) + + try { + return { + version: await f.version() } - reply({ version: version }) - }) + } catch (err) { + throw boom.badRequest(err.message) + } } }) @@ -59,16 +61,14 @@ module.exports = (server) => { server.route({ method: 'POST', path: '/spawn', - handler: (request, reply) => { + handler: async (request) => { const payload = request.payload || {} // TODO: use the ../src/index.js so that the right Factory is picked const f = new FactoryDaemon({ type: payload.type }) - f.spawn(payload.options, (err, ipfsd) => { - if (err) { - return reply(boom.badRequest(err)) - } + try { + const ipfsd = await f.spawn(payload.options) const id = hat() const initialized = ipfsd.initialized nodes[id] = ipfsd @@ -86,8 +86,14 @@ module.exports = (server) => { } } - reply({ id: id, api: api, initialized: initialized }) - }) + return { + id, + api, + initialized + } + } catch (err) { + throw boom.badRequest(err.message) + } } }) @@ -97,18 +103,19 @@ module.exports = (server) => { server.route({ method: 'POST', path: '/init', - handler: (request, reply) => { + handler: async (request) => { const id = request.query.id - const payload = request.payload || {} - nodes[id].init(payload.initOpts, (err) => { - if (err) { - return reply(boom.badRequest(err)) - } + try { + await nodes[id].init(payload.initOpts) - reply({ initialized: nodes[id].initialized }) - }) + return { + initialized: nodes[id].initialized + } + } catch (err) { + throw boom.badRequest(err.message) + } }, config: routeConfig }) @@ -119,24 +126,23 @@ module.exports = (server) => { server.route({ method: 'POST', path: '/start', - handler: (request, reply) => { + handler: async (request) => { const id = request.query.id - const payload = request.payload || {} const flags = payload.flags || [] - nodes[id].start(flags, (err) => { - if (err) { - return reply(boom.badRequest(err)) - } + try { + await nodes[id].start(flags) - reply({ + return { api: { apiAddr: nodes[id].apiAddr.toString(), gatewayAddr: nodes[id].gatewayAddr.toString() } - }) - }) + } + } catch (err) { + throw boom.badRequest(err.message) + } }, config: routeConfig }) @@ -147,10 +153,10 @@ module.exports = (server) => { server.route({ method: 'GET', path: '/api-addr', - handler: (request, reply) => { + handler: (request) => { const id = request.query.id - reply({ apiAddr: nodes[id].apiAddr.toString() }) + return { apiAddr: nodes[id].apiAddr.toString() } }, config: routeConfig }) @@ -162,9 +168,10 @@ module.exports = (server) => { server.route({ method: 'GET', path: '/getaway-addr', - handler: (request, reply) => { + handler: (request) => { const id = request.query.id - reply({ getawayAddr: nodes[id].gatewayAddr.toString() }) + + return { getawayAddr: nodes[id].gatewayAddr.toString() } }, config: routeConfig }) @@ -177,15 +184,16 @@ module.exports = (server) => { server.route({ method: 'POST', path: '/cleanup', - handler: (request, reply) => { + handler: async (request, h) => { const id = request.query.id - nodes[id].cleanup((err) => { - if (err) { - return reply(boom.badRequest(err)) - } - reply().code(200) - }) + try { + await nodes[id].cleanup() + + return h.response().code(200) + } catch (err) { + throw boom.badRequest(err.message) + } }, config: routeConfig }) @@ -196,16 +204,17 @@ module.exports = (server) => { server.route({ method: 'POST', path: '/stop', - handler: (request, reply) => { + handler: async (request, h) => { const id = request.query.id const timeout = request.payload.timeout - nodes[id].stop(timeout, (err) => { - if (err) { - return reply(boom.badRequest(err)) - } - reply().code(200) - }) + try { + await nodes[id].stop(timeout) + + return h.response().code(200) + } catch (err) { + throw boom.badRequest(err.message) + } }, config: routeConfig }) @@ -219,16 +228,17 @@ module.exports = (server) => { server.route({ method: 'POST', path: '/kill', - handler: (request, reply) => { + handler: async (request, h) => { const id = request.query.id const timeout = request.payload.timeout - nodes[id].killProcess(timeout, (err) => { - if (err) { - return reply(boom.badRequest(err)) - } - reply().code(200) - }) + try { + await nodes[id].killProcess(timeout) + + return h.response().code(200) + } catch (err) { + throw boom.badRequest(err.message) + } }, config: routeConfig }) @@ -239,10 +249,10 @@ module.exports = (server) => { server.route({ method: 'GET', path: '/pid', - handler: (request, reply) => { + handler: (request) => { const id = request.query.id - reply({ pid: nodes[id].pid }) + return { pid: nodes[id].pid } }, config: routeConfig }) @@ -255,16 +265,17 @@ module.exports = (server) => { server.route({ method: 'GET', path: '/config', - handler: (request, reply) => { + handler: async (request) => { const id = request.query.id const key = request.query.key - nodes[id].getConfig(key, (err, config) => { - if (err) { - return reply(boom.badRequest(err)) - } - reply({ config: config }) - }) + try { + const config = await nodes[id].getConfig(key) + + return { config: config } + } catch (err) { + throw boom.badRequest(err.message) + } }, config: defaults({}, { validate: { @@ -281,18 +292,18 @@ module.exports = (server) => { server.route({ method: 'PUT', path: '/config', - handler: (request, reply) => { + handler: async (request, h) => { const id = request.query.id const key = request.payload.key const val = request.payload.value - nodes[id].setConfig(key, val, (err) => { - if (err) { - return reply(boom.badRequest(err)) - } + try { + await nodes[id].setConfig(key, val) + } catch (err) { + throw boom.badRequest(err.message) + } - reply().code(200) - }) + return h.response().code(200) }, config: defaults({}, { validate: { diff --git a/src/endpoint/server.js b/src/endpoint/server.js index 05c42bd2..b9da9fc2 100644 --- a/src/endpoint/server.js +++ b/src/endpoint/server.js @@ -1,6 +1,6 @@ 'use strict' -const Hapi = require('hapi') +const Hapi = require('@hapi/hapi') const routes = require('./routes') /** @@ -22,12 +22,8 @@ class Server { * @param {function(Error, Hapi.Server): void} cb * @returns {void} */ - start (cb) { - cb = cb || (() => {}) - - this.server = new Hapi.Server() - - this.server.connection({ + async start () { + this.server = new Hapi.Server({ port: this.port, host: 'localhost', routes: { @@ -37,13 +33,9 @@ class Server { routes(this.server) - this.server.start((err) => { - if (err) { - return cb(err) - } + await this.server.start() - cb(null, this.server) - }) + return this.server } /** @@ -52,10 +44,8 @@ class Server { * @param {function(err: Error)} [cb] - {@link https://github.com/hapijs/hapi/blob/v16.6.2/API.md#serverstopoptions-callback Hapi docs} * @returns {void} */ - stop (cb) { - cb = cb || (() => {}) - - this.server.stop(cb) + stop () { + return this.server.stop() } } diff --git a/src/factory-client.js b/src/factory-client.js index 1ce59c27..bfa9236c 100644 --- a/src/factory-client.js +++ b/src/factory-client.js @@ -34,86 +34,55 @@ class FactoryClient { * useful in browsers to be able to generate temp * repos manually * - * @param {string} type - the type of the node - * @param {function(Error, string): void} callback - * @returns {void} + * @returns {Promise} */ - tmpDir (type, callback) { - request + async tmpDir () { + const res = await request .get(`${this.baseUrl}/util/tmp-dir`) - .end((err, res) => { - if (err) { - return callback(new Error(err.response ? err.response.body.message : err)) - } - callback(null, res.body.tmpDir) - }) + return res.body.tmpDir } /** * Get the version of the IPFS Daemon. * * @param {Object} [options={}] - * @param {function(Error, string): void} callback - * @returns {undefined} + * @returns {Promise} */ - version (options, callback) { - if (typeof options === 'function') { - callback = options - options = undefined - } - + async version (options = {}) { options = options || { type: this.options.type } - request + const res = await request .get(`${this.baseUrl}/version`) .query(options) - .end((err, res) => { - if (err) { - return callback(new Error(err.response ? err.response.body.message : err)) - } - callback(null, res.body.version) - }) + return res.body.version } /** * Spawn a remote daemon using ipfs-http-client * * @param {SpawnOptions} [options={}] - * @param {function(Error, DaemonClient)} callback - * @return {void} + * @return {Promise} */ - spawn (options, callback) { - if (typeof options === 'function') { - callback = options - options = {} - } - - options = options || {} - - request + async spawn (options = {}) { + const res = await request .post(`${this.baseUrl}/spawn`) .send({ options: options, type: this.options.type }) - .end((err, res) => { - if (err) { - return callback(new Error(err.response ? err.response.body.message : err)) - } - - const apiAddr = res.body.api ? res.body.api.apiAddr : '' - const gatewayAddr = res.body.api ? res.body.api.gatewayAddr : '' - - const ipfsd = new DaemonClient( - this.baseUrl, - res.body.id, - res.body.initialized, - apiAddr, - gatewayAddr, - { IpfsClient: this.options.IpfsClient } - ) - - callback(null, ipfsd) - }) + + const apiAddr = res.body.api ? res.body.api.apiAddr : '' + const gatewayAddr = res.body.api ? res.body.api.gatewayAddr : '' + + const ipfsd = new DaemonClient( + this.baseUrl, + res.body.id, + res.body.initialized, + apiAddr, + gatewayAddr, + { IpfsClient: this.options.IpfsClient } + ) + + return ipfsd } } diff --git a/src/factory-daemon.js b/src/factory-daemon.js index 65279de1..13f386f1 100644 --- a/src/factory-daemon.js +++ b/src/factory-daemon.js @@ -2,7 +2,6 @@ const defaultsDeep = require('lodash.defaultsdeep') const clone = require('lodash.clone') -const series = require('async/series') const path = require('path') const tmpDir = require('./utils/tmp-dir') const Daemon = require('./ipfsd-daemon') @@ -34,31 +33,25 @@ class FactoryDaemon { * *Here for completeness* * * @param {String} type - the type of the node - * @param {function(Error, string): void} callback - * @returns {void} + * @returns {Promise} */ - tmpDir (type, callback) { - callback(null, tmpDir(type === 'js')) + tmpDir (type) { + return tmpDir(type === 'js') } /** * Get the version of the IPFS Daemon. * * @param {Object} [options={}] - * @param {function(Error, (string|Object)): void} callback - Receives `Error` or `version` that might be one of the following: + * @returns {Promise} - Resolves to `version` that might be one of the following: * - if type is `go` a version string like `ipfs version ` * - if type is `js` a version string like `js-ipfs version ` * - if type is `proc` an object with the following properties: * - version - the ipfs version * - repo - the repo version * - commit - the commit hash for this version - * @returns {void} */ - version (options, callback) { - if (typeof options === 'function') { - callback = options - options = {} - } + version (options = {}) { options = Object.assign( { IpfsClient: this.options.IpfsClient }, options, @@ -68,7 +61,7 @@ class FactoryDaemon { // TODO: (2) This spawns a whole daemon just to get his version? There is // a way to get the version while the daemon is offline... const d = new Daemon(options) - d.version(callback) + return d.version() } /** @@ -78,12 +71,7 @@ class FactoryDaemon { * @param {function(Error, Daemon): void} callback - Callback receives Error or a Daemon instance, Daemon has a `api` property which is an `ipfs-http-client` instance. * @returns {void} */ - spawn (options, callback) { - if (typeof options === 'function') { - callback = options - options = {} - } - + async spawn (options = {}) { // TODO this options parsing is daunting. Refactor and move to a separate // func documenting what it is trying to do. options = defaultsDeep( @@ -125,18 +113,15 @@ class FactoryDaemon { const node = new Daemon(options) - series([ - (cb) => options.init - ? node.init(options.initOptions, cb) - : cb(null, node), - (cb) => options.start - ? node.start(options.args, cb) - : cb() - ], (err) => { - if (err) { return callback(err) } - - callback(null, node) - }) + if (options.init) { + await node.init(options.initOptions) + } + + if (options.start) { + await node.start(options.args) + } + + return node } } diff --git a/src/factory-in-proc.js b/src/factory-in-proc.js index 9889adf7..775bbbe3 100644 --- a/src/factory-in-proc.js +++ b/src/factory-in-proc.js @@ -2,9 +2,7 @@ const defaults = require('lodash.defaultsdeep') const clone = require('lodash.clone') -const series = require('async/series') const path = require('path') -const once = require('once') const tmpDir = require('./utils/tmp-dir') const repoUtils = require('./utils/repo/nodejs') const InProc = require('./ipfsd-in-proc') @@ -36,30 +34,26 @@ class FactoryInProc { * * *Here for completeness* * - * @param {string} type - the type of the node - * @param {function(Error, string): void} callback - * @returns {void} + * @returns {Promise} */ - tmpDir (type, callback) { - callback(null, tmpDir(true)) + tmpDir () { + return tmpDir(true) } /** * Get the version of the currently used go-ipfs binary. * * @param {Object} [options={}] - * @param {function(Error, string): void} callback - * @returns {void} + * @returns {Promise} */ - version (options, callback) { - if (typeof options === 'function') { - callback = options - options = {} - } - - const node = new InProc(options) - node.once('ready', () => { - node.version(callback) + version (options = {}) { + return new Promise((resolve, reject) => { + const node = new InProc(options) + node.once('ready', () => { + node.version() + .then(resolve, reject) + }) + node.once('error', reject) }) } @@ -67,15 +61,9 @@ class FactoryInProc { * Spawn JSIPFS instances * * @param {SpawnOptions} [opts={}] - various config options and ipfs config parameters - * @param {function(Error, InProc): void} callback - a callback that receives an array with an `ipfs-instance` attached to the node and a `Node` - * @returns {void} + * @returns {Promise} - Resolves to an array with an `ipfs-instance` attached to the node and a `Node` */ - spawn (opts, callback) { - if (typeof opts === 'function') { - callback = opts - opts = defaultOptions - } - + async spawn (opts = {}) { const options = defaults({}, opts, defaultOptions) options.init = typeof options.init !== 'undefined' ? options.init @@ -105,32 +93,29 @@ class FactoryInProc { options.exec = options.exec || this.options.exec if (typeof options.exec !== 'function') { - return callback(new Error(`'type' proc requires 'exec' to be a coderef`)) + throw new Error(`'type' proc requires 'exec' to be a coderef`) } const node = new InProc(options) - const callbackOnce = once((err) => { - if (err) { - return callback(err) - } - callback(null, node) + + await new Promise((resolve, reject) => { + node.once('error', reject) + node.once('ready', () => { + resolve() + }) }) - node.once('error', callbackOnce) - - series([ - (cb) => node.once('ready', cb), - (cb) => repoUtils.repoExists(node.path, (err, initialized) => { - if (err) { return cb(err) } - node.initialized = initialized - cb() - }), - (cb) => options.init - ? node.init(cb) - : cb(), - (cb) => options.start - ? node.start(options.args, cb) - : cb() - ], callbackOnce) + + node.initialized = await repoUtils.repoExists(node.path) + + if (options.init) { + await node.init() + } + + if (options.start) { + await node.start(options.args) + } + + return node } } diff --git a/src/ipfsd-client.js b/src/ipfsd-client.js index d9607191..3f557c61 100644 --- a/src/ipfsd-client.js +++ b/src/ipfsd-client.js @@ -20,6 +20,22 @@ function createApi (apiAddr, gwAddr, IpfsClient) { return api } +function translateError (err) { + let message = err.message + + if (err.response && err.response.body && err.response.body.message) { + message = err.response.body.message + } + + const output = new Error(message) + output.status = err.status + output.response = err.response + output.stack = err.stack + output.message = message + + throw output +} + /** * Creates an instance of Client. * @@ -89,24 +105,16 @@ class Client { * @param {function (Error, Node)} cb * @returns {undefined} */ - init (initOpts, cb) { - if (typeof initOpts === 'function') { - cb = initOpts - initOpts = {} - } - - request + async init (initOpts = {}) { + const res = await request .post(`${this.baseUrl}/init`) .query({ id: this._id }) .send({ initOpts }) - .end((err, res) => { - if (err) { - return cb(new Error(err.response ? err.response.body.message : err)) - } - - this.initialized = res.body.initialized - cb(null, this) - }) + .catch(translateError) + + this.initialized = res.body.initialized + + return this } /** @@ -117,11 +125,11 @@ class Client { * @param {function(Error)} cb * @returns {undefined} */ - cleanup (cb) { - request + cleanup () { + return request .post(`${this.baseUrl}/cleanup`) .query({ id: this._id }) - .end((err) => { cb(err) }) + .catch(translateError) } /** @@ -131,29 +139,21 @@ class Client { * @param {function(Error, IpfsClient)} cb * @returns {undefined} */ - start (flags, cb) { - if (typeof flags === 'function') { - cb = flags - flags = [] - } - - request + async start (flags = []) { + const res = await request .post(`${this.baseUrl}/start`) .query({ id: this._id }) .send({ flags }) - .end((err, res) => { - if (err) { - return cb(new Error(err.response ? err.response.body.message : err)) - } + .catch(translateError) + + this.started = true - this.started = true + const apiAddr = res.body.api ? res.body.api.apiAddr : '' + const gatewayAddr = res.body.api ? res.body.api.gatewayAddr : '' - const apiAddr = res.body.api ? res.body.api.apiAddr : '' - const gatewayAddr = res.body.api ? res.body.api.gatewayAddr : '' + this.api = createApi(apiAddr, gatewayAddr, this.options.IpfsClient || IpfsClient) - this.api = createApi(apiAddr, gatewayAddr, this.options.IpfsClient || IpfsClient) - return cb(null, this.api) - }) + return this.api } /** @@ -163,25 +163,14 @@ class Client { * @param {function(Error)} [cb] * @returns {undefined} */ - stop (timeout, cb) { - if (typeof timeout === 'function') { - cb = timeout - timeout = undefined - } - - cb = cb || (() => {}) - request + async stop (timeout) { + await request .post(`${this.baseUrl}/stop`) .query({ id: this._id }) .send({ timeout }) - .end((err) => { - if (err) { - return cb(new Error(err.response.body.message)) - } - - this.started = false - cb(null) - }) + .catch(translateError) + + this.started = false } /** @@ -194,25 +183,14 @@ class Client { * @param {function()} [cb] - Called when the process was killed. * @returns {undefined} */ - killProcess (timeout, cb) { - if (typeof timeout === 'function') { - cb = timeout - timeout = undefined - } - - cb = cb || (() => {}) - request + async killProcess (timeout) { + await request .post(`${this.baseUrl}/kill`) .query({ id: this._id }) .send({ timeout }) - .end((err) => { - if (err) { - return cb(new Error(err.response.body.message)) - } - - this.started = false - cb(null) - }) + .catch(translateError) + + this.started = false } /** @@ -221,17 +199,13 @@ class Client { * @param {function(Error, number): void} cb - receives the pid * @returns {void} */ - pid (cb) { - request + async pid () { + const res = await request .get(`${this.baseUrl}/pid`) .query({ id: this._id }) - .end((err, res) => { - if (err) { - return cb(new Error(err.response ? err.response.body.message : err)) - } + .catch(translateError) - cb(null, res.body.pid) - }) + return res.body.pid } /** @@ -243,27 +217,19 @@ class Client { * @param {function(Error, (Object|string))} cb * @returns {void} */ - getConfig (key, cb) { - if (typeof key === 'function') { - cb = key - key = undefined - } - + async getConfig (key) { const qr = { id: this._id } if (key) { qr.key = key } - request + + const res = await request .get(`${this.baseUrl}/config`) .query(qr) - .end((err, res) => { - if (err) { - return cb(new Error(err.response ? err.response.body.message : err)) - } + .catch(translateError) - cb(null, res.body.config) - }) + return res.body.config } /** @@ -274,17 +240,11 @@ class Client { * @param {function(Error)} cb * @returns {void} */ - setConfig (key, value, cb) { - request.put(`${this.baseUrl}/config`) + async setConfig (key, value) { + await request.put(`${this.baseUrl}/config`) .send({ key, value }) .query({ id: this._id }) - .end((err) => { - if (err) { - return cb(new Error(err.response ? err.response.body.message : err)) - } - - cb(null) - }) + .catch(translateError) } } diff --git a/src/ipfsd-daemon.js b/src/ipfsd-daemon.js index 9cb34aa7..29c59c0c 100644 --- a/src/ipfsd-daemon.js +++ b/src/ipfsd-daemon.js @@ -1,13 +1,9 @@ 'use strict' -const fs = require('fs') -const waterfall = require('async/waterfall') -const series = require('async/series') const IpfsClient = require('ipfs-http-client') const multiaddr = require('multiaddr') -const rimraf = require('rimraf') +const fs = require('fs-extra') const path = require('path') -const once = require('once') const defaults = require('lodash.defaults') const debug = require('debug') const os = require('os') @@ -18,7 +14,6 @@ const daemonLog = { err: debug('ipfsd-ctl:daemon:stderr') } -const safeParse = require('safe-json-parse/callback') const safeStringify = require('safe-json-stringify') const tmpDir = require('./utils/tmp-dir') @@ -32,6 +27,15 @@ const GRACE_PERIOD = 10500 // amount of ms to wait before sigkill for non disposable repos const NON_DISPOSABLE_GRACE_PERIOD = 10500 * 3 +function translateError (err) { + // get the actual error message to be the err.message + let message = err.message + err.message = err.stderr + err.stderr = message + + throw err +} + /** * ipfsd for a go-ipfs or js-ipfs daemon * Create a new node. @@ -146,17 +150,9 @@ class Daemon { * @param {number} [initOptions.bits=2048] - The bit size of the identiy key. * @param {string} [initOptions.directory=IPFS_PATH] - The location of the repo. * @param {string} [initOptions.pass] - The passphrase of the keychain. - * @param {function (Error, Daemon): void} callback - * @returns {void} + * @returns {Promise} */ - init (initOptions, callback) { - if (typeof initOptions === 'function') { - callback = initOptions - initOptions = {} - } - - initOptions = initOptions || {} - + async init (initOptions = {}) { if (initOptions.directory && initOptions.directory !== this.path) { this.path = initOptions.directory } @@ -183,58 +179,44 @@ class Daemon { log(`ignoring "profile" option, not supported for ${this.opts.type} node`) } } - run(this, args, { env: this.env }, (err, result) => { - if (err) { - return callback(err) - } - waterfall([ - (cb) => this.getConfig(cb), - (conf, cb) => this.replaceConfig(defaults({}, this.opts.config, conf), cb) - ], (err) => { - if (err) { return callback(err) } - this.clean = false - this.initialized = true - return callback(null, this) - }) - }) + await run(this, args, { env: this.env }) + .catch(translateError) + + const conf = await this.getConfig() + + await this.replaceConfig(defaults({}, this.opts.config, conf)) + + this.clean = false + this.initialized = true + + return this } /** * Delete the repo that was being used. If the node was marked as disposable this will be called automatically when the process is exited. * - * @param {function(Error): void} callback - * @returns {void} + * @returns {Promise} */ - cleanup (callback) { + async cleanup () { if (this.clean) { - return callback() + return } this.clean = true - rimraf(this.path, callback) + + await fs.remove(this.path) } /** * Start the daemon. * * @param {Array} [flags=[]] - Flags to be passed to the `ipfs daemon` command. - * @param {function(Error, IpfsClient): void} callback - * @return {void} + * @return {Promise} */ - start (flags, callback) { - if (typeof flags === 'function') { - callback = flags - flags = [] - } - if (typeof flags === 'object' && Object.keys(flags).length === 0) { - flags = [] - } - + start (flags = []) { const args = ['daemon'].concat(flags || []) - callback = once(callback) - const setApiAddr = (addr) => { this._apiAddr = multiaddr(addr) this.api = (this.opts.IpfsClient || IpfsClient)(addr) @@ -249,72 +231,74 @@ class Daemon { } const api = this.runningNodeApi + if (api) { setApiAddr(api) this._started = true - return callback(null, this.api) + return this.api } let output = '' - this.subprocess = run(this, args, { - env: this.env, - stderr: (data) => { - data = String(data) + return new Promise(async (resolve, reject) => { + this.subprocess = run(this, args, { + env: this.env, + stderr: (data) => { + data = String(data) - if (data) { - daemonLog.err(data.trim()) - } - }, - stdout: (data) => { - data = String(data) + if (data) { + daemonLog.err(data.trim()) + } + }, + stdout: (data) => { + data = String(data) - if (data) { - daemonLog.info(data.trim()) - } + if (data) { + daemonLog.info(data.trim()) + } - output += data + output += data - const apiMatch = output.trim().match(/API .*listening on:? (.*)/) - const gwMatch = output.trim().match(/Gateway .*listening on:? (.*)/) + const apiMatch = output.trim().match(/API .*listening on:? (.*)/) + const gwMatch = output.trim().match(/Gateway .*listening on:? (.*)/) - if (apiMatch && apiMatch.length > 0) { - setApiAddr(apiMatch[1]) - } + if (apiMatch && apiMatch.length > 0) { + setApiAddr(apiMatch[1]) + } - if (gwMatch && gwMatch.length > 0) { - setGatewayAddr(gwMatch[1]) - } + if (gwMatch && gwMatch.length > 0) { + setGatewayAddr(gwMatch[1]) + } - if (output.match(/(?:daemon is running|Daemon is ready)/)) { - // we're good - this._started = true - callback(null, this.api) + if (output.match(/(?:daemon is running|Daemon is ready)/)) { + // we're good + this._started = true + resolve(this.api) + } } + }) + + try { + await this.subprocess + .catch(translateError) + } catch (err) { + reject(err) } - }, callback) + }) } /** * Stop the daemon. * * @param {number} [timeout] - Use timeout to specify the grace period in ms before hard stopping the daemon. Otherwise, a grace period of 10500 ms will be used for disposable nodes and 10500 * 3 ms for non disposable nodes. - * @param {function(Error): void} callback - * @return {void} + * @return {Promise} */ - stop (timeout, callback) { - if (typeof timeout === 'function') { - callback = timeout - timeout = null - } - - callback = callback || function noop () {} - + async stop (timeout) { if (!this.subprocess) { - return callback() + return } - this.killProcess(timeout, callback) + await this.killProcess(timeout) } /** @@ -327,57 +311,54 @@ class Daemon { * Note: timeout is ignored for proc nodes * * @param {Number} [timeout] - Use timeout to specify the grace period in ms before hard stopping the daemon. Otherwise, a grace period of 10500 ms will be used for disposable nodes and 10500 * 3 ms for non disposable nodes. - * @param {function(Error): void} callback - Called when the process was killed. - * @returns {void} + * @returns {Promise} */ - killProcess (timeout, callback) { - if (typeof timeout === 'function') { - callback = timeout - timeout = null - } - + killProcess (timeout) { if (!timeout) { timeout = this.disposable ? GRACE_PERIOD : NON_DISPOSABLE_GRACE_PERIOD } - // need a local var for the closure, as we clear the var. - const subprocess = this.subprocess - const grace = setTimeout(() => { - log('kill timeout, using SIGKILL', subprocess.pid) - subprocess.kill('SIGKILL') - }, timeout) - - subprocess.once('exit', () => { - log('killed', subprocess.pid) - clearTimeout(grace) + return new Promise((resolve, reject) => { + // need a local var for the closure, as we clear the var. + const subprocess = this.subprocess this.subprocess = null - this._started = false - if (this.disposable) { - return this.cleanup(callback) + + const grace = setTimeout(() => { + log('kill timeout, using SIGKILL', subprocess.pid) + subprocess.kill('SIGKILL') + }, timeout) + + subprocess.once('exit', () => { + log('killed', subprocess.pid) + clearTimeout(grace) + this._started = false + + if (this.disposable) { + return this.cleanup().then(resolve, reject) + } + + resolve() + }) + + if (this.api) { + log('kill via api', subprocess.pid) + this.api.shutdown(() => null) + } else { + log('killing', subprocess.pid) + subprocess.kill('SIGTERM') } - setImmediate(callback) }) - - if (this.api) { - log('kill via api', subprocess.pid) - this.api.shutdown(() => null) - } else { - log('killing', subprocess.pid) - subprocess.kill('SIGTERM') - } - this.subprocess = null } /** * Get the pid of the `ipfs daemon` process. * - * @param {function(Error, number): void} callback - receives the pid - * @returns {void} + * @returns {number} */ - pid (callback) { - callback(this.subprocess && this.subprocess.pid) + pid () { + return this.subprocess && this.subprocess.pid } /** @@ -386,36 +367,21 @@ class Daemon { * If no `key` is passed, the whole config is returned as an object. * * @param {string} [key] - A specific config to retrieve. - * @param {function(Error, (Object|string)): void} callback - * @returns {void} + * @returns {Promise} */ - getConfig (key = 'show', callback) { - if (typeof key === 'function') { - callback = key - key = 'show' + async getConfig (key = 'show') { + const { + stdout + } = await run(this, ['config', key], { + env: this.env + }) + .catch(translateError) + + if (key === 'show') { + return JSON.parse(stdout) } - let config = '' - - series([ - (cb) => run( - this, - ['config', key], - { - env: this.env, - stdout: (data) => { - config += String(data) - } - }, - cb - ), - (cb) => { - if (key === 'show') { - return safeParse(config, cb) - } - cb(null, config.trim()) - } - ], (error, results) => callback(error, results && results[results.length - 1])) + return stdout.trim() } /** @@ -423,53 +389,45 @@ class Daemon { * * @param {string} key - The key of the config entry to change/set. * @param {string} value - The config value to change/set. - * @param {function(Error): void} callback - * @returns {void} + * @returns {Promise} */ - setConfig (key, value, callback) { - setConfigValue(this, key, value, callback) + setConfig (key, value) { + return setConfigValue(this, key, value) + .catch(translateError) } /** * Replace the current config with the provided one * * @param {object} config - * @param {function(Error): void} callback - * @returns {void} + * @returns {Promise} */ - replaceConfig (config, callback) { + async replaceConfig (config) { const tmpFile = path.join(os.tmpdir(), hat()) + // TODO: we're using tmp file here until // https://github.com/ipfs/js-ipfs/pull/785 // is ready - series([ - (cb) => fs.writeFile(tmpFile, safeStringify(config), cb), - (cb) => run( - this, - ['config', 'replace', `${tmpFile}`], - { env: this.env }, - cb - ) - ], (err) => { - if (err) { return callback(err) } - fs.unlink(tmpFile, callback) - }) + await fs.writeFile(tmpFile, safeStringify(config)) + await run(this, ['config', 'replace', `${tmpFile}`], { env: this.env }) + .catch(translateError) + await fs.unlink(tmpFile) } /** * Get the version of ipfs * - * @param {function(Error, string): void} callback - * @returns {void} + * @returns {Promise} */ - version (callback) { - let stdout = '' - run(this, ['version'], { - env: this.env, - stdout: (data) => { - stdout += String(data) - } - }, (error) => callback(error, stdout.trim())) + async version () { + const { + stdout + } = await run(this, ['version'], { + env: this.env + }) + .catch(translateError) + + return stdout.trim() } } diff --git a/src/ipfsd-in-proc.js b/src/ipfsd-in-proc.js index ed39d90a..824baf4c 100644 --- a/src/ipfsd-in-proc.js +++ b/src/ipfsd-in-proc.js @@ -3,7 +3,6 @@ const multiaddr = require('multiaddr') const defaultsDeep = require('lodash.defaultsdeep') const defaults = require('lodash.defaults') -const waterfall = require('async/waterfall') const debug = require('debug') const EventEmitter = require('events') const repoUtils = require('./utils/repo/nodejs') @@ -129,15 +128,9 @@ class InProc extends EventEmitter { * @param {number} [initOptions.bits=2048] - The bit size of the identiy key. * @param {string} [initOptions.directory=IPFS_PATH] - The location of the repo. * @param {string} [initOptions.pass] - The passphrase of the keychain. - * @param {function (Error, InProc)} callback - * @returns {undefined} + * @returns {Promise} */ - init (initOptions, callback) { - if (typeof initOptions === 'function') { - callback = initOptions - initOptions = {} - } - + async init (initOptions = {}) { const bits = initOptions.keysize ? initOptions.bits : this.bits // do not just set a default keysize, // in case we decide to change it at @@ -146,22 +139,18 @@ class InProc extends EventEmitter { initOptions.bits = bits log(`initializing with keysize: ${bits}`) } - this.exec.init(initOptions, (err) => { - if (err) { - return callback(err) - } - const self = this - waterfall([ - (cb) => this.getConfig(cb), - (conf, cb) => this.replaceConfig(defaults({}, this.opts.config, conf), cb) - ], (err) => { - if (err) { return callback(err) } - self.clean = false - self.initialized = true - return callback(null, this) - }) - }) + await this.exec.init(initOptions) + + const self = this + const conf = await this.getConfig() + + await this.replaceConfig(defaults({}, this.opts.config, conf)) + + self.clean = false + self.initialized = true + + return this } /** @@ -169,78 +158,55 @@ class InProc extends EventEmitter { * If the node was marked as `disposable` this will be called * automatically when the process is exited. * - * @param {function(Error)} callback - * @returns {undefined} + * @returns {Promise} */ - cleanup (callback) { + cleanup () { if (this.clean) { - return callback() + return } - repoUtils.removeRepo(this.path, callback) + return repoUtils.removeRepo(this.path) } /** * Start the daemon. * - * @param {Array} [flags=[]] - Flags to be passed to the `ipfs daemon` command. - * @param {function(Error, IpfsClient)} callback - * @returns {undefined} + * @returns {Promise} */ - start (flags, callback) { - if (typeof flags === 'function') { - callback = flags - flags = undefined // not used - } + async start () { + await this.exec.start() - this.exec.start((err) => { - if (err) { - return callback(err) - } + this._started = true + this.api = this.exec - this._started = true - this.api = this.exec - this.exec.config.get((err, conf) => { - if (err) { - return callback(err) - } + const conf = await this.exec.config.get() - this._apiAddr = conf.Addresses.API - this._gatewayAddr = conf.Addresses.Gateway + this._apiAddr = conf.Addresses.API + this._gatewayAddr = conf.Addresses.Gateway - this.api.apiHost = multiaddr(conf.Addresses.API).nodeAddress().host - this.api.apiPort = multiaddr(conf.Addresses.API).nodeAddress().port + this.api.apiHost = multiaddr(conf.Addresses.API).nodeAddress().host + this.api.apiPort = multiaddr(conf.Addresses.API).nodeAddress().port - callback(null, this.api) - }) - }) + return this.api } /** * Stop the daemon. * - * @param {function(Error)} [callback] - * @returns {undefined} + * @returns {Promise} */ - stop (callback) { - callback = callback || function noop () {} - + async stop () { if (!this.exec) { - return callback() + return } - this.exec.stop((err) => { - if (err) { - return callback(err) - } + await this.exec.stop() - this._started = false - if (this.disposable) { - return this.cleanup(callback) - } + this._started = false - return callback() - }) + if (this.disposable) { + return this.cleanup() + } } /** @@ -249,11 +215,10 @@ class InProc extends EventEmitter { * First `SIGTERM` is sent, after 10.5 seconds `SIGKILL` is sent * if the process hasn't exited yet. * - * @param {function()} callback - Called when the process was killed. - * @returns {undefined} + * @returns {Promise} */ - killProcess (callback) { - this.stop(callback) + killProcess () { + return this.stop() } /** @@ -271,16 +236,10 @@ class InProc extends EventEmitter { * If no `key` is passed, the whole config is returned as an object. * * @param {string} [key] - A specific config to retrieve. - * @param {function(Error, (Object|string))} callback - * @returns {undefined} + * @returns {Promise} */ - getConfig (key, callback) { - if (typeof key === 'function') { - callback = key - key = undefined - } - - this.exec.config.get(key, callback) + getConfig (key) { + return this.exec.config.get(key) } /** @@ -288,32 +247,29 @@ class InProc extends EventEmitter { * * @param {string} key * @param {string} value - * @param {function(Error)} callback - * @returns {undefined} + * @returns {Promise} */ - setConfig (key, value, callback) { - this.exec.config.set(key, value, callback) + setConfig (key, value) { + return this.exec.config.set(key, value) } /** * Replace the current config with the provided one * * @param {Object} config - * @param {function(Error)} callback - * @return {undefined} + * @return {Promise} */ - replaceConfig (config, callback) { - this.exec.config.replace(config, callback) + replaceConfig (config) { + return this.exec.config.replace(config) } /** * Get the version of ipfs * - * @param {function(Error, string)} callback - * @returns {undefined} + * @returns {Promise} */ - version (callback) { - this.exec.version(callback) + version () { + return this.exec.version() } } diff --git a/src/utils/configure-node.js b/src/utils/configure-node.js deleted file mode 100644 index 2ed7b216..00000000 --- a/src/utils/configure-node.js +++ /dev/null @@ -1,10 +0,0 @@ -'use strict' - -const eachOfSeries = require('async/eachOfSeries') -const setConfigValue = require('./set-config-value') - -module.exports = (node, conf, callback) => { - eachOfSeries(conf, (value, key, cb) => { - setConfigValue(node, key, JSON.stringify(value), cb) - }, callback) -} diff --git a/src/utils/exec.js b/src/utils/exec.js index 1febac04..46419542 100644 --- a/src/utils/exec.js +++ b/src/utils/exec.js @@ -6,12 +6,7 @@ const path = require('path') const execa = require('execa') const noop = () => {} -function exec (cmd, args, opts, callback) { - if (typeof opts === 'function') { - callback = opts - opts = {} - } - +function exec (cmd, args, opts) { opts = Object.assign({}, { stdout: noop, stderr: noop @@ -21,12 +16,6 @@ function exec (cmd, args, opts, callback) { const command = execa(cmd, args, { env: opts.env }) command.stderr.on('data', opts.stderr) command.stdout.on('data', opts.stdout) - command - .then(r => { - callback(null, r) - }, err => { - callback(err) - }) return command } diff --git a/src/utils/find-ipfs-executable.js b/src/utils/find-ipfs-executable.js index ac4e95df..cdee4667 100644 --- a/src/utils/find-ipfs-executable.js +++ b/src/utils/find-ipfs-executable.js @@ -27,6 +27,7 @@ module.exports = (type, rootPath) => { if (fs.existsSync(npm3Path)) { return npm3Path } + if (fs.existsSync(npm2Path)) { return npm2Path } diff --git a/src/utils/parse-config.js b/src/utils/parse-config.js index e7985910..ac0703e0 100644 --- a/src/utils/parse-config.js +++ b/src/utils/parse-config.js @@ -1,13 +1,10 @@ 'use strict' -const waterfall = require('async/waterfall') -const fs = require('fs') +const fs = require('fs-extra') const path = require('path') -const safeParse = require('safe-json-parse/callback') -module.exports = (configPath, callback) => { - waterfall([ - (cb) => fs.readFile(path.join(configPath, 'config'), cb), - (file, cb) => safeParse(file.toString(), cb) - ], callback) +module.exports = async (configPath) => { + const file = await fs.readFile(path.join(configPath, 'config')) + + return JSON.parse(file.toString()) } diff --git a/src/utils/repo/browser.js b/src/utils/repo/browser.js index 42503dce..6a448e89 100644 --- a/src/utils/repo/browser.js +++ b/src/utils/repo/browser.js @@ -2,26 +2,22 @@ const hat = require('hat') const Dexie = require('dexie').default -const setImmediate = require('async/setImmediate') function createTempRepoPath () { return '/ipfs-' + hat() } -function removeRepo (repoPath, callback) { +function removeRepo (repoPath) { Dexie.delete(repoPath) - setImmediate(callback) } -function repoExists (repoPath, cb) { +async function repoExists (repoPath) { const db = new Dexie(repoPath) - db.open(repoPath) - .then((store) => { - const table = store.table(repoPath) - return table - .count((cnt) => cb(null, cnt > 0)) - .catch(cb) - }).catch(cb) + const store = await db.open(repoPath) + const table = store.table(repoPath) + const count = await table.count() + + return count > 0 } module.exports = { diff --git a/src/utils/repo/nodejs.js b/src/utils/repo/nodejs.js index 03482140..082b86ec 100644 --- a/src/utils/repo/nodejs.js +++ b/src/utils/repo/nodejs.js @@ -3,29 +3,33 @@ const os = require('os') const path = require('path') const hat = require('hat') -const rimraf = require('rimraf') -const fs = require('fs') +const fs = require('fs-extra') -function removeRepo (dir, callback) { - fs.access(dir, (err) => { - if (err) { +function removeRepo (dir) { + try { + return fs.remove(dir) + } catch (err) { + if (err.code === 'ENOENT') { // Does not exist so all good - return callback() + return } - rimraf(dir, callback) - }) + throw err + } } function createTempRepoPath () { return path.join(os.tmpdir(), '/ipfs-test-' + hat()) } -function repoExists (repoPath, cb) { - fs.access(`${repoPath}/config`, (err) => { - if (err) { return cb(null, false) } - cb(null, true) - }) +async function repoExists (repoPath) { + try { + await fs.access(`${repoPath}/config`) + + return true + } catch (err) { + return false + } } module.exports = { diff --git a/src/utils/run.js b/src/utils/run.js index 9dd00894..ad499505 100644 --- a/src/utils/run.js +++ b/src/utils/run.js @@ -4,7 +4,7 @@ const os = require('os') const isWindows = os.platform() === 'win32' const exec = require('./exec') -module.exports = (node, args, opts, callback) => { +module.exports = (node, args, opts = {}) => { let executable = node.exec if (isWindows && executable.endsWith('.js')) { @@ -16,5 +16,5 @@ module.exports = (node, args, opts, callback) => { // Don't pass on arguments that were passed into the node executable opts.execArgv = [] - return exec(executable, args, opts, callback) + return exec(executable, args, opts) } diff --git a/src/utils/set-config-value.js b/src/utils/set-config-value.js index cd59aad1..3f7ed0c9 100644 --- a/src/utils/set-config-value.js +++ b/src/utils/set-config-value.js @@ -2,11 +2,10 @@ const run = require('./run') -module.exports = (node, key, value, callback) => { - run( +module.exports = (node, key, value) => { + return run( node, ['config', key, value, '--json'], - { env: node.env }, - callback + { env: node.env } ) } diff --git a/test/add-retrieve.spec.js b/test/add-retrieve.spec.js index 1b6d4db9..f7d99a68 100644 --- a/test/add-retrieve.spec.js +++ b/test/add-retrieve.spec.js @@ -1,8 +1,6 @@ /* eslint-env mocha */ -/* eslint max-nested-callbacks: ["error", 8] */ 'use strict' -const waterfall = require('async/waterfall') const chai = require('chai') const dirtyChai = require('dirty-chai') const expect = chai.expect @@ -20,46 +18,35 @@ describe('data can be put and fetched', () => { tests.forEach((dfOpts) => describe(`${dfOpts.type}`, () => { let ipfsd - before(function (done) { + before(async function () { this.timeout(30 * 1000) const f = IPFSFactory.create(dfOpts) - f.spawn({ initOptions: { bits: dfOpts.bits, profile: 'test' } }, (err, _ipfsd) => { - expect(err).to.not.exist() - expect(_ipfsd).to.exist() - expect(_ipfsd.api).to.exist() - expect(_ipfsd.api).to.have.property('id') + ipfsd = await f.spawn({ initOptions: { bits: dfOpts.bits, profile: 'test' } }) - ipfsd = _ipfsd - done() - }) + expect(ipfsd).to.exist() + expect(ipfsd.api).to.exist() + expect(ipfsd.api).to.have.property('id') }) - after(function (done) { + after(async function () { this.timeout(20 * 1000) - ipfsd.stop(done) + await ipfsd.stop() }) - it('put and fetch a block', function (done) { + it('put and fetch a block', async function () { this.timeout(20 * 1000) const data = Buffer.from('blorb') + const block = await ipfsd.api.block.put(data) + const cidStr = block.cid.toBaseEncodedString() - waterfall([ - (cb) => ipfsd.api.block.put(data, cb), - (block, cb) => { - const cidStr = block.cid.toBaseEncodedString() - expect(cidStr) - .to.eql('QmPv52ekjS75L4JmHpXVeuJ5uX2ecSfSZo88NSyxwA3rAQ') + expect(cidStr) + .to.eql('QmPv52ekjS75L4JmHpXVeuJ5uX2ecSfSZo88NSyxwA3rAQ') - ipfsd.api.block.get(cidStr, cb) - }, - (block, cb) => { - expect(block.data).to.eql(data) - cb() - } - ], done) + const fetched = await ipfsd.api.block.get(cidStr) + expect(fetched.data).to.eql(data) }) })) }) diff --git a/test/api.spec.js b/test/api.spec.js index 82446e20..091631b6 100644 --- a/test/api.spec.js +++ b/test/api.spec.js @@ -7,7 +7,6 @@ const dirtyChai = require('dirty-chai') const expect = chai.expect chai.use(dirtyChai) -const series = require('async/series') const multiaddr = require('multiaddr') const path = require('path') const DaemonFactory = require('../src') @@ -36,89 +35,67 @@ describe('ipfsd.api for Daemons', () => { df = DaemonFactory.create(dfOpts) }) - it('test the ipfsd.api', function (done) { + it('test the ipfsd.api', async function () { this.timeout(50 * 1000) // TODO skip in browser - can we avoid using file system operations here? if (!isNode) { return this.skip() } - let ipfsd - let api - - series([ - (cb) => { - df.spawn({ - start: false, - config: config, - initOptions: { bits: 1024, profile: 'test' } - }, (err, _ipfsd) => { - expect(err).to.not.exist() - ipfsd = _ipfsd - ipfsd.start((err, _api) => { - expect(err).to.not.exist() - api = _api - cb() - }) - }) - }, - (cb) => { - api.addFromFs(path.join(__dirname, 'fixtures/'), { - recursive: true - }, (err, res) => { - expect(err).to.not.exist() - expect(res.length).to.equal(4) - - const fixuresDir = res.find(file => file.path === 'fixtures') - expect(fixuresDir).to.have.property( - 'hash', - 'QmNiv9nS9xipNafXqApzGqwajU8EaXrS2vUPJftg5ZEDUb' - ) - - const testFile = res.find(file => file.path === 'fixtures/test.txt') - expect(testFile).to.have.property( - 'hash', - 'Qmf412jQZiuVUtdgnB36FXFX7xg5V6KEbSJ4dpQuhkLyfD' - ) - - cb() - }) - }, - (cb) => { - ipfsd.stop(cb) - } - ], done) + const ipfsd = await df.spawn({ + start: false, + config: config, + initOptions: { bits: 1024, profile: 'test' } + }) + const api = await ipfsd.start() + + const res = await api.addFromFs(path.join(__dirname, 'fixtures/'), { + recursive: true + }) + + expect(res.length).to.equal(4) + + const fixuresDir = res.find(file => file.path === 'fixtures') + expect(fixuresDir).to.have.property( + 'hash', + 'QmNiv9nS9xipNafXqApzGqwajU8EaXrS2vUPJftg5ZEDUb' + ) + + const testFile = res.find(file => file.path === 'fixtures/test.txt') + expect(testFile).to.have.property( + 'hash', + 'Qmf412jQZiuVUtdgnB36FXFX7xg5V6KEbSJ4dpQuhkLyfD' + ) + + await ipfsd.stop() }) - it('check if API and Gateway addrs are correct', function (done) { + it('check if API and Gateway addrs are correct', async function () { this.timeout(50 * 1000) - df.spawn({ + const ipfsd = await df.spawn({ config: config, initOptions: { bits: 1024, profile: 'test' } - }, (err, _ipfsd) => { - expect(err).to.not.exist() - const ipfsd = _ipfsd - - // Check for props in daemon - expect(ipfsd).to.have.property('apiAddr') - expect(ipfsd).to.have.property('gatewayAddr') - expect(ipfsd.apiAddr).to.not.be.null() - expect(multiaddr.isMultiaddr(ipfsd.apiAddr)).to.equal(true) - expect(ipfsd.gatewayAddr).to.not.be.null() - expect(multiaddr.isMultiaddr(ipfsd.gatewayAddr)).to.equal(true) - - // Check for props in ipfs-http-client instance - expect(ipfsd.api).to.have.property('apiHost') - expect(ipfsd.api).to.have.property('apiPort') - expect(ipfsd.api).to.have.property('gatewayHost') - expect(ipfsd.api).to.have.property('gatewayPort') - expect(ipfsd.api.apiHost).to.equal('127.0.0.1') - expect(ipfsd.api.apiPort).to.equal(API_PORT) - expect(ipfsd.api.gatewayHost).to.equal('127.0.0.1') - expect(ipfsd.api.gatewayPort).to.equal(GW_PORT) - - ipfsd.stop(done) }) + + // Check for props in daemon + expect(ipfsd).to.have.property('apiAddr') + expect(ipfsd).to.have.property('gatewayAddr') + expect(ipfsd.apiAddr).to.not.be.null() + expect(multiaddr.isMultiaddr(ipfsd.apiAddr)).to.equal(true) + expect(ipfsd.gatewayAddr).to.not.be.null() + expect(multiaddr.isMultiaddr(ipfsd.gatewayAddr)).to.equal(true) + + // Check for props in ipfs-http-client instance + expect(ipfsd.api).to.have.property('apiHost') + expect(ipfsd.api).to.have.property('apiPort') + expect(ipfsd.api).to.have.property('gatewayHost') + expect(ipfsd.api).to.have.property('gatewayPort') + expect(ipfsd.api.apiHost).to.equal('127.0.0.1') + expect(ipfsd.api.apiPort).to.equal(API_PORT) + expect(ipfsd.api.gatewayHost).to.equal('127.0.0.1') + expect(ipfsd.api.gatewayPort).to.equal(GW_PORT) + + await ipfsd.stop() }) })) }) diff --git a/test/custom-api.spec.js b/test/custom-api.spec.js index 43694433..37406b6e 100644 --- a/test/custom-api.spec.js +++ b/test/custom-api.spec.js @@ -13,7 +13,7 @@ const IpfsFactory = require('../src') describe('custom API', function () { this.timeout(30 * 1000) - it('should create a factory with a custom API', done => { + it('should create a factory with a custom API', async function () { const mockApi = {} const f = IpfsFactory.create({ @@ -22,12 +22,11 @@ describe('custom API', function () { IpfsClient: () => mockApi }) - f.spawn({ initOptions: { profile: 'test' } }, (err, ipfsd) => { - if (err) return done(err) - expect(ipfsd.api).to.equal(mockApi) - // Restore a real API so that the node can be stopped properly - ipfsd.api = IpfsClient(ipfsd.apiAddr) - ipfsd.stop(done) - }) + const ipfsd = await f.spawn({ initOptions: { profile: 'test' } }) + + expect(ipfsd.api).to.equal(mockApi) + // Restore a real API so that the node can be stopped properly + ipfsd.api = IpfsClient(ipfsd.apiAddr) + await ipfsd.stop() }) }) diff --git a/test/endpoint/client.js b/test/endpoint/client.js index 780839ef..192784bf 100644 --- a/test/endpoint/client.js +++ b/test/endpoint/client.js @@ -30,7 +30,7 @@ describe('client', () => { mock.clearRoutes() }) - it('should handle valid request', (done) => { + it('should handle valid request', async () => { mock.post('http://localhost:9999/spawn', (req) => { expect(req.body.options.opt1).to.equal('hello!') return { @@ -44,14 +44,11 @@ describe('client', () => { } }) - client.spawn({ opt1: 'hello!' }, (err, ipfsd) => { - expect(err).to.not.exist() - expect(ipfsd).to.exist() - expect(ipfsd.apiAddr.toString()).to.equal('/ip4/127.0.0.1/tcp/5001') - expect(ipfsd.gatewayAddr.toString()).to.equal('/ip4/127.0.0.1/tcp/8080') - node = ipfsd - done() - }) + const ipfsd = await client.spawn({ opt1: 'hello!' }) + expect(ipfsd).to.exist() + expect(ipfsd.apiAddr.toString()).to.equal('/ip4/127.0.0.1/tcp/5001') + expect(ipfsd.gatewayAddr.toString()).to.equal('/ip4/127.0.0.1/tcp/8080') + node = ipfsd }) }) @@ -60,9 +57,10 @@ describe('client', () => { mock.clearRoutes() }) - it('should handle valid request', (done) => { + it('should handle valid request', async () => { + const badReq = boom.badRequest() + mock.post('http://localhost:9999/spawn', () => { - const badReq = boom.badRequest() return { status: badReq.output.statusCode, body: { @@ -71,11 +69,12 @@ describe('client', () => { } }) - client.spawn((err, ipfsd) => { - expect(err).to.exist() - expect(ipfsd).to.not.exist() - done() - }) + try { + await client.spawn() + expect.fail('Should have thrown') + } catch (err) { + expect(err.response.body.message).to.equal(badReq.message) + } }) }) }) @@ -86,7 +85,7 @@ describe('client', () => { mock.clearRoutes() }) - it('should handle valid request', (done) => { + it('should handle valid request', async () => { mock.post('http://localhost:9999/init', (req) => { expect(req.query.id).to.exist() @@ -97,11 +96,8 @@ describe('client', () => { } }) - node.init({ bits: 512 }, (err) => { - expect(err).to.not.exist() - expect(node.initialized).to.be.ok() - done() - }) + await node.init({ bits: 512 }) + expect(node.initialized).to.be.ok() }) }) @@ -110,9 +106,10 @@ describe('client', () => { mock.clearRoutes() }) - it('should handle valid request', (done) => { + it('should handle valid request', async () => { + const badReq = boom.badRequest() + mock.post('http://localhost:9999/init', () => { - const badReq = boom.badRequest() return { status: badReq.output.statusCode, body: { @@ -121,10 +118,12 @@ describe('client', () => { } }) - node.init((err) => { - expect(err).to.exist() - done() - }) + try { + await node.init() + expect.fail('Should have thrown') + } catch (err) { + expect(err.response.body.message).to.equal(badReq.message) + } }) }) }) @@ -135,15 +134,12 @@ describe('client', () => { mock.clearRoutes() }) - it('should handle valid request', (done) => { + it('should handle valid request', async () => { mock.post('http://localhost:9999/cleanup', (req) => { expect(req.query.id).to.exist() }) - node.cleanup((err) => { - expect(err).to.not.exist() - done() - }) + await node.cleanup() }) }) @@ -152,9 +148,10 @@ describe('client', () => { mock.clearRoutes() }) - it('should handle invalid request', (done) => { + it('should handle invalid request', async () => { + const badReq = boom.badRequest() + mock.post('http://localhost:9999/cleanup', () => { - const badReq = boom.badRequest() return { status: badReq.output.statusCode, body: { @@ -163,10 +160,12 @@ describe('client', () => { } }) - node.init((err) => { - expect(err).to.exist() - done() - }) + try { + await node.cleanup() + expect.fail('Should have errored') + } catch (err) { + expect(err.response.body.message).to.equal(badReq.message) + } }) }) }) @@ -177,7 +176,7 @@ describe('client', () => { mock.clearRoutes() }) - it('should handle valid request', (done) => { + it('should handle valid request', async () => { mock.post('http://localhost:9999/start', (req) => { expect(req.query.id).to.exist() expect(req.body.flags).to.exist() @@ -193,11 +192,8 @@ describe('client', () => { } }) - node.start(['--enable-pubsub-experiment'], (err, api) => { - expect(err).to.not.exist() - expect(api).to.exist() - done() - }) + const api = await node.start(['--enable-pubsub-experiment']) + expect(api).to.exist() }) }) @@ -206,9 +202,10 @@ describe('client', () => { mock.clearRoutes() }) - it('should handle invalid request', (done) => { + it('should handle invalid request', async () => { + const badReq = boom.badRequest() + mock.post('http://localhost:9999/start', () => { - const badReq = boom.badRequest() return { status: badReq.output.statusCode, body: { @@ -217,10 +214,12 @@ describe('client', () => { } }) - node.start((err) => { - expect(err).to.exist() - done() - }) + try { + await node.start() + expect.fail('Should have errored') + } catch (err) { + expect(err.response.body.message).to.equal(badReq.message) + } }) }) }) @@ -231,15 +230,12 @@ describe('client', () => { mock.clearRoutes() }) - it('should handle valid request', (done) => { + it('should handle valid request', async () => { mock.post('http://localhost:9999/stop', (req) => { expect(req.query.id).to.exist() }) - node.stop((err) => { - expect(err).to.not.exist() - done() - }) + await node.stop() }) }) @@ -248,9 +244,10 @@ describe('client', () => { mock.clearRoutes() }) - it('should handle invalid request', (done) => { + it('should handle invalid request', async () => { + const badReq = boom.badRequest() + mock.post('http://localhost:9999/stop', () => { - const badReq = boom.badRequest() return { status: badReq.output.statusCode, body: { @@ -259,10 +256,12 @@ describe('client', () => { } }) - node.stop((err) => { - expect(err).to.exist() - done() - }) + try { + await node.stop() + expect.fail('Should have errored') + } catch (err) { + expect(err.response.body.message).to.equal(badReq.message) + } }) }) }) @@ -273,15 +272,12 @@ describe('client', () => { mock.clearRoutes() }) - it('should handle valid request', (done) => { + it('should handle valid request', async () => { mock.post('http://localhost:9999/stop', (req) => { expect(req.query.id).to.exist() }) - node.stop(1000, (err) => { - expect(err).to.not.exist() - done() - }) + await node.stop(1000) }) }) @@ -290,9 +286,10 @@ describe('client', () => { mock.clearRoutes() }) - it('should handle invalid request', (done) => { + it('should handle invalid request', async () => { + const badReq = boom.badRequest() + mock.post('http://localhost:9999/stop', () => { - const badReq = boom.badRequest() return { status: badReq.output.statusCode, body: { @@ -301,10 +298,12 @@ describe('client', () => { } }) - node.stop((err) => { - expect(err).to.exist() - done() - }) + try { + await node.stop(1000) + expect.fail('Should have errored') + } catch (err) { + expect(err.response.body.message).to.equal(badReq.message) + } }) }) }) @@ -315,15 +314,12 @@ describe('client', () => { mock.clearRoutes() }) - it('should handle valid request', (done) => { + it('should handle valid request', async () => { mock.post('http://localhost:9999/kill', (req) => { expect(req.query.id).to.exist() }) - node.killProcess((err) => { - expect(err).to.not.exist() - done() - }) + await node.killProcess() }) }) @@ -332,9 +328,10 @@ describe('client', () => { mock.clearRoutes() }) - it('should handle invalid request', (done) => { + it('should handle invalid request', async () => { + const badReq = boom.badRequest() + mock.post('http://localhost:9999/kill', () => { - const badReq = boom.badRequest() return { status: badReq.output.statusCode, body: { @@ -343,10 +340,12 @@ describe('client', () => { } }) - node.killProcess((err) => { - expect(err).to.exist() - done() - }) + try { + await node.killProcess() + expect.fail('Should have errored') + } catch (err) { + expect(err.response.body.message).to.equal(badReq.message) + } }) }) }) @@ -357,7 +356,7 @@ describe('client', () => { mock.clearRoutes() }) - it('should handle valid request', (done) => { + it('should handle valid request', async () => { mock.get('http://localhost:9999/pid', (req) => { expect(req.query.id).to.exist() return { @@ -367,11 +366,8 @@ describe('client', () => { } }) - node.pid((err, res) => { - expect(err).to.not.exist() - expect(res).to.equal(1) - done() - }) + const res = await node.pid() + expect(res).to.equal(1) }) }) @@ -380,9 +376,10 @@ describe('client', () => { mock.clearRoutes() }) - it('should handle invalid request', (done) => { + it('should handle invalid request', async () => { + const badReq = boom.badRequest() + mock.get('http://localhost:9999/pid', () => { - const badReq = boom.badRequest() return { status: badReq.output.statusCode, body: { @@ -391,10 +388,12 @@ describe('client', () => { } }) - node.pid((err) => { - expect(err).to.exist() - done() - }) + try { + await node.pid() + expect.fail('Should have errored') + } catch (err) { + expect(err.response.body.message).to.equal(badReq.message) + } }) }) }) @@ -405,7 +404,7 @@ describe('client', () => { mock.clearRoutes() }) - it('should handle valid request', (done) => { + it('should handle valid request', async () => { mock.get('http://localhost:9999/config', (req) => { expect(req.query.id).to.exist() expect(req.query.key).to.equal('foo') @@ -418,11 +417,8 @@ describe('client', () => { } }) - node.getConfig('foo', (err, res) => { - expect(err).to.not.exist() - expect(res.foo).to.equal('bar') - done() - }) + const res = await node.getConfig('foo') + expect(res.foo).to.equal('bar') }) }) @@ -431,9 +427,10 @@ describe('client', () => { mock.clearRoutes() }) - it('should handle invalid request', (done) => { + it('should handle invalid request', async () => { + const badReq = boom.badRequest() + mock.get('http://localhost:9999/config', () => { - const badReq = boom.badRequest() return { status: badReq.output.statusCode, body: { @@ -442,10 +439,12 @@ describe('client', () => { } }) - node.getConfig((err) => { - expect(err).to.exist() - done() - }) + try { + await node.getConfig('foo') + expect.fail('Should have errored') + } catch (err) { + expect(err.response.body.message).to.equal(badReq.message) + } }) }) }) @@ -456,17 +455,14 @@ describe('client', () => { mock.clearRoutes() }) - it('should handle valid request', (done) => { + it('should handle valid request', async () => { mock.put('http://localhost:9999/config', (req) => { expect(req.query.id).to.exist() expect(req.body.key).to.equal('foo') expect(req.body.value).to.equal('bar') }) - node.setConfig('foo', 'bar', (err) => { - expect(err).to.not.exist() - done() - }) + await node.setConfig('foo', 'bar') }) }) @@ -475,9 +471,10 @@ describe('client', () => { mock.clearRoutes() }) - it('should handle invalid request', (done) => { + it('should handle invalid request', async () => { + const badReq = boom.badRequest() + mock.put('http://localhost:9999/config', () => { - const badReq = boom.badRequest() return { status: badReq.output.statusCode, body: { @@ -486,10 +483,12 @@ describe('client', () => { } }) - node.setConfig('foo', 'bar', (err) => { - expect(err).to.exist() - done() - }) + try { + await node.setConfig('foo', 'bar') + expect.fail('Should have errored') + } catch (err) { + expect(err.response.body.message).to.equal(badReq.message) + } }) }) }) diff --git a/test/endpoint/routes.js b/test/endpoint/routes.js index a51c817c..c920f67a 100644 --- a/test/endpoint/routes.js +++ b/test/endpoint/routes.js @@ -9,22 +9,22 @@ chai.use(dirtyChai) const proxyquire = require('proxyquire') const multiaddr = require('multiaddr') -const Hapi = require('hapi') +const Hapi = require('@hapi/hapi') const routes = proxyquire( '../../src/endpoint/routes', { '../factory-daemon': class { - spawn (ops, cb) { + async spawn () { const node = {} node.apiAddr = multiaddr('/ip4/127.0.0.1/tcp/5001') node.gatewayAddr = multiaddr('/ip4/127.0.0.1/tcp/8080') node.started = false - node.init = (opts, cb) => cb(null, node) - node.cleanup = (cb) => cb() + node.init = () => Promise.resolve(node) + node.cleanup = () => Promise.resolve() - node.start = (_, cb) => { + node.start = () => { node.started = true const api = {} @@ -35,21 +35,24 @@ const routes = proxyquire( api.gatewayPort = node.gatewayAddr.nodeAddress().port node.api = api - cb(null, api) + + return Promise.resolve(api) } - node.stop = (timeout, cb) => node.killProcess(timeout, cb) + node.stop = (timeout) => node.killProcess(timeout) - node.killProcess = (timeout, cb) => { + node.killProcess = (timeout) => { node.started = false - cb() + return Promise.resolve() } - node.pid = (cb) => cb(null, 1) - node.getConfig = (key, cb) => cb(null, { foo: 'bar' }) - node.setConfig = (key, val, cb) => cb() + node.pid = () => Promise.resolve(1) + node.getConfig = (key) => Promise.resolve({ foo: 'bar' }) + node.setConfig = (key, val) => Promise.resolve() + + await node.start() - node.start(null, () => cb(null, node)) + return node } } } @@ -61,303 +64,280 @@ describe('routes', () => { before(() => { server = new Hapi.Server() - server.connection() routes(server) }) - after((done) => server.stop(done)) + after(async () => { + await server.stop() + }) describe('POST /spawn', () => { - it('should return 200', (done) => { - server.inject({ + it('should return 200', async () => { + const res = await server.inject({ method: 'POST', url: '/spawn', headers: { 'content-type': 'application/json' } - }, (res) => { - expect(res.statusCode).to.equal(200) - expect(res.result.id).to.exist() - expect(res.result.api.apiAddr).to.exist() - expect(res.result.api.gatewayAddr).to.exist() - - id = res.result.id - done() }) + expect(res.statusCode).to.equal(200) + expect(res.result.id).to.exist() + expect(res.result.api.apiAddr).to.exist() + expect(res.result.api.gatewayAddr).to.exist() + + id = res.result.id }) }) describe('GET /api-addr', () => { - it('should return 200', (done) => { - server.inject({ + it('should return 200', async () => { + const res = await server.inject({ method: 'GET', url: `/api-addr?id=${id}`, headers: { 'content-type': 'application/json' }, payload: { id } - }, (res) => { - expect(res.statusCode).to.equal(200) - expect(res.result.apiAddr).to.exist() - done() }) + + expect(res.statusCode).to.equal(200) + expect(res.result.apiAddr).to.exist() }) - it('should return 400', (done) => { - server.inject({ + it('should return 400', async () => { + const res = await server.inject({ method: 'GET', url: '/api-addr', headers: { 'content-type': 'application/json' } - }, (res) => { - expect(res.statusCode).to.equal(400) - done() }) + + expect(res.statusCode).to.equal(400) }) }) describe('GET /getaway-addr', () => { - it('should return 200', (done) => { - server.inject({ + it('should return 200', async () => { + const res = await server.inject({ method: 'GET', url: `/getaway-addr?id=${id}`, headers: { 'content-type': 'application/json' }, payload: { id } - }, (res) => { - expect(res.statusCode).to.equal(200) - expect(res.result.getawayAddr).to.exist() - done() }) + + expect(res.statusCode).to.equal(200) + expect(res.result.getawayAddr).to.exist() }) - it('should return 400', (done) => { - server.inject({ + it('should return 400', async () => { + const res = await server.inject({ method: 'GET', url: '/getaway-addr', headers: { 'content-type': 'application/json' } - }, (res) => { - expect(res.statusCode).to.equal(400) - done() }) + + expect(res.statusCode).to.equal(400) }) }) describe('POST /init', () => { - it('should return 200', (done) => { - server.inject({ + it('should return 200', async () => { + const res = await server.inject({ method: 'POST', url: `/init?id=${id}`, headers: { 'content-type': 'application/json' }, payload: { id } - }, (res) => { - expect(res.statusCode).to.equal(200) - done() }) + + expect(res.statusCode).to.equal(200) }) - it('should return 400', (done) => { - server.inject({ + it('should return 400', async () => { + const res = await server.inject({ method: 'POST', url: '/init', headers: { 'content-type': 'application/json' } - }, (res) => { - expect(res.statusCode).to.equal(400) - done() }) + + expect(res.statusCode).to.equal(400) }) }) describe('POST /cleanup', () => { - it('should return 200', (done) => { - server.inject({ + it('should return 200', async () => { + const res = await server.inject({ method: 'POST', url: `/cleanup?id=${id}`, headers: { 'content-type': 'application/json' }, payload: { id } - }, (res) => { - expect(res.statusCode).to.equal(200) - done() }) + + expect(res.statusCode).to.equal(200) }) - it('should return 400', (done) => { - server.inject({ + it('should return 400', async () => { + const res = await server.inject({ method: 'POST', url: '/cleanup', headers: { 'content-type': 'application/json' } - }, (res) => { - expect(res.statusCode).to.equal(400) - done() }) + + expect(res.statusCode).to.equal(400) }) }) describe('POST /start', () => { - it('should return 200', (done) => { - server.inject({ + it('should return 200', async () => { + const res = await server.inject({ method: 'POST', url: `/start?id=${id}`, headers: { 'content-type': 'application/json' }, payload: { id } - }, (res) => { - expect(res.statusCode).to.equal(200) - done() }) + + expect(res.statusCode).to.equal(200) }) - it('should return 400', (done) => { - server.inject({ + it('should return 400', async () => { + const res = await server.inject({ method: 'POST', url: '/start', headers: { 'content-type': 'application/json' } - }, (res) => { - expect(res.statusCode).to.equal(400) - done() }) + + expect(res.statusCode).to.equal(400) }) }) describe('POST /stop', () => { - it('should return 200 without timeout', (done) => { - server.inject({ + it('should return 200 without timeout', async () => { + const res = await server.inject({ method: 'POST', url: `/stop?id=${id}`, headers: { 'content-type': 'application/json' }, payload: { id } - }, (res) => { - expect(res.statusCode).to.equal(200) - done() }) + + expect(res.statusCode).to.equal(200) }) - it('should return 200 with timeout', (done) => { - server.inject({ + it('should return 200 with timeout', async () => { + const res = await server.inject({ method: 'POST', url: `/stop?id=${id}`, headers: { 'content-type': 'application/json' }, payload: { id, timeout: 1000 } - }, (res) => { - expect(res.statusCode).to.equal(200) - done() }) + + expect(res.statusCode).to.equal(200) }) - it('should return 400', (done) => { - server.inject({ + it('should return 400', async () => { + const res = await server.inject({ method: 'POST', url: '/stop', headers: { 'content-type': 'application/json' } - }, (res) => { - expect(res.statusCode).to.equal(400) - done() }) + + expect(res.statusCode).to.equal(400) }) }) describe('POST /kill', () => { - it('should return 200', (done) => { - server.inject({ + it('should return 200', async () => { + const res = await server.inject({ method: 'POST', url: `/kill?id=${id}`, headers: { 'content-type': 'application/json' }, payload: { id } - }, (res) => { - expect(res.statusCode).to.equal(200) - done() }) + + expect(res.statusCode).to.equal(200) }) - it('should return 200 with timeout', (done) => { - server.inject({ + it('should return 200 with timeout', async () => { + const res = await server.inject({ method: 'POST', url: `/kill?id=${id}`, headers: { 'content-type': 'application/json' }, payload: { id, timeout: 1000 } - }, (res) => { - expect(res.statusCode).to.equal(200) - done() }) + + expect(res.statusCode).to.equal(200) }) - it('should return 400', (done) => { - server.inject({ + it('should return 400', async () => { + const res = await server.inject({ method: 'POST', url: '/kill', headers: { 'content-type': 'application/json' } - }, (res) => { - expect(res.statusCode).to.equal(400) - done() }) + + expect(res.statusCode).to.equal(400) }) }) describe('GET /pid', () => { - it('should return 200', (done) => { - server.inject({ + it('should return 200', async () => { + const res = await server.inject({ method: 'GET', url: `/pid?id=${id}`, headers: { 'content-type': 'application/json' }, payload: { id } - }, (res) => { - expect(res.statusCode).to.equal(200) - done() }) + + expect(res.statusCode).to.equal(200) }) - it('should return 400', (done) => { - server.inject({ + it('should return 400', async () => { + const res = await server.inject({ method: 'GET', url: '/pid', headers: { 'content-type': 'application/json' } - }, (res) => { - expect(res.statusCode).to.equal(400) - done() }) + + expect(res.statusCode).to.equal(400) }) }) describe('GET /config', () => { - it('should return 200', (done) => { - server.inject({ + it('should return 200', async () => { + const res = await server.inject({ method: 'GET', url: `/config?id=${id}`, headers: { 'content-type': 'application/json' }, payload: { id } - }, (res) => { - expect(res.statusCode).to.equal(200) - done() }) + + expect(res.statusCode).to.equal(200) }) - it('should return 400', (done) => { - server.inject({ + it('should return 400', async () => { + const res = await server.inject({ method: 'GET', url: '/config', headers: { 'content-type': 'application/json' } - }, (res) => { - expect(res.statusCode).to.equal(400) - done() }) + + expect(res.statusCode).to.equal(400) }) }) describe('PUT /config', () => { - it('should return 200', (done) => { - server.inject({ + it('should return 200', async () => { + const res = await server.inject({ method: 'PUT', url: `/config?id=${id}`, headers: { 'content-type': 'application/json' }, payload: { key: 'foo', value: 'bar' } - }, (res) => { - expect(res.statusCode).to.equal(200) - done() }) + + expect(res.statusCode).to.equal(200) }) - it('should return 400', (done) => { - server.inject({ + it('should return 400', async () => { + const res = await server.inject({ method: 'PUT', url: '/config', headers: { 'content-type': 'application/json' } - }, (res) => { - expect(res.statusCode).to.equal(400) - done() }) + + expect(res.statusCode).to.equal(400) }) }) }) diff --git a/test/endpoint/server.js b/test/endpoint/server.js index e2e1fac4..7e2468ec 100644 --- a/test/endpoint/server.js +++ b/test/endpoint/server.js @@ -12,21 +12,18 @@ const portUsed = require('detect-port') describe('endpoint server', () => { let server - it('.start', function (done) { + it('.start', async function () { this.timeout(10 * 1000) server = new Server({ port: 12345 }) - server.start((err) => { - expect(err).to.not.exist() - portUsed(12345, (err, port) => { - expect(err).to.not.exist() - expect(port).to.not.equal(12345) - done() - }) - }) + await server.start() + + const port = await portUsed(12345) + + expect(port).to.not.equal(12345) }) - it('.stop', (done) => { - server.stop(done) + it('.stop', async () => { + await server.stop() }) }) diff --git a/test/exec.node.js b/test/exec.node.js index 3ef3e316..3946c6a3 100644 --- a/test/exec.node.js +++ b/test/exec.node.js @@ -10,6 +10,7 @@ const isrunning = require('is-running') const path = require('path') const exec = require('../src/utils/exec') const os = require('os') +const delay = require('delay') const isWindows = os.platform() === 'win32' @@ -19,30 +20,16 @@ function token () { return Math.random().toString().substr(2) } -function psExpect (pid, shouldBeRunning, grace, callback) { - setTimeout(() => { - const actual = isrunning(pid) +async function psExpect (pid, shouldBeRunning, grace) { + await delay(200) - if (actual !== shouldBeRunning && grace > 0) { - return psExpect(pid, shouldBeRunning, --grace, callback) - } - - callback(null, actual) - }, 200) -} - -function makeCheck (n, done) { - let i = 0 + const actual = isrunning(pid) - return (err) => { - if (err) { - return done(err) - } - - if (++i === n) { - done() - } + if (actual !== shouldBeRunning && grace > 0) { + return psExpect(pid, shouldBeRunning, --grace) } + + return actual } // TODO The test vector, `tail` is no longer a good test vector as it is not @@ -58,11 +45,11 @@ describe('exec', () => { return } - it('captures stderr and stdout', (done) => { + it('captures stderr and stdout', async () => { let stdout = '' let stderr = '' - exec(process.execPath, [ + await exec(process.execPath, [ path.resolve(path.join(__dirname, 'fixtures', 'talky.js')) ], { stdout: (data) => { @@ -71,84 +58,72 @@ describe('exec', () => { stderr: (data) => { stderr += String(data) } - }, (error) => { - expect(error).to.not.exist() - expect(stdout).to.equal('hello\n') - expect(stderr).to.equal('world\n') - - done() }) - }) - it('survives process errors and captures exit code and stderr', (done) => { - exec(process.execPath, [ - path.resolve(path.join(__dirname, 'fixtures', 'error.js')) - ], {}, (error) => { - expect(error.message).to.contain('Goodbye cruel world!') + expect(stdout).to.equal('hello\n') + expect(stderr).to.equal('world\n') + }) - done() - }) + it('survives process errors and captures exit code and stderr', async () => { + try { + await exec(process.execPath, [ + path.resolve(path.join(__dirname, 'fixtures', 'error.js')) + ]) + expect.fail('Should have errored') + } catch (err) { + expect(err.exitCode).to.equal(1) + expect(err.stderr).to.contain('Goodbye cruel world!') + } }) - it('SIGTERM kills hang', (done) => { + it('SIGTERM kills hang', async () => { const tok = token() - - const check = makeCheck(2, done) const hang = 'tail -f /dev/null'.split(' ') const args = hang.concat(tok) + const p = exec(args[0], args.slice(1)) - const p = exec(args[0], args.slice(1), {}, (err) => { - expect(err).to.exist() - expect(err.killed).to.be.ok() - expect(err.signal).to.equal('SIGTERM') - check() - }) + let running = await psExpect(p.pid, true, 10) + expect(running).to.be.ok() - psExpect(p.pid, true, 10, (err, running) => { - expect(err).to.not.exist() - expect(running).to.be.ok() + p.kill('SIGTERM') // should kill it - p.kill('SIGTERM') // should kill it - psExpect(p.pid, false, 10, (err, running) => { - expect(err).to.not.exist() - expect(running).to.not.be.ok() - check() - }) - }) - }) + running = await psExpect(p.pid, false, 10) + expect(running).to.not.be.ok() - it('SIGKILL kills survivor', (done) => { - const check = makeCheck(2, done) + try { + await p + expect.fail('Should have errored') + } catch (err) { + expect(err.killed).to.be.ok() + expect(err.signal).to.equal('SIGTERM') + } + }) + it('SIGKILL kills survivor', async () => { const tok = token() + const p = exec(survivor, [tok], {}) - const p = exec(survivor, [tok], {}, (err) => { - expect(err).to.exist() - expect(err.killed).to.be.ok() - expect(err.signal).to.equal('SIGKILL') - check() - }) + let running = await psExpect(p.pid, true, 10) + expect(running).to.be.ok() - psExpect(p.pid, true, 10, (err, running) => { - expect(err).to.not.exist() - expect(running).to.be.ok() + // should not kill it + p.kill('SIGTERM') - // should not kill it - p.kill('SIGTERM') + running = await psExpect(p.pid, true, 10) + expect(running).to.be.ok() - psExpect(p.pid, true, 10, (err, running) => { - expect(err).to.not.exist() - expect(running).to.be.ok() + // will kill it + p.kill('SIGKILL') - // will kill it - p.kill('SIGKILL') + running = await psExpect(p.pid, false, 50) + expect(running).to.not.be.ok() - psExpect(p.pid, false, 50, (err, running) => { - expect(err).to.not.exist() - expect(running).to.not.be.ok() - check() - }) - }) - }) + try { + await p + expect.fail('Should have errored') + } catch (err) { + expect(err.killed).to.be.ok() + expect(err.signal).to.equal('SIGKILL') + } }) }) diff --git a/test/npm-install.node.js b/test/npm-install.node.js index 88676798..adb9b123 100644 --- a/test/npm-install.node.js +++ b/test/npm-install.node.js @@ -7,9 +7,7 @@ const dirtyChai = require('dirty-chai') const expect = chai.expect chai.use(dirtyChai) -const fs = require('fs') -const rimraf = require('rimraf') -const mkdirp = require('mkdirp') +const fs = require('fs-extra') const path = require('path') const os = require('os') const isWindows = os.platform() === 'win32' @@ -42,48 +40,44 @@ types.forEach((type) => { process.env.testpath = oldPath }) - it('has the correct path when installed with npm3', (done) => { + it('has the correct path when installed with npm3', async () => { let execPath = type === 'js' ? 'ipfsd-ctl-test/node_modules/ipfs/src/cli' : 'ipfsd-ctl-test/node_modules/go-ipfs-dep/go-ipfs' let npm3Path = path.join(tmp, execPath) - mkdirp(npm3Path, (err) => { - expect(err).to.not.exist() + await fs.mkdirp(npm3Path) + fs.writeFileSync(path.join(npm3Path, appName)) - fs.writeFileSync(path.join(npm3Path, appName)) - delete require.cache[require.resolve('../src/ipfsd-daemon.js')] - const Daemon = require('../src/ipfsd-daemon.js') + delete require.cache[require.resolve('../src/ipfsd-daemon.js')] + const Daemon = require('../src/ipfsd-daemon.js') - const node = new Daemon({ type }) - expect(node.exec) - .to.eql(path.join(tmp, `${execPath}/${appName}`)) + const node = new Daemon({ type }) + expect(node.exec) + .to.eql(path.join(tmp, `${execPath}/${appName}`)) - rimraf(path.join(tmp, 'ipfsd-ctl-test'), done) - }) + await fs.remove(path.join(tmp, 'ipfsd-ctl-test')) }) - it('has the correct path when installed with npm2', (done) => { + it('has the correct path when installed with npm2', async () => { const execPath = type === 'js' ? 'ipfsd-ctl-test/node_modules/ipfsd-ctl/node_modules/ipfs/src/cli' : 'ipfsd-ctl-test/node_modules/ipfsd-ctl/node_modules/go-ipfs-dep/go-ipfs' const npm2Path = path.join(tmp, execPath) - mkdirp(npm2Path, (err) => { - expect(err).to.not.exist() + await fs.mkdirp(npm2Path) + fs.writeFileSync(path.join(npm2Path, appName)) - fs.writeFileSync(path.join(npm2Path, appName)) - delete require.cache[require.resolve('../src/ipfsd-daemon.js')] - const Daemon = require('../src/ipfsd-daemon.js') + delete require.cache[require.resolve('../src/ipfsd-daemon.js')] + const Daemon = require('../src/ipfsd-daemon.js') - const node = new Daemon({ type }) - expect(node.exec) - .to.eql(path.join(tmp, `${execPath}/${appName}`)) + const node = new Daemon({ type }) + expect(node.exec) + .to.eql(path.join(tmp, `${execPath}/${appName}`)) - rimraf(path.join(tmp, 'ipfsd-ctl-test'), done) - }) + await fs.remove(path.join(tmp, 'ipfsd-ctl-test')) }) }) }) diff --git a/test/spawn-options.spec.js b/test/spawn-options.spec.js index 0e94fa68..3199488f 100644 --- a/test/spawn-options.spec.js +++ b/test/spawn-options.spec.js @@ -2,20 +2,16 @@ /* eslint max-nested-callbacks: ["error", 8] */ 'use strict' -const series = require('async/series') -const waterfall = require('async/waterfall') const chai = require('chai') const dirtyChai = require('dirty-chai') const expect = chai.expect chai.use(dirtyChai) -const fs = require('fs') +const fs = require('fs-extra') const isNode = require('detect-node') const hat = require('hat') const IPFSFactory = require('../src') const JSIPFS = require('ipfs') -const once = require('once') - const tests = [ { type: 'go', bits: 1024 }, { type: 'js', bits: 512 }, @@ -40,15 +36,19 @@ describe('Spawn options', function () { f = IPFSFactory.create(fOpts) }) - it('f.version', function (done) { + it('f.version', async function () { this.timeout(20 * 1000) - f.version({ type: fOpts.type, exec: fOpts.exec }, (err, version) => { - expect(err).to.not.exist() - if (fOpts.type === 'proc') { version = version.version } - expect(version).to.be.eql(VERSION_STRING) - done() + let version = await f.version({ + type: fOpts.type, + exec: fOpts.exec }) + + if (fOpts.type === 'proc') { + version = version.version + } + + expect(version).to.be.eql(VERSION_STRING) }) describe('init and start', () => { @@ -58,16 +58,14 @@ describe('Spawn options', function () { let ipfsd let repoPath - before((done) => { - f.tmpDir(fOpts.type, (err, tmpDir) => { - expect(err).to.not.exist() - repoPath = tmpDir - prevRepoPath = repoPath - done() - }) + before(async () => { + const tmpDir = await f.tmpDir(fOpts.type) + + repoPath = tmpDir + prevRepoPath = repoPath }) - it('f.spawn', (done) => { + it('f.spawn', async () => { const options = { repoPath: repoPath, init: false, @@ -76,87 +74,65 @@ describe('Spawn options', function () { initOptions: { bits: fOpts.bits, profile: 'test' } } - f.spawn(options, (err, _ipfsd) => { - expect(err).to.not.exist() - expect(_ipfsd).to.exist() - expect(_ipfsd.api).to.not.exist() - expect(_ipfsd.initialized).to.eql(false) + ipfsd = await f.spawn(options) + expect(ipfsd).to.exist() + expect(ipfsd.api).to.not.exist() + expect(ipfsd.initialized).to.eql(false) - ipfsd = _ipfsd - repoPath = _ipfsd.repoPath - done() - }) + repoPath = ipfsd.repoPath }) - it('ipfsd.init', function (done) { + it('ipfsd.init', async function () { this.timeout(20 * 1000) - ipfsd.init((err) => { - expect(err).to.not.exist() - expect(ipfsd.initialized).to.be.ok() - done() - }) + await ipfsd.init() + expect(ipfsd.initialized).to.be.ok() }) - it('ipfsd.start', function (done) { + it('ipfsd.start', async function () { this.timeout(20 * 1000) - ipfsd.start((err, api) => { - expect(err).to.not.exist() - expect(api).to.exist() - expect(api.id).to.exist() - done() - }) + const api = await ipfsd.start() + expect(api).to.exist() + expect(api.id).to.exist() }) - it('ipfsd.stop', function (done) { + it('ipfsd.stop', async function () { this.timeout(20 * 1000) - ipfsd.stop(done) + await ipfsd.stop() }) }) describe('spawn from an initialized repo', () => { let ipfsd - it('f.spawn', function (done) { + it('f.spawn', async function () { this.timeout(20 * 1000) - const options = { + ipfsd = await f.spawn({ repoPath: prevRepoPath, init: false, start: false, disposable: false - } - - f.spawn(options, (err, _ipfsd) => { - expect(err).to.not.exist() - expect(_ipfsd).to.exist() - - ipfsd = _ipfsd - - expect(ipfsd.api).to.not.exist() - expect(ipfsd.initialized).to.eql(true) - - done() }) + expect(ipfsd).to.exist() + expect(ipfsd.api).to.not.exist() + expect(ipfsd.initialized).to.eql(true) }) - it('ipfsd.start', function (done) { + it('ipfsd.start', async function () { this.timeout(20 * 1000) - ipfsd.start((err, api) => { - expect(err).to.not.exist() - expect(api).to.exist() - expect(api.id).to.exist() - done() - }) + const api = await ipfsd.start() + expect(api).to.exist() + expect(api.id).to.exist() }) - it('ipfsd.stop', function (done) { + it('ipfsd.stop', async function () { this.timeout(20 * 1000) - ipfsd.stop(done) + await ipfsd.stop() }) }) }) @@ -164,25 +140,19 @@ describe('Spawn options', function () { describe('spawn a node and attach api', () => { let ipfsd - it('create init and start node', function (done) { + it('create init and start node', async function () { this.timeout(20 * 1000) - f.spawn({ initOptions: { bits: fOpts.bits, profile: 'test' } }, - (err, _ipfsd) => { - expect(err).to.not.exist() - expect(_ipfsd).to.exist() - expect(_ipfsd.api).to.exist() - expect(_ipfsd.api.id).to.exist() - - ipfsd = _ipfsd - done() - }) + ipfsd = await f.spawn({ initOptions: { bits: fOpts.bits, profile: 'test' } }) + expect(ipfsd).to.exist() + expect(ipfsd.api).to.exist() + expect(ipfsd.api.id).to.exist() }) - it('ipfsd.stop', function (done) { + it('ipfsd.stop', async function () { this.timeout(20 * 1000) - ipfsd.stop(done) + await ipfsd.stop() }) }) @@ -203,36 +173,43 @@ describe('Spawn options', function () { ] } - it('swarm contains default addrs', function (done) { + it('swarm contains default addrs', async function () { this.timeout(20 * 1000) if (!isNode && fOpts.type === 'proc') { this.skip() } - f.spawn({ + const ipfsd = await f.spawn({ defaultAddrs: true, initOptions: { bits: fOpts.bits, profile: 'test' } - }, (err, ipfsd) => { - expect(err).to.not.exist() - ipfsd.getConfig('Addresses.Swarm', (err, config) => { - expect(err).to.not.exist() - if (fOpts.type !== 'proc') { - config = JSON.parse(config) - } - - expect(config).to.deep.equal(addrs[fOpts.type]) - ipfsd.stop(done) - }) }) + + let config = await ipfsd.getConfig('Addresses.Swarm') + + if (fOpts.type !== 'proc') { + config = JSON.parse(config) + } + + expect(config).to.deep.equal(addrs[fOpts.type]) + + await ipfsd.stop() }) }) describe('custom config options', () => { - it('custom config', function (done) { + let ipfsd + + after(async () => { + if (ipfsd) { + await ipfsd.stop() + } + }) + + it('custom config', async function () { this.timeout(50 * 1000) const addr = '/ip4/127.0.0.1/tcp/5678' @@ -247,38 +224,33 @@ describe('Spawn options', function () { Bootstrap: ['/dns4/wss0.bootstrap.libp2p.io/tcp/443/wss/ipfs/QmZMxNdpMkewiVZLMRxaNxUeZpDUb34pWjZ1kZvsd16Zic'] } - const options = { config: config, initOptions: { bits: fOpts.bits, profile: 'test' } } - - waterfall([ - (cb) => f.spawn(options, cb), - (ipfsd, cb) => ipfsd.getConfig('Addresses.API', (err, config) => { - expect(err).to.not.exist() - expect(config).to.eql(addr) - cb(null, ipfsd) - }), - (ipfsd, cb) => ipfsd.getConfig('Addresses.Swarm', (err, config) => { - expect(err).to.not.exist() - - // TODO why would this not be always the same type? - if (typeof config === 'string') { - config = JSON.parse(config) - } - expect(config).to.eql([swarmAddr1]) - // expect(config).to.include(swarmAddr1) - cb(null, ipfsd) - }), - (ipfsd, cb) => ipfsd.getConfig('Bootstrap', (err, bootstrapConfig) => { - expect(err).to.not.exist() - if (typeof bootstrapConfig === 'string') { - bootstrapConfig = JSON.parse(bootstrapConfig) - } - expect(bootstrapConfig).to.deep.equal(config.Bootstrap) - cb(null, ipfsd) - }) - ], (err, ipfsd) => { - expect(err).to.not.exist() - ipfsd.stop(done) + ipfsd = await f.spawn({ + config: config, + initOptions: { + bits: fOpts.bits, + profile: 'test' + } }) + + const apiConfig = await ipfsd.getConfig('Addresses.API') + expect(apiConfig).to.eql(addr) + + let swarmConfig = await ipfsd.getConfig('Addresses.Swarm') + + // TODO why would this not be always the same type? + if (typeof swarmConfig === 'string') { + swarmConfig = JSON.parse(swarmConfig) + } + expect(swarmConfig).to.eql([swarmAddr1]) + expect(swarmConfig).to.include(swarmAddr1) + + let bootstrapConfig = await ipfsd.getConfig('Bootstrap') + + if (typeof bootstrapConfig === 'string') { + bootstrapConfig = JSON.parse(bootstrapConfig) + } + + expect(bootstrapConfig).to.deep.equal(config.Bootstrap) }) }) @@ -290,15 +262,11 @@ describe('Spawn options', function () { let ipfsd let repoPath - before((done) => { - f.tmpDir(fOpts.type, (err, tmpDir) => { - expect(err).to.not.exist() - repoPath = tmpDir - done() - }) + before(async () => { + repoPath = await f.tmpDir(fOpts.type) }) - it('allows passing custom repo path to spawn', function (done) { + it('allows passing custom repo path to spawn', async function () { this.timeout(20 * 1000) const config = { @@ -321,30 +289,19 @@ describe('Spawn options', function () { initOptions: { bits: fOpts.bits, profile: 'test' } } - series([ - (cb) => f.spawn(options, (err, _ipfsd) => { - expect(err).to.not.exist() - ipfsd = _ipfsd - cb() - }), - (cb) => { - ipfsd.init(cb) - }, - (cb) => { - ipfsd.start(cb) - } - ], (err) => { - expect(err).to.not.exist() - if (isNode) { - // We can only check if it really got created when run in Node.js - expect(fs.existsSync(repoPath)).to.be.ok() - } - done() - }) + ipfsd = await f.spawn(options) + await ipfsd.init() + await ipfsd.start() + + if (isNode) { + // We can only check if it really got created when run in Node.js + expect(fs.existsSync(repoPath)).to.be.ok() + } }) - after((done) => { - ipfsd.stop(() => ipfsd.cleanup(done)) + after(async () => { + await ipfsd.stop() + await ipfsd.cleanup() }) }) @@ -353,7 +310,7 @@ describe('Spawn options', function () { let ipfsd - it('spawn with pubsub', function (done) { + it('spawn with pubsub', async function () { this.timeout(20 * 1000) const options = { @@ -361,123 +318,86 @@ describe('Spawn options', function () { initOptions: { bits: fOpts.bits, profile: 'test' } } - f.spawn(options, (err, _ipfsd) => { - expect(err).to.not.exist() - ipfsd = _ipfsd - done() - }) + ipfsd = await f.spawn(options) }) - it('check that pubsub was enabled', (done) => { + it('check that pubsub was enabled', () => { const topic = `test-topic-${hat()}` const data = Buffer.from('hey there') - const handler = (msg) => { - expect(msg.data).to.eql(data) - expect(msg).to.have.property('seqno') - expect(Buffer.isBuffer(msg.seqno)).to.eql(true) - expect(msg).to.have.property('topicIDs').eql([topic]) - done() - } + return new Promise(async (resolve, reject) => { + const handler = (msg) => { + try { + expect(msg.data).to.eql(data) + expect(msg).to.have.property('seqno') + expect(Buffer.isBuffer(msg.seqno)).to.eql(true) + expect(msg).to.have.property('topicIDs').eql([topic]) + resolve() + } catch (err) { + reject(err) + } + } - ipfsd.api.pubsub.subscribe(topic, handler, (err) => { - expect(err).to.not.exist() + await ipfsd.api.pubsub.subscribe(topic, handler) ipfsd.api.pubsub.publish(topic, data) }) }) - it('ipfsd.stop', function (done) { + it('ipfsd.stop', async function () { this.timeout(20 * 1000) - ipfsd.stop(done) + await ipfsd.stop() }) }) describe('change config while running', () => { let ipfsd - before(function (done) { + before(async function () { this.timeout(20 * 1000) - f.spawn({ initOptions: { bits: fOpts.bits, profile: 'test' } }, - (err, _ipfsd) => { - expect(err).to.not.exist() - ipfsd = _ipfsd - done() - }) + ipfsd = await f.spawn({ + initOptions: { + bits: fOpts.bits, + profile: 'test' + } + }) }) - after(function (done) { + after(async function () { this.timeout(20 * 1000) - ipfsd.stop(done) + await ipfsd.stop() }) - it('ipfsd.getConfig', (done) => { - ipfsd.getConfig((err, config) => { - expect(err).to.not.exist() - expect(config).to.exist() - done() - }) + it('ipfsd.getConfig', async () => { + const config = await ipfsd.getConfig() + expect(config).to.exist() }) - it('ipfsd.getConfig of specific value', (done) => { - ipfsd.getConfig('Bootstrap', (err, config) => { - expect(err).to.not.exist() - expect(config).to.exist() - done() - }) + it('ipfsd.getConfig of specific value', async () => { + const config = await ipfsd.getConfig('Bootstrap') + expect(config).to.exist() }) - it('Should set a config value', function (done) { + it('Should set a config value', async function () { this.timeout(20 * 1000) - series([ - (cb) => ipfsd.setConfig('Bootstrap', 'null', cb), - (cb) => ipfsd.getConfig('Bootstrap', cb) - ], (err, res) => { - expect(err).to.not.exist() - expect(res[1]).to.eql('null') - done() - }) - }) + await ipfsd.setConfig('Bootstrap', 'null') + const res = await ipfsd.getConfig('Bootstrap') - it('error on invalid config', function (done) { - // TODO: fix this - js doesn't fail on invalid config - if (fOpts.type !== 'go') { return this.skip() } - - ipfsd.setConfig('Bootstrap', 'true', (err) => { - expect(err.message) - .to.match(/(?:Error: )?failed to set config value/mgi) - done() - }) + expect(res).to.eql('null') }) - }) - - describe(`don't callback twice on error`, () => { - if (fOpts.type !== 'proc') { return } - it('spawn with error', (done) => { - this.timeout(20 * 1000) - // `once.strict` should throw if its called more than once - const callback = once.strict((err, ipfsd) => { - if (err) { return done(err) } - ipfsd.once('error', () => {}) // avoid EventEmitter throws - - // Do an operation, just to make sure we're working - ipfsd.api.id((err) => { - if (err) { - return done(err) - } + it('error on invalid config', async function () { + // TODO: fix this - js doesn't fail on invalid config + if (fOpts.type !== 'go') { + return this.skip() + } - // Do something to make stopping fail - ipfsd.exec._repo.close((err) => { - if (err) { return done(err) } - ipfsd.stop((err) => { - expect(err).to.exist() - done() - }) - }) - }) - }) - f.spawn(callback) + try { + await ipfsd.setConfig('Bootstrap', 'true') + expect.fail('Should have errored') + } catch (err) { + expect(err.message).to.contain('failed to set config value') + } }) }) })) diff --git a/test/start-stop.node.js b/test/start-stop.node.js index 2a5e12e1..3713dcaf 100644 --- a/test/start-stop.node.js +++ b/test/start-stop.node.js @@ -7,11 +7,10 @@ const dirtyChai = require('dirty-chai') const expect = chai.expect chai.use(dirtyChai) -const async = require('async') const fs = require('fs') const path = require('path') const isrunning = require('is-running') - +const delay = require('delay') const findIpfsExecutable = require('../src/utils/find-ipfs-executable') const tempDir = require('../src/utils/tmp-dir') const IPFSFactory = require('../src') @@ -35,24 +34,20 @@ tests.forEach((fOpts) => { let pid let stopped = false - before(function (done) { + before(async function () { this.timeout(50 * 1000) const f = IPFSFactory.create(dfConfig) - f.spawn({ + ipfsd = await f.spawn({ init: true, start: false, disposable: true, initOptions: { bits: fOpts.bits, profile: 'test' } - }, (err, _ipfsd) => { - expect(err).to.not.exist() - expect(_ipfsd).to.exist() - - ipfsd = _ipfsd - repoPath = ipfsd.path - done() }) + expect(ipfsd).to.exist() + + repoPath = ipfsd.path }) it('should return a node', () => { @@ -63,89 +58,80 @@ tests.forEach((fOpts) => { expect(exec).to.include.string(ipfsd.exec) }) - it('daemon should not be running', (done) => { - ipfsd.pid((pid) => { - expect(pid).to.not.exist() - done() - }) + it('daemon should not be running', async () => { + const pid = await ipfsd.pid() + expect(pid).to.not.exist() }) - it('.start', function (done) { + it('.start', async function () { this.timeout(20 * 1000) - ipfsd.start((err, ipfs) => { - expect(err).to.not.exist() - - ipfsd.pid((_pid) => { - pid = _pid - api = ipfs + api = await ipfsd.start() + pid = await ipfsd.pid() - expect(isrunning(pid)).to.be.ok() - done() - }) - }) + expect(isrunning(pid)).to.be.ok() }) it('is running', () => { expect(api.id).to.exist() }) - it('.stop', function (done) { + it('.stop', async function () { this.timeout(20 * 1000) - ipfsd.stop((err) => { - expect(err).to.not.exist() - let tries = 5 - - const interval = setInterval(() => { - const running = isrunning(pid) - if (!running || tries-- <= 0) { - clearInterval(interval) - expect(running).to.not.be.ok() - stopped = true - done() - } - }, 200) - }) + await ipfsd.stop() + + for (let i = 0; i < 5; i++) { + const running = isrunning(pid) + + if (!running) { + stopped = true + + return + } + + delay(200) + } + + expect.fail('Did not stop') }) - it('is stopped', function (done) { + it('is stopped', async function () { // shutdown grace period is already 10500 this.timeout(20 * 1000) - ipfsd.pid((pid) => { - expect(pid).to.not.exist() - expect(stopped).to.equal(true) - expect(fs.existsSync(path.join(ipfsd.path, 'repo.lock'))).to.not.be.ok() - expect(fs.existsSync(path.join(ipfsd.path, 'api'))).to.not.be.ok() - done() - }) + const pid = await ipfsd.pid() + + expect(pid).to.not.exist() + expect(stopped).to.equal(true) + expect(fs.existsSync(path.join(ipfsd.path, 'repo.lock'))).to.not.be.ok() + expect(fs.existsSync(path.join(ipfsd.path, 'api'))).to.not.be.ok() }) it('repo should cleaned up', () => { expect(fs.existsSync(repoPath)).to.not.be.ok() }) - it('fail on start with non supported flags', function (done) { + it('fail on start with non supported flags', async function () { // TODO js-ipfs doesn't fail on unrecognized args. // Decided what should be the desired behaviour - if (fOpts.type === 'js') { return this.skip() } + if (fOpts.type === 'js') { + return this.skip() + } const df = IPFSFactory.create(dfConfig) - df.spawn({ + const ipfsd = await df.spawn({ start: false, initOptions: { bits: fOpts.bits, profile: 'test' } - }, (err, ipfsd) => { - expect(err).to.not.exist() - ipfsd.start(['--should-not-exist'], (err) => { - expect(err).to.exist() - expect(err.message) - .to.match(/unknown option "should-not-exist"/) - - done() - }) }) + + try { + await ipfsd.start(['--should-not-exist']) + expect.fail('Should have errored') + } catch (err) { + expect(err.message).to.contain('unknown option "should-not-exist"') + } }) }) @@ -156,24 +142,20 @@ tests.forEach((fOpts) => { let pid let stopped = false - before(function (done) { + before(async function () { this.timeout(50 * 1000) const f = IPFSFactory.create(dfConfig) - f.spawn({ + ipfsd = await f.spawn({ init: true, start: false, disposable: true, initOptions: { bits: fOpts.bits, profile: 'test' } - }, (err, _ipfsd) => { - expect(err).to.not.exist() - expect(_ipfsd).to.exist() - - ipfsd = _ipfsd - repoPath = ipfsd.path - done() }) + + expect(ipfsd).to.exist() + repoPath = ipfsd.path }) it('should return a node', () => { @@ -184,103 +166,89 @@ tests.forEach((fOpts) => { expect(exec).to.include.string(ipfsd.exec) }) - it('daemon should not be running', (done) => { - ipfsd.pid((pid) => { - expect(pid).to.not.exist() - done() - }) + it('daemon should not be running', async () => { + const pid = await ipfsd.pid() + expect(pid).to.not.exist() }) - it('.start', function (done) { + it('.start', async function () { this.timeout(20 * 1000) - ipfsd.start((err, ipfs) => { - expect(err).to.not.exist() - - ipfsd.pid((_pid) => { - pid = _pid - api = ipfs + api = await ipfsd.start() + pid = await ipfsd.pid() - expect(isrunning(pid)).to.be.ok() - done() - }) - }) + expect(isrunning(pid)).to.be.ok() }) it('is running', () => { expect(api.id).to.exist() }) - it('.stop with timeout', function (done) { + it('.stop with timeout', async function () { this.timeout(15000 + 10) // should not take longer than timeout - ipfsd.stop(15000, (err) => { - expect(err).to.not.exist() - stopped = !isrunning(pid) - expect(stopped).to.be.ok() - done() - }) + + await ipfsd.stop(15000) + + stopped = !isrunning(pid) + expect(stopped).to.be.ok() }) - it('is stopped', function (done) { + it('is stopped', async function () { // shutdown grace period is already 10500 this.timeout(20 * 1000) - ipfsd.pid((pid) => { - expect(pid).to.not.exist() - expect(stopped).to.equal(true) - expect(fs.existsSync(path.join(ipfsd.path, 'repo.lock'))).to.not.be.ok() - expect(fs.existsSync(path.join(ipfsd.path, 'api'))).to.not.be.ok() - done() - }) + const pid = await ipfsd.pid() + expect(pid).to.not.exist() + expect(stopped).to.equal(true) + expect(fs.existsSync(path.join(ipfsd.path, 'repo.lock'))).to.not.be.ok() + expect(fs.existsSync(path.join(ipfsd.path, 'api'))).to.not.be.ok() }) it('repo should cleaned up', () => { expect(fs.existsSync(repoPath)).to.not.be.ok() }) - it('fail on start with non supported flags', function (done) { + it('fail on start with non supported flags', async function () { // TODO js-ipfs doesn't fail on unrecognized args. // Decided what should be the desired behaviour - if (fOpts.type === 'js') { return this.skip() } + if (fOpts.type === 'js') { + return this.skip() + } const df = IPFSFactory.create(dfConfig) - df.spawn({ + const ipfsd = await df.spawn({ start: false, initOptions: { bits: fOpts.bits, profile: 'test' } - }, (err, ipfsd) => { - expect(err).to.not.exist() - ipfsd.start(['--should-not-exist'], (err) => { - expect(err).to.exist() - expect(err.message) - .to.match(/unknown option "should-not-exist"/) - - done() - }) }) + + try { + await ipfsd.start(['--should-not-exist']) + expect.fail('Should have errored') + } catch (err) { + expect(err.message).to.contain('unknown option "should-not-exist"') + } }) }) describe('start and stop with custom exec path', () => { let ipfsd - before(function (done) { + before(async function () { this.timeout(50 * 1000) const df = IPFSFactory.create(dfConfig) - df.spawn({ + ipfsd = await df.spawn({ exec, initOptions: { bits: fOpts.bits, profile: 'test' } - }, (err, daemon) => { - expect(err).to.not.exist() - expect(daemon).to.exist() - - ipfsd = daemon - done() }) + + expect(ipfsd).to.exist() }) - after((done) => ipfsd.stop(done)) + after(async () => { + await ipfsd.stop() + }) it('should return a node', () => { expect(ipfsd).to.exist() @@ -294,25 +262,23 @@ tests.forEach((fOpts) => { describe('start and stop with custom ENV exec path', () => { let ipfsd - before(function (done) { + before(async function () { this.timeout(50 * 1000) const df = IPFSFactory.create(dfConfig) process.env = Object.assign({}, process.env, fOpts.type === 'go' ? { IPFS_GO_EXEC: exec } : { IPFS_JS_EXEC: exec }) - df.spawn({ + ipfsd = await df.spawn({ initOptions: { bits: fOpts.bits, profile: 'test' } - }, (err, daemon) => { - expect(err).to.not.exist() - expect(daemon).to.exist() - - ipfsd = daemon - done() }) + + expect(ipfsd).to.exist() }) - after((done) => ipfsd.stop(done)) + after(async () => { + await ipfsd.stop() + }) it('should return a node', () => { expect(ipfsd).to.exist() @@ -327,47 +293,44 @@ tests.forEach((fOpts) => { let ipfsd let exec - before(function (done) { + before(async function () { this.timeout(50 * 1000) const df = IPFSFactory.create(dfConfig) exec = findIpfsExecutable(fOpts.type) - df.spawn({ + ipfsd = await df.spawn({ exec, initOptions: { bits: fOpts.bits, profile: 'test' } - }, (err, daemon) => { - expect(err).to.not.exist() - expect(daemon).to.exist() - - ipfsd = daemon - done() }) + + expect(ipfsd).to.exist() }) - after((done) => ipfsd.stop(done)) + after(async () => { + await ipfsd.stop() + }) it('should return a node', () => { expect(ipfsd).to.exist() }) - it('shoul attach to running node', function (done) { + it('should attach to running node', async function () { this.timeout(50 * 1000) const df = IPFSFactory.create(dfConfig) - df.spawn({ + const daemon = await df.spawn({ initOptions: { bits: fOpts.bits, profile: 'test' }, repoPath: ipfsd.repoPath, disposable: false - }, (err, daemon) => { - expect(err).to.not.exist() - daemon.start((err, api) => { - expect(err).to.not.exist() - expect(api).to.exist() - expect(ipfsd.apiAddr).to.be.eql(daemon.apiAddr) - daemon.stop(done) - }) }) + + const api = await daemon.start() + + expect(api).to.exist() + expect(ipfsd.apiAddr).to.be.eql(daemon.apiAddr) + + await daemon.stop() }) }) @@ -375,111 +338,101 @@ tests.forEach((fOpts) => { this.timeout(20 * 1000) let ipfsd - before((done) => { + before(async () => { const df = IPFSFactory.create(dfConfig) const exec = path.join('invalid', 'exec', 'ipfs') - df.spawn({ + ipfsd = await df.spawn({ init: false, start: false, exec: exec, initOptions: { bits: fOpts.bits, profile: 'test' } - }, (err, daemon) => { - expect(err).to.not.exist() - expect(daemon).to.exist() - - ipfsd = daemon - done() }) + + expect(ipfsd).to.exist() }) - after((done) => ipfsd.stop(done)) + after(async () => { + await ipfsd.stop() + }) - it('should fail on init', (done) => { - ipfsd.init((err, node) => { + it('should fail on init', async () => { + try { + await ipfsd.init() + expect.fail('Should have errored') + } catch (err) { expect(err).to.exist() - expect(node).to.not.exist() - done() - }) + } }) }) describe('start and stop multiple times', () => { let ipfsd - before(function (done) { + before(async function () { this.timeout(20 * 1000) const f = IPFSFactory.create(dfConfig) - async.series([ - (cb) => f.spawn({ - init: false, - start: false, - disposable: false, - repoPath: tempDir(fOpts.type), - initOptions: { bits: fOpts.bits, profile: 'test' }, - config: { - Addresses: { - Swarm: [`/ip4/127.0.0.1/tcp/0`], - API: `/ip4/127.0.0.1/tcp/0`, - Gateway: `/ip4/127.0.0.1/tcp/0` - } + ipfsd = await f.spawn({ + init: false, + start: false, + disposable: false, + repoPath: tempDir(fOpts.type), + initOptions: { + bits: fOpts.bits, + profile: 'test' + }, + config: { + Addresses: { + Swarm: [`/ip4/127.0.0.1/tcp/0`], + API: `/ip4/127.0.0.1/tcp/0`, + Gateway: `/ip4/127.0.0.1/tcp/0` } - }, (err, daemon) => { - expect(err).to.not.exist() - expect(daemon).to.exist() + } + }) - ipfsd = daemon - cb() - }), - (cb) => ipfsd.init(cb), - (cb) => ipfsd.start(cb) - ], done) + expect(ipfsd).to.exist() + + await ipfsd.init() + await ipfsd.start() }) it('should return a node', function () { expect(ipfsd).to.exist() }) - it('daemon should be running', function (done) { - ipfsd.pid((pid) => { - expect(pid).to.exist() - done() - }) + it('daemon should be running', async function () { + const pid = await ipfsd.pid() + + expect(pid).to.exist() }) - it('.stop', function (done) { + it('.stop', async function () { this.timeout(20 * 1000) - ipfsd.stop((err) => { - expect(err).to.not.exist() - ipfsd.pid((pid) => { - expect(pid).to.not.exist() - done() - }) - }) + await ipfsd.stop() + + const pid = await ipfsd.pid() + + expect(pid).to.not.exist() }) - it('.start', function (done) { + it('.start', async function () { this.timeout(20 * 1000) - ipfsd.start((err) => { - expect(err).to.not.exist() - ipfsd.pid((pid) => { - expect(pid).to.exist() - done() - }) - }) + await ipfsd.start() + + const pid = await ipfsd.pid() + + expect(pid).to.exist() }) - it('.stop and cleanup', function (done) { + it('.stop and cleanup', async function () { this.timeout(20 * 1000) - ipfsd.stop((err) => { - expect(err).to.not.exist() - ipfsd.cleanup(done) - }) + await ipfsd.stop() + await ipfsd.cleanup() }) }) })