Comparing version 1.4.12 to 1.5.0
'use strict'; | ||
const await = require('asyncawait/await'); | ||
const async = require('asyncawait/async'); | ||
const Log = require('../src/log'); | ||
@@ -14,3 +12,3 @@ const Entry = require('../src/entry'); | ||
ipfsd.disposableApi((err, ipfs) => { | ||
if(err) console.error(err); | ||
if(err) reject(err) | ||
resolve(ipfs); | ||
@@ -39,8 +37,24 @@ }); | ||
let lastTenSeconds = 0; | ||
let store; | ||
let log; | ||
const queryLoop = () => { | ||
log.add(totalQueries) | ||
.then(() => { | ||
totalQueries ++; | ||
lastTenSeconds ++; | ||
queriesPerSecond ++; | ||
setTimeout(() => { | ||
process.nextTick(queryLoop); | ||
}, 0) | ||
}) | ||
.catch((e) => { | ||
console.log(e) | ||
process.exit(0); | ||
}) | ||
} | ||
let run = (() => { | ||
console.log("Starting IPFS..."); | ||
console.log("Starting benchmark..."); | ||
startIpfs().then(async((ipfs) => { | ||
startIpfs().then((ipfs) => { | ||
// Output metrics at 1 second interval | ||
@@ -59,15 +73,8 @@ setInterval(() => { | ||
const log = new Log(ipfs, 'A'); | ||
for(var i = 0; i < 20000; i ++) { | ||
await(log.add(totalQueries)); | ||
totalQueries ++; | ||
lastTenSeconds ++; | ||
queriesPerSecond ++; | ||
} | ||
log = new Log(ipfs, 'A'); | ||
queryLoop() | ||
}).catch((e) => console.error(e)) | ||
process.exit(0); | ||
})).catch((e) => console.error(e)); | ||
})(); | ||
module.exports = run; |
{ | ||
"name": "ipfs-log", | ||
"version": "1.4.12", | ||
"version": "1.5.0", | ||
"description": "Append-only log for IPFS", | ||
@@ -10,6 +10,5 @@ "main": "src/log.js", | ||
"bluebird": "^3.3.5", | ||
"buffer": "^4.5.1", | ||
"lazy.js": "^0.4.2", | ||
"lodash.differencewith": "^4.4.0", | ||
"lodash.flatten": "^4.3.0", | ||
"lodash.take": "^4.1.1", | ||
"lodash.unionwith": "^4.5.0" | ||
@@ -23,2 +22,3 @@ }, | ||
"babel-preset-es2015": "^6.6.0", | ||
"buffer": "^4.5.1", | ||
"brfs": "^1.4.3", | ||
@@ -28,3 +28,3 @@ "exports-loader": "^0.6.3", | ||
"ipfs": "^0.13.0", | ||
"ipfs-api": "^6.0.3", | ||
"ipfs-api": "^7.0.0", | ||
"ipfs-merkle-dag": "^0.6.0", | ||
@@ -34,4 +34,3 @@ "ipfsd-ctl": "^0.14.0", | ||
"libp2p-ipfs-browser": "^0.12.0", | ||
"logplease": "^1.2.7", | ||
"mocha": "^2.4.5", | ||
"mocha": "^3.0.1", | ||
"stream-http": "^2.2.1", | ||
@@ -38,0 +37,0 @@ "transform-loader": "^0.2.3", |
177
README.md
@@ -25,2 +25,3 @@ # ipfs-log | ||
## Install | ||
``` | ||
@@ -34,9 +35,18 @@ npm install ipfs-log | ||
### Quick Example | ||
### Quick Start | ||
Install dependencies: | ||
``` | ||
npm install ipfs-log ipfs | ||
``` | ||
Run a simple program: | ||
```javascript | ||
const IPFS = require('ipfs') | ||
const Log = require('ipfs-log'); | ||
const Log = require('ipfs-log') | ||
const ipfs = new IPFS(); | ||
const log = new Log(ipfs, 'A'); | ||
const ipfs = new IPFS() | ||
const log = new Log(ipfs, 'A') | ||
@@ -47,35 +57,57 @@ log.add({ some: 'data' }) | ||
// [Entry { | ||
// payload: { some: 'data' }, | ||
// hash: 'QmYiefTHzCLNroCfKw7YTUy9Yo53sCfwzyU5p7SBBxTcmD', | ||
// next: [] | ||
// }, | ||
// Entry { | ||
// payload: 'text', | ||
// hash: 'QmdNFpoyXLNdR8Wx5LYZBLcXH8aAEopSMnnubWLn4AciCZ', | ||
// next: [ 'QmYiefTHzCLNroCfKw7YTUy9Yo53sCfwzyU5p7SBBxTcmD' ] | ||
// }] | ||
// [ | ||
// { | ||
// payload: { some: 'data' }, | ||
// hash: 'QmYiefTHzCLNroCfKw7YTUy9Yo53sCfwzyU5p7SBBxTcmD', | ||
// next: [] | ||
// }, | ||
// { | ||
// payload: 'text', | ||
// hash: 'QmdNFpoyXLNdR8Wx5LYZBLcXH8aAEopSMnnubWLn4AciCZ', | ||
// next: [ 'QmYiefTHzCLNroCfKw7YTUy9Yo53sCfwzyU5p7SBBxTcmD' ] | ||
// } | ||
// ] | ||
``` | ||
### Node.js | ||
See [examples](https://github.com/haadcode/ipfs-log/tree/master/examples) for details. | ||
#### Run | ||
``` | ||
node examples/log.js | ||
``` | ||
#### Code | ||
```javascript | ||
const IPFS = require('ipfs') | ||
const Log = require('ipfs-log'); | ||
const Log = require('ipfs-log') | ||
const log = new Log(new IPFS(), 'A', 'db name', { maxHistory: 1000 }); | ||
const log = new Log(new IPFS(), 'A', { maxHistory: 1000 }) | ||
log.add('one') | ||
.then((entry1) => { | ||
console.log('Entry1:', entry1.hash, entry1.payload); | ||
return log.add('two'); | ||
console.log('Entry1:', entry1.hash, entry1.payload) | ||
return log.add('two') | ||
}) | ||
.then((entry2) => { | ||
console.log('Entry2:', entry2.hash, entry2.payload); | ||
console.log('Entry2.next:', entry2.next[0]); // == entry1.hash | ||
}); | ||
console.log('Entry2:', entry2.hash, entry2.payload) | ||
console.log('Entry2.next:', entry2.next[0]) // == entry1.hash | ||
}) | ||
``` | ||
### Browser | ||
*The distribution package for browsers is located in [dist/ipfslog.min.js](https://github.com/haadcode/ipfs-log/tree/master/dist)* | ||
See [examples/browser](https://github.com/haadcode/ipfs-log/tree/master/examples/browser) for details. | ||
#### Run | ||
Open [examples/browser/index.html](https://github.com/haadcode/ipfs-log/tree/master/examples/browser/index.html) or [examples/browser/browser.html](https://github.com/haadcode/ipfs-log/tree/master/examples/browser/browser.html) in your browser. | ||
#### Code | ||
```html | ||
@@ -90,12 +122,12 @@ <html> | ||
<script type="text/javascript"> | ||
const ipfs = new window.Ipfs(); | ||
const ipfs = new window.Ipfs() | ||
const log = new Log(ipfs, 'A') | ||
log.add('one') | ||
.then((entry1) => { | ||
console.log('Entry1:', entry1.hash, entry1.payload, entry1); | ||
console.log('Entry1:', entry1.hash, entry1.payload, entry1) | ||
return log.add('two') | ||
}) | ||
.then((entry2) => { | ||
console.log('Entry2:', entry2.hash, entry2.payload, entry2); | ||
console.log("Entry2.next:", entry2.next[0]); | ||
console.log('Entry2:', entry2.hash, entry2.payload, entry2) | ||
console.log("Entry2.next:", entry2.next[0]) | ||
}); | ||
@@ -107,3 +139,3 @@ </script> | ||
#### Building the examples | ||
#### Building the browser examples | ||
@@ -118,16 +150,19 @@ ``` | ||
### Log | ||
```javascript | ||
const Log = require('ipfs-log'); | ||
const Log = require('ipfs-log') | ||
``` | ||
#### Instance methods | ||
##### constructor(ipfs, id, [name], [options]) | ||
#### Instance Methods | ||
##### constructor(ipfs, id, [options]) | ||
Create a log. The first argument is an `ipfs` instance which can be of type `js-ipfs` or `js-ipfs-api`. See https://github.com/ipfs/js-ipfs-api for IPFS api documentation. | ||
```javascript | ||
const ipfs = require('ipfs')(); // ipfs javascript implementation | ||
const ipfs = require('ipfs')() // ipfs javascript implementation | ||
// Or | ||
const ipfs = require('ipfs-api')(); // local ipfs daemon (go-ipfs) | ||
const ipfs = require('ipfs-api')() // local ipfs daemon (go-ipfs) | ||
const log = new Log(ipfs, 'userid', 'name of the log'); | ||
const log = new Log(ipfs, 'logid') // 'logid' is a unique identifier for the log, this can usually be a user id | ||
``` | ||
@@ -139,4 +174,2 @@ | ||
`name` is the name of the log for convenience purposes. | ||
`options` are the following: | ||
@@ -150,2 +183,3 @@ ```javscript | ||
##### add(data) | ||
Add a log entry. The new entry gets the references to previous entries automatically. Returns a *Promise* that resolves to the added `Entry`. | ||
@@ -160,3 +194,4 @@ | ||
// [Entry { | ||
//[ | ||
// { | ||
// payload: { some: 'data' }, | ||
@@ -166,10 +201,12 @@ // hash: 'QmYiefTHzCLNroCfKw7YTUy9Yo53sCfwzyU5p7SBBxTcmD', | ||
// }, | ||
// Entry { | ||
// { | ||
// payload: 'text', | ||
// hash: 'QmdNFpoyXLNdR8Wx5LYZBLcXH8aAEopSMnnubWLn4AciCZ', | ||
// next: [ 'QmYiefTHzCLNroCfKw7YTUy9Yo53sCfwzyU5p7SBBxTcmD' ] | ||
// }] | ||
// } | ||
//] | ||
``` | ||
##### join(other) | ||
Joins the log with `other` log. Fetches history up to `options.maxHistory` items, ie. items that are not in this log but referred to in items in `other`. Returns a *Promise* that resolves to an `Array` of items that were added. | ||
@@ -181,3 +218,3 @@ | ||
log1.join(log2).then((added) => console.log(added)); // ==> ['D', 'E'] | ||
log1.join(log2).then((added) => console.log(added)) // ==> ['D', 'E'] | ||
@@ -188,2 +225,3 @@ // log1.items ==> ['A', 'B', 'C', 'D', 'E'] | ||
##### items | ||
Returns an `Array` of all items in the log. | ||
@@ -197,17 +235,18 @@ | ||
##### snapshot | ||
Returns a *snapshot* of the log with items in the current batch. Current batch are the items in the log that have been added locally after the latest join with another log. | ||
```javascript | ||
const snapshot = log.snapshot; | ||
const snapshot = log.snapshot | ||
// snapshot ==> { id: 'log id', items: ['A', 'B', 'C']} | ||
``` | ||
#### Class methods | ||
#### Static Methods | ||
All class methods take an `ipfs` instance as the first parameter. The ipfs can be of `js-ipfs` or `js-ipfs-api`. See https://github.com/ipfs/js-ipfs-api for IPFS api documentation. | ||
All static methods take an `ipfs` instance as the first parameter. The ipfs can be of `js-ipfs` or `js-ipfs-api`. See https://github.com/ipfs/js-ipfs-api for IPFS api documentation. | ||
```javascript | ||
const ipfs = require('ipfs')(); // js-ipfs | ||
const ipfs = require('ipfs')() // js-ipfs | ||
// Or | ||
const ipfs = require('ipfs-api')(); // local ipfs daemon | ||
const ipfs = require('ipfs-api')() // local ipfs daemon | ||
``` | ||
@@ -218,6 +257,7 @@ | ||
##### getIpfsHash(ipfs, log) | ||
Get the IPFS hash of this log. Returns a `Promise` that resolves to an IPFS `hash`. | ||
```javascript | ||
Log.getIpfsHash(ipfs, log).then((hash) => console.log(hash)); | ||
Log.getIpfsHash(ipfs, log).then((hash) => console.log(hash)) | ||
// ==> 'Qm...abc123' | ||
@@ -227,42 +267,47 @@ ``` | ||
##### fromIpfsHash(ipfs, hash) | ||
Create a log from an IPFS hash. Returns a `Promise` that resolves to a `Log` instance. | ||
```javascript | ||
Log.fromIpfsHash(ipfs, hash).then((log) => console.log(log)); | ||
Log.fromIpfsHash(ipfs, hash).then((log) => console.log(log)) | ||
// ==> instance of Log | ||
``` | ||
##### fromSnapshot(ipfs, snapshot) | ||
Create a log from a log snapshot. Returns a `Promise` that resolves a `Log` instance. | ||
## Tests | ||
```javascript | ||
const original = new Log(ipfs, 'id') | ||
``` | ||
npm install | ||
npm test | ||
``` | ||
// Add items to the original log | ||
// ... | ||
## Build | ||
const snapshot = original.snapshot; | ||
Log.fromSnapshot(ipfs, snapshot).then((log) => console.log(log)); | ||
// ==> log is instance of Log and contains the same entries as original log | ||
The build script will build the distribution file for browsers. | ||
``` | ||
npm run build | ||
``` | ||
### Entry | ||
## Benchmark | ||
**TODO: document [Entry](https://github.com/haadcode/ipfs-log/blob/master/examples/entry.js).** | ||
There's a simple [benchmark program](https://github.com/haadcode/ipfs-log/blob/master/examples/benchmark.js) that can be used to compare performance between two version of `ipfs-log`. It measures write ops / second. | ||
## Tests | ||
``` | ||
npm install | ||
npm test | ||
node examples/benchmark.js | ||
``` | ||
## Build | ||
The build script will build the distribution file for browsers. | ||
This will output: | ||
``` | ||
npm run build | ||
Starting benchmark... | ||
131 queries per second, 131 queries in 1 seconds | ||
50 queries per second, 181 queries in 2 seconds | ||
44 queries per second, 225 queries in 3 seconds | ||
84 queries per second, 309 queries in 4 seconds | ||
111 queries per second, 420 queries in 5 seconds | ||
142 queries per second, 562 queries in 6 seconds | ||
157 queries per second, 719 queries in 7 seconds | ||
195 queries per second, 914 queries in 8 seconds | ||
171 queries per second, 1085 queries in 9 seconds | ||
--> Average of 125 q/s in the last 10 seconds | ||
... | ||
``` | ||
## TODO | ||
- Node.js Stream API | ||
- Support for encrypting the hashes | ||
- Support for payload encryption |
@@ -1,59 +0,74 @@ | ||
'use strict'; | ||
'use strict' | ||
class Entry { | ||
constructor(payload, next) { | ||
this.payload = payload || null; | ||
this.hash = null; | ||
this.next = next ? (next instanceof Array ? next : [next]) : []; | ||
this.next = this.next.map((next) => next instanceof Entry ? next.hash : next) // Convert instances of Entry to hashes | ||
} | ||
module.exports = class Entry { | ||
// Returns a Promise<Entry> | ||
// Example: | ||
// Entry.create(ipfs, "hello") | ||
// .then((entry) => console.log(entry)) // { hash: "Qm...Foo", payload: "hello", next: null } | ||
static create(ipfs, data, next = []) { | ||
if (!ipfs) throw new Error("Entry requires ipfs instance") | ||
get asJson() { | ||
let res = { payload: this.payload } | ||
let next = this.next.map((entry) => entry instanceof Entry ? entry.hash : entry) // Convert instances of Entry to hashes | ||
Object.assign(res, { next: next }); | ||
return res; | ||
} | ||
// convert single objects to an array and entry objects to single hashes | ||
let nexts = next !== null && next instanceof Array | ||
? next.map((e) => e.hash ? e.hash : e) | ||
: [(next !== null && next.hash ? next.hash : next)] | ||
hasChild(a) { | ||
for(let i = 0; i < this.next.length; i++) { | ||
if(this.next[i] === a.hash) | ||
return true; | ||
let entry = { | ||
hash: null, // "Qm...Foo", we'll set the hash after ipfsfying the data structure, | ||
payload: data, // Can be any JSON.stringifyable data | ||
next: nexts // Array of IPFS hashes | ||
} | ||
return false; | ||
} | ||
static create(ipfs, data, next) { | ||
if(!ipfs) throw new Error("Entry requires ipfs instance") | ||
if(data instanceof Entry) return Promise.resolve(data); | ||
const entry = new Entry(data, next); | ||
return Entry.getIpfsHash(ipfs, entry.asJson) | ||
return Entry.toIpfsHash(ipfs, entry) | ||
.then((hash) => { | ||
entry.hash = hash; | ||
return entry; | ||
}); | ||
entry.hash = hash | ||
return entry | ||
}) | ||
} | ||
static getIpfsHash(ipfs, entry) { | ||
if(!ipfs) throw new Error("Entry requires ipfs instance") | ||
// Returns a Promise<String> | ||
// Example: | ||
// Entry.toIpfsHash(ipfs, entry) | ||
// .then((hash) => console.log(hash)) // "Qm...Foo" | ||
static toIpfsHash(ipfs, entry) { | ||
if (!ipfs) throw new Error("Entry requires ipfs instance") | ||
const data = new Buffer(JSON.stringify(entry)) | ||
return ipfs.object.put(data) | ||
.then((res) => res.toJSON().Hash); | ||
.then((res) => res.toJSON().Hash) | ||
} | ||
// Returns a Promise<Entry> | ||
// Example: | ||
// Entry.fromIpfsHash(ipfs, "Qm...Foo") | ||
// .then((entry) => console.log(entry)) // { hash: "Qm...Foo", payload: "hello", next: null } | ||
static fromIpfsHash(ipfs, hash) { | ||
if(!ipfs) throw new Error("Entry requires ipfs instance") | ||
if(!hash) throw new Error("Invalid hash: " + hash) | ||
if (!ipfs) throw new Error("Entry requires ipfs instance") | ||
if (!hash) throw new Error("Invalid hash: " + hash) | ||
return ipfs.object.get(hash, { enc: 'base58' }) | ||
.then((obj) => { | ||
const f = JSON.parse(obj.toJSON().Data) | ||
return Entry.create(ipfs, f.payload, f.next); | ||
}); | ||
const data = JSON.parse(obj.toJSON().Data) | ||
const entry = { | ||
hash: hash, | ||
payload: data.payload, | ||
next: data.next | ||
} | ||
return entry | ||
}) | ||
} | ||
static equals(a, b) { | ||
return a.hash === b.hash; | ||
// Returns a boolean | ||
// Example: | ||
// const hasChild = Entry.hasChild(entry1, entry2) | ||
// true|false | ||
static hasChild(entry1, entry2) { | ||
return entry1.next.includes(entry2.hash) | ||
} | ||
// Returns a boolean | ||
// Example: | ||
// const equal = Entry.compare(entry1, entry2) | ||
// true|false | ||
static compare(a, b) { | ||
return a.hash === b.hash | ||
} | ||
} | ||
module.exports = Entry; |
125
src/log.js
@@ -1,2 +0,2 @@ | ||
'use strict'; | ||
'use strict' | ||
@@ -6,26 +6,25 @@ const unionWith = require('lodash.unionwith') | ||
const flatten = require('lodash.flatten') | ||
const Lazy = require('lazy.js'); | ||
const Promise = require('bluebird'); | ||
const Entry = require('./entry'); | ||
const take = require('lodash.take') | ||
const Promise = require('bluebird') | ||
const Entry = require('./entry') | ||
const MaxBatchSize = 10; // How many items to keep per local batch | ||
const MaxHistory = 256; // How many items to fetch on join | ||
const MaxBatchSize = 10 // How many items to keep per local batch | ||
const MaxHistory = 256 // How many items to fetch on join | ||
class Log { | ||
constructor(ipfs, id, name, opts) { | ||
this.id = id; | ||
this.name = name; | ||
this._ipfs = ipfs; | ||
this._items = opts && opts.items ? opts.items : []; | ||
constructor(ipfs, id, opts) { | ||
this.id = id | ||
this._ipfs = ipfs | ||
this._items = opts && opts.items ? opts.items : [] | ||
this.options = { maxHistory: MaxHistory }; | ||
Object.assign(this.options, opts); | ||
delete this.options.items; | ||
this.options = { maxHistory: MaxHistory } | ||
Object.assign(this.options, opts) | ||
delete this.options.items | ||
this._currentBatch = []; | ||
this._heads = []; | ||
this._currentBatch = [] | ||
this._heads = [] | ||
} | ||
get items() { | ||
return this._items.concat(this._currentBatch); | ||
return this._items.concat(this._currentBatch) | ||
} | ||
@@ -41,37 +40,34 @@ | ||
add(data) { | ||
if(this._currentBatch.length >= MaxBatchSize) | ||
this._commit(); | ||
if (this._currentBatch.length >= MaxBatchSize) | ||
this._commit() | ||
return Entry.create(this._ipfs, data, this._heads) | ||
.then((entry) => { | ||
this._heads = [entry.hash]; | ||
this._currentBatch.push(entry); | ||
return entry; | ||
}); | ||
this._heads = [entry.hash] | ||
this._currentBatch.push(entry) | ||
return entry | ||
}) | ||
} | ||
join(other) { | ||
if(!other.items) throw new Error("The log to join must be an instance of Log") | ||
const diff = differenceWith(other.items, this.items, Entry.equals); | ||
if (!other.items) throw new Error("The log to join must be an instance of Log") | ||
const newItems = other.items.slice(0, Math.max(this.options.maxHistory, 1)) | ||
const diff = differenceWith(newItems, this.items, Entry.compare) | ||
// TODO: need deterministic sorting for the union | ||
const final = unionWith(this._currentBatch, diff, Entry.equals); | ||
this._items = this._items.concat(final); | ||
this._currentBatch = []; | ||
const final = unionWith(this._currentBatch, diff, Entry.compare) | ||
this._items = this._items.concat(final) | ||
this._currentBatch = [] | ||
const nexts = Lazy(diff) | ||
.map((f) => f.next) | ||
.flatten() | ||
.take(this.options.maxHistory) | ||
.toArray(); | ||
const nexts = take(flatten(diff.map((f) => f.next)), this.options.maxHistory) | ||
// Fetch history | ||
return Promise.map(nexts, (f) => { | ||
let all = this.items.map((a) => a.hash); | ||
let all = this.items.map((a) => a.hash) | ||
return this._fetchRecursive(this._ipfs, f, all, this.options.maxHistory - nexts.length, 0) | ||
.then((history) => { | ||
history.forEach((b) => this._insert(b)); | ||
return history; | ||
}); | ||
history.forEach((b) => this._insert(b)) | ||
return history | ||
}) | ||
}, { concurrency: 1 }).then((res) => { | ||
this._heads = Log.findHeads(this); | ||
this._heads = Log.findHeads(this) | ||
return flatten(res).concat(diff) | ||
@@ -82,20 +78,20 @@ }) | ||
_insert(entry) { | ||
let indices = Lazy(entry.next).map((next) => Lazy(this._items).map((f) => f.hash).indexOf(next)) // Find the item's parent's indices | ||
const index = indices.toArray().length > 0 ? Math.max(indices.max() + 1, 0) : 0; // find the largest index (latest parent) | ||
this._items.splice(index, 0, entry); | ||
return entry; | ||
let indices = entry.next.map((next) => this._items.map((f) => f.hash).indexOf(next)) // Find the item's parent's indices | ||
const index = indices.length > 0 ? Math.max(Math.max.apply(null, indices) + 1, 0) : 0 // find the largest index (latest parent) | ||
this._items.splice(index, 0, entry) | ||
return entry | ||
} | ||
_commit() { | ||
this._items = this._items.concat(this._currentBatch); | ||
this._currentBatch = []; | ||
this._items = this._items.concat(this._currentBatch) | ||
this._currentBatch = [] | ||
} | ||
_fetchRecursive(ipfs, hash, all, amount, depth) { | ||
const isReferenced = (list, item) => Lazy(list).reverse().find((f) => f === item) !== undefined; | ||
let result = []; | ||
const isReferenced = (list, item) => list.reverse().find((f) => f === item) !== undefined | ||
let result = [] | ||
// If the given hash is in the given log (all) or if we're at maximum depth, return | ||
if(isReferenced(all, hash) || depth >= amount) | ||
return Promise.resolve(result); | ||
if (isReferenced(all, hash) || depth >= amount) | ||
return Promise.resolve(result) | ||
@@ -105,43 +101,42 @@ // Create the entry and add it to the result | ||
.then((entry) => { | ||
result.push(entry); | ||
all.push(hash); | ||
depth ++; | ||
result.push(entry) | ||
all.push(hash) | ||
depth ++ | ||
return Promise.map(entry.next, (f) => this._fetchRecursive(ipfs, f, all, amount, depth), { concurrency: 1 }) | ||
.then((res) => flatten(res.concat(result))) | ||
}); | ||
}) | ||
} | ||
static getIpfsHash(ipfs, log) { | ||
if(!ipfs) throw new Error("Ipfs instance not defined") | ||
const data = new Buffer(JSON.stringify(log.snapshot)); | ||
if (!ipfs) throw new Error("Ipfs instance not defined") | ||
const data = new Buffer(JSON.stringify(log.snapshot)) | ||
return ipfs.object.put(data) | ||
.then((res) => res.toJSON().Hash); | ||
.then((res) => res.toJSON().Hash) | ||
} | ||
static fromIpfsHash(ipfs, hash, options) { | ||
if(!ipfs) throw new Error("Ipfs instance not defined") | ||
if(!hash) throw new Error("Invalid hash: " + hash) | ||
if(!options) options = {}; | ||
let logData; | ||
if (!ipfs) throw new Error("Ipfs instance not defined") | ||
if (!hash) throw new Error("Invalid hash: " + hash) | ||
if (!options) options = {} | ||
let logData | ||
return ipfs.object.get(hash, { enc: 'base58' }) | ||
.then((res) => logData = JSON.parse(res.toJSON().Data)) | ||
.then((res) => { | ||
if(!logData.items) throw new Error("Not a Log instance") | ||
if (!logData.items) throw new Error("Not a Log instance") | ||
return Promise.all(logData.items.map((f) => Entry.fromIpfsHash(ipfs, f))) | ||
}) | ||
.then((items) => Object.assign(options, { items: items })) | ||
.then((items) => new Log(ipfs, logData.id, '', options)) | ||
.then((items) => new Log(ipfs, logData.id, options)) | ||
} | ||
static findHeads(log) { | ||
return Lazy(log.items) | ||
return log.items | ||
.reverse() | ||
.filter((f) => !Log.isReferencedInChain(log, f)) | ||
.map((f) => f.hash) | ||
.toArray(); | ||
} | ||
static isReferencedInChain(log, item) { | ||
return Lazy(log.items).reverse().find((e) => e.hasChild(item)) !== undefined; | ||
return log.items.reverse().find((e) => Entry.hasChild(e, item)) !== undefined | ||
} | ||
@@ -154,2 +149,2 @@ | ||
module.exports = Log; | ||
module.exports = Log |
@@ -1,11 +0,11 @@ | ||
'use strict'; | ||
'use strict' | ||
const _ = require('lodash'); | ||
const assert = require('assert'); | ||
const async = require('asyncawait/async'); | ||
const await = require('asyncawait/await'); | ||
const IpfsApis = require('./ipfs-test-apis'); | ||
const Entry = require('../src/entry'); | ||
const _ = require('lodash') | ||
const assert = require('assert') | ||
const async = require('asyncawait/async') | ||
const await = require('asyncawait/await') | ||
const IpfsApis = require('./ipfs-test-apis') | ||
const Entry = require('../src/entry') | ||
let ipfs, ipfsDaemon; | ||
let ipfs, ipfsDaemon | ||
@@ -15,217 +15,144 @@ IpfsApis.forEach(function(ipfsApi) { | ||
describe('Entry with ' + ipfsApi.name, function() { | ||
this.timeout(40000); | ||
before(async((done) => { | ||
this.timeout(20000) | ||
before(async(() => { | ||
try { | ||
ipfs = await(ipfsApi.start()); | ||
ipfs = await(ipfsApi.start()) | ||
} catch(e) { | ||
console.log(e); | ||
assert.equal(e, null); | ||
console.log(e) | ||
assert.equal(e, null) | ||
} | ||
this.timeout(2000); | ||
done(); | ||
})); | ||
})) | ||
after(async((done) => { | ||
await(ipfsApi.stop()); | ||
done(); | ||
})); | ||
after(async(() => { | ||
await(ipfsApi.stop()) | ||
})) | ||
describe('create', () => { | ||
it('creates a an empty entry', async((done) => { | ||
const expectedHash = 'QmfAouPZ2Cu3Cjbjm63RVeWJt6L9QjTSyFLe9SK5dWXN1j'; | ||
const entry = await(Entry.create(ipfs)); | ||
assert.equal(entry.payload, null); | ||
assert.equal(entry.next.length, 0); | ||
assert.equal(entry.hash, expectedHash); | ||
done(); | ||
})); | ||
it('creates a an empty entry', async(() => { | ||
const expectedHash = 'QmQf2q7RCqfs8G1nhow4MHqiRTg3fAAwgr9tKi5jhvsfuk' | ||
const entry = await(Entry.create(ipfs)) | ||
assert.equal(entry.payload, null) | ||
assert.equal(entry.next.length, 0) | ||
assert.equal(entry.hash, expectedHash) | ||
})) | ||
it('creates a entry with payload', async((done) => { | ||
const expectedHash = 'QmP2wHv43QtH3aCj4pUXeoVmkdtqNBVtx5bfYyNSH6LmXG'; | ||
const payload = 'hello world'; | ||
const entry = await(Entry.create(ipfs, payload)); | ||
assert.equal(entry.payload, payload); | ||
assert.equal(entry.next.length, 0); | ||
assert.equal(entry.hash, expectedHash); | ||
done(); | ||
})); | ||
it('creates a entry with payload', async(() => { | ||
const expectedHash = 'QmTVyxLqh3qZkWZbpxkjX5hd4WXADWDxt2EamFApfYpsRv' | ||
const payload = 'hello world' | ||
const entry = await(Entry.create(ipfs, payload)) | ||
assert.equal(entry.payload, payload) | ||
assert.equal(entry.next.length, 0) | ||
assert.equal(entry.hash, expectedHash) | ||
})) | ||
it('creates a entry with payload and next', async((done) => { | ||
const expectedHash = 'QmW94BLFbGNbaPjgGasX1rV9aYdE2qxnQnUBf9PbLkiBUo'; | ||
const payload1 = 'hello world'; | ||
const payload2 = 'hello again'; | ||
const entry1 = await(Entry.create(ipfs, payload1)); | ||
const entry2 = await(Entry.create(ipfs, payload2, entry1)); | ||
assert.equal(entry2.payload, payload2); | ||
assert.equal(entry2.next.length, 1); | ||
assert.equal(entry2.hash, expectedHash); | ||
done(); | ||
})); | ||
it('creates a entry with payload and next', async(() => { | ||
const expectedHash = 'QmRzyeUuW5F8zxmEgJG3wRPH3i3W7iwPweza7UUHhXfK93' | ||
const payload1 = 'hello world' | ||
const payload2 = 'hello again' | ||
const entry1 = await(Entry.create(ipfs, payload1)) | ||
const entry2 = await(Entry.create(ipfs, payload2, entry1)) | ||
assert.equal(entry2.payload, payload2) | ||
assert.equal(entry2.next.length, 1) | ||
assert.equal(entry2.hash, expectedHash) | ||
})) | ||
it('`next` parameter can be a string', async((done) => { | ||
const entry1 = await(Entry.create(ipfs, null)); | ||
const entry2 = await(Entry.create(ipfs, null, entry1.hash)); | ||
assert.equal(typeof entry2.next[0] === 'string', true); | ||
done(); | ||
})); | ||
it('`next` parameter can be a string', async(() => { | ||
const entry1 = await(Entry.create(ipfs, null)) | ||
const entry2 = await(Entry.create(ipfs, null, entry1.hash)) | ||
assert.equal(typeof entry2.next[0] === 'string', true) | ||
})) | ||
it('`next` parameter can be an instance of Entry', async((done) => { | ||
const entry1 = await(Entry.create(ipfs, null)); | ||
const entry2 = await(Entry.create(ipfs, null, entry1)); | ||
assert.equal(typeof entry2.next[0] === 'string', true); | ||
done(); | ||
})); | ||
it('`next` parameter can be an instance of Entry', async(() => { | ||
const entry1 = await(Entry.create(ipfs, null)) | ||
const entry2 = await(Entry.create(ipfs, null, entry1)) | ||
assert.equal(typeof entry2.next[0] === 'string', true) | ||
})) | ||
it('throws an error if ipfs is not defined', async((done) => { | ||
it('throws an error if ipfs is not defined', async(() => { | ||
try { | ||
const entry = await(Entry.create()); | ||
const entry = await(Entry.create()) | ||
} catch(e) { | ||
assert.equal(e.message, 'Entry requires ipfs instance'); | ||
assert.equal(e.message, 'Entry requires ipfs instance') | ||
} | ||
done(); | ||
})); | ||
}); | ||
})) | ||
}) | ||
describe('toIpfsHash', () => { | ||
it('returns an ipfs hash', async(() => { | ||
const expectedHash = 'QmcNjhK93qwkFhGiPJ8kyPhPWagSoJoRBEx9BwSE5w8iLW' | ||
const entry = await(Entry.create(ipfs)) | ||
const hash = await(Entry.toIpfsHash(ipfs, entry)) | ||
assert.equal(hash, expectedHash) | ||
})) | ||
}) | ||
describe('fromIpfsHash', () => { | ||
it('creates a entry from ipfs hash', async((done) => { | ||
const expectedHash = 'QmW94BLFbGNbaPjgGasX1rV9aYdE2qxnQnUBf9PbLkiBUo'; | ||
const payload1 = 'hello world'; | ||
const payload2 = 'hello again'; | ||
const entry1 = await(Entry.create(ipfs, payload1)); | ||
const entry2 = await(Entry.create(ipfs, payload2, entry1)); | ||
const final = await(Entry.fromIpfsHash(ipfs, entry2.hash)); | ||
assert.equal(final.payload, payload2); | ||
assert.equal(final.next.length, 1); | ||
assert.equal(final.next[0], entry1.hash); | ||
assert.equal(final.hash, expectedHash); | ||
done(); | ||
})); | ||
it('creates a entry from ipfs hash', async(() => { | ||
const expectedHash = 'QmRzyeUuW5F8zxmEgJG3wRPH3i3W7iwPweza7UUHhXfK93' | ||
const payload1 = 'hello world' | ||
const payload2 = 'hello again' | ||
const entry1 = await(Entry.create(ipfs, payload1)) | ||
const entry2 = await(Entry.create(ipfs, payload2, entry1)) | ||
const final = await(Entry.fromIpfsHash(ipfs, entry2.hash)) | ||
assert.equal(final.payload, payload2) | ||
assert.equal(final.next.length, 1) | ||
assert.equal(final.next[0], entry1.hash) | ||
assert.equal(final.hash, expectedHash) | ||
})) | ||
it('throws an error if ipfs is not present', async((done) => { | ||
it('throws an error if ipfs is not present', async(() => { | ||
try { | ||
const entry = await(Entry.fromIpfsHash()); | ||
const entry = await(Entry.fromIpfsHash()) | ||
} catch(e) { | ||
assert.equal(e.message, 'Entry requires ipfs instance'); | ||
assert.equal(e.message, 'Entry requires ipfs instance') | ||
} | ||
done(); | ||
})); | ||
})) | ||
it('throws an error if hash is undefined', async((done) => { | ||
it('throws an error if hash is undefined', async(() => { | ||
try { | ||
const entry = await(Entry.fromIpfsHash(ipfs)); | ||
const entry = await(Entry.fromIpfsHash(ipfs)) | ||
} catch(e) { | ||
assert.equal(e.message, 'Invalid hash: undefined'); | ||
assert.equal(e.message, 'Invalid hash: undefined') | ||
} | ||
done(); | ||
})); | ||
}); | ||
})) | ||
}) | ||
describe('hasChild', () => { | ||
it('returns true if entry has a child', async((done) => { | ||
const payload1 = 'hello world'; | ||
const payload2 = 'hello again'; | ||
const entry1 = await(Entry.create(ipfs, payload1)); | ||
const entry2 = await(Entry.create(ipfs, payload2, entry1)); | ||
assert.equal(entry2.hasChild(entry1), true); | ||
done(); | ||
})); | ||
it('returns true if entry has a child', async(() => { | ||
const payload1 = 'hello world' | ||
const payload2 = 'hello again' | ||
const entry1 = await(Entry.create(ipfs, payload1)) | ||
const entry2 = await(Entry.create(ipfs, payload2, entry1)) | ||
assert.equal(Entry.hasChild(entry2, entry1), true) | ||
})) | ||
it('returns false if entry does not have a child', async((done) => { | ||
const payload1 = 'hello world'; | ||
const payload2 = 'hello again'; | ||
const entry1 = await(Entry.create(ipfs, payload1)); | ||
const entry2 = await(Entry.create(ipfs, payload2)); | ||
const entry3 = await(Entry.create(ipfs, payload2, entry2)); | ||
assert.equal(entry2.hasChild(entry1), false); | ||
assert.equal(entry3.hasChild(entry1), false); | ||
assert.equal(entry3.hasChild(entry2), true); | ||
done(); | ||
})); | ||
}); | ||
it('returns false if entry does not have a child', async(() => { | ||
const payload1 = 'hello world' | ||
const payload2 = 'hello again' | ||
const entry1 = await(Entry.create(ipfs, payload1)) | ||
const entry2 = await(Entry.create(ipfs, payload2)) | ||
const entry3 = await(Entry.create(ipfs, payload2, entry2)) | ||
assert.equal(Entry.hasChild(entry2, entry1), false) | ||
assert.equal(Entry.hasChild(entry3, entry1), false) | ||
assert.equal(Entry.hasChild(entry3, entry2), true) | ||
})) | ||
}) | ||
describe('getIpfsHash', () => { | ||
it('returns an ipfs hash', async((done) => { | ||
const expectedHash = 'QmfAouPZ2Cu3Cjbjm63RVeWJt6L9QjTSyFLe9SK5dWXN1j'; | ||
const entry = await(Entry.create(ipfs)); | ||
const hash = await(Entry.getIpfsHash(ipfs, entry.asJson)); | ||
assert.equal(hash, expectedHash); | ||
done(); | ||
})); | ||
}); | ||
describe('compare', () => { | ||
it('returns true if entries are the same', async(() => { | ||
const payload1 = 'hello world' | ||
const entry1 = await(Entry.create(ipfs, payload1)) | ||
const entry2 = await(Entry.create(ipfs, payload1)) | ||
assert.equal(Entry.compare(entry1, entry2), true) | ||
})) | ||
describe('asJson', () => { | ||
it('returns the entry as json with empty values', async((done) => { | ||
const payload = 'hello world'; | ||
const entry = await(Entry.create(ipfs, payload)); | ||
assert.notEqual(entry.asJson, null); | ||
assert.equal(entry.asJson.payload, payload); | ||
assert.equal(entry.asJson.next.length, 0); | ||
done(); | ||
})); | ||
it('returns the entry as json with values', async((done) => { | ||
const payload = 'hello world'; | ||
const entry1 = await(Entry.create(ipfs, payload)); | ||
const entry2 = await(Entry.create(ipfs, payload, entry1)); | ||
assert.equal(typeof entry2.next[0] === 'string', true); | ||
assert.notEqual(entry2.asJson, null); | ||
assert.equal(entry2.asJson.payload, payload); | ||
assert.equal(entry2.asJson.next.length, 1); | ||
assert.equal(entry2.asJson.next[0], entry1.hash); | ||
done(); | ||
})); | ||
it('returns entry as json with values when next is a hash', async((done) => { | ||
const payload = 'hello world'; | ||
const entry1 = await(Entry.create(ipfs, payload)); | ||
const entry2 = await(Entry.create(ipfs, payload, [entry1.hash])); | ||
assert.equal(typeof entry2.next[0] === 'string', true); | ||
assert.notEqual(entry2.asJson, null); | ||
assert.equal(entry2.asJson.payload, payload); | ||
assert.equal(entry2.asJson.next.length, 1); | ||
assert.equal(entry2.asJson.next[0], entry1.hash); | ||
done(); | ||
})); | ||
}); | ||
describe('equals', () => { | ||
it('entrys are equal when the payload is the same', async((done) => { | ||
const payload = 'hello world 1'; | ||
const entry1 = await(Entry.create(ipfs, payload)); | ||
const entry2 = await(Entry.create(ipfs, payload)); | ||
assert.equal(Entry.equals(entry1, entry2), true); | ||
done(); | ||
})); | ||
it('entrys are not equal when the payload is different', async((done) => { | ||
const payload1 = 'hello world 1'; | ||
const payload2 = 'hello world 2'; | ||
const entry1 = await(Entry.create(ipfs, payload1)); | ||
const entry2 = await(Entry.create(ipfs, payload2)); | ||
assert.equal(Entry.equals(entry1, entry2), false); | ||
done(); | ||
})); | ||
it('entrys are equal when next references and payloads are the same', async((done) => { | ||
const payload = 'hello world 1'; | ||
const entry1 = await(Entry.create(ipfs, payload)); | ||
const entry2 = await(Entry.create(ipfs, null, entry1)); | ||
const entry3 = await(Entry.create(ipfs, null, entry1)); | ||
assert.equal(Entry.equals(entry2, entry3), true); | ||
done(); | ||
})); | ||
it('entrys are not equal when next references are not the same', async((done) => { | ||
const payload1 = 'hello world 1'; | ||
const payload2 = 'hello world 2'; | ||
const entry1 = await(Entry.create(ipfs, payload1)); | ||
const entry2 = await(Entry.create(ipfs, payload2)); | ||
const entry3 = await(Entry.create(ipfs, null, entry1)); | ||
const entry4 = await(Entry.create(ipfs, null, entry2)); | ||
assert.equal(Entry.equals(entry3, entry4), false); | ||
done(); | ||
})); | ||
}); | ||
}); | ||
}); | ||
it('returns true if entries are not the same', async(() => { | ||
const payload1 = 'hello world1' | ||
const payload2 = 'hello world2' | ||
const entry1 = await(Entry.create(ipfs, payload1)) | ||
const entry2 = await(Entry.create(ipfs, payload2)) | ||
assert.equal(Entry.compare(entry1, entry2), false) | ||
})) | ||
}) | ||
}) | ||
}) |
@@ -1,6 +0,6 @@ | ||
'use strict'; | ||
'use strict' | ||
const IPFS = require('ipfs') | ||
const ipfsd = require('ipfsd-ctl'); | ||
const MemIpfs = require('./mem-ipfs'); | ||
const ipfsd = require('ipfsd-ctl') | ||
const MemIpfs = require('./mem-ipfs') | ||
@@ -19,6 +19,6 @@ const IpfsApis = [ | ||
return new Promise((resolve, reject) => { | ||
const ipfs = new IPFS(); | ||
// ipfs.goOnline(() => resolve(ipfs)); | ||
resolve(ipfs); | ||
}); | ||
const ipfs = new IPFS() | ||
// ipfs.goOnline(() => resolve(ipfs)) | ||
resolve(ipfs) | ||
}) | ||
}, | ||
@@ -34,14 +34,14 @@ stop: () => Promise.resolve() | ||
ipfsd.disposableApi((err, ipfs) => { | ||
if(err) console.error(err); | ||
resolve(ipfs); | ||
}); | ||
if(err) console.error(err) | ||
resolve(ipfs) | ||
}) | ||
// ipfsd.local((err, node) => { | ||
// if(err) reject(err); | ||
// ipfsDaemon = node; | ||
// if(err) reject(err) | ||
// ipfsDaemon = node | ||
// ipfsDaemon.startDaemon((err, ipfs) => { | ||
// if(err) reject(err); | ||
// resolve(ipfs); | ||
// }); | ||
// }); | ||
}); | ||
// if(err) reject(err) | ||
// resolve(ipfs) | ||
// }) | ||
// }) | ||
}) | ||
}, | ||
@@ -51,4 +51,4 @@ stop: () => Promise.resolve() | ||
} | ||
]; | ||
] | ||
module.exports = IpfsApis; | ||
module.exports = IpfsApis |
1147
test/log.spec.js
@@ -1,12 +0,12 @@ | ||
'use strict'; | ||
'use strict' | ||
const _ = require('lodash'); | ||
const assert = require('assert'); | ||
const async = require('asyncawait/async'); | ||
const await = require('asyncawait/await'); | ||
const IpfsApis = require('./ipfs-test-apis'); | ||
const Log = require('../src/log'); | ||
const Entry = require('../src/entry'); | ||
const _ = require('lodash') | ||
const assert = require('assert') | ||
const async = require('asyncawait/async') | ||
const await = require('asyncawait/await') | ||
const IpfsApis = require('./ipfs-test-apis') | ||
const Log = require('../src/log') | ||
const Entry = require('../src/entry') | ||
let ipfs, ipfsDaemon; | ||
let ipfs, ipfsDaemon | ||
@@ -16,658 +16,594 @@ IpfsApis.forEach(function(ipfsApi) { | ||
describe('Log with ' + ipfsApi.name, function() { | ||
this.timeout(40000); | ||
before(async((done) => { | ||
this.timeout(20000) | ||
before(async(() => { | ||
try { | ||
ipfs = await(ipfsApi.start()); | ||
ipfs = await(ipfsApi.start()) | ||
} catch(e) { | ||
console.log(e); | ||
assert.equal(e, null); | ||
console.log(e) | ||
assert.equal(e, null) | ||
} | ||
this.timeout(2000); | ||
done(); | ||
})); | ||
})) | ||
after(async((done) => { | ||
await(ipfsApi.stop()); | ||
done(); | ||
})); | ||
after(async(() => { | ||
await(ipfsApi.stop()) | ||
})) | ||
describe('create', async(() => { | ||
it('creates an empty log', () => { | ||
const log = new Log(ipfs, 'A'); | ||
assert.equal(log.id, 'A'); | ||
assert.equal(log._items instanceof Array, true); | ||
assert.equal(log._items.length, 0); | ||
assert.equal(log._currentBatch instanceof Array, true); | ||
assert.equal(log._currentBatch.length, 0); | ||
assert.equal(log._ipfs, ipfs); | ||
assert.equal(log.hash, null); | ||
}); | ||
const log = new Log(ipfs, 'A') | ||
assert.equal(log.id, 'A') | ||
assert.equal(log._items instanceof Array, true) | ||
assert.equal(log._items.length, 0) | ||
assert.equal(log._currentBatch instanceof Array, true) | ||
assert.equal(log._currentBatch.length, 0) | ||
assert.equal(log._ipfs, ipfs) | ||
assert.equal(log.hash, null) | ||
}) | ||
it('throws an error if ipfs is not defined', async((done) => { | ||
it('throws an error if ipfs is not defined', async(() => { | ||
try { | ||
const log = new Log(); | ||
const log = new Log() | ||
} catch(e) { | ||
assert.equal(e.message, 'Ipfs instance not defined'); | ||
assert.equal(e.message, 'Ipfs instance not defined') | ||
} | ||
done(); | ||
})); | ||
})) | ||
it('throws an error if id is not defined', async((done) => { | ||
it('throws an error if id is not defined', async(() => { | ||
try { | ||
const log = new Log(ipfs); | ||
const log = new Log(ipfs) | ||
} catch(e) { | ||
assert.equal(e.message, 'id is not defined'); | ||
assert.equal(e.message, 'id is not defined') | ||
} | ||
done(); | ||
})); | ||
})) | ||
it('default maxHistory is 256', async((done) => { | ||
const log = new Log(ipfs, 'A', 'db'); | ||
assert.equal(log.options.maxHistory, 256); | ||
done(); | ||
})); | ||
it('default maxHistory is 256', async(() => { | ||
const log = new Log(ipfs, 'A') | ||
assert.equal(log.options.maxHistory, 256) | ||
})) | ||
it('takes maxHistory as an option', async((done) => { | ||
const log = new Log(ipfs, 'A', 'db', { maxHistory: 100 }); | ||
assert.equal(log.options.maxHistory, 100); | ||
done(); | ||
})); | ||
it('takes maxHistory as an option', async(() => { | ||
const log = new Log(ipfs, 'A', { maxHistory: 100 }) | ||
assert.equal(log.options.maxHistory, 100) | ||
})) | ||
it('sets maxHistory if not provided in options', async((done) => { | ||
const log = new Log(ipfs, 'A', 'db'); | ||
assert.equal(log.options.maxHistory, 256); | ||
done(); | ||
})); | ||
it('sets maxHistory if not provided in options', async(() => { | ||
const log = new Log(ipfs, 'A') | ||
assert.equal(log.options.maxHistory, 256) | ||
})) | ||
it('sets maxHistory if other options are provided', async((done) => { | ||
const log = new Log(ipfs, 'A', 'db', { hello: "world" }); | ||
assert.equal(log.options.maxHistory, 256); | ||
done(); | ||
})); | ||
it('sets maxHistory if other options are provided', async(() => { | ||
const log = new Log(ipfs, 'A', { hello: "world" }) | ||
assert.equal(log.options.maxHistory, 256) | ||
})) | ||
})); | ||
})) | ||
describe('serialize', async(() => { | ||
let log; | ||
let log | ||
const expectedData = { | ||
id: "A", | ||
items: [ | ||
'QmRMUN4WJdpYydRLpbipaNoLQNXiw9ifRpPht5APaLFqrR', | ||
'Qmcpgub1qRG5XHed1qNciwb74uasUhQVEhP35oaZZ7UWbi', | ||
'QmQM4Xg6EGGGEKRYu3jX3cpTcXK53XvSgQpxZd2qGY1L2V' | ||
'QmUrqiypsLPAWN24Y3gHarmDTgvW97bTUiXnqN53ySXM9V', | ||
'QmTRF2oGMG7L5yP6LU1bpy2DEdLTzkRByS9nshRkMAFhBy', | ||
'QmQG1rPMjt1RQPQTu6cJfSMSS8GddcSw9GxLkVtRB32pMD' | ||
] | ||
}; | ||
} | ||
beforeEach(async((done) => { | ||
log = new Log(ipfs, 'A'); | ||
await(log.add("one")); | ||
await(log.add("two")); | ||
await(log.add("three")); | ||
done(); | ||
})); | ||
beforeEach(async(() => { | ||
log = new Log(ipfs, 'A') | ||
await(log.add("one")) | ||
await(log.add("two")) | ||
await(log.add("three")) | ||
})) | ||
describe('snapshot', async(() => { | ||
it('returns the current batch of items', async((done) => { | ||
assert.equal(JSON.stringify(log.snapshot), JSON.stringify(expectedData)); | ||
done(); | ||
})); | ||
})); | ||
it('returns the current batch of items', async(() => { | ||
assert.equal(JSON.stringify(log.snapshot), JSON.stringify(expectedData)) | ||
})) | ||
})) | ||
describe('getIpfsHash', async(() => { | ||
it('returns the log as ipfs hash', async((done) => { | ||
const expectedHash = 'QmaRz4njJX2W8QYwWLa1jhEbYUdJhhqibsBbnRYuWgr1r7'; | ||
const log = new Log(ipfs, 'A'); | ||
const hash = await(Log.getIpfsHash(ipfs, log)); | ||
assert.equal(hash, expectedHash); | ||
done(); | ||
})); | ||
it('returns the log as ipfs hash', async(() => { | ||
const expectedHash = 'QmaRz4njJX2W8QYwWLa1jhEbYUdJhhqibsBbnRYuWgr1r7' | ||
const log = new Log(ipfs, 'A') | ||
const hash = await(Log.getIpfsHash(ipfs, log)) | ||
assert.equal(hash, expectedHash) | ||
})) | ||
it('log serialized to ipfs contains the correct data', async((done) => { | ||
const expectedData = { id: "A", items: [] }; | ||
const log = new Log(ipfs, 'A'); | ||
const hash = await(Log.getIpfsHash(ipfs, log)); | ||
const res = await(ipfs.object.get(hash, { enc: 'base58' })); | ||
const result = JSON.parse(res.toJSON().Data); | ||
assert.equal(result.id, expectedData.id); | ||
assert.equal(result.items.length, expectedData.items.length); | ||
done(); | ||
})); | ||
it('log serialized to ipfs contains the correct data', async(() => { | ||
const expectedData = { id: "A", items: [] } | ||
const log = new Log(ipfs, 'A') | ||
const hash = await(Log.getIpfsHash(ipfs, log)) | ||
const res = await(ipfs.object.get(hash, { enc: 'base58' })) | ||
const result = JSON.parse(res.toJSON().Data) | ||
assert.equal(result.id, expectedData.id) | ||
assert.equal(result.items.length, expectedData.items.length) | ||
})) | ||
it('throws an error if ipfs is not defined', async((done) => { | ||
it('throws an error if ipfs is not defined', async(() => { | ||
try { | ||
const log = new Log(ipfs, 'A'); | ||
const hash = await(Log.getIpfsHash(null, log)); | ||
const log = new Log(ipfs, 'A') | ||
const hash = await(Log.getIpfsHash(null, log)) | ||
} catch(e) { | ||
assert.equal(e.message, 'Ipfs instance not defined'); | ||
assert.equal(e.message, 'Ipfs instance not defined') | ||
} | ||
done(); | ||
})); | ||
})); | ||
})) | ||
})) | ||
describe('fromIpfsHash', async(() => { | ||
it('creates an empty log from ipfs hash', async((done) => { | ||
const expectedData = { id: "A", items: [] }; | ||
const log = new Log(ipfs, 'A'); | ||
const hash = await(Log.getIpfsHash(ipfs, log)); | ||
const res = await(Log.fromIpfsHash(ipfs, hash)); | ||
assert.equal(JSON.stringify(res.snapshot), JSON.stringify(expectedData)); | ||
done(); | ||
})); | ||
it('creates an empty log from ipfs hash', async(() => { | ||
const expectedData = { id: "A", items: [] } | ||
const log = new Log(ipfs, 'A') | ||
const hash = await(Log.getIpfsHash(ipfs, log)) | ||
const res = await(Log.fromIpfsHash(ipfs, hash)) | ||
assert.equal(JSON.stringify(res.snapshot), JSON.stringify(expectedData)) | ||
})) | ||
it('creates a log from ipfs hash', async((done) => { | ||
const hash = await(Log.getIpfsHash(ipfs, log)); | ||
const res = await(Log.fromIpfsHash(ipfs, hash)); | ||
assert.equal(res.items.length, 3); | ||
assert.equal(res.items[0].hash, expectedData.items[0]); | ||
assert.equal(res.items[1].hash, expectedData.items[1]); | ||
assert.equal(res.items[2].hash, expectedData.items[2]); | ||
done(); | ||
})); | ||
it('creates a log from ipfs hash', async(() => { | ||
const hash = await(Log.getIpfsHash(ipfs, log)) | ||
const res = await(Log.fromIpfsHash(ipfs, hash)) | ||
assert.equal(res.items.length, 3) | ||
assert.equal(res.items[0].hash, expectedData.items[0]) | ||
assert.equal(res.items[1].hash, expectedData.items[1]) | ||
assert.equal(res.items[2].hash, expectedData.items[2]) | ||
})) | ||
it('throws an error when data from hash is not instance of Log', async((done) => { | ||
it('throws an error when data from hash is not instance of Log', async(() => { | ||
try { | ||
await(Log.fromIpfsHash(ipfs, 'QmRMUN4WJdpYydRLpbipaNoLQNXiw9ifRpPht5APaLFqrR')); | ||
await(Log.fromIpfsHash(ipfs, 'QmUrqiypsLPAWN24Y3gHarmDTgvW97bTUiXnqN53ySXM9V')) | ||
} catch(e) { | ||
assert.equal(e.message, 'Not a Log instance'); | ||
done(); | ||
assert.equal(e.message, 'Not a Log instance') | ||
} | ||
})); | ||
})) | ||
})); | ||
})); | ||
})) | ||
})) | ||
describe('items', () => { | ||
it('returns all entrys in the log', async((done) => { | ||
const log = new Log(ipfs, 'A'); | ||
let items = log.items; | ||
assert.equal(log.items instanceof Array, true); | ||
assert.equal(log.items.length, 0); | ||
await(log.add("hello1")); | ||
await(log.add("hello2")); | ||
await(log.add("hello3")); | ||
assert.equal(log.items instanceof Array, true); | ||
assert.equal(log.items.length, 3); | ||
assert.equal(log.items[0].payload, 'hello1'); | ||
assert.equal(log.items[1].payload, 'hello2'); | ||
assert.equal(log.items[2].payload, 'hello3'); | ||
assert.equal(log._items.length, 0); | ||
assert.equal(log._currentBatch.length, 3); | ||
done(); | ||
})); | ||
it('returns all entrys in the log', async(() => { | ||
const log = new Log(ipfs, 'A') | ||
let items = log.items | ||
assert.equal(log.items instanceof Array, true) | ||
assert.equal(log.items.length, 0) | ||
await(log.add("hello1")) | ||
await(log.add("hello2")) | ||
await(log.add("hello3")) | ||
assert.equal(log.items instanceof Array, true) | ||
assert.equal(log.items.length, 3) | ||
assert.equal(log.items[0].payload, 'hello1') | ||
assert.equal(log.items[1].payload, 'hello2') | ||
assert.equal(log.items[2].payload, 'hello3') | ||
assert.equal(log._items.length, 0) | ||
assert.equal(log._currentBatch.length, 3) | ||
})) | ||
it('returns all entrys from current batch and all known entrys', async((done) => { | ||
const log = new Log(ipfs, 'A'); | ||
let items = log.items; | ||
assert.equal(log.items instanceof Array, true); | ||
assert.equal(log.items.length, 0); | ||
await(log.add("hello1")); | ||
await(log.add("hello2")); | ||
log._commit(); | ||
await(log.add("hello3")); | ||
it('returns all entrys from current batch and all known entrys', async(() => { | ||
const log = new Log(ipfs, 'A') | ||
let items = log.items | ||
assert.equal(log.items instanceof Array, true) | ||
assert.equal(log.items.length, 0) | ||
await(log.add("hello1")) | ||
await(log.add("hello2")) | ||
log._commit() | ||
await(log.add("hello3")) | ||
assert.equal(log.items instanceof Array, true); | ||
assert.equal(log.items.length, 3); | ||
assert.equal(log.items[0].payload, 'hello1'); | ||
assert.equal(log.items[1].payload, 'hello2'); | ||
assert.equal(log.items[2].payload, 'hello3'); | ||
assert.equal(log._items.length, 2); | ||
assert.equal(log._currentBatch.length, 1); | ||
done(); | ||
})); | ||
}); | ||
assert.equal(log.items instanceof Array, true) | ||
assert.equal(log.items.length, 3) | ||
assert.equal(log.items[0].payload, 'hello1') | ||
assert.equal(log.items[1].payload, 'hello2') | ||
assert.equal(log.items[2].payload, 'hello3') | ||
assert.equal(log._items.length, 2) | ||
assert.equal(log._currentBatch.length, 1) | ||
})) | ||
}) | ||
describe('add', () => { | ||
it('adds an item to an empty log', async((done) => { | ||
const log = new Log(ipfs, 'A'); | ||
await(log.add("hello1")); | ||
const item = log.items[0]; | ||
assert.equal(log.items.length, 1); | ||
assert.equal(log._currentBatch.length, 1); | ||
assert.equal(log._items.length, 0); | ||
assert.equal(item, log._currentBatch[0]); | ||
assert.equal(item.payload, 'hello1'); | ||
done(); | ||
})); | ||
it('adds an item to an empty log', async(() => { | ||
const log = new Log(ipfs, 'A') | ||
await(log.add("hello1")) | ||
const item = log.items[0] | ||
assert.equal(log.items.length, 1) | ||
assert.equal(log._currentBatch.length, 1) | ||
assert.equal(log._items.length, 0) | ||
assert.equal(item, log._currentBatch[0]) | ||
assert.equal(item.payload, 'hello1') | ||
})) | ||
it('adds 100 items to a log', async((done) => { | ||
const log = new Log(ipfs, 'A'); | ||
const amount = 100; | ||
it('adds 100 items to a log', async(() => { | ||
const log = new Log(ipfs, 'A') | ||
const amount = 100 | ||
for(let i = 1; i <= amount; i ++) { | ||
await(log.add("hello" + i)); | ||
await(log.add("hello" + i)) | ||
} | ||
const last = _.last(log.items); | ||
assert.equal(log.items.length, amount); | ||
assert.equal(last.payload, 'hello' + amount); | ||
assert.notEqual(last.next.length, 0); | ||
const last = _.last(log.items) | ||
assert.equal(log.items.length, amount) | ||
assert.equal(last.payload, 'hello' + amount) | ||
assert.notEqual(last.next.length, 0) | ||
})) | ||
done(); | ||
})); | ||
it('commits the log after batch size was reached', async(() => { | ||
const log = new Log(ipfs, 'A') | ||
it('commits the log after batch size was reached', async((done) => { | ||
const log = new Log(ipfs, 'A'); | ||
for(let i = 1; i <= Log.batchSize; i ++) { | ||
await(log.add("hello" + i)); | ||
await(log.add("hello" + i)) | ||
} | ||
assert.equal(log.items.length, Log.batchSize); | ||
assert.equal(log._currentBatch.length, Log.batchSize); | ||
assert.equal(log._items.length, 0); | ||
assert.equal(log.items.length, Log.batchSize) | ||
assert.equal(log._currentBatch.length, Log.batchSize) | ||
assert.equal(log._items.length, 0) | ||
const item = _.last(log.items); | ||
assert.equal(log.items.length, Log.batchSize); | ||
assert.equal(item.payload, 'hello' + Log.batchSize); | ||
assert.notEqual(item.next.length, 0); | ||
const item = _.last(log.items) | ||
assert.equal(log.items.length, Log.batchSize) | ||
assert.equal(item.payload, 'hello' + Log.batchSize) | ||
assert.notEqual(item.next.length, 0) | ||
})) | ||
}) | ||
done(); | ||
})); | ||
it('adds an Entry to the log', async((done) => { | ||
const expectedHash = 'QmW94BLFbGNbaPjgGasX1rV9aYdE2qxnQnUBf9PbLkiBUo'; | ||
const payload1 = 'hello world'; | ||
const payload2 = 'hello again'; | ||
const entry1 = await(Entry.create(ipfs, payload1)); | ||
const entry2 = await(Entry.create(ipfs, payload2, entry1)); | ||
const log = new Log(ipfs, 'A'); | ||
await(log.add(entry1)); | ||
await(log.add(entry2)); | ||
assert.equal(log.items.length, 2); | ||
assert.equal(log.items[0] instanceof Entry, true); | ||
assert.equal(log.items[0].payload, payload1); | ||
assert.equal(log.items[1] instanceof Entry, true); | ||
assert.equal(log.items[1].payload, payload2); | ||
done(); | ||
})); | ||
}); | ||
describe('join', () => { | ||
let log1, log2, log3, log4; | ||
let log1, log2, log3, log4 | ||
beforeEach(async((done) => { | ||
log1 = new Log(ipfs, 'A'); | ||
log2 = new Log(ipfs, 'B'); | ||
log3 = new Log(ipfs, 'C'); | ||
log4 = new Log(ipfs, 'D'); | ||
done(); | ||
})); | ||
beforeEach(async(() => { | ||
log1 = new Log(ipfs, 'A') | ||
log2 = new Log(ipfs, 'B') | ||
log3 = new Log(ipfs, 'C') | ||
log4 = new Log(ipfs, 'D') | ||
})) | ||
it('throws an error if passed argument is not an instance of Log', async((done) => { | ||
it('throws an error if passed argument is not an instance of Log', async(() => { | ||
try { | ||
await(log1.join({})); | ||
await(log1.join({})) | ||
} catch(e) { | ||
assert.equal(e.message, 'The log to join must be an instance of Log'); | ||
done(); | ||
assert.equal(e.message, 'The log to join must be an instance of Log') | ||
} | ||
})); | ||
})) | ||
it('joins only unique items', async((done) => { | ||
await(log1.add("helloA1")); | ||
await(log1.add("helloA2")); | ||
await(log2.add("helloB1")); | ||
await(log2.add("helloB2")); | ||
await(log1.join(log2)); | ||
await(log1.join(log2)); | ||
it('joins only unique items', async(() => { | ||
await(log1.add("helloA1")) | ||
await(log1.add("helloA2")) | ||
await(log2.add("helloB1")) | ||
await(log2.add("helloB2")) | ||
await(log1.join(log2)) | ||
await(log1.join(log2)) | ||
assert.equal(log1.items.length, 4); | ||
assert.equal(log1.items[0].hash, 'QmUEH5SEuRZhZ7RETwEX2df2BtTR2xUYZR3qBrhjnxqocb'); | ||
assert.equal(log1.items[1].hash, 'Qma1PaYbyW1rZA4npPnuJzA3ov5Je4N9cvAn2p6Ju1iPQS'); | ||
assert.equal(log1.items[2].hash, 'QmdZwCR96sP61aaTbcLj9DXy9EaiMhTXLRrTxPSXpcZCct'); | ||
assert.equal(log1.items[3].hash, 'QmR3jTmVNQGfq4m6sDk2koFHFkSRMxJqSjrTHU293yyWMv'); | ||
assert.equal(log1.items.length, 4) | ||
assert.equal(log1.items[0].hash, 'QmQjjAwSt8qQQTQ52Kt3qMvS5squGiiEvrSRrkxYYMY3k2') | ||
assert.equal(log1.items[1].hash, 'QmYFAWWAPXkyQuND7P8Fm2aLgTH7eAA44wYX8EeaYVGom9') | ||
assert.equal(log1.items[2].hash, 'QmZDQ6FGJ1dAUogJK73Hm9TZmTe9GYx6VJYNgnh7C3cTD1') | ||
assert.equal(log1.items[3].hash, 'Qmcn8T7WfjLd73tUpRRwYGtKc2UwdAD5sCfWYRepsbWUo3') | ||
const last = _.last(log1.items); | ||
assert.equal(last.next.length, 1); | ||
assert.equal(last.next[0], 'QmdZwCR96sP61aaTbcLj9DXy9EaiMhTXLRrTxPSXpcZCct'); | ||
done(); | ||
})); | ||
const last = _.last(log1.items) | ||
assert.equal(last.next.length, 1) | ||
assert.equal(last.next[0], 'QmZDQ6FGJ1dAUogJK73Hm9TZmTe9GYx6VJYNgnh7C3cTD1') | ||
})) | ||
it('joins logs two ways', async((done) => { | ||
await(log1.add("helloA1")); | ||
await(log1.add("helloA2")); | ||
await(log2.add("helloB1")); | ||
await(log2.add("helloB2")); | ||
await(log1.join(log2)); | ||
await(log2.join(log1)); | ||
it('joins logs two ways', async(() => { | ||
await(log1.add("helloA1")) | ||
await(log1.add("helloA2")) | ||
await(log2.add("helloB1")) | ||
await(log2.add("helloB2")) | ||
await(log1.join(log2)) | ||
await(log2.join(log1)) | ||
const lastItem1 = _.last(log1.items); | ||
assert.equal(log1._currentBatch.length, 0); | ||
assert.equal(log1._items.length, 4); | ||
assert.equal(lastItem1.payload, 'helloB2'); | ||
const lastItem1 = _.last(log1.items) | ||
assert.equal(log1._currentBatch.length, 0) | ||
assert.equal(log1._items.length, 4) | ||
assert.equal(lastItem1.payload, 'helloB2') | ||
const lastItem2 = _.last(log2.items); | ||
assert.equal(log2._currentBatch.length, 0); | ||
assert.equal(log2._items.length, 4); | ||
assert.equal(lastItem2.payload, 'helloA2'); | ||
done(); | ||
})); | ||
const lastItem2 = _.last(log2.items) | ||
assert.equal(log2._currentBatch.length, 0) | ||
assert.equal(log2._items.length, 4) | ||
assert.equal(lastItem2.payload, 'helloA2') | ||
})) | ||
it('joins logs twice', async((done) => { | ||
await(log1.add("helloA1")); | ||
await(log2.add("helloB1")); | ||
await(log2.join(log1)); | ||
await(log1.add("helloA2")); | ||
await(log2.add("helloB2")); | ||
await(log2.join(log1)); | ||
it('joins logs twice', async(() => { | ||
await(log1.add("helloA1")) | ||
await(log2.add("helloB1")) | ||
await(log2.join(log1)) | ||
await(log1.add("helloA2")) | ||
await(log2.add("helloB2")) | ||
await(log2.join(log1)) | ||
const secondItem = log2.items[1]; | ||
const lastItem = _.last(log2.items); | ||
const secondItem = log2.items[1] | ||
const lastItem = _.last(log2.items) | ||
assert.equal(log2._currentBatch.length, 0); | ||
assert.equal(log2._items.length, 4); | ||
assert.equal(secondItem.payload, 'helloA1'); | ||
assert.equal(lastItem.payload, 'helloA2'); | ||
done(); | ||
})); | ||
assert.equal(log2._currentBatch.length, 0) | ||
assert.equal(log2._items.length, 4) | ||
assert.equal(secondItem.payload, 'helloA1') | ||
assert.equal(lastItem.payload, 'helloA2') | ||
})) | ||
it('joins 4 logs to one', async((done) => { | ||
await(log1.add("helloA1")); | ||
await(log1.add("helloA2")); | ||
await(log2.add("helloB1")); | ||
await(log2.add("helloB2")); | ||
await(log3.add("helloC1")); | ||
await(log3.add("helloC2")); | ||
await(log4.add("helloD1")); | ||
await(log4.add("helloD2")); | ||
await(log1.join(log2)); | ||
await(log1.join(log3)); | ||
await(log1.join(log4)); | ||
it('joins 4 logs to one', async(() => { | ||
await(log1.add("helloA1")) | ||
await(log1.add("helloA2")) | ||
await(log2.add("helloB1")) | ||
await(log2.add("helloB2")) | ||
await(log3.add("helloC1")) | ||
await(log3.add("helloC2")) | ||
await(log4.add("helloD1")) | ||
await(log4.add("helloD2")) | ||
await(log1.join(log2)) | ||
await(log1.join(log3)) | ||
await(log1.join(log4)) | ||
const secondItem = log1.items[1]; | ||
const lastItem = _.last(log1.items); | ||
const secondItem = log1.items[1] | ||
const lastItem = _.last(log1.items) | ||
assert.equal(log1._currentBatch.length, 0); | ||
assert.equal(log1._items.length, 8); | ||
assert.equal(secondItem.payload, 'helloA2'); | ||
assert.equal(lastItem.payload, 'helloD2'); | ||
done(); | ||
})); | ||
assert.equal(log1._currentBatch.length, 0) | ||
assert.equal(log1._items.length, 8) | ||
assert.equal(secondItem.payload, 'helloA2') | ||
assert.equal(lastItem.payload, 'helloD2') | ||
})) | ||
it('joins logs from 4 logs', async((done) => { | ||
await(log1.add("helloA1")); | ||
await(log1.join(log2)); | ||
await(log2.add("helloB1")); | ||
await(log2.join(log1)); | ||
await(log1.add("helloA2")); | ||
await(log2.add("helloB2")); | ||
await(log1.join(log3)); | ||
await(log3.join(log1)); | ||
await(log3.add("helloC1")); | ||
await(log4.add("helloD1")); | ||
await(log3.add("helloC2")); | ||
await(log4.add("helloD2")); | ||
await(log1.join(log3)); | ||
await(log1.join(log2)); | ||
await(log4.join(log2)); | ||
await(log4.join(log1)); | ||
await(log4.join(log3)); | ||
await(log4.add("helloD3")); | ||
await(log4.add("helloD4")); | ||
const secondItem = log4.items[1]; | ||
const lastItem1 = _.last(log4._items); | ||
const lastItem2 = _.last(log4.items); | ||
assert.equal(log4._currentBatch.length, 2); | ||
assert.equal(log4._items.length, 8); | ||
assert.equal(secondItem.payload, 'helloD2'); | ||
assert.equal(lastItem1.payload, 'helloC2'); | ||
assert.equal(lastItem2.payload, 'helloD4'); | ||
done(); | ||
})); | ||
it('joins logs from 4 logs', async(() => { | ||
await(log1.add("helloA1")) | ||
await(log1.join(log2)) | ||
await(log2.add("helloB1")) | ||
await(log2.join(log1)) | ||
await(log1.add("helloA2")) | ||
await(log2.add("helloB2")) | ||
await(log1.join(log3)) | ||
await(log3.join(log1)) | ||
await(log3.add("helloC1")) | ||
await(log4.add("helloD1")) | ||
await(log3.add("helloC2")) | ||
await(log4.add("helloD2")) | ||
await(log1.join(log3)) | ||
await(log1.join(log2)) | ||
await(log4.join(log2)) | ||
await(log4.join(log1)) | ||
await(log4.join(log3)) | ||
await(log4.add("helloD3")) | ||
await(log4.add("helloD4")) | ||
const secondItem = log4.items[1] | ||
const lastItem1 = _.last(log4._items) | ||
const lastItem2 = _.last(log4.items) | ||
assert.equal(log4._currentBatch.length, 2) | ||
assert.equal(log4._items.length, 8) | ||
assert.equal(secondItem.payload, 'helloD2') | ||
assert.equal(lastItem1.payload, 'helloC2') | ||
assert.equal(lastItem2.payload, 'helloD4') | ||
})) | ||
it('fetches items from history on join', async((done) => { | ||
const count = 32; | ||
it('fetches items from history on join', async(() => { | ||
const count = 32 | ||
for(let i = 1; i < count + 1; i ++) { | ||
await(log1.add("first " + i)); | ||
await(log2.add("second " + i)); | ||
await(log1.add("first " + i)) | ||
await(log2.add("second " + i)) | ||
} | ||
const hash1 = await(Log.getIpfsHash(ipfs, log1)); | ||
const hash2 = await(Log.getIpfsHash(ipfs, log2)); | ||
const hash1 = await(Log.getIpfsHash(ipfs, log1)) | ||
const hash2 = await(Log.getIpfsHash(ipfs, log2)) | ||
const other1 = await(Log.fromIpfsHash(ipfs, hash1)); | ||
const other2 = await(Log.fromIpfsHash(ipfs, hash2)); | ||
await(log3.join(other1)); | ||
const other1 = await(Log.fromIpfsHash(ipfs, hash1)) | ||
const other2 = await(Log.fromIpfsHash(ipfs, hash2)) | ||
await(log3.join(other1)) | ||
assert.equal(_.includes(log3.items.map((a) => a.hash), undefined), false); | ||
assert.equal(log3.items.length, count); | ||
assert.equal(log3.items[0].payload, "first 1"); | ||
assert.equal(_.last(log3.items).payload, "first " + count); | ||
assert.equal(_.includes(log3.items.map((a) => a.hash), undefined), false) | ||
assert.equal(log3.items.length, count) | ||
assert.equal(log3.items[0].payload, "first 1") | ||
assert.equal(_.last(log3.items).payload, "first " + count) | ||
await(log3.join(other2)); | ||
assert.equal(log3.items.length, count * 2); | ||
assert.equal(log3.items[0].payload, "second 1"); | ||
assert.equal(_.last(log3.items).payload, "second " + count); | ||
done(); | ||
})); | ||
await(log3.join(other2)) | ||
assert.equal(log3.items.length, count * 2) | ||
assert.equal(log3.items[0].payload, "second 1") | ||
assert.equal(_.last(log3.items).payload, "second " + count) | ||
})) | ||
it('orders fetched items correctly', async((done) => { | ||
const count = Log.batchSize * 3; | ||
it('orders fetched items correctly', async(() => { | ||
const count = Log.batchSize * 3 | ||
for(let i = 1; i < (count * 2) + 1; i ++) | ||
await(log1.add("first " + i)); | ||
await(log1.add("first " + i)) | ||
const hash1 = await(Log.getIpfsHash(ipfs, log1)); | ||
const other1 = await(Log.fromIpfsHash(ipfs, hash1)); | ||
await(log3.join(other1)); | ||
const hash1 = await(Log.getIpfsHash(ipfs, log1)) | ||
const other1 = await(Log.fromIpfsHash(ipfs, hash1)) | ||
await(log3.join(other1)) | ||
assert.equal(log3.items[0].payload, "first 1"); | ||
assert.equal(log3.items[log3.items.length - 1].payload, "first " + count * 2); | ||
assert.equal(log3.items.length, count * 2); | ||
assert.equal(log3.items[0].payload, "first 1") | ||
assert.equal(log3.items[log3.items.length - 1].payload, "first " + count * 2) | ||
assert.equal(log3.items.length, count * 2) | ||
// Second batch | ||
for(let i = 1; i < count + 1; i ++) | ||
await(log2.add("second " + i)); | ||
await(log2.add("second " + i)) | ||
const hash2 = await(Log.getIpfsHash(ipfs, log2)); | ||
const other2 = await(Log.fromIpfsHash(ipfs, hash2)); | ||
await(log3.join(other2)); | ||
const hash2 = await(Log.getIpfsHash(ipfs, log2)) | ||
const other2 = await(Log.fromIpfsHash(ipfs, hash2)) | ||
await(log3.join(other2)) | ||
assert.equal(log3.items.length, count + count * 2); | ||
assert.equal(log3.items[0].payload, "second 1"); | ||
assert.equal(log3.items[1].payload, "second 2"); | ||
assert.equal(_.last(log3.items).payload, "second " + count); | ||
done(); | ||
})); | ||
}); | ||
assert.equal(log3.items.length, count + count * 2) | ||
assert.equal(log3.items[0].payload, "second 1") | ||
assert.equal(log3.items[1].payload, "second 2") | ||
assert.equal(_.last(log3.items).payload, "second " + count) | ||
})) | ||
}) | ||
describe('_fetchRecursive', () => { | ||
it('returns two items when neither are in the log', async((done) => { | ||
const log1 = new Log(ipfs, 'A'); | ||
it('returns two items when neither are in the log', async(() => { | ||
const log1 = new Log(ipfs, 'A') | ||
const entry1 = await(Entry.create(ipfs, 'one')) | ||
const entry2 = await(Entry.create(ipfs, 'two', entry1)) | ||
const items = await(log1._fetchRecursive(ipfs, entry2.hash, [], 1000, 0)); | ||
assert.equal(items.length, 2); | ||
assert.equal(items[0].hash, 'QmRMUN4WJdpYydRLpbipaNoLQNXiw9ifRpPht5APaLFqrR'); | ||
assert.equal(items[1].hash, 'Qmcpgub1qRG5XHed1qNciwb74uasUhQVEhP35oaZZ7UWbi'); | ||
done(); | ||
})); | ||
const items = await(log1._fetchRecursive(ipfs, entry2.hash, [], 1000, 0)) | ||
assert.equal(items.length, 2) | ||
assert.equal(items[0].hash, 'QmUrqiypsLPAWN24Y3gHarmDTgvW97bTUiXnqN53ySXM9V') | ||
assert.equal(items[1].hash, 'QmTRF2oGMG7L5yP6LU1bpy2DEdLTzkRByS9nshRkMAFhBy') | ||
})) | ||
it('returns three items when none are in the log', async((done) => { | ||
const log1 = new Log(ipfs, 'A'); | ||
it('returns three items when none are in the log', async(() => { | ||
const log1 = new Log(ipfs, 'A') | ||
const entry1 = await(Entry.create(ipfs, 'one')) | ||
const entry2 = await(Entry.create(ipfs, 'two', entry1)) | ||
const entry3 = await(Entry.create(ipfs, 'three', entry2)) | ||
const items = await(log1._fetchRecursive(ipfs, entry3.hash, [], 1000, 0)); | ||
assert.equal(items.length, 3); | ||
assert.equal(items[0].hash, 'QmRMUN4WJdpYydRLpbipaNoLQNXiw9ifRpPht5APaLFqrR'); | ||
assert.equal(items[1].hash, 'Qmcpgub1qRG5XHed1qNciwb74uasUhQVEhP35oaZZ7UWbi'); | ||
assert.equal(items[2].hash, 'QmQM4Xg6EGGGEKRYu3jX3cpTcXK53XvSgQpxZd2qGY1L2V'); | ||
done(); | ||
})); | ||
const items = await(log1._fetchRecursive(ipfs, entry3.hash, [], 1000, 0)) | ||
assert.equal(items.length, 3) | ||
assert.equal(items[0].hash, 'QmUrqiypsLPAWN24Y3gHarmDTgvW97bTUiXnqN53ySXM9V') | ||
assert.equal(items[1].hash, 'QmTRF2oGMG7L5yP6LU1bpy2DEdLTzkRByS9nshRkMAFhBy') | ||
assert.equal(items[2].hash, 'QmQG1rPMjt1RQPQTu6cJfSMSS8GddcSw9GxLkVtRB32pMD') | ||
})) | ||
it('returns all items when none are in the log', async((done) => { | ||
const log1 = new Log(ipfs, 'A'); | ||
let entrys = []; | ||
const amount = Log.batchSize * 4; | ||
it('returns all items when none are in the log', async(() => { | ||
const log1 = new Log(ipfs, 'A') | ||
let entrys = [] | ||
const amount = Log.batchSize * 4 | ||
for(let i = 1; i <= amount; i ++) { | ||
const prev = _.last(entrys); | ||
const n = await(Entry.create(ipfs, 'entry' + i, prev ? prev : null)) | ||
entrys.push(n); | ||
const prev = _.last(entrys) | ||
const n = await(Entry.create(ipfs, 'entry' + i, prev)) | ||
entrys.push(n) | ||
} | ||
const items = await(log1._fetchRecursive(ipfs, _.last(entrys).hash, [], 1000, 0)); | ||
assert.equal(items.length, amount); | ||
assert.equal(items[0].hash, entrys[0].hash); | ||
assert.equal(_.last(items).hash, _.last(entrys).hash); | ||
done(); | ||
})); | ||
const items = await(log1._fetchRecursive(ipfs, _.last(entrys).hash, [], 1000, 0)) | ||
assert.equal(items.length, amount) | ||
assert.equal(items[0].hash, entrys[0].hash) | ||
assert.equal(_.last(items).hash, _.last(entrys).hash) | ||
})) | ||
it('returns only the items that are not in the log', async((done) => { | ||
const log1 = new Log(ipfs, 'A'); | ||
it('returns only the items that are not in the log', async(() => { | ||
const log1 = new Log(ipfs, 'A') | ||
const entry1 = await(log1.add('one')) | ||
const entry2 = await(Entry.create(ipfs, 'two', entry1)) | ||
const entry3 = await(Entry.create(ipfs, 'three', entry2)) | ||
const allHashes = log1.items.map((a) => a.hash); | ||
const items = await(log1._fetchRecursive(ipfs, entry3.hash, allHashes, 1000, 0)); | ||
assert.equal(items.length, 2); | ||
assert.equal(items[0].hash, 'Qmcpgub1qRG5XHed1qNciwb74uasUhQVEhP35oaZZ7UWbi'); | ||
assert.equal(items[1].hash, 'QmQM4Xg6EGGGEKRYu3jX3cpTcXK53XvSgQpxZd2qGY1L2V'); | ||
done(); | ||
})); | ||
}); | ||
const allHashes = log1.items.map((a) => a.hash) | ||
const items = await(log1._fetchRecursive(ipfs, entry3.hash, allHashes, 1000, 0)) | ||
assert.equal(items.length, 2) | ||
assert.equal(items[0].hash, 'QmTRF2oGMG7L5yP6LU1bpy2DEdLTzkRByS9nshRkMAFhBy') | ||
assert.equal(items[1].hash, 'QmQG1rPMjt1RQPQTu6cJfSMSS8GddcSw9GxLkVtRB32pMD') | ||
})) | ||
}) | ||
describe('findHeads', () => { | ||
it('finds one head after one item', async((done) => { | ||
const log1 = new Log(ipfs, 'A'); | ||
const log2 = new Log(ipfs, 'B'); | ||
const log3 = new Log(ipfs, 'C'); | ||
it('finds one head after one item', async(() => { | ||
const log1 = new Log(ipfs, 'A') | ||
const log2 = new Log(ipfs, 'B') | ||
const log3 = new Log(ipfs, 'C') | ||
await(log1.add("helloA1")); | ||
await(log1.add("helloA1")) | ||
const heads = log1._heads; | ||
assert.equal(heads.length, 1); | ||
assert.equal(heads[0], 'QmUEH5SEuRZhZ7RETwEX2df2BtTR2xUYZR3qBrhjnxqocb'); | ||
done(); | ||
})); | ||
const heads = log1._heads | ||
assert.equal(heads.length, 1) | ||
assert.equal(heads[0], 'QmQjjAwSt8qQQTQ52Kt3qMvS5squGiiEvrSRrkxYYMY3k2') | ||
})) | ||
it('finds one head after two items', async((done) => { | ||
const log1 = new Log(ipfs, 'A'); | ||
const log2 = new Log(ipfs, 'B'); | ||
const log3 = new Log(ipfs, 'C'); | ||
it('finds one head after two items', async(() => { | ||
const log1 = new Log(ipfs, 'A') | ||
const log2 = new Log(ipfs, 'B') | ||
const log3 = new Log(ipfs, 'C') | ||
await(log1.add("helloA1")); | ||
await(log1.add("helloA2")); | ||
await(log1.add("helloA1")) | ||
await(log1.add("helloA2")) | ||
const heads = log1._heads; | ||
assert.equal(heads.length, 1); | ||
assert.equal(heads[0], 'Qma1PaYbyW1rZA4npPnuJzA3ov5Je4N9cvAn2p6Ju1iPQS'); | ||
done(); | ||
})); | ||
const heads = log1._heads | ||
assert.equal(heads.length, 1) | ||
assert.equal(heads[0], 'QmYFAWWAPXkyQuND7P8Fm2aLgTH7eAA44wYX8EeaYVGom9') | ||
})) | ||
it('finds two heads after a join', async((done) => { | ||
const log1 = new Log(ipfs, 'A'); | ||
const log2 = new Log(ipfs, 'B'); | ||
it('finds two heads after a join', async(() => { | ||
const log1 = new Log(ipfs, 'A') | ||
const log2 = new Log(ipfs, 'B') | ||
await(log1.add("helloA1")); | ||
const expectedHead1 = await(log1.add("helloA2")); | ||
await(log1.add("helloA1")) | ||
const expectedHead1 = await(log1.add("helloA2")) | ||
await(log2.add("helloB1")); | ||
const expectedHead2 = await(log2.add("helloB2")); | ||
await(log2.add("helloB1")) | ||
const expectedHead2 = await(log2.add("helloB2")) | ||
await(log1.join(log2)); | ||
await(log1.join(log2)) | ||
const heads = log1._heads; | ||
assert.equal(heads.length, 2); | ||
assert.equal(heads[0], expectedHead2.hash); | ||
assert.equal(heads[1], expectedHead1.hash); | ||
done(); | ||
})); | ||
const heads = log1._heads | ||
assert.equal(heads.length, 2) | ||
assert.equal(heads[0], expectedHead2.hash) | ||
assert.equal(heads[1], expectedHead1.hash) | ||
})) | ||
it('finds one head after two joins', async((done) => { | ||
const log1 = new Log(ipfs, 'A'); | ||
const log2 = new Log(ipfs, 'B'); | ||
it('finds one head after two joins', async(() => { | ||
const log1 = new Log(ipfs, 'A') | ||
const log2 = new Log(ipfs, 'B') | ||
await(log1.add("helloA1")); | ||
await(log1.add("helloA2")); | ||
await(log2.add("helloB1")); | ||
await(log2.add("helloB2")); | ||
await(log1.join(log2)); | ||
await(log1.add("helloA3")); | ||
const expectedHead = await(log1.add("helloA4")); | ||
await(log1.join(log2)); | ||
await(log1.add("helloA1")) | ||
await(log1.add("helloA2")) | ||
await(log2.add("helloB1")) | ||
await(log2.add("helloB2")) | ||
await(log1.join(log2)) | ||
await(log1.add("helloA3")) | ||
const expectedHead = await(log1.add("helloA4")) | ||
await(log1.join(log2)) | ||
const heads = log1._heads; | ||
assert.equal(heads.length, 1); | ||
assert.equal(heads[0], expectedHead.hash); | ||
done(); | ||
})); | ||
const heads = log1._heads | ||
assert.equal(heads.length, 1) | ||
assert.equal(heads[0], expectedHead.hash) | ||
})) | ||
it('finds two heads after three joins', async((done) => { | ||
const log1 = new Log(ipfs, 'A'); | ||
const log2 = new Log(ipfs, 'B'); | ||
const log3 = new Log(ipfs, 'C'); | ||
it('finds two heads after three joins', async(() => { | ||
const log1 = new Log(ipfs, 'A') | ||
const log2 = new Log(ipfs, 'B') | ||
const log3 = new Log(ipfs, 'C') | ||
await(log1.add("helloA1")); | ||
await(log1.add("helloA2")); | ||
await(log2.add("helloB1")); | ||
await(log2.add("helloB2")); | ||
await(log1.join(log2)); | ||
await(log1.add("helloA3")); | ||
const expectedHead1 = await(log1.add("helloA4")); | ||
await(log3.add("helloC1")); | ||
await(log3.add("helloC2")); | ||
await(log2.join(log3)); | ||
const expectedHead2 = await(log2.add("helloB3")); | ||
await(log1.join(log2)); | ||
await(log1.add("helloA1")) | ||
await(log1.add("helloA2")) | ||
await(log2.add("helloB1")) | ||
await(log2.add("helloB2")) | ||
await(log1.join(log2)) | ||
await(log1.add("helloA3")) | ||
const expectedHead1 = await(log1.add("helloA4")) | ||
await(log3.add("helloC1")) | ||
await(log3.add("helloC2")) | ||
await(log2.join(log3)) | ||
const expectedHead2 = await(log2.add("helloB3")) | ||
await(log1.join(log2)) | ||
const heads = log1._heads; | ||
assert.equal(heads.length, 2); | ||
assert.equal(heads[0], expectedHead2.hash); | ||
assert.equal(heads[1], expectedHead1.hash); | ||
done(); | ||
})); | ||
const heads = log1._heads | ||
assert.equal(heads.length, 2) | ||
assert.equal(heads[0], expectedHead2.hash) | ||
assert.equal(heads[1], expectedHead1.hash) | ||
})) | ||
it('finds three heads after three joins', async((done) => { | ||
const log1 = new Log(ipfs, 'A'); | ||
const log2 = new Log(ipfs, 'B'); | ||
const log3 = new Log(ipfs, 'C'); | ||
it('finds three heads after three joins', async(() => { | ||
const log1 = new Log(ipfs, 'A') | ||
const log2 = new Log(ipfs, 'B') | ||
const log3 = new Log(ipfs, 'C') | ||
await(log1.add("helloA1")); | ||
await(log1.add("helloA2")); | ||
await(log2.add("helloB1")); | ||
await(log2.add("helloB2")); | ||
await(log1.join(log2)); | ||
await(log1.add("helloA3")); | ||
const expectedHead1 = await(log1.add("helloA4")); | ||
await(log3.add("helloC1")); | ||
const expectedHead2 = await(log2.add("helloB3")); | ||
const expectedHead3 = await(log3.add("helloC2")); | ||
await(log1.join(log2)); | ||
await(log1.join(log3)); | ||
await(log1.add("helloA1")) | ||
await(log1.add("helloA2")) | ||
await(log2.add("helloB1")) | ||
await(log2.add("helloB2")) | ||
await(log1.join(log2)) | ||
await(log1.add("helloA3")) | ||
const expectedHead1 = await(log1.add("helloA4")) | ||
await(log3.add("helloC1")) | ||
const expectedHead2 = await(log2.add("helloB3")) | ||
const expectedHead3 = await(log3.add("helloC2")) | ||
await(log1.join(log2)) | ||
await(log1.join(log3)) | ||
const heads = log1._heads; | ||
assert.equal(heads.length, 3); | ||
assert.equal(heads[0], expectedHead3.hash); | ||
assert.equal(heads[1], expectedHead2.hash); | ||
assert.equal(heads[2], expectedHead1.hash); | ||
done(); | ||
})); | ||
}); | ||
const heads = log1._heads | ||
assert.equal(heads.length, 3) | ||
assert.equal(heads[0], expectedHead3.hash) | ||
assert.equal(heads[1], expectedHead2.hash) | ||
assert.equal(heads[2], expectedHead1.hash) | ||
})) | ||
}) | ||
describe('isReferencedInChain', () => { | ||
it('returns true if another entry in the log references the given entry', async((done) => { | ||
const log = new Log(ipfs, 'A'); | ||
const entry1 = await(log.add('one')); | ||
const entry2 = await(log.add('two')); | ||
const res = Log.isReferencedInChain(log, entry1); | ||
it('returns true if another entry in the log references the given entry', async(() => { | ||
const log = new Log(ipfs, 'A') | ||
const entry1 = await(log.add('one')) | ||
const entry2 = await(log.add('two')) | ||
const res = Log.isReferencedInChain(log, entry1) | ||
assert.equal(res, true) | ||
done(); | ||
})); | ||
})) | ||
it('returns false if no other entry in the log references the given entry', async((done) => { | ||
const log = new Log(ipfs, 'A'); | ||
const entry1 = await(log.add('one')); | ||
const entry2 = await(log.add('two')); | ||
const res = Log.isReferencedInChain(log, entry2); | ||
it('returns false if no other entry in the log references the given entry', async(() => { | ||
const log = new Log(ipfs, 'A') | ||
const entry1 = await(log.add('one')) | ||
const entry2 = await(log.add('two')) | ||
const res = Log.isReferencedInChain(log, entry2) | ||
assert.equal(res, false) | ||
done(); | ||
})); | ||
}); | ||
})) | ||
}) | ||
describe('_commit', () => { | ||
it('moves entrys from current batch to all known entrys', async((done) => { | ||
const log = new Log(ipfs, 'A'); | ||
const entry1 = await(log.add('one')); | ||
const entry2 = await(log.add('two')); | ||
it('moves entrys from current batch to all known entrys', async(() => { | ||
const log = new Log(ipfs, 'A') | ||
const entry1 = await(log.add('one')) | ||
const entry2 = await(log.add('two')) | ||
@@ -677,17 +613,16 @@ assert.equal(log._items.length, 0) | ||
log._commit(); | ||
log._commit() | ||
assert.equal(log._items.length, 2) | ||
assert.equal(log._currentBatch.length, 0) | ||
done(); | ||
})); | ||
}); | ||
})) | ||
}) | ||
describe('_insert', () => { | ||
it('insert entry to the log before current batch if parent is in current bathc', async((done) => { | ||
const log = new Log(ipfs, 'A'); | ||
const entry1 = await(log.add('one')); | ||
const entry2 = await(log.add('two')); | ||
it('insert entry to the log before current batch if parent is in current bathc', async(() => { | ||
const log = new Log(ipfs, 'A') | ||
const entry1 = await(log.add('one')) | ||
const entry2 = await(log.add('two')) | ||
const entry3 = await(Entry.create(ipfs, 'three', entry1)) | ||
log._insert(entry3); | ||
log._insert(entry3) | ||
assert.equal(log.items.length, 3) | ||
@@ -697,80 +632,76 @@ assert.equal(log.items[0].payload, 'three') | ||
assert.equal(log._items[0].payload, 'three') | ||
done(); | ||
})); | ||
})) | ||
it('insert to the log after the parent when parent is not in the current batch', async((done) => { | ||
const log = new Log(ipfs, 'A'); | ||
const entry1 = await(log.add('one')); | ||
const entry2 = await(log.add('two')); | ||
it('insert to the log after the parent when parent is not in the current batch', async(() => { | ||
const log = new Log(ipfs, 'A') | ||
const entry1 = await(log.add('one')) | ||
const entry2 = await(log.add('two')) | ||
const entry3 = await(Entry.create(ipfs, 'three', entry1)) | ||
log._commit(); | ||
log._insert(entry3); | ||
log._commit() | ||
log._insert(entry3) | ||
assert.equal(log.items.length, 3) | ||
assert.equal(log.items[1].payload, 'three') | ||
done(); | ||
})); | ||
}); | ||
})) | ||
}) | ||
describe('is a CRDT', () => { | ||
let log1, log2, log3; | ||
let log1, log2, log3 | ||
beforeEach(async((done) => { | ||
log1 = new Log(ipfs, 'A'); | ||
log2 = new Log(ipfs, 'B'); | ||
log3 = new Log(ipfs, 'C'); | ||
done(); | ||
})); | ||
beforeEach(async(() => { | ||
log1 = new Log(ipfs, 'A') | ||
log2 = new Log(ipfs, 'B') | ||
log3 = new Log(ipfs, 'C') | ||
})) | ||
it('join is associative', async((done) => { | ||
await(log1.add("helloA1")); | ||
await(log1.add("helloA2")); | ||
await(log2.add("helloB1")); | ||
await(log2.add("helloB2")); | ||
await(log3.add("helloC1")); | ||
await(log3.add("helloC2")); | ||
it('join is associative', async(() => { | ||
await(log1.add("helloA1")) | ||
await(log1.add("helloA2")) | ||
await(log2.add("helloB1")) | ||
await(log2.add("helloB2")) | ||
await(log3.add("helloC1")) | ||
await(log3.add("helloC2")) | ||
// a + (b + c) | ||
await(log2.join(log3)); | ||
await(log1.join(log2)); | ||
await(log2.join(log3)) | ||
await(log1.join(log2)) | ||
const res1 = log1.items.map((e) => e.hash).join(","); | ||
const res1 = log1.items.map((e) => e.hash).join(",") | ||
log1 = new Log(ipfs, 'A'); | ||
log2 = new Log(ipfs, 'B'); | ||
log3 = new Log(ipfs, 'C'); | ||
await(log1.add("helloA1")); | ||
await(log1.add("helloA2")); | ||
await(log2.add("helloB1")); | ||
await(log2.add("helloB2")); | ||
await(log3.add("helloC1")); | ||
await(log3.add("helloC2")); | ||
log1 = new Log(ipfs, 'A') | ||
log2 = new Log(ipfs, 'B') | ||
log3 = new Log(ipfs, 'C') | ||
await(log1.add("helloA1")) | ||
await(log1.add("helloA2")) | ||
await(log2.add("helloB1")) | ||
await(log2.add("helloB2")) | ||
await(log3.add("helloC1")) | ||
await(log3.add("helloC2")) | ||
// (a + b) + c | ||
await(log1.join(log2)); | ||
await(log1.join(log3)); | ||
await(log1.join(log2)) | ||
await(log1.join(log3)) | ||
const res2 = log1.items.map((e) => e.hash).join(","); | ||
const res2 = log1.items.map((e) => e.hash).join(",") | ||
// associativity: a + (b + c) == (a + b) + c | ||
const len = (46 + 1) * 6- 1; // 46 == ipfs hash, +1 == .join(","), * 4 == number of items, -1 == last item doesn't get a ',' from .join | ||
const len = (46 + 1) * 6- 1 // 46 == ipfs hash, +1 == .join(","), * 4 == number of items, -1 == last item doesn't get a ',' from .join | ||
assert.equal(res1.length, len) | ||
assert.equal(res2.length, len) | ||
assert.equal(res1, res2); | ||
done(); | ||
})); | ||
assert.equal(res1, res2) | ||
})) | ||
it('join is commutative', async((done) => { | ||
await(log1.add("helloA1")); | ||
await(log1.add("helloA2")); | ||
await(log2.join(log1)); | ||
await(log2.add("helloB1")); | ||
await(log2.add("helloB2")); | ||
it('join is commutative', async(() => { | ||
await(log1.add("helloA1")) | ||
await(log1.add("helloA2")) | ||
await(log2.join(log1)) | ||
await(log2.add("helloB1")) | ||
await(log2.add("helloB2")) | ||
// b + a | ||
await(log2.join(log1)); | ||
await(log2.join(log1)) | ||
const res1 = log2.items.map((e) => e.hash).join(","); | ||
const res1 = log2.items.map((e) => e.hash).join(",") | ||
log1 = new Log(ipfs, 'A'); | ||
log2 = new Log(ipfs, 'B'); | ||
log1 = new Log(ipfs, 'A') | ||
log2 = new Log(ipfs, 'B') | ||
await(log1.add("helloA1")) | ||
@@ -783,33 +714,31 @@ await(log1.add("helloA2")) | ||
// a + b | ||
await(log1.join(log2)); | ||
await(log1.join(log2)) | ||
const res2 = log1.items.map((e) => e.hash).join(","); | ||
const res2 = log1.items.map((e) => e.hash).join(",") | ||
// commutativity: a + (b + c) == (a + b) + c | ||
const len = (46 + 1) * 4 - 1; // 46 == ipfs hash length, +1 == .join(","), * 4 == number of items, -1 == last item doesn't get a ',' from .join | ||
// commutativity: a + b + c == b + a + c | ||
const len = (46 + 1) * 4 - 1 // 46 == ipfs hash length, +1 == .join(","), * 4 == number of items, -1 == last item doesn't get a ',' from .join | ||
assert.equal(res1.length, len) | ||
assert.equal(res2.length, len) | ||
assert.equal(res1, res2); | ||
done(); | ||
})); | ||
assert.equal(res1, res2) | ||
})) | ||
it('join is idempotent', async((done) => { | ||
await(log1.add("helloA1")); | ||
await(log1.add("helloA2")); | ||
await(log1.add("helloA3")); | ||
await(log2.add("helloA1")); | ||
await(log2.add("helloA2")); | ||
await(log2.add("helloA3")); | ||
it('join is idempotent', async(() => { | ||
await(log1.add("helloA1")) | ||
await(log1.add("helloA2")) | ||
await(log1.add("helloA3")) | ||
await(log2.add("helloA1")) | ||
await(log2.add("helloA2")) | ||
await(log2.add("helloA3")) | ||
// idempotence: a + a = a | ||
await(log1.join(log2)); | ||
await(log1.join(log2)) | ||
assert.equal(log1.id, 'A'); | ||
assert.equal(log1.items.length, 3); | ||
done(); | ||
})); | ||
}); | ||
}); | ||
assert.equal(log1.id, 'A') | ||
assert.equal(log1.items.length, 3) | ||
})) | ||
}) | ||
}) | ||
}); | ||
}) |
@@ -1,2 +0,2 @@ | ||
const webpack = require('webpack'); | ||
const webpack = require('webpack') | ||
@@ -17,3 +17,3 @@ module.exports = { | ||
new webpack.optimize.UglifyJsPlugin({ | ||
mangle: true, | ||
mangle: false, | ||
compress: { warnings: false } | ||
@@ -49,2 +49,2 @@ }) | ||
} | ||
}; | ||
} |
@@ -1,3 +0,3 @@ | ||
const webpack = require('webpack'); | ||
const path = require('path'); | ||
const webpack = require('webpack') | ||
const path = require('path') | ||
@@ -16,3 +16,3 @@ module.exports = { | ||
new webpack.optimize.UglifyJsPlugin({ | ||
mangle: true, | ||
mangle: false, | ||
compress: { warnings: false } | ||
@@ -55,2 +55,2 @@ }) | ||
} | ||
}; | ||
} |
Sorry, the diff of this file is too big to display
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
252046
5
299
20
1943
+ Addedlodash.take@^4.1.1
+ Addedlodash.take@4.1.1(transitive)
- Removedbuffer@^4.5.1
- Removedlazy.js@^0.4.2
- Removedbase64-js@1.5.1(transitive)
- Removedbuffer@4.9.2(transitive)
- Removedieee754@1.2.1(transitive)
- Removedisarray@1.0.0(transitive)
- Removedlazy.js@0.4.3(transitive)