├── .aegir.js ├── .gitignore ├── .npmignore ├── .travis.yml ├── CHANGELOG.md ├── LICENSE ├── README.md ├── SPEC ├── BITSWAP.md ├── BLOCK.md ├── BOOTSTRAP.md ├── CONFIG.md ├── DAG.md ├── DHT.md ├── FILES.md ├── KEY.md ├── MISCELLANEOUS.md ├── NAME.md ├── OBJECT.md ├── PIN.md ├── PUBSUB.md ├── README.md ├── REFS.md ├── REPO.md ├── STATS.md └── SWARM.md ├── img ├── badge.png ├── badge.sketch ├── badge.svg └── badge@2x.png ├── maintainer.json ├── package.json ├── src ├── add.js ├── bitswap │ ├── .tern-port │ ├── index.js │ ├── stat.js │ ├── utils.js │ └── wantlist.js ├── block │ ├── get.js │ ├── index.js │ ├── put.js │ ├── rm.js │ └── stat.js ├── bootstrap │ ├── add.js │ ├── index.js │ ├── list.js │ └── rm.js ├── cat.js ├── config │ ├── get.js │ ├── index.js │ ├── profiles │ │ ├── apply.js │ │ ├── index.js │ │ └── list.js │ ├── replace.js │ └── set.js ├── dag │ ├── get.js │ ├── index.js │ ├── put.js │ └── tree.js ├── dht │ ├── find-peer.js │ ├── find-provs.js │ ├── get.js │ ├── index.js │ ├── provide.js │ ├── put.js │ ├── query.js │ └── utils.js ├── files │ ├── chmod.js │ ├── cp.js │ ├── flush.js │ ├── index.js │ ├── ls.js │ ├── mkdir.js │ ├── mv.js │ ├── read.js │ ├── rm.js │ ├── stat.js │ ├── touch.js │ └── write.js ├── get.js ├── index.js ├── key │ ├── export.js │ ├── gen.js │ ├── import.js │ ├── index.js │ ├── list.js │ ├── rename.js │ └── rm.js ├── ls.js ├── miscellaneous │ ├── dns.js │ ├── id.js │ ├── index.js │ ├── resolve.js │ ├── stop.js │ └── version.js ├── name-pubsub │ ├── cancel.js │ ├── index.js │ ├── state.js │ └── subs.js ├── name │ ├── index.js │ ├── publish.js │ ├── resolve.js │ └── utils.js ├── object │ ├── data.js │ ├── get.js │ ├── index.js │ ├── links.js │ ├── new.js │ ├── patch │ │ ├── add-link.js │ │ ├── append-data.js │ │ ├── index.js │ │ ├── rm-link.js │ │ └── set-data.js │ ├── put.js │ ├── stat.js │ └── utils.js ├── pin │ ├── add.js │ ├── index.js │ ├── ls.js │ ├── rm.js │ └── utils.js ├── ping │ ├── index.js │ ├── ping.js │ └── utils.js ├── pubsub │ ├── index.js │ ├── ls.js │ ├── peers.js │ ├── publish.js │ ├── subscribe.js │ ├── unsubscribe.js │ └── utils.js ├── refs-local.js ├── refs.js ├── repo │ ├── gc.js │ ├── index.js │ ├── stat.js │ └── version.js ├── stats │ ├── bitswap.js │ ├── bw.js │ ├── index.js │ ├── repo.js │ └── utils.js ├── swarm │ ├── addrs.js │ ├── connect.js │ ├── disconnect.js │ ├── index.js │ ├── local-addrs.js │ └── peers.js └── utils │ ├── echo-http-server.js │ ├── index.js │ ├── mocha.js │ └── suite.js └── test ├── fixtures ├── .gitattributes ├── 15mb.random ├── hidden-files-folder │ ├── .hiddenTest.txt │ ├── alice.txt │ ├── files │ │ ├── hello.txt │ │ └── ipfs.txt │ ├── hello-link │ ├── holmes.txt │ ├── ipfs-add.js │ ├── jungle.txt │ └── pp.txt ├── refs-test │ ├── animals │ │ ├── land │ │ │ ├── african.txt │ │ │ ├── americas.txt │ │ │ └── australian.txt │ │ └── sea │ │ │ ├── atlantic.txt │ │ │ └── indian.txt │ ├── atlantic-animals │ ├── fruits │ │ └── tropical.txt │ └── mushroom.txt ├── ssl │ ├── cert.pem │ └── privkey.pem ├── test-folder │ ├── alice.txt │ ├── files │ │ ├── hello.txt │ │ └── ipfs.txt │ ├── hello-link │ ├── holmes.txt │ ├── ipfs-add.js │ ├── jungle.txt │ └── pp.txt ├── testfile.txt └── weird name folder [v0] │ ├── add │ ├── cat │ ├── files │ ├── hello.txt │ └── ipfs.txt │ ├── hello-link │ ├── ipfs-add │ ├── ls │ └── version └── interface.spec.js /.aegir.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | module.exports = { 4 | lint: { 5 | files: [ 6 | 'src/**/*.js', 7 | 'test/*.js' 8 | ] 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | package-lock.json 2 | yarn.lock 3 | 4 | # Logs 5 | logs 6 | *.log 7 | npm-debug.log* 8 | 9 | # Runtime data 10 | pids 11 | *.pid 12 | *.seed 13 | 14 | # Directory for instrumented libs generated by jscoverage/JSCover 15 | lib-cov 16 | 17 | # Coverage directory used by tools like istanbul 18 | coverage 19 | 20 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 21 | .grunt 22 | 23 | # node-waf configuration 24 | .lock-wscript 25 | 26 | # Compiled binary addons (http://nodejs.org/api/addons.html) 27 | build/Release 28 | 29 | # Dependency directory 30 | node_modules 31 | 32 | # Optional npm cache directory 33 | .npm 34 | 35 | # Optional REPL history 36 | .node_repl_history 37 | 38 | # Dignified.js builds 39 | dist 40 | lib 41 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | 5 | # Runtime data 6 | pids 7 | *.pid 8 | *.seed 9 | 10 | # Directory for instrumented libs generated by jscoverage/JSCover 11 | lib-cov 12 | 13 | # Coverage directory used by tools like istanbul 14 | coverage 15 | 16 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 17 | .grunt 18 | 19 | # Compiled binary addons (http://nodejs.org/api/addons.html) 20 | build/Release 21 | 22 | # Optional npm cache directory 23 | .npm 24 | 25 | # Optional REPL history 26 | .node_repl_history 27 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | 3 | cache: npm 4 | 5 | node_js: 6 | - '10' 7 | 8 | script: 9 | - npx aegir dep-check 10 | - npm run lint 11 | 12 | notifications: 13 | email: false 14 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2018 Protocol Labs, Inc. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /SPEC/BITSWAP.md: -------------------------------------------------------------------------------- 1 | # Bitswap API 2 | 3 | * [bitswap.wantlist](#bitswapwantlist) 4 | * [bitswap.stat](#bitswapstat) 5 | 6 | ### `bitswap.wantlist` 7 | 8 | > Returns the wantlist, optionally filtered by peer ID 9 | 10 | #### `ipfs.bitswap.wantlist([peerId])` 11 | 12 | **Returns** 13 | 14 | | Type | Description | 15 | | -------- | -------- | 16 | | `Promise` | An array of [CID][cid]s currently in the wantlist | 17 | 18 | **Example:** 19 | 20 | ```JavaScript 21 | const list = await ipfs.bitswap.wantlist() 22 | console.log(list) 23 | // [ CID('QmHash') ] 24 | 25 | const list2 = await ipfs.bitswap.wantlist(peerId) 26 | console.log(list2) 27 | // [ CID('QmHash') ] 28 | ``` 29 | 30 | A great source of [examples][] can be found in the tests for this API. 31 | 32 | #### `bitswap.stat` 33 | 34 | > Show diagnostic information on the bitswap agent. 35 | 36 | ##### `ipfs.bitswap.stat()` 37 | 38 | Note: `bitswap.stat` and `stats.bitswap` can be used interchangeably. 39 | 40 | **Returns** 41 | 42 | | Type | Description | 43 | | -------- | -------- | 44 | | `Promise` | An object that contains information about the bitswap agent | 45 | 46 | The returned object contains the following keys: 47 | 48 | - `provideBufLen` is an integer. 49 | - `wantlist` (array of [CID][cid]s) 50 | - `peers` (array of peer IDs as Strings) 51 | - `blocksReceived` is a [BigNumber Int][1] 52 | - `dataReceived` is a [BigNumber Int][1] 53 | - `blocksSent` is a [BigNumber Int][1] 54 | - `dataSent` is a [BigNumber Int][1] 55 | - `dupBlksReceived` is a [BigNumber Int][1] 56 | - `dupDataReceived` is a [BigNumber Int][1] 57 | 58 | **Example:** 59 | 60 | ```JavaScript 61 | const stats = await ipfs.bitswap.stat() 62 | console.log(stats) 63 | // { 64 | // provideBufLen: 0, 65 | // wantlist: [ CID('QmSoLPppuBtQSGwKDZT2M73ULpjvfd3aZ6ha4oFGL1KrGM') ], 66 | // peers: 67 | // [ 'QmSoLPppuBtQSGwKDZT2M73ULpjvfd3aZ6ha4oFGL1KrGM', 68 | // 'QmSoLSafTMBsPKadTEgaXctDQVcqN88CNLHXMkTNwMKPnu', 69 | // 'QmSoLer265NRgSp2LA3dPaeykiS1J6DifTC88f5uVQKNAd' ], 70 | // blocksReceived: 0, 71 | // dataReceived: 0, 72 | // blocksSent: 0, 73 | // dataSent: 0, 74 | // dupBlksReceived: 0, 75 | // dupDataReceived: 0 76 | // } 77 | ``` 78 | 79 | A great source of [examples][] can be found in the tests for this API. 80 | 81 | [1]: https://github.com/MikeMcl/bignumber.js/ 82 | [examples]: https://github.com/ipfs/interface-ipfs-core/blob/master/src/bitswap 83 | [cid]: https://www.npmjs.com/package/cids 84 | -------------------------------------------------------------------------------- /SPEC/BOOTSTRAP.md: -------------------------------------------------------------------------------- 1 | # Bootstrap API 2 | 3 | > Manipulates the `bootstrap list`, which contains 4 | the addresses of the bootstrap nodes. These are the trusted peers from 5 | which to learn about other peers in the network. 6 | 7 | > Only edit this list if you understand the risks of adding or removing nodes 8 | 9 | * [bootstrap.add](#bootstrapadd) 10 | * [bootstrap.list](#bootstraplist) 11 | * [bootstrap.rm](#bootstraprm) 12 | 13 | #### `bootstrap.add` 14 | 15 | > Add a peer address to the bootstrap list 16 | 17 | ##### `ipfs.bootstrap.add(addr, [options])` 18 | 19 | - `addr` is a [multiaddr](https://github.com/multiformats/js-multiaddr) to a peer node 20 | - `options.default` if true, add the default peers to the list 21 | 22 | Note: If passing the `default` option, `addr` is an optional parameter (may be `undefined`/`null`) and options may be passed as the first argument. i.e. `ipfs.bootstrap.add({ default: true })` 23 | 24 | **Returns** 25 | 26 | | Type | Description | 27 | | -------- | -------- | 28 | | `Promise` | An object that contains an array with all the added addresses | 29 | 30 | example of the returned object: 31 | 32 | ```JavaScript 33 | { 34 | Peers: [address1, address2, ...] 35 | } 36 | ``` 37 | 38 | **Example:** 39 | 40 | ```JavaScript 41 | const validIp4 = '/ip4/104....9z' 42 | 43 | const res = await ipfs.bootstrap.add(validIp4) 44 | console.log(res.Peers) 45 | // Logs: 46 | // ['/ip4/104....9z'] 47 | ``` 48 | 49 | A great source of [examples][] can be found in the tests for this API. 50 | 51 | #### `bootstrap.list` 52 | 53 | > List all peer addresses in the bootstrap list 54 | 55 | ##### `ipfs.bootstrap.list()` 56 | 57 | **Returns** 58 | 59 | | Type | Description | 60 | | -------- | -------- | 61 | | `Promise` | An object that contains an array with all the bootstrap addresses | 62 | 63 | example of the returned object: 64 | 65 | ```JavaScript 66 | { 67 | Peers: [address1, address2, ...] 68 | } 69 | ``` 70 | 71 | **Example:** 72 | 73 | ```JavaScript 74 | const res = await ipfs.bootstrap.list() 75 | console.log(res.Peers) 76 | // Logs: 77 | // [address1, address2, ...] 78 | ``` 79 | 80 | A great source of [examples][] can be found in the tests for this API. 81 | 82 | #### `bootstrap.rm` 83 | 84 | > Remove a peer address from the bootstrap list 85 | 86 | ##### `ipfs.bootstrap.rm(peer, [options])` 87 | 88 | - `addr` is a [multiaddr](https://github.com/multiformats/js-multiaddr) to a peer node 89 | - `options.all` if true, remove all peers from the list 90 | 91 | Note: If passing the `all` option, `addr` is an optional parameter (may be `undefined`/`null`) and options may be passed as the first argument. i.e. `ipfs.bootstrap.rm({ all: true })` 92 | 93 | **Returns** 94 | 95 | | Type | Description | 96 | | -------- | -------- | 97 | | `Promise` | An object that contains an array with all the removed addresses | 98 | 99 | ```JavaScript 100 | { 101 | Peers: [address1, address2, ...] 102 | } 103 | ``` 104 | 105 | **Example:** 106 | 107 | ```JavaScript 108 | const res = await ipfs.bootstrap.rm(null, { all: true }) 109 | console.log(res.Peers) 110 | // Logs: 111 | // [address1, address2, ...] 112 | ``` 113 | 114 | A great source of [examples][] can be found in the tests for this API. 115 | 116 | [examples]: https://github.com/ipfs/interface-ipfs-core/blob/master/src/bootstrap 117 | -------------------------------------------------------------------------------- /SPEC/CONFIG.md: -------------------------------------------------------------------------------- 1 | # Config API 2 | 3 | * [config.get](#configget) 4 | * [config.set](#configset) 5 | * [config.replace](#configreplace) 6 | * [config.profiles.list](#configprofileslist) 7 | * [config.profiles.apply](#configprofilesapply) 8 | 9 | #### `config.get` 10 | 11 | > Returns the currently being used config. If the daemon is off, it returns the stored config. 12 | 13 | ##### `ipfs.config.get([key])` 14 | 15 | `key` is the key of the value that should be fetched from the config file. If no key is passed, then the whole config should be returned. `key` should be of type String. 16 | 17 | **Returns** 18 | 19 | | Type | Description | 20 | | -------- | -------- | 21 | | `Promise` | An object containing the configuration of the IPFS node | 22 | 23 | **Example:** 24 | 25 | ```JavaScript 26 | const config = await ipfs.config.get() 27 | console.log(config) 28 | ``` 29 | 30 | A great source of [examples][] can be found in the tests for this API. 31 | 32 | #### `config.set` 33 | 34 | > Adds or replaces a config value. 35 | 36 | ##### `ipfs.config.set(key, value)` 37 | 38 | `key` is the key value that will be added or replaced (in case of the value already). `key` should be of type String. 39 | 40 | `value` value to be set. 41 | 42 | **Returns** 43 | 44 | | Type | Description | 45 | | -------- | -------- | 46 | | `Promise` | If action is successfully completed. Otherwise an error will be thrown | 47 | 48 | Note that this operation will **not** spark the restart of any service, i.e: if a config.replace changes the multiaddrs of the Swarm, Swarm will have to be restarted manually for the changes to take difference. 49 | 50 | **Example:** 51 | 52 | ```JavaScript 53 | await ipfs.config.set('Discovery.MDNS.Enabled', false) 54 | // MDNS Discovery was set to false 55 | ``` 56 | 57 | A great source of [examples][] can be found in the tests for this API. 58 | 59 | #### `config.replace` 60 | 61 | > Adds or replaces a config file. 62 | 63 | ##### `ipfs.config.replace(config)` 64 | 65 | `config` is a JSON object that contains the new config. 66 | 67 | **Returns** 68 | 69 | | Type | Description | 70 | | -------- | -------- | 71 | | `Promise` | If action is successfully completed. Otherwise an error will be thrown | 72 | 73 | Note that this operation will **not** spark the restart of any service, i.e: if a config.replace changes the multiaddrs of the Swarm, Swarm will have to be restarted manually for the changes to take difference. 74 | 75 | **Example:** 76 | 77 | ```JavaScript 78 | await ipfs.config.replace(newConfig) 79 | // config has been replaced 80 | ``` 81 | 82 | A great source of [examples][] can be found in the tests for this API. 83 | 84 | #### `config.profiles.list` 85 | 86 | > List available config profiles 87 | 88 | ##### `ipfs.config.profiles.list([options])` 89 | 90 | `options` is a object. 91 | 92 | **Returns** 93 | 94 | | Type | Description | 95 | | -------- | -------- | 96 | | `Promise` | An array with all the available config profiles | 97 | 98 | **Example:** 99 | 100 | ```JavaScript 101 | const profiles = await ipfs.config.profiles.list() 102 | profiles.forEach(profile => { 103 | console.info(profile.name, profile.description) 104 | }) 105 | ``` 106 | 107 | A great source of [examples][] can be found in the tests for this API. 108 | 109 | #### `config.profiles.apply` 110 | 111 | > Apply a config profile 112 | 113 | ##### `ipfs.config.profiles.apply(name, [options])` 114 | 115 | `name` is a string. Call `config.profiles.list()` for a list of valid profile names. 116 | `options` an object that might contain the following values: 117 | - `dryRun` is a boolean which if true does not apply the profile 118 | 119 | **Returns** 120 | 121 | | Type | Description | 122 | | -------- | -------- | 123 | | `Promise` | An object containing both the `original` and `updated` config | 124 | 125 | **Example:** 126 | 127 | ```JavaScript 128 | const diff = await ipfs.config.profiles.apply('lowpower') 129 | console.info(diff.original) 130 | console.info(diff.updated) 131 | ``` 132 | 133 | Note that you will need to restart your node for config changes to take effect. 134 | 135 | A great source of [examples][] can be found in the tests for this API. 136 | 137 | [examples]: https://github.com/ipfs/interface-ipfs-core/blob/master/src/config 138 | -------------------------------------------------------------------------------- /SPEC/PIN.md: -------------------------------------------------------------------------------- 1 | # Pin API 2 | 3 | * [pin.add](#pinadd) 4 | * [pin.ls](#pinls) 5 | * [pin.rm](#pinrm) 6 | 7 | #### `pin.add` 8 | 9 | > Adds an IPFS object to the pinset and also stores it to the IPFS repo. pinset is the set of hashes currently pinned (not gc'able). 10 | 11 | ##### `ipfs.pin.add(hash, [options])` 12 | 13 | Where: 14 | 15 | - `hash` is an IPFS multihash. 16 | - `options` is an object that can contain the following keys 17 | - `recursive` (`boolean`) - Recursively pin the object linked. Type: bool. Default: `true` 18 | - `timeout` (`number`|`string`) - Throw an error if the request does not complete within the specified milliseconds timeout. If `timeout` is a string, the value is parsed as a [human readable duration](https://www.npmjs.com/package/parse-duration). There is no timeout by default. 19 | 20 | **Returns** 21 | 22 | | Type | Description | 23 | | -------- | -------- | 24 | | `Promise<{ cid: CID }>` | An array of objects that represent the files that were pinned | 25 | 26 | an array of objects is returned, each of the form: 27 | 28 | ```JavaScript 29 | { 30 | cid: CID('QmHash') 31 | } 32 | ``` 33 | 34 | **Example:** 35 | 36 | ```JavaScript 37 | const pinset = await ipfs.pin.add('QmWATWQ7fVPP2EFGu71UkfnqhYXDYH566qy47CnJDgvs8u') 38 | console.log(pinset) 39 | // Logs: 40 | // [ { cid: CID('QmWATWQ7fVPP2EFGu71UkfnqhYXDYH566qy47CnJDgvs8u') } ] 41 | ``` 42 | 43 | A great source of [examples][] can be found in the tests for this API. 44 | 45 | #### `pin.ls` 46 | 47 | > List all the objects pinned to local storage or under a specific hash. 48 | 49 | ##### `ipfs.pin.ls([cid], [options])` 50 | 51 | Where: 52 | 53 | - `cid` - a [CID][cid] instance or CID as a string or an array of CIDs. 54 | - `options` - is an object that can contain the following keys: 55 | - `type` - filter by this type of pin ("recursive", "direct" or "indirect") 56 | 57 | **Returns** 58 | 59 | | Type | Description | 60 | | -------- | -------- | 61 | | `AsyncIterable<{ cid: CID, type: string }>` | An async iterable that yields currently pinned objects with `cid` and `type` properties. `cid` is a [CID][cid] of the pinned node, `type` is the pin type ("recursive", "direct" or "indirect") | 62 | 63 | **Example:** 64 | 65 | ```JavaScript 66 | for await (const { cid, type } of ipfs.pin.ls()) { 67 | console.log({ cid, type }) 68 | } 69 | // { cid: CID(Qmc5XkteJdb337s7VwFBAGtiaoj2QCEzyxtNRy3iMudc3E), type: 'recursive' } 70 | // { cid: CID(QmZbj5ruYneZb8FuR9wnLqJCpCXMQudhSdWhdhp5U1oPWJ), type: 'indirect' } 71 | // { cid: CID(QmSo73bmN47gBxMNqbdV6rZ4KJiqaArqJ1nu5TvFhqqj1R), type: 'indirect' } 72 | ``` 73 | 74 | A great source of [examples][] can be found in the tests for this API. 75 | 76 | #### `pin.rm` 77 | 78 | > Remove a hash from the pinset 79 | 80 | ##### `ipfs.pin.rm(hash, [options])` 81 | 82 | Where: 83 | - `hash` is a multihash. 84 | - `options` is an object that can contain the following keys 85 | - 'recursive' - Recursively unpin the object linked. Type: bool. Default: `true` 86 | 87 | **Returns** 88 | 89 | | Type | Description | 90 | | -------- | -------- | 91 | | `Promise<{ cid: CID }>` | An array of unpinned objects | 92 | 93 | **Example:** 94 | 95 | ```JavaScript 96 | const pinset = await ipfs.pin.rm('QmWATWQ7fVPP2EFGu71UkfnqhYXDYH566qy47CnJDgvs8u') 97 | console.log(pinset) 98 | // prints the hashes that were unpinned 99 | // [ { cid: CID('QmWATWQ7fVPP2EFGu71UkfnqhYXDYH566qy47CnJDgvs8u') } ] 100 | ``` 101 | 102 | A great source of [examples][] can be found in the tests for this API. 103 | 104 | [examples]: https://github.com/ipfs/interface-ipfs-core/blob/master/src/pin 105 | [cid]: https://www.npmjs.com/package/cids 106 | -------------------------------------------------------------------------------- /SPEC/README.md: -------------------------------------------------------------------------------- 1 | # IPFS Core API 2 | 3 | This directory contains the description of the core IPFS API. In order to be considered "valid", an IPFS core implementation must expose the API described here. You can also use this loose spec as documentation for consuming the core APIs. There is an outline of the contents of that directory in the [API section of this repository's root README file](../README.md#api). 4 | -------------------------------------------------------------------------------- /SPEC/REFS.md: -------------------------------------------------------------------------------- 1 | # Refs API 2 | 3 | * [refs](#refs) 4 | * [refs.local](#refslocal) 5 | 6 | #### `refs` 7 | 8 | > Get links (references) from an object. 9 | 10 | ##### `ipfs.refs(ipfsPath, [options])` 11 | 12 | `ipfsPath` can be of type: 13 | 14 | - [`cid`][cid] of type: 15 | - a [CID](https://github.com/ipfs/js-cid) instance 16 | - [Buffer][b], the raw Buffer of the cid 17 | - String, the base58 encoded version of the cid 18 | - String, including the ipfs handler, a cid and a path to traverse to, ie: 19 | - '/ipfs/QmXEmhrMpbVvTh61FNAxP9nU7ygVtyvZA8HZDUaqQCAb66' 20 | - '/ipfs/QmXEmhrMpbVvTh61FNAxP9nU7ygVtyvZA8HZDUaqQCAb66/a.txt' 21 | - 'QmXEmhrMpbVvTh61FNAxP9nU7ygVtyvZA8HZDUaqQCAb66/a.txt' 22 | 23 | `options` is an optional object that may contain the following keys: 24 | - `recursive (false)`: recursively list references of child nodes 25 | - `unique (false)`: omit duplicate references from output 26 | - `format ("")`: output edges with given format. Available tokens: ``, ``, `` 27 | - `edges (false)`: output references in edge format: `" -> "` 28 | - `maxDepth (1)`: only for recursive refs, limits fetch and listing to the given depth 29 | - `timeout (number|string)`: Throw an error if the request does not complete within the specified milliseconds timeout. If `timeout` is a string, the value is parsed as a [human readable duration](https://www.npmjs.com/package/parse-duration). There is no timeout by default. 30 | 31 | **Returns** 32 | 33 | | Type | Description | 34 | | -------- | -------- | 35 | | `AsyncIterable` | An async iterable that yields objects representing the links (references) | 36 | 37 | Each yielded object is of the form: 38 | 39 | ```js 40 | { 41 | ref: string, 42 | err: Error | null 43 | } 44 | ``` 45 | 46 | **Example:** 47 | 48 | ```JavaScript 49 | for await (const ref of ipfs.refs(ipfsPath, { recursive: true })) { 50 | if (ref.err) { 51 | console.error(ref.err) 52 | } else { 53 | console.log(ref.ref) 54 | // output: "QmHash" 55 | } 56 | } 57 | ``` 58 | 59 | #### `refs.local` 60 | 61 | > Output all local references (CIDs of all blocks in the blockstore) 62 | 63 | ##### `ipfs.refs.local()` 64 | 65 | **Returns** 66 | 67 | | Type | Description | 68 | | -------- | -------- | 69 | | `AsyncIterable` | An async iterable that yields objects representing the links (references) | 70 | 71 | Each yielded object is of the form: 72 | 73 | ```js 74 | { 75 | ref: string, 76 | err: Error | null 77 | } 78 | ``` 79 | 80 | **Example:** 81 | 82 | ```JavaScript 83 | for await (const ref of ipfs.refs.local()) { 84 | if (ref.err) { 85 | console.error(ref.err) 86 | } else { 87 | console.log(ref.ref) 88 | // output: "QmHash" 89 | } 90 | } 91 | ``` 92 | 93 | [examples]: https://github.com/ipfs/interface-ipfs-core/blob/master/src/files-regular 94 | [b]: https://www.npmjs.com/package/buffer 95 | [cid]: https://www.npmjs.com/package/cids 96 | [blob]: https://developer.mozilla.org/en-US/docs/Web/API/Blob 97 | -------------------------------------------------------------------------------- /SPEC/REPO.md: -------------------------------------------------------------------------------- 1 | # Repo API 2 | 3 | * [repo.gc](#repogc) 4 | * [repo.stat](#repostat) 5 | * [repo.version](#repoversion) 6 | 7 | #### `repo.gc` 8 | 9 | > Perform a garbage collection sweep on the repo. 10 | 11 | ##### `ipfs.repo.gc([options])` 12 | 13 | Where: 14 | 15 | - `options` is an object that contains following properties 16 | - `quiet` writes a minimal output. 17 | - `stream-errors` stream errors. 18 | 19 | **Returns** 20 | 21 | | Type | Description | 22 | | -------- | -------- | 23 | | `AsyncIterable` | An async iterable that yields objects describing nodes that were garbage collected | 24 | 25 | Each yielded object contains the following properties: 26 | 27 | - `err` is an `Error` if it was not possible to GC a particular block. 28 | - `cid` is the [CID][cid] of the block that was Garbage Collected. 29 | 30 | **Example:** 31 | 32 | ```JavaScript 33 | for await (const res of ipfs.repo.gc()) { 34 | console.log(res) 35 | } 36 | ``` 37 | 38 | #### `repo.stat` 39 | 40 | > Get stats for the currently used repo. 41 | 42 | ##### `ipfs.repo.stat([options])` 43 | 44 | `stats.repo` and `repo.stat` can be used interchangeably. 45 | 46 | Where: 47 | 48 | - `options` is an object that contains following properties 49 | - `human` a Boolean value to output `repoSize` in MiB. 50 | 51 | **Returns** 52 | 53 | | Type | Description | 54 | | -------- | -------- | 55 | | `Promise` | An object containing the repo's info | 56 | 57 | the returned object has the following keys: 58 | 59 | - `numObjects` is a [BigNumber Int][1]. 60 | - `repoSize` is a [BigNumber Int][1], in bytes. 61 | - `repoPath` is a string. 62 | - `version` is a string. 63 | - `storageMax` is a [BigNumber Int][1]. 64 | 65 | **Example:** 66 | 67 | ```JavaScript 68 | const stats = await ipfs.repo.stat() 69 | console.log(stats) 70 | 71 | // { numObjects: 15, 72 | // repoSize: 64190, 73 | // repoPath: 'C:\\Users\\henri\\AppData\\Local\\Temp\\ipfs_687c6eb3da07d3b16fe3c63ce17560e9', 74 | // version: 'fs-repo@6', 75 | // storageMax: 10000000000 } 76 | ``` 77 | 78 | #### `repo.version` 79 | 80 | > Show the repo version. 81 | 82 | ##### `ipfs.repo.version()` 83 | 84 | **Returns** 85 | 86 | | Type | Description | 87 | | -------- | -------- | 88 | | `Promise` | A String containing the repo's version | 89 | 90 | **Example:** 91 | 92 | ```JavaScript 93 | const version = await ipfs.repo.version() 94 | console.log(version) 95 | 96 | // "6" 97 | ``` 98 | 99 | [1]: https://github.com/MikeMcl/bignumber.js/ 100 | [cid]: https://www.npmjs.com/package/cids 101 | -------------------------------------------------------------------------------- /SPEC/STATS.md: -------------------------------------------------------------------------------- 1 | # Stats API 2 | 3 | * [stats.bitswap](#statsbitswap) 4 | * [stats.repo](#statsrepo) 5 | * [stats.bw](#statsbw) 6 | 7 | #### `stats.bitswap` 8 | 9 | > Show diagnostic information on the bitswap agent. 10 | 11 | Note: `stats.bitswap` and `bitswap.stat` can be used interchangeably. See [`bitswap.stat`](./BITSWAP.md#bitswapstat) for more details. 12 | 13 | #### `stats.repo` 14 | 15 | > Get stats for the currently used repo. 16 | 17 | Note: `stats.repo` and `repo.stat` can be used interchangeably. See [`repo.stat`](./REPO.md#repostat) for more details. 18 | 19 | #### `stats.bw` 20 | 21 | > Get IPFS bandwidth information. 22 | 23 | ##### `ipfs.stats.bw([options])` 24 | 25 | Where: 26 | 27 | - `options` is an optional object that might contain the following keys: 28 | - `peer` specifies a peer to print bandwidth for. 29 | - `proto` specifies a protocol to print bandwidth for. 30 | - `poll` is used to yield bandwidth info at an interval. 31 | - `interval` is the time interval to wait between updating output, if `poll` is `true`. 32 | 33 | **Returns** 34 | 35 | | Type | Description | 36 | | -------- | -------- | 37 | | `AsyncIterable` | An async iterable that yields IPFS bandwidth information | 38 | 39 | Each yielded object contains the following keys: 40 | 41 | - `totalIn` - is a [BigNumber Int][bigNumber], in bytes. 42 | - `totalOut` - is a [BigNumber Int][bigNumber], in bytes. 43 | - `rateIn` - is a [BigNumber Int][bigNumber], in bytes. 44 | - `rateOut` - is a [BigNumber Int][bigNumber], in bytes. 45 | 46 | **Example:** 47 | 48 | ```JavaScript 49 | for await (const stats of ipfs.stats.bw()) { 50 | console.log(stats) 51 | } 52 | // { totalIn: BigNumber {...}, 53 | // totalOut: BigNumber {...}, 54 | // rateIn: BigNumber {...}, 55 | // rateOut: BigNumber {...} } 56 | ``` 57 | 58 | A great source of [examples][] can be found in the tests for this API. 59 | 60 | [bigNumber]: https://github.com/MikeMcl/bignumber.js/ 61 | [examples]: https://github.com/ipfs/interface-ipfs-core/blob/master/src/stats 62 | -------------------------------------------------------------------------------- /img/badge.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ipfs-inactive/interface-js-ipfs-core/38bf852da912162e7d3b9bd0cec395c32fd2ec48/img/badge.png -------------------------------------------------------------------------------- /img/badge.sketch: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ipfs-inactive/interface-js-ipfs-core/38bf852da912162e7d3b9bd0cec395c32fd2ec48/img/badge.sketch -------------------------------------------------------------------------------- /img/badge.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | IPFS Core API 13 | 14 | 15 | 16 | e 17 | 18 | 19 | l 20 | 21 | 22 | b 23 | 24 | 25 | i 26 | 27 | 28 | t 29 | 30 | 31 | a 32 | 33 | 34 | p 35 | 36 | 37 | m 38 | 39 | 40 | o 41 | 42 | 43 | C 44 | 45 | 46 | 57 | 58 | 59 | -------------------------------------------------------------------------------- /img/badge@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ipfs-inactive/interface-js-ipfs-core/38bf852da912162e7d3b9bd0cec395c32fd2ec48/img/badge@2x.png -------------------------------------------------------------------------------- /maintainer.json: -------------------------------------------------------------------------------- 1 | { 2 | "repoLeadMaintainer": { 3 | "name": "Alan Shaw", 4 | "email": "alan.shaw@protocol.ai", 5 | "username": "alanshaw" 6 | }, 7 | "workingGroup": { 8 | "name": "JS IPFS", 9 | "entryPoint": "https://github.com/ipfs/js-core" 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/bitswap/.tern-port: -------------------------------------------------------------------------------- 1 | 36541 -------------------------------------------------------------------------------- /src/bitswap/index.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | const { createSuite } = require('../utils/suite') 3 | 4 | const tests = { 5 | stat: require('./stat'), 6 | wantlist: require('./wantlist') 7 | } 8 | 9 | module.exports = createSuite(tests) 10 | -------------------------------------------------------------------------------- /src/bitswap/stat.js: -------------------------------------------------------------------------------- 1 | /* eslint-env mocha */ 2 | 'use strict' 3 | 4 | const { getDescribe, getIt, expect } = require('../utils/mocha') 5 | const { expectIsBitswap } = require('../stats/utils') 6 | 7 | /** @typedef { import("ipfsd-ctl/src/factory") } Factory */ 8 | /** 9 | * @param {Factory} common 10 | * @param {Object} options 11 | */ 12 | module.exports = (common, options) => { 13 | const describe = getDescribe(options) 14 | const it = getIt(options) 15 | 16 | describe('.bitswap.stat', function () { 17 | this.timeout(60 * 1000) 18 | let ipfs 19 | 20 | before(async () => { 21 | ipfs = (await common.spawn()).api 22 | }) 23 | 24 | after(() => common.clean()) 25 | 26 | it('should get bitswap stats', async () => { 27 | const res = await ipfs.bitswap.stat() 28 | expectIsBitswap(null, res) 29 | }) 30 | 31 | it('should not get bitswap stats when offline', async () => { 32 | const node = await common.spawn() 33 | await node.stop() 34 | 35 | return expect(node.api.bitswap.stat()).to.eventually.be.rejected() 36 | }) 37 | }) 38 | } 39 | -------------------------------------------------------------------------------- /src/bitswap/utils.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const delay = require('delay') 4 | 5 | async function waitForWantlistKey (ipfs, key, opts = {}) { 6 | opts.timeout = opts.timeout || 10000 7 | opts.interval = opts.interval || 100 8 | 9 | const end = Date.now() + opts.timeout 10 | 11 | while (Date.now() < end) { 12 | const list = await ipfs.bitswap.wantlist(opts.peerId) 13 | 14 | if (list.some(cid => cid.toString() === key)) { 15 | return 16 | } 17 | 18 | await delay(opts.interval) 19 | } 20 | 21 | throw new Error(`Timed out waiting for ${key} in wantlist`) 22 | } 23 | 24 | module.exports.waitForWantlistKey = waitForWantlistKey 25 | -------------------------------------------------------------------------------- /src/bitswap/wantlist.js: -------------------------------------------------------------------------------- 1 | /* eslint-env mocha */ 2 | 'use strict' 3 | 4 | const { getDescribe, getIt, expect } = require('../utils/mocha') 5 | const { waitForWantlistKey } = require('./utils') 6 | const { isWebWorker } = require('ipfs-utils/src/env') 7 | 8 | /** @typedef { import("ipfsd-ctl/src/factory") } Factory */ 9 | /** 10 | * @param {Factory} common 11 | * @param {Object} options 12 | */ 13 | module.exports = (common, options) => { 14 | const describe = getDescribe(options) 15 | const it = getIt(options) 16 | 17 | describe('.bitswap.wantlist', function () { 18 | this.timeout(60 * 1000) 19 | 20 | let ipfsA 21 | let ipfsB 22 | const key = 'QmUBdnXXPyoDFXj3Hj39dNJ5VkN3QFRskXxcGaYFBB8CNR' 23 | 24 | before(async () => { 25 | ipfsA = (await common.spawn()).api 26 | // webworkers are not dialable because webrtc is not available 27 | ipfsB = (await common.spawn({ type: isWebWorker ? 'go' : undefined })).api 28 | // Add key to the wantlist for ipfsB 29 | ipfsB.block.get(key).catch(() => { /* is ok, expected on teardown */ }) 30 | 31 | await ipfsA.swarm.connect(ipfsB.peerId.addresses[0]) 32 | }) 33 | 34 | after(() => common.clean()) 35 | 36 | it('should get the wantlist', function () { 37 | return waitForWantlistKey(ipfsB, key) 38 | }) 39 | 40 | it('should get the wantlist by peer ID for a different node', function () { 41 | return waitForWantlistKey(ipfsA, key, { 42 | peerId: ipfsB.peerId.id, 43 | timeout: 60 * 1000 44 | }) 45 | }) 46 | 47 | it('should not get the wantlist when offline', async () => { 48 | const node = await common.spawn() 49 | await node.stop() 50 | 51 | return expect(node.api.bitswap.stat()).to.eventually.be.rejected() 52 | }) 53 | }) 54 | } 55 | -------------------------------------------------------------------------------- /src/block/get.js: -------------------------------------------------------------------------------- 1 | /* eslint-env mocha */ 2 | 'use strict' 3 | 4 | const multihash = require('multihashes') 5 | const CID = require('cids') 6 | const { getDescribe, getIt, expect } = require('../utils/mocha') 7 | 8 | /** @typedef { import("ipfsd-ctl/src/factory") } Factory */ 9 | /** 10 | * @param {Factory} common 11 | * @param {Object} options 12 | */ 13 | module.exports = (common, options) => { 14 | const describe = getDescribe(options) 15 | const it = getIt(options) 16 | 17 | describe('.block.get', () => { 18 | const data = Buffer.from('blorb') 19 | let ipfs, hash 20 | 21 | before(async () => { 22 | ipfs = (await common.spawn()).api 23 | const block = await ipfs.block.put(data) 24 | hash = block.cid.multihash 25 | }) 26 | 27 | after(() => common.clean()) 28 | 29 | it('should get by CID object', async () => { 30 | const cid = new CID(hash) 31 | const block = await ipfs.block.get(cid) 32 | 33 | expect(block.data).to.eql(Buffer.from('blorb')) 34 | expect(block.cid.multihash).to.eql(cid.multihash) 35 | }) 36 | 37 | it('should get by CID in string', async () => { 38 | const block = await ipfs.block.get(multihash.toB58String(hash)) 39 | 40 | expect(block.data).to.eql(Buffer.from('blorb')) 41 | expect(block.cid.multihash).to.eql(hash) 42 | }) 43 | 44 | it('should get an empty block', async () => { 45 | const res = await ipfs.block.put(Buffer.alloc(0), { 46 | format: 'dag-pb', 47 | mhtype: 'sha2-256', 48 | version: 0 49 | }) 50 | 51 | const block = await ipfs.block.get(res.cid) 52 | 53 | expect(block.data).to.eql(Buffer.alloc(0)) 54 | }) 55 | 56 | it('should get a block added as CIDv0 with a CIDv1', async () => { 57 | const input = Buffer.from(`TEST${Date.now()}`) 58 | 59 | const res = await ipfs.block.put(input, { version: 0 }) 60 | 61 | const cidv0 = res.cid 62 | expect(cidv0.version).to.equal(0) 63 | 64 | const cidv1 = cidv0.toV1() 65 | 66 | const block = await ipfs.block.get(cidv1) 67 | expect(block.data).to.eql(input) 68 | }) 69 | 70 | it('should get a block added as CIDv1 with a CIDv0', async () => { 71 | const input = Buffer.from(`TEST${Date.now()}`) 72 | 73 | const res = await ipfs.block.put(input, { version: 1 }) 74 | 75 | const cidv1 = res.cid 76 | expect(cidv1.version).to.equal(1) 77 | 78 | const cidv0 = cidv1.toV0() 79 | 80 | const block = await ipfs.block.get(cidv0) 81 | expect(block.data).to.eql(input) 82 | }) 83 | 84 | it('should return an error for an invalid CID', () => { 85 | return expect(ipfs.block.get('invalid')).to.eventually.be.rejected 86 | .and.be.an.instanceOf(Error) 87 | .and.have.property('message') 88 | .that.include('Non-base58 character') 89 | }) 90 | }) 91 | } 92 | -------------------------------------------------------------------------------- /src/block/index.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | const { createSuite } = require('../utils/suite') 3 | 4 | const tests = { 5 | get: require('./get'), 6 | put: require('./put'), 7 | rm: require('./rm'), 8 | stat: require('./stat') 9 | } 10 | 11 | module.exports = createSuite(tests) 12 | -------------------------------------------------------------------------------- /src/block/put.js: -------------------------------------------------------------------------------- 1 | /* eslint-env mocha */ 2 | 'use strict' 3 | 4 | const Block = require('ipfs-block') 5 | const multihash = require('multihashes') 6 | const CID = require('cids') 7 | const { getDescribe, getIt, expect } = require('../utils/mocha') 8 | 9 | /** @typedef { import("ipfsd-ctl/src/factory") } Factory */ 10 | /** 11 | * @param {Factory} common 12 | * @param {Object} options 13 | */ 14 | module.exports = (common, options) => { 15 | const describe = getDescribe(options) 16 | const it = getIt(options) 17 | 18 | describe('.block.put', () => { 19 | let ipfs 20 | 21 | before(async () => { 22 | ipfs = (await common.spawn()).api 23 | }) 24 | 25 | after(() => common.clean()) 26 | 27 | it('should put a buffer, using defaults', async () => { 28 | const expectedHash = 'QmPv52ekjS75L4JmHpXVeuJ5uX2ecSfSZo88NSyxwA3rAQ' 29 | const blob = Buffer.from('blorb') 30 | 31 | const block = await ipfs.block.put(blob) 32 | 33 | expect(block.data).to.be.eql(blob) 34 | expect(block.cid.multihash).to.eql(multihash.fromB58String(expectedHash)) 35 | }) 36 | 37 | it('should put a buffer, using CID', async () => { 38 | const expectedHash = 'QmPv52ekjS75L4JmHpXVeuJ5uX2ecSfSZo88NSyxwA3rAQ' 39 | const cid = new CID(expectedHash) 40 | const blob = Buffer.from('blorb') 41 | 42 | const block = await ipfs.block.put(blob, { cid: cid }) 43 | 44 | expect(block.data).to.be.eql(blob) 45 | expect(block.cid.multihash).to.eql(multihash.fromB58String(expectedHash)) 46 | }) 47 | 48 | it('should put a buffer, using CID string', async () => { 49 | const expectedCid = 'bafyreietui4xdkiu4xvmx4fi2jivjtndbhb4drzpxomrjvd4mdz4w2avra' 50 | const blob = Buffer.from(JSON.stringify({ hello: 'world' })) 51 | 52 | const block = await ipfs.block.put(blob, { cid: expectedCid }) 53 | 54 | expect(block.data).to.be.eql(blob) 55 | expect(block.cid.toString()).to.eql(expectedCid) 56 | }) 57 | 58 | it('should put a buffer, using options', async () => { 59 | const blob = Buffer.from(`TEST${Date.now()}`) 60 | 61 | const block = await ipfs.block.put(blob, { 62 | format: 'raw', 63 | mhtype: 'sha2-512', 64 | version: 1 65 | }) 66 | 67 | expect(block.data).to.be.eql(blob) 68 | expect(block.cid.version).to.equal(1) 69 | expect(block.cid.codec).to.equal('raw') 70 | expect(multihash.decode(block.cid.multihash).name).to.equal('sha2-512') 71 | }) 72 | 73 | it('should put a Block instance', async () => { 74 | const expectedHash = 'QmPv52ekjS75L4JmHpXVeuJ5uX2ecSfSZo88NSyxwA3rAQ' 75 | const cid = new CID(expectedHash) 76 | const b = new Block(Buffer.from('blorb'), cid) 77 | 78 | const block = await ipfs.block.put(b) 79 | 80 | expect(block.data).to.eql(Buffer.from('blorb')) 81 | expect(block.cid.multihash).to.eql(multihash.fromB58String(expectedHash)) 82 | }) 83 | 84 | it('should error with array of blocks', () => { 85 | const blob = Buffer.from('blorb') 86 | 87 | return expect(ipfs.block.put([blob, blob])).to.eventually.be.rejected 88 | .and.be.an.instanceOf(Error) 89 | }) 90 | }) 91 | } 92 | -------------------------------------------------------------------------------- /src/block/stat.js: -------------------------------------------------------------------------------- 1 | /* eslint-env mocha */ 2 | 'use strict' 3 | 4 | const CID = require('cids') 5 | const { getDescribe, getIt, expect } = require('../utils/mocha') 6 | 7 | /** @typedef { import("ipfsd-ctl/src/factory") } Factory */ 8 | /** 9 | * @param {Factory} common 10 | * @param {Object} options 11 | */ 12 | module.exports = (common, options) => { 13 | const describe = getDescribe(options) 14 | const it = getIt(options) 15 | 16 | describe('.block.stat', () => { 17 | const data = Buffer.from('blorb') 18 | let ipfs, hash 19 | 20 | before(async () => { 21 | ipfs = (await common.spawn()).api 22 | const block = await ipfs.block.put(data) 23 | hash = block.cid.multihash 24 | }) 25 | 26 | after(() => common.clean()) 27 | 28 | it('should stat by CID', async () => { 29 | const cid = new CID(hash) 30 | const stats = await ipfs.block.stat(cid) 31 | expect(stats.cid.toString()).to.equal(cid.toString()) 32 | expect(stats).to.have.property('size') 33 | }) 34 | 35 | it('should return error for missing argument', () => { 36 | return expect(ipfs.block.stat(null)).to.eventually.be.rejected 37 | .and.be.an.instanceOf(Error) 38 | }) 39 | 40 | it('should return error for invalid argument', () => { 41 | return expect(ipfs.block.stat('invalid')).to.eventually.be.rejected 42 | .and.be.an.instanceOf(Error) 43 | }) 44 | }) 45 | } 46 | -------------------------------------------------------------------------------- /src/bootstrap/add.js: -------------------------------------------------------------------------------- 1 | /* eslint-env mocha */ 2 | 'use strict' 3 | 4 | const { getDescribe, getIt, expect } = require('../utils/mocha') 5 | 6 | const invalidArg = 'this/Is/So/Invalid/' 7 | const validIp4 = '/ip4/104.236.176.52/tcp/4001/p2p/QmSoLnSGccFuZQJzRadHn95W2CrSFmZuTdDWP8HXaHca9z' 8 | 9 | /** @typedef { import("ipfsd-ctl/src/factory") } Factory */ 10 | /** 11 | * @param {Factory} common 12 | * @param {Object} options 13 | */ 14 | module.exports = (common, options) => { 15 | const describe = getDescribe(options) 16 | const it = getIt(options) 17 | 18 | describe('.bootstrap.add', function () { 19 | this.timeout(100 * 1000) 20 | 21 | let ipfs 22 | 23 | before(async () => { 24 | ipfs = (await common.spawn()).api 25 | }) 26 | 27 | after(() => common.clean()) 28 | 29 | it('should return an error when called with an invalid arg', () => { 30 | return expect(ipfs.bootstrap.add(invalidArg)).to.eventually.be.rejected 31 | .and.be.an.instanceOf(Error) 32 | }) 33 | 34 | it('should return a list containing the bootstrap peer when called with a valid arg (ip4)', async () => { 35 | const res = await ipfs.bootstrap.add(validIp4) 36 | 37 | expect(res).to.be.eql({ Peers: [validIp4] }) 38 | const peers = res.Peers 39 | expect(peers).to.have.property('length').that.is.equal(1) 40 | }) 41 | 42 | it('should return a list of bootstrap peers when called with the default option', async () => { 43 | const res = await ipfs.bootstrap.add(null, { default: true }) 44 | 45 | const peers = res.Peers 46 | expect(peers).to.have.property('length').that.is.gt(1) 47 | }) 48 | 49 | it('should prevent duplicate inserts of bootstrap peers', async () => { 50 | await ipfs.bootstrap.rm(null, { all: true }) 51 | 52 | const added = await ipfs.bootstrap.add(validIp4) 53 | expect(added).to.have.property('Peers').that.deep.equals([validIp4]) 54 | 55 | const addedAgain = await ipfs.bootstrap.add(validIp4) 56 | expect(addedAgain).to.have.property('Peers').that.deep.equals([validIp4]) 57 | 58 | const list = await ipfs.bootstrap.list() 59 | expect(list).to.have.property('Peers').that.deep.equals([validIp4]) 60 | }) 61 | }) 62 | } 63 | -------------------------------------------------------------------------------- /src/bootstrap/index.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | const { createSuite } = require('../utils/suite') 3 | 4 | const tests = { 5 | add: require('./add'), 6 | list: require('./list'), 7 | rm: require('./rm') 8 | } 9 | 10 | module.exports = createSuite(tests) 11 | -------------------------------------------------------------------------------- /src/bootstrap/list.js: -------------------------------------------------------------------------------- 1 | /* eslint-env mocha */ 2 | 'use strict' 3 | 4 | const { getDescribe, getIt, expect } = require('../utils/mocha') 5 | 6 | /** @typedef { import("ipfsd-ctl/src/factory") } Factory */ 7 | /** 8 | * @param {Factory} common 9 | * @param {Object} options 10 | */ 11 | module.exports = (common, options) => { 12 | const describe = getDescribe(options) 13 | const it = getIt(options) 14 | 15 | describe('.bootstrap.list', function () { 16 | this.timeout(100 * 1000) 17 | 18 | let ipfs 19 | 20 | before(async () => { ipfs = (await common.spawn()).api }) 21 | 22 | after(() => common.clean()) 23 | 24 | it('should return a list of peers', async () => { 25 | const res = await ipfs.bootstrap.list() 26 | 27 | const peers = res.Peers 28 | expect(peers).to.exist() 29 | }) 30 | }) 31 | } 32 | -------------------------------------------------------------------------------- /src/bootstrap/rm.js: -------------------------------------------------------------------------------- 1 | /* eslint-env mocha */ 2 | 'use strict' 3 | 4 | const { getDescribe, getIt, expect } = require('../utils/mocha') 5 | 6 | /** @typedef { import("ipfsd-ctl/src/factory") } Factory */ 7 | /** 8 | * @param {Factory} common 9 | * @param {Object} options 10 | */ 11 | module.exports = (common, options) => { 12 | const describe = getDescribe(options) 13 | const it = getIt(options) 14 | 15 | const invalidArg = 'this/Is/So/Invalid/' 16 | const validIp4 = '/ip4/104.236.176.52/tcp/4001/p2p/QmSoLnSGccFuZQJzRadHn95W2CrSFmZuTdDWP8HXaHca9z' 17 | 18 | describe('.bootstrap.rm', function () { 19 | this.timeout(100 * 1000) 20 | 21 | let ipfs 22 | 23 | before(async () => { ipfs = (await common.spawn()).api }) 24 | 25 | after(() => common.clean()) 26 | 27 | it('should return an error when called with an invalid arg', () => { 28 | return expect(ipfs.bootstrap.rm(invalidArg)).to.eventually.be.rejected 29 | .and.be.an.instanceOf(Error) 30 | }) 31 | 32 | it('should return an empty list because no peers removed when called without an arg or options', async () => { 33 | const res = await ipfs.bootstrap.rm(null) 34 | 35 | const peers = res.Peers 36 | expect(peers).to.have.property('length').that.is.equal(0) 37 | }) 38 | 39 | it('should return a list containing the peer removed when called with a valid arg (ip4)', async () => { 40 | const addRes = await ipfs.bootstrap.add(validIp4) 41 | expect(addRes).to.be.eql({ Peers: [validIp4] }) 42 | 43 | const rmRes = await ipfs.bootstrap.rm(validIp4) 44 | expect(rmRes).to.be.eql({ Peers: [validIp4] }) 45 | 46 | const peers = rmRes.Peers 47 | expect(peers).to.have.property('length').that.is.equal(1) 48 | }) 49 | 50 | it('should return a list of all peers removed when all option is passed', async () => { 51 | const addRes = await ipfs.bootstrap.add(null, { default: true }) 52 | const addedPeers = addRes.Peers 53 | 54 | const rmRes = await ipfs.bootstrap.rm(null, { all: true }) 55 | const removedPeers = rmRes.Peers 56 | 57 | expect(removedPeers).to.eql(addedPeers) 58 | }) 59 | }) 60 | } 61 | -------------------------------------------------------------------------------- /src/config/get.js: -------------------------------------------------------------------------------- 1 | /* eslint-env mocha */ 2 | 'use strict' 3 | 4 | const { getDescribe, getIt, expect } = require('../utils/mocha') 5 | 6 | /** @typedef { import("ipfsd-ctl/src/factory") } Factory */ 7 | /** 8 | * @param {Factory} common 9 | * @param {Object} options 10 | */ 11 | module.exports = (common, options) => { 12 | const describe = getDescribe(options) 13 | const it = getIt(options) 14 | 15 | describe('.config.get', function () { 16 | this.timeout(30 * 1000) 17 | let ipfs 18 | 19 | before(async () => { ipfs = (await common.spawn()).api }) 20 | 21 | after(() => common.clean()) 22 | 23 | it('should retrieve the whole config', async () => { 24 | const config = await ipfs.config.get() 25 | 26 | expect(config).to.be.an('object') 27 | }) 28 | 29 | it('should retrieve a value through a key', async () => { 30 | const peerId = await ipfs.config.get('Identity.PeerID') 31 | expect(peerId).to.exist() 32 | }) 33 | 34 | it('should retrieve a value through a nested key', async () => { 35 | const swarmAddrs = await ipfs.config.get('Addresses.Swarm') 36 | expect(swarmAddrs).to.exist() 37 | }) 38 | 39 | it('should fail on non valid key', () => { 40 | return expect(ipfs.config.get(1234)).to.eventually.be.rejected() 41 | }) 42 | 43 | it('should fail on non existent key', () => { 44 | return expect(ipfs.config.get('Bananas')).to.eventually.be.rejected() 45 | }) 46 | }) 47 | } 48 | -------------------------------------------------------------------------------- /src/config/index.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | const { createSuite } = require('../utils/suite') 3 | 4 | const tests = { 5 | get: require('./get'), 6 | set: require('./set'), 7 | replace: require('./replace'), 8 | profiles: require('./profiles') 9 | } 10 | 11 | module.exports = createSuite(tests) 12 | -------------------------------------------------------------------------------- /src/config/profiles/apply.js: -------------------------------------------------------------------------------- 1 | /* eslint-env mocha */ 2 | 'use strict' 3 | 4 | const { getDescribe, getIt, expect } = require('../../utils/mocha') 5 | /** @typedef { import("ipfsd-ctl/src/factory") } Factory */ 6 | /** 7 | * @param {Factory} common 8 | * @param {Object} options 9 | */ 10 | module.exports = (common, options) => { 11 | const describe = getDescribe(options) 12 | const it = getIt(options) 13 | 14 | describe('.config.profiles.apply', function () { 15 | this.timeout(30 * 1000) 16 | let ipfs 17 | 18 | before(async () => { 19 | ipfs = (await common.spawn()).api 20 | }) 21 | 22 | after(() => common.clean()) 23 | 24 | it('should apply a config profile', async () => { 25 | const diff = await ipfs.config.profiles.apply('lowpower') 26 | expect(diff.original.Swarm.ConnMgr.LowWater).to.not.equal(diff.updated.Swarm.ConnMgr.LowWater) 27 | 28 | const newConfig = await ipfs.config.get() 29 | expect(newConfig.Swarm.ConnMgr.LowWater).to.equal(diff.updated.Swarm.ConnMgr.LowWater) 30 | }) 31 | 32 | it('should strip private key from diff output', async () => { 33 | const originalConfig = await ipfs.config.get() 34 | const diff = await ipfs.config.profiles.apply('default-networking', { dryRun: true }) 35 | 36 | // should have stripped private key from diff output 37 | expect(originalConfig).to.have.nested.property('Identity.PrivKey') 38 | expect(diff).to.not.have.nested.property('original.Identity.PrivKey') 39 | expect(diff).to.not.have.nested.property('updated.Identity.PrivKey') 40 | }) 41 | 42 | it('should not apply a config profile in dry-run mode', async () => { 43 | const originalConfig = await ipfs.config.get() 44 | 45 | await ipfs.config.profiles.apply('server', { dryRun: true }) 46 | 47 | const updatedConfig = await ipfs.config.get() 48 | 49 | expect(updatedConfig).to.deep.equal(originalConfig) 50 | }) 51 | }) 52 | } 53 | -------------------------------------------------------------------------------- /src/config/profiles/index.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | const { createSuite } = require('../../utils/suite') 3 | 4 | const tests = { 5 | list: require('./list'), 6 | apply: require('./apply') 7 | } 8 | 9 | module.exports = createSuite(tests) 10 | -------------------------------------------------------------------------------- /src/config/profiles/list.js: -------------------------------------------------------------------------------- 1 | /* eslint-env mocha */ 2 | 'use strict' 3 | 4 | const { getDescribe, getIt, expect } = require('../../utils/mocha') 5 | /** @typedef { import("ipfsd-ctl/src/factory") } Factory */ 6 | /** 7 | * @param {Factory} common 8 | * @param {Object} options 9 | */ 10 | module.exports = (common, options) => { 11 | const describe = getDescribe(options) 12 | const it = getIt(options) 13 | 14 | describe('.config.profiles.list', function () { 15 | this.timeout(30 * 1000) 16 | let ipfs 17 | 18 | before(async () => { 19 | ipfs = (await common.spawn()).api 20 | }) 21 | 22 | after(() => common.clean()) 23 | 24 | it('should list config profiles', async () => { 25 | const profiles = await ipfs.config.profiles.list() 26 | 27 | expect(profiles).to.be.an('array') 28 | expect(profiles).not.to.be.empty() 29 | 30 | profiles.forEach(profile => { 31 | expect(profile.name).to.be.a('string') 32 | expect(profile.description).to.be.a('string') 33 | }) 34 | }) 35 | }) 36 | } 37 | -------------------------------------------------------------------------------- /src/config/replace.js: -------------------------------------------------------------------------------- 1 | /* eslint-env mocha */ 2 | 'use strict' 3 | 4 | const { getDescribe, getIt, expect } = require('../utils/mocha') 5 | /** @typedef { import("ipfsd-ctl/src/factory") } Factory */ 6 | /** 7 | * @param {Factory} common 8 | * @param {Object} options 9 | */ 10 | module.exports = (common, options) => { 11 | const describe = getDescribe(options) 12 | const it = getIt(options) 13 | 14 | describe('.config.replace', function () { 15 | this.timeout(30 * 1000) 16 | let ipfs 17 | 18 | before(async () => { 19 | ipfs = (await common.spawn()).api 20 | }) 21 | 22 | after(() => common.clean()) 23 | 24 | const config = { 25 | Fruit: 'Bananas' 26 | } 27 | 28 | it('should replace the whole config', async () => { 29 | await ipfs.config.replace(config) 30 | 31 | const _config = await ipfs.config.get() 32 | expect(_config).to.deep.equal(config) 33 | }) 34 | 35 | it('should replace to empty config', async () => { 36 | await ipfs.config.replace({}) 37 | 38 | const _config = await ipfs.config.get() 39 | expect(_config).to.deep.equal({}) 40 | }) 41 | }) 42 | } 43 | -------------------------------------------------------------------------------- /src/config/set.js: -------------------------------------------------------------------------------- 1 | /* eslint-env mocha */ 2 | 'use strict' 3 | 4 | const { getDescribe, getIt, expect } = require('../utils/mocha') 5 | /** @typedef { import("ipfsd-ctl/src/factory") } Factory */ 6 | /** 7 | * @param {Factory} common 8 | * @param {Object} options 9 | */ 10 | module.exports = (common, options) => { 11 | const describe = getDescribe(options) 12 | const it = getIt(options) 13 | 14 | describe('.config.set', function () { 15 | this.timeout(30 * 1000) 16 | let ipfs 17 | 18 | before(async () => { 19 | ipfs = (await common.spawn()).api 20 | }) 21 | 22 | after(() => common.clean()) 23 | 24 | it('should set a new key', async () => { 25 | await ipfs.config.set('Fruit', 'banana') 26 | 27 | const fruit = await ipfs.config.get('Fruit') 28 | expect(fruit).to.equal('banana') 29 | }) 30 | 31 | it('should set an already existing key', async () => { 32 | await ipfs.config.set('Fruit', 'morango') 33 | 34 | const fruit = await ipfs.config.get('Fruit') 35 | expect(fruit).to.equal('morango') 36 | }) 37 | 38 | it('should set a number', async () => { 39 | const key = 'Discovery.MDNS.Interval' 40 | const val = 11 41 | 42 | await ipfs.config.set(key, val) 43 | 44 | const result = await ipfs.config.get(key) 45 | expect(result).to.equal(val) 46 | }) 47 | 48 | it('should set a boolean', async () => { 49 | const value = true 50 | const key = 'Discovery.MDNS.Enabled' 51 | 52 | await ipfs.config.set(key, value) 53 | expect(await ipfs.config.get(key)).to.equal(value) 54 | }) 55 | 56 | it('should set the other boolean', async () => { 57 | const value = false 58 | const key = 'Discovery.MDNS.Enabled' 59 | 60 | await ipfs.config.set(key, value) 61 | expect(await ipfs.config.get(key)).to.equal(value) 62 | }) 63 | 64 | it('should set a JSON object', async () => { 65 | const key = 'API.HTTPHeaders.Access-Control-Allow-Origin' 66 | const val = ['http://example.io'] 67 | 68 | await ipfs.config.set(key, val) 69 | 70 | const result = await ipfs.config.get(key) 71 | expect(result).to.deep.equal(val) 72 | }) 73 | 74 | it('should fail on non valid key', () => { 75 | return expect(ipfs.config.set(Buffer.from('heeey'), '')).to.eventually.be.rejected() 76 | }) 77 | 78 | it('should fail on non valid value', () => { 79 | return expect(ipfs.config.set('Fruit', Buffer.from('abc'))).to.eventually.be.rejected() 80 | }) 81 | }) 82 | } 83 | -------------------------------------------------------------------------------- /src/dag/index.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | const { createSuite } = require('../utils/suite') 3 | 4 | const tests = { 5 | get: require('./get'), 6 | put: require('./put'), 7 | tree: require('./tree') 8 | } 9 | 10 | module.exports = createSuite(tests) 11 | -------------------------------------------------------------------------------- /src/dag/put.js: -------------------------------------------------------------------------------- 1 | /* eslint-env mocha */ 2 | 'use strict' 3 | 4 | const dagPB = require('ipld-dag-pb') 5 | const DAGNode = dagPB.DAGNode 6 | const dagCBOR = require('ipld-dag-cbor') 7 | const CID = require('cids') 8 | const multihash = require('multihashes') 9 | const { getDescribe, getIt, expect } = require('../utils/mocha') 10 | 11 | /** @typedef { import("ipfsd-ctl/src/factory") } Factory */ 12 | /** 13 | * @param {Factory} common 14 | * @param {Object} options 15 | */ 16 | module.exports = (common, options) => { 17 | const describe = getDescribe(options) 18 | const it = getIt(options) 19 | 20 | describe('.dag.put', () => { 21 | let ipfs 22 | 23 | before(async () => { ipfs = (await common.spawn()).api }) 24 | 25 | after(() => common.clean()) 26 | 27 | let pbNode 28 | let cborNode 29 | 30 | before((done) => { 31 | const someData = Buffer.from('some data') 32 | 33 | try { 34 | pbNode = new DAGNode(someData) 35 | } catch (err) { 36 | return done(err) 37 | } 38 | 39 | cborNode = { 40 | data: someData 41 | } 42 | 43 | done() 44 | }) 45 | 46 | it('should put dag-pb with default hash func (sha2-256)', () => { 47 | return ipfs.dag.put(pbNode, { 48 | format: 'dag-pb', 49 | hashAlg: 'sha2-256' 50 | }) 51 | }) 52 | 53 | it('should put dag-pb with custom hash func (sha3-512)', () => { 54 | return ipfs.dag.put(pbNode, { 55 | format: 'dag-pb', 56 | hashAlg: 'sha3-512' 57 | }) 58 | }) 59 | 60 | it('should put dag-cbor with default hash func (sha2-256)', () => { 61 | return ipfs.dag.put(cborNode, { 62 | format: 'dag-cbor', 63 | hashAlg: 'sha2-256' 64 | }) 65 | }) 66 | 67 | it('should put dag-cbor with custom hash func (sha3-512)', () => { 68 | return ipfs.dag.put(cborNode, { 69 | format: 'dag-cbor', 70 | hashAlg: 'sha3-512' 71 | }) 72 | }) 73 | 74 | it('should return the cid', async () => { 75 | const cid = await ipfs.dag.put(cborNode, { 76 | format: 'dag-cbor', 77 | hashAlg: 'sha2-256' 78 | }) 79 | expect(cid).to.exist() 80 | expect(CID.isCID(cid)).to.equal(true) 81 | 82 | const _cid = await dagCBOR.util.cid(dagCBOR.util.serialize(cborNode)) 83 | expect(cid.buffer).to.eql(_cid.buffer) 84 | }) 85 | 86 | it('should not fail when calling put without options', () => { 87 | return ipfs.dag.put(cborNode) 88 | }) 89 | 90 | it('should set defaults when calling put without options', async () => { 91 | const cid = await ipfs.dag.put(cborNode) 92 | expect(cid.codec).to.equal('dag-cbor') 93 | expect(multihash.decode(cid.multihash).name).to.equal('sha2-256') 94 | }) 95 | 96 | it('should override hash algoritm default and resolve with it', async () => { 97 | const cid = await ipfs.dag.put(cborNode, { 98 | format: 'dag-cbor', 99 | hashAlg: 'sha3-512' 100 | }) 101 | expect(cid.codec).to.equal('dag-cbor') 102 | expect(multihash.decode(cid.multihash).name).to.equal('sha3-512') 103 | }) 104 | 105 | it.skip('should put by passing the cid instead of format and hashAlg', (done) => {}) 106 | }) 107 | } 108 | -------------------------------------------------------------------------------- /src/dag/tree.js: -------------------------------------------------------------------------------- 1 | /* eslint-env mocha */ 2 | 'use strict' 3 | 4 | const dagPB = require('ipld-dag-pb') 5 | const DAGNode = dagPB.DAGNode 6 | const dagCBOR = require('ipld-dag-cbor') 7 | const all = require('it-all') 8 | const { getDescribe, getIt, expect } = require('../utils/mocha') 9 | 10 | /** @typedef { import("ipfsd-ctl/src/factory") } Factory */ 11 | /** 12 | * @param {Factory} common 13 | * @param {Object} options 14 | */ 15 | module.exports = (common, options) => { 16 | const describe = getDescribe(options) 17 | const it = getIt(options) 18 | 19 | describe('.dag.tree', () => { 20 | let ipfs 21 | 22 | before(async () => { ipfs = (await common.spawn()).api }) 23 | 24 | after(() => common.clean()) 25 | 26 | let nodePb 27 | let nodeCbor 28 | let cidPb 29 | let cidCbor 30 | 31 | before(async function () { 32 | nodePb = new DAGNode(Buffer.from('I am inside a Protobuf')) 33 | cidPb = await dagPB.util.cid(nodePb.serialize()) 34 | 35 | nodeCbor = { 36 | someData: 'I am inside a Cbor object', 37 | pb: cidPb 38 | } 39 | cidCbor = await dagCBOR.util.cid(dagCBOR.util.serialize(nodeCbor)) 40 | 41 | await ipfs.dag.put(nodePb, { format: 'dag-pb', hashAlg: 'sha2-256' }) 42 | await ipfs.dag.put(nodeCbor, { format: 'dag-cbor', hashAlg: 'sha2-256' }) 43 | }) 44 | 45 | it('should get tree with CID', async () => { 46 | const paths = await all(ipfs.dag.tree(cidCbor)) 47 | expect(paths).to.eql([ 48 | 'pb', 49 | 'someData' 50 | ]) 51 | }) 52 | 53 | it('should get tree with CID and path', async () => { 54 | const paths = await all(ipfs.dag.tree(cidCbor, 'someData')) 55 | expect(paths).to.eql([]) 56 | }) 57 | 58 | it('should get tree with CID and path as String', async () => { 59 | const cidCborStr = cidCbor.toBaseEncodedString() 60 | 61 | const paths = await all(ipfs.dag.tree(cidCborStr + '/someData')) 62 | expect(paths).to.eql([]) 63 | }) 64 | 65 | it('should get tree with CID recursive (accross different formats)', async () => { 66 | const paths = await all(ipfs.dag.tree(cidCbor, { recursive: true })) 67 | expect(paths).to.have.members([ 68 | 'pb', 69 | 'someData', 70 | 'pb/Links', 71 | 'pb/Data' 72 | ]) 73 | }) 74 | 75 | it('should get tree with CID and path recursive', async () => { 76 | const paths = await all(ipfs.dag.tree(cidCbor, 'pb', { recursive: true })) 77 | expect(paths).to.have.members([ 78 | 'Links', 79 | 'Data' 80 | ]) 81 | }) 82 | }) 83 | } 84 | -------------------------------------------------------------------------------- /src/dht/find-peer.js: -------------------------------------------------------------------------------- 1 | /* eslint-env mocha */ 2 | 'use strict' 3 | 4 | const { getDescribe, getIt, expect } = require('../utils/mocha') 5 | 6 | /** @typedef { import("ipfsd-ctl/src/factory") } Factory */ 7 | /** 8 | * @param {Factory} common 9 | * @param {Object} options 10 | */ 11 | module.exports = (common, options) => { 12 | const describe = getDescribe(options) 13 | const it = getIt(options) 14 | 15 | describe('.dht.findPeer', function () { 16 | this.timeout(80 * 1000) 17 | 18 | let nodeA 19 | let nodeB 20 | 21 | before(async () => { 22 | nodeA = (await common.spawn()).api 23 | nodeB = (await common.spawn()).api 24 | await nodeB.swarm.connect(nodeA.peerId.addresses[0]) 25 | }) 26 | 27 | after(() => common.clean()) 28 | 29 | it('should find other peers', async () => { 30 | const nodeBId = await nodeB.id() 31 | const res = await nodeA.dht.findPeer(nodeBId.id) 32 | const id = res.id.toString() 33 | 34 | const nodeAddresses = nodeBId.addresses.map((addr) => addr.nodeAddress()) 35 | const peerAddresses = res.addrs.map(ma => ma.nodeAddress()) 36 | 37 | expect(id).to.be.eql(nodeB.peerId.id) 38 | expect(peerAddresses).to.deep.include(nodeAddresses[0]) 39 | }) 40 | 41 | it('should fail to find other peer if peer does not exist', () => { 42 | return expect(nodeA.dht.findPeer('Qmd7qZS4T7xXtsNFdRoK1trfMs5zU94EpokQ9WFtxdPxsZ')).to.eventually.be.rejected() 43 | }) 44 | }) 45 | } 46 | -------------------------------------------------------------------------------- /src/dht/find-provs.js: -------------------------------------------------------------------------------- 1 | /* eslint-env mocha */ 2 | 'use strict' 3 | 4 | const { getDescribe, getIt, expect } = require('../utils/mocha') 5 | const all = require('it-all') 6 | const { fakeCid } = require('./utils') 7 | 8 | /** @typedef { import("ipfsd-ctl/src/factory") } Factory */ 9 | /** 10 | * @param {Factory} common 11 | * @param {Object} options 12 | */ 13 | module.exports = (common, options) => { 14 | const describe = getDescribe(options) 15 | const it = getIt(options) 16 | 17 | describe('.dht.findProvs', function () { 18 | this.timeout(20000) 19 | let nodeA 20 | let nodeB 21 | let nodeC 22 | 23 | before(async () => { 24 | nodeA = (await common.spawn()).api 25 | nodeB = (await common.spawn()).api 26 | nodeC = (await common.spawn()).api 27 | await Promise.all([ 28 | nodeB.swarm.connect(nodeA.peerId.addresses[0]), 29 | nodeC.swarm.connect(nodeB.peerId.addresses[0]) 30 | ]) 31 | }) 32 | 33 | after(() => common.clean()) 34 | 35 | let providedCid 36 | before('add providers for the same cid', async function () { 37 | this.timeout(10 * 1000) 38 | 39 | const cids = await Promise.all([ 40 | nodeB.object.new('unixfs-dir'), 41 | nodeC.object.new('unixfs-dir') 42 | ]) 43 | 44 | providedCid = cids[0] 45 | 46 | await Promise.all([ 47 | all(nodeB.dht.provide(providedCid)), 48 | all(nodeC.dht.provide(providedCid)) 49 | ]) 50 | }) 51 | 52 | it('should be able to find providers', async function () { 53 | this.timeout(20 * 1000) 54 | 55 | const provs = await all(nodeA.dht.findProvs(providedCid, { numProviders: 2 })) 56 | const providerIds = provs.map((p) => p.id.toString()) 57 | 58 | expect(providerIds).to.have.members([ 59 | nodeB.peerId.id, 60 | nodeC.peerId.id 61 | ]) 62 | }) 63 | 64 | it('should take options to override timeout config', async function () { 65 | const options = { 66 | timeout: 1 67 | } 68 | 69 | const cidV0 = await fakeCid() 70 | 71 | await expect(all(nodeA.dht.findProvs(cidV0, options))).to.be.rejected() 72 | }) 73 | }) 74 | } 75 | -------------------------------------------------------------------------------- /src/dht/get.js: -------------------------------------------------------------------------------- 1 | /* eslint-env mocha */ 2 | 'use strict' 3 | 4 | const hat = require('hat') 5 | const { getDescribe, getIt, expect } = require('../utils/mocha') 6 | 7 | /** @typedef { import("ipfsd-ctl/src/factory") } Factory */ 8 | /** 9 | * @param {Factory} common 10 | * @param {Object} options 11 | */ 12 | module.exports = (common, options) => { 13 | const describe = getDescribe(options) 14 | const it = getIt(options) 15 | 16 | describe('.dht.get', function () { 17 | this.timeout(80 * 1000) 18 | 19 | let nodeA 20 | let nodeB 21 | 22 | before(async () => { 23 | nodeA = (await common.spawn()).api 24 | nodeB = (await common.spawn()).api 25 | await nodeA.swarm.connect(nodeB.peerId.addresses[0]) 26 | }) 27 | 28 | after(() => common.clean()) 29 | 30 | it('should error when getting a non-existent key from the DHT', () => { 31 | return expect(nodeA.dht.get('non-existing', { timeout: 100 })).to.eventually.be.rejected 32 | .and.be.an.instanceOf(Error) 33 | }) 34 | 35 | // TODO: revisit this test - it puts an invalid key and so go-ipfs throws 36 | // "invalid record keytype" - it needs to put a valid key and value for it to 37 | // be a useful test. 38 | it.skip('should get a value after it was put on another node', async () => { 39 | const key = Buffer.from(hat()) 40 | const value = Buffer.from(hat()) 41 | 42 | await nodeB.dht.put(key, value) 43 | const result = await nodeA.dht.get(key) 44 | 45 | expect(result).to.eql(value) 46 | }) 47 | }) 48 | } 49 | -------------------------------------------------------------------------------- /src/dht/index.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | const { createSuite } = require('../utils/suite') 3 | 4 | const tests = { 5 | put: require('./put'), 6 | get: require('./get'), 7 | findPeer: require('./find-peer'), 8 | provide: require('./provide'), 9 | findProvs: require('./find-provs'), 10 | query: require('./query') 11 | } 12 | 13 | module.exports = createSuite(tests) 14 | -------------------------------------------------------------------------------- /src/dht/provide.js: -------------------------------------------------------------------------------- 1 | /* eslint-env mocha */ 2 | 'use strict' 3 | 4 | const CID = require('cids') 5 | const all = require('it-all') 6 | const { getDescribe, getIt, expect } = require('../utils/mocha') 7 | 8 | /** @typedef { import("ipfsd-ctl/src/factory") } Factory */ 9 | /** 10 | * @param {Factory} common 11 | * @param {Object} options 12 | */ 13 | module.exports = (common, options) => { 14 | const describe = getDescribe(options) 15 | const it = getIt(options) 16 | 17 | describe('.dht.provide', function () { 18 | this.timeout(80 * 1000) 19 | 20 | let ipfs 21 | 22 | before(async () => { 23 | ipfs = (await common.spawn()).api 24 | const nodeB = (await common.spawn()).api 25 | await ipfs.swarm.connect(nodeB.peerId.addresses[0]) 26 | }) 27 | 28 | after(() => common.clean()) 29 | 30 | it('should provide local CID', async () => { 31 | const res = await all(ipfs.add(Buffer.from('test'))) 32 | 33 | await all(ipfs.dht.provide(res[0].cid)) 34 | }) 35 | 36 | it('should not provide if block not found locally', () => { 37 | const cid = new CID('Qmd7qZS4T7xXtsNFdRoK1trfMs5zU94EpokQ9WFtxdPxsZ') 38 | 39 | return expect(all(ipfs.dht.provide(cid))).to.eventually.be.rejected 40 | .and.be.an.instanceOf(Error) 41 | .and.have.property('message') 42 | .that.include('not found locally') 43 | }) 44 | 45 | it('should allow multiple CIDs to be passed', async () => { 46 | const res = await all(ipfs.add([ 47 | { content: Buffer.from('t0') }, 48 | { content: Buffer.from('t1') } 49 | ])) 50 | 51 | await all(ipfs.dht.provide(res.map(f => f.cid))) 52 | }) 53 | 54 | it('should provide a CIDv1', async () => { 55 | const res = await all(ipfs.add(Buffer.from('test'), { cidVersion: 1 })) 56 | await all(ipfs.dht.provide(res[0].cid)) 57 | }) 58 | 59 | it('should error on non CID arg', () => { 60 | return expect(all(ipfs.dht.provide({}))).to.eventually.be.rejected() 61 | }) 62 | 63 | it('should error on array containing non CID arg', () => { 64 | return expect(all(ipfs.dht.provide([{}]))).to.eventually.be.rejected() 65 | }) 66 | }) 67 | } 68 | -------------------------------------------------------------------------------- /src/dht/put.js: -------------------------------------------------------------------------------- 1 | /* eslint-env mocha */ 2 | 'use strict' 3 | 4 | const { getDescribe, getIt } = require('../utils/mocha') 5 | 6 | /** @typedef { import("ipfsd-ctl/src/factory") } Factory */ 7 | /** 8 | * @param {Factory} common 9 | * @param {Object} options 10 | */ 11 | module.exports = (common, options) => { 12 | const describe = getDescribe(options) 13 | const it = getIt(options) 14 | 15 | describe('.dht.put', function () { 16 | this.timeout(80 * 1000) 17 | 18 | let nodeA 19 | let nodeB 20 | 21 | before(async () => { 22 | nodeA = (await common.spawn()).api 23 | nodeB = (await common.spawn()).api 24 | await nodeA.swarm.connect(nodeB.peerId.addresses[0]) 25 | }) 26 | 27 | after(() => common.clean()) 28 | 29 | it('should put a value to the DHT', async function () { 30 | this.timeout(80 * 1000) 31 | 32 | const key = Buffer.from('QmUNLLsPACCz1vLxQVkXqqLX5R1X345qqfHbsf67hvA3Nn') 33 | const data = Buffer.from('data') 34 | 35 | await nodeA.dht.put(key, data) 36 | }) 37 | }) 38 | } 39 | -------------------------------------------------------------------------------- /src/dht/query.js: -------------------------------------------------------------------------------- 1 | /* eslint-env mocha */ 2 | 'use strict' 3 | 4 | const { getDescribe, getIt, expect } = require('../utils/mocha') 5 | const all = require('it-all') 6 | 7 | /** @typedef { import("ipfsd-ctl/src/factory") } Factory */ 8 | /** 9 | * @param {Factory} common 10 | * @param {Object} options 11 | */ 12 | module.exports = (common, options) => { 13 | const describe = getDescribe(options) 14 | const it = getIt(options) 15 | 16 | describe('.dht.query', function () { 17 | this.timeout(80 * 1000) 18 | 19 | let nodeA 20 | let nodeB 21 | 22 | before(async () => { 23 | nodeA = (await common.spawn()).api 24 | nodeB = (await common.spawn()).api 25 | await nodeB.swarm.connect(nodeA.peerId.addresses[0]) 26 | }) 27 | 28 | after(() => common.clean()) 29 | 30 | it('should return the other node in the query', async function () { 31 | const timeout = 150 * 1000 32 | this.timeout(timeout) 33 | 34 | try { 35 | const peers = await all(nodeA.dht.query(nodeB.peerId.id, { timeout: timeout - 1000 })) 36 | expect(peers.map(p => p.id.toString())).to.include(nodeB.peerId.id) 37 | } catch (err) { 38 | if (err.name === 'TimeoutError') { 39 | // This test is meh. DHT works best with >= 20 nodes. Therefore a 40 | // failure might happen, but we don't want to report it as such. 41 | // Hence skip the test before the timeout is reached 42 | this.skip() 43 | } else { 44 | throw err 45 | } 46 | } 47 | }) 48 | }) 49 | } 50 | -------------------------------------------------------------------------------- /src/dht/utils.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const multihashing = require('multihashing-async') 4 | const CID = require('cids') 5 | 6 | exports.fakeCid = async (data) => { 7 | const bytes = data || Buffer.from(`TEST${Date.now()}`) 8 | const mh = await multihashing(bytes, 'sha2-256') 9 | return new CID(0, 'dag-pb', mh) 10 | } 11 | -------------------------------------------------------------------------------- /src/files/chmod.js: -------------------------------------------------------------------------------- 1 | /* eslint-env mocha */ 2 | 'use strict' 3 | 4 | const hat = require('hat') 5 | const { getDescribe, getIt, expect } = require('../utils/mocha') 6 | 7 | module.exports = (common, options) => { 8 | const describe = getDescribe(options) 9 | const it = getIt(options) 10 | 11 | describe('.files.chmod', function () { 12 | this.timeout(40 * 1000) 13 | 14 | let ipfs 15 | 16 | async function testMode (mode, expectedMode) { 17 | const testPath = `/test-${hat()}` 18 | 19 | await ipfs.files.write(testPath, Buffer.from('Hello, world!'), { 20 | create: true 21 | }) 22 | await ipfs.files.chmod(testPath, mode) 23 | 24 | const stat = await ipfs.files.stat(testPath) 25 | expect(stat).to.have.property('mode').that.equals(expectedMode) 26 | } 27 | 28 | before(async () => { 29 | ipfs = (await common.spawn()).api 30 | }) 31 | 32 | after(() => common.clean()) 33 | 34 | it('should change file mode', async function () { 35 | const mode = parseInt('544', 8) 36 | await testMode(mode, mode) 37 | }) 38 | 39 | it('should change file mode as string', async function () { 40 | const mode = parseInt('544', 8) 41 | await testMode('544', mode) 42 | }) 43 | 44 | it('should change file mode to 0', async function () { 45 | const mode = 0 46 | await testMode(mode, mode) 47 | }) 48 | 49 | it('should change directory mode', async function () { 50 | const testPath = `/test-${hat()}` 51 | const mode = parseInt('544', 8) 52 | 53 | await ipfs.files.mkdir(testPath, { 54 | create: true 55 | }) 56 | await ipfs.files.chmod(testPath, mode) 57 | 58 | const stat = await ipfs.files.stat(testPath) 59 | expect(stat).to.have.property('mode').that.equals(mode) 60 | }) 61 | }) 62 | } 63 | -------------------------------------------------------------------------------- /src/files/cp.js: -------------------------------------------------------------------------------- 1 | /* eslint-env mocha */ 2 | 'use strict' 3 | 4 | const hat = require('hat') 5 | const all = require('it-all') 6 | const concat = require('it-concat') 7 | const { fixtures } = require('../utils') 8 | const { getDescribe, getIt, expect } = require('../utils/mocha') 9 | 10 | /** @typedef { import("ipfsd-ctl/src/factory") } Factory */ 11 | /** 12 | * @param {Factory} common 13 | * @param {Object} options 14 | */ 15 | module.exports = (common, options) => { 16 | const describe = getDescribe(options) 17 | const it = getIt(options) 18 | 19 | describe('.files.cp', function () { 20 | this.timeout(40 * 1000) 21 | 22 | let ipfs 23 | 24 | before(async () => { ipfs = (await common.spawn()).api }) 25 | 26 | after(() => common.clean()) 27 | 28 | it('should copy file, expect error', () => { 29 | const testDir = `/test-${hat()}` 30 | 31 | return expect(ipfs.files.cp(`${testDir}/c`, `${testDir}/b`)).to.eventually.be.rejected() 32 | }) 33 | 34 | it('should copy file, expect no error', async () => { 35 | const testDir = `/test-${hat()}` 36 | 37 | await ipfs.files.mkdir(testDir, { parents: true }) 38 | await ipfs.files.write(`${testDir}/a`, Buffer.from('TEST'), { create: true }) 39 | await ipfs.files.cp(`${testDir}/a`, `${testDir}/b`) 40 | }) 41 | 42 | it('should copy dir, expect error', () => { 43 | const testDir = `/test-${hat()}` 44 | 45 | return expect(ipfs.files.cp(`${testDir}/lv1/lv3`, `${testDir}/lv1/lv4`)).to.eventually.be.rejected() 46 | }) 47 | 48 | it('should copy dir, expect no error', async () => { 49 | const testDir = `/test-${hat()}` 50 | 51 | await ipfs.files.mkdir(`${testDir}/lv1/lv2`, { parents: true }) 52 | await ipfs.files.cp(`${testDir}/lv1/lv2`, `${testDir}/lv1/lv3`) 53 | }) 54 | 55 | it('should copy from outside of mfs', async () => { 56 | const [{ cid }] = await all(ipfs.add(fixtures.smallFile.data)) 57 | const testFilePath = `/${hat()}` 58 | await ipfs.files.cp(`/ipfs/${cid}`, testFilePath) 59 | const testFileData = await concat(ipfs.files.read(testFilePath)) 60 | expect(testFileData.slice()).to.eql(fixtures.smallFile.data) 61 | }) 62 | 63 | it('should respect metadata when copying files', async function () { 64 | const testSrcPath = `/test-${hat()}` 65 | const testDestPath = `/test-${hat()}` 66 | const mode = parseInt('0321', 8) 67 | const mtime = new Date() 68 | const seconds = Math.floor(mtime.getTime() / 1000) 69 | const expectedMtime = { 70 | secs: seconds, 71 | nsecs: (mtime - (seconds * 1000)) * 1000 72 | } 73 | 74 | await ipfs.files.write(testSrcPath, Buffer.from('TEST'), { 75 | create: true, 76 | mode, 77 | mtime 78 | }) 79 | await ipfs.files.cp(testSrcPath, testDestPath) 80 | 81 | const stats = await ipfs.files.stat(testDestPath) 82 | expect(stats).to.have.deep.property('mtime', expectedMtime) 83 | expect(stats).to.have.property('mode', mode) 84 | }) 85 | 86 | it('should respect metadata when copying directories', async function () { 87 | const testSrcPath = `/test-${hat()}` 88 | const testDestPath = `/test-${hat()}` 89 | const mode = parseInt('0321', 8) 90 | const mtime = new Date() 91 | const seconds = Math.floor(mtime.getTime() / 1000) 92 | const expectedMtime = { 93 | secs: seconds, 94 | nsecs: (mtime - (seconds * 1000)) * 1000 95 | } 96 | 97 | await ipfs.files.mkdir(testSrcPath, { 98 | mode, 99 | mtime 100 | }) 101 | await ipfs.files.cp(testSrcPath, testDestPath, { 102 | recursive: true 103 | }) 104 | 105 | const stats = await ipfs.files.stat(testDestPath) 106 | expect(stats).to.have.deep.property('mtime', expectedMtime) 107 | expect(stats).to.have.property('mode', mode) 108 | }) 109 | 110 | it('should respect metadata when copying from outside of mfs', async function () { 111 | const testDestPath = `/test-${hat()}` 112 | const mode = parseInt('0321', 8) 113 | const mtime = new Date() 114 | const seconds = Math.floor(mtime.getTime() / 1000) 115 | const expectedMtime = { 116 | secs: seconds, 117 | nsecs: (mtime - (seconds * 1000)) * 1000 118 | } 119 | 120 | const [{ 121 | cid 122 | }] = await all(ipfs.add({ 123 | content: fixtures.smallFile.data, 124 | mode, 125 | mtime 126 | })) 127 | await ipfs.files.cp(`/ipfs/${cid}`, testDestPath) 128 | 129 | const stats = await ipfs.files.stat(testDestPath) 130 | expect(stats).to.have.deep.property('mtime', expectedMtime) 131 | expect(stats).to.have.property('mode', mode) 132 | }) 133 | }) 134 | } 135 | -------------------------------------------------------------------------------- /src/files/flush.js: -------------------------------------------------------------------------------- 1 | /* eslint-env mocha */ 2 | 'use strict' 3 | 4 | const hat = require('hat') 5 | const { getDescribe, getIt, expect } = require('../utils/mocha') 6 | 7 | /** @typedef { import("ipfsd-ctl/src/factory") } Factory */ 8 | /** 9 | * @param {Factory} common 10 | * @param {Object} options 11 | */ 12 | module.exports = (common, options) => { 13 | const describe = getDescribe(options) 14 | const it = getIt(options) 15 | 16 | describe('.files.flush', function () { 17 | this.timeout(40 * 1000) 18 | 19 | let ipfs 20 | 21 | before(async () => { ipfs = (await common.spawn()).api }) 22 | 23 | after(() => common.clean()) 24 | 25 | it('should not flush not found file/dir, expect error', async () => { 26 | const testDir = `/test-${hat()}` 27 | 28 | try { 29 | await ipfs.files.flush(`${testDir}/404`) 30 | } catch (err) { 31 | expect(err).to.exist() 32 | } 33 | }) 34 | 35 | it('should flush root', async () => { 36 | const root = await ipfs.files.stat('/') 37 | const flushed = await ipfs.files.flush() 38 | 39 | expect(root.cid.toString()).to.equal(flushed.toString()) 40 | }) 41 | 42 | it('should flush specific dir', async () => { 43 | const testDir = `/test-${hat()}` 44 | 45 | await ipfs.files.mkdir(testDir, { parents: true }) 46 | 47 | const dirStats = await ipfs.files.stat(testDir) 48 | const flushed = await ipfs.files.flush(testDir) 49 | 50 | expect(dirStats.cid.toString()).to.equal(flushed.toString()) 51 | }) 52 | }) 53 | } 54 | -------------------------------------------------------------------------------- /src/files/index.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const { createSuite } = require('../utils/suite') 4 | 5 | const tests = { 6 | chmod: require('./chmod'), 7 | mkdir: require('./mkdir'), 8 | write: require('./write'), 9 | cp: require('./cp'), 10 | mv: require('./mv'), 11 | rm: require('./rm'), 12 | stat: require('./stat'), 13 | read: require('./read'), 14 | ls: require('./ls'), 15 | flush: require('./flush'), 16 | touch: require('./touch') 17 | } 18 | 19 | module.exports = createSuite(tests) 20 | -------------------------------------------------------------------------------- /src/files/ls.js: -------------------------------------------------------------------------------- 1 | /* eslint-env mocha */ 2 | 'use strict' 3 | 4 | const hat = require('hat') 5 | const all = require('it-all') 6 | const { fixtures } = require('../utils') 7 | const { getDescribe, getIt, expect } = require('../utils/mocha') 8 | 9 | /** @typedef { import("ipfsd-ctl/src/factory") } Factory */ 10 | /** 11 | * @param {Factory} common 12 | * @param {Object} options 13 | */ 14 | module.exports = (common, options) => { 15 | const describe = getDescribe(options) 16 | const it = getIt(options) 17 | 18 | describe('.files.ls', function () { 19 | this.timeout(40 * 1000) 20 | 21 | let ipfs 22 | 23 | before(async () => { ipfs = (await common.spawn()).api }) 24 | 25 | after(() => common.clean()) 26 | 27 | it('should not ls not found file/dir, expect error', () => { 28 | const testDir = `/test-${hat()}` 29 | 30 | return expect(all(ipfs.files.ls(`${testDir}/404`))).to.eventually.be.rejected() 31 | }) 32 | 33 | it('should ls directory', async () => { 34 | const testDir = `/test-${hat()}` 35 | 36 | await ipfs.files.mkdir(`${testDir}/lv1`, { parents: true }) 37 | await ipfs.files.write(`${testDir}/b`, Buffer.from('Hello, world!'), { create: true }) 38 | 39 | const entries = await all(ipfs.files.ls(testDir)) 40 | 41 | expect(entries).to.have.lengthOf(2) 42 | expect(entries[0].name).to.equal('b') 43 | expect(entries[0].type).to.equal(0) 44 | expect(entries[0].size).to.equal(13) 45 | expect(entries[0].cid.toString()).to.equal('QmcZojhwragQr5qhTeFAmELik623Z21e3jBTpJXoQ9si1T') 46 | expect(entries[1].name).to.equal('lv1') 47 | expect(entries[1].type).to.equal(1) 48 | expect(entries[1].size).to.equal(0) 49 | expect(entries[1].cid.toString()).to.equal('QmUNLLsPACCz1vLxQVkXqqLX5R1X345qqfHbsf67hvA3Nn') 50 | }) 51 | 52 | it('should ls directory and include metadata', async () => { 53 | const testDir = `/test-${hat()}` 54 | 55 | await ipfs.files.mkdir(`${testDir}/lv1`, { 56 | parents: true, 57 | mtime: { 58 | secs: 5 59 | } 60 | }) 61 | await ipfs.files.write(`${testDir}/b`, Buffer.from('Hello, world!'), { 62 | create: true, 63 | mtime: { 64 | secs: 5 65 | } 66 | }) 67 | 68 | const entries = await all(ipfs.files.ls(testDir, { long: true })) 69 | 70 | expect(entries).to.have.lengthOf(2) 71 | expect(entries[0].cid.toString()).to.equal('QmTVnczjg445RUAEYNH1wvhVa2rnPoWMfHMxQc6W7HHoyM') 72 | expect(entries[0].mode).to.equal(0o0644) 73 | expect(entries[0].mtime).to.deep.equal({ 74 | secs: 5, 75 | nsecs: 0 76 | }) 77 | expect(entries[1].cid.toString()).to.equal('QmXkBjmbtWUxXLa3s541UBSzPgvaAR7b8X3Amcp5D1VKTQ') 78 | expect(entries[1].mode).to.equal(0o0755) 79 | expect(entries[1].mtime).to.deep.equal({ 80 | secs: 5, 81 | nsecs: 0 82 | }) 83 | }) 84 | 85 | it('should ls from outside of mfs', async () => { 86 | const testFileName = hat() 87 | const [{ 88 | cid 89 | }] = await all(ipfs.add({ path: `/test/${testFileName}`, content: fixtures.smallFile.data })) 90 | const listing = await all(ipfs.files.ls('/ipfs/' + cid)) 91 | expect(listing).to.have.length(1) 92 | expect(listing[0].name).to.equal(cid.toString()) 93 | }) 94 | 95 | it('should list an empty directory', async () => { 96 | const testDir = `/test-${hat()}` 97 | await ipfs.files.mkdir(testDir) 98 | const contents = await all(ipfs.files.ls(testDir)) 99 | 100 | expect(contents).to.be.an('array').and.to.be.empty() 101 | }) 102 | 103 | it('should list a file directly', async () => { 104 | const fileName = `single-file-${hat()}.txt` 105 | const filePath = `/${fileName}` 106 | await ipfs.files.write(filePath, Buffer.from('Hello world'), { 107 | create: true 108 | }) 109 | const entries = await all(ipfs.files.ls(filePath)) 110 | 111 | expect(entries).to.have.lengthOf(1) 112 | expect(entries[0].name).to.equal(fileName) 113 | expect(entries[0].type).to.equal(0) 114 | expect(entries[0].size).to.equal(11) 115 | expect(entries[0].cid.toString()).to.equal('Qmetpc7cZmN25Wcc6R27cGCAvCDqCS5GjHG4v7xABEfpmJ') 116 | }) 117 | }) 118 | } 119 | -------------------------------------------------------------------------------- /src/files/mkdir.js: -------------------------------------------------------------------------------- 1 | /* eslint-env mocha */ 2 | 'use strict' 3 | 4 | const hat = require('hat') 5 | const { getDescribe, getIt, expect } = require('../utils/mocha') 6 | 7 | /** @typedef { import("ipfsd-ctl/src/factory") } Factory */ 8 | /** 9 | * @param {Factory} common 10 | * @param {Object} options 11 | */ 12 | module.exports = (common, options) => { 13 | const describe = getDescribe(options) 14 | const it = getIt(options) 15 | 16 | describe('.files.mkdir', function () { 17 | this.timeout(40 * 1000) 18 | 19 | let ipfs 20 | 21 | async function testMode (mode, expectedMode) { 22 | const testPath = `/test-${hat()}` 23 | await ipfs.files.mkdir(testPath, { 24 | mode 25 | }) 26 | 27 | const stats = await ipfs.files.stat(testPath) 28 | expect(stats).to.have.property('mode', expectedMode) 29 | } 30 | 31 | async function testMtime (mtime, expectedMtime) { 32 | const testPath = `/test-${hat()}` 33 | await ipfs.files.mkdir(testPath, { 34 | mtime 35 | }) 36 | 37 | const stats = await ipfs.files.stat(testPath) 38 | expect(stats).to.have.deep.property('mtime', expectedMtime) 39 | } 40 | 41 | before(async () => { ipfs = (await common.spawn()).api }) 42 | 43 | after(() => common.clean()) 44 | 45 | it('should make directory on root', () => { 46 | const testDir = `/test-${hat()}` 47 | 48 | return ipfs.files.mkdir(testDir) 49 | }) 50 | 51 | it('should make directory and its parents', () => { 52 | const testDir = `/test-${hat()}` 53 | 54 | return ipfs.files.mkdir(`${testDir}/lv1/lv2`, { parents: true }) 55 | }) 56 | 57 | it('should not make already existent directory', () => { 58 | return expect(ipfs.files.mkdir('/')).to.eventually.be.rejected() 59 | }) 60 | 61 | it('should make directory and have default mode', async function () { 62 | await testMode(undefined, parseInt('0755', 8)) 63 | }) 64 | 65 | it('should make directory and specify mode as string', async function () { 66 | const mode = '0321' 67 | await testMode(mode, parseInt(mode, 8)) 68 | }) 69 | 70 | it('should make directory and specify mode as number', async function () { 71 | const mode = parseInt('0321', 8) 72 | await testMode(mode, mode) 73 | }) 74 | 75 | it('should make directory and specify mtime as Date', async function () { 76 | const mtime = new Date(5000) 77 | await testMtime(mtime, { 78 | secs: 5, 79 | nsecs: 0 80 | }) 81 | }) 82 | 83 | it('should make directory and specify mtime as { nsecs, secs }', async function () { 84 | const mtime = { 85 | secs: 5, 86 | nsecs: 0 87 | } 88 | await testMtime(mtime, mtime) 89 | }) 90 | 91 | it('should make directory and specify mtime as timespec', async function () { 92 | await testMtime({ 93 | Seconds: 5, 94 | FractionalNanoseconds: 0 95 | }, { 96 | secs: 5, 97 | nsecs: 0 98 | }) 99 | }) 100 | 101 | it('should make directory and specify mtime as hrtime', async function () { 102 | const mtime = process.hrtime() 103 | await testMtime(mtime, { 104 | secs: mtime[0], 105 | nsecs: mtime[1] 106 | }) 107 | }) 108 | }) 109 | } 110 | -------------------------------------------------------------------------------- /src/files/mv.js: -------------------------------------------------------------------------------- 1 | /* eslint-env mocha */ 2 | 'use strict' 3 | 4 | const hat = require('hat') 5 | const { getDescribe, getIt, expect } = require('../utils/mocha') 6 | 7 | /** @typedef { import("ipfsd-ctl/src/factory") } Factory */ 8 | /** 9 | * @param {Factory} common 10 | * @param {Object} options 11 | */ 12 | module.exports = (common, options) => { 13 | const describe = getDescribe(options) 14 | const it = getIt(options) 15 | 16 | describe('.files.mv', function () { 17 | this.timeout(40 * 1000) 18 | 19 | let ipfs 20 | 21 | before(async () => { ipfs = (await common.spawn()).api }) 22 | 23 | before(async () => { 24 | await ipfs.files.mkdir('/test/lv1/lv2', { parents: true }) 25 | await ipfs.files.write('/test/a', Buffer.from('Hello, world!'), { create: true }) 26 | }) 27 | after(() => common.clean()) 28 | 29 | it('should not move not found file/dir, expect error', () => { 30 | const testDir = `/test-${hat()}` 31 | 32 | return expect(ipfs.files.mv(`${testDir}/404`, `${testDir}/a`)).to.eventually.be.rejected() 33 | }) 34 | 35 | it('should move file, expect no error', async () => { 36 | const testDir = `/test-${hat()}` 37 | 38 | await ipfs.files.mkdir(`${testDir}/lv1/lv2`, { parents: true }) 39 | await ipfs.files.write(`${testDir}/a`, Buffer.from('Hello, world!'), { create: true }) 40 | 41 | await ipfs.files.mv(`${testDir}/a`, `${testDir}/c`) 42 | }) 43 | 44 | it('should move dir, expect no error', async () => { 45 | const testDir = `/test-${hat()}` 46 | 47 | await ipfs.files.mkdir(`${testDir}/lv1/lv2`, { parents: true }) 48 | await ipfs.files.mv('/test/lv1/lv2', '/test/lv1/lv4') 49 | }) 50 | }) 51 | } 52 | -------------------------------------------------------------------------------- /src/files/read.js: -------------------------------------------------------------------------------- 1 | /* eslint-env mocha */ 2 | 'use strict' 3 | 4 | const hat = require('hat') 5 | const concat = require('it-concat') 6 | const all = require('it-all') 7 | const { fixtures } = require('../utils') 8 | const { getDescribe, getIt, expect } = require('../utils/mocha') 9 | 10 | /** @typedef { import("ipfsd-ctl/src/factory") } Factory */ 11 | /** 12 | * @param {Factory} common 13 | * @param {Object} options 14 | */ 15 | module.exports = (common, options) => { 16 | const describe = getDescribe(options) 17 | const it = getIt(options) 18 | 19 | describe('.files.read', function () { 20 | this.timeout(40 * 1000) 21 | 22 | let ipfs 23 | 24 | before(async () => { ipfs = (await common.spawn()).api }) 25 | 26 | after(() => common.clean()) 27 | 28 | it('should not read not found, expect error', () => { 29 | const testDir = `/test-${hat()}` 30 | 31 | return expect(ipfs.files.cp(`${testDir}/c`, `${testDir}/b`)).to.eventually.be.rejected 32 | .and.be.an.instanceOf(Error) 33 | .and.to.have.property('message') 34 | .that.include('does not exist') 35 | }) 36 | 37 | it('should read file', async () => { 38 | const testDir = `/test-${hat()}` 39 | 40 | await ipfs.files.mkdir(testDir) 41 | await ipfs.files.write(`${testDir}/a`, Buffer.from('Hello, world!'), { create: true }) 42 | 43 | const buf = await concat(ipfs.files.read(`${testDir}/a`)) 44 | 45 | expect(buf.slice()).to.eql(Buffer.from('Hello, world!')) 46 | }) 47 | 48 | it('should read from outside of mfs', async () => { 49 | const [{ cid }] = await all(ipfs.add(fixtures.smallFile.data)) 50 | const testFileData = await concat(ipfs.files.read(`/ipfs/${cid}`)) 51 | expect(testFileData.slice()).to.eql(fixtures.smallFile.data) 52 | }) 53 | }) 54 | } 55 | -------------------------------------------------------------------------------- /src/files/rm.js: -------------------------------------------------------------------------------- 1 | /* eslint-env mocha */ 2 | 'use strict' 3 | 4 | const hat = require('hat') 5 | const all = require('it-all') 6 | const { getDescribe, getIt, expect } = require('../utils/mocha') 7 | 8 | /** @typedef { import("ipfsd-ctl/src/factory") } Factory */ 9 | /** 10 | * @param {Factory} common 11 | * @param {Object} options 12 | */ 13 | module.exports = (common, options) => { 14 | const describe = getDescribe(options) 15 | const it = getIt(options) 16 | 17 | describe('.files.rm', function () { 18 | this.timeout(40 * 1000) 19 | 20 | let ipfs 21 | 22 | before(async () => { ipfs = (await common.spawn()).api }) 23 | 24 | after(() => common.clean()) 25 | 26 | it('should not remove not found file/dir, expect error', () => { 27 | const testDir = `/test-${hat()}` 28 | 29 | return expect(ipfs.files.rm(`${testDir}/a`)).to.eventually.be.rejected() 30 | }) 31 | 32 | it('should remove file, expect no error', async () => { 33 | const testDir = `/test-${hat()}` 34 | 35 | await ipfs.files.mkdir(testDir, { parents: true }) 36 | await ipfs.files.write(`${testDir}/c`, Buffer.from('Hello, world!'), { create: true }) 37 | 38 | await ipfs.files.rm(`${testDir}/c`) 39 | 40 | const contents = await all(ipfs.files.ls(testDir)) 41 | expect(contents).to.be.an('array').and.to.be.empty() 42 | }) 43 | 44 | it('should remove dir, expect no error', async () => { 45 | const testDir = `/test-${hat()}` 46 | 47 | await ipfs.files.mkdir(`${testDir}/lv1/lv2`, { parents: true }) 48 | 49 | await ipfs.files.rm(`${testDir}/lv1/lv2`, { recursive: true }) 50 | 51 | const lv1Contents = await all(ipfs.files.ls(`${testDir}/lv1`)) 52 | expect(lv1Contents).to.be.an('array').and.to.be.empty() 53 | }) 54 | }) 55 | } 56 | -------------------------------------------------------------------------------- /src/files/touch.js: -------------------------------------------------------------------------------- 1 | /* eslint-env mocha */ 2 | 'use strict' 3 | 4 | const hat = require('hat') 5 | const { getDescribe, getIt, expect } = require('../utils/mocha') 6 | const delay = require('delay') 7 | 8 | module.exports = (common, options) => { 9 | const describe = getDescribe(options) 10 | const it = getIt(options) 11 | 12 | describe('.files.touch', function () { 13 | this.timeout(10 * 1000) 14 | 15 | let ipfs 16 | 17 | async function testMtime (mtime, expectedMtime) { 18 | const testPath = `/test-${hat()}` 19 | 20 | await ipfs.files.write(testPath, Buffer.from('Hello, world!'), { 21 | create: true 22 | }) 23 | 24 | const stat = await ipfs.files.stat(testPath) 25 | expect(stat).to.not.have.deep.property('mtime', expectedMtime) 26 | 27 | await ipfs.files.touch(testPath, { 28 | mtime 29 | }) 30 | 31 | const stat2 = await ipfs.files.stat(testPath) 32 | expect(stat2).to.have.nested.deep.property('mtime', expectedMtime) 33 | } 34 | 35 | before(async () => { ipfs = (await common.spawn()).api }) 36 | 37 | after(() => common.clean()) 38 | 39 | it('should have default mtime', async function () { 40 | this.slow(5 * 1000) 41 | const testPath = `/test-${hat()}` 42 | 43 | await ipfs.files.write(testPath, Buffer.from('Hello, world!'), { 44 | create: true 45 | }) 46 | 47 | const stat = await ipfs.files.stat(testPath) 48 | expect(stat).to.not.have.property('mtime') 49 | 50 | await ipfs.files.touch(testPath) 51 | 52 | const stat2 = await ipfs.files.stat(testPath) 53 | expect(stat2).to.have.property('mtime').that.does.not.deep.equal({ 54 | secs: 0, 55 | nsecs: 0 56 | }) 57 | }) 58 | 59 | it('should update file mtime', async function () { 60 | this.slow(5 * 1000) 61 | const testPath = `/test-${hat()}` 62 | const mtime = new Date() 63 | const seconds = Math.floor(mtime.getTime() / 1000) 64 | 65 | await ipfs.files.write(testPath, Buffer.from('Hello, world!'), { 66 | create: true, 67 | mtime 68 | }) 69 | await delay(2000) 70 | await ipfs.files.touch(testPath) 71 | 72 | const stat = await ipfs.files.stat(testPath) 73 | expect(stat).to.have.nested.property('mtime.secs').that.is.greaterThan(seconds) 74 | }) 75 | 76 | it('should update directory mtime', async function () { 77 | this.slow(5 * 1000) 78 | const testPath = `/test-${hat()}` 79 | const mtime = new Date() 80 | const seconds = Math.floor(mtime.getTime() / 1000) 81 | 82 | await ipfs.files.mkdir(testPath, { 83 | create: true, 84 | mtime 85 | }) 86 | await delay(2000) 87 | await ipfs.files.touch(testPath) 88 | 89 | const stat2 = await ipfs.files.stat(testPath) 90 | expect(stat2).to.have.nested.property('mtime.secs').that.is.greaterThan(seconds) 91 | }) 92 | 93 | it('should set mtime as Date', async function () { 94 | await testMtime(new Date(5000), { 95 | secs: 5, 96 | nsecs: 0 97 | }) 98 | }) 99 | 100 | it('should set mtime as { nsecs, secs }', async function () { 101 | const mtime = { 102 | secs: 5, 103 | nsecs: 0 104 | } 105 | await testMtime(mtime, mtime) 106 | }) 107 | 108 | it('should set mtime as timespec', async function () { 109 | await testMtime({ 110 | Seconds: 5, 111 | FractionalNanoseconds: 0 112 | }, { 113 | secs: 5, 114 | nsecs: 0 115 | }) 116 | }) 117 | 118 | it('should set mtime as hrtime', async function () { 119 | const mtime = process.hrtime() 120 | await testMtime(mtime, { 121 | secs: mtime[0], 122 | nsecs: mtime[1] 123 | }) 124 | }) 125 | }) 126 | } 127 | -------------------------------------------------------------------------------- /src/files/write.js: -------------------------------------------------------------------------------- 1 | /* eslint-env mocha */ 2 | 'use strict' 3 | 4 | const hat = require('hat') 5 | const { getDescribe, getIt, expect } = require('../utils/mocha') 6 | 7 | /** @typedef { import("ipfsd-ctl/src/factory") } Factory */ 8 | /** 9 | * @param {Factory} common 10 | * @param {Object} options 11 | */ 12 | module.exports = (common, options) => { 13 | const describe = getDescribe(options) 14 | const it = getIt(options) 15 | 16 | describe('.files.write', function () { 17 | this.timeout(40 * 1000) 18 | 19 | let ipfs 20 | 21 | async function testMode (mode, expectedMode) { 22 | const testPath = `/test-${hat()}` 23 | 24 | await ipfs.files.write(testPath, Buffer.from('Hello, world!'), { 25 | create: true, 26 | parents: true, 27 | mode 28 | }) 29 | 30 | const stats = await ipfs.files.stat(testPath) 31 | expect(stats).to.have.property('mode', expectedMode) 32 | } 33 | 34 | async function testMtime (mtime, expectedMtime) { 35 | const testPath = `/test-${hat()}` 36 | 37 | await ipfs.files.write(testPath, Buffer.from('Hello, world!'), { 38 | create: true, 39 | parents: true, 40 | mtime 41 | }) 42 | 43 | const stats = await ipfs.files.stat(testPath) 44 | expect(stats).to.have.deep.property('mtime', expectedMtime) 45 | } 46 | 47 | before(async () => { ipfs = (await common.spawn()).api }) 48 | 49 | after(() => common.clean()) 50 | 51 | it('should not write to non existent file, expect error', function () { 52 | const testDir = `/test-${hat()}` 53 | 54 | return expect(ipfs.files.write(`${testDir}/a`, Buffer.from('Hello, world!'))).to.eventually.be.rejected() 55 | }) 56 | 57 | it('should write to non existent file with create flag', async function () { 58 | const testPath = `/test-${hat()}` 59 | 60 | await ipfs.files.write(testPath, Buffer.from('Hello, world!'), { create: true }) 61 | 62 | const stats = await ipfs.files.stat(testPath) 63 | expect(stats.type).to.equal('file') 64 | }) 65 | 66 | it('should write to deeply nested non existent file with create and parents flags', async function () { 67 | const testPath = `/foo/bar/baz/test-${hat()}` 68 | 69 | await ipfs.files.write(testPath, Buffer.from('Hello, world!'), { create: true, parents: true }) 70 | 71 | const stats = await ipfs.files.stat(testPath) 72 | expect(stats.type).to.equal('file') 73 | }) 74 | 75 | it('should write file and specify mode as a string', async function () { 76 | const mode = '0321' 77 | await testMode(mode, parseInt(mode, 8)) 78 | }) 79 | 80 | it('should write file and specify mode as a number', async function () { 81 | const mode = parseInt('0321', 8) 82 | await testMode(mode, mode) 83 | }) 84 | 85 | it('should write file and specify mtime as Date', async function () { 86 | const mtime = new Date() 87 | const seconds = Math.floor(mtime.getTime() / 1000) 88 | const expectedMtime = { 89 | secs: seconds, 90 | nsecs: (mtime.getTime() - (seconds * 1000)) * 1000 91 | } 92 | await testMtime(mtime, expectedMtime) 93 | }) 94 | 95 | it('should write file and specify mtime as { nsecs, secs }', async function () { 96 | const mtime = { 97 | secs: 5, 98 | nsecs: 0 99 | } 100 | await testMtime(mtime, mtime) 101 | }) 102 | 103 | it('should write file and specify mtime as timespec', async function () { 104 | await testMtime({ 105 | Seconds: 5, 106 | FractionalNanoseconds: 0 107 | }, { 108 | secs: 5, 109 | nsecs: 0 110 | }) 111 | }) 112 | 113 | it('should write file and specify mtime as hrtime', async function () { 114 | const mtime = process.hrtime() 115 | await testMtime(mtime, { 116 | secs: mtime[0], 117 | nsecs: mtime[1] 118 | }) 119 | }) 120 | }) 121 | } 122 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const { createSuite } = require('./utils/suite') 4 | 5 | exports.root = createSuite({ 6 | add: require('./add'), 7 | cat: require('./cat'), 8 | get: require('./get'), 9 | ls: require('./ls'), 10 | refs: require('./refs'), 11 | refsLocal: require('./refs-local') 12 | }) 13 | 14 | exports.files = require('./files') 15 | 16 | exports.bitswap = require('./bitswap') 17 | exports.block = require('./block') 18 | 19 | exports.dag = require('./dag') 20 | exports.object = require('./object') 21 | exports.pin = require('./pin') 22 | 23 | exports.bootstrap = require('./bootstrap') 24 | exports.dht = require('./dht') 25 | exports.name = require('./name') 26 | exports.namePubsub = require('./name-pubsub') 27 | exports.ping = require('./ping') 28 | exports.pubsub = require('./pubsub') 29 | exports.swarm = require('./swarm') 30 | 31 | exports.config = require('./config') 32 | exports.key = require('./key') 33 | exports.miscellaneous = require('./miscellaneous') 34 | exports.repo = require('./repo') 35 | exports.stats = require('./stats') 36 | -------------------------------------------------------------------------------- /src/key/export.js: -------------------------------------------------------------------------------- 1 | /* eslint-env mocha */ 2 | 'use strict' 3 | 4 | const hat = require('hat') 5 | const { getDescribe, getIt, expect } = require('../utils/mocha') 6 | 7 | /** @typedef { import("ipfsd-ctl/src/factory") } Factory */ 8 | /** 9 | * @param {Factory} common 10 | * @param {Object} options 11 | */ 12 | module.exports = (common, options) => { 13 | const describe = getDescribe(options) 14 | const it = getIt(options) 15 | 16 | describe('.key.export', () => { 17 | let ipfs 18 | 19 | before(async () => { 20 | ipfs = (await common.spawn()).api 21 | }) 22 | 23 | after(() => common.clean()) 24 | 25 | it('should export "self" key', async function () { 26 | const pem = await ipfs.key.export('self', hat()) 27 | expect(pem).to.exist() 28 | }) 29 | }) 30 | } 31 | -------------------------------------------------------------------------------- /src/key/gen.js: -------------------------------------------------------------------------------- 1 | /* eslint-env mocha */ 2 | 'use strict' 3 | 4 | const hat = require('hat') 5 | const { getDescribe, getIt, expect } = require('../utils/mocha') 6 | 7 | /** @typedef { import("ipfsd-ctl/src/factory") } Factory */ 8 | /** 9 | * @param {Factory} common 10 | * @param {Object} options 11 | */ 12 | module.exports = (common, options) => { 13 | const describe = getDescribe(options) 14 | const it = getIt(options) 15 | 16 | describe('.key.gen', () => { 17 | const keyTypes = [ 18 | { type: 'rsa', size: 2048 } 19 | ] 20 | 21 | let ipfs 22 | 23 | before(async () => { 24 | ipfs = (await common.spawn()).api 25 | }) 26 | 27 | after(() => common.clean()) 28 | 29 | keyTypes.forEach((kt) => { 30 | it(`should generate a new ${kt.type} key`, async function () { 31 | this.timeout(20 * 1000) 32 | const name = hat() 33 | const key = await ipfs.key.gen(name, kt) 34 | expect(key).to.exist() 35 | expect(key).to.have.property('name', name) 36 | expect(key).to.have.property('id') 37 | }) 38 | }) 39 | }) 40 | } 41 | -------------------------------------------------------------------------------- /src/key/import.js: -------------------------------------------------------------------------------- 1 | /* eslint-env mocha */ 2 | 'use strict' 3 | 4 | const hat = require('hat') 5 | const { getDescribe, getIt, expect } = require('../utils/mocha') 6 | 7 | /** @typedef { import("ipfsd-ctl/src/factory") } Factory */ 8 | /** 9 | * @param {Factory} common 10 | * @param {Object} options 11 | */ 12 | module.exports = (common, options) => { 13 | const describe = getDescribe(options) 14 | const it = getIt(options) 15 | 16 | describe('.key.import', () => { 17 | let ipfs 18 | 19 | before(async () => { 20 | ipfs = (await common.spawn()).api 21 | }) 22 | 23 | after(() => common.clean()) 24 | 25 | it('should import an exported key', async () => { 26 | const password = hat() 27 | 28 | const pem = await ipfs.key.export('self', password) 29 | expect(pem).to.exist() 30 | 31 | const key = await ipfs.key.import('clone', pem, password) 32 | expect(key).to.exist() 33 | expect(key).to.have.property('name', 'clone') 34 | expect(key).to.have.property('id') 35 | }) 36 | }) 37 | } 38 | -------------------------------------------------------------------------------- /src/key/index.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | const { createSuite } = require('../utils/suite') 3 | 4 | const tests = { 5 | gen: require('./gen'), 6 | list: require('./list'), 7 | rename: require('./rename'), 8 | rm: require('./rm'), 9 | export: require('./export'), 10 | import: require('./import') 11 | } 12 | 13 | module.exports = createSuite(tests) 14 | -------------------------------------------------------------------------------- /src/key/list.js: -------------------------------------------------------------------------------- 1 | /* eslint-env mocha */ 2 | 'use strict' 3 | 4 | const hat = require('hat') 5 | const { getDescribe, getIt, expect } = require('../utils/mocha') 6 | 7 | /** @typedef { import("ipfsd-ctl/src/factory") } Factory */ 8 | /** 9 | * @param {Factory} common 10 | * @param {Object} options 11 | */ 12 | module.exports = (common, options) => { 13 | const describe = getDescribe(options) 14 | const it = getIt(options) 15 | 16 | describe('.key.list', () => { 17 | let ipfs 18 | 19 | before(async () => { 20 | ipfs = (await common.spawn()).api 21 | }) 22 | 23 | after(() => common.clean()) 24 | 25 | it('should list all the keys', async function () { 26 | this.timeout(60 * 1000) 27 | 28 | const keys = await Promise.all([1, 2, 3].map(() => ipfs.key.gen(hat(), { type: 'rsa', size: 2048 }))) 29 | 30 | const res = await ipfs.key.list() 31 | expect(res).to.exist() 32 | expect(res).to.be.an('array') 33 | expect(res.length).to.be.above(keys.length - 1) 34 | 35 | keys.forEach(key => { 36 | const found = res.find(({ id, name }) => name === key.name && id === key.id) 37 | expect(found).to.exist() 38 | }) 39 | }) 40 | }) 41 | } 42 | -------------------------------------------------------------------------------- /src/key/rename.js: -------------------------------------------------------------------------------- 1 | /* eslint-env mocha */ 2 | 'use strict' 3 | 4 | const hat = require('hat') 5 | const { getDescribe, getIt, expect } = require('../utils/mocha') 6 | 7 | /** @typedef { import("ipfsd-ctl/src/factory") } Factory */ 8 | /** 9 | * @param {Factory} common 10 | * @param {Object} options 11 | */ 12 | module.exports = (common, options) => { 13 | const describe = getDescribe(options) 14 | const it = getIt(options) 15 | 16 | describe('.key.rename', () => { 17 | let ipfs 18 | 19 | before(async () => { 20 | ipfs = (await common.spawn()).api 21 | }) 22 | 23 | after(() => common.clean()) 24 | 25 | it('should rename a key', async function () { 26 | this.timeout(30 * 1000) 27 | 28 | const oldName = hat() 29 | const newName = hat() 30 | 31 | const key = await ipfs.key.gen(oldName, { type: 'rsa', size: 2048 }) 32 | 33 | const renameRes = await ipfs.key.rename(oldName, newName) 34 | expect(renameRes).to.exist() 35 | expect(renameRes).to.have.property('was', oldName) 36 | expect(renameRes).to.have.property('now', newName) 37 | expect(renameRes).to.have.property('id', key.id) 38 | 39 | const res = await ipfs.key.list() 40 | expect(res.find(k => k.name === newName)).to.exist() 41 | expect(res.find(k => k.name === oldName)).to.not.exist() 42 | }) 43 | }) 44 | } 45 | -------------------------------------------------------------------------------- /src/key/rm.js: -------------------------------------------------------------------------------- 1 | /* eslint-env mocha */ 2 | 'use strict' 3 | 4 | const hat = require('hat') 5 | const { getDescribe, getIt, expect } = require('../utils/mocha') 6 | 7 | /** @typedef { import("ipfsd-ctl/src/factory") } Factory */ 8 | /** 9 | * @param {Factory} common 10 | * @param {Object} options 11 | */ 12 | module.exports = (common, options) => { 13 | const describe = getDescribe(options) 14 | const it = getIt(options) 15 | 16 | describe('.key.rm', () => { 17 | let ipfs 18 | 19 | before(async () => { 20 | ipfs = (await common.spawn()).api 21 | }) 22 | 23 | after(() => common.clean()) 24 | 25 | it('should rm a key', async function () { 26 | this.timeout(30 * 1000) 27 | 28 | const key = await ipfs.key.gen(hat(), { type: 'rsa', size: 2048 }) 29 | 30 | const removeRes = await ipfs.key.rm(key.name) 31 | expect(removeRes).to.exist() 32 | expect(removeRes).to.have.property('name', key.name) 33 | expect(removeRes).to.have.property('id', key.id) 34 | 35 | const res = await ipfs.key.list() 36 | expect(res.find(k => k.name === key.name)).to.not.exist() 37 | }) 38 | }) 39 | } 40 | -------------------------------------------------------------------------------- /src/miscellaneous/dns.js: -------------------------------------------------------------------------------- 1 | /* eslint-env mocha */ 2 | 'use strict' 3 | 4 | const { getDescribe, getIt, expect } = require('../utils/mocha') 5 | 6 | /** @typedef { import("ipfsd-ctl/src/factory") } Factory */ 7 | /** 8 | * @param {Factory} common 9 | * @param {Object} options 10 | */ 11 | module.exports = (common, options) => { 12 | const describe = getDescribe(options) 13 | const it = getIt(options) 14 | 15 | describe('.dns', function () { 16 | this.timeout(60 * 1000) 17 | this.retries(3) 18 | let ipfs 19 | 20 | before(async () => { 21 | ipfs = (await common.spawn()).api 22 | }) 23 | 24 | after(() => common.clean()) 25 | 26 | it('should non-recursively resolve ipfs.io', async () => { 27 | const res = await ipfs.dns('ipfs.io', { recursive: false }) 28 | 29 | // matches pattern /ipns/ 30 | expect(res).to.match(/\/ipns\/.+$/) 31 | }) 32 | 33 | it('should recursively resolve ipfs.io', async () => { 34 | const res = await ipfs.dns('ipfs.io', { recursive: true }) 35 | 36 | // matches pattern /ipfs/ 37 | expect(res).to.match(/\/ipfs\/.+$/) 38 | }) 39 | 40 | it('should resolve subdomain docs.ipfs.io', async () => { 41 | const res = await ipfs.dns('docs.ipfs.io') 42 | 43 | // matches pattern /ipfs/ 44 | expect(res).to.match(/\/ipfs\/.+$/) 45 | }) 46 | }) 47 | } 48 | -------------------------------------------------------------------------------- /src/miscellaneous/id.js: -------------------------------------------------------------------------------- 1 | /* eslint-env mocha */ 2 | 'use strict' 3 | 4 | const { getDescribe, getIt, expect } = require('../utils/mocha') 5 | const Multiaddr = require('multiaddr') 6 | const CID = require('cids') 7 | 8 | /** @typedef { import("ipfsd-ctl/src/factory") } Factory */ 9 | /** 10 | * @param {Factory} common 11 | * @param {Object} options 12 | */ 13 | module.exports = (common, options) => { 14 | const describe = getDescribe(options) 15 | const it = getIt(options) 16 | 17 | describe('.id', function () { 18 | this.timeout(60 * 1000) 19 | let ipfs 20 | 21 | before(async () => { 22 | ipfs = (await common.spawn()).api 23 | }) 24 | 25 | after(() => common.clean()) 26 | 27 | it('should get the node ID', async () => { 28 | const res = await ipfs.id() 29 | expect(res).to.have.a.property('id').that.is.a('string') 30 | expect(CID.isCID(new CID(res.id))).to.equal(true) 31 | expect(res).to.have.a.property('publicKey') 32 | expect(res).to.have.a.property('addresses').that.is.an('array').and.all.satisfy(ma => Multiaddr.isMultiaddr(ma)) 33 | expect(res).to.have.a.property('agentVersion').that.is.a('string') 34 | expect(res).to.have.a.property('protocolVersion').that.is.a('string') 35 | }) 36 | }) 37 | } 38 | -------------------------------------------------------------------------------- /src/miscellaneous/index.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | const { createSuite } = require('../utils/suite') 3 | 4 | const tests = { 5 | id: require('./id'), 6 | version: require('./version'), 7 | dns: require('./dns'), 8 | stop: require('./stop'), 9 | resolve: require('./resolve') 10 | } 11 | 12 | module.exports = createSuite(tests) 13 | -------------------------------------------------------------------------------- /src/miscellaneous/resolve.js: -------------------------------------------------------------------------------- 1 | /* eslint-env mocha */ 2 | 'use strict' 3 | 4 | const isIpfs = require('is-ipfs') 5 | const loadFixture = require('aegir/fixtures') 6 | const hat = require('hat') 7 | const multibase = require('multibase') 8 | const { getDescribe, getIt, expect } = require('../utils/mocha') 9 | const all = require('it-all') 10 | const { isWebWorker } = require('ipfs-utils/src/env') 11 | 12 | /** @typedef { import("ipfsd-ctl/src/factory") } Factory */ 13 | /** 14 | * @param {Factory} common 15 | * @param {Object} options 16 | */ 17 | module.exports = (common, options) => { 18 | const describe = getDescribe(options) 19 | const it = getIt(options) 20 | 21 | describe('.resolve', function () { 22 | this.timeout(60 * 1000) 23 | let ipfs 24 | 25 | before(async () => { 26 | ipfs = (await common.spawn()).api 27 | }) 28 | 29 | after(() => common.clean()) 30 | 31 | it('should resolve an IPFS hash', async () => { 32 | const content = loadFixture('test/fixtures/testfile.txt', 'interface-ipfs-core') 33 | 34 | const [{ cid }] = await all(ipfs.add(content)) 35 | const path = await ipfs.resolve(`/ipfs/${cid}`) 36 | expect(path).to.equal(`/ipfs/${cid}`) 37 | }) 38 | 39 | it('should resolve an IPFS hash and return a base64url encoded CID in path', async () => { 40 | const [{ cid }] = await all(ipfs.add(Buffer.from('base64url encoded'))) 41 | const path = await ipfs.resolve(`/ipfs/${cid}`, { cidBase: 'base64url' }) 42 | const [,, cidStr] = path.split('/') 43 | 44 | expect(multibase.isEncoded(cidStr)).to.equal('base64url') 45 | }) 46 | 47 | // Test resolve turns /ipfs/QmRootHash/path/to/file into /ipfs/QmFileHash 48 | it('should resolve an IPFS path link', async () => { 49 | const path = 'path/to/testfile.txt' 50 | const content = loadFixture('test/fixtures/testfile.txt', 'interface-ipfs-core') 51 | const [{ cid: fileCid }, , , { cid: rootCid }] = await all(ipfs.add([{ path, content }], { wrapWithDirectory: true })) 52 | const resolve = await ipfs.resolve(`/ipfs/${rootCid}/${path}`) 53 | 54 | expect(resolve).to.equal(`/ipfs/${fileCid}`) 55 | }) 56 | 57 | it('should resolve up to the last node', async () => { 58 | const content = { path: { to: { file: hat() } } } 59 | const options = { format: 'dag-cbor', hashAlg: 'sha2-256' } 60 | const cid = await ipfs.dag.put(content, options) 61 | const path = `/ipfs/${cid}/path/to/file` 62 | const resolved = await ipfs.resolve(path) 63 | 64 | expect(resolved).to.equal(path) 65 | }) 66 | 67 | it('should resolve up to the last node across multiple nodes', async () => { 68 | const options = { format: 'dag-cbor', hashAlg: 'sha2-256' } 69 | const childCid = await ipfs.dag.put({ node: { with: { file: hat() } } }, options) 70 | const parentCid = await ipfs.dag.put({ path: { to: childCid } }, options) 71 | const resolved = await ipfs.resolve(`/ipfs/${parentCid}/path/to/node/with/file`) 72 | 73 | expect(resolved).to.equal(`/ipfs/${childCid}/node/with/file`) 74 | }) 75 | 76 | // Test resolve turns /ipns/domain.com into /ipfs/QmHash 77 | it('should resolve an IPNS DNS link', async function () { 78 | this.retries(3) 79 | const resolved = await ipfs.resolve('/ipns/ipfs.io') 80 | 81 | expect(isIpfs.ipfsPath(resolved)).to.be.true() 82 | }) 83 | 84 | it('should resolve IPNS link recursively', async function () { 85 | this.timeout(20 * 1000) 86 | // webworkers are not dialable because webrtc is not available 87 | const node = (await common.spawn({ type: isWebWorker ? 'go' : undefined })).api 88 | await ipfs.swarm.connect(node.peerId.addresses[0]) 89 | const [{ path }] = await all(ipfs.add(Buffer.from('should resolve a record recursive === true'))) 90 | const { id: keyId } = await ipfs.key.gen('key-name', { type: 'rsa', size: 2048 }) 91 | 92 | await ipfs.name.publish(path, { allowOffline: true }) 93 | await ipfs.name.publish(`/ipns/${ipfs.peerId.id}`, { allowOffline: true, key: 'key-name', resolve: false }) 94 | 95 | return expect(await ipfs.resolve(`/ipns/${keyId}`, { recursive: true })) 96 | .to.eq(`/ipfs/${path}`) 97 | }) 98 | }) 99 | } 100 | -------------------------------------------------------------------------------- /src/miscellaneous/stop.js: -------------------------------------------------------------------------------- 1 | /* eslint-env mocha */ 2 | 'use strict' 3 | 4 | const { getDescribe, getIt, expect } = require('../utils/mocha') 5 | 6 | /** @typedef { import("ipfsd-ctl/src/factory") } Factory */ 7 | /** 8 | * @param {Factory} common 9 | * @param {Object} options 10 | */ 11 | module.exports = (common, options) => { 12 | const describe = getDescribe(options) 13 | const it = getIt(options) 14 | 15 | describe('.stop', function () { 16 | this.timeout(60 * 1000) 17 | 18 | it('should stop the node', async () => { 19 | const ipfs = await common.spawn() 20 | 21 | // Should succeed because node is started 22 | await ipfs.api.swarm.peers() 23 | 24 | // Stop the node and try the call again 25 | await ipfs.stop() 26 | 27 | // Trying to use an API that requires a started node should return an error 28 | return expect(ipfs.api.swarm.peers()).to.eventually.be.rejected() 29 | }) 30 | }) 31 | } 32 | -------------------------------------------------------------------------------- /src/miscellaneous/version.js: -------------------------------------------------------------------------------- 1 | /* eslint-env mocha */ 2 | 'use strict' 3 | 4 | const { getDescribe, getIt, expect } = require('../utils/mocha') 5 | 6 | /** @typedef { import("ipfsd-ctl/src/factory") } Factory */ 7 | /** 8 | * @param {Factory} common 9 | * @param {Object} options 10 | */ 11 | module.exports = (common, options) => { 12 | const describe = getDescribe(options) 13 | const it = getIt(options) 14 | 15 | describe('.version', () => { 16 | let ipfs 17 | 18 | before(async () => { 19 | ipfs = (await common.spawn()).api 20 | }) 21 | 22 | after(() => common.clean()) 23 | 24 | it('should get the node version', async () => { 25 | const result = await ipfs.version() 26 | expect(result).to.have.a.property('version') 27 | expect(result).to.have.a.property('commit') 28 | expect(result).to.have.a.property('repo') 29 | }) 30 | }) 31 | } 32 | -------------------------------------------------------------------------------- /src/name-pubsub/cancel.js: -------------------------------------------------------------------------------- 1 | /* eslint-env mocha */ 2 | 'use strict' 3 | 4 | const PeerId = require('peer-id') 5 | const all = require('it-all') 6 | const { getDescribe, getIt, expect } = require('../utils/mocha') 7 | 8 | /** @typedef { import("ipfsd-ctl/src/factory") } Factory */ 9 | /** 10 | * @param {Factory} common 11 | * @param {Object} options 12 | */ 13 | module.exports = (common, options) => { 14 | const describe = getDescribe(options) 15 | const it = getIt(options) 16 | 17 | describe('.name.pubsub.cancel', () => { 18 | let ipfs 19 | let nodeId 20 | 21 | before(async () => { 22 | ipfs = (await common.spawn()).api 23 | nodeId = ipfs.peerId.id 24 | }) 25 | 26 | after(() => common.clean()) 27 | 28 | it('should return false when the name that is intended to cancel is not subscribed', async function () { 29 | this.timeout(60 * 1000) 30 | 31 | const res = await ipfs.name.pubsub.cancel(nodeId) 32 | expect(res).to.exist() 33 | expect(res).to.have.property('canceled') 34 | expect(res.canceled).to.eql(false) 35 | }) 36 | 37 | it('should cancel a subscription correctly returning true', async function () { 38 | this.timeout(300 * 1000) 39 | 40 | const peerId = await PeerId.create({ bits: 512 }) 41 | const id = peerId.toB58String() 42 | const ipnsPath = `/ipns/${id}` 43 | 44 | const subs = await ipfs.name.pubsub.subs() 45 | expect(subs).to.be.an('array').that.does.not.include(ipnsPath) 46 | 47 | await expect(all(ipfs.name.resolve(id))).to.be.rejected() 48 | 49 | const subs1 = await ipfs.name.pubsub.subs() 50 | const cancel = await ipfs.name.pubsub.cancel(ipnsPath) 51 | const subs2 = await ipfs.name.pubsub.subs() 52 | 53 | expect(subs1).to.be.an('array').that.does.include(ipnsPath) 54 | expect(cancel).to.have.property('canceled') 55 | expect(cancel.canceled).to.eql(true) 56 | expect(subs2).to.be.an('array').that.does.not.include(ipnsPath) 57 | }) 58 | }) 59 | } 60 | -------------------------------------------------------------------------------- /src/name-pubsub/index.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | const { createSuite } = require('../utils/suite') 3 | 4 | const tests = { 5 | cancel: require('./cancel'), 6 | state: require('./state'), 7 | subs: require('./subs') 8 | } 9 | 10 | module.exports = createSuite(tests) 11 | -------------------------------------------------------------------------------- /src/name-pubsub/state.js: -------------------------------------------------------------------------------- 1 | /* eslint-env mocha */ 2 | 'use strict' 3 | 4 | const { getDescribe, getIt, expect } = require('../utils/mocha') 5 | 6 | /** @typedef { import("ipfsd-ctl/src/factory") } Factory */ 7 | /** 8 | * @param {Factory} common 9 | * @param {Object} options 10 | */ 11 | module.exports = (common, options) => { 12 | const describe = getDescribe(options) 13 | const it = getIt(options) 14 | 15 | describe('.name.pubsub.state', () => { 16 | let ipfs 17 | 18 | before(async () => { 19 | ipfs = (await common.spawn()).api 20 | }) 21 | 22 | after(() => common.clean()) 23 | 24 | it('should get the current state of pubsub', async function () { 25 | this.timeout(50 * 1000) 26 | 27 | const res = await ipfs.name.pubsub.state() 28 | expect(res).to.exist() 29 | expect(res).to.have.property('enabled') 30 | expect(res.enabled).to.be.eql(true) 31 | }) 32 | }) 33 | } 34 | -------------------------------------------------------------------------------- /src/name-pubsub/subs.js: -------------------------------------------------------------------------------- 1 | /* eslint-env mocha */ 2 | 'use strict' 3 | 4 | const all = require('it-all') 5 | const { getDescribe, getIt, expect } = require('../utils/mocha') 6 | 7 | /** @typedef { import("ipfsd-ctl/src/factory") } Factory */ 8 | /** 9 | * @param {Factory} common 10 | * @param {Object} options 11 | */ 12 | module.exports = (common, options) => { 13 | const describe = getDescribe(options) 14 | const it = getIt(options) 15 | 16 | describe('.name.pubsub.subs', () => { 17 | let ipfs 18 | 19 | before(async () => { 20 | ipfs = (await common.spawn()).api 21 | }) 22 | 23 | after(() => common.clean()) 24 | 25 | it('should get an empty array as a result of subscriptions before any resolve', async function () { 26 | this.timeout(60 * 1000) 27 | 28 | const res = await ipfs.name.pubsub.subs() 29 | expect(res).to.exist() 30 | expect(res).to.eql([]) 31 | }) 32 | 33 | it('should get the list of subscriptions updated after a resolve', async function () { 34 | this.timeout(300 * 1000) 35 | const id = 'QmNP1ASen5ZREtiJTtVD3jhMKhoPb1zppET1tgpjHx2NGA' 36 | 37 | const subs = await ipfs.name.pubsub.subs() 38 | expect(subs).to.eql([]) // initally empty 39 | 40 | await expect(all(ipfs.name.resolve(id))).to.be.rejected() 41 | 42 | const res = await ipfs.name.pubsub.subs() 43 | expect(res).to.be.an('array').that.does.include(`/ipns/${id}`) 44 | }) 45 | }) 46 | } 47 | -------------------------------------------------------------------------------- /src/name/index.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | const { createSuite } = require('../utils/suite') 3 | 4 | const tests = { 5 | publish: require('./publish'), 6 | resolve: require('./resolve') 7 | } 8 | 9 | module.exports = createSuite(tests) 10 | -------------------------------------------------------------------------------- /src/name/publish.js: -------------------------------------------------------------------------------- 1 | /* eslint-env mocha */ 2 | 'use strict' 3 | 4 | const hat = require('hat') 5 | 6 | const { fixture } = require('./utils') 7 | const { getDescribe, getIt, expect } = require('../utils/mocha') 8 | const all = require('it-all') 9 | const last = require('it-last') 10 | 11 | /** @typedef { import("ipfsd-ctl/src/factory") } Factory */ 12 | /** 13 | * @param {Factory} common 14 | * @param {Object} options 15 | */ 16 | module.exports = (common, options) => { 17 | const describe = getDescribe(options) 18 | const it = getIt(options) 19 | 20 | describe('.name.publish offline', () => { 21 | const keyName = hat() 22 | let ipfs 23 | let nodeId 24 | 25 | before(async () => { 26 | ipfs = (await common.spawn()).api 27 | nodeId = ipfs.peerId.id 28 | await all(ipfs.add(fixture.data, { pin: false })) 29 | }) 30 | 31 | after(() => common.clean()) 32 | 33 | it('should publish an IPNS record with the default params', async function () { 34 | this.timeout(50 * 1000) 35 | 36 | const value = fixture.cid 37 | 38 | const res = await ipfs.name.publish(value, { allowOffline: true }) 39 | expect(res).to.exist() 40 | expect(res.name).to.equal(nodeId) 41 | expect(res.value).to.equal(`/ipfs/${value}`) 42 | }) 43 | 44 | it('should publish correctly with the lifetime option and resolve', async () => { 45 | const [{ path }] = await all(ipfs.add(Buffer.from('should publish correctly with the lifetime option and resolve'))) 46 | await ipfs.name.publish(path, { allowOffline: true, resolve: false, lifetime: '2h' }) 47 | expect(await last(ipfs.name.resolve(`/ipns/${nodeId}`))).to.eq(`/ipfs/${path}`) 48 | }) 49 | 50 | it('should publish correctly when the file was not added but resolve is disabled', async function () { 51 | this.timeout(50 * 1000) 52 | 53 | const value = 'QmPFVLPmp9zv5Z5KUqLhe2EivAGccQW2r7M7jhVJGLZoZU' 54 | 55 | const options = { 56 | resolve: false, 57 | lifetime: '1m', 58 | ttl: '10s', 59 | key: 'self', 60 | allowOffline: true 61 | } 62 | 63 | const res = await ipfs.name.publish(value, options) 64 | expect(res).to.exist() 65 | expect(res.name).to.equal(nodeId) 66 | expect(res.value).to.equal(`/ipfs/${value}`) 67 | }) 68 | 69 | it('should publish with a key received as param, instead of using the key of the node', async function () { 70 | this.timeout(90 * 1000) 71 | 72 | const value = fixture.cid 73 | const options = { 74 | resolve: false, 75 | lifetime: '24h', 76 | ttl: '10s', 77 | key: keyName, 78 | allowOffline: true 79 | } 80 | 81 | const key = await ipfs.key.gen(keyName, { type: 'rsa', size: 2048 }) 82 | const res = await ipfs.name.publish(value, options) 83 | 84 | expect(res).to.exist() 85 | expect(res.name).to.equal(key.id) 86 | expect(res.value).to.equal(`/ipfs/${value}`) 87 | }) 88 | }) 89 | } 90 | -------------------------------------------------------------------------------- /src/name/utils.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const loadFixture = require('aegir/fixtures') 4 | 5 | exports.fixture = Object.freeze({ 6 | data: loadFixture('test/fixtures/testfile.txt', 'interface-ipfs-core'), 7 | cid: 'Qma4hjFTnCasJ8PVp3mZbZK5g2vGDT4LByLJ7m8ciyRFZP' 8 | }) 9 | -------------------------------------------------------------------------------- /src/object/data.js: -------------------------------------------------------------------------------- 1 | /* eslint-env mocha */ 2 | 'use strict' 3 | 4 | const hat = require('hat') 5 | const { getDescribe, getIt, expect } = require('../utils/mocha') 6 | 7 | /** @typedef { import("ipfsd-ctl/src/factory") } Factory */ 8 | /** 9 | * @param {Factory} common 10 | * @param {Object} options 11 | */ 12 | module.exports = (common, options) => { 13 | const describe = getDescribe(options) 14 | const it = getIt(options) 15 | 16 | describe('.object.data', function () { 17 | this.timeout(80 * 1000) 18 | 19 | let ipfs 20 | 21 | before(async () => { 22 | ipfs = (await common.spawn()).api 23 | }) 24 | 25 | after(() => common.clean()) 26 | 27 | it('should get data by multihash', async () => { 28 | const testObj = { 29 | Data: Buffer.from(hat()), 30 | Links: [] 31 | } 32 | 33 | const nodeCid = await ipfs.object.put(testObj) 34 | 35 | const data = await ipfs.object.data(nodeCid) 36 | expect(testObj.Data).to.deep.equal(data) 37 | }) 38 | 39 | it('should get data by base58 encoded multihash string', async () => { 40 | const testObj = { 41 | Data: Buffer.from(hat()), 42 | Links: [] 43 | } 44 | 45 | const nodeCid = await ipfs.object.put(testObj) 46 | 47 | const data = await ipfs.object.data(nodeCid.toV0().toString(), { enc: 'base58' }) 48 | expect(testObj.Data).to.eql(data) 49 | }) 50 | 51 | it('returns error for request without argument', () => { 52 | return expect(ipfs.object.data(null)).to.eventually.be.rejected.and.be.an.instanceOf(Error) 53 | }) 54 | 55 | it('returns error for request with invalid argument', () => { 56 | return expect(ipfs.object.data('invalid', { enc: 'base58' })).to.eventually.be.rejected.and.be.an.instanceOf(Error) 57 | }) 58 | }) 59 | } 60 | -------------------------------------------------------------------------------- /src/object/index.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | const { createSuite } = require('../utils/suite') 3 | 4 | const tests = { 5 | new: require('./new'), 6 | put: require('./put'), 7 | get: require('./get'), 8 | data: require('./data'), 9 | links: require('./links'), 10 | stat: require('./stat'), 11 | patch: require('./patch') 12 | } 13 | 14 | module.exports = createSuite(tests) 15 | -------------------------------------------------------------------------------- /src/object/links.js: -------------------------------------------------------------------------------- 1 | /* eslint-env mocha */ 2 | 'use strict' 3 | 4 | const dagPB = require('ipld-dag-pb') 5 | const DAGNode = dagPB.DAGNode 6 | const hat = require('hat') 7 | const { getDescribe, getIt, expect } = require('../utils/mocha') 8 | const { asDAGLink } = require('./utils') 9 | const all = require('it-all') 10 | 11 | /** @typedef { import("ipfsd-ctl/src/factory") } Factory */ 12 | /** 13 | * @param {Factory} common 14 | * @param {Object} options 15 | */ 16 | module.exports = (common, options) => { 17 | const describe = getDescribe(options) 18 | const it = getIt(options) 19 | 20 | describe('.object.links', function () { 21 | this.timeout(80 * 1000) 22 | 23 | let ipfs 24 | 25 | before(async () => { 26 | ipfs = (await common.spawn()).api 27 | }) 28 | 29 | after(() => common.clean()) 30 | 31 | it('should get empty links by multihash', async () => { 32 | const testObj = { 33 | Data: Buffer.from(hat()), 34 | Links: [] 35 | } 36 | 37 | const cid = await ipfs.object.put(testObj) 38 | const node = await ipfs.object.get(cid) 39 | const links = await ipfs.object.links(cid) 40 | 41 | expect(node.Links).to.eql(links) 42 | }) 43 | 44 | it('should get links by multihash', async () => { 45 | const node1a = new DAGNode(Buffer.from('Some data 1')) 46 | const node2 = new DAGNode(Buffer.from('Some data 2')) 47 | 48 | const link = await asDAGLink(node2, 'some-link') 49 | 50 | const node1b = new DAGNode(node1a.Data, node1a.Links.concat(link)) 51 | const node1bCid = await ipfs.object.put(node1b) 52 | 53 | const links = await ipfs.object.links(node1bCid) 54 | expect(node1b.Links[0]).to.eql({ 55 | Hash: links[0].Hash, 56 | Tsize: links[0].Tsize, 57 | Name: links[0].Name 58 | }) 59 | }) 60 | 61 | it('should get links by base58 encoded multihash', async () => { 62 | const testObj = { 63 | Data: Buffer.from(hat()), 64 | Links: [] 65 | } 66 | 67 | const cid = await ipfs.object.put(testObj) 68 | const node = await ipfs.object.get(cid) 69 | 70 | const links = await ipfs.object.links(cid.buffer, { enc: 'base58' }) 71 | expect(node.Links).to.deep.equal(links) 72 | }) 73 | 74 | it('should get links by base58 encoded multihash string', async () => { 75 | const testObj = { 76 | Data: Buffer.from(hat()), 77 | Links: [] 78 | } 79 | 80 | const cid = await ipfs.object.put(testObj) 81 | const node = await ipfs.object.get(cid) 82 | 83 | const links = await ipfs.object.links(cid.toBaseEncodedString(), { enc: 'base58' }) 84 | expect(node.Links).to.deep.equal(links) 85 | }) 86 | 87 | it('should get links from CBOR object', async () => { 88 | const hashes = [] 89 | 90 | const res1 = await all(ipfs.add(Buffer.from('test data'))) 91 | hashes.push(res1[0].cid) 92 | 93 | const res2 = await all(ipfs.add(Buffer.from('more test data'))) 94 | hashes.push(res2[0].cid) 95 | 96 | const obj = { 97 | some: 'data', 98 | mylink: hashes[0], 99 | myobj: { 100 | anotherLink: hashes[1] 101 | } 102 | } 103 | const cid = await ipfs.dag.put(obj) 104 | 105 | const links = await ipfs.object.links(cid) 106 | expect(links.length).to.eql(2) 107 | 108 | // TODO: js-ipfs succeeds but go returns empty strings for link name 109 | // const names = [links[0].name, links[1].name] 110 | // expect(names).includes('mylink') 111 | // expect(names).includes('myobj/anotherLink') 112 | 113 | const cids = [links[0].Hash.toString(), links[1].Hash.toString()] 114 | expect(cids).includes(hashes[0].toString()) 115 | expect(cids).includes(hashes[1].toString()) 116 | }) 117 | 118 | it('returns error for request without argument', () => { 119 | return expect(ipfs.object.links(null)).to.eventually.be.rejected.and.be.an.instanceOf(Error) 120 | }) 121 | 122 | it('returns error for request with invalid argument', () => { 123 | return expect(ipfs.object.links('invalid', { enc: 'base58' })).to.eventually.be.rejected.and.be.an.instanceOf(Error) 124 | }) 125 | }) 126 | } 127 | -------------------------------------------------------------------------------- /src/object/new.js: -------------------------------------------------------------------------------- 1 | /* eslint-env mocha */ 2 | 'use strict' 3 | 4 | const { getDescribe, getIt, expect } = require('../utils/mocha') 5 | 6 | /** @typedef { import("ipfsd-ctl/src/factory") } Factory */ 7 | /** 8 | * @param {Factory} common 9 | * @param {Object} options 10 | */ 11 | module.exports = (common, options) => { 12 | const describe = getDescribe(options) 13 | const it = getIt(options) 14 | 15 | describe('.object.new', function () { 16 | this.timeout(80 * 1000) 17 | 18 | let ipfs 19 | 20 | before(async () => { 21 | ipfs = (await common.spawn()).api 22 | }) 23 | 24 | after(() => common.clean()) 25 | 26 | it('should create a new object with no template', async () => { 27 | const cid = await ipfs.object.new() 28 | expect(cid.toBaseEncodedString()).to.equal('QmdfTbBqBPQ7VNxZEYEj14VmRuZBkqFbiwReogJgS1zR1n') 29 | }) 30 | 31 | it('should create a new object with unixfs-dir template', async () => { 32 | const cid = await ipfs.object.new('unixfs-dir') 33 | expect(cid.toBaseEncodedString()).to.equal('QmUNLLsPACCz1vLxQVkXqqLX5R1X345qqfHbsf67hvA3Nn') 34 | }) 35 | }) 36 | } 37 | -------------------------------------------------------------------------------- /src/object/patch/add-link.js: -------------------------------------------------------------------------------- 1 | /* eslint-env mocha */ 2 | 'use strict' 3 | 4 | const dagPB = require('ipld-dag-pb') 5 | const DAGNode = dagPB.DAGNode 6 | const { getDescribe, getIt, expect } = require('../../utils/mocha') 7 | const { asDAGLink } = require('../utils') 8 | 9 | /** @typedef { import("ipfsd-ctl/src/factory") } Factory */ 10 | /** 11 | * @param {Factory} common 12 | * @param {Object} options 13 | */ 14 | module.exports = (common, options) => { 15 | const describe = getDescribe(options) 16 | const it = getIt(options) 17 | 18 | describe('.object.patch.addLink', function () { 19 | this.timeout(80 * 1000) 20 | 21 | let ipfs 22 | 23 | before(async () => { 24 | ipfs = (await common.spawn()).api 25 | }) 26 | 27 | after(() => common.clean()) 28 | 29 | it('should add a link to an existing node', async () => { 30 | const obj = { 31 | Data: Buffer.from('patch test object'), 32 | Links: [] 33 | } 34 | // link to add 35 | const node2 = new DAGNode(Buffer.from('some other node')) 36 | // note: we need to put the linked obj, otherwise IPFS won't 37 | // timeout. Reason: it needs the node to get its size 38 | await ipfs.object.put(node2) 39 | const link = await asDAGLink(node2, 'link-to-node') 40 | 41 | // manual create dag step by step 42 | const node1a = new DAGNode(obj.Data, obj.Links) 43 | const node1b = new DAGNode(node1a.Data, node1a.Links.concat(link)) 44 | const node1bCid = await ipfs.object.put(node1b) 45 | 46 | // add link with patch.addLink 47 | const testNodeCid = await ipfs.object.put(obj) 48 | const cid = await ipfs.object.patch.addLink(testNodeCid, link) 49 | 50 | // assert both are equal 51 | expect(node1bCid).to.eql(cid) 52 | 53 | /* TODO: revisit this assertions. 54 | // note: make sure we can link js plain objects 55 | const content = Buffer.from(JSON.stringify({ 56 | title: 'serialized object' 57 | }, null, 0)) 58 | const result = await ipfs.add(content) 59 | expect(result).to.exist() 60 | expect(result).to.have.lengthOf(1) 61 | const object = result.pop() 62 | const node3 = { 63 | name: object.hash, 64 | multihash: object.hash, 65 | size: object.size 66 | } 67 | const node = await ipfs.object.patch.addLink(testNodeWithLinkMultihash, node3) 68 | expect(node).to.exist() 69 | testNodeWithLinkMultihash = node.multihash 70 | testLinkPlainObject = node3 71 | */ 72 | }) 73 | 74 | it('returns error for request without arguments', () => { 75 | return expect(ipfs.object.patch.addLink(null, null, null)).to.eventually.be.rejected.and.be.an.instanceOf(Error) 76 | }) 77 | 78 | it('returns error for request with only one invalid argument', () => { 79 | return expect(ipfs.object.patch.addLink('invalid', null, null)).to.eventually.be.rejected.and.be.an.instanceOf(Error) 80 | }) 81 | }) 82 | } 83 | -------------------------------------------------------------------------------- /src/object/patch/append-data.js: -------------------------------------------------------------------------------- 1 | /* eslint-env mocha */ 2 | 'use strict' 3 | 4 | const { getDescribe, getIt, expect } = require('../../utils/mocha') 5 | 6 | /** @typedef { import("ipfsd-ctl/src/factory") } Factory */ 7 | /** 8 | * @param {Factory} common 9 | * @param {Object} options 10 | */ 11 | module.exports = (common, options) => { 12 | const describe = getDescribe(options) 13 | const it = getIt(options) 14 | 15 | describe('.object.patch.appendData', function () { 16 | this.timeout(80 * 1000) 17 | 18 | let ipfs 19 | 20 | before(async () => { 21 | ipfs = (await common.spawn()).api 22 | }) 23 | 24 | after(() => common.clean()) 25 | 26 | it('should append data to an existing node', async () => { 27 | const obj = { 28 | Data: Buffer.from('patch test object'), 29 | Links: [] 30 | } 31 | 32 | const nodeCid = await ipfs.object.put(obj) 33 | const patchedNodeCid = await ipfs.object.patch.appendData(nodeCid, Buffer.from('append')) 34 | expect(patchedNodeCid).to.not.deep.equal(nodeCid) 35 | }) 36 | 37 | it('returns error for request without key & data', () => { 38 | return expect(ipfs.object.patch.appendData(null, null)).to.eventually.be.rejected.and.be.an.instanceOf(Error) 39 | }) 40 | 41 | it('returns error for request without data', () => { 42 | const filePath = 'test/fixtures/test-data/badnode.json' 43 | 44 | return expect(ipfs.object.patch.appendData(null, filePath)).to.eventually.be.rejected.and.be.an.instanceOf(Error) 45 | }) 46 | }) 47 | } 48 | -------------------------------------------------------------------------------- /src/object/patch/index.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | const { createSuite } = require('../../utils/suite') 3 | 4 | const tests = { 5 | addLink: require('./add-link'), 6 | rmLink: require('./rm-link'), 7 | appendData: require('./append-data'), 8 | setData: require('./set-data') 9 | } 10 | 11 | module.exports = createSuite(tests, 'patch') 12 | -------------------------------------------------------------------------------- /src/object/patch/rm-link.js: -------------------------------------------------------------------------------- 1 | /* eslint-env mocha */ 2 | 'use strict' 3 | 4 | const { getDescribe, getIt, expect } = require('../../utils/mocha') 5 | const { asDAGLink } = require('../utils') 6 | 7 | /** @typedef { import("ipfsd-ctl/src/factory") } Factory */ 8 | /** 9 | * @param {Factory} common 10 | * @param {Object} options 11 | */ 12 | module.exports = (common, options) => { 13 | const describe = getDescribe(options) 14 | const it = getIt(options) 15 | 16 | describe('.object.patch.rmLink', function () { 17 | this.timeout(80 * 1000) 18 | 19 | let ipfs 20 | 21 | before(async () => { 22 | ipfs = (await common.spawn()).api 23 | }) 24 | 25 | after(() => common.clean()) 26 | 27 | it('should remove a link from an existing node', async () => { 28 | const obj1 = { 29 | Data: Buffer.from('patch test object 1'), 30 | Links: [] 31 | } 32 | 33 | const obj2 = { 34 | Data: Buffer.from('patch test object 2'), 35 | Links: [] 36 | } 37 | 38 | const nodeCid = await ipfs.object.put(obj1) 39 | const childCid = await ipfs.object.put(obj2) 40 | const child = await ipfs.object.get(childCid) 41 | const childAsDAGLink = await asDAGLink(child, 'my-link') 42 | const parentCid = await ipfs.object.patch.addLink(nodeCid, childAsDAGLink) 43 | const withoutChildCid = await ipfs.object.patch.rmLink(parentCid, childAsDAGLink) 44 | 45 | expect(withoutChildCid).to.not.deep.equal(parentCid) 46 | expect(withoutChildCid).to.deep.equal(nodeCid) 47 | 48 | /* TODO: revisit this assertions. 49 | const node = await ipfs.object.patch.rmLink(testNodeWithLinkMultihash, testLinkPlainObject) 50 | expect(node.multihash).to.not.deep.equal(testNodeWithLinkMultihash) 51 | */ 52 | }) 53 | 54 | it('returns error for request without arguments', () => { 55 | return expect(ipfs.object.patch.rmLink(null, null)).to.eventually.be.rejected 56 | .and.be.an.instanceOf(Error) 57 | }) 58 | 59 | it('returns error for request only one invalid argument', () => { 60 | return expect(ipfs.object.patch.rmLink('invalid', null)).to.eventually.be.rejected 61 | .and.be.an.instanceOf(Error) 62 | }) 63 | 64 | it('returns error for request with invalid first argument', () => { 65 | const root = '' 66 | const link = 'foo' 67 | 68 | return expect(ipfs.object.patch.rmLink(root, link)).to.eventually.be.rejected 69 | .and.be.an.instanceOf(Error) 70 | }) 71 | }) 72 | } 73 | -------------------------------------------------------------------------------- /src/object/patch/set-data.js: -------------------------------------------------------------------------------- 1 | /* eslint-env mocha */ 2 | 'use strict' 3 | 4 | const { getDescribe, getIt, expect } = require('../../utils/mocha') 5 | 6 | /** @typedef { import("ipfsd-ctl/src/factory") } Factory */ 7 | /** 8 | * @param {Factory} common 9 | * @param {Object} options 10 | */ 11 | module.exports = (common, options) => { 12 | const describe = getDescribe(options) 13 | const it = getIt(options) 14 | 15 | describe('.object.patch.setData', function () { 16 | this.timeout(80 * 1000) 17 | 18 | let ipfs 19 | 20 | before(async () => { 21 | ipfs = (await common.spawn()).api 22 | }) 23 | 24 | after(() => common.clean()) 25 | 26 | it('should set data for an existing node', async () => { 27 | const obj = { 28 | Data: Buffer.from('patch test object'), 29 | Links: [] 30 | } 31 | const patchData = Buffer.from('set') 32 | 33 | const nodeCid = await ipfs.object.put(obj) 34 | const patchedNodeCid = await ipfs.object.patch.setData(nodeCid, patchData) 35 | const patchedNode = await ipfs.object.get(patchedNodeCid) 36 | 37 | expect(nodeCid).to.not.deep.equal(patchedNodeCid) 38 | expect(patchedNode.Data).to.eql(patchData) 39 | }) 40 | 41 | it('returns error for request without key & data', () => { 42 | return expect(ipfs.object.patch.setData(null, null)).to.eventually.be.rejected.and.be.an.instanceOf(Error) 43 | }) 44 | 45 | it('returns error for request without data', () => { 46 | const filePath = 'test/fixtures/test-data/badnode.json' 47 | 48 | return expect(ipfs.object.patch.setData(null, filePath)).to.eventually.be.rejected.and.be.an.instanceOf(Error) 49 | }) 50 | }) 51 | } 52 | -------------------------------------------------------------------------------- /src/object/put.js: -------------------------------------------------------------------------------- 1 | /* eslint-env mocha */ 2 | 'use strict' 3 | 4 | const dagPB = require('ipld-dag-pb') 5 | const DAGNode = dagPB.DAGNode 6 | const hat = require('hat') 7 | const { getDescribe, getIt, expect } = require('../utils/mocha') 8 | const { asDAGLink } = require('./utils') 9 | 10 | /** @typedef { import("ipfsd-ctl/src/factory") } Factory */ 11 | /** 12 | * @param {Factory} common 13 | * @param {Object} options 14 | */ 15 | module.exports = (common, options) => { 16 | const describe = getDescribe(options) 17 | const it = getIt(options) 18 | 19 | describe('.object.put', function () { 20 | this.timeout(80 * 1000) 21 | 22 | let ipfs 23 | 24 | before(async () => { 25 | ipfs = (await common.spawn()).api 26 | }) 27 | 28 | after(() => common.clean()) 29 | 30 | it('should put an object', async () => { 31 | const obj = { 32 | Data: Buffer.from(hat()), 33 | Links: [] 34 | } 35 | 36 | const cid = await ipfs.object.put(obj) 37 | const node = await ipfs.object.get(cid) 38 | 39 | const nodeJSON = node.toJSON() 40 | expect(obj.Data).to.deep.equal(nodeJSON.data) 41 | expect(obj.Links).to.deep.equal(nodeJSON.links) 42 | }) 43 | 44 | it('should put a JSON encoded Buffer', async () => { 45 | const obj = { 46 | Data: Buffer.from(hat()), 47 | Links: [] 48 | } 49 | 50 | const obj2 = { 51 | Data: obj.Data.toString(), 52 | Links: obj.Links 53 | } 54 | 55 | const buf = Buffer.from(JSON.stringify(obj2)) 56 | 57 | const cid = await ipfs.object.put(buf, { enc: 'json' }) 58 | 59 | const node = await ipfs.object.get(cid) 60 | const nodeJSON = node.toJSON() 61 | expect(nodeJSON.data).to.eql(node.Data) 62 | }) 63 | 64 | it('should put a Protobuf encoded Buffer', async () => { 65 | const node = new DAGNode(Buffer.from(hat())) 66 | const serialized = node.serialize() 67 | 68 | const cid = await ipfs.object.put(serialized, { enc: 'protobuf' }) 69 | const node2 = await ipfs.object.get(cid) 70 | expect(node2.Data).to.deep.equal(node.Data) 71 | expect(node2.Links).to.deep.equal(node.Links) 72 | }) 73 | 74 | it('should put a Buffer as data', async () => { 75 | const data = Buffer.from(hat()) 76 | 77 | const cid = await ipfs.object.put(data) 78 | const node = await ipfs.object.get(cid) 79 | const nodeJSON = node.toJSON() 80 | expect(data).to.deep.equal(nodeJSON.data) 81 | expect([]).to.deep.equal(nodeJSON.links) 82 | }) 83 | 84 | it('should put a Protobuf DAGNode', async () => { 85 | const dNode = new DAGNode(Buffer.from(hat())) 86 | 87 | const cid = await ipfs.object.put(dNode) 88 | const node = await ipfs.object.get(cid) 89 | expect(dNode.Data).to.deep.equal(node.Data) 90 | expect(dNode.Links).to.deep.equal(node.Links) 91 | }) 92 | 93 | it('should fail if a string is passed', () => { 94 | return expect(ipfs.object.put(hat())).to.eventually.be.rejected() 95 | }) 96 | 97 | it('should put a Protobuf DAGNode with a link', async () => { 98 | const node1a = new DAGNode(Buffer.from(hat())) 99 | const node2 = new DAGNode(Buffer.from(hat())) 100 | 101 | const link = await asDAGLink(node2, 'some-link') 102 | 103 | const node1b = new DAGNode(node1a.Data, node1a.Links.concat(link)) 104 | 105 | const cid = await ipfs.object.put(node1b) 106 | const node = await ipfs.object.get(cid) 107 | expect(node1b.Data).to.deep.equal(node.Data) 108 | expect(node1b.Links).to.deep.equal(node.Links) 109 | }) 110 | }) 111 | } 112 | -------------------------------------------------------------------------------- /src/object/stat.js: -------------------------------------------------------------------------------- 1 | /* eslint-env mocha */ 2 | 'use strict' 3 | 4 | const dagPB = require('ipld-dag-pb') 5 | const DAGNode = dagPB.DAGNode 6 | const { getDescribe, getIt, expect } = require('../utils/mocha') 7 | const { asDAGLink } = require('./utils') 8 | 9 | /** @typedef { import("ipfsd-ctl/src/factory") } Factory */ 10 | /** 11 | * @param {Factory} common 12 | * @param {Object} options 13 | */ 14 | module.exports = (common, options) => { 15 | const describe = getDescribe(options) 16 | const it = getIt(options) 17 | 18 | describe('.object.stat', function () { 19 | this.timeout(80 * 1000) 20 | 21 | let ipfs 22 | 23 | before(async () => { 24 | ipfs = (await common.spawn()).api 25 | }) 26 | 27 | after(() => common.clean()) 28 | 29 | it('should get stats by multihash', async () => { 30 | const testObj = { 31 | Data: Buffer.from('get test object'), 32 | Links: [] 33 | } 34 | 35 | const cid = await ipfs.object.put(testObj) 36 | const stats = await ipfs.object.stat(cid) 37 | const expected = { 38 | Hash: 'QmNggDXca24S6cMPEYHZjeuc4QRmofkRrAEqVL3Ms2sdJZ', 39 | NumLinks: 0, 40 | BlockSize: 17, 41 | LinksSize: 2, 42 | DataSize: 15, 43 | CumulativeSize: 17 44 | } 45 | expect(expected).to.deep.equal(stats) 46 | }) 47 | 48 | it('should respect timeout option', async () => { 49 | const testObj = { 50 | Data: Buffer.from('get test object'), 51 | Links: [] 52 | } 53 | 54 | await ipfs.object.put(testObj) 55 | 56 | const timeout = 2 57 | const startTime = new Date() 58 | const badCid = 'QmNggDXca24S6cMPEYHZjeuc4QRmofkRrAEqVL3MzzzzzZ' 59 | 60 | const err = await expect(ipfs.object.stat(badCid, { timeout: `${timeout}s` })).to.be.rejected() 61 | const timeForRequest = (new Date() - startTime) / 1000 62 | 63 | if (err.code) { 64 | expect(err.code).to.equal('ERR_TIMEOUT') 65 | } else { 66 | expect(err.message).to.equal('failed to get block for QmNggDXca24S6cMPEYHZjeuc4QRmofkRrAEqVL3MzzzzzZ: context deadline exceeded') 67 | } 68 | 69 | expect(timeForRequest).to.not.lessThan(timeout - 0.1) 70 | expect(timeForRequest).to.not.greaterThan(timeout + 1) 71 | }) 72 | 73 | it('should get stats for object with links by multihash', async () => { 74 | const node1a = new DAGNode(Buffer.from('Some data 1')) 75 | const node2 = new DAGNode(Buffer.from('Some data 2')) 76 | 77 | const link = await asDAGLink(node2, 'some-link') 78 | 79 | const node1b = new DAGNode(node1a.Data, node1a.Links.concat(link)) 80 | const node1bCid = await ipfs.object.put(node1b) 81 | 82 | const stats = await ipfs.object.stat(node1bCid) 83 | const expected = { 84 | Hash: 'QmPR7W4kaADkAo4GKEVVPQN81EDUFCHJtqejQZ5dEG7pBC', 85 | NumLinks: 1, 86 | BlockSize: 64, 87 | LinksSize: 53, 88 | DataSize: 11, 89 | CumulativeSize: 77 90 | } 91 | expect(expected).to.eql(stats) 92 | }) 93 | 94 | it('should get stats by base58 encoded multihash', async () => { 95 | const testObj = { 96 | Data: Buffer.from('get test object'), 97 | Links: [] 98 | } 99 | 100 | const cid = await ipfs.object.put(testObj) 101 | 102 | const stats = await ipfs.object.stat(cid.buffer) 103 | const expected = { 104 | Hash: 'QmNggDXca24S6cMPEYHZjeuc4QRmofkRrAEqVL3Ms2sdJZ', 105 | NumLinks: 0, 106 | BlockSize: 17, 107 | LinksSize: 2, 108 | DataSize: 15, 109 | CumulativeSize: 17 110 | } 111 | expect(expected).to.deep.equal(stats) 112 | }) 113 | 114 | it('should get stats by base58 encoded multihash string', async () => { 115 | const testObj = { 116 | Data: Buffer.from('get test object'), 117 | Links: [] 118 | } 119 | 120 | const cid = await ipfs.object.put(testObj) 121 | 122 | const stats = await ipfs.object.stat(cid.toBaseEncodedString()) 123 | const expected = { 124 | Hash: 'QmNggDXca24S6cMPEYHZjeuc4QRmofkRrAEqVL3Ms2sdJZ', 125 | NumLinks: 0, 126 | BlockSize: 17, 127 | LinksSize: 2, 128 | DataSize: 15, 129 | CumulativeSize: 17 130 | } 131 | expect(expected).to.deep.equal(stats) 132 | }) 133 | 134 | it('returns error for request without argument', () => { 135 | return expect(ipfs.object.stat(null)).to.eventually.be.rejected.and.be.an.instanceOf(Error) 136 | }) 137 | 138 | it('returns error for request with invalid argument', () => { 139 | return expect(ipfs.object.stat('invalid', { enc: 'base58' })).to.eventually.be.rejected.and.be.an.instanceOf(Error) 140 | }) 141 | }) 142 | } 143 | -------------------------------------------------------------------------------- /src/object/utils.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const dagPB = require('ipld-dag-pb') 4 | 5 | const calculateCid = node => dagPB.util.cid(node.serialize(), { cidVersion: 0 }) 6 | 7 | const asDAGLink = async (node, name = '') => { 8 | const cid = await calculateCid(node) 9 | return new dagPB.DAGLink(name, node.size, cid) 10 | } 11 | 12 | module.exports = { 13 | calculateCid, 14 | asDAGLink 15 | } 16 | -------------------------------------------------------------------------------- /src/pin/add.js: -------------------------------------------------------------------------------- 1 | /* eslint-env mocha */ 2 | 'use strict' 3 | 4 | const { fixtures } = require('./utils') 5 | const { getDescribe, getIt, expect } = require('../utils/mocha') 6 | const all = require('it-all') 7 | 8 | /** @typedef { import("ipfsd-ctl/src/factory") } Factory */ 9 | /** 10 | * @param {Factory} common 11 | * @param {Object} options 12 | */ 13 | module.exports = (common, options) => { 14 | const describe = getDescribe(options) 15 | const it = getIt(options) 16 | 17 | describe('.pin.add', function () { 18 | this.timeout(50 * 1000) 19 | 20 | let ipfs 21 | before(async () => { 22 | ipfs = (await common.spawn()).api 23 | await Promise.all(fixtures.files.map(file => { 24 | return all(ipfs.add(file.data, { pin: false })) 25 | })) 26 | }) 27 | 28 | after(() => common.clean()) 29 | 30 | it('should add a pin', async () => { 31 | const pinset = await ipfs.pin.add(fixtures.files[0].cid, { recursive: false }) 32 | expect(pinset.map(p => p.cid.toString())).to.include(fixtures.files[0].cid) 33 | }) 34 | }) 35 | } 36 | -------------------------------------------------------------------------------- /src/pin/index.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | const { createSuite } = require('../utils/suite') 3 | 4 | const tests = { 5 | ls: require('./ls'), 6 | rm: require('./rm'), 7 | add: require('./add') 8 | } 9 | 10 | module.exports = createSuite(tests) 11 | -------------------------------------------------------------------------------- /src/pin/rm.js: -------------------------------------------------------------------------------- 1 | /* eslint-env mocha */ 2 | 'use strict' 3 | 4 | const { fixtures } = require('./utils') 5 | const { getDescribe, getIt, expect } = require('../utils/mocha') 6 | const all = require('it-all') 7 | 8 | /** @typedef { import("ipfsd-ctl/src/factory") } Factory */ 9 | /** 10 | * @param {Factory} common 11 | * @param {Object} options 12 | */ 13 | module.exports = (common, options) => { 14 | const describe = getDescribe(options) 15 | const it = getIt(options) 16 | 17 | describe('.pin.rm', function () { 18 | this.timeout(50 * 1000) 19 | 20 | let ipfs 21 | before(async () => { 22 | ipfs = (await common.spawn()).api 23 | await all(ipfs.add(fixtures.files[0].data, { pin: false })) 24 | await ipfs.pin.add(fixtures.files[0].cid, { recursive: true }) 25 | await all(ipfs.add(fixtures.files[1].data, { pin: false })) 26 | await ipfs.pin.add(fixtures.files[1].cid, { recursive: false }) 27 | }) 28 | 29 | after(() => common.clean()) 30 | 31 | it('should remove a recursive pin', async () => { 32 | const removedPinset = await ipfs.pin.rm(fixtures.files[0].cid, { recursive: true }) 33 | expect(removedPinset.map(p => p.cid.toString())).to.deep.equal([fixtures.files[0].cid]) 34 | 35 | const pinset = await all(ipfs.pin.ls({ type: 'recursive' })) 36 | expect(pinset.map(p => ({ ...p, cid: p.cid.toString() }))).to.not.deep.include({ 37 | cid: fixtures.files[0].cid, 38 | type: 'recursive' 39 | }) 40 | }) 41 | 42 | it('should remove a direct pin', async () => { 43 | const removedPinset = await ipfs.pin.rm(fixtures.files[1].cid, { recursive: false }) 44 | expect(removedPinset.map(p => p.cid.toString())).to.deep.equal([fixtures.files[1].cid]) 45 | 46 | const pinset = await all(ipfs.pin.ls({ type: 'direct' })) 47 | expect(pinset.map(p => p.cid.toString())).to.not.include(fixtures.files[1].cid) 48 | }) 49 | }) 50 | } 51 | -------------------------------------------------------------------------------- /src/pin/utils.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const loadFixture = require('aegir/fixtures') 4 | 5 | exports.fixtures = Object.freeze({ 6 | // NOTE: files under 'directory' need to be different than standalone ones in 'files' 7 | directory: Object.freeze({ 8 | cid: 'QmY8KdYQSYKFU5hM7F5ioZ5yYSgV5VZ1kDEdqfRL3rFgcd', 9 | files: Object.freeze([Object.freeze({ 10 | path: 'test-folder/ipfs-add.js', 11 | data: loadFixture('test/fixtures/test-folder/ipfs-add.js', 'interface-ipfs-core'), 12 | cid: 'QmbKtKBrmeRHjNCwR4zAfCJdMVu6dgmwk9M9AE9pUM9RgG' 13 | }), Object.freeze({ 14 | path: 'test-folder/files/ipfs.txt', 15 | data: loadFixture('test/fixtures/test-folder/files/ipfs.txt', 'interface-ipfs-core'), 16 | cid: 'QmdFyxZXsFiP4csgfM5uPu99AvFiKH62CSPDw5TP92nr7w' 17 | })]) 18 | }), 19 | files: Object.freeze([Object.freeze({ 20 | data: loadFixture('test/fixtures/testfile.txt', 'interface-ipfs-core'), 21 | cid: 'Qma4hjFTnCasJ8PVp3mZbZK5g2vGDT4LByLJ7m8ciyRFZP' 22 | }), Object.freeze({ 23 | data: loadFixture('test/fixtures/test-folder/files/hello.txt', 'interface-ipfs-core'), 24 | cid: 'QmY9cxiHqTFoWamkQVkpmmqzBrY3hCBEL2XNu3NtX74Fuu' 25 | })]) 26 | }) 27 | -------------------------------------------------------------------------------- /src/ping/index.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | const { createSuite } = require('../utils/suite') 3 | 4 | const tests = { 5 | ping: require('./ping') 6 | } 7 | 8 | module.exports = createSuite(tests) 9 | -------------------------------------------------------------------------------- /src/ping/ping.js: -------------------------------------------------------------------------------- 1 | /* eslint-env mocha */ 2 | 'use strict' 3 | 4 | const { getDescribe, getIt, expect } = require('../utils/mocha') 5 | const { expectIsPingResponse, isPong } = require('./utils') 6 | const all = require('it-all') 7 | const { isWebWorker } = require('ipfs-utils/src/env') 8 | 9 | /** @typedef { import("ipfsd-ctl/src/factory") } Factory */ 10 | /** 11 | * @param {Factory} common 12 | * @param {Object} options 13 | */ 14 | module.exports = (common, options) => { 15 | const describe = getDescribe(options) 16 | const it = getIt(options) 17 | 18 | describe('.ping', function () { 19 | this.timeout(60 * 1000) 20 | 21 | let ipfsA 22 | let ipfsB 23 | 24 | before(async () => { 25 | ipfsA = (await common.spawn()).api 26 | // webworkers are not dialable because webrtc is not available 27 | ipfsB = (await common.spawn({ type: isWebWorker ? 'go' : undefined })).api 28 | await ipfsA.swarm.connect(ipfsB.peerId.addresses[0]) 29 | }) 30 | 31 | after(() => common.clean()) 32 | 33 | it('should send the specified number of packets', async () => { 34 | const count = 3 35 | const responses = await all(ipfsA.ping(ipfsB.peerId.id, { count })) 36 | responses.forEach(expectIsPingResponse) 37 | 38 | const pongs = responses.filter(isPong) 39 | expect(pongs.length).to.equal(count) 40 | }) 41 | 42 | it('should fail when pinging a peer that is not available', () => { 43 | const notAvailablePeerId = 'QmUmaEnH1uMmvckMZbh3yShaasvELPW4ZLPWnB4entMTEn' 44 | const count = 2 45 | 46 | return expect(all(ipfsA.ping(notAvailablePeerId, { count }))).to.eventually.be.rejected() 47 | }) 48 | 49 | it('should fail when pinging an invalid peer Id', () => { 50 | const invalidPeerId = 'not a peer ID' 51 | const count = 2 52 | 53 | return expect(all(ipfsA.ping(invalidPeerId, { count }))).to.eventually.be.rejected() 54 | }) 55 | }) 56 | } 57 | -------------------------------------------------------------------------------- /src/ping/utils.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const { expect } = require('../utils/mocha') 4 | 5 | function expectIsPingResponse (obj) { 6 | expect(obj).to.have.a.property('success') 7 | expect(obj).to.have.a.property('time') 8 | expect(obj).to.have.a.property('text') 9 | expect(obj.success).to.be.a('boolean') 10 | expect(obj.time).to.be.a('number') 11 | expect(obj.text).to.be.a('string') 12 | } 13 | 14 | exports.expectIsPingResponse = expectIsPingResponse 15 | 16 | // Determine if a ping response object is a pong, or something else, like a status message 17 | function isPong (pingResponse) { 18 | return Boolean(pingResponse && pingResponse.success && !pingResponse.text) 19 | } 20 | 21 | exports.isPong = isPong 22 | -------------------------------------------------------------------------------- /src/pubsub/index.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | const { createSuite } = require('../utils/suite') 3 | 4 | const tests = { 5 | publish: require('./publish'), 6 | subscribe: require('./subscribe'), 7 | unsubscribe: require('./unsubscribe'), 8 | peers: require('./peers'), 9 | ls: require('./ls') 10 | } 11 | 12 | module.exports = createSuite(tests) 13 | -------------------------------------------------------------------------------- /src/pubsub/ls.js: -------------------------------------------------------------------------------- 1 | /* eslint-env mocha */ 2 | 'use strict' 3 | 4 | const { getTopic } = require('./utils') 5 | const { getDescribe, getIt, expect } = require('../utils/mocha') 6 | const delay = require('delay') 7 | 8 | /** @typedef { import("ipfsd-ctl/src/factory") } Factory */ 9 | /** 10 | * @param {Factory} common 11 | * @param {Object} options 12 | */ 13 | module.exports = (common, options) => { 14 | const describe = getDescribe(options) 15 | const it = getIt(options) 16 | 17 | describe('.pubsub.ls', function () { 18 | this.timeout(80 * 1000) 19 | 20 | let ipfs 21 | let subscribedTopics = [] 22 | before(async () => { 23 | ipfs = (await common.spawn()).api 24 | }) 25 | 26 | afterEach(async () => { 27 | for (let i = 0; i < subscribedTopics.length; i++) { 28 | await ipfs.pubsub.unsubscribe(subscribedTopics[i]) 29 | } 30 | subscribedTopics = [] 31 | await delay(100) 32 | }) 33 | 34 | after(() => common.clean()) 35 | 36 | it('should return an empty list when no topics are subscribed', async () => { 37 | const topics = await ipfs.pubsub.ls() 38 | expect(topics.length).to.equal(0) 39 | }) 40 | 41 | it('should return a list with 1 subscribed topic', async () => { 42 | const sub1 = () => {} 43 | const topic = getTopic() 44 | subscribedTopics = [topic] 45 | 46 | await ipfs.pubsub.subscribe(topic, sub1) 47 | const topics = await ipfs.pubsub.ls() 48 | expect(topics).to.be.eql([topic]) 49 | }) 50 | 51 | it('should return a list with 3 subscribed topics', async () => { 52 | const topics = [{ 53 | name: 'one', 54 | handler () {} 55 | }, { 56 | name: 'two', 57 | handler () {} 58 | }, { 59 | name: 'three', 60 | handler () {} 61 | }] 62 | 63 | subscribedTopics = topics.map(t => t.name) 64 | 65 | for (let i = 0; i < topics.length; i++) { 66 | await ipfs.pubsub.subscribe(topics[i].name, topics[i].handler) 67 | } 68 | 69 | const list = await ipfs.pubsub.ls() 70 | expect(list.sort()).to.eql(topics.map(t => t.name).sort()) 71 | }) 72 | }) 73 | } 74 | -------------------------------------------------------------------------------- /src/pubsub/peers.js: -------------------------------------------------------------------------------- 1 | /* eslint-env mocha */ 2 | 'use strict' 3 | 4 | const { waitForPeers, getTopic } = require('./utils') 5 | const { getDescribe, getIt, expect } = require('../utils/mocha') 6 | const delay = require('delay') 7 | const { isWebWorker } = require('ipfs-utils/src/env') 8 | 9 | /** @typedef { import("ipfsd-ctl/src/factory") } Factory */ 10 | /** 11 | * @param {Factory} common 12 | * @param {Object} options 13 | */ 14 | module.exports = (common, options) => { 15 | const describe = getDescribe(options) 16 | const it = getIt(options) 17 | 18 | describe('.pubsub.peers', function () { 19 | this.timeout(80 * 1000) 20 | 21 | let ipfs1 22 | let ipfs2 23 | let ipfs3 24 | let subscribedTopics = [] 25 | before(async () => { 26 | ipfs1 = (await common.spawn()).api 27 | // webworkers are not dialable because webrtc is not available 28 | ipfs2 = (await common.spawn({ type: isWebWorker ? 'go' : undefined })).api 29 | ipfs3 = (await common.spawn({ type: isWebWorker ? 'go' : undefined })).api 30 | 31 | const ipfs2Addr = ipfs2.peerId.addresses 32 | .find(ma => ma.nodeAddress().address === '127.0.0.1') 33 | const ipfs3Addr = ipfs3.peerId.addresses 34 | .find(ma => ma.nodeAddress().address === '127.0.0.1') 35 | 36 | await ipfs1.swarm.connect(ipfs2Addr) 37 | await ipfs1.swarm.connect(ipfs3Addr) 38 | await ipfs2.swarm.connect(ipfs3Addr) 39 | }) 40 | 41 | afterEach(async () => { 42 | const nodes = [ipfs1, ipfs2, ipfs3] 43 | for (let i = 0; i < subscribedTopics.length; i++) { 44 | const topic = subscribedTopics[i] 45 | await Promise.all(nodes.map(ipfs => ipfs.pubsub.unsubscribe(topic))) 46 | } 47 | subscribedTopics = [] 48 | await delay(100) 49 | }) 50 | 51 | after(() => common.clean()) 52 | 53 | it('should not error when not subscribed to a topic', async () => { 54 | const topic = getTopic() 55 | const peers = await ipfs1.pubsub.peers(topic) 56 | expect(peers).to.exist() 57 | // Should be empty() but as mentioned below go-ipfs returns more than it should 58 | // expect(peers).to.be.empty() 59 | }) 60 | 61 | it('should not return extra peers', async () => { 62 | // Currently go-ipfs returns peers that have not been 63 | // subscribed to the topic. Enable when go-ipfs has been fixed 64 | const sub1 = () => {} 65 | const sub2 = () => {} 66 | const sub3 = () => {} 67 | 68 | const topic = getTopic() 69 | const topicOther = topic + 'different topic' 70 | 71 | subscribedTopics = [topic, topicOther] 72 | 73 | await ipfs1.pubsub.subscribe(topic, sub1) 74 | await ipfs2.pubsub.subscribe(topicOther, sub2) 75 | await ipfs3.pubsub.subscribe(topicOther, sub3) 76 | 77 | const peers = await ipfs1.pubsub.peers(topic) 78 | expect(peers).to.be.empty() 79 | }) 80 | 81 | it('should return peers for a topic - one peer', async () => { 82 | // Currently go-ipfs returns peers that have not been 83 | // subscribed to the topic. Enable when go-ipfs has been fixed 84 | const sub1 = () => {} 85 | const sub2 = () => {} 86 | const sub3 = () => {} 87 | const topic = getTopic() 88 | 89 | subscribedTopics = [topic] 90 | 91 | await ipfs1.pubsub.subscribe(topic, sub1) 92 | await ipfs2.pubsub.subscribe(topic, sub2) 93 | await ipfs3.pubsub.subscribe(topic, sub3) 94 | 95 | await waitForPeers(ipfs1, topic, [ipfs2.peerId.id], 30000) 96 | }) 97 | 98 | it('should return peers for a topic - multiple peers', async () => { 99 | const sub1 = () => {} 100 | const sub2 = () => {} 101 | const sub3 = () => {} 102 | const topic = getTopic() 103 | 104 | subscribedTopics = [topic] 105 | 106 | await ipfs1.pubsub.subscribe(topic, sub1) 107 | await ipfs2.pubsub.subscribe(topic, sub2) 108 | await ipfs3.pubsub.subscribe(topic, sub3) 109 | 110 | await waitForPeers(ipfs1, topic, [ipfs2.peerId.id, ipfs3.peerId.id], 30000) 111 | }) 112 | }) 113 | } 114 | -------------------------------------------------------------------------------- /src/pubsub/publish.js: -------------------------------------------------------------------------------- 1 | /* eslint-env mocha */ 2 | 'use strict' 3 | 4 | const hat = require('hat') 5 | const { getTopic } = require('./utils') 6 | const { getDescribe, getIt } = require('../utils/mocha') 7 | 8 | /** @typedef { import("ipfsd-ctl/src/factory") } Factory */ 9 | /** 10 | * @param {Factory} common 11 | * @param {Object} options 12 | */ 13 | module.exports = (common, options) => { 14 | const describe = getDescribe(options) 15 | const it = getIt(options) 16 | 17 | describe('.pubsub.publish', function () { 18 | this.timeout(80 * 1000) 19 | 20 | let ipfs 21 | 22 | before(async () => { 23 | ipfs = (await common.spawn()).api 24 | }) 25 | 26 | after(() => common.clean()) 27 | 28 | it('should publish message from string', () => { 29 | const topic = getTopic() 30 | return ipfs.pubsub.publish(topic, 'hello friend') 31 | }) 32 | 33 | it('should publish message from buffer', () => { 34 | const topic = getTopic() 35 | return ipfs.pubsub.publish(topic, Buffer.from(hat())) 36 | }) 37 | 38 | it('should publish 10 times within time limit', async () => { 39 | const count = 10 40 | const topic = getTopic() 41 | 42 | for (let i = 0; i < count; i++) { 43 | await ipfs.pubsub.publish(topic, Buffer.from(hat())) 44 | } 45 | }) 46 | }) 47 | } 48 | -------------------------------------------------------------------------------- /src/pubsub/unsubscribe.js: -------------------------------------------------------------------------------- 1 | /* eslint-env mocha */ 2 | 'use strict' 3 | 4 | const { isBrowser, isWebWorker, isElectronRenderer } = require('ipfs-utils/src/env') 5 | const { getTopic } = require('./utils') 6 | const { getDescribe, getIt, expect } = require('../utils/mocha') 7 | const delay = require('delay') 8 | 9 | /** @typedef { import("ipfsd-ctl/src/factory") } Factory */ 10 | /** 11 | * @param {Factory} common 12 | * @param {Object} options 13 | */ 14 | module.exports = (common, options) => { 15 | const describe = getDescribe(options) 16 | const it = getIt(options) 17 | 18 | describe('.pubsub.unsubscribe', function () { 19 | this.timeout(80 * 1000) 20 | 21 | let ipfs 22 | 23 | before(async () => { 24 | ipfs = (await common.spawn()).api 25 | }) 26 | 27 | after(() => common.clean()) 28 | 29 | // Browser/worker has max ~5 open HTTP requests to the same origin 30 | const count = isBrowser || isWebWorker || isElectronRenderer ? 5 : 10 31 | 32 | it(`should subscribe and unsubscribe ${count} times`, async () => { 33 | const someTopic = getTopic() 34 | const handlers = Array.from(Array(count), () => msg => {}) 35 | 36 | for (let i = 0; i < count; i++) { 37 | await ipfs.pubsub.subscribe(someTopic, handlers[i]) 38 | } 39 | 40 | for (let i = 0; i < count; i++) { 41 | await ipfs.pubsub.unsubscribe(someTopic, handlers[i]) 42 | } 43 | 44 | await delay(100) 45 | const topics = await ipfs.pubsub.ls() 46 | expect(topics).to.eql([]) 47 | }) 48 | 49 | it(`should subscribe ${count} handlers and unsubscribe once with no reference to the handlers`, async () => { 50 | const someTopic = getTopic() 51 | for (let i = 0; i < count; i++) { 52 | await ipfs.pubsub.subscribe(someTopic, (msg) => {}) 53 | } 54 | await ipfs.pubsub.unsubscribe(someTopic) 55 | 56 | await delay(100) 57 | const topics = await ipfs.pubsub.ls() 58 | expect(topics).to.eql([]) 59 | }) 60 | }) 61 | } 62 | -------------------------------------------------------------------------------- /src/pubsub/utils.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const hat = require('hat') 4 | const delay = require('delay') 5 | 6 | async function waitForPeers (ipfs, topic, peersToWait, waitForMs) { 7 | const start = Date.now() 8 | 9 | while (true) { 10 | const peers = await ipfs.pubsub.peers(topic) 11 | const everyPeerFound = peersToWait.every(p => peers.includes(p)) 12 | 13 | if (everyPeerFound) { 14 | return 15 | } 16 | 17 | if (Date.now() > start + waitForMs) { 18 | throw new Error(`Timed out waiting for peers to be subscribed to "${topic}"`) 19 | } 20 | 21 | await delay(10) 22 | } 23 | } 24 | 25 | exports.waitForPeers = waitForPeers 26 | 27 | exports.getTopic = () => 'pubsub-tests-' + hat() 28 | -------------------------------------------------------------------------------- /src/refs-local.js: -------------------------------------------------------------------------------- 1 | /* eslint-env mocha */ 2 | 'use strict' 3 | 4 | const { fixtures } = require('./utils') 5 | const { getDescribe, getIt, expect } = require('./utils/mocha') 6 | const all = require('it-all') 7 | 8 | /** @typedef { import("ipfsd-ctl/src/factory") } Factory */ 9 | /** 10 | * @param {Factory} common 11 | * @param {Object} options 12 | */ 13 | module.exports = (common, options) => { 14 | const describe = getDescribe(options) 15 | const it = getIt(options) 16 | 17 | describe('.refs.local', function () { 18 | this.timeout(60 * 1000) 19 | 20 | let ipfs 21 | 22 | before(async () => { 23 | ipfs = (await common.spawn()).api 24 | }) 25 | 26 | after(() => common.clean()) 27 | 28 | it('should get local refs', async function () { 29 | const content = (name) => ({ 30 | path: `test-folder/${name}`, 31 | content: fixtures.directory.files[name] 32 | }) 33 | 34 | const dirs = [ 35 | content('pp.txt'), 36 | content('holmes.txt') 37 | ] 38 | 39 | await all(ipfs.add(dirs)) 40 | 41 | const refs = await all(ipfs.refs.local()) 42 | 43 | const cids = refs.map(r => r.ref) 44 | expect(cids).to.include('QmVwdDCY4SPGVFnNCiZnX5CtzwWDn6kAM98JXzKxE3kCmn') 45 | expect(cids).to.include('QmR4nFjTu18TyANgC65ArNWp5Yaab1gPzQ4D8zp7Kx3vhr') 46 | }) 47 | }) 48 | } 49 | -------------------------------------------------------------------------------- /src/repo/index.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | const { createSuite } = require('../utils/suite') 3 | 4 | const tests = { 5 | version: require('./version'), 6 | stat: require('./stat'), 7 | gc: require('./gc') 8 | } 9 | 10 | module.exports = createSuite(tests) 11 | -------------------------------------------------------------------------------- /src/repo/stat.js: -------------------------------------------------------------------------------- 1 | /* eslint-env mocha */ 2 | 'use strict' 3 | 4 | const { expectIsRepo } = require('../stats/utils') 5 | const { getDescribe, getIt } = require('../utils/mocha') 6 | 7 | /** @typedef { import("ipfsd-ctl/src/factory") } Factory */ 8 | /** 9 | * @param {Factory} common 10 | * @param {Object} options 11 | */ 12 | module.exports = (common, options) => { 13 | const describe = getDescribe(options) 14 | const it = getIt(options) 15 | 16 | describe('.repo.stat', () => { 17 | let ipfs 18 | 19 | before(async () => { 20 | ipfs = (await common.spawn()).api 21 | }) 22 | 23 | after(() => common.clean()) 24 | 25 | it('should get repo stats', async () => { 26 | const res = await ipfs.repo.stat() 27 | expectIsRepo(null, res) 28 | }) 29 | }) 30 | } 31 | -------------------------------------------------------------------------------- /src/repo/version.js: -------------------------------------------------------------------------------- 1 | /* eslint-env mocha */ 2 | 'use strict' 3 | 4 | const { getDescribe, getIt, expect } = require('../utils/mocha') 5 | 6 | /** @typedef { import("ipfsd-ctl/src/factory") } Factory */ 7 | /** 8 | * @param {Factory} common 9 | * @param {Object} options 10 | */ 11 | module.exports = (common, options) => { 12 | const describe = getDescribe(options) 13 | const it = getIt(options) 14 | 15 | describe('.repo.version', () => { 16 | let ipfs 17 | 18 | before(async () => { 19 | ipfs = (await common.spawn()).api 20 | }) 21 | 22 | after(() => common.clean()) 23 | 24 | it('should get the repo version', async () => { 25 | const version = await ipfs.repo.version() 26 | expect(version).to.exist() 27 | }) 28 | }) 29 | } 30 | -------------------------------------------------------------------------------- /src/stats/bitswap.js: -------------------------------------------------------------------------------- 1 | /* eslint-env mocha */ 2 | 'use strict' 3 | 4 | const { getDescribe, getIt } = require('../utils/mocha') 5 | const { expectIsBitswap } = require('./utils') 6 | 7 | /** @typedef { import("ipfsd-ctl/src/factory") } Factory */ 8 | /** 9 | * @param {Factory} common 10 | * @param {Object} options 11 | */ 12 | module.exports = (common, options) => { 13 | const describe = getDescribe(options) 14 | const it = getIt(options) 15 | 16 | describe('.stats.bitswap', () => { 17 | let ipfs 18 | 19 | before(async () => { 20 | ipfs = (await common.spawn()).api 21 | }) 22 | 23 | after(() => common.clean()) 24 | 25 | it('should get bitswap stats', async () => { 26 | const res = await ipfs.stats.bitswap() 27 | expectIsBitswap(null, res) 28 | }) 29 | }) 30 | } 31 | -------------------------------------------------------------------------------- /src/stats/bw.js: -------------------------------------------------------------------------------- 1 | /* eslint-env mocha */ 2 | 'use strict' 3 | 4 | const { expectIsBandwidth } = require('./utils') 5 | const { getDescribe, getIt } = require('../utils/mocha') 6 | const last = require('it-last') 7 | 8 | /** @typedef { import("ipfsd-ctl/src/factory") } Factory */ 9 | /** 10 | * @param {Factory} common 11 | * @param {Object} options 12 | */ 13 | module.exports = (common, options) => { 14 | const describe = getDescribe(options) 15 | const it = getIt(options) 16 | 17 | describe('.stats.bw', () => { 18 | let ipfs 19 | 20 | before(async () => { 21 | ipfs = (await common.spawn()).api 22 | }) 23 | 24 | after(() => common.clean()) 25 | 26 | it('should get bandwidth stats ', async () => { 27 | const res = await last(ipfs.stats.bw()) 28 | expectIsBandwidth(null, res) 29 | }) 30 | }) 31 | } 32 | -------------------------------------------------------------------------------- /src/stats/index.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | const { createSuite } = require('../utils/suite') 3 | 4 | const tests = { 5 | bitswap: require('./bitswap'), 6 | bw: require('./bw'), 7 | repo: require('./repo') 8 | } 9 | 10 | module.exports = createSuite(tests) 11 | -------------------------------------------------------------------------------- /src/stats/repo.js: -------------------------------------------------------------------------------- 1 | /* eslint-env mocha */ 2 | 'use strict' 3 | 4 | const { expectIsRepo } = require('./utils') 5 | const { getDescribe, getIt } = require('../utils/mocha') 6 | 7 | /** @typedef { import("ipfsd-ctl/src/factory") } Factory */ 8 | /** 9 | * @param {Factory} common 10 | * @param {Object} options 11 | */ 12 | module.exports = (common, options) => { 13 | const describe = getDescribe(options) 14 | const it = getIt(options) 15 | 16 | describe('.stats.repo', () => { 17 | let ipfs 18 | 19 | before(async () => { 20 | ipfs = (await common.spawn()).api 21 | }) 22 | 23 | after(() => common.clean()) 24 | 25 | it('should get repo stats', async () => { 26 | const res = await ipfs.stats.repo() 27 | expectIsRepo(null, res) 28 | }) 29 | }) 30 | } 31 | -------------------------------------------------------------------------------- /src/stats/utils.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const { expect } = require('../utils/mocha') 4 | 5 | const isBigInt = (n) => { 6 | return n.constructor.name === 'BigNumber' 7 | } 8 | 9 | exports.expectIsBitswap = (err, stats) => { 10 | expect(err).to.not.exist() 11 | expect(stats).to.exist() 12 | expect(stats).to.have.a.property('provideBufLen') 13 | expect(stats).to.have.a.property('wantlist') 14 | expect(stats).to.have.a.property('peers') 15 | expect(stats).to.have.a.property('blocksReceived') 16 | expect(stats).to.have.a.property('dataReceived') 17 | expect(stats).to.have.a.property('blocksSent') 18 | expect(stats).to.have.a.property('dataSent') 19 | expect(stats).to.have.a.property('dupBlksReceived') 20 | expect(stats).to.have.a.property('dupDataReceived') 21 | 22 | expect(stats.provideBufLen).to.a('number') 23 | expect(stats.wantlist).to.be.an('array') 24 | expect(stats.peers).to.be.an('array') 25 | expect(isBigInt(stats.blocksReceived)).to.eql(true) 26 | expect(isBigInt(stats.dataReceived)).to.eql(true) 27 | expect(isBigInt(stats.blocksSent)).to.eql(true) 28 | expect(isBigInt(stats.dataSent)).to.eql(true) 29 | expect(isBigInt(stats.dupBlksReceived)).to.eql(true) 30 | expect(isBigInt(stats.dupDataReceived)).to.eql(true) 31 | } 32 | 33 | exports.expectIsBandwidth = (err, stats) => { 34 | expect(err).to.not.exist() 35 | expect(stats).to.exist() 36 | expect(stats).to.have.a.property('totalIn') 37 | expect(stats).to.have.a.property('totalOut') 38 | expect(stats).to.have.a.property('rateIn') 39 | expect(stats).to.have.a.property('rateOut') 40 | expect(isBigInt(stats.totalIn)).to.eql(true) 41 | expect(isBigInt(stats.totalOut)).to.eql(true) 42 | expect(isBigInt(stats.rateIn)).to.eql(true) 43 | expect(isBigInt(stats.rateOut)).to.eql(true) 44 | } 45 | 46 | exports.expectIsRepo = (err, res) => { 47 | expect(err).to.not.exist() 48 | expect(res).to.exist() 49 | expect(res).to.have.a.property('numObjects') 50 | expect(res).to.have.a.property('repoSize') 51 | expect(res).to.have.a.property('repoPath') 52 | expect(res).to.have.a.property('version') 53 | expect(res).to.have.a.property('storageMax') 54 | expect(isBigInt(res.numObjects)).to.eql(true) 55 | expect(isBigInt(res.repoSize)).to.eql(true) 56 | expect(isBigInt(res.storageMax)).to.eql(true) 57 | expect(res.repoPath).to.be.a('string') 58 | expect(res.version).to.be.a('string') 59 | } 60 | -------------------------------------------------------------------------------- /src/swarm/addrs.js: -------------------------------------------------------------------------------- 1 | /* eslint-env mocha */ 2 | 'use strict' 3 | 4 | const CID = require('cids') 5 | const Multiaddr = require('multiaddr') 6 | const { getDescribe, getIt, expect } = require('../utils/mocha') 7 | const { isWebWorker } = require('ipfs-utils/src/env') 8 | 9 | /** @typedef { import("ipfsd-ctl/src/factory") } Factory */ 10 | /** 11 | * @param {Factory} common 12 | * @param {Object} options 13 | */ 14 | module.exports = (common, options) => { 15 | const describe = getDescribe(options) 16 | const it = getIt(options) 17 | 18 | describe('.swarm.addrs', function () { 19 | this.timeout(80 * 1000) 20 | 21 | let ipfsA 22 | let ipfsB 23 | 24 | before(async () => { 25 | ipfsA = (await common.spawn()).api 26 | // webworkers are not dialable because webrtc is not available 27 | ipfsB = (await common.spawn({ type: isWebWorker ? 'go' : undefined })).api 28 | await ipfsA.swarm.connect(ipfsB.peerId.addresses[0]) 29 | }) 30 | 31 | after(() => common.clean()) 32 | 33 | it('should get a list of node addresses', async () => { 34 | const peerInfos = await ipfsA.swarm.addrs() 35 | expect(peerInfos).to.not.be.empty() 36 | expect(peerInfos).to.be.an('array') 37 | 38 | expect(peerInfos).to.all.satisfy(peerInfo => { 39 | expect(CID.isCID(new CID(peerInfo.id))).to.be.true() 40 | expect(peerInfo).to.have.a.property('addrs').that.is.an('array').and.all.satisfy(ma => Multiaddr.isMultiaddr(ma)) 41 | 42 | return true 43 | }) 44 | }) 45 | }) 46 | } 47 | -------------------------------------------------------------------------------- /src/swarm/connect.js: -------------------------------------------------------------------------------- 1 | /* eslint-env mocha */ 2 | 'use strict' 3 | 4 | const { getDescribe, getIt, expect } = require('../utils/mocha') 5 | const { isWebWorker } = require('ipfs-utils/src/env') 6 | 7 | /** @typedef { import("ipfsd-ctl/src/factory") } Factory */ 8 | /** 9 | * @param {Factory} common 10 | * @param {Object} options 11 | */ 12 | module.exports = (common, options) => { 13 | const describe = getDescribe(options) 14 | const it = getIt(options) 15 | 16 | describe('.swarm.connect', function () { 17 | this.timeout(80 * 1000) 18 | let ipfsA 19 | let ipfsB 20 | 21 | before(async () => { 22 | ipfsA = (await common.spawn()).api 23 | // webworkers are not dialable because webrtc is not available 24 | ipfsB = (await common.spawn({ type: isWebWorker ? 'go' : undefined })).api 25 | }) 26 | 27 | after(() => common.clean()) 28 | 29 | it('should connect to a peer', async () => { 30 | let peers 31 | 32 | peers = await ipfsA.swarm.peers() 33 | expect(peers).to.have.length(0) 34 | 35 | await ipfsA.swarm.connect(ipfsB.peerId.addresses[0]) 36 | 37 | peers = await ipfsA.swarm.peers() 38 | expect(peers).to.have.length.above(0) 39 | }) 40 | }) 41 | } 42 | -------------------------------------------------------------------------------- /src/swarm/disconnect.js: -------------------------------------------------------------------------------- 1 | /* eslint-env mocha */ 2 | 'use strict' 3 | 4 | const { getDescribe, getIt, expect } = require('../utils/mocha') 5 | const { isWebWorker } = require('ipfs-utils/src/env') 6 | 7 | /** @typedef { import("ipfsd-ctl/src/factory") } Factory */ 8 | /** 9 | * @param {Factory} common 10 | * @param {Object} options 11 | */ 12 | module.exports = (common, options) => { 13 | const describe = getDescribe(options) 14 | const it = getIt(options) 15 | 16 | describe('.swarm.disconnect', function () { 17 | this.timeout(80 * 1000) 18 | 19 | let ipfsA 20 | let ipfsB 21 | 22 | before(async () => { 23 | ipfsA = (await common.spawn()).api 24 | // webworkers are not dialable because webrtc is not available 25 | ipfsB = (await common.spawn({ type: isWebWorker ? 'go' : undefined })).api 26 | await ipfsA.swarm.connect(ipfsB.peerId.addresses[0]) 27 | }) 28 | 29 | after(() => common.clean()) 30 | 31 | it('should disconnect from a peer', async () => { 32 | let peers 33 | 34 | peers = await ipfsA.swarm.peers() 35 | expect(peers).to.have.length.above(0) 36 | 37 | await ipfsA.swarm.disconnect(ipfsB.peerId.addresses[0]) 38 | 39 | peers = await ipfsA.swarm.peers() 40 | expect(peers).to.have.length(0) 41 | }) 42 | }) 43 | } 44 | -------------------------------------------------------------------------------- /src/swarm/index.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | const { createSuite } = require('../utils/suite') 3 | 4 | const tests = { 5 | connect: require('./connect'), 6 | peers: require('./peers'), 7 | addrs: require('./addrs'), 8 | localAddrs: require('./local-addrs'), 9 | disconnect: require('./disconnect') 10 | } 11 | 12 | module.exports = createSuite(tests) 13 | -------------------------------------------------------------------------------- /src/swarm/local-addrs.js: -------------------------------------------------------------------------------- 1 | /* eslint-env mocha */ 2 | 'use strict' 3 | 4 | const { getDescribe, getIt, expect } = require('../utils/mocha') 5 | const { isWebWorker } = require('ipfs-utils/src/env') 6 | 7 | /** @typedef { import("ipfsd-ctl/src/factory") } Factory */ 8 | /** 9 | * @param {Factory} common 10 | * @param {Object} options 11 | */ 12 | module.exports = (common, options) => { 13 | const describe = getDescribe(options) 14 | const it = getIt(options) 15 | 16 | describe('.swarm.localAddrs', function () { 17 | this.timeout(80 * 1000) 18 | 19 | let ipfs 20 | 21 | before(async () => { 22 | ipfs = (await common.spawn()).api 23 | }) 24 | 25 | after(() => common.clean()) 26 | 27 | it('should list local addresses the node is listening on', async () => { 28 | const multiaddrs = await ipfs.swarm.localAddrs() 29 | 30 | expect(multiaddrs).to.be.an.instanceOf(Array) 31 | 32 | if (isWebWorker && common.opts.type === 'proc') { 33 | expect(multiaddrs).to.have.lengthOf(0) 34 | } else { 35 | expect(multiaddrs).to.not.be.empty() 36 | } 37 | }) 38 | }) 39 | } 40 | -------------------------------------------------------------------------------- /src/utils/echo-http-server.js: -------------------------------------------------------------------------------- 1 | /* eslint-env browser */ 2 | 'use strict' 3 | 4 | const http = require('http') 5 | const URL = require('url').URL || self.URL 6 | 7 | const defaultPort = 11080 8 | 9 | // Create a mock of remote HTTP server that can return arbitrary text in response 10 | // or redirect to other URL. Used in tests of ipfs.addFromURL etc 11 | // It needs to be available to tests run in browsers: 12 | // start it from Node via .aegir.js/hooks/browser/pre|post (example in js-ipfs) 13 | module.exports.createServer = () => { 14 | const handler = (req, res) => { 15 | // Relaxed CORS to enable use in tests in web browser with fetch 16 | res.setHeader('Access-Control-Allow-Origin', '*') 17 | res.setHeader('Access-Control-Request-Method', '*') 18 | res.setHeader('Access-Control-Allow-Methods', 'OPTIONS, GET, DELETE') 19 | res.setHeader('Access-Control-Allow-Headers', '*') 20 | if (req.method === 'OPTIONS') { 21 | res.writeHead(200) 22 | res.end() 23 | return 24 | } 25 | // get the path without query or hash 26 | const { pathname } = new URL(`https://127.0.0.1${req.url}`) 27 | if (pathname.startsWith('/echo/')) { 28 | // Respond with text passed in URL after /echo/ 29 | const [, text] = pathname.split('/echo/') 30 | res.setHeader('Content-Type', 'text/plain') 31 | res.write(decodeURIComponent(text)) 32 | } else if (req.url.startsWith('/302/')) { 33 | // Return a redirect to a passed URL 34 | const [, location] = pathname.split('/302/') 35 | const url = decodeURI(location) 36 | res.statusCode = 302 37 | res.setHeader('Location', url) 38 | } else { 39 | res.statusCode = 500 40 | } 41 | res.end() 42 | } 43 | 44 | const server = http.createServer(handler) 45 | 46 | server.start = (opts) => new Promise((resolve, reject) => { 47 | server.once('error', reject) 48 | server.listen(Object.assign({ port: defaultPort, host: '127.0.0.1' }, opts), () => { 49 | server.removeListener('error', reject) 50 | resolve() 51 | }) 52 | }) 53 | 54 | server.stop = () => new Promise((resolve, reject) => { 55 | server.once('error', reject) 56 | server.close((err) => { 57 | server.removeListener('error', reject) 58 | err ? reject(err) : resolve() 59 | }) 60 | }) 61 | 62 | return server 63 | } 64 | 65 | module.exports.defaultPort = defaultPort 66 | module.exports.url = `http://127.0.0.1:${module.exports.defaultPort}` 67 | module.exports.echoUrl = (text) => `${module.exports.url}/echo/${encodeURIComponent(text)}` 68 | module.exports.redirectUrl = (url) => `${module.exports.url}/302/${encodeURI(url)}` 69 | -------------------------------------------------------------------------------- /src/utils/index.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const loadFixture = require('aegir/fixtures') 4 | 5 | exports.fixtures = Object.freeze({ 6 | directory: Object.freeze({ 7 | cid: 'QmVvjDy7yF7hdnqE8Hrf4MHo5ABDtb5AbX6hWbD3Y42bXP', 8 | files: Object.freeze({ 9 | 'pp.txt': loadFixture('test/fixtures/test-folder/pp.txt', 'interface-ipfs-core'), 10 | 'holmes.txt': loadFixture('test/fixtures/test-folder/holmes.txt', 'interface-ipfs-core'), 11 | 'jungle.txt': loadFixture('test/fixtures/test-folder/jungle.txt', 'interface-ipfs-core'), 12 | 'alice.txt': loadFixture('test/fixtures/test-folder/alice.txt', 'interface-ipfs-core'), 13 | 'files/hello.txt': loadFixture('test/fixtures/test-folder/files/hello.txt', 'interface-ipfs-core'), 14 | 'files/ipfs.txt': loadFixture('test/fixtures/test-folder/files/ipfs.txt', 'interface-ipfs-core') 15 | }) 16 | }), 17 | smallFile: Object.freeze({ 18 | cid: 'Qma4hjFTnCasJ8PVp3mZbZK5g2vGDT4LByLJ7m8ciyRFZP', 19 | data: loadFixture('test/fixtures/testfile.txt', 'interface-ipfs-core') 20 | }), 21 | bigFile: Object.freeze({ 22 | cid: 'Qme79tX2bViL26vNjPsF3DP1R9rMKMvnPYJiKTTKPrXJjq', 23 | data: loadFixture('test/fixtures/15mb.random', 'interface-ipfs-core') 24 | }) 25 | }) 26 | -------------------------------------------------------------------------------- /src/utils/mocha.js: -------------------------------------------------------------------------------- 1 | /* eslint-env mocha */ 2 | 'use strict' 3 | 4 | const chai = require('chai') 5 | 6 | // Do not reorder these statements - https://github.com/chaijs/chai/issues/1298 7 | chai.use(require('chai-as-promised')) 8 | chai.use(require('dirty-chai')) 9 | chai.use(require('chai-things')) 10 | 11 | module.exports.expect = chai.expect 12 | 13 | const isObject = (o) => Object.prototype.toString.call(o) === '[object Object]' 14 | 15 | // Get a "describe" function that is optionally 'skipped' or 'onlyed' 16 | // If skip/only are boolean true, or an object with a reason property, then we 17 | // want to skip/only the whole suite 18 | function getDescribe (config) { 19 | if (config) { 20 | if (config.skip === true) return describe.skip 21 | 22 | if (isObject(config.skip)) { 23 | if (!config.skip.reason) return describe.skip 24 | 25 | const _describe = (name, impl) => { 26 | describe.skip(`${name} (${config.skip.reason})`, impl) 27 | } 28 | 29 | _describe.skip = describe.skip 30 | _describe.only = describe.only // eslint-disable-line 31 | 32 | return _describe 33 | } 34 | 35 | if (config.only === true) return describe.only // eslint-disable-line 36 | } 37 | 38 | return describe 39 | } 40 | 41 | module.exports.getDescribe = getDescribe 42 | 43 | // Get an "it" function that is optionally 'skipped' or 'onlyed' 44 | // If skip/only is an array, then we _might_ want to skip/only the specific 45 | // test if one of the items in the array is the same as the test name or if one 46 | // of the items in the array is an object with a name property that is the same 47 | // as the test name. 48 | function getIt (config) { 49 | if (!config) return it 50 | 51 | const _it = (name, impl) => { 52 | if (Array.isArray(config.skip)) { 53 | const skip = config.skip 54 | .map((s) => isObject(s) ? s : { name: s }) 55 | .find((s) => s.name === name) 56 | 57 | if (skip) { 58 | if (skip.reason) name = `${name} (${skip.reason})` 59 | return it.skip(name, impl) 60 | } 61 | } 62 | 63 | if (Array.isArray(config.only)) { 64 | const only = config.only 65 | .map((o) => isObject(o) ? o : { name: o }) 66 | .find((o) => o.name === name) 67 | 68 | if (only) { 69 | if (only.reason) name = `${name} (${only.reason})` 70 | return it.only(name, impl) // eslint-disable-line no-only-tests/no-only-tests 71 | } 72 | } 73 | 74 | it(name, impl) 75 | } 76 | 77 | _it.skip = it.skip 78 | _it.only = it.only // eslint-disable-line no-only-tests/no-only-tests 79 | 80 | return _it 81 | } 82 | 83 | module.exports.getIt = getIt 84 | -------------------------------------------------------------------------------- /src/utils/suite.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const isObject = (o) => Object.prototype.toString.call(o) === '[object Object]' 4 | 5 | function createSuite (tests, parent) { 6 | const suite = (createCommon, options) => { 7 | Object.keys(tests).forEach(t => { 8 | const opts = Object.assign({}, options) 9 | const suiteName = parent ? `${parent}.${t}` : t 10 | 11 | if (Array.isArray(opts.skip)) { 12 | const skip = opts.skip 13 | .map((s) => isObject(s) ? s : { name: s }) 14 | .find((s) => s.name === suiteName) 15 | 16 | if (skip) { 17 | opts.skip = skip 18 | } 19 | } 20 | 21 | if (Array.isArray(opts.only)) { 22 | if (opts.only.includes(suiteName)) { 23 | opts.only = true 24 | } 25 | } 26 | 27 | tests[t](createCommon, opts) 28 | }) 29 | } 30 | 31 | return Object.assign(suite, tests) 32 | } 33 | 34 | exports.createSuite = createSuite 35 | -------------------------------------------------------------------------------- /test/fixtures/.gitattributes: -------------------------------------------------------------------------------- 1 | * -text 2 | -------------------------------------------------------------------------------- /test/fixtures/15mb.random: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ipfs-inactive/interface-js-ipfs-core/38bf852da912162e7d3b9bd0cec395c32fd2ec48/test/fixtures/15mb.random -------------------------------------------------------------------------------- /test/fixtures/hidden-files-folder/.hiddenTest.txt: -------------------------------------------------------------------------------- 1 | Aha! You found me! 2 | -------------------------------------------------------------------------------- /test/fixtures/hidden-files-folder/files/hello.txt: -------------------------------------------------------------------------------- 1 | Hello 2 | -------------------------------------------------------------------------------- /test/fixtures/hidden-files-folder/files/ipfs.txt: -------------------------------------------------------------------------------- 1 | IPFS 2 | -------------------------------------------------------------------------------- /test/fixtures/hidden-files-folder/hello-link: -------------------------------------------------------------------------------- 1 | Hello 2 | -------------------------------------------------------------------------------- /test/fixtures/hidden-files-folder/ipfs-add.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | 'use strict' 4 | 5 | const ipfs = require('../src')('localhost', 5001) 6 | const files = process.argv.slice(2) 7 | 8 | ipfs.add(files, { recursive: true }, function (err, res) { 9 | if (err || !res) return console.log(err) 10 | 11 | for (let i = 0; i < res.length; i++) { 12 | console.log('added', res[i].Hash, res[i].Name) 13 | } 14 | }) 15 | -------------------------------------------------------------------------------- /test/fixtures/hidden-files-folder/jungle.txt: -------------------------------------------------------------------------------- 1 | Mowgli's Brothers 2 | 3 | Now Rann the Kite brings home the night 4 | That Mang the Bat sets free-- 5 | The herds are shut in byre and hut 6 | For loosed till dawn are we. 7 | This is the hour of pride and power, 8 | Talon and tush and claw. 9 | Oh, hear the call!--Good hunting all 10 | That keep the Jungle Law! 11 | Night-Song in the Jungle 12 | 13 | It was seven o'clock of a very warm evening in the Seeonee hills when 14 | Father Wolf woke up from his day's rest, scratched himself, yawned, and 15 | spread out his paws one after the other to get rid of the sleepy feeling 16 | in their tips. Mother Wolf lay with her big gray nose dropped across her 17 | four tumbling, squealing cubs, and the moon shone into the mouth of the 18 | cave where they all lived. "Augrh!" said Father Wolf. "It is time to 19 | hunt again." He was going to spring down hill when a little shadow with 20 | a bushy tail crossed the threshold and whined: "Good luck go with you, O 21 | Chief of the Wolves. And good luck and strong white teeth go with noble 22 | children that they may never forget the hungry in this world." 23 | 24 | It was the jackal--Tabaqui, the Dish-licker--and the wolves of India 25 | despise Tabaqui because he runs about making mischief, and telling 26 | tales, and eating rags and pieces of leather from the village 27 | rubbish-heaps. But they are afraid of him too, because Tabaqui, more 28 | than anyone else in the jungle, is apt to go mad, and then he forgets 29 | that he was ever afraid of anyone, and runs through the forest biting 30 | everything in his way. Even the tiger runs and hides when little Tabaqui 31 | goes mad, for madness is the most disgraceful thing that can overtake 32 | a wild creature. We call it hydrophobia, but they call it dewanee--the 33 | madness--and run. 34 | 35 | "Enter, then, and look," said Father Wolf stiffly, "but there is no food 36 | here." 37 | 38 | "For a wolf, no," said Tabaqui, "but for so mean a person as myself a 39 | dry bone is a good feast. Who are we, the Gidur-log [the jackal people], 40 | to pick and choose?" He scuttled to the back of the cave, where he 41 | found the bone of a buck with some meat on it, and sat cracking the end 42 | merrily. 43 | 44 | "All thanks for this good meal," he said, licking his lips. "How 45 | beautiful are the noble children! How large are their eyes! And so young 46 | too! Indeed, indeed, I might have remembered that the children of kings 47 | -------------------------------------------------------------------------------- /test/fixtures/refs-test/animals/land/african.txt: -------------------------------------------------------------------------------- 1 | elephant 2 | rhinocerous -------------------------------------------------------------------------------- /test/fixtures/refs-test/animals/land/americas.txt: -------------------------------------------------------------------------------- 1 | ñandu 2 | tapir -------------------------------------------------------------------------------- /test/fixtures/refs-test/animals/land/australian.txt: -------------------------------------------------------------------------------- 1 | emu 2 | kangaroo -------------------------------------------------------------------------------- /test/fixtures/refs-test/animals/sea/atlantic.txt: -------------------------------------------------------------------------------- 1 | dolphin 2 | whale -------------------------------------------------------------------------------- /test/fixtures/refs-test/animals/sea/indian.txt: -------------------------------------------------------------------------------- 1 | cuttlefish 2 | octopus -------------------------------------------------------------------------------- /test/fixtures/refs-test/atlantic-animals: -------------------------------------------------------------------------------- 1 | dolphin 2 | whale -------------------------------------------------------------------------------- /test/fixtures/refs-test/fruits/tropical.txt: -------------------------------------------------------------------------------- 1 | banana 2 | pineapple -------------------------------------------------------------------------------- /test/fixtures/refs-test/mushroom.txt: -------------------------------------------------------------------------------- 1 | mushroom -------------------------------------------------------------------------------- /test/fixtures/ssl/cert.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIC8jCCAdqgAwIBAgIUA7b/br1Ovf/mNDhm3P26ewfHbpIwDQYJKoZIhvcNAQEL 3 | BQAwFDESMBAGA1UEAwwJbG9jYWxob3N0MCAXDTE5MDEyNDE4MjE0OVoYDzIxMTkw 4 | MTI0MTgyMTQ5WjAUMRIwEAYDVQQDDAlsb2NhbGhvc3QwggEiMA0GCSqGSIb3DQEB 5 | AQUAA4IBDwAwggEKAoIBAQCuskZ+qCJz72ihFgrF6yvjy7pXpETtHXJK+elXoU6M 6 | oWNukWOgk9EZQow43wM6pQR/rTxDWE9W/qSpLb08cvW03+RlbyQn0lkO327rN9Nd 7 | VjP1Nu6WwAdk6U0CaGdNe4dwxc69eB3ZS4B32d/GIpIti23F3bRxAKE15km+Ufhj 8 | X1NGuFqbJOYHfbqOMkgMlkO54y4gAJa5tQnb3n0pNzpIklSzBO65T/u1HAsVDVZW 9 | BgVW/pqourvh6TtjCA3LJp33T9IcItTAlYbiFM0hKytlePzaHG6OKRazO6Z46l/V 10 | gTvRY90B7LrQWnSY3saLYuv9Gs5qQvEvthP/U6LroXjNAgMBAAGjOjA4MBQGA1Ud 11 | EQQNMAuCCWxvY2FsaG9zdDALBgNVHQ8EBAMCB4AwEwYDVR0lBAwwCgYIKwYBBQUH 12 | AwEwDQYJKoZIhvcNAQELBQADggEBAJHi6CJk5aZViLK+dm/ruV2vBiqGuRgfuviJ 13 | Mb+iApO39Q/PjxE2IQoVVcf7Rpml2SSARyN7K9cxLdSFFZn3Wgq3yHXB6vhsyGO+ 14 | r17awBEI08PUlCuYVlE/mEzHGUGYbR0whIQSWK+gLMSQ2NG11DJyIPnErZYM1XSS 15 | p9ERjyR4KXC33RxEc0AtGZsGgCThGkmwas3v702pzGfDd3qpbXztb+jdbfUVMUj4 16 | Wrzhps9JZ6HJ8RZBjnSMMqmWDbvJI+2aG0Ky6BYChrARLn9H7rCMgfe0l0QIL5br 17 | T1BqL+HHCVNiyt82+byg5mjpcsKvojCrQVVcQBs1xUkk6F45LeU= 18 | -----END CERTIFICATE----- 19 | -------------------------------------------------------------------------------- /test/fixtures/ssl/privkey.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN PRIVATE KEY----- 2 | MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQCuskZ+qCJz72ih 3 | FgrF6yvjy7pXpETtHXJK+elXoU6MoWNukWOgk9EZQow43wM6pQR/rTxDWE9W/qSp 4 | Lb08cvW03+RlbyQn0lkO327rN9NdVjP1Nu6WwAdk6U0CaGdNe4dwxc69eB3ZS4B3 5 | 2d/GIpIti23F3bRxAKE15km+UfhjX1NGuFqbJOYHfbqOMkgMlkO54y4gAJa5tQnb 6 | 3n0pNzpIklSzBO65T/u1HAsVDVZWBgVW/pqourvh6TtjCA3LJp33T9IcItTAlYbi 7 | FM0hKytlePzaHG6OKRazO6Z46l/VgTvRY90B7LrQWnSY3saLYuv9Gs5qQvEvthP/ 8 | U6LroXjNAgMBAAECggEAYukJRNkJeL7KbLpAK0M1vGoy/UBCzkXn2k+ZMEZiZPlT 9 | hNzInbhToYuuPNz3xRJ9c5SwFClB8q2GqUr+Y+Vq/JfvhwbgX7OXPPaApKkdATG3 10 | hVUuzSe4iAgX1A8svg/85Xr5zQjfTZKUEEfJjTMxtJvG8UrPyVNj81KJ2joq+oek 11 | CspuQrhP9mpUaPBGmBykP0qbPqRO/E/eqMMEyfURDNXQjH1i4TLzUsIjd5IdkA9h 12 | DoC4fzK6R6LTjRdIEXK2xJGSKgIDnNu7sC72tTLRrbd4NeMjWg/yWEd7W/qQIy/6 13 | JG1Zj8zgjK1UnkPxZIwGuSOgCcNSCjA0WiSkF6sGeQKBgQDmvLIqCDaNDt+EOLoM 14 | HqXyoedriaJwzyTVUPf+EFHwwrOo/HoKesfca5coNIEJHMp5DjS+syPVTwKOfK82 15 | ipPT9UuW+F9N+dWGKjCiZ0vwc6ijrq2pEeXuxrJWsIJ3yoKTXZWTRbRnu6kA+dVJ 16 | XGwqwnHni8cikAhdMzGyGi0vXwKBgQDB0tIfGOwmHo+0W94KjnIoB8dA/Cy9K0V+ 17 | l/9IBX8I5/hFV2BF4IZoWepJ15UAJR855Tw5+0ea+/QpdSrl9MmmUQ0Hftvlmypp 18 | XYIDbWewgKHZOO6TnCs6FLtm6Zrq/1yrppnTslLXq/4QkOPSbQ9uY83N3upxfRIb 19 | V9//OLMDUwKBgHk7d9kBy7e9ss8EByzLBaJAUxl7jW/8RnwWONayuHrpsf/9+Bl9 20 | fXlgxmEHhSzGhdOpFSmFcjRneQ5okJ71nMpnPboq8dhEhl4h2L/byliiTF8ELpaA 21 | ovEcUSOfRk2uh4DqUOa6XxmJzjiHC/uppeOpmrNwC8crKlndxiSwAEG9AoGAFNLS 22 | kla6IEpORCFOlLHDH/vd82RkZhp9B+HKonE8ubc6XDDL/hXmOtXWLwLDVlWmqjCv 23 | rMcLZWJGVCHrbvNCquSwUqrVczCdeN579mRNrI/VU6IjN6aimkXZ8G+Onkq7KRHo 24 | Gu9gqR0oWZ1HbLcc3k5IsSKO64x1YoypWyE7UlMCgYBaMIyJb8hd+lcgTy+WNJwZ 25 | ovcp8KoxUFONA/fd3iyWKYPq+5OTD5iDOy9LKXUrlYduFEQog6vUamlQyoyTplLs 26 | d3sIeeu9RRvuCidKzHtmQNAOw8adIa4GAnBvJ2G2oZ3lWnDgIbKgHido+YSyyI2E 27 | jZOVuk3817sAOTaWSlQEZQ== 28 | -----END PRIVATE KEY----- 29 | -------------------------------------------------------------------------------- /test/fixtures/test-folder/files/hello.txt: -------------------------------------------------------------------------------- 1 | Hello 2 | -------------------------------------------------------------------------------- /test/fixtures/test-folder/files/ipfs.txt: -------------------------------------------------------------------------------- 1 | IPFS 2 | -------------------------------------------------------------------------------- /test/fixtures/test-folder/hello-link: -------------------------------------------------------------------------------- 1 | files/hello.txt -------------------------------------------------------------------------------- /test/fixtures/test-folder/ipfs-add.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | 'use strict' 4 | 5 | const ipfs = require('../src')('localhost', 5001) 6 | const files = process.argv.slice(2) 7 | 8 | ipfs.add(files, { recursive: true }, function (err, res) { 9 | if (err || !res) return console.log(err) 10 | 11 | for (let i = 0; i < res.length; i++) { 12 | console.log('added', res[i].Hash, res[i].Name) 13 | } 14 | }) 15 | -------------------------------------------------------------------------------- /test/fixtures/test-folder/jungle.txt: -------------------------------------------------------------------------------- 1 | Mowgli's Brothers 2 | 3 | Now Rann the Kite brings home the night 4 | That Mang the Bat sets free-- 5 | The herds are shut in byre and hut 6 | For loosed till dawn are we. 7 | This is the hour of pride and power, 8 | Talon and tush and claw. 9 | Oh, hear the call!--Good hunting all 10 | That keep the Jungle Law! 11 | Night-Song in the Jungle 12 | 13 | It was seven o'clock of a very warm evening in the Seeonee hills when 14 | Father Wolf woke up from his day's rest, scratched himself, yawned, and 15 | spread out his paws one after the other to get rid of the sleepy feeling 16 | in their tips. Mother Wolf lay with her big gray nose dropped across her 17 | four tumbling, squealing cubs, and the moon shone into the mouth of the 18 | cave where they all lived. "Augrh!" said Father Wolf. "It is time to 19 | hunt again." He was going to spring down hill when a little shadow with 20 | a bushy tail crossed the threshold and whined: "Good luck go with you, O 21 | Chief of the Wolves. And good luck and strong white teeth go with noble 22 | children that they may never forget the hungry in this world." 23 | 24 | It was the jackal--Tabaqui, the Dish-licker--and the wolves of India 25 | despise Tabaqui because he runs about making mischief, and telling 26 | tales, and eating rags and pieces of leather from the village 27 | rubbish-heaps. But they are afraid of him too, because Tabaqui, more 28 | than anyone else in the jungle, is apt to go mad, and then he forgets 29 | that he was ever afraid of anyone, and runs through the forest biting 30 | everything in his way. Even the tiger runs and hides when little Tabaqui 31 | goes mad, for madness is the most disgraceful thing that can overtake 32 | a wild creature. We call it hydrophobia, but they call it dewanee--the 33 | madness--and run. 34 | 35 | "Enter, then, and look," said Father Wolf stiffly, "but there is no food 36 | here." 37 | 38 | "For a wolf, no," said Tabaqui, "but for so mean a person as myself a 39 | dry bone is a good feast. Who are we, the Gidur-log [the jackal people], 40 | to pick and choose?" He scuttled to the back of the cave, where he 41 | found the bone of a buck with some meat on it, and sat cracking the end 42 | merrily. 43 | 44 | "All thanks for this good meal," he said, licking his lips. "How 45 | beautiful are the noble children! How large are their eyes! And so young 46 | too! Indeed, indeed, I might have remembered that the children of kings 47 | -------------------------------------------------------------------------------- /test/fixtures/test-folder/pp.txt: -------------------------------------------------------------------------------- 1 | PRIDE AND PREJUDICE 2 | 3 | By Jane Austen 4 | 5 | 6 | 7 | Chapter 1 8 | 9 | 10 | It is a truth universally acknowledged, that a single man in possession 11 | of a good fortune, must be in want of a wife. 12 | 13 | However little known the feelings or views of such a man may be on his 14 | first entering a neighbourhood, this truth is so well fixed in the minds 15 | of the surrounding families, that he is considered the rightful property 16 | of some one or other of their daughters. 17 | 18 | "My dear Mr. Bennet," said his lady to him one day, "have you heard that 19 | Netherfield Park is let at last?" 20 | 21 | Mr. Bennet replied that he had not. 22 | 23 | "But it is," returned she; "for Mrs. Long has just been here, and she 24 | told me all about it." 25 | 26 | Mr. Bennet made no answer. 27 | 28 | "Do you not want to know who has taken it?" cried his wife impatiently. 29 | 30 | "_You_ want to tell me, and I have no objection to hearing it." 31 | 32 | This was invitation enough. 33 | 34 | "Why, my dear, you must know, Mrs. Long says that Netherfield is taken 35 | by a young man of large fortune from the north of England; that he came 36 | down on Monday in a chaise and four to see the place, and was so much 37 | delighted with it, that he agreed with Mr. Morris immediately; that he 38 | is to take possession before Michaelmas, and some of his servants are to 39 | be in the house by the end of next week." 40 | 41 | "What is his name?" 42 | 43 | "Bingley." 44 | 45 | "Is he married or single?" 46 | 47 | "Oh! Single, my dear, to be sure! A single man of large fortune; four or 48 | five thousand a year. What a fine thing for our girls!" 49 | 50 | "How so? How can it affect them?" 51 | 52 | "My dear Mr. Bennet," replied his wife, "how can you be so tiresome! You 53 | must know that I am thinking of his marrying one of them." 54 | 55 | "Is that his design in settling here?" 56 | 57 | "Design! Nonsense, how can you talk so! But it is very likely that he 58 | _may_ fall in love with one of them, and therefore you must visit him as 59 | soon as he comes." 60 | 61 | "I see no occasion for that. You and the girls may go, or you may send 62 | them by themselves, which perhaps will be still better, for as you are 63 | as handsome as any of them, Mr. Bingley may like you the best of the 64 | party." 65 | 66 | "My dear, you flatter me. I certainly _have_ had my share of beauty, but 67 | I do not pretend to be anything extraordinary now. When a woman has five 68 | grown-up daughters, she ought to give over thinking of her own beauty." 69 | 70 | "In such cases, a woman has not often much beauty to think of." 71 | 72 | "But, my dear, you must indeed go and see Mr. Bingley when he comes into 73 | the neighbourhood." 74 | 75 | "It is more than I engage for, I assure you." 76 | 77 | "But consider your daughters. Only think what an establishment it would 78 | be for one of them. Sir William and Lady Lucas are determined to 79 | go, merely on that account, for in general, you know, they visit no 80 | newcomers. Indeed you must go, for it will be impossible for _us_ to 81 | visit him if you do not." 82 | 83 | "You are over-scrupulous, surely. I dare say Mr. Bingley will be very 84 | glad to see you; and I will send a few lines by you to assure him of my 85 | hearty consent to his marrying whichever he chooses of the girls; though 86 | I must throw in a good word for my little Lizzy." 87 | 88 | "I desire you will do no such thing. Lizzy is not a bit better than the 89 | others; and I am sure she is not half so handsome as Jane, nor half so 90 | good-humoured as Lydia. But you are always giving _her_ the preference." 91 | 92 | "They have none of them much to recommend them," replied he; "they are 93 | all silly and ignorant like other girls; but Lizzy has something more of 94 | quickness than her sisters." 95 | 96 | "Mr. Bennet, how _can_ you abuse your own children in such a way? You 97 | take delight in vexing me. You have no compassion for my poor nerves." 98 | 99 | "You mistake me, my dear. I have a high respect for your nerves. They 100 | are my old friends. I have heard you mention them with consideration 101 | these last twenty years at least." 102 | 103 | "Ah, you do not know what I suffer." 104 | 105 | "But I hope you will get over it, and live to see many young men of four 106 | thousand a year come into the neighbourhood." 107 | 108 | "It will be no use to us, if twenty such should come, since you will not 109 | visit them." 110 | 111 | "Depend upon it, my dear, that when there are twenty, I will visit them 112 | all." 113 | 114 | Mr. Bennet was so odd a mixture of quick parts, sarcastic humour, 115 | reserve, and caprice, that the experience of three-and-twenty years had 116 | been insufficient to make his wife understand his character. _Her_ mind 117 | was less difficult to develop. She was a woman of mean understanding, 118 | little information, and uncertain temper. When she was discontented, 119 | she fancied herself nervous. The business of her life was to get her 120 | daughters married; its solace was visiting and news. 121 | -------------------------------------------------------------------------------- /test/fixtures/testfile.txt: -------------------------------------------------------------------------------- 1 | Plz add me! 2 | -------------------------------------------------------------------------------- /test/fixtures/weird name folder [v0]/add: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const ipfs = require('../src')('localhost', 5001) 4 | 5 | const f1 = 'Hello' 6 | const f2 = 'World' 7 | 8 | ipfs.add([new Buffer(f1), new Buffer(f2)], function (err, res) { 9 | if (err || !res) return console.log(err) 10 | 11 | for (let i = 0; i < res.length; i++) { 12 | console.log(res[i]) 13 | } 14 | }) 15 | 16 | ipfs.add(['./files/hello.txt', './files/ipfs.txt'], function (err, res) { 17 | if (err || !res) return console.log(err) 18 | 19 | for (let i = 0; i < res.length; i++) { 20 | console.log(res[i]) 21 | } 22 | }) 23 | -------------------------------------------------------------------------------- /test/fixtures/weird name folder [v0]/cat: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const ipfs = require('../src')('localhost', 5001) 4 | 5 | const hash = [ 6 | 'QmdFyxZXsFiP4csgfM5uPu99AvFiKH62CSPDw5TP92nr7w', 7 | 'QmY9cxiHqTFoWamkQVkpmmqzBrY3hCBEL2XNu3NtX74Fuu' 8 | ] 9 | 10 | ipfs.cat(hash, function (err, res) { 11 | if (err || !res) return console.log(err) 12 | 13 | if (res.readable) { 14 | res.pipe(process.stdout) 15 | } else { 16 | console.log(res) 17 | } 18 | }) 19 | -------------------------------------------------------------------------------- /test/fixtures/weird name folder [v0]/files/hello.txt: -------------------------------------------------------------------------------- 1 | Hello 2 | -------------------------------------------------------------------------------- /test/fixtures/weird name folder [v0]/files/ipfs.txt: -------------------------------------------------------------------------------- 1 | IPFS 2 | -------------------------------------------------------------------------------- /test/fixtures/weird name folder [v0]/hello-link: -------------------------------------------------------------------------------- 1 | Hello 2 | -------------------------------------------------------------------------------- /test/fixtures/weird name folder [v0]/ipfs-add: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | 'use strict' 4 | 5 | const ipfs = require('../src')('localhost', 5001) 6 | const files = process.argv.slice(2) 7 | 8 | ipfs.add(files, {recursive: true}, function (err, res) { 9 | if (err || !res) return console.log(err) 10 | 11 | for (let i = 0; i < res.length; i++) { 12 | console.log('added', res[i].Hash, res[i].Name) 13 | } 14 | }) 15 | -------------------------------------------------------------------------------- /test/fixtures/weird name folder [v0]/ls: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const ipfs = require('../src')('localhost', 5001) 4 | 5 | const hash = ['QmdbHK6gMiecyjjSoPnfJg6iKMF7v6E2NkoBgGpmyCoevh'] 6 | 7 | ipfs.ls(hash, function (err, res) { 8 | if (err || !res) return console.log(err) 9 | 10 | res.Objects.forEach(function (node) { 11 | console.log(node.Hash) 12 | 13 | console.log('Links [%d]', node.Links.length) 14 | node.Links.forEach(function (link, i) { 15 | console.log('[%d]', i, link) 16 | }) 17 | }) 18 | }) 19 | -------------------------------------------------------------------------------- /test/fixtures/weird name folder [v0]/version: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const ipfs = require('../src')('localhost', 5001) 4 | 5 | ipfs.commands(function (err, res) { 6 | if (err) throw err 7 | console.log(res) 8 | }) 9 | -------------------------------------------------------------------------------- /test/interface.spec.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | --------------------------------------------------------------------------------