From bd4d34b6e627308c63b1d34c779f5a8f600b9e71 Mon Sep 17 00:00:00 2001 From: achingbrain Date: Wed, 30 May 2018 11:43:01 +0100 Subject: [PATCH] feat: add mfs implementation --- examples/browser-script-tag/index.html | 2 +- examples/circuit-relaying/package.json | 2 +- package.json | 7 +- src/cli/bin.js | 80 +++--- src/cli/commands/dag/get.js | 2 +- src/cli/commands/files.js | 20 -- src/cli/commands/files/get.js | 1 + src/cli/utils.js | 9 +- src/core/components/index.js | 1 + src/core/index.js | 9 + src/http/api/resources/files.js | 19 +- src/http/api/routes/files.js | 3 + src/http/error-handler.js | 9 +- src/http/gateway/resources/gateway.js | 7 +- test/browser.js | 8 + test/cli/commands.js | 3 +- test/cli/file.js | 2 +- test/cli/files.js | 34 +-- test/cli/init.js | 2 +- test/cli/ls.js | 2 +- test/cli/pin.js | 2 +- test/core/interface.spec.js | 51 +--- test/http-api/files.js | 355 +++++++++++++++++++++++++ test/http-api/index.js | 1 + test/http-api/interface.js | 49 +--- test/utils/ipfs-exec.js | 2 +- 26 files changed, 488 insertions(+), 194 deletions(-) delete mode 100644 src/cli/commands/files.js create mode 100644 test/browser.js diff --git a/examples/browser-script-tag/index.html b/examples/browser-script-tag/index.html index 50ef0819e3..f95a295fd7 100644 --- a/examples/browser-script-tag/index.html +++ b/examples/browser-script-tag/index.html @@ -30,7 +30,7 @@

Some suggestions

node.files.add(new node.types.Buffer('Hello world!'), (err, filesAdded) => { if (err) { - return console.error('Error - ipfs files add', err, res) + return console.error('Error - ipfs add', err, res) } filesAdded.forEach((file) => console.log('successfully stored', file.hash)) diff --git a/examples/circuit-relaying/package.json b/examples/circuit-relaying/package.json index 3e92c83f8a..6a3e5cf58d 100644 --- a/examples/circuit-relaying/package.json +++ b/examples/circuit-relaying/package.json @@ -18,7 +18,7 @@ "ipfs-pubsub-room": "~0.3.0" }, "devDependencies": { - "aegir": "^13.0.5", + "aegir": "^14.0.0", "http-server": "~0.10.0", "ipfs-css": "~0.2.0", "parcel-bundler": "^1.6.2", diff --git a/package.json b/package.json index 864dc977a7..a22df33108 100644 --- a/package.json +++ b/package.json @@ -66,6 +66,7 @@ "chai": "^4.1.2", "delay": "^2.0.0", "detect-node": "^2.0.3", + "detect-webworker": "^1.0.0", "dir-compare": "^1.4.0", "dirty-chai": "^2.0.1", "eslint-plugin-react": "^7.7.0", @@ -107,11 +108,12 @@ "hoek": "^5.0.3", "human-to-milliseconds": "^1.0.0", "interface-datastore": "~0.4.1", - "ipfs-api": "^22.1.1", + "ipfs-api": "^22.2.1", "ipfs-bitswap": "~0.20.0", "ipfs-block": "~0.7.1", "ipfs-block-service": "~0.14.0", "ipfs-http-response": "~0.1.2", + "ipfs-mfs": "~0.0.13", "ipfs-multipart": "~0.1.0", "ipfs-repo": "~0.22.1", "ipfs-unixfs": "~0.1.15", @@ -170,7 +172,8 @@ "through2": "^2.0.3", "update-notifier": "^2.5.0", "yargs": "^11.0.0", - "yargs-parser": "^10.0.0" + "yargs-parser": "^10.0.0", + "yargs-promise": "^1.1.0" }, "optionalDependencies": { "prom-client": "^11.0.0", diff --git a/src/cli/bin.js b/src/cli/bin.js index 2ccf2f66a0..fff221e947 100755 --- a/src/cli/bin.js +++ b/src/cli/bin.js @@ -2,13 +2,14 @@ 'use strict' +const YargsPromise = require('yargs-promise') const yargs = require('yargs') const updateNotifier = require('update-notifier') const readPkgUp = require('read-pkg-up') -const fs = require('fs') -const path = require('path') const utils = require('./utils') const print = utils.print +const mfs = require('ipfs-mfs/cli') +const debug = require('debug')('ipfs:cli') const pkg = readPkgUp.sync({cwd: __dirname}).pkg updateNotifier({ @@ -18,10 +19,6 @@ updateNotifier({ const args = process.argv.slice(2) -// Determine if the first argument is a sub-system command -const commandNames = fs.readdirSync(path.join(__dirname, 'commands')) -const isCommand = commandNames.includes(`${args[0]}.js`) - const cli = yargs .option('silent', { desc: 'Write no output', @@ -34,14 +31,6 @@ const cli = yargs type: 'string', default: '' }) - .commandDir('commands', { - // Only include the commands for the sub-system we're using, or include all - // if no sub-system command has been passed. - include (path, filename) { - if (!isCommand) return true - return `${args[0]}.js` === filename - } - }) .epilog(utils.ipfsPathHelp) .demandCommand(1) .fail((msg, err, yargs) => { @@ -56,20 +45,6 @@ const cli = yargs yargs.showHelp() }) -// If not a sub-system command then load the top level aliases -if (!isCommand) { - // NOTE: This creates an alias of - // `jsipfs files {add, get, cat}` to `jsipfs {add, get, cat}`. - // This will stay until https://github.com/ipfs/specs/issues/98 is resolved. - const addCmd = require('./commands/files/add') - const catCmd = require('./commands/files/cat') - const getCmd = require('./commands/files/get') - const aliases = [addCmd, catCmd, getCmd] - aliases.forEach((alias) => { - cli.command(alias.command, alias.describe, alias.builder, alias.handler) - }) -} - // Need to skip to avoid locking as these commands // don't require a daemon if (args[0] === 'daemon' || args[0] === 'init') { @@ -77,6 +52,8 @@ if (args[0] === 'daemon' || args[0] === 'init') { .help() .strict() .completion() + .command(require('./commands/daemon')) + .command(require('./commands/init')) .parse(args) } else { // here we have to make a separate yargs instance with @@ -86,19 +63,54 @@ if (args[0] === 'daemon' || args[0] === 'init') { if (err) { throw err } + utils.getIPFS(argv, (err, ipfs, cleanup) => { - if (err) { throw err } + if (err) { + throw err + } + + // add mfs commands + mfs(cli) + + // NOTE: This creates an alias of + // `jsipfs files {add, get, cat}` to `jsipfs {add, get, cat}`. + // This will stay until https://github.com/ipfs/specs/issues/98 is resolved. + const addCmd = require('./commands/files/add') + const catCmd = require('./commands/files/cat') + const getCmd = require('./commands/files/get') + const aliases = [addCmd, catCmd, getCmd] + aliases.forEach((alias) => { + cli.command(alias) + }) cli + .commandDir('commands') .help() .strict() .completion() - .parse(args, { ipfs: ipfs }, (err, argv, output) => { - if (output) { print(output) } - cleanup(() => { - if (err) { throw err } - }) + const parser = new YargsPromise(cli, { ipfs }) + parser.parse(args) + .then(({ data, argv }) => { + if (data) { + print(data) + } + + return cleanup() + }) + .catch((arg) => { + debug(arg) + + // the argument can have a different shape depending on where the error came from + if (arg.message) { + print(arg.message) + } else if (arg.error && arg.error.message) { + print(arg.error.message) + } else { + print('Unknown error, please re-run the command with DEBUG=ipfs:cli to see debug output') + } + + process.exit(1) }) }) }) diff --git a/src/cli/commands/dag/get.js b/src/cli/commands/dag/get.js index 6088d2eb34..d2795f7829 100644 --- a/src/cli/commands/dag/get.js +++ b/src/cli/commands/dag/get.js @@ -41,7 +41,7 @@ module.exports = { // * reads as 'agree in' if (node._json) { delete node._json.multihash - node._json.data = '0x' + node._json.data.toString('hex') + node._json.data = node._json.data.toString('base64') print(JSON.stringify(node._json)) return } diff --git a/src/cli/commands/files.js b/src/cli/commands/files.js deleted file mode 100644 index 1e24b3c226..0000000000 --- a/src/cli/commands/files.js +++ /dev/null @@ -1,20 +0,0 @@ -'use strict' - -const print = require('../utils').print -const lsCmd = require('./ls') - -module.exports = { - command: 'files ', - - description: 'Operations over files (add, cat, get, ls, etc)', - - builder (yargs) { - return yargs - .commandDir('files') - .command(lsCmd) - }, - - handler (argv) { - print('Type `jsipfs files --help` for more instructions') - } -} diff --git a/src/cli/commands/files/get.js b/src/cli/commands/files/get.js index c177fb8719..d9e0a39a67 100644 --- a/src/cli/commands/files/get.js +++ b/src/cli/commands/files/get.js @@ -30,6 +30,7 @@ function fileHandler (dir) { callback(err) } else { const fullFilePath = path.join(dir, file.path) + if (file.content) { file.content .pipe(fs.createWriteStream(fullFilePath)) diff --git a/src/cli/utils.js b/src/cli/utils.js index f993e0caef..02f830f6c6 100644 --- a/src/cli/utils.js +++ b/src/cli/utils.js @@ -9,6 +9,7 @@ const log = debug('cli') log.error = debug('cli:error') const Progress = require('progress') const byteman = require('byteman') +const promisify = require('promisify-es6') exports = module.exports @@ -40,7 +41,7 @@ function getAPICtl (apiAddr) { exports.getIPFS = (argv, callback) => { if (argv.api || isDaemonOn()) { - return callback(null, getAPICtl(argv.api), (cb) => cb()) + return callback(null, getAPICtl(argv.api), promisify((cb) => cb())) } // Required inline to reduce startup time @@ -55,13 +56,13 @@ exports.getIPFS = (argv, callback) => { } }) - const cleanup = (cb) => { + const cleanup = promisify((cb) => { if (node && node._repo && !node._repo.closed) { - node._repo.close(() => cb()) + node._repo.close((err) => cb(err)) } else { cb() } - } + }) node.on('error', (err) => { throw err diff --git a/src/core/components/index.js b/src/core/components/index.js index 7221575007..9eb36ad4c3 100644 --- a/src/core/components/index.js +++ b/src/core/components/index.js @@ -26,3 +26,4 @@ exports.dht = require('./dht') exports.dns = require('./dns') exports.key = require('./key') exports.stats = require('./stats') +exports.mfs = require('ipfs-mfs/core') diff --git a/src/core/index.js b/src/core/index.js index 1b561f96c6..1d0e411ffa 100644 --- a/src/core/index.js +++ b/src/core/index.js @@ -133,6 +133,15 @@ class IPFS extends EventEmitter { isIPFS: isIPFS } + // ipfs.files + const mfs = components.mfs(this) + + Object.keys(mfs).forEach(key => { + if (mfs.hasOwnProperty(key)) { + this.files[key] = mfs[key] + } + }) + boot(this) } } diff --git a/src/http/api/resources/files.js b/src/http/api/resources/files.js index 1cd93e158e..a3b7e8eaed 100644 --- a/src/http/api/resources/files.js +++ b/src/http/api/resources/files.js @@ -34,7 +34,8 @@ exports.parseKey = (request, reply) => { if (!request.query.arg) { return reply({ Message: "Argument 'key' is required", - Code: 0 + Code: 0, + Type: 'error' }).code(400).takeover() } @@ -54,7 +55,8 @@ exports.parseKey = (request, reply) => { log.error(err) return reply({ Message: 'invalid ipfs ref path', - Code: 0 + Code: 0, + Type: 'error' }).code(500).takeover() } @@ -81,9 +83,9 @@ exports.cat = { if (err) { log.error(err) if (err.message === 'No such file') { - reply({Message: 'No such file'}).code(500) + reply({Message: 'No such file', Code: 0, Type: 'error'}).code(500) } else { - reply({Message: 'Failed to cat file: ' + err, Code: 0}).code(500) + reply({Message: 'Failed to cat file: ' + err, Code: 0, Type: 'error'}).code(500) } return } @@ -177,7 +179,8 @@ exports.add = { if (!request.payload) { return reply({ Message: 'Array, Buffer, or String is required.', - code: 0 + Code: 0, + Type: 'error' }).code(400).takeover() } @@ -211,7 +214,8 @@ exports.add = { if (!filesParsed) { return reply({ Message: "File argument 'data' is required.", - code: 0 + Code: 0, + Type: 'error' }).code(400).takeover() } fileAdder.end() @@ -302,7 +306,8 @@ exports.immutableLs = { if (err) { return reply({ Message: 'Failed to list dir: ' + err.message, - Code: 0 + Code: 0, + Type: 'error' }).code(500).takeover() } diff --git a/src/http/api/routes/files.js b/src/http/api/routes/files.js index 2400f6012b..fc8222f180 100644 --- a/src/http/api/routes/files.js +++ b/src/http/api/routes/files.js @@ -1,6 +1,7 @@ 'use strict' const resources = require('./../resources') +const mfs = require('ipfs-mfs/http') module.exports = (server) => { const api = server.select('API') @@ -54,4 +55,6 @@ module.exports = (server) => { handler: resources.files.immutableLs.handler } }) + + mfs(api) } diff --git a/src/http/error-handler.js b/src/http/error-handler.js index 012a98f173..0c490d4639 100644 --- a/src/http/error-handler.js +++ b/src/http/error-handler.js @@ -14,9 +14,15 @@ module.exports = (api, server) => { let statusCode = 200 let msg = 'Sorry, something went wrong, please retrace your steps.' + let code = 1 if (res.isBoom) { statusCode = res.output.payload.statusCode + msg = res.output.payload.message + + if (res.data && res.data.code !== undefined) { + code = res.data.code + } if (res.message && res.isDeveloperError) { msg = res.message.replace('Uncaught error: ', '') @@ -36,7 +42,8 @@ module.exports = (api, server) => { reply({ Message: msg, - Code: 1 + Code: code, + Type: 'error' }).code(statusCode) return } diff --git a/src/http/gateway/resources/gateway.js b/src/http/gateway/resources/gateway.js index aef2451a33..c3a866c359 100644 --- a/src/http/gateway/resources/gateway.js +++ b/src/http/gateway/resources/gateway.js @@ -17,7 +17,8 @@ module.exports = { if (!request.params.cid) { return reply({ Message: 'Path Resolve error: path must contain at least one component', - Code: 0 + Code: 0, + Type: 'error' }).code(400).takeover() } @@ -63,10 +64,10 @@ module.exports = { return reply(errorToString).code(404) case (errorToString.startsWith('Error: multihash length inconsistent')): case (errorToString.startsWith('Error: Non-base58 character')): - return reply({ Message: errorToString, code: 0 }).code(400) + return reply({ Message: errorToString, Code: 0, Type: 'error' }).code(400) default: log.error(err) - return reply({ Message: errorToString, code: 0 }).code(500) + return reply({ Message: errorToString, Code: 0, Type: 'error' }).code(500) } } } diff --git a/test/browser.js b/test/browser.js new file mode 100644 index 0000000000..4b41564257 --- /dev/null +++ b/test/browser.js @@ -0,0 +1,8 @@ +'use strict' + +const isWebWorker = require('detect-webworker') + +if (isWebWorker) { + // https://github.com/Joris-van-der-Wel/karma-mocha-webworker/issues/4 + global.MFS_DISABLE_CONCURRENCY = true +} diff --git a/test/cli/commands.js b/test/cli/commands.js index 7a8502bc4c..2c0c5fc033 100644 --- a/test/cli/commands.js +++ b/test/cli/commands.js @@ -4,7 +4,8 @@ const expect = require('chai').expect const runOnAndOff = require('../utils/on-and-off') -const commandCount = 78 +const commandCount = 77 + describe('commands', () => runOnAndOff((thing) => { let ipfs diff --git a/test/cli/file.js b/test/cli/file.js index e20a16101f..e4a5e0c9ce 100644 --- a/test/cli/file.js +++ b/test/cli/file.js @@ -12,7 +12,7 @@ describe('file ls', () => runOnAndOff((thing) => { before(function () { this.timeout(50 * 1000) ipfs = thing.ipfs - return ipfs('files add -r test/fixtures/test-data/recursive-get-dir') + return ipfs('add -r test/fixtures/test-data/recursive-get-dir') }) it('prints a filename', () => { diff --git a/test/cli/files.js b/test/cli/files.js index 32fe28fc00..2f749f1cfe 100644 --- a/test/cli/files.js +++ b/test/cli/files.js @@ -126,7 +126,7 @@ describe('files', () => runOnAndOff((thing) => { it('add with progress', function () { this.timeout(30 * 1000) - return ipfs('files add -p src/init-files/init-docs/readme') + return ipfs('add -p src/init-files/init-docs/readme') .then((out) => { expect(out) .to.eql('added QmPZ9gcCEpqKTo6aq61g2nXGUhM4iCL3ewB6LDXZCtioEB readme\n') @@ -136,7 +136,7 @@ describe('files', () => runOnAndOff((thing) => { it('add', function () { this.timeout(30 * 1000) - return ipfs('files add src/init-files/init-docs/readme') + return ipfs('add src/init-files/init-docs/readme') .then((out) => { expect(out) .to.eql('added QmPZ9gcCEpqKTo6aq61g2nXGUhM4iCL3ewB6LDXZCtioEB readme\n') @@ -156,7 +156,7 @@ describe('files', () => runOnAndOff((thing) => { it('add recursively test', function () { this.timeout(60 * 1000) - return ipfs('files add -r test/fixtures/test-data/recursive-get-dir') + return ipfs('add -r test/fixtures/test-data/recursive-get-dir') .then((out) => { expect(out).to.eql(recursiveGetDirResults.join('\n') + '\n') }) @@ -165,7 +165,7 @@ describe('files', () => runOnAndOff((thing) => { it('add directory with trailing slash test', function () { this.timeout(30 * 1000) - return ipfs('files add -r test/fixtures/test-data/recursive-get-dir/') + return ipfs('add -r test/fixtures/test-data/recursive-get-dir/') .then((out) => { expect(out).to.eql(recursiveGetDirResults.join('\n') + '\n') }) @@ -179,7 +179,7 @@ describe('files', () => runOnAndOff((thing) => { 'added QmXJGoo27bg7ExNAtr9vRcivxDwcfHtkxatGno9HrUdR16 odd-name-[v0]' ] - return ipfs('files add -r test/fixtures/odd-name-[v0]') + return ipfs('add -r test/fixtures/odd-name-[v0]') .then((out) => { expect(out).to.eql(expected.join('\n') + '\n') }) @@ -260,7 +260,7 @@ describe('files', () => runOnAndOff((thing) => { it('add --quiet', function () { this.timeout(30 * 1000) - return ipfs('files add -q src/init-files/init-docs/readme') + return ipfs('add -q src/init-files/init-docs/readme') .then((out) => { expect(out) .to.eql('added QmPZ9gcCEpqKTo6aq61g2nXGUhM4iCL3ewB6LDXZCtioEB\n') @@ -270,7 +270,7 @@ describe('files', () => runOnAndOff((thing) => { it('add --quieter', function () { this.timeout(30 * 1000) - return ipfs('files add -Q -w test/fixtures/test-data/hello test/test-data/node.json') + return ipfs('add -Q -w test/fixtures/test-data/hello test/test-data/node.json') .then((out) => { expect(out) .to.eql('QmYRMUVULBfj7WrdPESnwnyZmtayN6Sdrwh1nKcQ9QgQeZ\n') @@ -280,7 +280,7 @@ describe('files', () => runOnAndOff((thing) => { it('add --silent', function () { this.timeout(30 * 1000) - return ipfs('files add --silent src/init-files/init-docs/readme') + return ipfs('add --silent src/init-files/init-docs/readme') .then((out) => { expect(out) .to.eql('') @@ -288,7 +288,7 @@ describe('files', () => runOnAndOff((thing) => { }) it('add --only-hash outputs correct hash', function () { - return ipfs('files add --only-hash src/init-files/init-docs/readme') + return ipfs('add --only-hash src/init-files/init-docs/readme') .then(out => expect(out) .to.eql('added QmPZ9gcCEpqKTo6aq61g2nXGUhM4iCL3ewB6LDXZCtioEB readme\n') @@ -302,7 +302,7 @@ describe('files', () => runOnAndOff((thing) => { const filepath = path.join(os.tmpdir(), `${content}.txt`) fs.writeFileSync(filepath, content) - return ipfs(`files add --only-hash ${filepath}`) + return ipfs(`add --only-hash ${filepath}`) .then(out => { const hash = out.split(' ')[1] @@ -322,7 +322,7 @@ describe('files', () => runOnAndOff((thing) => { const content = String(Math.random()) fs.writeFileSync(filePath, content) - return ipfs(`files add -Q ${filePath}`) + return ipfs(`add -Q ${filePath}`) .then(out => { const hash = out.trim() return ipfs(`pin ls ${hash}`) @@ -337,7 +337,7 @@ describe('files', () => runOnAndOff((thing) => { const content = String(Math.random()) fs.writeFileSync(filePath, content) - return ipfs(`files add -Q --pin=false ${filePath}`) + return ipfs(`add -Q --pin=false ${filePath}`) .then(out => ipfs.fail(`pin ls ${out.trim()}`)) .then(() => clean(filePath)) }) @@ -358,7 +358,7 @@ describe('files', () => runOnAndOff((thing) => { it('cat', function () { this.timeout(30 * 1000) - return ipfs('files cat QmPZ9gcCEpqKTo6aq61g2nXGUhM4iCL3ewB6LDXZCtioEB') + return ipfs('cat QmPZ9gcCEpqKTo6aq61g2nXGUhM4iCL3ewB6LDXZCtioEB') .then((out) => { expect(out).to.eql(readme) }) @@ -376,7 +376,7 @@ describe('files', () => runOnAndOff((thing) => { it('cat part of a file using `count`', function () { this.timeout(30 * 1000) - return ipfs('files cat QmPZ9gcCEpqKTo6aq61g2nXGUhM4iCL3ewB6LDXZCtioEB --offset 21 --count 5') + return ipfs('cat QmPZ9gcCEpqKTo6aq61g2nXGUhM4iCL3ewB6LDXZCtioEB --offset 21 --count 5') .then((out) => { expect(out).to.eql(readme.substring(21, 26)) }) @@ -385,7 +385,7 @@ describe('files', () => runOnAndOff((thing) => { it('cat part of a file using `length`', function () { this.timeout(30 * 1000) - return ipfs('files cat QmPZ9gcCEpqKTo6aq61g2nXGUhM4iCL3ewB6LDXZCtioEB --offset 21 --length 5') + return ipfs('cat QmPZ9gcCEpqKTo6aq61g2nXGUhM4iCL3ewB6LDXZCtioEB --offset 21 --length 5') .then((out) => { expect(out).to.eql(readme.substring(21, 26)) }) @@ -402,7 +402,7 @@ describe('files', () => runOnAndOff((thing) => { it('get', function () { this.timeout(20 * 1000) - return ipfs('files get QmPZ9gcCEpqKTo6aq61g2nXGUhM4iCL3ewB6LDXZCtioEB') + return ipfs('get QmPZ9gcCEpqKTo6aq61g2nXGUhM4iCL3ewB6LDXZCtioEB') .then((out) => { expect(out) .to.eql('Saving file(s) QmPZ9gcCEpqKTo6aq61g2nXGUhM4iCL3ewB6LDXZCtioEB\n') @@ -437,7 +437,7 @@ describe('files', () => runOnAndOff((thing) => { const outDir = path.join(process.cwd(), 'Qmaj2NmcyAXT8dFmZRRytE12wpcaHADzbChKToMEjBsj5Z') rimraf(outDir) - return ipfs('files get Qmaj2NmcyAXT8dFmZRRytE12wpcaHADzbChKToMEjBsj5Z') + return ipfs('get Qmaj2NmcyAXT8dFmZRRytE12wpcaHADzbChKToMEjBsj5Z') .then((out) => { expect(out).to.eql( 'Saving file(s) Qmaj2NmcyAXT8dFmZRRytE12wpcaHADzbChKToMEjBsj5Z\n' diff --git a/test/cli/init.js b/test/cli/init.js index 86e0dbacda..b1a826c1db 100644 --- a/test/cli/init.js +++ b/test/cli/init.js @@ -42,7 +42,7 @@ describe('init', function () { // Test that the following was written when init-ing the repo // jsipfs files cat /ipfs/QmfGBRT6BbWJd7yUc2uYdaUZJBbnEFvTqehPFoSMQ6wgdr/readme - let command = out.substring(out.indexOf('files cat'), out.length - 2 /* omit the newline char */) + let command = out.substring(out.indexOf('cat'), out.length - 2 /* omit the newline char */) return ipfs(command) }).then((out) => expect(out).to.equal(readme)) }) diff --git a/test/cli/ls.js b/test/cli/ls.js index bd4ed58bfd..4eccf82c07 100644 --- a/test/cli/ls.js +++ b/test/cli/ls.js @@ -9,7 +9,7 @@ describe('ls', () => runOnAndOff((thing) => { before(() => { ipfs = thing.ipfs - return ipfs('files add -r test/fixtures/test-data/recursive-get-dir') + return ipfs('add -r test/fixtures/test-data/recursive-get-dir') }) it('prints added files', function () { diff --git a/test/cli/pin.js b/test/cli/pin.js index f6c77eb67a..1795f9a640 100644 --- a/test/cli/pin.js +++ b/test/cli/pin.js @@ -24,7 +24,7 @@ describe('pin', () => runOnAndOff(thing => { before(function () { this.timeout(15 * 1000) ipfs = thing.ipfs - return ipfs(`files add -r ${fixturePath}`) + return ipfs(`add -r ${fixturePath}`) }) describe('rm', function () { diff --git a/test/core/interface.spec.js b/test/core/interface.spec.js index 7f761bdf7b..997dab7748 100644 --- a/test/core/interface.spec.js +++ b/test/core/interface.spec.js @@ -22,59 +22,12 @@ describe('interface-ipfs-core tests', () => { skip: { reason: 'TODO: DHT is not implemented in js-ipfs yet!' } }) - tests.files(defaultCommonFactory, { - skip: [ - { - name: 'cp', - reason: 'TODO: MFS is not implemented in js-ipfs yet!' - }, - { - name: 'mkdir', - reason: 'TODO: MFS is not implemented in js-ipfs yet!' - }, - { - name: 'stat', - reason: 'TODO: MFS is not implemented in js-ipfs yet!' - }, - { - name: 'rm', - reason: 'TODO: MFS is not implemented in js-ipfs yet!' - }, - { - name: 'read', - reason: 'TODO: MFS is not implemented in js-ipfs yet!' - }, - { - name: 'readReadableStream', - reason: 'TODO: MFS is not implemented in js-ipfs yet!' - }, - { - name: 'readPullStream', - reason: 'TODO: MFS is not implemented in js-ipfs yet!' - }, - { - name: 'write', - reason: 'TODO: MFS is not implemented in js-ipfs yet!' - }, - { - name: 'mv', - reason: 'TODO: MFS is not implemented in js-ipfs yet!' - }, - { - name: 'flush', - reason: 'TODO: MFS is not implemented in js-ipfs yet!' - }, - { - name: 'ls', - reason: 'TODO: MFS is not implemented in js-ipfs yet!' - } - ] - }) + tests.files(defaultCommonFactory) tests.key(CommonFactory.create({ spawnOptions: { args: ['--pass ipfs-is-awesome-software'], - initOptions: { bits: 512 } + initOptions: { bits: 1024 } } })) diff --git a/test/http-api/files.js b/test/http-api/files.js index 7e85697299..01839f49e1 100644 --- a/test/http-api/files.js +++ b/test/http-api/files.js @@ -7,6 +7,9 @@ const dirtyChai = require('dirty-chai') const DaemonFactory = require('ipfsd-ctl') const expect = chai.expect chai.use(dirtyChai) +const { + FILE_TYPES +} = require('ipfs-mfs') const df = DaemonFactory.create({ exec: 'src/cli/bin.js' }) @@ -46,4 +49,356 @@ describe('.files', () => { }) }) }) + + describe('.ls', function () { + it('lists empty directory', () => { + return ipfs.files.ls() + .then(files => { + expect(files).to.be.empty() + }) + }) + + it('lists files', () => { + const fileName = `single-file-${Math.random()}.txt` + + return ipfs.files.write(`/${fileName}`, Buffer.from('Hello world'), { + create: true + }) + .then(() => ipfs.files.ls()) + .then(files => { + expect(files.length).to.equal(1) + expect(files[0].name).to.equal(fileName) + }) + }) + + it('lists files in directories', () => { + const dirName = `dir-${Math.random()}` + const fileName = `file-in-dir-${Math.random()}.txt` + + return ipfs.files.write(`/${dirName}/${fileName}`, Buffer.from('Hello world'), { + create: true, + parents: true + }) + .then(() => ipfs.files.ls(`/${dirName}`)) + .then(files => { + expect(files.length).to.equal(1) + expect(files[0].name).to.equal(fileName) + }) + }) + }) + + describe('.cp', function () { + it('copies a file', () => { + const source = `source-file-${Math.random()}.txt` + const destination = `destination-file-${Math.random()}.txt` + + return ipfs.files.write(`/${source}`, Buffer.from('Hello world'), { + create: true + }) + .then(() => ipfs.files.cp(`/${source}`, `/${destination}`)) + .then(() => ipfs.files.ls(`/`, { + long: true + })) + .then(files => { + const sourceFile = files + .filter(file => file.name === source) + .pop() + + expect(sourceFile.type).to.equal(FILE_TYPES.file) + + const destFile = files + .filter(file => file.name === destination) + .pop() + + expect(destFile.type).to.equal(FILE_TYPES.file) + }) + }) + + it('copies a directory', () => { + const source = `source-dir-${Math.random()}` + const destination = `destination-dir-${Math.random()}` + + return ipfs.files.mkdir(`/${source}`) + .then(() => ipfs.files.cp(`/${source}`, `/${destination}`)) + .then(() => ipfs.files.ls(`/`, { + long: true + })) + .then(files => { + const sourceDir = files + .filter(file => file.name === source) + .pop() + + expect(sourceDir.type).to.equal(FILE_TYPES.directory) + + const destDir = files + .filter(file => file.name === destination) + .pop() + + expect(destDir.type).to.equal(FILE_TYPES.directory) + }) + }) + + it('copies a file with array args', () => { + const source = `source-file-${Math.random()}.txt` + const destination = `destination-file-${Math.random()}.txt` + + return ipfs.files.write(`/${source}`, Buffer.from('Hello world'), { + create: true + }) + .then(() => ipfs.files.cp([`/${source}`, `/${destination}`])) + .then(() => ipfs.files.ls(`/`, { + long: true + })) + .then(files => { + const sourceFile = files + .filter(file => file.name === source) + .pop() + + expect(sourceFile.type).to.equal(FILE_TYPES.file) + + const destFile = files + .filter(file => file.name === destination) + .pop() + + expect(destFile.type).to.equal(FILE_TYPES.file) + }) + }) + + it('copies a directory with array args', () => { + const source = `source-dir-${Math.random()}` + const destination = `destination-dir-${Math.random()}` + + return ipfs.files.mkdir(`/${source}`) + .then(() => ipfs.files.cp([`/${source}`, `/${destination}`])) + .then(() => ipfs.files.ls(`/`, { + long: true + })) + .then(files => { + const sourceDir = files + .filter(file => file.name === source) + .pop() + + expect(sourceDir.type).to.equal(FILE_TYPES.directory) + + const destDir = files + .filter(file => file.name === destination) + .pop() + + expect(destDir.type).to.equal(FILE_TYPES.directory) + }) + }) + }) + + describe('.mkdir', function () { + it('makes a directory', () => { + const directory = `directory-${Math.random()}` + + return ipfs.files.mkdir(`/${directory}`) + .then(() => ipfs.files.ls(`/`, { + long: true + })) + .then(files => { + const dir = files + .filter(file => file.name === directory) + .pop() + + expect(dir.type).to.equal(FILE_TYPES.directory) + }) + }) + }) + + describe('.mv', function () { + it('moves a file', () => { + const source = `source-file-${Math.random()}.txt` + const destination = `destination-file-${Math.random()}.txt` + + return ipfs.files.write(`/${source}`, Buffer.from('Hello world'), { + create: true + }) + .then(() => ipfs.files.mv(`/${source}`, `/${destination}`)) + .then(() => ipfs.files.ls(`/`)) + .then(files => { + const sourceFile = files + .filter(file => file.name === source) + .pop() + + expect(sourceFile).to.not.exist() + + const destFile = files + .filter(file => file.name === destination) + .pop() + + expect(destFile.type).to.equal(FILE_TYPES.file) + }) + }) + + it('moves a directory', () => { + const source = `source-dir-${Math.random()}` + const destination = `destination-dir-${Math.random()}` + + return ipfs.files.mkdir(`/${source}`) + .then(() => ipfs.files.mv(`/${source}`, `/${destination}`)) + .then(() => ipfs.files.ls(`/`, { + long: true + })) + .then(files => { + const sourceDir = files + .filter(file => file.name === source) + .pop() + + expect(sourceDir).to.not.exist() + + const destDir = files + .filter(file => file.name === destination) + .pop() + + expect(destDir.type).to.equal(FILE_TYPES.directory) + }) + }) + + it('moves a file with array args', () => { + const source = `source-file-${Math.random()}.txt` + const destination = `destination-file-${Math.random()}.txt` + + return ipfs.files.write(`/${source}`, Buffer.from('Hello world'), { + create: true + }) + .then(() => ipfs.files.mv([`/${source}`, `/${destination}`])) + .then(() => ipfs.files.ls(`/`)) + .then(files => { + const sourceFile = files + .filter(file => file.name === source) + .pop() + + expect(sourceFile).to.not.exist() + + const destFile = files + .filter(file => file.name === destination) + .pop() + + expect(destFile.type).to.equal(FILE_TYPES.file) + }) + }) + + it('moves a directory with array args', () => { + const source = `source-dir-${Math.random()}` + const destination = `destination-dir-${Math.random()}` + + return ipfs.files.mkdir(`/${source}`) + .then(() => ipfs.files.mv([`/${source}`, `/${destination}`])) + .then(() => ipfs.files.ls(`/`, { + long: true + })) + .then(files => { + const sourceDir = files + .filter(file => file.name === source) + .pop() + + expect(sourceDir).to.not.exist() + + const destDir = files + .filter(file => file.name === destination) + .pop() + + expect(destDir.type).to.equal(FILE_TYPES.directory) + }) + }) + }) + + describe('.read', function () { + it('reads a file', () => { + const fileName = `single-file-${Math.random()}.txt` + const content = Buffer.from('Hello world') + + return ipfs.files.write(`/${fileName}`, content, { + create: true + }) + .then(() => ipfs.files.read(`/${fileName}`)) + .then(buffer => { + expect(buffer).to.deep.equal(content) + }) + }) + }) + + describe('.rm', function () { + it('removes a file', () => { + const fileName = `single-file-${Math.random()}.txt` + + return ipfs.files.write(`/${fileName}`, Buffer.from('Hello world'), { + create: true + }) + .then(() => ipfs.files.rm(`/${fileName}`)) + .then(() => ipfs.files.ls(`/`)) + .then(files => { + const file = files + .filter(file => file.name === fileName) + .pop() + + expect(file).to.not.exist() + }) + }) + + it('removes a directory', () => { + const dirName = `dir-${Math.random()}` + const fileName = `file-in-dir-${Math.random()}.txt` + + return ipfs.files.write(`/${dirName}/${fileName}`, Buffer.from('Hello world'), { + create: true, + parents: true + }) + .then(() => ipfs.files.rm(`/${dirName}`, { + recursive: true + })) + .then(() => ipfs.files.ls(`/`)) + .then(files => { + const dir = files + .filter(file => file.name === dirName) + .pop() + + expect(dir).to.not.exist() + }) + }) + }) + + describe('.stat', function () { + it('stats a file', () => { + const fileName = `single-file-${Math.random()}.txt` + + return ipfs.files.write(`/${fileName}`, Buffer.from('Hello world'), { + create: true + }) + .then(() => ipfs.files.stat(`/${fileName}`)) + .then(stats => { + expect(stats).to.deep.equal({ + blocks: 1, + cumulativeSize: 69, + hash: 'Qmetpc7cZmN25Wcc6R27cGCAvCDqCS5GjHG4v7xABEfpmJ', + local: undefined, + size: 11, + sizeLocal: undefined, + type: 'file', + withLocality: false + }) + }) + }) + + it('stats a directory', () => { + const dirName = `dir-${Math.random()}` + + return ipfs.files.mkdir(`/${dirName}`) + .then(() => ipfs.files.stat(`/${dirName}`)) + .then(stats => { + expect(stats).to.deep.equal({ + blocks: 0, + cumulativeSize: 4, + hash: 'QmUNLLsPACCz1vLxQVkXqqLX5R1X345qqfHbsf67hvA3Nn', + local: undefined, + size: 0, + sizeLocal: undefined, + type: 'directory', + withLocality: false + }) + }) + }) + }) }) diff --git a/test/http-api/index.js b/test/http-api/index.js index d619014896..901cb212ef 100644 --- a/test/http-api/index.js +++ b/test/http-api/index.js @@ -9,3 +9,4 @@ require('./inject') require('./interface') require('./object') require('./version') +require('./files') diff --git a/test/http-api/interface.js b/test/http-api/interface.js index 1f2955377b..e123093687 100644 --- a/test/http-api/interface.js +++ b/test/http-api/interface.js @@ -25,54 +25,7 @@ describe('interface-ipfs-core over ipfs-api tests', () => { skip: { reason: 'TODO: DHT is not implemented in js-ipfs yet!' } }) - tests.files(defaultCommonFactory, { - skip: [ - { - name: 'cp', - reason: 'TODO: MFS is not implemented in js-ipfs yet!' - }, - { - name: 'mkdir', - reason: 'TODO: MFS is not implemented in js-ipfs yet!' - }, - { - name: 'stat', - reason: 'TODO: MFS is not implemented in js-ipfs yet!' - }, - { - name: 'rm', - reason: 'TODO: MFS is not implemented in js-ipfs yet!' - }, - { - name: 'read', - reason: 'TODO: MFS is not implemented in js-ipfs yet!' - }, - { - name: 'readReadableStream', - reason: 'TODO: MFS is not implemented in js-ipfs yet!' - }, - { - name: 'readPullStream', - reason: 'TODO: MFS is not implemented in js-ipfs yet!' - }, - { - name: 'write', - reason: 'TODO: MFS is not implemented in js-ipfs yet!' - }, - { - name: 'mv', - reason: 'TODO: MFS is not implemented in js-ipfs yet!' - }, - { - name: 'flush', - reason: 'TODO: MFS is not implemented in js-ipfs yet!' - }, - { - name: 'ls', - reason: 'TODO: MFS is not implemented in js-ipfs yet!' - } - ] - }) + tests.files(defaultCommonFactory) tests.key(CommonFactory.create({ spawnOptions: { diff --git a/test/utils/ipfs-exec.js b/test/utils/ipfs-exec.js index 38742b76b5..786a3f7f3b 100644 --- a/test/utils/ipfs-exec.js +++ b/test/utils/ipfs-exec.js @@ -13,7 +13,7 @@ const _ = require('lodash') // The top level export is a function that can be passed a `repoPath` // and optional `opts` to customize the execution of the commands. // This function returns the actual executer, which consists of -// `ipfs('files get ')` and `ipfs.fail('files get ')` +// `ipfs('get ')` and `ipfs.fail('files get ')` // The first one executes and asserts that the command ran successfully // and returns a promise which is resolved to `stdout` of the command. // The `.fail` variation asserts that the command exited with `Code > 0`