Merge remote-tracking branch 'origin/reafctor/ipfs-log'

This commit is contained in:
haad 2016-03-21 10:32:52 +01:00
commit 98b2bd933b
11 changed files with 21 additions and 1909 deletions

View File

@ -14,6 +14,7 @@
"main": "src/Client.js",
"dependencies": {
"asyncawait": "^1.0.1",
"ipfs-log": "^1.0.1",
"lazy.js": "^0.4.2",
"lodash": "^4.3.0",
"orbit-common": "^0.1.0",

View File

@ -21,7 +21,7 @@ class Client {
if(password === undefined) password = '';
if(subscribe === undefined) subscribe = true;
this.db.use(channel, this.user, password);
await(this.db.use(channel, this.user, password));
this.db.events[channel].on('write', this._onWrite.bind(this));
this.db.events[channel].on('sync', this._onSync.bind(this));

View File

@ -4,7 +4,7 @@ const Lazy = require('lazy.js');
const EventEmitter = require('events').EventEmitter;
const async = require('asyncawait/async');
const await = require('asyncawait/await');
const OrbitList = require('./list/OrbitList');
const Log = require('ipfs-log');
const DBOperation = require('./db/Operation');
const Post = require('./post/Post');
@ -18,7 +18,7 @@ class OrbitDB {
/* Public methods */
use(channel, user, password) {
this.user = user;
this._logs[channel] = new OrbitList(this._ipfs, this.user.username);
this._logs[channel] = await(Log.create(this._ipfs, this.user.username));
this.events[channel] = new EventEmitter();
}
@ -26,7 +26,7 @@ class OrbitDB {
// console.log("--> Head:", hash)
if(hash && this._logs[channel]) {
const oldCount = this._logs[channel].items.length;
const other = await(OrbitList.fromIpfsHash(this._ipfs, hash));
const other = await(Log.fromIpfsHash(this._ipfs, hash));
await(this._logs[channel].join(other));
// Only emit the event if something was added
@ -102,7 +102,7 @@ class OrbitDB {
// Find an items from the sequence (list of operations)
return sequence
.map((f) => await(f.fetchPayload())) // IO - fetch the actual OP from ipfs. consider merging with LL.
.map((f) => await(OrbitDB.fetchPayload(this._ipfs, f.payload))) // IO - fetch the actual OP from ipfs. consider merging with LL.
.skipWhile((f) => key && f.key !== key) // Drop elements until we have the first one requested
.map(_createLWWSet) // Return items as LWW (ignore values after the first found)
.compact() // Remove nulls
@ -113,33 +113,26 @@ class OrbitDB {
// Write an op to the db
_write(channel, password, operation, key, value) {
const hash = await(DBOperation.create(this._ipfs, this._logs[channel], this.user, operation, key, value));
const listHash = await(this._logs[channel].ipfsHash);
const listHash = await(Log.getIpfsHash(this._ipfs, this._logs[channel]));
this.events[channel].emit('write', channel, listHash);
return hash;
}
static fetchPayload(ipfs, hash) {
return new Promise((resolve, reject) => {
ipfs.object.get(hash)
.then((payload) => {
let data = JSON.parse(payload.Data);
Object.assign(data, { hash: hash });
if(data.key === null)
Object.assign(data, { key: hash });
resolve(data);
})
.catch(reject);
});
}
}
// TODO: move to where this is needed
// static fetchPayload(hash) {
// return new Promise(async((resolve, reject) => {
// if(hash) {
// this._ipfs.object.get(hash)
// .then((payload) => {
// this.Payload = JSON.parse(payload.Data);
// console.log(this.Payload, hash, this.hash)
// let res = this.Payload;
// Object.assign(res, { hash: hash });
// if(this.Payload.key === null)
// Object.assign(res, { key: hash });
// console.log(this.Payload)
// console.log(this)
// resolve(res);
// })
// .catch(reject);
// } else {
// resolve(this.Payload);
// }
// }));
// }
module.exports = OrbitDB;

View File

@ -1,114 +0,0 @@
'use strict';
const _ = require('lodash');
const Lazy = require('lazy.js');
const Node = require('./Node');
class List {
constructor(id, seq, ver, items) {
this.id = id;
// this.seq = seq || 0;
// this.ver = ver || 0;
this._items = items || [];
this._currentBatch = [];
}
/* Methods */
add(data) {
const heads = List.findHeads(this.items);
const node = new Node(this.id, -1, -1, data, heads);
this._currentBatch.push(node);
// this.ver ++;
}
join(other) {
// this.seq = (other.seq && other.seq > this.seq ? other.seq : this.seq) + 1;
// this.ver = 0;
// TODO: figure out how to do real equality check with Lazy.js
// const current = Lazy(this._currentBatch).difference(this._items);
// const others = Lazy(other.items).difference(current.toA);
// const final = current.union(others).uniq((f) => f.compactId);
const current = _.differenceWith(this._currentBatch, this._items, Node.equals);
const others = _.differenceWith(other.items, this._items, Node.equals);
const final = _.unionWith(current, others, Node.equals);
this._items = Lazy(this._items).concat(final).toArray();
this._currentBatch = [];
}
clear() {
this._items = [];
this._currentBatch = [];
// this.seq = 0;
// this.ver = 0;
}
/* Private methods */
_commit() {
const current = Lazy(this._currentBatch).difference(this._items).toArray();
this._items = this._items.concat(current);
this._currentBatch = [];
// this.ver = 0;
// this.seq ++;
}
/* Properties */
get items() {
return this._items.concat(this._currentBatch);
}
get compactId() {
// return "" + this.id + "." + this.seq + "." + this.ver;
return "" + this.id;
}
get asJson() {
return {
id: this.id,
// seq: this.seq,
// ver: this.ver,
items: this._currentBatch.map((f) => f.asJson)
};
}
/* Static methods */
static fromJson(json) {
let list = new List(json.id);
// list.seq = json.seq;
// list.ver = json.ver;
list._items = Lazy(json.items)
// .map((f) => new Node(f.id, f.seq, f.ver, f.data, f.next))
.map((f) => new Node(f.id, -1, -1, f.data, f.next))
.unique()
.toArray();
return list;
}
static findHeads(list) {
return Lazy(list)
.reverse()
.indexBy((f) => f.id)
.pairs()
.map((f) => f[1])
.filter((f) => !List.isReferencedInChain(list, f))
.toArray();
}
static isReferencedInChain(all, item) {
return Lazy(all).reverse().find((e) => e.hasChild(item)) !== undefined;
}
printTree() {
const printCurrent = (f, padding) => {
padding += " |";
let s = padding + "-" + f.compactId + " '" + f.data + "'\n";
if(f.next.length > 0) {
s += f.next.map((e) => printCurrent(e, padding)).join("");
}
return s;
}
console.log(" LIST\n" + this.items.reverse().map((e) => printCurrent(e, "")).join(""));
}
}
module.exports = List;

View File

@ -1,44 +0,0 @@
'use strict';
class Node {
constructor(id, seq, ver, data, next) {
this.id = id;
this.seq = seq;
this.ver = ver;
this.data = data || null;
// this.next = next ? next.map((f) => f.compactId ? f.compactId : f) : [];
this.next = next ? next.map((f) => f.id ? f.id : f) : [];
}
hasChild(a) {
// const id = a.compactId;
const id = a.id;
for(let i = 0; i < this.next.length; i++) {
if(this.next[i] === id)
return true;
}
return false;
}
get compactId() {
return "" + this.id;
// return "" + this.id + "." + this.seq + "." + this.ver;
}
get heads() {
// return Object.keys(this.next).map((e) => this.next[e]);
return this.next;
}
get asJson() {
// return { id: this.id, seq: this.seq, ver: this.ver, data: this.data, next: this.next };
return { id: this.id, data: this.data, next: this.next };
}
static equals(a, b) {
// return a.id === b.id && a.seq === b.seq && a.ver === b.ver;
return a.id === b.id;
}
}
module.exports = Node;

View File

@ -1,178 +0,0 @@
'use strict';
const _ = require('lodash');
const Lazy = require('lazy.js');
const async = require('asyncawait/async');
const await = require('asyncawait/await');
const OrbitNode = require('./OrbitNode');
const MaxBatchSize = 10; // How many items per sequence. Saves a snapshot to ipfs in batches of this many items.
const MaxHistory = 256; // How many items to fetch in the chain per join
class OrbitList {
constructor(ipfs, id, seq, ver, items) {
this.id = id;
this._ipfs = ipfs;
this._items = items || [];
this._currentBatch = [];
}
add(data) {
if(this._currentBatch.length >= MaxBatchSize)
this._commit();
const heads = OrbitList.findHeads(this.items);
const node = new OrbitNode(this._ipfs, this.id, data, heads);
this._currentBatch.push(node);
}
join(other) {
const current = _.differenceWith(this._currentBatch, this._items, OrbitNode.equals);
const others = _.differenceWith(other.items, this._items, OrbitNode.equals);
const final = _.unionWith(current, others, OrbitNode.equals);
this._items = Lazy(this._items).concat(final).toArray();
this._currentBatch = [];
const history = _.flatten(await(this._fetchHistory(other.items)));
history.forEach((f) => this._insert(f)) // Insert to the list
}
clear() {
this._items = [];
this._currentBatch = [];
}
/* Private methods */
_fetchHistory(items) {
var _fetchAsync = async(() => {
return new Promise(async((resolve, reject) => {
let allHashes = this._items.map((a) => a.hash);
const handle = Lazy(items)
.reverse() // Start from the latest item
.map((f) => f.next).flatten() // Go through all heads
.filter((f) => !(f instanceof OrbitNode === true)) // OrbitNode vs. {}, filter out instances (we already have them in mem)
.async() // Do the next map asynchronously
.map(async((f) => _.flatten(await(this._fetchRecursive(f, allHashes, MaxHistory, 0))))) // IO - fetch items recursively
.take(MaxHistory) // How many items from the history we should fetch
.toArray();
// console.log("--> Fetched", res.length, "items from the history");
handle.onComplete(resolve);
}));
})
return await(_fetchAsync());
}
// Fetch items in the linked list recursively
_fetchRecursive(hash, all, amount, depth) {
var _fetchAsync = async(() => {
return new Promise(async((resolve, reject) => {
const isReferenced = (list, item) => Lazy(list).reverse().find((f) => f === item) !== undefined;
let result = [];
if(depth >= amount) {
resolve(result)
return;
}
if(isReferenced(all, hash)) {
resolve(result);
return;
}
const item = await(OrbitNode.fromIpfsHash(this._ipfs, hash)); // IO - get from IPFS
result.push(item);
all.push(hash);
depth ++;
const handle = Lazy(item.next)
.async()
.map(async((f) => _.flatten(await(this._fetchRecursive(f, all, amount, depth)))))
.toArray()
handle.onComplete((res) => {
result = result.concat(res);
resolve(result);
});
}));
})
return await(_fetchAsync());
}
// Insert to the list right after the latest parent
_insert(item) {
let indices = Lazy(item.next).map((next) => Lazy(this._items).map((f) => f.hash).indexOf(next)) // Find the item's parent's indices
const index = indices.length > 0 ? Math.max(indices.max() + 1, 0) : 0; // find the largest index (latest parent)
this._items.splice(index, 0, item);
return item;
}
_commit() {
const current = Lazy(this._currentBatch).difference(this._items).toArray();
this._items = this._items.concat(current);
this._currentBatch = [];
}
/* Properties */
get items() {
return this._items.concat(this._currentBatch);
}
get ipfsHash() {
const toIpfs = async(() => {
return new Promise(async((resolve, reject) => {
var data = await(this.asJson)
const list = await(this._ipfs.object.put(new Buffer(JSON.stringify({ Data: JSON.stringify(data) }))));
resolve(list.Hash);
}));
});
this.hash = await(toIpfs());
return this.hash;
}
get asJson() {
return {
id: this.id,
items: this._currentBatch.map((f) => await(f.ipfsHash))
}
}
static findHeads(list) {
return Lazy(list)
.reverse()
.indexBy((f) => f.id)
.pairs()
.map((f) => f[1])
.filter((f) => !OrbitList.isReferencedInChain(list, f))
.toArray();
}
static isReferencedInChain(all, item) {
return Lazy(all).reverse().find((e) => e.hasChild(item)) !== undefined;
}
static fromIpfsHash(ipfs, hash) {
const fromIpfs = async(() => {
return new Promise(async((resolve, reject) => {
const l = await(ipfs.object.get(hash));
const list = OrbitList.fromJson(ipfs, JSON.parse(l.Data));
resolve(list);
}));
});
return await(fromIpfs());
}
static fromJson(ipfs, json) {
const items = json.items.map((f) => await(OrbitNode.fromIpfsHash(ipfs, f)));
return new OrbitList(ipfs, json.id, -1, -1, items);
}
static get batchSize() {
return MaxBatchSize;
}
static get maxHistory() {
return MaxHistory;
}
}
module.exports = OrbitList;

View File

@ -1,74 +0,0 @@
'use strict';
const async = require('asyncawait/async');
const await = require('asyncawait/await');
class OrbitNode {
constructor(ipfs, id, data, next, hash) {
this._ipfs = ipfs;
this.id = id;
this.data = data || null;
this.next = next || [];
this.hash = hash ? hash : this.ipfsHash;
}
fetchPayload() {
return new Promise(async((resolve, reject) => {
if(!this.Payload) {
this._ipfs.object.get(this.data)
.then((payload) => {
this.Payload = JSON.parse(payload.Data);
let res = this.Payload;
Object.assign(res, { hash: this.data });
if(this.Payload.key === null)
Object.assign(res, { key: this.data });
resolve(res);
})
.catch(reject);
} else {
resolve(this.Payload);
}
}));
}
hasChild(a) {
const id = a;
for(let i = 0; i < this.next.length; i++) {
if(this.next[i] === id)
return true;
}
return false;
}
get ipfsHash() {
if(!this.hash) {
const r = await(this._ipfs.object.put(new Buffer(JSON.stringify({ Data: JSON.stringify(this.asJson) }))));
this.hash = r.Hash;
}
return this.hash;
}
get asJson() {
let res = { id: this.id, data: this.data }
let items = this.next.map((f) => f.hash);
Object.assign(res, { next: items });
return res;
}
static fromIpfsHash(ipfs, hash) {
return new Promise(async((resolve, reject) => {
ipfs.object.get(hash)
.then((obj) => {
const f = JSON.parse(obj.Data)
const node = new OrbitNode(ipfs, f.id, f.data, f.next, hash)
resolve(node);
}).catch(reject);
}));
}
static equals(a, b) {
return a.hash === b.hash;
}
}
module.exports = OrbitNode;

View File

@ -1,67 +0,0 @@
// 'use strict';
// const assert = require('assert');
// const List = require('../src/list/List');
// const Node = require('../src/list/Node');
// describe('Node', () => {
// describe('Constructor', () => {
// it('initializes member variables', (done) => {
// const node = new Node('A', 0, 0, 'hello', []);
// assert.equal(node.id, 'A');
// assert.equal(node.seq, 0);
// assert.equal(node.ver, 0);
// assert.equal(node.data, 'hello');
// assert.equal(node.next instanceof Array, true);
// done();
// });
// it('initializes member variables without specified data and next', (done) => {
// const node = new Node('A', 0, 0);
// assert.equal(node.id, 'A');
// assert.equal(node.seq, 0);
// assert.equal(node.ver, 0);
// assert.equal(node.data, null);
// assert.equal(node.next instanceof Array, true);
// done();
// });
// });
// describe('compactId', () => {
// it('presents the node as a string with id, sequence and version', (done) => {
// const node1 = new Node('A', 0, 0);
// const node2 = new Node('B', 123, 456);
// assert.equal(node1.compactId, 'A.0.0');
// assert.equal(node2.compactId, 'B.123.456');
// done();
// });
// });
// describe('equals', () => {
// it('check if nodes are equal', (done) => {
// const node1 = new Node('A', 0, 0);
// const node2 = new Node('A', 0, 0);
// const node3 = new Node('A', 1, 1);
// const node4 = new Node('B', 123, 456);
// assert.equal(Node.equals(node1, node1), true);
// assert.equal(Node.equals(node1, node2), true);
// assert.equal(Node.equals(node1, node3), false);
// assert.equal(Node.equals(node1, node4), false);
// assert.equal(Node.equals(node3, node4), false);
// done();
// });
// });
// describe('hasChild', () => {
// it('finds the child reference', (done) => {
// const a = new Node('A', 0, 0);
// const b = new Node('B', 0, 0, "hello", [a]);
// const c = new Node('C', 0, 0, "hello", [a, b]);
// assert.equal(b.hasChild(a), true)
// assert.equal(c.hasChild(a), true)
// assert.equal(c.hasChild(b), true)
// done();
// });
// });
// });

View File

@ -1,513 +0,0 @@
// 'use strict';
// const _ = require('lodash');
// var assert = require('assert');
// var List = require('../src/list/List');
// var Node = require('../src/list/Node');
// describe('List', () => {
// describe('Constructor', () => {
// it('initializes member variables', (done) => {
// const list = new List('A');
// assert.equal(list.id, 'A');
// assert.equal(list.seq, 0);
// assert.equal(list.ver, 0);
// assert.equal(list._items instanceof Array, true);
// assert.equal(list._currentBatch instanceof Array, true);
// assert.equal(list._items.length, 0);
// assert.equal(list._currentBatch.length, 0);
// done();
// });
// });
// describe('fromJson', () => {
// it('creates a list from parsed json', (done) => {
// const list = new List('A');
// list.add("hello1")
// list.add("hello2")
// list.add("hello3")
// const str = JSON.stringify(list.asJson, null, 2)
// const res = List.fromJson(JSON.parse(str));
// assert.equal(res.id, 'A');
// assert.equal(res.seq, 0);
// assert.equal(res.ver, 3);
// assert.equal(res.items.length, 3);
// assert.equal(res.items[0].compactId, 'A.0.0');
// assert.equal(res.items[1].compactId, 'A.0.1');
// assert.equal(res.items[2].compactId, 'A.0.2');
// done();
// });
// });
// describe('asJson', () => {
// it('presents the list as json', (done) => {
// const list = new List('A');
// list.add("hello1")
// list.add("hello2")
// list.add("hello3")
// const expected = {
// id: 'A',
// seq: 0,
// ver: 3,
// items: [
// { id: 'A', seq: 0, ver: 0, data: 'hello1', next: [] },
// { id: 'A', seq: 0, ver: 1, data: 'hello2', next: ['A.0.0'] },
// { id: 'A', seq: 0, ver: 2, data: 'hello3', next: ['A.0.1'] }
// ]
// };
// assert.equal(_.isEqual(list.asJson, expected), true);
// done();
// });
// });
// describe('items', () => {
// it('returns items', (done) => {
// const list = new List('A');
// let items = list.items;
// assert.equal(list.items instanceof Array, true);
// assert.equal(list.items.length, 0);
// list.add("hello1")
// list.add("hello2")
// assert.equal(list.items instanceof Array, true);
// assert.equal(list.items.length, 2);
// assert.equal(list.items[0].data, 'hello1');
// assert.equal(list.items[1].data, 'hello2');
// done();
// });
// });
// describe('add', () => {
// it('adds an item to an empty list', (done) => {
// const list = new List('A');
// list.add("hello1")
// const item = list.items[0];
// assert.equal(list.id, 'A');
// assert.equal(list.seq, 0);
// assert.equal(list.ver, 1);
// assert.equal(list.items.length, 1);
// assert.equal(list._currentBatch.length, 1);
// assert.equal(list._items.length, 0);
// assert.equal(item, list._currentBatch[0]);
// assert.equal(item.id, 'A');
// assert.equal(item.seq, 0);
// assert.equal(item.ver, 0);
// assert.equal(item.data, 'hello1');
// done();
// });
// it('adds 100 items to a list', (done) => {
// const list = new List('A');
// for(let i = 1; i < 101; i ++) {
// list.add("hello" + i);
// }
// assert.equal(list.id, 'A');
// assert.equal(list.seq, 0);
// assert.equal(list.ver, 100);
// assert.equal(list.items.length, 100);
// assert.equal(list._currentBatch.length, 100);
// assert.equal(list._items.length, 0);
// const item = list.items[list.items.length - 1];
// assert.equal(item, list._currentBatch[list._currentBatch.length - 1]);
// assert.equal(item.id, 'A');
// assert.equal(item.seq, 0);
// assert.equal(item.ver, 99);
// assert.equal(item.data, 'hello100');
// assert.equal(item.next, 'A.0.98');
// done();
// });
// });
// describe('join', () => {
// it('increases the sequence and resets the version if other list has the same or higher sequence', (done) => {
// const list1 = new List('A');
// const list2 = new List('B');
// list2.seq = 7;
// list1.add("helloA1")
// assert.equal(list1.id, 'A');
// assert.equal(list1.seq, 0);
// assert.equal(list1.ver, 1);
// list2.add("helloB1")
// list1.join(list2);
// assert.equal(list1.id, 'A');
// assert.equal(list1.seq, 8);
// assert.equal(list1.ver, 0);
// done();
// });
// it('increases the sequence by one if other list has lower sequence', (done) => {
// const list1 = new List('A');
// const list2 = new List('B');
// list1.seq = 4;
// list2.seq = 1;
// list2.add("helloB1")
// list1.join(list2);
// assert.equal(list1.id, 'A');
// assert.equal(list1.seq, 5);
// assert.equal(list1.ver, 0);
// done();
// });
// it('finds the next head when adding a new element', (done) => {
// const list1 = new List('A');
// list1.add("helloA1")
// list1.add("helloA2")
// list1.add("helloA3")
// assert.equal(list1._currentBatch.length, 3);
// assert.equal(list1._currentBatch[2].next.length, 1);
// assert.equal(list1._currentBatch[2].next[0], 'A.0.1');
// done();
// });
// it('finds the next heads (two) after a join', (done) => {
// const list1 = new List('A');
// const list2 = new List('B');
// list1.add("helloA1")
// list2.add("helloB1")
// list2.add("helloB2")
// list1.join(list2);
// list1.add("helloA2")
// assert.equal(list1._currentBatch.length, 1);
// assert.equal(list1._currentBatch[0].next.length, 2);
// assert.equal(list1._currentBatch[0].next[1], 'A.0.0');
// assert.equal(list1._currentBatch[0].next[0], 'B.0.1');
// done();
// });
// it('finds the next head (one) after a join', (done) => {
// const list1 = new List('A');
// const list2 = new List('B');
// list1.add("helloA1")
// list2.add("helloB1")
// list2.add("helloB2")
// list1.join(list2);
// list1.add("helloA2")
// list1.add("helloA3")
// assert.equal(list1._currentBatch.length, 2);
// assert.equal(list1._currentBatch[1].next.length, 1);
// assert.equal(list1._currentBatch[1].next[0], 'A.1.0');
// done();
// });
// it('finds the next heads after two joins', (done) => {
// const list1 = new List('A');
// const list2 = new List('B');
// list1.add("helloA1")
// list1.add("helloA2")
// list2.add("helloB1")
// list2.add("helloB2")
// list1.join(list2);
// list1.add("helloA3")
// list1.join(list2);
// list1.add("helloA4")
// const lastItem = list1.items[list1.items.length - 1];
// assert.equal(list1.items.length, 6);
// assert.equal(lastItem.next.length, 1);
// assert.equal(lastItem.next[0], 'A.1.0');
// done();
// });
// it('finds the next heads after multiple joins', (done) => {
// const list1 = new List('A');
// const list2 = new List('B');
// const list3 = new List('C');
// const list4 = new List('D');
// list1.add("helloA1")
// list1.add("helloA2")
// list2.add("helloB1")
// list2.add("helloB2")
// list1.join(list2);
// list3.add("helloC1")
// list4.add("helloD1")
// list1.join(list3);
// list1.add("helloA3")
// list2.join(list1);
// list1.join(list2);
// list2.join(list4);
// list4.add("helloD2")
// list4.add("helloD3")
// list1.add("helloA4")
// list1.join(list4);
// list1.add("helloA5")
// const lastItem = list1.items[list1.items.length - 1];
// assert.equal(lastItem.next[1], 'A.4.0');
// assert.equal(lastItem.next[0], 'D.0.2');
// assert.equal(list1.items.length, 11);
// assert.equal(lastItem.next.length, 2);
// done();
// });
// it('joins list of one item with list of two items', (done) => {
// const list1 = new List('A');
// const list2 = new List('B');
// list1.add("helloA1")
// list2.add("helloB1")
// list2.add("helloB2")
// list1.join(list2);
// const firstItem = list1.items[0];
// assert.equal(list1.id, 'A');
// assert.equal(list1.seq, 1);
// assert.equal(list1.ver, 0);
// assert.equal(list1._currentBatch.length, 0);
// assert.equal(list1._items.length, 3);
// assert.equal(firstItem.id, 'A');
// assert.equal(firstItem.seq, 0);
// assert.equal(firstItem.ver, 0);
// assert.equal(firstItem.data, 'helloA1');
// done();
// });
// it('joins lists two ways', (done) => {
// const list1 = new List('A');
// const list2 = new List('B');
// list1.add("helloA1")
// list1.add("helloA2")
// list2.add("helloB1")
// list2.add("helloB2")
// list1.join(list2);
// list2.join(list1);
// const lastItem1 = list1.items[list1.items.length - 1];
// assert.equal(list1.id, 'A');
// assert.equal(list1.seq, 1);
// assert.equal(list1.ver, 0);
// assert.equal(list1._currentBatch.length, 0);
// assert.equal(list1._items.length, 4);
// assert.equal(lastItem1.id, 'B');
// assert.equal(lastItem1.seq, 0);
// assert.equal(lastItem1.ver, 1);
// assert.equal(lastItem1.data, 'helloB2');
// const lastItem2 = list2.items[list2.items.length - 1];
// assert.equal(list2.id, 'B');
// assert.equal(list2.seq, 2);
// assert.equal(list2.ver, 0);
// assert.equal(list2._currentBatch.length, 0);
// assert.equal(list2._items.length, 4);
// assert.equal(lastItem2.id, 'A');
// assert.equal(lastItem2.seq, 0);
// assert.equal(lastItem2.ver, 1);
// assert.equal(lastItem2.data, 'helloA2');
// done();
// });
// it('joins lists twice', (done) => {
// const list1 = new List('A');
// const list2 = new List('B');
// list1.add("helloA1")
// list2.add("helloB1")
// list2.join(list1);
// list1.add("helloA2")
// list2.add("helloB2")
// list2.join(list1);
// const secondItem = list2.items[list2.items.length - 2];
// const lastItem = list2.items[list2.items.length - 1];
// assert.equal(list2.id, 'B');
// assert.equal(list2.seq, 2);
// assert.equal(list2.ver, 0);
// assert.equal(list2._currentBatch.length, 0);
// assert.equal(list2._items.length, 4);
// assert.equal(secondItem.id, 'B');
// assert.equal(secondItem.seq, 1);
// assert.equal(secondItem.ver, 0);
// assert.equal(secondItem.data, 'helloB2');
// assert.equal(lastItem.id, 'A');
// assert.equal(lastItem.seq, 0);
// assert.equal(lastItem.ver, 1);
// assert.equal(lastItem.data, 'helloA2');
// done();
// });
// it('joins 4 lists to one', (done) => {
// const list1 = new List('A');
// const list2 = new List('B');
// const list3 = new List('C');
// const list4 = new List('D');
// list1.add("helloA1")
// list2.add("helloB1")
// list1.add("helloA2")
// list2.add("helloB2")
// list3.add("helloC1")
// list4.add("helloD1")
// list3.add("helloC2")
// list4.add("helloD2")
// list1.join(list2);
// list1.join(list3);
// list1.join(list4);
// const secondItem = list1.items[list1.items.length - 2];
// const lastItem = list1.items[list1.items.length - 1];
// assert.equal(list1.id, 'A');
// assert.equal(list1.seq, 3);
// assert.equal(list1.ver, 0);
// assert.equal(list1._currentBatch.length, 0);
// assert.equal(list1._items.length, 8);
// assert.equal(secondItem.id, 'D');
// assert.equal(secondItem.seq, 0);
// assert.equal(secondItem.ver, 0);
// assert.equal(secondItem.data, 'helloD1');
// assert.equal(lastItem.id, 'D');
// assert.equal(lastItem.seq, 0);
// assert.equal(lastItem.ver, 1);
// assert.equal(lastItem.data, 'helloD2');
// done();
// });
// it('joins lists from 4 lists', (done) => {
// const list1 = new List('A');
// const list2 = new List('B');
// const list3 = new List('C');
// const list4 = new List('D');
// list1.add("helloA1")
// list1.join(list2);
// list2.add("helloB1")
// list2.join(list1);
// list1.add("helloA2")
// list2.add("helloB2")
// list1.join(list3);
// list3.join(list1);
// list3.add("helloC1")
// list4.add("helloD1")
// list3.add("helloC2")
// list4.add("helloD2")
// list1.join(list3);
// list1.join(list2);
// list4.join(list2);
// list4.join(list1);
// list4.join(list3);
// list4.add("helloD3")
// list4.add("helloD4")
// const secondItem = list4.items[1];
// const lastItem1 = list4._items[list4._items.length - 1];
// const lastItem2 = list4.items[list4.items.length - 1];
// assert.equal(list4.id, 'D');
// assert.equal(list4.seq, 7);
// assert.equal(list4.ver, 2);
// assert.equal(list4._currentBatch.length, 2);
// assert.equal(list4._items.length, 8);
// assert.equal(secondItem.id, 'D');
// assert.equal(secondItem.seq, 0);
// assert.equal(secondItem.ver, 1);
// assert.equal(secondItem.data, 'helloD2');
// assert.equal(lastItem1.id, 'C');
// assert.equal(lastItem1.seq, 3);
// assert.equal(lastItem1.ver, 1);
// assert.equal(lastItem1.data, 'helloC2');
// assert.equal(lastItem2.id, 'D');
// assert.equal(lastItem2.seq, 7);
// assert.equal(lastItem2.ver, 1);
// assert.equal(lastItem2.data, 'helloD4');
// done();
// });
// it('data is consistent after joining lists', (done) => {
// const list1 = new List('A');
// const list2 = new List('B');
// const list3 = new List('C');
// list1.add("helloA1")
// list2.add("helloB1")
// list3.add("helloC1")
// list1.join(list2);
// list2.join(list1);
// list1.add("helloA2")
// list1.add("helloA3")
// list2.add("helloB2")
// list2.add("helloB3")
// list1.join(list2);
// list2.join(list1);
// list1.add("helloA4")
// list1.add("helloA5")
// list2.add("helloB4")
// list2.add("helloB5")
// list3.add("helloC2")
// list3.add("helloC3")
// list1.add("helloA6")
// list2.join(list1);
// list1.join(list2);
// list1.add("helloA7")
// list1.add("helloA8")
// list2.join(list1);
// list1.join(list2);
// list2.add("helloB6")
// list2.add("helloB7")
// list2.add("helloB8")
// list1.join(list2);
// list2.join(list1);
// list1.join(list3);
// list2.join(list3);
// list3.join(list2);
// list3.join(list1);
// assert.equal(list1._items.length, 19);
// assert.equal(list1.items.map((e) => e.compactId).join(", "), "A.0.0, B.0.0, A.1.0, A.1.1, B.2.0, B.2.1, A.3.0, A.3.1, A.3.2, B.4.0, B.4.1, A.6.0, A.6.1, B.7.0, B.7.1, B.7.2, C.0.0, C.0.1, C.0.2")
// assert.equal(list2.items.map((e) => e.compactId).join(", "), "B.0.0, A.0.0, B.2.0, B.2.1, A.1.0, A.1.1, B.4.0, B.4.1, A.3.0, A.3.1, A.3.2, A.6.0, A.6.1, B.7.0, B.7.1, B.7.2, C.0.0, C.0.1, C.0.2")
// assert.equal(list3.items.map((e) => e.compactId).join(", "), "C.0.0, C.0.1, C.0.2, B.0.0, A.0.0, B.2.0, B.2.1, A.1.0, A.1.1, B.4.0, B.4.1, A.3.0, A.3.1, A.3.2, A.6.0, A.6.1, B.7.0, B.7.1, B.7.2")
// done();
// });
// });
// describe('_findHeads', () => {
// it('TODO', (done) => {
// done();
// });
// });
// describe('_isReferencedInChain', () => {
// it('TODO', (done) => {
// done();
// });
// });
// });

View File

@ -1,794 +0,0 @@
'use strict';
const _ = require('lodash');
const async = require('asyncawait/async');
const await = require('asyncawait/await');
const assert = require('assert');
const ipfsDaemon = require('orbit-common/lib/ipfs-daemon');
const ipfsAPI = require('orbit-common/lib/ipfs-api-promised');
const List = require('../src/list/OrbitList');
const List2 = require('../src/list/List');
const Node = require('../src/list/OrbitNode');
const startIpfs = async (() => {
return new Promise(async((resolve, reject) => {
const ipfsd = await(ipfsDaemon());
resolve(ipfsd.ipfs);
}));
});
let ipfs;
describe('OrbitList', async(function() {
this.timeout(5000);
before(async((done) => {
try {
ipfs = await(startIpfs());
} catch(e) {
assert.equals(e, null);
}
done();
}));
describe('Constructor', async(() => {
it('initializes member variables', async((done) => {
const list = new List(ipfs, 'A');
assert.equal(list.id, 'A');
// assert.equal(list.seq, 0);
// assert.equal(list.ver, 0);
assert.equal(list._items instanceof Array, true);
assert.equal(list._currentBatch instanceof Array, true);
assert.equal(list._items.length, 0);
assert.equal(list._currentBatch.length, 0);
assert.equal(list._ipfs, ipfs);
assert.equal(list.hash, null);
done();
}));
}));
describe('ipfsHash', async(() => {
it('returns the list as ipfs hash', async((done) => {
const list = new List(ipfs, 'A');
const hash = list.ipfsHash;
assert.equal(hash.startsWith('Qm'), true);
done();
}));
it('saves the list to ipfs', async((done) => {
const list = new List(ipfs, 'A');
const hash = list.ipfsHash;
const l = await(ipfsAPI.getObject(ipfs, hash));
assert.equal(l.toString(), ({ Links: [], Data: '{"id":"A","seq":0,"ver":0,"items":[]}' }).toString());
done();
}));
}));
describe('fromIpfsHash', () => {
it('creates a list from ipfs hash', async((done) => {
const list = new List(ipfs, 'A');
list.add("hello1")
list.add("hello2")
list.add("hello3")
const hash = list.ipfsHash;
const res = List.fromIpfsHash(ipfs, hash);
assert.equal(res.id, 'A');
// assert.equal(res.seq, 0);
// assert.equal(res.ver, 3);
assert.equal(res.items.length, 3);
// assert.equal(res.items[0].compactId, 'A.0.0');
assert.equal(res.items[0].hash, 'QmQgLATYR4Af3oCVaQ8LHumvkwtjHfZ3Tumz8RfKMxHZfo');
// assert.equal(res.items[1].compactId, 'A.0.1');
assert.equal(res.items[1].hash, 'QmTLP2MccfhZGjXHZgLL1euh2d2PkEhLf7G1pGR2Hdg4h6');
// assert.equal(res.items[2].compactId, 'A.0.2');
assert.equal(res.items[2].hash, 'QmUX8qTWAqumSxzviV4YvWyWT727pqgZX6XdViWZdoL8fC');
done();
}));
});
describe('serialize', async(() => {
let list;
const expected = {
id: "A",
items: [
"QmQgLATYR4Af3oCVaQ8LHumvkwtjHfZ3Tumz8RfKMxHZfo",
"QmTLP2MccfhZGjXHZgLL1euh2d2PkEhLf7G1pGR2Hdg4h6",
"QmUX8qTWAqumSxzviV4YvWyWT727pqgZX6XdViWZdoL8fC"
]
};
before(async((done) => {
list = new List(ipfs, 'A');
list.add("hello1")
list.add("hello2")
list.add("hello3")
done();
}));
describe('asJson', async(() => {
it('presents the list as json', async((done) => {
assert.equal(JSON.stringify(list.asJson), JSON.stringify(expected));
done();
}));
}));
describe('fromJson', () => {
it('creates a list from parsed json', async((done) => {
const str = JSON.stringify(list.asJson, null, 2)
const res = List.fromJson(ipfs, JSON.parse(str));
assert.equal(res.id, 'A');
// assert.equal(res.seq, 0);
// assert.equal(res.ver, 3);
assert.equal(res.items.length, 3);
assert.equal(res.items[0].hash, 'QmQgLATYR4Af3oCVaQ8LHumvkwtjHfZ3Tumz8RfKMxHZfo');
assert.equal(res.items[1].hash, 'QmTLP2MccfhZGjXHZgLL1euh2d2PkEhLf7G1pGR2Hdg4h6');
assert.equal(res.items[2].hash, 'QmUX8qTWAqumSxzviV4YvWyWT727pqgZX6XdViWZdoL8fC');
done();
}));
});
}));
describe('items', () => {
it('returns items', async((done) => {
const list = new List(ipfs, 'A');
let items = list.items;
assert.equal(list.items instanceof Array, true);
assert.equal(list.items.length, 0);
list.add("hello1")
list.add("hello2")
assert.equal(list.items instanceof Array, true);
assert.equal(list.items.length, 2);
assert.equal(list.items[0].data, 'hello1');
assert.equal(list.items[1].data, 'hello2');
done();
}));
});
describe('add', () => {
it('adds an item to an empty list', async((done) => {
const list = new List(ipfs, 'A');
list.add("hello1")
const item = list.items[0];
assert.equal(list.id, 'A');
// assert.equal(list.seq, 0);
// assert.equal(list.ver, 1);
assert.equal(list.items.length, 1);
assert.equal(list._currentBatch.length, 1);
assert.equal(list._items.length, 0);
assert.equal(item, list._currentBatch[0]);
assert.equal(item.id, 'A');
// assert.equal(item.seq, 0);
// assert.equal(item.ver, 0);
assert.equal(item.data, 'hello1');
done();
}));
it('adds 100 items to a list', async((done) => {
const list = new List(ipfs, 'A');
const amount = 100;
for(let i = 1; i <= amount; i ++) {
list.add("hello" + i);
}
assert.equal(list.id, 'A');
assert.equal(list.items.length, amount);
const item = list.items[list.items.length - 1];
assert.equal(item.id, 'A');
assert.equal(item.data, 'hello' + amount);
assert.notEqual(item.next.length, 0);
done();
}));
it('commits a list after batch size was reached', async((done) => {
const list = new List(ipfs, 'A');
for(let i = 1; i <= List.batchSize; i ++) {
list.add("hello" + i);
}
assert.equal(list.id, 'A');
// assert.equal(list.seq, 0);
// assert.equal(list.ver, List.batchSize);
assert.equal(list.items.length, List.batchSize);
assert.equal(list._currentBatch.length, List.batchSize);
assert.equal(list._items.length, 0);
const item = list.items[list.items.length - 1];
assert.equal(item.id, 'A');
// assert.equal(item.seq, 0);
assert.equal(list.items.length, List.batchSize);
assert.equal(item.data, 'hello' + List.batchSize);
assert.notEqual(item.next.length, 0);
done();
}));
});
describe('join', () => {
it('joins unique items', async((done) => {
const list1 = new List(ipfs, 'A');
const list2 = new List(ipfs, 'B');
list1.add("helloA1")
list1.add("helloA2")
list2.add("helloB1")
list2.add("helloB2")
list1.join(list2);
list1.add("helloA3")
list1.join(list2);
list1.join(list2);
list1.add("helloA4")
list1.add("helloA5")
const lastItem = list1.items[list1.items.length - 1];
assert.equal(list1.items.length, 7);
assert.equal(lastItem.next.length, 1);
assert.equal(lastItem.next[0].hash, 'QmPT7qgkysupMuMf2k8yESBL8A55Cva9Y8pqTuA1AMWB4m');
done();
}));
it('finds the next head when adding a new element', async((done) => {
const list1 = new List(ipfs, 'A');
list1.add("helloA1")
list1.add("helloA2")
list1.add("helloA3")
assert.equal(list1._currentBatch.length, 3);
assert.equal(list1._currentBatch[0].next.length, 0);
assert.equal(list1._currentBatch[1].next.length, 1);
assert.equal(list1._currentBatch[1].next[0].hash, 'QmQQhCyFHWAwNm7k5CbDF5iKuHfT2VJU5qUkncQDNAfTbL');
assert.equal(list1._currentBatch[2].next.length, 1);
assert.equal(list1._currentBatch[2].next[0].hash, 'QmbKmxJv6mhGvvUQwU3A5BBWvpBMVvGkpFr5AMrrqib6EH');
done();
}));
it('finds the next heads (two) after a join', async((done) => {
const list1 = new List(ipfs, 'A');
const list2 = new List(ipfs, 'B');
list1.add("helloA1")
list2.add("helloB1")
list2.add("helloB2")
list1.join(list2);
list1.add("helloA2")
assert.equal(list1._currentBatch.length, 1);
assert.equal(list1._currentBatch[0].next.length, 2);
// assert.equal(list1._currentBatch[0].next[1].compactId, 'A.0.0');
assert.equal(list1._currentBatch[0].next[1].hash, 'QmQQhCyFHWAwNm7k5CbDF5iKuHfT2VJU5qUkncQDNAfTbL');
// assert.equal(list1._currentBatch[0].next[0].compactId, 'B.0.1');
assert.equal(list1._currentBatch[0].next[0].hash, 'QmUnaa1dT2FEYmK1nDD5fJ53Pd2iEDtGL3oLM5M7VLBqHJ');
done();
}));
it('finds the next head (one) after a join', async((done) => {
const list1 = new List(ipfs, 'A');
const list2 = new List(ipfs, 'B');
list1.add("helloA1")
list2.add("helloB1")
list2.add("helloB2")
list1.join(list2);
list1.add("helloA2")
list1.add("helloA3")
assert.equal(list1._currentBatch.length, 2);
assert.equal(list1._currentBatch[1].next.length, 1);
// assert.equal(list1._currentBatch[1].next[0].compactId, 'A.1.0');
assert.equal(list1._currentBatch[1].next[0].hash, 'QmaGPFKz6qJLuLhUFN2J48FVZttVF2y2gmgU3666H21BWQ');
assert.equal(list1._currentBatch[0].next.length, 2);
// assert.equal(list1._currentBatch[0].next[0].compactId, 'B.0.1');
assert.equal(list1._currentBatch[0].next[0].hash, 'QmUnaa1dT2FEYmK1nDD5fJ53Pd2iEDtGL3oLM5M7VLBqHJ');
// assert.equal(list1._currentBatch[0].next[1].compactId, 'A.0.0');
assert.equal(list1._currentBatch[0].next[1].hash, 'QmQQhCyFHWAwNm7k5CbDF5iKuHfT2VJU5qUkncQDNAfTbL');
done();
}));
it('finds the next heads after two joins', async((done) => {
const list1 = new List(ipfs, 'A');
const list2 = new List(ipfs, 'B');
list1.add("helloA1")
list1.add("helloA2")
list2.add("helloB1")
list2.add("helloB2")
list1.join(list2);
list1.add("helloA3")
list1.join(list2);
list1.add("helloA4")
list1.add("helloA5")
const lastItem = list1.items[list1.items.length - 1];
assert.equal(list1.items.length, 7);
assert.equal(lastItem.next.length, 1);
// assert.equal(lastItem.next[0].compactId, 'A.2.0');
assert.equal(lastItem.next[0].hash, 'QmPT7qgkysupMuMf2k8yESBL8A55Cva9Y8pqTuA1AMWB4m');
done();
}));
it('finds the next heads after multiple joins', async((done) => {
const list1 = new List(ipfs, 'A');
const list2 = new List(ipfs, 'B');
const list3 = new List(ipfs, 'C');
const list4 = new List(ipfs, 'D');
list1.add("helloA1")
list1.add("helloA2")
list2.add("helloB1")
list2.add("helloB2")
list1.join(list2);
list3.add("helloC1")
list4.add("helloD1")
list1.join(list3);
list1.add("helloA3")
list2.join(list1);
list1.join(list2);
list2.join(list4);
list4.add("helloD2")
list4.add("helloD3")
list1.add("helloA4")
list1.join(list4);
list1.add("helloA5")
const lastItem = list1.items[list1.items.length - 1];
assert.equal(list1.items.length, 11);
assert.equal(lastItem.next.length, 2);
// assert.equal(lastItem.next[1].compactId, 'A.4.0');
assert.equal(lastItem.next[1].hash, 'QmdyS6VUP5wAENpdT1sp6o1JJwMaKajPg1W441MD71r4pE');
// assert.equal(lastItem.next[0].compactId, 'D.0.2');
assert.equal(lastItem.next[0].hash, 'QmfWbsDTKd3zhRoJZ2oGeBYmgsnerK3SmFYXZtydgADec7');
done();
}));
it('joins list of one item with list of two items', async((done) => {
const list1 = new List(ipfs, 'A');
const list2 = new List(ipfs, 'B');
list1.add("helloA1")
list2.add("helloB1")
list2.add("helloB2")
list1.join(list2);
const lastItem = list1.items[list1.items.length - 1];
assert.equal(list1.id, 'A');
// assert.equal(list1.seq, 1);
// assert.equal(list1.ver, 0);
assert.equal(list1._currentBatch.length, 0);
assert.equal(list1._items.length, 3);
assert.equal(lastItem.id, 'B');
// assert.equal(lastItem.seq, 0);
// assert.equal(lastItem.ver, 1);
assert.equal(lastItem.data, 'helloB2');
done();
}));
it('joins lists two ways', async((done) => {
const list1 = new List(ipfs, 'A');
const list2 = new List(ipfs, 'B');
list1.add("helloA1")
list1.add("helloA2")
list2.add("helloB1")
list2.add("helloB2")
list1.join(list2);
list2.join(list1);
const lastItem1 = list1.items[list1.items.length - 1];
assert.equal(list1.id, 'A');
// assert.equal(list1.seq, 1);
// assert.equal(list1.ver, 0);
assert.equal(list1._currentBatch.length, 0);
assert.equal(list1._items.length, 4);
assert.equal(lastItem1.id, 'B');
// assert.equal(lastItem1.seq, 0);
// assert.equal(lastItem1.ver, 1);
assert.equal(lastItem1.data, 'helloB2');
const lastItem2 = list2.items[list2.items.length - 1];
assert.equal(list2.id, 'B');
// assert.equal(list2.seq, 2);
// assert.equal(list2.ver, 0);
assert.equal(list2._currentBatch.length, 0);
assert.equal(list2._items.length, 4);
assert.equal(lastItem2.id, 'A');
// assert.equal(lastItem2.seq, 0);
// assert.equal(lastItem2.ver, 1);
assert.equal(lastItem2.data, 'helloA2');
done();
}));
it('joins lists twice', async((done) => {
const list1 = new List(ipfs, 'A');
const list2 = new List(ipfs, 'B');
list1.add("helloA1")
list2.add("helloB1")
list2.join(list1);
list1.add("helloA2")
list2.add("helloB2")
list2.join(list1);
const secondItem = list2.items[1];
const lastItem = list2.items[list2.items.length - 1];
assert.equal(list2.id, 'B');
// assert.equal(list2.seq, 2);
// assert.equal(list2.ver, 0);
assert.equal(list2._currentBatch.length, 0);
assert.equal(list2._items.length, 4);
assert.equal(secondItem.id, 'A');
// assert.equal(secondItem.seq, 0);
// assert.equal(secondItem.ver, 0);
assert.equal(secondItem.data, 'helloA1');
assert.equal(lastItem.id, 'A');
// assert.equal(lastItem.seq, 0);
// assert.equal(lastItem.ver, 1);
assert.equal(lastItem.data, 'helloA2');
done();
}));
it('joins 4 lists to one', async((done) => {
const list1 = new List(ipfs, 'A');
const list2 = new List(ipfs, 'B');
const list3 = new List(ipfs, 'C');
const list4 = new List(ipfs, 'D');
list1.add("helloA1")
list2.add("helloB1")
list1.add("helloA2")
list2.add("helloB2")
list3.add("helloC1")
list4.add("helloD1")
list3.add("helloC2")
list4.add("helloD2")
list1.join(list2);
list1.join(list3);
list1.join(list4);
const secondItem = list1.items[1];
const lastItem = list1.items[list1.items.length - 1];
assert.equal(list1.id, 'A');
// assert.equal(list1.seq, 3);
// assert.equal(list1.ver, 0);
assert.equal(list1._currentBatch.length, 0);
assert.equal(list1._items.length, 8);
assert.equal(secondItem.id, 'A');
// assert.equal(secondItem.seq, 0);
// assert.equal(secondItem.ver, 1);
assert.equal(secondItem.data, 'helloA2');
assert.equal(lastItem.id, 'D');
// assert.equal(lastItem.seq, 0);
// assert.equal(lastItem.ver, 1);
assert.equal(lastItem.data, 'helloD2');
done();
}));
it('joins lists from 4 lists', async((done) => {
const list1 = new List(ipfs, 'A');
const list2 = new List(ipfs, 'B');
const list3 = new List(ipfs, 'C');
const list4 = new List(ipfs, 'D');
list1.add("helloA1")
list1.join(list2);
list2.add("helloB1")
list2.join(list1);
list1.add("helloA2")
list2.add("helloB2")
list1.join(list3);
list3.join(list1);
list3.add("helloC1")
list4.add("helloD1")
list3.add("helloC2")
list4.add("helloD2")
list1.join(list3);
list1.join(list2);
list4.join(list2);
list4.join(list1);
list4.join(list3);
list4.add("helloD3")
list4.add("helloD4")
const secondItem = list4.items[1];
const lastItem1 = list4._items[list4._items.length - 1];
const lastItem2 = list4.items[list4.items.length - 1];
assert.equal(list4.id, 'D');
// assert.equal(list4.seq, 7);
// assert.equal(list4.ver, 2);
assert.equal(list4._currentBatch.length, 2);
assert.equal(list4._items.length, 8);
assert.equal(secondItem.id, 'D');
// assert.equal(secondItem.seq, 0);
// assert.equal(secondItem.ver, 1);
assert.equal(secondItem.data, 'helloD2');
assert.equal(lastItem1.id, 'C');
// assert.equal(lastItem1.seq, 3);
// assert.equal(lastItem1.ver, 1);
assert.equal(lastItem1.data, 'helloC2');
assert.equal(lastItem2.id, 'D');
// assert.equal(lastItem2.seq, 7);
// assert.equal(lastItem2.ver, 1);
assert.equal(lastItem2.data, 'helloD4');
done();
}));
it('fetches items from history on join', async((done) => {
const list1 = new List(ipfs, 'A');
const list2 = new List(ipfs, 'AAA');
const count = 32;
for(let i = 1; i < count + 1; i ++) {
list1.add("first " + i);
list2.add("second " + i);
}
const hash1 = list1.ipfsHash;
const hash2 = list2.ipfsHash;
const final = new List(ipfs, 'B');
const other1 = List.fromIpfsHash(ipfs, hash1);
const other2 = List.fromIpfsHash(ipfs, hash2);
final.join(other1);
assert.equal(_.includes(final.items.map((a) => a.hash), undefined), false);
assert.equal(final.items.length, count);
assert.equal(final.items[0].data, "first 1");
assert.equal(final.items[final.items.length - 1].data, "first " + count);
final.join(other2);
assert.equal(final.items.length, count * 2);
assert.equal(final.items[0].data, "second 1");
assert.equal(final.items[final.items.length - 1].data, "second " + count);
done();
}));
it('orders fetched items correctly', async((done) => {
const list1 = new List(ipfs, 'A');
const list2 = new List(ipfs, 'AAA');
const final = new List(ipfs, 'B');
const count = List.batchSize * 3;
for(let i = 1; i < (count * 2) + 1; i ++)
list1.add("first " + i);
const hash1 = list1.ipfsHash;
const other1 = List.fromIpfsHash(ipfs, hash1);
final.join(other1);
assert.equal(final.items[0].data, "first 1");
assert.equal(final.items[final.items.length - 1].data, "first " + count * 2);
assert.equal(final.items.length, count * 2);
// Second batch
for(let i = 1; i < count + 1; i ++)
list2.add("second " + i);
const hash2 = list2.ipfsHash;
const other2 = List.fromIpfsHash(ipfs, hash2);
final.join(other2);
// console.log(final.items.map((e) => e.comptactId))
assert.equal(final.items.length, count + count * 2);
assert.equal(final.items[0].data, "second 1");
assert.equal(final.items[1].data, "second 2");
assert.equal(final.items[final.items.length - 1].data, "second " + count);
done();
}));
});
describe('findHeads', () => {
it('finds the next head', async((done) => {
const list1 = new List(ipfs, 'A');
const list2 = new List(ipfs, 'B');
const list3 = new List(ipfs, 'C');
list1.add("helloA1")
list1.add("helloA2")
list2.add("helloB1")
list2.add("helloB2")
list1.join(list2);
list1.add("helloA3")
list1.add("helloA4")
list3.add("helloC1")
list3.add("helloC2")
list2.join(list3);
list2.add("helloB3")
list1.join(list2);
list1.add("helloA5")
list1.add("helloA6")
const heads = List2.findHeads(list1.items)
assert.equal(heads.length, 1);
assert.equal(heads[0].hash, 'QmfUmywxAP8ZXNkxN1CJJ3UsbmEracdeDCzaZzid9zwoe9');
done();
}));
it('finds the next heads', async((done) => {
const list1 = new List(ipfs, 'A');
const list2 = new List(ipfs, 'B');
const list3 = new List(ipfs, 'C');
list1.add("helloA1")
list1.add("helloA2")
list2.add("helloB1")
list2.add("helloB2")
list1.join(list2);
list1.add("helloA3")
list1.add("helloA4")
list3.add("helloC1")
list3.add("helloC2")
list2.join(list3);
list2.add("helloB3")
list1.join(list2);
const heads = List2.findHeads(list1.items)
assert.equal(heads.length, 2);
assert.equal(heads[0].hash, 'QmT8oHPp2x5t5sKy2jw3ZXKihm1danLbmHMbQ6nnzWRdj6');
assert.equal(heads[1].hash, 'QmPT7qgkysupMuMf2k8yESBL8A55Cva9Y8pqTuA1AMWB4m');
done();
}));
});
describe('is a CRDT', () => {
it('join is associative', async((done) => {
let list1 = new List(ipfs, 'A');
let list2 = new List(ipfs, 'B');
let list3 = new List(ipfs, 'C');
list1.add("helloA1")
list1.add("helloA2")
list2.add("helloB1")
list2.add("helloB2")
list3.add("helloC1")
list3.add("helloC2")
// a + (b + c)
list2.join(list3);
list1.join(list2);
const res1 = list1.items.map((e) => e.hash).join(",");
list1 = new List(ipfs, 'A');
list2 = new List(ipfs, 'B');
list3 = new List(ipfs, 'C');
list1.add("helloA1")
list1.add("helloA2")
list2.add("helloB1")
list2.add("helloB2")
list3.add("helloC1")
list3.add("helloC2")
// (a + b) + c
list1.join(list2);
list1.join(list3);
const res2 = list1.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
assert.equal(res1.length, len)
assert.equal(res2.length, len)
assert.equal(res1, res2);
done();
}));
it('join is commutative', async((done) => {
let list1 = new List(ipfs, 'A');
let list2 = new List(ipfs, 'B');
list1.add("helloA1")
list1.add("helloA2")
list2.join(list1);
list2.add("helloB1")
list2.add("helloB2")
// b + a
list2.join(list1);
const res1 = list2.items.map((e) => e.hash).join(",");
list1 = new List(ipfs, 'A');
list2 = new List(ipfs, 'B');
list1.add("helloA1")
list1.add("helloA2")
list2.join(list1);
list2.add("helloB1")
list2.add("helloB2")
// a + b
list1.join(list2);
const res2 = list1.items.map((e) => e.hash).join(",");
// commutativity: a + (b + c) == (a + b) + c
const len = (46 + 1) * 4 - 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();
}));
it('join is idempotent', async((done) => {
const list1 = new List(ipfs, 'A');
list1.add("helloA1")
list1.add("helloA2")
list1.add("helloA3")
const list2 = new List(ipfs, 'A');
list2.add("helloA1")
list2.add("helloA2")
list2.add("helloA3")
// idempotence: a + a = a
list1.join(list2);
assert.equal(list1.id, 'A');
// assert.equal(list1.seq, 1);
// assert.equal(list1.ver, 0);
assert.equal(list1.items.length, 3);
// assert.equal(list1.items[0].ver, 0);
// assert.equal(list1.items[1].ver, 1);
// assert.equal(list1.items[2].ver, 2);
done();
}));
});
describe('clear', () => {
it('TODO', (done) => {
done();
});
});
describe('_fetchHistory', () => {
it('TODO', (done) => {
done();
});
});
describe('_fetchRecursive', () => {
it('TODO', (done) => {
done();
});
});
describe('_insert', () => {
it('TODO', (done) => {
done();
});
});
describe('_commit', () => {
it('TODO', (done) => {
done();
});
});
describe('isReferencedInChain', () => {
it('TODO', (done) => {
done();
});
});
}));

View File

@ -1,98 +0,0 @@
'use strict';
const _ = require('lodash');
const async = require('asyncawait/async');
const await = require('asyncawait/await');
const assert = require('assert');
const ipfsDaemon = require('orbit-common/lib/ipfs-daemon');
const ipfsAPI = require('orbit-common/lib/ipfs-api-promised');
const Node = require('../src/list/OrbitNode');
const startIpfs = async (() => {
return new Promise(async((resolve, reject) => {
const ipfsd = await(ipfsDaemon());
resolve(ipfsd.ipfs);
}));
});
let ipfs;
describe('OrbitNode', function() {
this.timeout(5000);
before(async((done) => {
ipfs = await(startIpfs());
done();
}));
describe('Constructor', () => {
it('initializes member variables', async((done) => {
const node = new Node(ipfs, 'A');
assert.equal(node.id, 'A');
assert.equal(node.data, null);
assert.equal(node.next.length, 0);
assert.equal(node.hash, 'QmbibmqvDT4LHo6oEFRMgRhKUBw6Fn7SLCp57GKu5eY1uY');
assert.equal(node._ipfs, ipfs);
done();
}));
it('initializes member variables with data', async((done) => {
const node = new Node(ipfs, 'A', 'QmbibmqvDT4LHo6oEFRMgRhKUBw6Fn7SLCp57GKu5eY1uY');
assert.equal(node.id, 'A');
assert.equal(node.data, 'QmbibmqvDT4LHo6oEFRMgRhKUBw6Fn7SLCp57GKu5eY1uY');
assert.equal(node.next.length, 0);
assert.equal(node.hash, 'Qme1c5GGCtkBLZV4dVsZtJj2ZREraupAfyJXrYrzfEVKN5');
assert.equal(node._ipfs, ipfs);
done();
}));
});
describe('fetchPayload', () => {
it('TODO', async((done) => {
done();
}));
});
describe('hasChild', () => {
it('TODO', async((done) => {
done();
}));
});
describe('fetchPayload', () => {
it('TODO', async((done) => {
done();
}));
});
describe('heads', () => {
it('TODO', async((done) => {
done();
}));
});
describe('ipfsHash', () => {
it('TODO', async((done) => {
done();
}));
});
describe('asJson', () => {
it('TODO', async((done) => {
done();
}));
});
describe('fromIpfsHash', () => {
it('TODO', async((done) => {
done();
}));
});
describe('equals', () => {
it('TODO', async((done) => {
done();
}));
});
});