diff --git a/package.json b/package.json index 6666894..8d81ad4 100644 --- a/package.json +++ b/package.json @@ -42,7 +42,7 @@ "detect-node": "^2.0.3", "detect-webworker": "^1.0.0", "dirty-chai": "^2.0.1", - "ipfs": "~0.30.0", + "ipfs": "~0.31.2", "pull-buffer-stream": "^1.0.0", "safe-buffer": "^5.1.1", "tmp": "~0.0.33" @@ -56,7 +56,7 @@ "filereader-stream": "^2.0.0", "interface-datastore": "~0.4.2", "ipfs-unixfs": "~0.1.15", - "ipfs-unixfs-engine": "~0.31.3", + "ipfs-unixfs-engine": "~0.32.1", "is-pull-stream": "~0.0.0", "is-stream": "^1.1.0", "joi": "^13.4.0", @@ -65,6 +65,7 @@ "once": "^1.4.0", "promisify-es6": "^1.0.3", "pull-cat": "^1.1.11", + "pull-defer": "~0.2.2", "pull-paramap": "^1.2.2", "pull-pushable": "^2.2.0", "pull-stream": "^3.6.8", diff --git a/src/cli/ls.js b/src/cli/ls.js index 5532dd0..bd9a6b0 100644 --- a/src/cli/ls.js +++ b/src/cli/ls.js @@ -21,6 +21,13 @@ module.exports = { coerce: asBoolean, describe: 'Use long listing format.' }, + unsorted: { + alias: 'U', + type: 'boolean', + default: false, + coerce: asBoolean, + describe: 'Do not sort; list entries in directory order.' + }, cidBase: { alias: 'cid-base', default: 'base58btc', @@ -33,12 +40,14 @@ module.exports = { path, ipfs, long, + unsorted, cidBase } = argv argv.resolve( ipfs.files.ls(path || FILE_SEPARATOR, { long, + unsorted, cidBase }) .then(files => { diff --git a/src/core/index.js b/src/core/index.js index a96859e..f6712b8 100644 --- a/src/core/index.js +++ b/src/core/index.js @@ -5,28 +5,38 @@ const { createLock } = require('./utils') +// These operations are read-locked at the function level and will execute simultaneously const readOperations = { ls: require('./ls'), - read: require('./read'), - readPullStream: require('./read-pull-stream'), - readReadableStream: require('./read-readable-stream'), stat: require('./stat') } +// These operations are locked at the function level and will execute in series const writeOperations = { cp: require('./cp'), flush: require('./flush'), mkdir: require('./mkdir'), mv: require('./mv'), - rm: require('./rm'), - write: require('./write') + rm: require('./rm') +} + +// These operations are asynchronous and manage their own locking +const upwrappedOperations = { + write: require('./write'), + read: require('./read') +} + +// These operations are synchronous and manage their own locking +const upwrappedSynchronousOperations = { + readPullStream: require('./read-pull-stream'), + readReadableStream: require('./read-readable-stream') } -const wrap = (ipfs, mfs, operations, lock) => { +const wrap = ({ + ipfs, mfs, operations, lock +}) => { Object.keys(operations).forEach(key => { - if (operations.hasOwnProperty(key)) { - mfs[key] = promisify(lock(operations[key](ipfs))) - } + mfs[key] = promisify(lock(operations[key](ipfs))) }) } @@ -51,8 +61,20 @@ module.exports = (ipfs, options) => { const mfs = {} - wrap(ipfs, mfs, readOperations, readLock) - wrap(ipfs, mfs, writeOperations, writeLock) + wrap({ + ipfs, mfs, operations: readOperations, lock: readLock + }) + wrap({ + ipfs, mfs, operations: writeOperations, lock: writeLock + }) + + Object.keys(upwrappedOperations).forEach(key => { + mfs[key] = promisify(upwrappedOperations[key](ipfs)) + }) + + Object.keys(upwrappedSynchronousOperations).forEach(key => { + mfs[key] = upwrappedSynchronousOperations[key](ipfs) + }) return mfs } diff --git a/src/core/ls.js b/src/core/ls.js index 9d4f103..0f89a75 100644 --- a/src/core/ls.js +++ b/src/core/ls.js @@ -13,7 +13,8 @@ const { const defaultOptions = { long: false, - cidBase: 'base58btc' + cidBase: 'base58btc', + unsorted: false } module.exports = (ipfs) => { @@ -64,6 +65,17 @@ module.exports = (ipfs) => { } }, + // https://github.com/ipfs/go-ipfs/issues/5181 + (files, cb) => { + if (options.unsorted) { + return cb(null, files) + } + + return cb(null, files.sort((a, b) => { + return b.name.localeCompare(a.name) + })) + }, + // https://github.com/ipfs/go-ipfs/issues/5026 (files, cb) => cb(null, files.map(file => { if (FILE_TYPES.hasOwnProperty(file.type)) { @@ -77,18 +89,7 @@ module.exports = (ipfs) => { } return file - })), - - // https://github.com/ipfs/go-ipfs/issues/5181 - (files, cb) => { - if (options.long) { - return cb(null, files.sort((a, b) => { - return b.name.localeCompare(a.name) - })) - } - - cb(null, files) - } + })) ], callback) } } diff --git a/src/core/mkdir.js b/src/core/mkdir.js index 8370645..2ac62a9 100644 --- a/src/core/mkdir.js +++ b/src/core/mkdir.js @@ -50,8 +50,8 @@ module.exports = (ipfs) => { return cb(new Error('file already exists')) } - if (error.message.includes('did not exist')) { - log(`${path} did not exist`) + if (error.message.includes('does not exist')) { + log(`${path} does not exist`) return cb() } diff --git a/src/core/read-pull-stream.js b/src/core/read-pull-stream.js index fbaddc9..eaa517c 100644 --- a/src/core/read-pull-stream.js +++ b/src/core/read-pull-stream.js @@ -2,11 +2,14 @@ const exporter = require('ipfs-unixfs-engine').exporter const pull = require('pull-stream/pull') +const once = require('pull-stream/sources/once') +const asyncMap = require('pull-stream/throughs/async-map') +const defer = require('pull-defer') const collect = require('pull-stream/sinks/collect') -const waterfall = require('async/waterfall') const UnixFs = require('ipfs-unixfs') const { - traverseTo + traverseTo, + createLock } = require('./utils') const log = require('debug')('ipfs:mfs:read-pull-stream') @@ -16,39 +19,49 @@ const defaultOptions = { } module.exports = (ipfs) => { - return function mfsReadPullStream (path, options, callback) { - if (typeof options === 'function') { - callback = options - options = {} - } - + return function mfsReadPullStream (path, options = {}) { options = Object.assign({}, defaultOptions, options) log(`Reading ${path}`) - waterfall([ - (done) => traverseTo(ipfs, path, { - parents: false - }, done), - (result, done) => { + const deferred = defer.source() + + pull( + once(path), + asyncMap((path, cb) => { + createLock().readLock((next) => { + traverseTo(ipfs, path, { + parents: false + }, next) + })(cb) + }), + asyncMap((result, cb) => { const node = result.node const meta = UnixFs.unmarshal(node.data) if (meta.type !== 'file') { - return done(new Error(`${path} was not a file`)) + return cb(new Error(`${path} was not a file`)) } - waterfall([ - (next) => pull( - exporter(node.multihash, ipfs.dag, { - offset: options.offset, - length: options.length - }), - collect(next) - ), - (files, next) => next(null, files[0].content) - ], done) - } - ], callback) + pull( + exporter(node.multihash, ipfs.dag, { + offset: options.offset, + length: options.length + }), + collect((error, files) => { + cb(error, error ? null : files[0].content) + }) + ) + }), + collect((error, streams) => { + if (error) { + return deferred.abort(error) + } + + deferred.resolve(streams[0]) + }) + ) + + return deferred } } diff --git a/src/core/read-readable-stream.js b/src/core/read-readable-stream.js index 938cd6f..50eceb2 100644 --- a/src/core/read-readable-stream.js +++ b/src/core/read-readable-stream.js @@ -1,19 +1,10 @@ 'use strict' -const waterfall = require('async/waterfall') const readPullStream = require('./read-pull-stream') const toStream = require('pull-stream-to-stream') module.exports = (ipfs) => { - return function mfsReadReadableStream (path, options, callback) { - if (typeof options === 'function') { - callback = options - options = {} - } - - waterfall([ - (cb) => readPullStream(ipfs)(path, options, cb), - (stream, cb) => cb(null, toStream.source(stream)) - ], callback) + return function mfsReadReadableStream (path, options = {}) { + return toStream.source(readPullStream(ipfs)(path, options)) } } diff --git a/src/core/read.js b/src/core/read.js index f12b3b4..df924da 100644 --- a/src/core/read.js +++ b/src/core/read.js @@ -2,7 +2,6 @@ const pull = require('pull-stream/pull') const collect = require('pull-stream/sinks/collect') -const waterfall = require('async/waterfall') const readPullStream = require('./read-pull-stream') module.exports = (ipfs) => { @@ -12,15 +11,15 @@ module.exports = (ipfs) => { options = {} } - waterfall([ - (cb) => readPullStream(ipfs)(path, options, cb), - (stream, cb) => pull( - stream, - collect(cb) - ), - (buffers, cb) => { - cb(null, Buffer.concat(buffers)) - } - ], callback) + pull( + readPullStream(ipfs)(path, options), + collect((error, buffers) => { + if (error) { + return callback(error) + } + + return callback(null, Buffer.concat(buffers)) + }) + ) } } diff --git a/src/core/utils/create-lock.js b/src/core/utils/create-lock.js index c9def4c..f3ddcdf 100644 --- a/src/core/utils/create-lock.js +++ b/src/core/utils/create-lock.js @@ -3,7 +3,13 @@ const mortice = require('mortice') const log = require('debug')('ipfs:mfs:lock') +let lock + module.exports = (repoOwner) => { + if (lock) { + return lock + } + const mutex = mortice({ // ordinarily the main thread would store the read/write lock but // if we are the thread that owns the repo, we can store the lock @@ -17,7 +23,8 @@ module.exports = (repoOwner) => { mutex[`${type}Lock`](() => { return new Promise((resolve, reject) => { args.push((error, result) => { - log(`${type} operation callback invoked${error ? ' with error' : ''}`) + log(`${type} operation callback invoked${error ? ' with error: ' + error.message : ''}`) + if (error) { return reject(error) } @@ -35,7 +42,7 @@ module.exports = (repoOwner) => { cb(null, result) }) .catch((error) => { - log(`Finished ${type} operation with error`) + log(`Finished ${type} operation with error: ${error.message}`) if (callback) { return callback(error) } @@ -46,7 +53,7 @@ module.exports = (repoOwner) => { }) } - return { + lock = { readLock: (func) => { return function () { const args = Array.from(arguments) @@ -65,4 +72,6 @@ module.exports = (repoOwner) => { } } } + + return lock } diff --git a/src/core/utils/index.js b/src/core/utils/index.js index e186fd5..2b4a5c0 100644 --- a/src/core/utils/index.js +++ b/src/core/utils/index.js @@ -11,6 +11,7 @@ module.exports = { formatCid: require('./format-cid'), limitStreamBytes: require('./limit-stream-bytes'), loadNode: require('./load-node'), + toPullSource: require('./to-pull-source'), toSourcesAndDestination: require('./to-sources-and-destination'), toSources: require('./to-sources'), traverseTo: require('./traverse-to'), diff --git a/src/core/utils/to-pull-source.js b/src/core/utils/to-pull-source.js new file mode 100644 index 0000000..94e1dec --- /dev/null +++ b/src/core/utils/to-pull-source.js @@ -0,0 +1,66 @@ +'use strict' + +const toPull = require('stream-to-pull-stream') +const isStream = require('is-stream') +const fileReaderStream = require('filereader-stream') +const isPullStream = require('is-pull-stream') +const fs = require('fs') +const values = require('pull-stream/sources/values') +const log = require('debug')('ipfs:mfs:utils:to-pull-source') +const waterfall = require('async/waterfall') + +const toPullSource = (content, options, callback) => { + if (!content) { + return callback(new Error('paths must start with a leading /')) + } + + // Buffers + if (Buffer.isBuffer(content)) { + log('Content was a buffer') + + options.length = options.length || content.length + + return callback(null, values([content])) + } + + // Paths, node only + if (typeof content === 'string' || content instanceof String) { + log('Content was a path') + + // Find out the file size if options.length has not been specified + return waterfall([ + (done) => options.length ? done(null, { + size: options.length + }) : fs.stat(content, done), + (stats, done) => { + options.length = stats.size + + done(null, toPull.source(fs.createReadStream(content))) + } + ], callback) + } + + // HTML5 Blob objects (including Files) + if (global.Blob && content instanceof global.Blob) { + log('Content was an HTML5 Blob') + options.length = options.length || content.size + + content = fileReaderStream(content) + } + + // Node streams + if (isStream(content)) { + log('Content was a Node stream') + return callback(null, toPull.source(content)) + } + + // Pull stream + if (isPullStream.isSource(content)) { + log('Content was a pull-stream') + return callback(null, content) + } + + callback(new Error(`Don't know how to convert ${content} into a pull stream source`)) +} + +module.exports = toPullSource diff --git a/src/core/utils/traverse-to.js b/src/core/utils/traverse-to.js index f4dfd51..c1949e3 100644 --- a/src/core/utils/traverse-to.js +++ b/src/core/utils/traverse-to.js @@ -84,9 +84,9 @@ const traverseToMfsObject = (ipfs, path, options, callback) => { log(`index ${index} pathSegments.length ${pathSegments.length} pathSegment ${pathSegment} lastComponent ${lastComponent}`, options) if (lastComponent && !options.createLastComponent) { - return done(new Error(`Path ${path.path} did not exist`)) + return done(new Error(`Path ${path.path} does not exist`)) } else if (!lastComponent && !options.parents) { - let message = `Cannot find ${path.path} - ${pathSegment} did not exist` + let message = `Cannot find ${path.path} - ${pathSegment} does not exist` if (options.withCreateHint) { message += ': Try again with the --parents flag to create it' diff --git a/src/core/write/index.js b/src/core/write/index.js index 2856f67..fb53fed 100644 --- a/src/core/write/index.js +++ b/src/core/write/index.js @@ -5,24 +5,21 @@ const CID = require('cids') const waterfall = require('async/waterfall') const parallel = require('async/parallel') const { + createLock, updateMfsRoot, validatePath, traverseTo, addLink, updateTree, - limitStreamBytes + limitStreamBytes, + toPullSource } = require('../utils') const values = require('pull-stream/sources/values') const log = require('debug')('ipfs:mfs:write') const importNode = require('./import-node') const updateNode = require('./update-node') -const toPull = require('stream-to-pull-stream') -const isStream = require('is-stream') -const fileReaderStream = require('filereader-stream') -const isPullStream = require('is-pull-stream') const cat = require('pull-cat') const pull = require('pull-stream/pull') -const fs = require('fs') const defaultOptions = { offset: 0, // the offset in the file to begin writing @@ -41,60 +38,6 @@ const defaultOptions = { leafType: 'raw' } -const toPullSource = (content, options, callback) => { - if (!content) { - return callback(new Error('paths must start with a leading /')) - } - - // Buffers - if (Buffer.isBuffer(content)) { - log('Content was a buffer') - - options.length = options.length || content.length - - return callback(null, values([content])) - } - - // Paths, node only - if (typeof content === 'string' || content instanceof String) { - log('Content was a path') - - // Find out the file size if options.length has not been specified - return waterfall([ - (done) => options.length ? done(null, { - size: options.length - }) : fs.stat(content, done), - (stats, done) => { - options.length = stats.size - - done(null, toPull.source(fs.createReadStream(content))) - } - ], callback) - } - - // HTML5 Blob objects (including Files) - if (global.Blob && content instanceof global.Blob) { - log('Content was an HTML5 Blob') - options.length = options.length || content.size - - content = fileReaderStream(content) - } - - // Node streams - if (isStream(content)) { - log('Content was a Node stream') - return callback(null, toPull.source(content)) - } - - // Pull stream - if (isPullStream.isSource(content)) { - log('Content was a pull-stream') - return callback(null, content) - } - - callback(new Error(`Don't know how to convert ${content} into a pull stream source`)) -} - module.exports = function mfsWrite (ipfs) { return promisify((path, content, options, callback) => { if (typeof options === 'function') { @@ -132,10 +75,22 @@ module.exports = function mfsWrite (ipfs) { // walk the mfs tree to the containing folder node ([source, path], done) => { waterfall([ - (next) => traverseTo(ipfs, path.directory, Object.assign({}, options, { - createLastComponent: options.parents - }), (error, result) => next(error, source, result)), - (source, containingFolder, next) => { + (next) => { + const opts = Object.assign({}, options, { + createLastComponent: options.parents + }) + + if (opts.createLastComponent) { + createLock().writeLock((callback) => { + traverseTo(ipfs, path.directory, opts, (error, result) => callback(error, { source, containingFolder: result })) + })(next) + } else { + createLock().readLock((callback) => { + traverseTo(ipfs, path.directory, opts, (error, result) => callback(error, { source, containingFolder: result })) + })(next) + } + }, + ({ source, containingFolder }, next) => { updateOrImport(ipfs, options, path, source, containingFolder, next) } ], done) @@ -186,26 +141,43 @@ const updateOrImport = (ipfs, options, path, source, containingFolder, callback) } }, - // Add or replace the DAGLink in the containing directory - (child, next) => addLink(ipfs, { - parent: containingFolder.node, - name: path.name, - child: { - multihash: child.multihash || child.hash, - size: child.size - }, - flush: options.flush - }, (error, newContaingFolder) => { - // Store new containing folder CID - containingFolder.node = newContaingFolder - - next(error) - }), - - // Update the MFS tree from the containingFolder upwards - (next) => updateTree(ipfs, containingFolder, next), + // The slow bit is done, now add or replace the DAGLink in the containing directory + // re-reading the path to the containing folder in case it has changed in the interim + (child, next) => { + createLock().writeLock((callback) => { + const opts = Object.assign({}, options, { + createLastComponent: options.parents + }) + + traverseTo(ipfs, path.directory, opts, (error, containingFolder) => { + if (error) { + return callback(error) + } - // Update the MFS record with the new CID for the root of the tree - (newRoot, next) => updateMfsRoot(ipfs, newRoot.node.multihash, next) - ], callback) + waterfall([ + (next) => { + addLink(ipfs, { + parent: containingFolder.node, + name: path.name, + child: { + multihash: child.multihash || child.hash, + size: child.size + }, + flush: options.flush + }, (error, newContaingFolder) => { + // Store new containing folder CID + containingFolder.node = newContaingFolder + + next(error) + }) + }, + // Update the MFS tree from the containingFolder upwards + (next) => updateTree(ipfs, containingFolder, next), + + // Update the MFS record with the new CID for the root of the tree + (newRoot, next) => updateMfsRoot(ipfs, newRoot.node.multihash, next) + ], callback) + }) + })(next) + }], callback) } diff --git a/src/http/read.js b/src/http/read.js index 78c995b..1bbf147 100644 --- a/src/http/read.js +++ b/src/http/read.js @@ -1,6 +1,9 @@ 'use strict' const Joi = require('joi') +const { + PassThrough +} = require('stream') const mfsRead = (api) => { api.route({ @@ -17,30 +20,34 @@ const mfsRead = (api) => { length } = request.query - return ipfs.files.readReadableStream(arg, { + const stream = ipfs.files.readReadableStream(arg, { offset, length }) - .then(stream => { - if (!stream._read) { - // make the stream look like a Streams2 to appease Hapi - stream._read = () => {} - stream._readableState = {} - } - reply(stream).header('X-Stream-Output', '1') - }) - .catch(error => { - if (error.message.includes('did not exist')) { - error.message = 'file does not exist' - } + if (!stream._read) { + // make the stream look like a Streams2 to appease Hapi + stream._read = () => {} + stream._readableState = {} + } - reply({ - Message: error.message, - Code: 0, - Type: 'error' - }).code(500).takeover() - }) + stream.once('data', (chunk) => { + const passThrough = new PassThrough() + + reply(passThrough) + .header('X-Stream-Output', '1') + + passThrough.write(chunk) + stream.pipe(passThrough) + }) + + stream.once('error', (error) => { + reply({ + Message: error.message, + Code: 0, + Type: 'error' + }).code(500).takeover() + }) }, validate: { options: { diff --git a/test/cp.spec.js b/test/cp.spec.js index d2f05b1..a2d054c 100644 --- a/test/cp.spec.js +++ b/test/cp.spec.js @@ -62,7 +62,7 @@ describe('cp', function () { throw new Error('No error was thrown for a non-existent file') }) .catch(error => { - expect(error.message).to.contain('did not exist') + expect(error.message).to.contain('does not exist') }) }) diff --git a/test/ls.spec.js b/test/ls.spec.js index c503632..c9f575c 100644 --- a/test/ls.spec.js +++ b/test/ls.spec.js @@ -165,7 +165,7 @@ describe('ls', function () { throw new Error('No error was thrown for a non-existent file') }) .catch(error => { - expect(error.message).to.contain('did not exist') + expect(error.message).to.contain('does not exist') }) }) }) diff --git a/test/mkdir.spec.js b/test/mkdir.spec.js index e3f7ea8..e9ed563 100644 --- a/test/mkdir.spec.js +++ b/test/mkdir.spec.js @@ -55,7 +55,7 @@ describe('mkdir', function () { parents: false }) .catch(error => { - expect(error.message).to.contain('foo did not exist') + expect(error.message).to.contain('foo does not exist') }) }) diff --git a/test/mv.spec.js b/test/mv.spec.js index 7aa66e4..3361bc0 100644 --- a/test/mv.spec.js +++ b/test/mv.spec.js @@ -67,7 +67,7 @@ describe('mv', function () { throw new Error('File was copied but not removed') }) .catch(error => { - expect(error.message).to.contain(`Path ${source} did not exist`) + expect(error.message).to.contain(`Path ${source} does not exist`) }) }) @@ -88,7 +88,7 @@ describe('mv', function () { throw new Error('Directory was copied but not removed') }) .catch(error => { - expect(error.message).to.contain(`Path ${source} did not exist`) + expect(error.message).to.contain(`Path ${source} does not exist`) }) }) @@ -115,7 +115,7 @@ describe('mv', function () { throw new Error('Directory was copied but not removed') }) .catch(error => { - expect(error.message).to.contain(`Cannot find ${source} - ${directory} did not exist`) + expect(error.message).to.contain(`Cannot find ${source} - ${directory} does not exist`) }) }) }) diff --git a/test/read.spec.js b/test/read.spec.js index db6f7a8..9451d5f 100644 --- a/test/read.spec.js +++ b/test/read.spec.js @@ -32,11 +32,15 @@ describe('read', function () { const methods = [{ name: 'read', - read: function () { return mfs.read.apply(mfs, arguments) }, + read: function () { + return mfs.read.apply(mfs, arguments) + }, collect: (buffer) => buffer }, { name: 'readPullStream', - read: function () { return mfs.readPullStream.apply(mfs, arguments) }, + read: function () { + return Promise.resolve(mfs.readPullStream.apply(mfs, arguments)) + }, collect: (stream) => { return new Promise((resolve, reject) => { pull( @@ -53,7 +57,9 @@ describe('read', function () { } }, { name: 'readReadableStream', - read: function () { return mfs.readReadableStream.apply(mfs, arguments) }, + read: function () { + return Promise.resolve(mfs.readReadableStream.apply(mfs, arguments)) + }, collect: (stream) => { return new Promise((resolve, reject) => { let data = Buffer.alloc(0) @@ -66,7 +72,9 @@ describe('read', function () { resolve(data) }) - stream.on('error', (error) => reject(error)) + stream.on('error', (error) => { + reject(error) + }) }) } }] @@ -149,6 +157,7 @@ describe('read', function () { const path = '/' return method.read(path) + .then((stream) => method.collect(stream)) .catch(error => { expect(error.message).to.contain('was not a file') }) diff --git a/test/rm.spec.js b/test/rm.spec.js index 3be9ae2..905d287 100644 --- a/test/rm.spec.js +++ b/test/rm.spec.js @@ -76,7 +76,7 @@ describe('rm', function () { throw new Error('File was not removed') }) .catch(error => { - expect(error.message).to.contain(`Path ${file} did not exist`) + expect(error.message).to.contain(`Path ${file} does not exist`) }) }) @@ -100,14 +100,14 @@ describe('rm', function () { throw new Error('File #1 was not removed') }) .catch(error => { - expect(error.message).to.contain(`Path ${file1} did not exist`) + expect(error.message).to.contain(`Path ${file1} does not exist`) }) .then(() => mfs.stat(file2)) .then(() => { throw new Error('File #2 was not removed') }) .catch(error => { - expect(error.message).to.contain(`Path ${file2} did not exist`) + expect(error.message).to.contain(`Path ${file2} does not exist`) }) }) @@ -123,7 +123,7 @@ describe('rm', function () { throw new Error('Directory was not removed') }) .catch(error => { - expect(error.message).to.contain(`Path ${directory} did not exist`) + expect(error.message).to.contain(`Path ${directory} does not exist`) }) }) @@ -143,14 +143,14 @@ describe('rm', function () { throw new Error('File was not removed') }) .catch(error => { - expect(error.message).to.contain(`Path ${subdirectory} did not exist`) + expect(error.message).to.contain(`Path ${subdirectory} does not exist`) }) .then(() => mfs.ls(directory)) .then(() => { throw new Error('Directory was not removed') }) .catch(error => { - expect(error.message).to.contain(`Path ${directory} did not exist`) + expect(error.message).to.contain(`Path ${directory} does not exist`) }) }) @@ -170,14 +170,14 @@ describe('rm', function () { throw new Error('File was not removed') }) .catch(error => { - expect(error.message).to.contain(`Path ${file} did not exist`) + expect(error.message).to.contain(`Path ${file} does not exist`) }) .then(() => mfs.stat(`/${directory}`)) .then(() => { throw new Error('Directory was not removed') }) .catch(error => { - expect(error.message).to.contain(`${directory} did not exist`) + expect(error.message).to.contain(`${directory} does not exist`) }) }) }) diff --git a/test/stat.spec.js b/test/stat.spec.js index bd3847f..0456416 100644 --- a/test/stat.spec.js +++ b/test/stat.spec.js @@ -57,7 +57,7 @@ describe('stat', function () { throw new Error('No error was thrown for a non-existent file') }) .catch(error => { - expect(error.message).to.contain('Path /i-do-not-exist did not exist') + expect(error.message).to.contain('Path /i-do-not-exist does not exist') }) }) diff --git a/test/write.spec.js b/test/write.spec.js index 34bfca8..a4248a1 100644 --- a/test/write.spec.js +++ b/test/write.spec.js @@ -213,7 +213,7 @@ describe('write', function () { throw new Error('Writing a file to a non-existent folder without the --parents flag should have failed') }) .catch((error) => { - expect(error.message).to.contain('did not exist') + expect(error.message).to.contain('does not exist') }) })