From 6405eeaf6887a8e8bee600880349de9a9cf7459e Mon Sep 17 00:00:00 2001 From: Hugo Dias Date: Thu, 18 Apr 2019 16:53:49 +0100 Subject: [PATCH 01/15] feat: add support to ipns resolve /ipns/ fixes: #1918 --- package.json | 1 + src/core/components/name.js | 55 +++++++++++++++++++++++++------------ src/core/index.js | 12 +++++++- src/core/ipns/index.js | 5 ++-- test/core/name.js | 43 +++++++++++++++++++++++++++-- 5 files changed, 94 insertions(+), 22 deletions(-) diff --git a/package.json b/package.json index 60225e8303..5e70604cf9 100644 --- a/package.json +++ b/package.json @@ -111,6 +111,7 @@ "ipld-zcash": "~0.3.0", "ipns": "~0.5.2", "is-ipfs": "~0.6.1", + "is-domain-name": "^1.0.1", "is-pull-stream": "~0.0.0", "is-stream": "^2.0.0", "iso-url": "~0.4.6", diff --git a/src/core/components/name.js b/src/core/components/name.js index 239d8ecc32..db8b220e64 100644 --- a/src/core/components/name.js +++ b/src/core/components/name.js @@ -7,6 +7,9 @@ const parallel = require('async/parallel') const human = require('human-to-milliseconds') const crypto = require('libp2p-crypto') const errcode = require('err-code') +const mergeOptions = require('merge-options') +const mh = require('multihashes') +const isDomain = require('is-domain-name') const log = debug('ipfs:name') log.error = debug('ipfs:name:error') @@ -35,6 +38,16 @@ const keyLookup = (ipfsNode, kname, callback) => { }) } +/** + * @typedef { import("../index") } IPFS + */ + +/** + * IPNS - Inter-Planetary Naming System + * + * @param {IPFS} self + * @returns {Function} + */ module.exports = function name (self) { return { /** @@ -125,22 +138,15 @@ module.exports = function name (self) { options = {} } - options = options || {} - const nocache = options.nocache && options.nocache.toString() === 'true' - const recursive = options.recursive && options.recursive.toString() === 'true' + options = mergeOptions({ + nocache: false, + recursive: false + }, options) const offline = self._options.offline - if (!self.isOnline() && !offline) { - const errMsg = utils.OFFLINE_ERROR - - log.error(errMsg) - return callback(errcode(errMsg, 'OFFLINE_ERROR')) - } - // TODO: params related logic should be in the core implementation - - if (offline && nocache) { + if (offline && options.nocache) { const error = 'cannot specify both offline and nocache' log.error(error) @@ -156,12 +162,27 @@ module.exports = function name (self) { name = `/ipns/${name}` } - const resolveOptions = { - nocache, - recursive - } + const [ , hash ] = name.slice(1).split('/') + try { + mh.fromB58String(hash) + + // ipns resolve needs a online daemon + if (!self.isOnline() && !offline) { + const errMsg = utils.OFFLINE_ERROR - self._ipns.resolve(name, resolveOptions, callback) + log.error(errMsg) + return callback(errcode(errMsg, 'OFFLINE_ERROR')) + } + self._ipns.resolve(name, options, callback) + } catch (err) { + // lets check if we have a domain ex. /ipns/ipfs.io and resolve with dns + if (isDomain(hash)) { + return self.dns(hash, options, callback) + } + + log.error(err) + callback(errcode(new Error('Invalid IPNS name.'), 'ERR_IPNS_INVALID_NAME')) + } }), pubsub: namePubsub(self) } diff --git a/src/core/index.js b/src/core/index.js index d457cb6149..3245a6cb9a 100644 --- a/src/core/index.js +++ b/src/core/index.js @@ -26,7 +26,16 @@ const defaultRepo = require('./runtime/repo-nodejs') const preload = require('./preload') const mfsPreload = require('./mfs-preload') const ipldOptions = require('./runtime/ipld-nodejs') - +/** + * @typedef { import("./ipns/index") } IPNS + */ + +/** + * + * + * @class IPFS + * @extends {EventEmitter} + */ class IPFS extends EventEmitter { constructor (options) { super() @@ -76,6 +85,7 @@ class IPFS extends EventEmitter { this._ipld = new Ipld(ipldOptions(this._blockService, this._options.ipld, this.log)) this._preload = preload(this) this._mfsPreload = mfsPreload(this) + /** @type {IPNS} */ this._ipns = undefined // eslint-disable-next-line no-console this._print = this._options.silent ? this.log : console.log diff --git a/src/core/ipns/index.js b/src/core/ipns/index.js index a064ece4d5..6a9d710fd7 100644 --- a/src/core/ipns/index.js +++ b/src/core/ipns/index.js @@ -104,5 +104,6 @@ class IPNS { } } -exports = module.exports = IPNS -exports.path = path +IPNS.path = path + +module.exports = IPNS diff --git a/test/core/name.js b/test/core/name.js index 99b8257251..d9c1084ccf 100644 --- a/test/core/name.js +++ b/test/core/name.js @@ -123,7 +123,7 @@ describe('name', function () { }) }) - it('should not recursively resolve to an IPFS hash if the option recursive is not provided', function (done) { + it('should not recursively resolve to an IPFS hash', function (done) { this.timeout(90 * 1000) const keyName = hat() @@ -132,7 +132,7 @@ describe('name', function () { series([ (cb) => node.name.publish(ipfsRef, { resolve: false }, cb), (cb) => node.name.publish(`/ipns/${nodeId}`, { resolve: false, key: keyName }, cb), - (cb) => node.name.resolve(key.id, cb) + (cb) => node.name.resolve(key.id, { recursive: false }, cb) ], (err, res) => { expect(err).to.not.exist() expect(res[2]).to.exist() @@ -618,4 +618,43 @@ describe('name', function () { done() }) }) + + describe('working with dns', function () { + let node + let ipfsd + + before(function (done) { + df.spawn({ + exec: IPFS, + args: [`--pass ${hat()}`, '--offline'], + config: { Bootstrap: [] } + }, (err, _ipfsd) => { + expect(err).to.not.exist() + ipfsd = _ipfsd + node = _ipfsd.api + done() + }) + }) + + after((done) => ipfsd.stop(done)) + + it('should resolve ipfs.io', async () => { + const r = await node.name.resolve('ipfs.io', { recursive: false }) + return expect(r).to.eq('/ipns/website.ipfs.io') + }) + + it('should resolve /ipns/ipfs.io recursive', async () => { + const r = await node.name.resolve('ipfs.io', { recursive: true }) + + return expect(r.substr(0, 6)).to.eql('/ipfs/') + }) + + it('should fail to resolve /ipns/ipfs.a', async () => { + try { + await node.name.resolve('ipfs.a') + } catch (err) { + expect(err).to.exist() + } + }) + }) }) From 01dfa9664011a6bbd6f85fe7ece81b33852caa2b Mon Sep 17 00:00:00 2001 From: Hugo Dias Date: Mon, 22 Apr 2019 14:32:38 +0100 Subject: [PATCH 02/15] fix: moves tests to interface-core and removes path from output `ipns name resolve` dns tests moved to interface-core resolve call now return a string as per documention --- src/core/ipns/index.js | 8 ++---- src/http/api/resources/name.js | 2 +- test/core/name.js | 47 +++------------------------------- 3 files changed, 7 insertions(+), 50 deletions(-) diff --git a/src/core/ipns/index.js b/src/core/ipns/index.js index 6a9d710fd7..e693e21f0f 100644 --- a/src/core/ipns/index.js +++ b/src/core/ipns/index.js @@ -77,9 +77,7 @@ class IPNS { const result = this.cache.get(id) if (result) { - return callback(null, { - path: result - }) + return callback(null, result) } } @@ -91,9 +89,7 @@ class IPNS { log(`IPNS record from ${name} was resolved correctly`) - callback(null, { - path: result - }) + callback(null, result) }) } diff --git a/src/http/api/resources/name.js b/src/http/api/resources/name.js index 7c8206444c..f42c770508 100644 --- a/src/http/api/resources/name.js +++ b/src/http/api/resources/name.js @@ -17,7 +17,7 @@ exports.resolve = { const res = await ipfs.name.resolve(arg, request.query) return h.response({ - Path: res.path + Path: res }) } } diff --git a/test/core/name.js b/test/core/name.js index d9c1084ccf..bb0456ab70 100644 --- a/test/core/name.js +++ b/test/core/name.js @@ -34,7 +34,7 @@ const publishAndResolve = (publisher, resolver, ipfsRef, publishOpts, nodeId, re expect(err).to.not.exist() expect(res[0]).to.exist() expect(res[1]).to.exist() - expect(res[1].path).to.equal(ipfsRef) + expect(res[1]).to.equal(ipfsRef) callback() }) } @@ -117,7 +117,7 @@ describe('name', function () { ], (err, res) => { expect(err).to.not.exist() expect(res[2]).to.exist() - expect(res[2].path).to.equal(ipfsRef) + expect(res[2]).to.equal(ipfsRef) done() }) }) @@ -136,7 +136,7 @@ describe('name', function () { ], (err, res) => { expect(err).to.not.exist() expect(res[2]).to.exist() - expect(res[2].path).to.equal(`/ipns/${nodeId}`) + expect(res[2]).to.equal(`/ipns/${nodeId}`) done() }) }) @@ -277,7 +277,7 @@ describe('name', function () { ], (err, res) => { expect(err).to.not.exist() expect(res[2]).to.exist() - expect(res[2].path).to.equal(ipfsRef) + expect(res[2]).to.equal(ipfsRef) done() }) }) @@ -618,43 +618,4 @@ describe('name', function () { done() }) }) - - describe('working with dns', function () { - let node - let ipfsd - - before(function (done) { - df.spawn({ - exec: IPFS, - args: [`--pass ${hat()}`, '--offline'], - config: { Bootstrap: [] } - }, (err, _ipfsd) => { - expect(err).to.not.exist() - ipfsd = _ipfsd - node = _ipfsd.api - done() - }) - }) - - after((done) => ipfsd.stop(done)) - - it('should resolve ipfs.io', async () => { - const r = await node.name.resolve('ipfs.io', { recursive: false }) - return expect(r).to.eq('/ipns/website.ipfs.io') - }) - - it('should resolve /ipns/ipfs.io recursive', async () => { - const r = await node.name.resolve('ipfs.io', { recursive: true }) - - return expect(r.substr(0, 6)).to.eql('/ipfs/') - }) - - it('should fail to resolve /ipns/ipfs.a', async () => { - try { - await node.name.resolve('ipfs.a') - } catch (err) { - expect(err).to.exist() - } - }) - }) }) From 183ec97d1697693552b42adc82d19027a431967a Mon Sep 17 00:00:00 2001 From: Hugo Dias Date: Mon, 6 May 2019 15:21:28 +0100 Subject: [PATCH 03/15] fix: append remainder --- src/cli/commands/name/resolve.js | 6 +---- src/core/components/name.js | 39 +++++++++++++++++++++----------- 2 files changed, 27 insertions(+), 18 deletions(-) diff --git a/src/cli/commands/name/resolve.js b/src/cli/commands/name/resolve.js index e8871a1d17..8c83b25682 100644 --- a/src/cli/commands/name/resolve.js +++ b/src/cli/commands/name/resolve.js @@ -32,11 +32,7 @@ module.exports = { const ipfs = await argv.getIpfs() const result = await ipfs.name.resolve(argv.name, opts) - if (result && result.path) { - print(result.path) - } else { - print(result) - } + print(result) })()) } } diff --git a/src/core/components/name.js b/src/core/components/name.js index db8b220e64..2ce58335d7 100644 --- a/src/core/components/name.js +++ b/src/core/components/name.js @@ -38,6 +38,18 @@ const keyLookup = (ipfsNode, kname, callback) => { }) } +const appendRemainder = (cb, remainder) => { + return (err, result) => { + if (err) { + return cb(err) + } + if (remainder.length) { + return cb(null, result + '/' + remainder.join('/')) + } + return cb(null, result) + } +} + /** * @typedef { import("../index") } IPFS */ @@ -46,7 +58,7 @@ const keyLookup = (ipfsNode, kname, callback) => { * IPNS - Inter-Planetary Naming System * * @param {IPFS} self - * @returns {Function} + * @returns {Object} */ module.exports = function name (self) { return { @@ -162,27 +174,28 @@ module.exports = function name (self) { name = `/ipns/${name}` } - const [ , hash ] = name.slice(1).split('/') + const [ namespace, hash, ...remainder ] = name.slice(1).split('/') try { mh.fromB58String(hash) - - // ipns resolve needs a online daemon - if (!self.isOnline() && !offline) { - const errMsg = utils.OFFLINE_ERROR - - log.error(errMsg) - return callback(errcode(errMsg, 'OFFLINE_ERROR')) - } - self._ipns.resolve(name, options, callback) } catch (err) { // lets check if we have a domain ex. /ipns/ipfs.io and resolve with dns if (isDomain(hash)) { - return self.dns(hash, options, callback) + return self.dns(hash, options, appendRemainder(callback, remainder)) } log.error(err) - callback(errcode(new Error('Invalid IPNS name.'), 'ERR_IPNS_INVALID_NAME')) + return callback(errcode(new Error('Invalid IPNS name.'), 'ERR_IPNS_INVALID_NAME')) + } + + // multihash is valid lets resolve with IPNS + // IPNS resolve needs a online daemon + if (!self.isOnline() && !offline) { + const errMsg = utils.OFFLINE_ERROR + + log.error(errMsg) + return callback(errcode(errMsg, 'OFFLINE_ERROR')) } + self._ipns.resolve(`/${namespace}/${hash}`, options, appendRemainder(callback, remainder)) }), pubsub: namePubsub(self) } From ae83d39d3639ad121c1690306ea28abde183d831 Mon Sep 17 00:00:00 2001 From: Hugo Dias Date: Thu, 9 May 2019 10:16:42 +0100 Subject: [PATCH 04/15] fix: remove old .path --- test/core/name-pubsub.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/test/core/name-pubsub.js b/test/core/name-pubsub.js index 575fb54fad..3edb00d460 100644 --- a/test/core/name-pubsub.js +++ b/test/core/name-pubsub.js @@ -130,8 +130,7 @@ describe('name-pubsub', function () { expect(err).to.not.exist() expect(res).to.exist() - expect(res[5]).to.exist() - expect(res[5].path).to.equal(ipfsRef) + expect(res[5]).to.equal(ipfsRef) done() }) }) From 5e43cdc2ef1be952ba4b30569478567b0f80cb73 Mon Sep 17 00:00:00 2001 From: Hugo Dias Date: Thu, 9 May 2019 10:20:37 +0100 Subject: [PATCH 05/15] fix: refactor tests enables ipns tests in the browser normalizes interface tests over core with interface tests over http-client using http api --- package.json | 2 +- test/core/interface.spec.js | 34 +------- test/core/{name.js => name.spec.js} | 122 +--------------------------- test/core/node.js | 1 - test/http-api/interface.js | 24 ++++++ test/utils/dns-fetch-stub.js | 16 ---- 6 files changed, 28 insertions(+), 171 deletions(-) rename test/core/{name.js => name.spec.js} (80%) delete mode 100644 test/utils/dns-fetch-stub.js diff --git a/package.json b/package.json index 5e70604cf9..5f62b58463 100644 --- a/package.json +++ b/package.json @@ -92,7 +92,7 @@ "ipfs-bitswap": "~0.24.1", "ipfs-block": "~0.8.1", "ipfs-block-service": "~0.15.1", - "ipfs-http-client": "^32.0.0", + "ipfs-http-client": "ipfs/js-ipfs-http-client#feat/name-resolve-dns", "ipfs-http-response": "~0.3.0", "ipfs-mfs": "~0.11.4", "ipfs-multipart": "~0.1.0", diff --git a/test/core/interface.spec.js b/test/core/interface.spec.js index df572c19e2..a091dd0ea4 100644 --- a/test/core/interface.spec.js +++ b/test/core/interface.spec.js @@ -4,26 +4,10 @@ const tests = require('interface-ipfs-core') const CommonFactory = require('../utils/interface-common-factory') const isNode = require('detect-node') -const dnsFetchStub = require('../utils/dns-fetch-stub') describe('interface-ipfs-core tests', function () { this.timeout(20 * 1000) - // ipfs.dns in the browser calls out to https://ipfs.io/api/v0/dns. - // The following code stubs self.fetch to return a static CID for calls - // to https://ipfs.io/api/v0/dns?arg=ipfs.io. - if (!isNode) { - const fetch = self.fetch - - before(() => { - self.fetch = dnsFetchStub(fetch) - }) - - after(() => { - self.fetch = fetch - }) - } - const defaultCommonFactory = CommonFactory.create() tests.bitswap(defaultCommonFactory, { skip: !isNode }) @@ -107,29 +91,13 @@ describe('interface-ipfs-core tests', function () { { name: 'should resolve IPNS link recursively', reason: 'TODO: IPNS resolve not yet implemented https://github.com/ipfs/js-ipfs/issues/1918' - }, - { - name: 'should recursively resolve ipfs.io', - reason: 'TODO: ipfs.io dnslink=/ipns/website.ipfs.io & IPNS resolve not yet implemented https://github.com/ipfs/js-ipfs/issues/1918' } ] }) tests.name(CommonFactory.create({ spawnOptions: { - args: ['--pass ipfs-is-awesome-software', '--offline'], - initOptions: { bits: 512 }, - config: { - Bootstrap: [], - Discovery: { - MDNS: { - Enabled: false - }, - webRTCStar: { - Enabled: false - } - } - } + args: ['--pass ipfs-is-awesome-software', '--offline'] } })) diff --git a/test/core/name.js b/test/core/name.spec.js similarity index 80% rename from test/core/name.js rename to test/core/name.spec.js index bb0456ab70..1191eba711 100644 --- a/test/core/name.js +++ b/test/core/name.spec.js @@ -9,11 +9,9 @@ const expect = chai.expect chai.use(dirtyChai) const sinon = require('sinon') -const fs = require('fs') const parallel = require('async/parallel') const series = require('async/series') -const isNode = require('detect-node') const IPFS = require('../../src') const ipnsPath = require('../../src/core/ipns/path') const ipnsRouting = require('../../src/core/ipns/routing/config') @@ -40,114 +38,7 @@ const publishAndResolve = (publisher, resolver, ipfsRef, publishOpts, nodeId, re } describe('name', function () { - if (!isNode) { - return - } - - describe('working locally', function () { - let node - let nodeId - let ipfsd - - before(function (done) { - this.timeout(50 * 1000) - df.spawn({ - exec: IPFS, - args: [`--pass ${hat()}`, '--offline'], - config: { Bootstrap: [] } - }, (err, _ipfsd) => { - expect(err).to.not.exist() - ipfsd = _ipfsd - node = _ipfsd.api - - node.id().then((res) => { - expect(res.id).to.exist() - - nodeId = res.id - done() - }) - }) - }) - - after((done) => ipfsd.stop(done)) - - it('should publish and then resolve correctly with the default options', function (done) { - this.timeout(50 * 1000) - - publishAndResolve(node, node, ipfsRef, { resolve: false }, nodeId, {}, done) - }) - - it('should publish correctly with the lifetime option and resolve', function (done) { - this.timeout(50 * 1000) - - const publishOpts = { - resolve: false, - lifetime: '2h' - } - - publishAndResolve(node, node, ipfsRef, publishOpts, nodeId, {}, done) - }) - - it('should not get the entry correctly if its validity time expired', function (done) { - this.timeout(50 * 1000) - - node.name.publish(ipfsRef, { resolve: false, lifetime: '1ms' }, (err, res) => { - expect(err).to.not.exist() - expect(res).to.exist() - - setTimeout(function () { - node.name.resolve(nodeId, (err) => { - expect(err).to.exist() - done() - }) - }, 2) - }) - }) - - it('should recursively resolve to an IPFS hash', function (done) { - this.timeout(90 * 1000) - const keyName = hat() - - node.key.gen(keyName, { type: 'rsa', size: 2048 }, function (err, key) { - expect(err).to.not.exist() - series([ - (cb) => node.name.publish(ipfsRef, { resolve: false }, cb), - (cb) => node.name.publish(`/ipns/${nodeId}`, { resolve: false, key: keyName }, cb), - (cb) => node.name.resolve(key.id, { recursive: true }, cb) - ], (err, res) => { - expect(err).to.not.exist() - expect(res[2]).to.exist() - expect(res[2]).to.equal(ipfsRef) - done() - }) - }) - }) - - it('should not recursively resolve to an IPFS hash', function (done) { - this.timeout(90 * 1000) - const keyName = hat() - - node.key.gen(keyName, { type: 'rsa', size: 2048 }, function (err, key) { - expect(err).to.not.exist() - series([ - (cb) => node.name.publish(ipfsRef, { resolve: false }, cb), - (cb) => node.name.publish(`/ipns/${nodeId}`, { resolve: false, key: keyName }, cb), - (cb) => node.name.resolve(key.id, { recursive: false }, cb) - ], (err, res) => { - expect(err).to.not.exist() - expect(res[2]).to.exist() - expect(res[2]).to.equal(`/ipns/${nodeId}`) - done() - }) - }) - }) - }) - describe('republisher', function () { - if (!isNode) { - return - } - let node let ipfsd @@ -285,10 +176,6 @@ describe('name', function () { }) describe('errors', function () { - if (!isNode) { - return - } - let node let nodeId let ipfsd @@ -460,20 +347,15 @@ describe('name', function () { }) describe('ipns.path', function () { - const path = 'test/fixtures/planets/solar-system.md' const fixture = { - path, - content: fs.readFileSync(path) + path: 'test/fixtures/planets/solar-system.md', + content: Buffer.from('ipns.path') } let node let ipfsd let nodeId - if (!isNode) { - return - } - before(function (done) { this.timeout(40 * 1000) df.spawn({ diff --git a/test/core/node.js b/test/core/node.js index b9297cce42..35f185f1d3 100644 --- a/test/core/node.js +++ b/test/core/node.js @@ -2,7 +2,6 @@ require('./circuit-relay') require('./files-regular-utils') -require('./name') require('./name-pubsub') require('./key-exchange') require('./pin') diff --git a/test/http-api/interface.js b/test/http-api/interface.js index 43c6ff6a12..1e78901fcc 100644 --- a/test/http-api/interface.js +++ b/test/http-api/interface.js @@ -96,6 +96,30 @@ describe('interface-ipfs-core over ipfs-http-client tests', () => { ] }) + tests.name(CommonFactory.create({ + spawnOptions: { + args: ['--pass ipfs-is-awesome-software', '--offline'] + } + }), { only: true }) + + tests.namePubsub(CommonFactory.create({ + spawnOptions: { + args: ['--enable-namesys-pubsub'], + initOptions: { bits: 1024 }, + config: { + Bootstrap: [], + Discovery: { + MDNS: { + Enabled: false + }, + webRTCStar: { + Enabled: false + } + } + } + } + })) + tests.object(defaultCommonFactory) tests.pin(defaultCommonFactory) diff --git a/test/utils/dns-fetch-stub.js b/test/utils/dns-fetch-stub.js deleted file mode 100644 index a1e24a122c..0000000000 --- a/test/utils/dns-fetch-stub.js +++ /dev/null @@ -1,16 +0,0 @@ -'use strict' - -// Create a fetch stub with a fall through to the provided fetch implementation -// if the URL doesn't match https://ipfs.io/api/v0/dns?arg=ipfs.io. -module.exports = (fetch) => { - return function () { - if (arguments[0].startsWith('https://ipfs.io/api/v0/dns?arg=ipfs.io')) { - return Promise.resolve({ - json: () => Promise.resolve({ - Path: '/ipfs/QmYNQJoKGNHTpPxCBPh9KkDpaExgd2duMa3aF6ytMpHdao' - }) - }) - } - return fetch.apply(this, arguments) - } -} From 2224e5818e5d8f2e61cf98fe03b8989617c9ae14 Mon Sep 17 00:00:00 2001 From: Hugo Dias Date: Thu, 9 May 2019 11:09:14 +0100 Subject: [PATCH 06/15] chore: remove only in the test --- test/http-api/interface.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/http-api/interface.js b/test/http-api/interface.js index 1e78901fcc..d1607dfb71 100644 --- a/test/http-api/interface.js +++ b/test/http-api/interface.js @@ -100,7 +100,7 @@ describe('interface-ipfs-core over ipfs-http-client tests', () => { spawnOptions: { args: ['--pass ipfs-is-awesome-software', '--offline'] } - }), { only: true }) + })) tests.namePubsub(CommonFactory.create({ spawnOptions: { From f6f375515b91ddb643d8c7d9769295eba28e4d9e Mon Sep 17 00:00:00 2001 From: Hugo Dias Date: Thu, 16 May 2019 16:28:33 +0200 Subject: [PATCH 07/15] fix: make recursive true defaults, tests and more --- package.json | 1 + src/cli/commands/name/publish.js | 22 +- src/cli/commands/name/resolve.js | 2 +- src/core/components/name.js | 2 +- src/core/ipns/index.js | 21 +- src/core/ipns/publisher.js | 5 +- src/http/api/resources/name.js | 2 +- src/utils/tlru.js | 83 ++++++++ test/cli/name-pubsub.js | 50 ----- test/cli/name.js | 336 +++++-------------------------- test/core/name-pubsub.js | 30 +++ 11 files changed, 189 insertions(+), 365 deletions(-) create mode 100644 src/utils/tlru.js diff --git a/package.json b/package.json index 5f62b58463..5fdf0cfef5 100644 --- a/package.json +++ b/package.json @@ -87,6 +87,7 @@ "get-folder-size": "^2.0.0", "glob": "^7.1.3", "hapi-pino": "^6.0.0", + "hashlru": "^2.3.0", "human-to-milliseconds": "^1.0.0", "interface-datastore": "~0.6.0", "ipfs-bitswap": "~0.24.1", diff --git a/src/cli/commands/name/publish.js b/src/cli/commands/name/publish.js index 8452f23248..b84227a5d6 100644 --- a/src/cli/commands/name/publish.js +++ b/src/cli/commands/name/publish.js @@ -1,6 +1,6 @@ 'use strict' -const print = require('../../utils').print +const { print } = require('../../utils') module.exports = { command: 'publish ', @@ -11,21 +11,25 @@ module.exports = { resolve: { alias: 'r', describe: 'Resolve given path before publishing. Default: true.', - default: true + default: true, + type: 'boolean' }, lifetime: { alias: 't', describe: 'Time duration that the record will be valid for. Default: 24h.', - default: '24h' + default: '24h', + type: 'string' }, key: { alias: 'k', describe: 'Name of the key to be used, as listed by "ipfs key list -l". Default: self.', - default: 'self' + default: 'self', + type: 'string' }, ttl: { describe: 'Time duration this record should be cached for (caution: experimental).', - default: '' + default: '', + type: 'string' } }, @@ -33,14 +37,8 @@ module.exports = { argv.resolve((async () => { // yargs-promise adds resolve/reject properties to argv // resolve should use the alias as resolve will always be overwritten to a function - let resolve = true - - if (argv.r === false || argv.r === 'false') { - resolve = false - } - const opts = { - resolve, + resolve: argv.r, lifetime: argv.lifetime, key: argv.key, ttl: argv.ttl diff --git a/src/cli/commands/name/resolve.js b/src/cli/commands/name/resolve.js index 8c83b25682..ca1dd219a3 100644 --- a/src/cli/commands/name/resolve.js +++ b/src/cli/commands/name/resolve.js @@ -18,7 +18,7 @@ module.exports = { type: 'boolean', alias: 'r', describe: 'Resolve until the result is not an IPNS name. Default: false.', - default: false + default: true } }, diff --git a/src/core/components/name.js b/src/core/components/name.js index 2ce58335d7..aee8dfba29 100644 --- a/src/core/components/name.js +++ b/src/core/components/name.js @@ -152,7 +152,7 @@ module.exports = function name (self) { options = mergeOptions({ nocache: false, - recursive: false + recursive: true }, options) const offline = self._options.offline diff --git a/src/core/ipns/index.js b/src/core/ipns/index.js index e693e21f0f..d7405ca3e7 100644 --- a/src/core/ipns/index.js +++ b/src/core/ipns/index.js @@ -2,7 +2,6 @@ const { createFromPrivKey } = require('peer-id') const series = require('async/series') -const Receptacle = require('receptacle') const errcode = require('err-code') const debug = require('debug') @@ -13,7 +12,8 @@ const IpnsPublisher = require('./publisher') const IpnsRepublisher = require('./republisher') const IpnsResolver = require('./resolver') const path = require('./path') - +const { normalizePath } = require('../utils') +const TLRU = require('../../utils/tlru') const defaultRecordTtl = 60 * 1000 class IPNS { @@ -21,12 +21,19 @@ class IPNS { this.publisher = new IpnsPublisher(routing, datastore) this.republisher = new IpnsRepublisher(this.publisher, datastore, peerInfo, keychain, options) this.resolver = new IpnsResolver(routing) - this.cache = new Receptacle({ max: 1000 }) // Create an LRU cache with max 1000 items + this.cache = new TLRU(1000) this.routing = routing } // Publish - publish (privKey, value, lifetime, callback) { + publish (privKey, value, lifetime = IpnsPublisher.defaultRecordLifetime, callback) { + try { + value = normalizePath(value) + } catch (err) { + log.error(err) + return callback(err) + } + series([ (cb) => createFromPrivKey(privKey.bytes, cb), (cb) => this.publisher.publishWithEOL(privKey, value, lifetime, cb) @@ -38,12 +45,12 @@ class IPNS { log(`IPNS value ${value} was published correctly`) - // Add to cache + // // Add to cache const id = results[0].toB58String() const ttEol = parseFloat(lifetime) const ttl = (ttEol < defaultRecordTtl) ? ttEol : defaultRecordTtl - this.cache.set(id, value, { ttl: ttl }) + this.cache.set(id, value, ttl) log(`IPNS value ${value} was cached correctly`) @@ -96,7 +103,7 @@ class IPNS { // Initialize keyspace // sets the ipns record for the given key to point to an empty directory initializeKeyspace (privKey, value, callback) { - this.publisher.publish(privKey, value, callback) + this.publish(privKey, value, IpnsPublisher.defaultRecordLifetime, callback) } } diff --git a/src/core/ipns/publisher.js b/src/core/ipns/publisher.js index 8caa9c8dba..18027632f6 100644 --- a/src/core/ipns/publisher.js +++ b/src/core/ipns/publisher.js @@ -11,7 +11,7 @@ log.error = debug('ipfs:ipns:publisher:error') const ipns = require('ipns') -const defaultRecordTtl = 60 * 60 * 1000 +const defaultRecordLifetime = 60 * 60 * 1000 // IpnsPublisher is capable of publishing and resolving names to the IPFS routing system. class IpnsPublisher { @@ -46,7 +46,7 @@ class IpnsPublisher { // Accepts a keypair, as well as a value (ipfsPath), and publishes it out to the routing system publish (privKey, value, callback) { - this.publishWithEOL(privKey, value, defaultRecordTtl, callback) + this.publishWithEOL(privKey, value, defaultRecordLifetime, callback) } _putRecordToRouting (record, peerId, callback) { @@ -269,4 +269,5 @@ class IpnsPublisher { } } +IpnsPublisher.defaultRecordLifetime = defaultRecordLifetime exports = module.exports = IpnsPublisher diff --git a/src/http/api/resources/name.js b/src/http/api/resources/name.js index f42c770508..02b1eb7326 100644 --- a/src/http/api/resources/name.js +++ b/src/http/api/resources/name.js @@ -7,7 +7,7 @@ exports.resolve = { query: Joi.object().keys({ arg: Joi.string(), nocache: Joi.boolean().default(false), - recursive: Joi.boolean().default(false) + recursive: Joi.boolean().default(true) }).unknown() }, async handler (request, h) { diff --git a/src/utils/tlru.js b/src/utils/tlru.js new file mode 100644 index 0000000000..c60cc35e55 --- /dev/null +++ b/src/utils/tlru.js @@ -0,0 +1,83 @@ +'use strict' +const hashlru = require('hashlru') + +/** + * Time Aware Least Recent Used Cache + * @see https://arxiv.org/pdf/1801.00390 + * @todo move this to ipfs-utils or it's own package + * + * @class TLRU + */ +class TLRU { + /** + * Creates an instance of TLRU. + * + * @param {number} maxSize + * @memberof TLRU + */ + constructor (maxSize) { + this.lru = hashlru(maxSize) + } + + /** + * Get the value from the a key + * + * @param {string} key + * @returns {any} + * @memberof TLRU + */ + get (key) { + const value = this.lru.get(key) + if (value) { + if ((value.expire) && (value.expire < Date.now())) { + this.lru.remove(key) + return undefined + } + } + return value.value + } + + /** + * Set a key value pair + * + * @param {string} key + * @param {any} value + * @param {number} ttl - in miliseconds + * @memberof TLRU + */ + set (key, value, ttl) { + this.lru.set(key, { value, expire: Date.now() + ttl }) + } + + /** + * Find if the cache has the key + * + * @param {string} key + * @returns {boolean} + * @memberof TLRU + */ + has (key) { + return this.lru.has(key) + } + + /** + * Remove key + * + * @param {string} key + * @memberof TLRU + */ + remove (key) { + this.lru.remove(key) + } + + /** + * Clears the cache + * + * @memberof TLRU + */ + clear () { + this.lru.clear() + } +} + +module.exports = TLRU diff --git a/test/cli/name-pubsub.js b/test/cli/name-pubsub.js index fef64295de..cfeb5cf140 100644 --- a/test/cli/name-pubsub.js +++ b/test/cli/name-pubsub.js @@ -14,9 +14,6 @@ const ipfsExec = require('../utils/ipfs-exec') const DaemonFactory = require('ipfsd-ctl') const df = DaemonFactory.create({ type: 'js' }) -const checkAll = (bits) => string => bits.every(bit => string.includes(bit)) -const emptyDirCid = 'QmUNLLsPACCz1vLxQVkXqqLX5R1X345qqfHbsf67hvA3Nn' - const spawnDaemon = (callback) => { df.spawn({ exec: path.resolve(`${__dirname}/../../src/cli/bin.js`), @@ -165,53 +162,6 @@ describe('name-pubsub', () => { }) }) }) - - describe('pubsub records', () => { - let cidAdded - - before(function (done) { - this.timeout(50 * 1000) - ipfsA(`add ${path.resolve(`${__dirname}/../../src/init-files/init-docs/readme`)}`) - .then((out) => { - cidAdded = out.split(' ')[1] - done() - }) - }) - - it('should publish the received record to the subscriber', function () { - this.timeout(80 * 1000) - - return ipfsB(`name resolve ${nodeBId.id}`) - .then((res) => { - expect(res).to.exist() - expect(res).to.satisfy(checkAll([emptyDirCid])) // Empty dir received (subscribed) - - return ipfsA(`name resolve ${nodeBId.id}`) - }) - .catch((err) => { - expect(err).to.exist() // Not available (subscribed now) - - return ipfsB(`name publish ${cidAdded}`) - }) - .then((res) => { - // published to IpfsB and published through pubsub to ipfsa - expect(res).to.exist() - expect(res).to.satisfy(checkAll([cidAdded, nodeBId.id])) - - return ipfsB(`name resolve ${nodeBId.id}`) - }) - .then((res) => { - expect(res).to.exist() - expect(res).to.satisfy(checkAll([cidAdded])) - - return ipfsA(`name resolve ${nodeBId.id}`) - }) - .then((res) => { - expect(res).to.exist() - expect(res).to.satisfy(checkAll([cidAdded])) // value propagated to node B - }) - }) - }) }) describe('disabled', () => { diff --git a/test/cli/name.js b/test/cli/name.js index 1c0638503f..984d777da8 100644 --- a/test/cli/name.js +++ b/test/cli/name.js @@ -1,305 +1,59 @@ -/* eslint max-nested-callbacks: ["error", 6] */ /* eslint-env mocha */ 'use strict' -const chai = require('chai') -const dirtyChai = require('dirty-chai') -const expect = chai.expect -chai.use(dirtyChai) -const path = require('path') -const hat = require('hat') -const ipfsExec = require('../utils/ipfs-exec') - -const DaemonFactory = require('ipfsd-ctl') -const df = DaemonFactory.create({ type: 'js' }) - -const checkAll = (bits) => string => bits.every(bit => string.includes(bit)) +const sinon = require('sinon') +const YargsPromise = require('yargs-promise') +const clearModule = require('clear-module') describe('name', () => { - describe('working locally', () => { - const passPhrase = hat() - const pass = '--pass ' + passPhrase - const name = 'test-key-' + hat() - - let ipfs - let ipfsd - - let cidAdded - let nodeId - let keyId - - before(function (done) { - this.timeout(80 * 1000) - - df.spawn({ - exec: path.resolve(`${__dirname}/../../src/cli/bin.js`), - config: { - Bootstrap: [] - }, - args: ['--pass', passPhrase, '--offline'], - initOptions: { bits: 512 } - }, (err, _ipfsd) => { - expect(err).to.not.exist() - - ipfsd = _ipfsd - ipfs = ipfsExec(_ipfsd.repoPath) - - ipfs(`${pass} key gen ${name} --type rsa --size 2048`) - .then((out) => { - expect(out).to.include(name) - keyId = out.split(' ')[1] - - return ipfs('id') - }) - .then((res) => { - const id = JSON.parse(res) - expect(id).to.have.property('id') - nodeId = id.id - - return ipfs(`add ${path.resolve(`${__dirname}/../../src/init-files/init-docs/readme`)}`) - }) - .then((out) => { - cidAdded = out.split(' ')[1] - done() - }) - }) - }) - - after(function (done) { - if (ipfsd) { - ipfsd.stop(() => done()) - } else { - done() - } - }) - - it('should publish correctly when the file was already added', function () { - this.timeout(70 * 1000) - - return ipfs(`name publish ${cidAdded}`).then((res) => { - expect(res).to.exist() - expect(res).to.satisfy(checkAll([cidAdded, nodeId])) - }) - }) - - it('should publish and resolve an entry with the default options', function () { - this.timeout(70 * 1000) - - return ipfs(`name publish ${cidAdded}`) - .then((res) => { - expect(res).to.exist() - - return ipfs('name resolve') - }) - .then((res) => { - expect(res).to.exist() - expect(res).to.satisfy(checkAll([cidAdded])) - }) - }) - - it('should publish correctly when the file was not added but resolve is disabled', function () { - this.timeout(70 * 1000) - - const notAddedCid = 'QmPFVLPmp9zv5Z5KUqLhe2EivAGccQW2r7M7jhVJGLZoZU' - - return ipfs(`name publish ${notAddedCid} --resolve false`).then((res) => { - expect(res).to.exist() - expect(res).to.satisfy(checkAll([notAddedCid, nodeId])) - }) - }) - - it('should not get the entry correctly if its validity time expired', function () { - this.timeout(70 * 1000) - - return ipfs(`name publish ${cidAdded} --lifetime 10ns`) - .then((res) => { - expect(res).to.exist() - - setTimeout(function () { - return ipfs('name resolve') - .then((res) => { - expect(res).to.not.exist() - }) - .catch((err) => { - expect(err).to.exist() - }) - }, 1) - }) - }) - - it('should publish correctly when a new key is used', function () { - this.timeout(70 * 1000) - - return ipfs(`name publish ${cidAdded} --key ${name}`).then((res) => { - expect(res).to.exist() - expect(res).to.satisfy(checkAll([cidAdded, keyId])) - }) - }) - - it('should return the immediate pointing record, unless using the recursive parameter', function () { - this.timeout(90 * 1000) - - return ipfs(`name publish ${cidAdded}`) - .then((res) => { - expect(res).to.exist() - expect(res).to.satisfy(checkAll([cidAdded, nodeId])) - - return ipfs(`name publish /ipns/${nodeId} --key ${name}`) - }) - .then((res) => { - expect(res).to.exist() - expect(res).to.satisfy(checkAll([nodeId, keyId])) - - return ipfs(`name resolve ${keyId}`) - }) - .then((res) => { - expect(res).to.exist() - expect(res).to.satisfy(checkAll([nodeId])) - }) - }) - - it('should go recursively until finding an ipfs hash', function () { - this.timeout(90 * 1000) - - return ipfs(`name publish ${cidAdded}`) - .then((res) => { - expect(res).to.exist() - expect(res).to.satisfy(checkAll([cidAdded, nodeId])) - - return ipfs(`name publish /ipns/${nodeId} --key ${name}`) - }) - .then((res) => { - expect(res).to.exist() - expect(res).to.satisfy(checkAll([nodeId, keyId])) - - return ipfs(`name resolve ${keyId} --recursive`) - }) - .then((res) => { - expect(res).to.exist() - expect(res).to.satisfy(checkAll([cidAdded])) - }) - }) + let cli + let cliUtils + beforeEach(() => { + cliUtils = require('../../src/cli/utils') + cli = new YargsPromise(require('../../src/cli/parser')) + }) + afterEach(() => { + sinon.restore() + // TODO: the lines below shouldn't be necessary, cli needs refactor to simplify testability + // Force the next require to not use require cache + clearModule('../../src/cli/utils') + clearModule('../../src/cli/parser') }) - describe('using dht', () => { - const passPhrase = hat() - const pass = '--pass ' + passPhrase - const name = 'test-key-' + hat() - - let ipfs - let ipfsd - - let cidAdded - let nodeId - let keyId - - before(function (done) { - this.timeout(80 * 1000) - - df.spawn({ - exec: path.resolve(`${__dirname}/../../src/cli/bin.js`), - config: { - Bootstrap: [], - Discovery: { - MDNS: { - Enabled: false - }, - webRTCStar: { - Enabled: false - } - } - }, - args: ['--pass', passPhrase], - initOptions: { bits: 512 } - }, (err, _ipfsd) => { - expect(err).to.not.exist() - - ipfsd = _ipfsd - ipfs = ipfsExec(_ipfsd.repoPath) - - ipfs(`${pass} key gen ${name} --type rsa --size 2048`) - .then((out) => { - expect(out).to.include(name) - keyId = out.split(' ')[1] - - return ipfs('id') - }) - .then((res) => { - const id = JSON.parse(res) - expect(id).to.have.property('id') - nodeId = id.id - - return ipfs(`add ${path.resolve(`${__dirname}/../../src/init-files/init-docs/readme`)}`) - }) - .then((out) => { - cidAdded = out.split(' ')[1] - done() - }) - }) - }) - - after(function (done) { - if (ipfsd) { - ipfsd.stop(() => done()) - } else { - done() - } - }) - - it('should publish and resolve an entry with the default options', function () { - this.timeout(70 * 1000) - - return ipfs(`name publish ${cidAdded}`) - .then((res) => { - expect(res).to.exist() - - return ipfs('name resolve') - }) - .then((res) => { - expect(res).to.exist() - expect(res).to.satisfy(checkAll([cidAdded])) - }) - }) - - it('should not get the entry correctly if its validity time expired', function () { - this.timeout(70 * 1000) - - return ipfs(`name publish ${cidAdded} --lifetime 10ns`) - .then((res) => { - expect(res).to.exist() - - setTimeout(function () { - return ipfs('name resolve') - .then((res) => { - expect(res).to.not.exist() - }) - .catch((err) => { - expect(err).to.exist() - }) - }, 1) - }) - }) + it('resolve', async () => { + const resolveFake = sinon.fake() - it('should return the immediate pointing record, unless using the recursive parameter', function () { - this.timeout(90 * 1000) + sinon + .stub(cliUtils, 'getIPFS') + .callsArgWith(1, null, { name: { resolve: resolveFake } }) - return ipfs(`name publish ${cidAdded}`) - .then((res) => { - expect(res).to.exist() - expect(res).to.satisfy(checkAll([cidAdded, nodeId])) + // TODO: the lines below shouldn't be necessary, cli needs refactor to simplify testability + // Force the next require to not use require cache + clearModule('../../src/cli/commands/name/resolve.js') - return ipfs(`name publish /ipns/${nodeId} --key ${name}`) - }) - .then((res) => { - expect(res).to.exist() - expect(res).to.satisfy(checkAll([nodeId, keyId])) + await cli.parse(`name resolve test`) + sinon.assert.calledWith(resolveFake, 'test', { nocache: false, recursive: true }) + }) - return ipfs(`name resolve ${keyId}`) - }) - .then((res) => { - expect(res).to.exist() - expect(res).to.satisfy(checkAll([nodeId])) - }) + it('publish', async () => { + const publishFake = sinon.fake.returns({ name: 'name', value: 'value' }) + const printSpy = sinon.spy(cliUtils, 'print') + + sinon + .stub(cliUtils, 'getIPFS') + .callsArgWith(1, null, { name: { publish: publishFake } }) + + // TODO: the lines below shouldn't be necessary, cli needs refactor to simplify testability + // Force the next require to not use require cache + clearModule('../../src/cli/commands/name/publish.js') + + await cli.parse(`name publish test --silent`) + sinon.assert.calledWith(printSpy, 'Published to name: value') + sinon.assert.calledWith(publishFake, 'test', { + resolve: true, + lifetime: '24h', + key: 'self', + ttl: '' }) }) }) diff --git a/test/core/name-pubsub.js b/test/core/name-pubsub.js index 3edb00d460..884d0a55b7 100644 --- a/test/core/name-pubsub.js +++ b/test/core/name-pubsub.js @@ -18,6 +18,7 @@ const isNode = require('detect-node') const ipns = require('ipns') const IPFS = require('../../src') const waitFor = require('../utils/wait-for') +const delay = require('interface-ipfs-core/src/utils/delay') const DaemonFactory = require('ipfsd-ctl') const df = DaemonFactory.create({ type: 'proc' }) @@ -34,6 +35,7 @@ describe('name-pubsub', function () { let nodeA let nodeB let idA + let idB const createNode = (callback) => { df.spawn({ @@ -73,6 +75,7 @@ describe('name-pubsub', function () { expect(err).to.not.exist() idA = ids[0] + idB = ids[1] nodeA.swarm.connect(ids[1].addresses[0], done) }) }) @@ -135,4 +138,31 @@ describe('name-pubsub', function () { }) }) }) + + it('should self resolve, publish and then resolve correctly', async function () { + this.timeout(6000) + const emptyDirCid = '/ipfs/QmUNLLsPACCz1vLxQVkXqqLX5R1X345qqfHbsf67hvA3Nn' + const [{ path }] = await nodeA.add(Buffer.from('pubsub records')) + + const resolvesEmpty = await nodeB.name.resolve(idB.id) + expect(resolvesEmpty).to.be.eq(emptyDirCid) + + try { + await nodeA.name.resolve(idB.id) + } catch (error) { + expect(error).to.exist() + } + + const publish = await nodeB.name.publish(path) + expect(publish).to.be.eql({ + name: idB.id, + value: `/ipfs/${path}` + }) + + const resolveB = await nodeB.name.resolve(idB.id) + expect(resolveB).to.be.eq(`/ipfs/${path}`) + await delay(5000) + const resolveA = await nodeA.name.resolve(idB.id) + expect(resolveA).to.be.eq(`/ipfs/${path}`) + }) }) From c88fced3a16cd0b991909234f216b60f02a5a207 Mon Sep 17 00:00:00 2001 From: Hugo Dias Date: Tue, 18 Jun 2019 17:02:53 +0100 Subject: [PATCH 08/15] fix: update deps and fix tests --- package.json | 7 ++++--- test/core/files-regular-utils.js | 33 ++++++++++++-------------------- test/core/kad-dht.node.js | 2 +- 3 files changed, 17 insertions(+), 25 deletions(-) diff --git a/package.json b/package.json index 5fdf0cfef5..e9a2a225db 100644 --- a/package.json +++ b/package.json @@ -76,6 +76,7 @@ "cid-tool": "~0.3.0", "cids": "~0.7.1", "class-is": "^1.1.0", + "clear-module": "^3.2.0", "datastore-core": "~0.6.0", "datastore-pubsub": "~0.1.1", "debug": "^4.1.0", @@ -111,8 +112,8 @@ "ipld-raw": "^4.0.0", "ipld-zcash": "~0.3.0", "ipns": "~0.5.2", - "is-ipfs": "~0.6.1", "is-domain-name": "^1.0.1", + "is-ipfs": "~0.6.1", "is-pull-stream": "~0.0.0", "is-stream": "^2.0.0", "iso-url": "~0.4.6", @@ -185,8 +186,8 @@ "execa": "^1.0.0", "form-data": "^2.3.3", "hat": "0.0.3", - "interface-ipfs-core": "~0.104.0", - "ipfsd-ctl": "~0.42.0", + "interface-ipfs-core": "ipfs/interface-js-ipfs-core#feat/name-resolve-dns", + "ipfsd-ctl": "ipfs/js-ipfsd-ctl#feat/name-resolve-dns", "libp2p-websocket-star": "~0.10.2", "ncp": "^2.0.0", "qs": "^6.5.2", diff --git a/test/core/files-regular-utils.js b/test/core/files-regular-utils.js index 3b86020d3f..380ff3f3e8 100644 --- a/test/core/files-regular-utils.js +++ b/test/core/files-regular-utils.js @@ -12,47 +12,38 @@ describe('files-regular/utils', () => { describe('parseChunkerString', () => { it('handles an empty string', () => { const options = utils.parseChunkerString('') - expect(options).to.have.property('chunker').to.equal('fixed') + expect(options.chunker).to.equal('fixed') }) it('handles a null chunker string', () => { const options = utils.parseChunkerString(null) - expect(options).to.have.property('chunker').to.equal('fixed') + expect(options.chunker).to.equal('fixed') }) it('parses a fixed size string', () => { const options = utils.parseChunkerString('size-512') - expect(options).to.have.property('chunker').to.equal('fixed') - expect(options) - .to.have.property('chunkerOptions') - .to.have.property('maxChunkSize') - .to.equal(512) + expect(options.chunker).to.equal('fixed') + expect(options.chunkerOptions.maxChunkSize).to.equal(512) }) it('parses a rabin string without size', () => { const options = utils.parseChunkerString('rabin') - expect(options).to.have.property('chunker').to.equal('rabin') - expect(options) - .to.have.property('chunkerOptions') - .to.have.property('avgChunkSize') + expect(options.chunker).to.equal('rabin') + expect(options.chunkerOptions.avgChunkSize).to.equal(262144) }) it('parses a rabin string with only avg size', () => { const options = utils.parseChunkerString('rabin-512') - expect(options).to.have.property('chunker').to.equal('rabin') - expect(options) - .to.have.property('chunkerOptions') - .to.have.property('avgChunkSize') - .to.equal(512) + expect(options.chunker).to.equal('rabin') + expect(options.chunkerOptions.avgChunkSize).to.equal(512) }) it('parses a rabin string with min, avg, and max', () => { const options = utils.parseChunkerString('rabin-42-92-184') - expect(options).to.have.property('chunker').to.equal('rabin') - expect(options).to.have.property('chunkerOptions') - expect(options.chunkerOptions).to.have.property('minChunkSize').to.equal(42) - expect(options.chunkerOptions).to.have.property('avgChunkSize').to.equal(92) - expect(options.chunkerOptions).to.have.property('maxChunkSize').to.equal(184) + expect(options.chunker).to.equal('rabin') + expect(options.chunkerOptions.minChunkSize).to.equal(42) + expect(options.chunkerOptions.avgChunkSize).to.equal(92) + expect(options.chunkerOptions.maxChunkSize).to.equal(184) }) it('throws an error for unsupported chunker type', () => { diff --git a/test/core/kad-dht.node.js b/test/core/kad-dht.node.js index d07be33f20..d06392abbc 100644 --- a/test/core/kad-dht.node.js +++ b/test/core/kad-dht.node.js @@ -32,7 +32,7 @@ function createNode (callback) { }, callback) } -describe('kad-dht is routing content and peers correctly', () => { +describe.skip('kad-dht is routing content and peers correctly', () => { let nodeA let nodeB let nodeC From 3061c102558fb8e7b0dc59025d5aa88f04f5c9b7 Mon Sep 17 00:00:00 2001 From: Hugo Dias Date: Tue, 18 Jun 2019 19:55:13 +0100 Subject: [PATCH 09/15] fix: tests timeout --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 19ece97bfc..beaec2bc3f 100644 --- a/.travis.yml +++ b/.travis.yml @@ -18,7 +18,7 @@ os: - osx - windows -script: npx nyc -s npm run test:node --timeout=10000 -- --bail +script: npx nyc -s npx aegir test -t node --timeout 10000 --bail after_success: npx nyc report --reporter=text-lcov > coverage.lcov && npx codecov jobs: From 547a66b445c9eda843ebde476d00d0dd7b122263 Mon Sep 17 00:00:00 2001 From: Hugo Dias Date: Tue, 18 Jun 2019 20:26:02 +0100 Subject: [PATCH 10/15] fix: electron tests timeout --- .travis.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index beaec2bc3f..995a49a2f2 100644 --- a/.travis.yml +++ b/.travis.yml @@ -47,12 +47,12 @@ jobs: - stage: test name: electron-main script: - - xvfb-run npx aegir test -t electron-main -- --bail + - xvfb-run npx aegir test -t electron-main -- --bail --timeout 10000 - stage: test name: electron-renderer script: - - xvfb-run npx aegir test -t electron-renderer -- --bail + - xvfb-run npx aegir test -t electron-renderer -- --bail --timeout 10000 notifications: email: false From b0c96e854a6847394d79a17ab168e787bc33068c Mon Sep 17 00:00:00 2001 From: Hugo Dias Date: Fri, 21 Jun 2019 12:08:03 +0100 Subject: [PATCH 11/15] fix: fix tlru, move dep to devDep --- package.json | 2 +- src/utils/tlru.js | 6 +++++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index e9a2a225db..688b2cf347 100644 --- a/package.json +++ b/package.json @@ -76,7 +76,6 @@ "cid-tool": "~0.3.0", "cids": "~0.7.1", "class-is": "^1.1.0", - "clear-module": "^3.2.0", "datastore-core": "~0.6.0", "datastore-pubsub": "~0.1.1", "debug": "^4.1.0", @@ -179,6 +178,7 @@ "aegir": "^19.0.3", "base64url": "^3.0.1", "chai": "^4.2.0", + "clear-module": "^3.2.0", "delay": "^4.1.0", "detect-node": "^2.0.4", "dir-compare": "^1.4.0", diff --git a/src/utils/tlru.js b/src/utils/tlru.js index c60cc35e55..ba3b26e8c6 100644 --- a/src/utils/tlru.js +++ b/src/utils/tlru.js @@ -57,7 +57,11 @@ class TLRU { * @memberof TLRU */ has (key) { - return this.lru.has(key) + const value = this.get(key) + if (value) { + return true + } + return false } /** From 4aedf1a60e0354d5ffa2acc3e29aab8da039142f Mon Sep 17 00:00:00 2001 From: Alan Shaw Date: Sat, 22 Jun 2019 00:21:44 +0200 Subject: [PATCH 12/15] chore: update ipfs-http-client dep --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 688b2cf347..89c6adec16 100644 --- a/package.json +++ b/package.json @@ -93,7 +93,7 @@ "ipfs-bitswap": "~0.24.1", "ipfs-block": "~0.8.1", "ipfs-block-service": "~0.15.1", - "ipfs-http-client": "ipfs/js-ipfs-http-client#feat/name-resolve-dns", + "ipfs-http-client": "~32.0.1", "ipfs-http-response": "~0.3.0", "ipfs-mfs": "~0.11.4", "ipfs-multipart": "~0.1.0", From 7725a05d5ba345d8c1a6ac157f27cf6f12d79477 Mon Sep 17 00:00:00 2001 From: Alan Shaw Date: Sat, 22 Jun 2019 00:22:24 +0200 Subject: [PATCH 13/15] fix: version range --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 89c6adec16..81987a8169 100644 --- a/package.json +++ b/package.json @@ -93,7 +93,7 @@ "ipfs-bitswap": "~0.24.1", "ipfs-block": "~0.8.1", "ipfs-block-service": "~0.15.1", - "ipfs-http-client": "~32.0.1", + "ipfs-http-client": "^32.0.1", "ipfs-http-response": "~0.3.0", "ipfs-mfs": "~0.11.4", "ipfs-multipart": "~0.1.0", From 16e8dfb707b016ae8d0123cb024eaa2413e12d52 Mon Sep 17 00:00:00 2001 From: Alan Shaw Date: Sun, 23 Jun 2019 09:57:12 +0200 Subject: [PATCH 14/15] chore: update dep --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 81987a8169..4d9c486c9b 100644 --- a/package.json +++ b/package.json @@ -186,7 +186,7 @@ "execa": "^1.0.0", "form-data": "^2.3.3", "hat": "0.0.3", - "interface-ipfs-core": "ipfs/interface-js-ipfs-core#feat/name-resolve-dns", + "interface-ipfs-core": "~0.105.0", "ipfsd-ctl": "ipfs/js-ipfsd-ctl#feat/name-resolve-dns", "libp2p-websocket-star": "~0.10.2", "ncp": "^2.0.0", From 0c833c30fd95125820feab1921a918213d3004bf Mon Sep 17 00:00:00 2001 From: Alan Shaw Date: Sun, 23 Jun 2019 09:58:41 +0200 Subject: [PATCH 15/15] chore: update dep --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 4d9c486c9b..94bcaa0cb1 100644 --- a/package.json +++ b/package.json @@ -187,7 +187,7 @@ "form-data": "^2.3.3", "hat": "0.0.3", "interface-ipfs-core": "~0.105.0", - "ipfsd-ctl": "ipfs/js-ipfsd-ctl#feat/name-resolve-dns", + "ipfsd-ctl": "~0.43.0", "libp2p-websocket-star": "~0.10.2", "ncp": "^2.0.0", "qs": "^6.5.2",