diff --git a/CHANGELOG.md b/CHANGELOG.md
index af8c75cd7..c41311373 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,13 @@
+
+# [40.2.0](https://github.com/ipfs/js-ipfs-http-client/compare/v40.1.0...v40.2.0) (2020-01-09)
+
+
+### Features
+
+* support UnixFSv1.5 metadata ([#1186](https://github.com/ipfs/js-ipfs-http-client/issues/1186)) ([da9d17a](https://github.com/ipfs/js-ipfs-http-client/commit/da9d17a))
+
+
+
# [40.1.0](https://github.com/ipfs/js-ipfs-http-client/compare/v40.0.1...v40.1.0) (2019-12-10)
diff --git a/README.md b/README.md
index 5c6a00070..9c6d3a3c7 100644
--- a/README.md
+++ b/README.md
@@ -413,7 +413,7 @@ The js-ipfs-http-client is a work in progress. As such, there's a few things you
- **[Check out the existing issues](https://github.com/ipfs/js-ipfs-http-client/issues)**!
- **Perform code reviews**. More eyes will help a) speed the project along b) ensure quality and c) reduce possible future bugs.
- **Add tests**. There can never be enough tests. Note that interface tests exist inside [`interface-ipfs-core`](https://github.com/ipfs/interface-ipfs-core/tree/master/js/src).
-- **Contribute to the [FAQ repository](https://github.com/ipfs/faq/issues)** with any questions you have about IPFS or any of the relevant technology. A good example would be asking, 'What is a merkledag tree?'. If you don't know a term, odds are, someone else doesn't either. Eventually, we should have a good understanding of where we need to improve communications and teaching together to make IPFS and IPN better.
+- **Contribute to the [FAQ repository](https://github.com/ipfs/faq/issues)** with any questions you have about IPFS or any of the relevant technology. A good example would be asking, 'What is a merkledag tree?'. If you don't know a term, odds are, someone else doesn't either. Eventually, we should have a good understanding of where we need to improve communications and teaching together to make IPFS and IPNS better.
**Want to hack on IPFS?**
diff --git a/examples/bundle-browserify/README.md b/examples/bundle-browserify/README.md
index a185527e0..def887200 100644
--- a/examples/bundle-browserify/README.md
+++ b/examples/bundle-browserify/README.md
@@ -11,7 +11,7 @@ As for any js-ipfs-http-client example, **you need a running IPFS daemon**, you
**Note:** If you load your app from a different domain than the one the daemon is running (most probably), you will need to set up CORS, see https://github.com/ipfs/js-ipfs-http-client#cors to learn how to do that.
-A quick (and dirty way to get it done) is:
+A quick (and dirty) way to get it done is:
```bash
> ipfs config --json API.HTTPHeaders.Access-Control-Allow-Origin "[\"*\"]"
diff --git a/examples/bundle-webpack/README.md b/examples/bundle-webpack/README.md
index 3b894b7f5..c85543240 100644
--- a/examples/bundle-webpack/README.md
+++ b/examples/bundle-webpack/README.md
@@ -11,7 +11,7 @@ As for any js-ipfs-http-client example, **you need a running IPFS daemon**, you
**Note:** If you load your app from a different domain than the one the daemon is running (most probably), you will need to set up CORS, see https://github.com/ipfs/js-ipfs-http-client#cors to learn how to do that.
-A quick (and dirty way to get it done) is:
+A quick (and dirty) way to get it done is:
```bash
> ipfs config --json API.HTTPHeaders.Access-Control-Allow-Origin "[\"*\"]"
diff --git a/package.json b/package.json
index abaf35d69..d0b0d7783 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "ipfs-http-client",
- "version": "40.1.0",
+ "version": "40.2.0",
"description": "A client library for the IPFS HTTP API",
"keywords": [
"ipfs"
@@ -54,7 +54,7 @@
"explain-error": "^1.0.4",
"form-data": "^3.0.0",
"ipfs-block": "~0.8.1",
- "ipfs-utils": "^0.4.0",
+ "ipfs-utils": "^0.4.2",
"ipld-dag-cbor": "~0.15.0",
"ipld-dag-pb": "^0.18.1",
"ipld-raw": "^4.0.0",
@@ -84,8 +84,8 @@
"cross-env": "^6.0.0",
"detect-node": "^2.0.4",
"go-ipfs-dep": "^0.4.22",
- "interface-ipfs-core": "^0.124.0",
- "ipfsd-ctl": "^0.47.1",
+ "interface-ipfs-core": "^0.126.0",
+ "ipfsd-ctl": "^1.0.0",
"ndjson": "^1.5.0",
"nock": "^11.4.0",
"pull-stream": "^3.6.14",
@@ -137,6 +137,7 @@
"Jeff Downie ",
"Jeromy ",
"Jeromy ",
+ "Jim Pick ",
"Joe Turgeon ",
"Jonathan ",
"Juan Batiz-Benet ",
@@ -158,6 +159,7 @@
"NÃckolas Goline ",
"Oli Evans ",
"Orie Steele ",
+ "Paul Cowgill ",
"Pedro Santos ",
"Pedro Santos ",
"Pedro Teixeira ",
diff --git a/src/add/form-data.browser.js b/src/add/form-data.browser.js
index 247396c42..484f67c6c 100644
--- a/src/add/form-data.browser.js
+++ b/src/add/form-data.browser.js
@@ -2,6 +2,7 @@
/* eslint-env browser */
const normaliseInput = require('ipfs-utils/src/files/normalise-input')
+const mtimeToObject = require('../lib/mtime-to-object')
exports.toFormData = async input => {
const files = normaliseInput(input)
@@ -9,6 +10,21 @@ exports.toFormData = async input => {
let i = 0
for await (const file of files) {
+ const headers = {}
+
+ if (file.mtime !== undefined && file.mtime !== null) {
+ const mtime = mtimeToObject(file.mtime)
+
+ if (mtime) {
+ headers.mtime = mtime.secs
+ headers['mtime-nsecs'] = mtime.nsecs
+ }
+ }
+
+ if (file.mode !== undefined && file.mode !== null) {
+ headers.mode = file.mode.toString(8).padStart(4, '0')
+ }
+
if (file.content) {
// In the browser there's _currently_ no streaming upload, buffer up our
// async iterator chunks and append a big Blob :(
@@ -18,9 +34,13 @@ exports.toFormData = async input => {
bufs.push(chunk)
}
- formData.append(`file-${i}`, new Blob(bufs, { type: 'application/octet-stream' }), encodeURIComponent(file.path))
+ formData.append(`file-${i}`, new Blob(bufs, { type: 'application/octet-stream' }), encodeURIComponent(file.path), {
+ header: headers
+ })
} else {
- formData.append(`dir-${i}`, new Blob([], { type: 'application/x-directory' }), encodeURIComponent(file.path))
+ formData.append(`dir-${i}`, new Blob([], { type: 'application/x-directory' }), encodeURIComponent(file.path), {
+ header: headers
+ })
}
i++
diff --git a/src/add/form-data.js b/src/add/form-data.js
index 96d55f672..1ce5050da 100644
--- a/src/add/form-data.js
+++ b/src/add/form-data.js
@@ -5,6 +5,7 @@ const { Buffer } = require('buffer')
const toStream = require('it-to-stream')
const normaliseInput = require('ipfs-utils/src/files/normalise-input')
const { isElectronRenderer } = require('ipfs-utils/src/env')
+const mtimeToObject = require('../lib/mtime-to-object')
exports.toFormData = async input => {
const files = normaliseInput(input)
@@ -12,6 +13,21 @@ exports.toFormData = async input => {
let i = 0
for await (const file of files) {
+ const headers = {}
+
+ if (file.mtime !== undefined && file.mtime !== null) {
+ const mtime = mtimeToObject(file.mtime)
+
+ if (mtime) {
+ headers.mtime = mtime.secs
+ headers['mtime-nsecs'] = mtime.nsecs
+ }
+ }
+
+ if (file.mode !== undefined && file.mode !== null) {
+ headers.mode = file.mode.toString(8).padStart(4, '0')
+ }
+
if (file.content) {
// In Node.js, FormData can be passed a stream so no need to buffer
formData.append(
@@ -26,13 +42,15 @@ exports.toFormData = async input => {
{
filepath: encodeURIComponent(file.path),
contentType: 'application/octet-stream',
- knownLength: file.content.length // Send Content-Length header if known
+ knownLength: file.content.length, // Send Content-Length header if known
+ header: headers
}
)
} else {
formData.append(`dir-${i}`, Buffer.alloc(0), {
filepath: encodeURIComponent(file.path),
- contentType: 'application/x-directory'
+ contentType: 'application/x-directory',
+ header: headers
})
}
diff --git a/src/add/index.js b/src/add/index.js
index df6afbad3..546e5428d 100644
--- a/src/add/index.js
+++ b/src/add/index.js
@@ -52,6 +52,16 @@ module.exports = configure(({ ky }) => {
}
})
-function toCoreInterface ({ name, hash, size }) {
- return { path: name, hash, size: parseInt(size) }
+function toCoreInterface ({ name, hash, size, mode, mtime }) {
+ const output = {
+ path: name,
+ hash,
+ size: parseInt(size)
+ }
+
+ if (mode !== undefined) {
+ output.mode = parseInt(mode, 8)
+ }
+
+ return output
}
diff --git a/src/files/chmod.js b/src/files/chmod.js
new file mode 100644
index 000000000..b4c0a11dd
--- /dev/null
+++ b/src/files/chmod.js
@@ -0,0 +1,25 @@
+'use strict'
+
+const configure = require('../lib/configure')
+const modeToString = require('../lib/mode-to-string')
+
+module.exports = configure(({ ky }) => {
+ return function chmod (path, mode, options) {
+ options = options || {}
+
+ const searchParams = new URLSearchParams(options.searchParams)
+ searchParams.append('arg', path)
+ searchParams.append('mode', modeToString(mode))
+ if (options.format) searchParams.set('format', options.format)
+ if (options.flush != null) searchParams.set('flush', options.flush)
+ if (options.hashAlg) searchParams.set('hash', options.hashAlg)
+ if (options.parents != null) searchParams.set('parents', options.parents)
+
+ return ky.post('files/chmod', {
+ timeout: options.timeout,
+ signal: options.signal,
+ headers: options.headers,
+ searchParams
+ }).text()
+ }
+})
diff --git a/src/files/index.js b/src/files/index.js
index 25e79fcab..ee6b7d8d8 100644
--- a/src/files/index.js
+++ b/src/files/index.js
@@ -8,6 +8,7 @@ module.exports = config => {
const read = require('./read')(config)
return {
+ chmod: callbackify.variadic(require('./chmod')(config)),
cp: callbackify.variadic(require('./cp')(config)),
mkdir: callbackify.variadic(require('./mkdir')(config)),
flush: callbackify.variadic(require('./flush')(config)),
@@ -19,6 +20,7 @@ module.exports = config => {
read: callbackify.variadic(concatify(read)),
readReadableStream: streamify.readable(read),
readPullStream: pullify.source(read),
+ touch: callbackify.variadic(require('./touch')(config)),
write: callbackify.variadic(require('./write')(config)),
mv: callbackify.variadic(require('./mv')(config))
}
diff --git a/src/files/ls.js b/src/files/ls.js
index 1baa3f656..51ee33912 100644
--- a/src/files/ls.js
+++ b/src/files/ls.js
@@ -4,7 +4,7 @@ const CID = require('cids')
const ndjson = require('iterable-ndjson')
const toIterable = require('../lib/stream-to-iterable')
const configure = require('../lib/configure')
-const toCamel = require('../lib/object-to-camel')
+const toCamelWithMetadata = require('../lib/object-to-camel-with-metadata')
module.exports = configure(({ ky }) => {
return async function * ls (path, options) {
@@ -32,11 +32,12 @@ module.exports = configure(({ ky }) => {
// go-ipfs does not yet support the "stream" option
if ('Entries' in result) {
for (const entry of result.Entries || []) {
- yield toCamel(entry)
+ yield toCamelWithMetadata(entry)
}
return
}
- yield toCamel(result)
+
+ yield toCamelWithMetadata(result)
}
}
})
diff --git a/src/files/mkdir.js b/src/files/mkdir.js
index 0fc3c238d..3a50c7728 100644
--- a/src/files/mkdir.js
+++ b/src/files/mkdir.js
@@ -1,10 +1,13 @@
'use strict'
const configure = require('../lib/configure')
+const modeToString = require('../lib/mode-to-string')
+const mtimeToObject = require('../lib/mtime-to-object')
module.exports = configure(({ ky }) => {
return (path, options) => {
options = options || {}
+ const mtime = mtimeToObject(options.mtime)
const searchParams = new URLSearchParams(options.searchParams)
searchParams.append('arg', path)
@@ -13,6 +16,14 @@ module.exports = configure(({ ky }) => {
if (options.flush != null) searchParams.set('flush', options.flush)
if (options.hashAlg) searchParams.set('hash', options.hashAlg)
if (options.parents != null) searchParams.set('parents', options.parents)
+ if (mtime) {
+ searchParams.set('mtime', mtime.secs)
+
+ if (mtime.nsecs != null) {
+ searchParams.set('mtimeNsecs', mtime.nsecs)
+ }
+ }
+ if (options.mode != null) searchParams.set('mode', modeToString(options.mode))
return ky.post('files/mkdir', {
timeout: options.timeout,
diff --git a/src/files/stat.js b/src/files/stat.js
index 98026283e..1b4af061b 100644
--- a/src/files/stat.js
+++ b/src/files/stat.js
@@ -1,7 +1,7 @@
'use strict'
const configure = require('../lib/configure')
-const toCamel = require('../lib/object-to-camel')
+const toCamelWithMetadata = require('../lib/object-to-camel-with-metadata')
module.exports = configure(({ ky }) => {
return async (path, options) => {
@@ -27,6 +27,7 @@ module.exports = configure(({ ky }) => {
}).json()
res.WithLocality = res.WithLocality || false
- return toCamel(res)
+
+ return toCamelWithMetadata(res)
}
})
diff --git a/src/files/touch.js b/src/files/touch.js
new file mode 100644
index 000000000..b38aca905
--- /dev/null
+++ b/src/files/touch.js
@@ -0,0 +1,29 @@
+'use strict'
+
+const configure = require('../lib/configure')
+const mtimeToObject = require('../lib/mtime-to-object')
+
+module.exports = configure(({ ky }) => {
+ return function touch (path, options) {
+ options = options || {}
+ const mtime = mtimeToObject(options.mtime)
+
+ const searchParams = new URLSearchParams(options.searchParams)
+ searchParams.append('arg', path)
+ if (mtime) {
+ searchParams.set('mtime', mtime.secs)
+ searchParams.set('mtimeNsecs', mtime.nsecs)
+ }
+ if (options.format) searchParams.set('format', options.format)
+ if (options.flush != null) searchParams.set('flush', options.flush)
+ if (options.hashAlg) searchParams.set('hash', options.hashAlg)
+ if (options.parents != null) searchParams.set('parents', options.parents)
+
+ return ky.post('files/touch', {
+ timeout: options.timeout,
+ signal: options.signal,
+ headers: options.headers,
+ searchParams
+ }).text()
+ }
+})
diff --git a/src/files/write.js b/src/files/write.js
index 77a772ea6..e31f0a8a2 100644
--- a/src/files/write.js
+++ b/src/files/write.js
@@ -2,10 +2,13 @@
const configure = require('../lib/configure')
const toFormData = require('../lib/buffer-to-form-data')
+const modeToString = require('../lib/mode-to-string')
+const mtimeToObject = require('../lib/mtime-to-object')
module.exports = configure(({ ky }) => {
return async (path, input, options) => {
options = options || {}
+ const mtime = mtimeToObject(options.mtime)
const searchParams = new URLSearchParams(options.searchParams)
searchParams.set('arg', path)
@@ -18,13 +21,24 @@ module.exports = configure(({ ky }) => {
if (options.parents != null) searchParams.set('parents', options.parents)
if (options.rawLeaves != null) searchParams.set('raw-leaves', options.rawLeaves)
if (options.truncate != null) searchParams.set('truncate', options.truncate)
+ if (mtime) {
+ searchParams.set('mtime', mtime.secs)
+
+ if (mtime.nsecs != null) {
+ searchParams.set('mtimeNsecs', mtime.nsecs)
+ }
+ }
const res = await ky.post('files/write', {
timeout: options.timeout,
signal: options.signal,
headers: options.headers,
searchParams,
- body: toFormData(input) // TODO: support inputs other than buffer as per spec
+ body: toFormData(input, {
+ mode: options.mode != null ? modeToString(options.mode) : undefined,
+ mtime: mtime ? mtime.secs : undefined,
+ mtimeNsecs: mtime ? mtime.nsecs : undefined
+ }) // TODO: support inputs other than buffer as per spec
})
return res.text()
diff --git a/src/lib/buffer-to-form-data.js b/src/lib/buffer-to-form-data.js
index 41f03383e..1a4830361 100644
--- a/src/lib/buffer-to-form-data.js
+++ b/src/lib/buffer-to-form-data.js
@@ -3,9 +3,25 @@
const FormData = require('form-data')
const { isElectronRenderer } = require('ipfs-utils/src/env')
-module.exports = buf => {
+module.exports = (buf, { mode, mtime, mtimeNsecs } = {}) => {
+ const headers = {}
+
+ if (mode != null) {
+ headers.mode = mode
+ }
+
+ if (mtime != null) {
+ headers.mtime = mtime
+
+ if (mtimeNsecs != null) {
+ headers['mtime-nsecs'] = mtimeNsecs
+ }
+ }
+
const formData = new FormData()
- formData.append('file', buf)
+ formData.append('file', buf, {
+ header: headers
+ })
return formData
}
diff --git a/src/lib/mode-to-string.js b/src/lib/mode-to-string.js
new file mode 100644
index 000000000..ee2742b9a
--- /dev/null
+++ b/src/lib/mode-to-string.js
@@ -0,0 +1,13 @@
+'use strict'
+
+module.exports = (mode) => {
+ if (mode === undefined || mode === null) {
+ return undefined
+ }
+
+ if (typeof mode === 'string' || mode instanceof String) {
+ return mode
+ }
+
+ return mode.toString(8).padStart(4, '0')
+}
diff --git a/src/lib/mtime-to-object.js b/src/lib/mtime-to-object.js
new file mode 100644
index 000000000..be89148f6
--- /dev/null
+++ b/src/lib/mtime-to-object.js
@@ -0,0 +1,56 @@
+'use strict'
+
+module.exports = function parseMtime (mtime) {
+ if (mtime == null) {
+ return undefined
+ }
+
+ // Javascript Date
+ if (mtime instanceof Date) {
+ const ms = mtime.getTime()
+ const secs = Math.floor(ms / 1000)
+
+ return {
+ secs: secs,
+ nsecs: (ms - (secs * 1000)) * 1000
+ }
+ }
+
+ // { secs, nsecs }
+ if (Object.prototype.hasOwnProperty.call(mtime, 'secs')) {
+ return {
+ secs: mtime.secs,
+ nsecs: mtime.nsecs
+ }
+ }
+
+ // UnixFS TimeSpec
+ if (Object.prototype.hasOwnProperty.call(mtime, 'Seconds')) {
+ return {
+ secs: mtime.Seconds,
+ nsecs: mtime.FractionalNanoseconds
+ }
+ }
+
+ // process.hrtime()
+ if (Array.isArray(mtime)) {
+ return {
+ secs: mtime[0],
+ nsecs: mtime[1]
+ }
+ }
+ /*
+ TODO: https://github.com/ipfs/aegir/issues/487
+
+ // process.hrtime.bigint()
+ if (typeof mtime === 'bigint') {
+ const secs = mtime / BigInt(1e9)
+ const nsecs = mtime - (secs * BigInt(1e9))
+
+ return {
+ secs: parseInt(secs),
+ nsecs: parseInt(nsecs)
+ }
+ }
+ */
+}
diff --git a/src/lib/object-to-camel-with-metadata.js b/src/lib/object-to-camel-with-metadata.js
new file mode 100644
index 000000000..55f16d0bb
--- /dev/null
+++ b/src/lib/object-to-camel-with-metadata.js
@@ -0,0 +1,24 @@
+'use strict'
+
+const toCamel = require('./object-to-camel')
+
+function toCamelWithMetadata (entry) {
+ const file = toCamel(entry)
+
+ if (Object.prototype.hasOwnProperty.call(file, 'mode')) {
+ file.mode = parseInt(file.mode, 8)
+ }
+
+ if (Object.prototype.hasOwnProperty.call(file, 'mtime')) {
+ file.mtime = {
+ secs: file.mtime,
+ nsecs: file.mtimeNsecs || 0
+ }
+
+ delete file.mtimeNsecs
+ }
+
+ return file
+}
+
+module.exports = toCamelWithMetadata
diff --git a/src/ls.js b/src/ls.js
index a9cd476f9..43e92a54a 100644
--- a/src/ls.js
+++ b/src/ls.js
@@ -48,7 +48,7 @@ module.exports = configure(({ ky }) => {
}
for (const link of result) {
- yield {
+ const entry = {
name: link.Name,
path: path + '/' + link.Name,
size: link.Size,
@@ -56,6 +56,22 @@ module.exports = configure(({ ky }) => {
type: typeOf(link),
depth: link.Depth || 1
}
+
+ if (link.Mode) {
+ entry.mode = parseInt(link.Mode, 8)
+ }
+
+ if (link.Mtime !== undefined && link.Mtime !== null) {
+ entry.mtime = {
+ secs: link.Mtime
+ }
+
+ if (link.MtimeNsecs !== undefined && link.MtimeNsecs !== null) {
+ entry.mtime.nsecs = link.MtimeNsecs
+ }
+ }
+
+ yield entry
}
}
})
diff --git a/test/commands.spec.js b/test/commands.spec.js
index 2f7b1f5e3..9e68a1e01 100644
--- a/test/commands.spec.js
+++ b/test/commands.spec.js
@@ -2,30 +2,18 @@
'use strict'
const { expect } = require('interface-ipfs-core/src/utils/mocha')
-const ipfsClient = require('../src')
const f = require('./utils/factory')
describe('.commands', function () {
this.timeout(60 * 1000)
- let ipfsd
let ipfs
before(async () => {
- ipfsd = await f.spawn({
- initOptions: {
- bits: 1024,
- profile: 'test'
- }
- })
- ipfs = ipfsClient(ipfsd.apiAddr)
+ ipfs = (await f.spawn()).api
})
- after(async () => {
- if (ipfsd) {
- await ipfsd.stop()
- }
- })
+ after(() => f.clean())
it('lists commands', async () => {
const res = await ipfs.commands()
diff --git a/test/constructor.spec.js b/test/constructor.spec.js
index 60bfc9f33..a1a000c84 100644
--- a/test/constructor.spec.js
+++ b/test/constructor.spec.js
@@ -93,24 +93,18 @@ describe('ipfs-http-client constructor tests', () => {
})
describe('integration', () => {
- let apiAddr
let ipfsd
before(async function () {
this.timeout(60 * 1000) // slow CI
- ipfsd = await f.spawn({ initOptions: { bits: 1024, profile: 'test' } })
- apiAddr = ipfsd.apiAddr.toString()
+ ipfsd = await f.spawn()
})
- after(async () => {
- if (ipfsd) {
- await ipfsd.stop()
- }
- })
+ after(() => f.clean())
it('can connect to an ipfs http api', async () => {
- await clientWorks(ipfsClient(apiAddr))
+ await clientWorks(ipfsClient(ipfsd.apiAddr))
})
})
})
diff --git a/test/dag.spec.js b/test/dag.spec.js
index cd58002ad..de9217d13 100644
--- a/test/dag.spec.js
+++ b/test/dag.spec.js
@@ -6,29 +6,17 @@
const { expect } = require('interface-ipfs-core/src/utils/mocha')
const { DAGNode } = require('ipld-dag-pb')
const CID = require('cids')
-const ipfsClient = require('../src')
const f = require('./utils/factory')
-let ipfsd
let ipfs
describe('.dag', function () {
this.timeout(20 * 1000)
before(async function () {
- ipfsd = await f.spawn({
- initOptions: {
- bits: 1024,
- profile: 'test'
- }
- })
- ipfs = ipfsClient(ipfsd.apiAddr)
+ ipfs = (await f.spawn()).api
})
- after(async () => {
- if (ipfsd) {
- await ipfsd.stop()
- }
- })
+ after(() => f.clean())
it('should be able to put and get a DAG node with format dag-pb', async () => {
const data = Buffer.from('some data')
diff --git a/test/diag.spec.js b/test/diag.spec.js
index e42716214..37a1911e7 100644
--- a/test/diag.spec.js
+++ b/test/diag.spec.js
@@ -3,7 +3,6 @@
const { expect } = require('interface-ipfs-core/src/utils/mocha')
const platform = require('browser-process-platform')
-const ipfsClient = require('../src')
const f = require('./utils/factory')
describe('.diag', function () {
@@ -12,24 +11,13 @@ describe('.diag', function () {
// go-ipfs does not support these on Windows
if (platform === 'win32') { return }
- let ipfsd
let ipfs
before(async () => {
- ipfsd = await f.spawn({
- initOptions: {
- bits: 1024,
- profile: 'test'
- }
- })
- ipfs = ipfsClient(ipfsd.apiAddr)
+ ipfs = (await f.spawn()).api
})
- after(async () => {
- if (ipfsd) {
- await ipfsd.stop()
- }
- })
+ after(() => f.clean())
describe('api API', () => {
// Disabled in go-ipfs 0.4.10
diff --git a/test/files-mfs.spec.js b/test/files-mfs.spec.js
index 6ae508d1d..0e3d6b1af 100644
--- a/test/files-mfs.spec.js
+++ b/test/files-mfs.spec.js
@@ -10,7 +10,6 @@ const values = require('pull-stream/sources/values')
const pull = require('pull-stream/pull')
const collect = require('pull-stream/sinks/collect')
-const ipfsClient = require('../src')
const f = require('./utils/factory')
const expectTimeout = require('./utils/expect-timeout')
@@ -31,26 +30,15 @@ const HASH_ALGS = [
describe('.files (the MFS API part)', function () {
this.timeout(20 * 1000)
- let ipfsd
let ipfs
const expectedMultihash = 'Qma4hjFTnCasJ8PVp3mZbZK5g2vGDT4LByLJ7m8ciyRFZP'
before(async () => {
- ipfsd = await f.spawn({
- initOptions: {
- bits: 1024,
- profile: 'test'
- }
- })
- ipfs = ipfsClient(ipfsd.apiAddr)
+ ipfs = (await f.spawn()).api
})
- after(async () => {
- if (ipfsd) {
- await ipfsd.stop()
- }
- })
+ after(() => f.clean())
it('.add file for testing', async () => {
const res = await ipfs.add(testfile)
diff --git a/test/get.spec.js b/test/get.spec.js
index 7c1a63e74..a6fb3aaa4 100644
--- a/test/get.spec.js
+++ b/test/get.spec.js
@@ -6,7 +6,6 @@
const { expect } = require('interface-ipfs-core/src/utils/mocha')
const loadFixture = require('aegir/fixtures')
-const ipfsClient = require('../src')
const f = require('./utils/factory')
describe('.get (specific go-ipfs features)', function () {
@@ -21,26 +20,14 @@ describe('.get (specific go-ipfs features)', function () {
data: fixture('test/fixtures/testfile.txt')
}
- let ipfsd
let ipfs
before(async () => {
- ipfsd = await f.spawn({
- initOptions: {
- bits: 1024,
- profile: 'test'
- }
- })
- ipfs = ipfsClient(ipfsd.apiAddr)
-
+ ipfs = (await f.spawn()).api
await ipfs.add(smallFile.data)
})
- after(async () => {
- if (ipfsd) {
- await ipfsd.stop()
- }
- })
+ after(() => f.clean())
it('no compression args', async () => {
const files = await ipfs.get(smallFile.cid)
diff --git a/test/interface.spec.js b/test/interface.spec.js
index dc41ec325..e09cac5a6 100644
--- a/test/interface.spec.js
+++ b/test/interface.spec.js
@@ -2,47 +2,41 @@
'use strict'
const tests = require('interface-ipfs-core')
-const isNode = require('detect-node')
-const CommonFactory = require('./utils/interface-common-factory')
+const merge = require('merge-options')
+const { isNode } = require('ipfs-utils/src/env')
+const { createFactory } = require('ipfsd-ctl')
+const { findBin } = require('ipfsd-ctl/src/utils')
const isWindows = process.platform && process.platform === 'win32'
+/** @typedef {import("ipfsd-ctl").ControllerOptions} ControllerOptions */
+
describe('interface-ipfs-core tests', () => {
- const defaultCommonFactory = CommonFactory.createAsync()
+ /** @type ControllerOptions */
+ const commonOptions = {
+ test: true,
+ ipfsHttpModule: {
+ path: require.resolve('../src'),
+ ref: require('../src')
+ },
+ ipfsOptions: {
+ pass: 'ipfs-is-awesome-software'
+ },
+ ipfsBin: findBin('go')
+ }
+ const commonFactory = createFactory(commonOptions)
- tests.bitswap(defaultCommonFactory, {
- skip: [
- // bitswap.stat
- {
- name: 'should not get bitswap stats when offline',
- reason: 'FIXME go-ipfs returns an error https://github.com/ipfs/go-ipfs/issues/4078'
- },
- // bitswap.wantlist
- {
- name: 'should not get the wantlist when offline',
- reason: 'FIXME go-ipfs returns an error https://github.com/ipfs/go-ipfs/issues/4078'
- },
- // bitswap.unwant
- {
- name: 'should remove a key from the wantlist',
- reason: 'FIXME why is this skipped?'
- },
- {
- name: 'should not remove a key from the wantlist when offline',
- reason: 'FIXME go-ipfs returns an error https://github.com/ipfs/go-ipfs/issues/4078'
- }
- ]
- })
+ tests.bitswap(commonFactory)
- tests.block(defaultCommonFactory, {
+ tests.block(commonFactory, {
skip: [{
name: 'should get a block added as CIDv1 with a CIDv0',
reason: 'go-ipfs does not support the `version` param'
}]
})
- tests.bootstrap(defaultCommonFactory)
+ tests.bootstrap(commonFactory)
- tests.config(defaultCommonFactory, {
+ tests.config(commonFactory, {
skip: [
// config.replace
{
@@ -60,7 +54,7 @@ describe('interface-ipfs-core tests', () => {
]
})
- tests.dag(defaultCommonFactory, {
+ tests.dag(commonFactory, {
skip: [
// dag.tree
{
@@ -87,7 +81,7 @@ describe('interface-ipfs-core tests', () => {
]
})
- tests.dht(defaultCommonFactory, {
+ tests.dht(commonFactory, {
skip: [
// dht.findpeer
{
@@ -107,47 +101,203 @@ describe('interface-ipfs-core tests', () => {
]
})
- tests.filesRegular(defaultCommonFactory, {
+ tests.filesMFS(commonFactory, {
skip: [
- // .addFromFs
- isNode ? null : {
- name: 'addFromFs',
- reason: 'Not designed to run in the browser'
+ {
+ name: 'should ls directory with long option',
+ reason: 'TODO unskip when go-ipfs supports --long https://github.com/ipfs/go-ipfs/pull/6528'
},
- // .catPullStream
{
- name: 'should export a chunk of a file',
+ name: 'should read from outside of mfs',
reason: 'TODO not implemented in go-ipfs yet'
},
{
- name: 'should export a chunk of a file in a Pull Stream',
+ name: 'should ls from outside of mfs',
reason: 'TODO not implemented in go-ipfs yet'
},
{
- name: 'should export a chunk of a file in a Readable Stream',
+ name: 'should change file mode',
+ reason: 'TODO not implemented in go-ipfs yet'
+ },
+ {
+ name: 'should change directory mode',
+ reason: 'TODO not implemented in go-ipfs yet'
+ },
+ {
+ name: 'should change file mode as string',
+ reason: 'TODO not implemented in go-ipfs yet'
+ },
+ {
+ name: 'should change file mode to 0',
+ reason: 'TODO not implemented in go-ipfs yet'
+ },
+ {
+ name: 'should update file mtime',
+ reason: 'TODO not implemented in go-ipfs yet'
+ },
+ {
+ name: 'should update directory mtime',
+ reason: 'TODO not implemented in go-ipfs yet'
+ },
+ {
+ name: 'should make directory and specify mode',
+ reason: 'TODO not implemented in go-ipfs yet'
+ },
+ {
+ name: 'should make directory and specify mtime',
+ reason: 'TODO not implemented in go-ipfs yet'
+ },
+ {
+ name: 'should write file and specify mode',
+ reason: 'TODO not implemented in go-ipfs yet'
+ },
+ {
+ name: 'should write file and specify mtime',
+ reason: 'TODO not implemented in go-ipfs yet'
+ },
+ {
+ name: 'should respect metadata when copying files',
+ reason: 'TODO not implemented in go-ipfs yet'
+ },
+ {
+ name: 'should respect metadata when copying directories',
+ reason: 'TODO not implemented in go-ipfs yet'
+ },
+ {
+ name: 'should respect metadata when copying from outside of mfs',
+ reason: 'TODO not implemented in go-ipfs yet'
+ },
+ {
+ name: 'ls directory with long option should include metadata',
+ reason: 'TODO not implemented in go-ipfs yet'
+ },
+ {
+ name: 'should have default mtime',
+ reason: 'TODO not implemented in go-ipfs yet'
+ },
+ {
+ name: 'should set mtime as Date',
+ reason: 'TODO not implemented in go-ipfs yet'
+ },
+ {
+ name: 'should set mtime as { nsecs, secs }',
+ reason: 'TODO not implemented in go-ipfs yet'
+ },
+ {
+ name: 'should set mtime as timespec',
+ reason: 'TODO not implemented in go-ipfs yet'
+ },
+ {
+ name: 'should set mtime as hrtime',
+ reason: 'TODO not implemented in go-ipfs yet'
+ },
+ {
+ name: 'should make directory and have default mode',
+ reason: 'TODO not implemented in go-ipfs yet'
+ },
+ {
+ name: 'should make directory and specify mode as string',
+ reason: 'TODO not implemented in go-ipfs yet'
+ },
+ {
+ name: 'should make directory and specify mode as number',
+ reason: 'TODO not implemented in go-ipfs yet'
+ },
+ {
+ name: 'should make directory and specify mtime as Date',
+ reason: 'TODO not implemented in go-ipfs yet'
+ },
+ {
+ name: 'should make directory and specify mtime as { nsecs, secs }',
+ reason: 'TODO not implemented in go-ipfs yet'
+ },
+ {
+ name: 'should make directory and specify mtime as timespec',
+ reason: 'TODO not implemented in go-ipfs yet'
+ },
+ {
+ name: 'should make directory and specify mtime as hrtime',
+ reason: 'TODO not implemented in go-ipfs yet'
+ },
+ {
+ name: 'should write file and specify mode as a string',
+ reason: 'TODO not implemented in go-ipfs yet'
+ },
+ {
+ name: 'should write file and specify mode as a number',
+ reason: 'TODO not implemented in go-ipfs yet'
+ },
+ {
+ name: 'should write file and specify mtime as Date',
+ reason: 'TODO not implemented in go-ipfs yet'
+ },
+ {
+ name: 'should write file and specify mtime as { nsecs, secs }',
+ reason: 'TODO not implemented in go-ipfs yet'
+ },
+ {
+ name: 'should write file and specify mtime as timespec',
+ reason: 'TODO not implemented in go-ipfs yet'
+ },
+ {
+ name: 'should write file and specify mtime as hrtime',
reason: 'TODO not implemented in go-ipfs yet'
}
]
})
- tests.filesMFS(defaultCommonFactory, {
+ tests.filesRegular(commonFactory, {
skip: [
+ // .addFromFs
+ isNode ? null : {
+ name: 'addFromFs',
+ reason: 'Not designed to run in the browser'
+ },
{
- name: 'should ls directory with long option',
- reason: 'TODO unskip when go-ipfs supports --long https://github.com/ipfs/go-ipfs/pull/6528'
+ name: 'should add with mode as string',
+ reason: 'TODO not implemented in go-ipfs yet'
},
{
- name: 'should read from outside of mfs',
+ name: 'should add with mode as number',
reason: 'TODO not implemented in go-ipfs yet'
},
{
- name: 'should ls from outside of mfs',
+ name: 'should add with mtime as Date',
+ reason: 'TODO not implemented in go-ipfs yet'
+ },
+ {
+ name: 'should add with mtime as { nsecs, secs }',
+ reason: 'TODO not implemented in go-ipfs yet'
+ },
+ {
+ name: 'should add with mtime as timespec',
+ reason: 'TODO not implemented in go-ipfs yet'
+ },
+ {
+ name: 'should add with mtime as hrtime',
+ reason: 'TODO not implemented in go-ipfs yet'
+ },
+ // .catPullStream
+ {
+ name: 'should export a chunk of a file',
+ reason: 'TODO not implemented in go-ipfs yet'
+ },
+ {
+ name: 'should export a chunk of a file in a Pull Stream',
+ reason: 'TODO not implemented in go-ipfs yet'
+ },
+ {
+ name: 'should export a chunk of a file in a Readable Stream',
+ reason: 'TODO not implemented in go-ipfs yet'
+ },
+ {
+ name: 'should ls with metadata',
reason: 'TODO not implemented in go-ipfs yet'
}
]
})
- tests.key(defaultCommonFactory, {
+ tests.key(commonFactory, {
skip: [
// key.export
{
@@ -162,21 +312,15 @@ describe('interface-ipfs-core tests', () => {
]
})
- tests.miscellaneous(defaultCommonFactory, {
- skip: [
- // stop
- {
- name: 'should stop the node',
- reason: 'FIXME go-ipfs returns an error https://github.com/ipfs/go-ipfs/issues/4078'
- }
- ]
- })
+ tests.miscellaneous(commonFactory)
- tests.name(CommonFactory.createAsync({
- spawnOptions: {
- args: ['--offline']
+ tests.name(createFactory(merge(commonOptions,
+ {
+ ipfsOptions: {
+ offline: true
+ }
}
- }), {
+ )), {
skip: [
// stop
{
@@ -186,12 +330,15 @@ describe('interface-ipfs-core tests', () => {
]
})
- tests.namePubsub(CommonFactory.createAsync({
- spawnOptions: {
- args: ['--enable-namesys-pubsub'],
- initOptions: { bits: 1024, profile: 'test' }
+ tests.namePubsub(createFactory(merge(commonOptions,
+ {
+ ipfsOptions: {
+ EXPERIMENTAL: {
+ ipnsPubsub: true
+ }
+ }
}
- }), {
+ )), {
skip: [
// name.pubsub.cancel
{
@@ -206,11 +353,11 @@ describe('interface-ipfs-core tests', () => {
]
})
- tests.object(defaultCommonFactory)
+ tests.object(commonFactory)
- tests.pin(defaultCommonFactory)
+ tests.pin(commonFactory)
- tests.ping(defaultCommonFactory, {
+ tests.ping(commonFactory, {
skip: [
{
name: 'should fail when pinging an unknown peer over pull stream',
@@ -227,10 +374,9 @@ describe('interface-ipfs-core tests', () => {
]
})
- tests.pubsub(CommonFactory.createAsync({
- spawnOptions: {
- args: ['--enable-pubsub-experiment'],
- initOptions: { bits: 1024, profile: 'test' }
+ tests.pubsub(createFactory(commonOptions, {
+ go: {
+ args: ['--enable-pubsub-experiment']
}
}), {
skip: isWindows ? [
@@ -246,9 +392,9 @@ describe('interface-ipfs-core tests', () => {
] : null
})
- tests.repo(defaultCommonFactory)
+ tests.repo(commonFactory)
- tests.stats(defaultCommonFactory)
+ tests.stats(commonFactory)
- tests.swarm(defaultCommonFactory)
+ tests.swarm(commonFactory)
})
diff --git a/test/key.spec.js b/test/key.spec.js
index 2e4e15714..180cb795b 100644
--- a/test/key.spec.js
+++ b/test/key.spec.js
@@ -1,32 +1,19 @@
/* eslint-env mocha */
-/* eslint max-nested-callbacks: ["error", 8] */
'use strict'
const { expect } = require('interface-ipfs-core/src/utils/mocha')
-const ipfsClient = require('../src')
const f = require('./utils/factory')
describe('.key', function () {
this.timeout(50 * 1000)
- let ipfsd
let ipfs
before(async () => {
- ipfsd = await f.spawn({
- initOptions: {
- bits: 1024,
- profile: 'test'
- }
- })
- ipfs = ipfsClient(ipfsd.apiAddr)
+ ipfs = (await f.spawn()).api
})
- after(async () => {
- if (ipfsd) {
- await ipfsd.stop()
- }
- })
+ after(() => f.clean())
describe('.gen', () => {
it('create a new rsa key', async () => {
diff --git a/test/log.spec.js b/test/log.spec.js
index 7f8e2c608..1a885028d 100644
--- a/test/log.spec.js
+++ b/test/log.spec.js
@@ -3,30 +3,18 @@
'use strict'
const { expect } = require('interface-ipfs-core/src/utils/mocha')
-const ipfsClient = require('../src')
const f = require('./utils/factory')
describe('.log', function () {
this.timeout(100 * 1000)
- let ipfsd
let ipfs
before(async () => {
- ipfsd = await f.spawn({
- initOptions: {
- bits: 1024,
- profile: 'test'
- }
- })
- ipfs = ipfsClient(ipfsd.apiAddr)
+ ipfs = (await f.spawn()).api
})
- after(async () => {
- if (ipfsd) {
- await ipfsd.stop()
- }
- })
+ after(() => f.clean())
it('.log.tail', async () => {
const i = setInterval(async () => {
diff --git a/test/ping.spec.js b/test/ping.spec.js
index 10c131b75..01636ed60 100644
--- a/test/ping.spec.js
+++ b/test/ping.spec.js
@@ -5,7 +5,6 @@ const { expect } = require('interface-ipfs-core/src/utils/mocha')
const pull = require('pull-stream/pull')
const collect = require('pull-stream/sinks/collect')
-const ipfsClient = require('../src')
const f = require('./utils/factory')
// Determine if a ping response object is a pong, or something else, like a status message
@@ -17,29 +16,14 @@ describe('.ping', function () {
this.timeout(20 * 1000)
let ipfs
- let ipfsd
let other
- let otherd
let otherId
before(async function () {
this.timeout(30 * 1000) // slow CI
- ipfsd = await f.spawn({
- initOptions: {
- bits: 1024,
- profile: 'test'
- }
- })
- ipfs = ipfsClient(ipfsd.apiAddr)
-
- otherd = await f.spawn({
- initOptions: {
- bits: 1024,
- profile: 'test'
- }
- })
- other = otherd.api
+ ipfs = (await f.spawn()).api
+ other = (await f.spawn()).api
const ma = (await ipfs.id()).addresses[0]
await other.swarm.connect(ma)
@@ -47,15 +31,7 @@ describe('.ping', function () {
otherId = (await other.id()).id
})
- after(async () => {
- if (ipfsd) {
- await ipfsd.stop()
- }
-
- if (otherd) {
- await otherd.stop()
- }
- })
+ after(() => f.clean())
it('.ping with default count', async () => {
const res = await ipfs.ping(otherId)
diff --git a/test/repo.spec.js b/test/repo.spec.js
index 84b5a42c3..21482135d 100644
--- a/test/repo.spec.js
+++ b/test/repo.spec.js
@@ -2,30 +2,18 @@
'use strict'
const { expect } = require('interface-ipfs-core/src/utils/mocha')
-const ipfsClient = require('../src')
const f = require('./utils/factory')
describe('.repo', function () {
this.timeout(50 * 1000) // slow CI
let ipfs
- let ipfsd
before(async () => {
- ipfsd = await f.spawn({
- initOptions: {
- bits: 1024,
- profile: 'test'
- }
- })
- ipfs = ipfsClient(ipfsd.apiAddr)
+ ipfs = (await f.spawn()).api
})
- after(async () => {
- if (ipfsd) {
- await ipfsd.stop()
- }
- })
+ after(() => f.clean())
it('.repo.gc', async () => {
const res = await ipfs.repo.gc()
diff --git a/test/stats.spec.js b/test/stats.spec.js
index 0b7084d0a..d60aaa330 100644
--- a/test/stats.spec.js
+++ b/test/stats.spec.js
@@ -2,30 +2,18 @@
'use strict'
const { expect } = require('interface-ipfs-core/src/utils/mocha')
-const ipfsClient = require('../src')
const f = require('./utils/factory')
describe('stats', function () {
this.timeout(50 * 1000) // slow CI
let ipfs
- let ipfsd
before(async () => {
- ipfsd = await f.spawn({
- initOptions: {
- bits: 1024,
- profile: 'test'
- }
- })
- ipfs = ipfsClient(ipfsd.apiAddr)
+ ipfs = (await f.spawn()).api
})
- after(async () => {
- if (ipfsd) {
- await ipfsd.stop()
- }
- })
+ after(() => f.clean())
it('.stats.bitswap', async () => {
const res = await ipfs.stats.bitswap()
diff --git a/test/utils/factory.js b/test/utils/factory.js
index aab6296fe..d6ac161ea 100644
--- a/test/utils/factory.js
+++ b/test/utils/factory.js
@@ -1,5 +1,15 @@
'use strict'
+const { createFactory } = require('ipfsd-ctl')
+const { findBin } = require('ipfsd-ctl/src/utils')
-const IPFSFactory = require('ipfsd-ctl')
+const factory = createFactory({
+ test: 'true',
+ type: 'go',
+ ipfsBin: findBin('go'),
+ ipfsHttpModule: {
+ path: require.resolve('../../src'),
+ ref: require('../../src')
+ }
+})
-module.exports = IPFSFactory.create()
+module.exports = factory
diff --git a/test/utils/interface-common-factory.js b/test/utils/interface-common-factory.js
deleted file mode 100644
index bd4720b6e..000000000
--- a/test/utils/interface-common-factory.js
+++ /dev/null
@@ -1,92 +0,0 @@
-/* eslint-env mocha */
-'use strict'
-
-const each = require('async/each')
-const IPFSFactory = require('ipfsd-ctl')
-const ipfsClient = require('../../src')
-const merge = require('merge-options')
-
-const DEFAULT_FACTORY_OPTIONS = {
- IpfsClient: ipfsClient
-}
-
-function createFactory (options) {
- options = options || {}
-
- options.factoryOptions = options.factoryOptions || { ...DEFAULT_FACTORY_OPTIONS }
- options.spawnOptions = options.spawnOptions || { initOptions: { bits: 1024, profile: 'test' } }
-
- const ipfsFactory = IPFSFactory.create(options.factoryOptions)
-
- return function createCommon () {
- const nodes = []
- let setup, teardown
-
- if (options.createSetup) {
- setup = options.createSetup({ ipfsFactory, nodes }, options)
- } else {
- setup = (callback) => {
- callback(null, {
- spawnNode (cb) {
- ipfsFactory.spawn(options.spawnOptions)
- .then((ipfsd) => {
- nodes.push(ipfsd)
- setImmediate(() => cb(null, ipfsd.api))
- })
- .catch(err => {
- setImmediate(() => cb(err))
- })
- }
- })
- }
- }
-
- if (options.createTeardown) {
- teardown = options.createTeardown({ ipfsFactory, nodes }, options)
- } else {
- teardown = callback => each(nodes, (node, cb) => {
- node
- .stop()
- .then(() => setImmediate(() => cb()))
- .catch(err => setImmediate(() => cb(err)))
- }, callback)
- }
-
- return { setup, teardown }
- }
-}
-
-function createAsync (options = {}) {
- return () => {
- const nodes = []
- const setup = async (setupOptions = {}) => {
- const ipfsFactory = IPFSFactory.create(merge(
- options.factoryOptions ? {} : { ...DEFAULT_FACTORY_OPTIONS },
- setupOptions.factoryOptions,
- options.factoryOptions
- ))
- const node = await ipfsFactory.spawn(merge(
- setupOptions.spawnOptions,
- options.spawnOptions || { initOptions: { profile: 'test' } }
- ))
- nodes.push(node)
-
- const id = await node.api.id()
- node.api.peerId = id
-
- return node.api
- }
-
- const teardown = () => {
- return Promise.all(nodes.map(n => n.stop()))
- }
- return {
- setup,
- teardown
- }
- }
-}
-module.exports = {
- createAsync,
- create: createFactory
-}