From 57c006feb2342d0f44d632a5be99398acbdd9cc7 Mon Sep 17 00:00:00 2001 From: haad Date: Thu, 25 Feb 2016 14:06:02 +0100 Subject: [PATCH] Fixing lists --- examples/benchmark.js | 7 +- examples/keyvalue.js | 9 +- examples/keyvalueReader.js | 50 +++++++++++ examples/reader.js | 27 +++--- examples/writer.js | 7 +- src/DataStore.js | 33 +++---- src/OrbitClient.js | 48 ++++++++--- src/PubSub.js | 18 +++- src/list/List.js | 23 +++-- src/list/Node.js | 4 + src/list/OrbitList.js | 80 +++++++++++++---- src/list/OrbitNode.js | 51 ++++++++--- test/list-tests.js | 27 ++---- test/orbit-client-tests.js | 1 - test/orbit-list-tests.js | 163 ++++++++++++++++++----------------- test/orbitlist-node-tests.js | 12 +-- 16 files changed, 362 insertions(+), 198 deletions(-) create mode 100644 examples/keyvalueReader.js diff --git a/examples/benchmark.js b/examples/benchmark.js index 2f22fd1..c504728 100644 --- a/examples/benchmark.js +++ b/examples/benchmark.js @@ -4,9 +4,10 @@ var async = require('asyncawait/async'); var OrbitClient = require('../src/OrbitClient'); var Timer = require('./Timer'); -// Redis host -var host = '178.62.229.175'; -var port = 6379; +// orbit-server +// const host = 'localhost'; +const host = '178.62.241.75'; +const port = 3333; var username = 'testrunner'; var password = ''; diff --git a/examples/keyvalue.js b/examples/keyvalue.js index 8739513..7da54a6 100644 --- a/examples/keyvalue.js +++ b/examples/keyvalue.js @@ -5,17 +5,18 @@ const await = require('asyncawait/await'); const OrbitClient = require('../src/OrbitClient'); const Timer = require('./Timer'); -// Redis +// orbit-server const host = 'localhost'; +// const host = '178.62.241.75'; const port = 3333; -const username = 'LambOfGod'; +const username = process.argv[3] ? process.argv[3] : 'LambOfGod'; const password = ''; let run = (async(() => { try { const orbit = OrbitClient.connect(host, port, username, password); - const channel = 'testing123'; + const channel = process.argv[2] ? process.argv[2] : 'testing123'; const db = orbit.channel(channel); let count = 1; @@ -23,7 +24,7 @@ let run = (async(() => { while(true) { const key = "username"; let timer = new Timer(true); - db.put(key, "Lamb Of God " + count); + db.put(key, username + " " + count); let v = db.get(key); console.log("---------------------------------------------------") diff --git a/examples/keyvalueReader.js b/examples/keyvalueReader.js new file mode 100644 index 0000000..b27aa03 --- /dev/null +++ b/examples/keyvalueReader.js @@ -0,0 +1,50 @@ +'use strict'; + +const async = require('asyncawait/async'); +const await = require('asyncawait/await'); +const OrbitClient = require('../src/OrbitClient'); +const Timer = require('./Timer'); + +// orbit-server +const host = 'localhost'; +// const host = '178.62.241.75'; +const port = 3333; + +const username = process.argv[3] ? process.argv[3] : 'LambOfGod'; +const password = ''; + +let running = false; +let run = (async(() => { + if(!running) { + try { + running = true; + const orbit = OrbitClient.connect(host, port, username, password); + const channel = process.argv[2] ? process.argv[2] : 'testing123'; + const db = orbit.channel(channel); + + let count = 1; + + setInterval(async(() => { + const key = "username"; + let timer = new Timer(true); + let v = db.get(key); + + console.log("---------------------------------------------------") + console.log("Key | Value") + console.log("---------------------------------------------------") + console.log(`${key} | ${v}`); + console.log("---------------------------------------------------") + console.log(`Query #${count} took ${timer.stop(true)} ms\n`); + + count ++; + running = false; + }), 1000); + } catch(e) { + console.error("error:", e); + console.error(e.stack); + process.exit(1); + } + } +}))(); + +module.exports = run; diff --git a/examples/reader.js b/examples/reader.js index 65a13a6..5e55a01 100644 --- a/examples/reader.js +++ b/examples/reader.js @@ -5,17 +5,20 @@ const await = require('asyncawait/await'); const OrbitClient = require('../src/OrbitClient'); const Timer = require('./Timer'); -// Redis +// orbit-server const host = 'localhost'; -const port = 6379; +// const host = '178.62.241.75'; +const port = 3333; -const username = 'LambOfGod'; +const username = process.argv[3] ? process.argv[3] : 'LambOfGod'; const password = ''; +const prefix = process.argv[4] ? process.argv[4] : 'LambOfGod'; + let run = (async(() => { try { var orbit = OrbitClient.connect(host, port, username, password); - const c1 = 'c1'; + const c1 = process.argv[2] ? process.argv[2] : 'c1';; const channel = orbit.channel(c1); let count = 1; @@ -26,23 +29,25 @@ let run = (async(() => { if(!running) { running = true; - // let timer = new Timer(true); - channel.add("Hello " + count); - // console.log(`Query #${count} took ${timer.stop(true)} ms\n`); + let timer = new Timer(true); + channel.add(prefix + count); + console.log(`Query #${count} took ${timer.stop(true)} ms\n`); - const c = channel.iterator({ limit: -1 }).collect().length; - let items = channel.iterator({ limit: 5 }).collect(); + let timer2 = new Timer(true); + // const c = channel.iterator({ limit: -1 }).collect().length; + let items = channel.iterator({ limit: -1 }).collect(); console.log("---------------------------------------------------") console.log("Key | Value") console.log("---------------------------------------------------") console.log(items.map((e) => `${e.payload.key} | ${e.payload.value}`).join("\n")); console.log("---------------------------------------------------") - console.log(`Found ${items.length} items from ${c}\n`); + // console.log(`Found ${items.length} items from ${c}\n`); + console.log(`Query 2 #${count} took ${timer2.stop(true)} ms\n`); running = false; count ++; } - }), 500); + }), 2000); } catch(e) { console.error("error:", e); diff --git a/examples/writer.js b/examples/writer.js index 5bbab7f..b95c5f9 100644 --- a/examples/writer.js +++ b/examples/writer.js @@ -5,9 +5,10 @@ var await = require('asyncawait/await'); var OrbitClient = require('../src/OrbitClient'); var Timer = require('./Timer'); -// Redis -var host = 'localhost'; -var port = 6379; +// orbit-server +// const host = 'localhost'; +const host = '178.62.241.75'; +const port = 3333; var username = process.argv[2] ? process.argv[2] : 'DankoJones'; var password = ''; diff --git a/src/DataStore.js b/src/DataStore.js index 586bf7c..8fed5b2 100644 --- a/src/DataStore.js +++ b/src/DataStore.js @@ -30,6 +30,13 @@ class DataStore { return this._fetchRecursive(options); } + _fetchOne(item) { + return new Promise(async((resolve, reject) => { + await(item.getPayload()); + resolve({ hash: item.data, payload: item.Payload }); + })); + } + _fetchRecursive(options, currentAmount, deleted, res) { const opts = { amount: options && options.amount ? options.amount : DefaultAmount, @@ -47,17 +54,20 @@ class DataStore { let handledItems = deleted ? deleted : []; let item; + // Fetch the item from ipfs const node = this.list.items[this.list.items.length - currentAmount - 1]; - if(node) - item = await(this._fetchOne(node)); + if(node) item = await(this._fetchOne(node)); + + const canAdd = (firstHash, key, foundItemsCount) => { + return (!opts.key || (opts.key && opts.key === item.payload.key)) && + (!opts.first || (opts.first && (opts.first === item.payload.key && foundItemsCount === 0)) + || (opts.first && (opts.first !== item.payload.key && foundItemsCount > 0))) + }; if(item && item.payload) { - const wasHandled = _.includes(handledItems, item.payload.key); + const wasHandled = _.includes(handledItems, item.payload.key); // Last Write Wins, if it was handled, ignore the rest if((item.payload.op === HashCacheOps.Put || item.payload.op === HashCacheOps.Add) && !wasHandled) { - if((!opts.key || (opts.key && opts.key === item.payload.key)) && - (!opts.first || (opts.first && (opts.first === item.payload.key && result.length === 0)) - || (opts.first && (opts.first !== item.payload.key && result.length > 0)))) - { + if(canAdd(opts.first, item.payload.key, result.length)) { result.push(item); handledItems.push(item.payload.key); } @@ -85,15 +95,6 @@ class DataStore { return result; } - - _fetchOne(item) { - return new Promise((resolve, reject) => { - await(item.getPayload()); - const f = item.compact(); - const res = { hash: f.data, payload: f.Payload }; - resolve(res); - }); - } } module.exports = DataStore; diff --git a/src/OrbitClient.js b/src/OrbitClient.js index ae30acb..bfea6cd 100644 --- a/src/OrbitClient.js +++ b/src/OrbitClient.js @@ -15,6 +15,8 @@ const PubSub = require('./PubSub'); const List = require('./list/OrbitList'); const DataStore = require('./DataStore'); +var Timer = require('../examples/Timer'); + const pubkey = Keystore.getKeys().publicKey; const privkey = Keystore.getKeys().privateKey; @@ -27,11 +29,22 @@ class OrbitClient { channel(hash, password) { if(password === undefined) password = ''; - this._pubsub.subscribe(hash, password, async((hash, message) => { + const processMsg = async((hash, message) => { const other = await(List.fromIpfsHash(this._ipfs, message)); - if(other.id !== this.user.username) + if(other.id !== this.user.username) { this._store.join(other); - })); + } + }); + + const onLatest = async((hash, message) => { + console.log("--> Received latest list:", message) + if(message) { + const other = await(List.fromIpfsHash(this._ipfs, message)); + this._store.join(other); + } + }); + + this._pubsub.subscribe(hash, password, processMsg, onLatest); return { iterator: (options) => this._iterator(hash, password, options), @@ -106,9 +119,12 @@ class OrbitClient { } _publish(data) { - let post = new Post(data); - // post.encrypt(privkey, pubkey); - return await (ipfsAPI.putObject(this._ipfs, JSON.stringify(post))); + return new Promise((resolve, reject) => { + let post = new Post(data); + // post.encrypt(privkey, pubkey); + const res = await (ipfsAPI.putObject(this._ipfs, JSON.stringify(post))); + resolve(res); + }) } _createMessage(channel, password, operation, key, value) { @@ -121,13 +137,13 @@ class OrbitClient { /* DB Operations */ _add(channel, password, data) { - const post = this._publish(data); + const post = await(this._publish(data)); const key = post.Hash; return await(this._createOperation(channel, password, HashCacheOps.Add, key, post.Hash, data)); } _put(channel, password, key, data) { - const post = this._publish(data); + const post = await(this._publish(data)); return await(this._createOperation(channel, password, HashCacheOps.Put, key, post.Hash)); } @@ -136,12 +152,18 @@ class OrbitClient { } _createOperation(channel, password, operation, key, value, data) { - const hash = this._createMessage(channel, password, operation, key, value); - const res = await(this._store.add(hash)); - const listHash = await(this._store.list.getIpfsHash()); - await(this._pubsub.publish(channel, listHash)); - // return res; + var create = async(() => { + return new Promise(async((resolve, reject) => { + const hash = this._createMessage(channel, password, operation, key, value); + const res = await(this._store.add(hash)); + const listHash = await(this._store.list.getIpfsHash()); + await(this._pubsub.publish(channel, listHash)); + resolve(); + })); + }) + await(create()); return key; + // return res; } _deleteChannel(channel, password) { diff --git a/src/PubSub.js b/src/PubSub.js index fb12044..7cad6d0 100644 --- a/src/PubSub.js +++ b/src/PubSub.js @@ -8,13 +8,25 @@ class Pubsub { this.ipfs = ipfs; this._subscriptions = {}; this._socket = io(`http://${host}:${port}`); - this._socket.on('connect', (socket) => console.log('Connected to', `http://${host}:${port}`)); + this._socket.on('connect', (socket) => console.log(`Connected to http://${host}:${port}`)); + this._socket.on('disconnect', (socket) => console.log(`Disconnected from http://${host}:${port}`)); + this._socket.on('event', (e) => console.log('Event:', e)); + this._socket.on('error', (e) => console.log('error:', e)); this._socket.on('message', this._handleMessage.bind(this)); + this._socket.on('latest', (hash, message) => { + console.log(">", hash, message); + if(this._subscriptions[hash]) { + this._subscriptions[hash].head = message; + + if(this._subscriptions[hash].onLatest) + this._subscriptions[hash].onLatest(hash, message); + } + }); } - subscribe(hash, password, callback) { + subscribe(hash, password, callback, onLatest) { if(!this._subscriptions[hash]) { - this._subscriptions[hash] = { head: null, callback: callback }; + this._subscriptions[hash] = { head: null, callback: callback, onLatest: onLatest }; this._socket.emit('subscribe', { channel: hash }); } } diff --git a/src/list/List.js b/src/list/List.js index 10ca246..733587e 100644 --- a/src/list/List.js +++ b/src/list/List.js @@ -4,14 +4,18 @@ const _ = require('lodash'); const Node = require('./Node'); class List { - constructor(id) { + constructor(id, seq, ver, items) { this.id = id; - this.seq = 0; - this.ver = 0; - this._items = []; + this.seq = seq ? seq : 0; + this.ver = ver ? ver : 0; + this._items = items ? items : []; this._currentBatch = []; } + get compactId() { + return "" + this.id + "." + this.seq + "." + this.ver; + } + get items() { return this._items.concat(this._currentBatch); } @@ -41,7 +45,7 @@ class List { } _isReferencedInChain(all, item) { - let isReferenced = _.findLast(all, (e) => this._references(e, item)) !== undefined; + const isReferenced = _.findLast(all, (e) => this._references(e, item)) !== undefined; return isReferenced; } @@ -51,7 +55,7 @@ class List { _references(a, b) { for(let i = 0; i < a.next.length; i ++) { - if(b.compactId === a.next[i]) + if(b.compactId === a.next[i].compactId) return true; } return false; @@ -70,14 +74,9 @@ class List { id: this.id, seq: this.seq, ver: this.ver, - items: this._currentBatch.map((f) => f.compact()) + items: this._currentBatch.map((f) => f.toJson()) } } - - toString() { - const items = this.items.map((f) => JSON.stringify(f.compact())).join("\n"); - return `id: ${this.id}, seq: ${this.seq}, ver: ${this.ver}, items:\n${items}`; - } } module.exports = List; diff --git a/src/list/Node.js b/src/list/Node.js index aa0544f..fca3850 100644 --- a/src/list/Node.js +++ b/src/list/Node.js @@ -16,6 +16,10 @@ class Node { compact() { return { id: this.id, seq: this.seq, ver: this.ver, data: this.data, next: this.next } } + + toJson() { + return { id: this.id, seq: this.seq, ver: this.ver, data: this.data, next: this.next } + } } module.exports = Node; diff --git a/src/list/OrbitList.js b/src/list/OrbitList.js index db8c071..31b1a0f 100644 --- a/src/list/OrbitList.js +++ b/src/list/OrbitList.js @@ -7,25 +7,54 @@ const ipfsAPI = require('orbit-common/lib/ipfs-api-promised'); const List = require('./List'); const Node = require('./OrbitNode'); -const MaxBatchSize = 200; +const MaxBatchSize = 10; // How many items per sequence. Saves a snapshot to ipfs in batches of this many items. +const MaxHistory = 1000; // How many items to fetch in the chain per join class OrbitList extends List { constructor(id, ipfs) { super(id); this._ipfs = ipfs; this.hash = null; + this.next = null; } add(data) { - const heads = super._findHeads(this.items); - const node = new Node(this._ipfs, this.id, this.seq, this.ver, data, heads); - this._currentBatch.push(node); - this.ver ++; - if(this.ver >= MaxBatchSize) this._commit(); - return node.ipfsHash; + const heads = super._findHeads(this.items); + const node = new Node(this._ipfs, this.id, this.seq, this.ver, data, heads); + node._commit(); + this._currentBatch.push(node); + this.ver ++; + + } + + join(other) { + super.join(other); + + // WIP: fetch missing nodes + let depth = 0; + + const isReferenced = (all, item) => _.findLast(all, (f) => f === item) !== undefined; + const fetchRecursive = (hash) => { + hash = hash instanceof Node === true ? hash.hash : hash; + let allHashes = this._items.map((a) => a.hash); + depth ++; + if(!isReferenced(allHashes, hash)) { + const item = Node.fromIpfsHash(this._ipfs, hash); + if(item.next && depth < MaxHistory) { + item.heads.forEach(fetchRecursive); + const indices = item.heads.map((k) => _.findIndex(this._items, (b) => b.hash === k)); + const idx = indices.length > 0 ? Math.max(_.max(indices) + 1, 0) : 0; + this._items.splice(idx, 0, item) + // console.log("added", item.compactId, "at", idx, item.data, depth); + } + } + }; + + other.items.forEach((e) => e.heads.forEach(fetchRecursive)); + // console.log("--> Fetched", MaxHistory, "items from the history\n"); } clear() { @@ -34,23 +63,38 @@ class OrbitList extends List { } getIpfsHash() { - const list = await(ipfsAPI.putObject(this._ipfs, JSON.stringify(this.toJson()))); - return list.Hash; + return new Promise(async((resolve, reject) => { + var data = await(this.toJson()) + const list = await(ipfsAPI.putObject(this._ipfs, JSON.stringify(data))); + resolve(list.Hash); + })); } static fromIpfsHash(ipfs, hash) { - const l = await(ipfsAPI.getObject(ipfs, hash)); - const list = OrbitList.fromJson(ipfs, JSON.parse(l.Data)); - return list; + return new Promise(async((resolve, reject) => { + const l = await(ipfsAPI.getObject(ipfs, hash)); + const list = OrbitList.fromJson(ipfs, JSON.parse(l.Data)); + resolve(list); + })); + } + + toJson() { + let items = {}; + this._currentBatch.forEach((f) => Object.defineProperty(items, f.compactId.toString(), { value: f.ipfsHash, enumerable: true })); + return { + id: this.id, + seq: this.seq, + ver: this.ver, + items: items + } } static fromJson(ipfs, json) { - let list = new List(json.id); - list.seq = json.seq; - list.ver = json.ver; - // list._items = _.uniqWith(json.items.map((f) => new Node(ipfs, f.id, f.seq, f.ver, f.data, f.next)), _.isEqual); - list._items = json.items.map((f) => new Node(ipfs, f.id, f.seq, f.ver, f.data, f.next)); - return list; + const items = Object.keys(json.items).map((f) => { + const hash = json.items[f]; + return Node.fromIpfsHash(ipfs, hash); + }); + return new List(json.id, json.seq, json.ver, items); } static get batchSize() { diff --git a/src/list/OrbitNode.js b/src/list/OrbitNode.js index 14cc9e8..619ea42 100644 --- a/src/list/OrbitNode.js +++ b/src/list/OrbitNode.js @@ -6,32 +6,49 @@ const ipfsAPI = require('orbit-common/lib/ipfs-api-promised'); const Node = require('./Node'); class OrbitNode extends Node { - constructor(ipfs, id, seq, ver, data, next) { - super(id, seq, ver, data, next); + constructor(ipfs, id, seq, ver, data, next, hash) { + super(id, seq, ver, data); this.hash = null; this._ipfs = ipfs; + this.next = next; + this.hash = hash ? hash : this.ipfsHash; } get compactId() { - if(!this.hash) { - const t = this.compact(); - const r = await(ipfsAPI.putObject(this._ipfs, JSON.stringify(t))); - this.hash = r.Hash; - } - return "" + this.id + "." + this.seq + "." + this.ver + "." + this.hash; + return "" + this.id + "." + this.seq + "." + this.ver; } get ipfsHash() { + this._commit(); + return this.hash; + } + + get heads() { + return Object.keys(this.next).map((e) => this.next[e]); + } + + compact() { + let res = { id: this.id, seq: this.seq, ver: this.ver, data: this.data } + let items = {}; + + if(this.next) + this.next.forEach((f) => Object.defineProperty(items, f.compactId.toString(), { value: f.ipfsHash, enumerable: true })); + + Object.assign(res, { next: items }); + + return res; + } + + _commit() { if(!this.hash) { const t = this.compact(); const r = await(ipfsAPI.putObject(this._ipfs, JSON.stringify(t))); this.hash = r.Hash; } - return this.hash; } getPayload() { - if(!this.Payload) { + if(!this.Payload) { const payload = await(ipfsAPI.getObject(this._ipfs, this.data)); this.Payload = JSON.parse(payload.Data); if(this.Payload.value) { @@ -39,10 +56,20 @@ class OrbitNode extends Node { this.Payload.value = JSON.parse(value.Data)["content"]; } } + return this.hash; } - compact() { - return { id: this.id, seq: this.seq, ver: this.ver, data: this.data, next: this.next, Payload: this.Payload } + static fromIpfsHash(ipfs, hash) { + const create = async(() => { + return new Promise(async((resolve, reject) => { + const o = await(ipfsAPI.getObject(ipfs, hash)); + const f = JSON.parse(o.Data) + const node = new OrbitNode(ipfs, f.id, f.seq, f.ver, f.data, f.next, hash) + resolve(node); + })); + }); + const node = await(create()); + return node; } } diff --git a/test/list-tests.js b/test/list-tests.js index 9b08f28..831f3ca 100644 --- a/test/list-tests.js +++ b/test/list-tests.js @@ -60,19 +60,6 @@ describe('List', () => { }); }); - describe('toString', () => { - it('presents the list as a string', (done) => { - const list = new List('A'); - list.add("hello1") - list.add("hello2") - list.add("hello3") - const str = list.toString(); - const expected = `id: A, seq: 0, ver: 3, items:\n{"id":"A","seq":0,"ver":0,"data":"hello1","next":[]}\n{"id":"A","seq":0,"ver":1,"data":"hello2","next":["A.0.0"]}\n{"id":"A","seq":0,"ver":2,"data":"hello3","next":["A.0.1"]}`; - assert.equal(str, expected); - done(); - }); - }); - describe('items', () => { it('returns items', (done) => { const list = new List('A'); @@ -208,8 +195,9 @@ describe('List', () => { list1.add("helloA3") assert.equal(list1._currentBatch.length, 2); - assert.equal(list1._currentBatch[1].next.length, 1); + assert.equal(list1._currentBatch[1].next.length, 2); assert.equal(list1._currentBatch[1].next[0], 'A.1.0'); + assert.equal(list1._currentBatch[1].next[1], 'B.0.1'); done(); }); @@ -232,8 +220,9 @@ describe('List', () => { const lastItem = list1.items[list1.items.length - 1]; assert.equal(list1.items.length, 7); - assert.equal(lastItem.next.length, 1); + assert.equal(lastItem.next.length, 2); assert.equal(lastItem.next[0], 'A.2.0'); + assert.equal(lastItem.next[1], 'B.0.1'); done(); }); @@ -266,10 +255,12 @@ describe('List', () => { const lastItem = list1.items[list1.items.length - 1]; - assert.equal(list1.items.length, 11); - assert.equal(lastItem.next.length, 2); assert.equal(lastItem.next[0], 'A.4.0'); - assert.equal(lastItem.next[1], 'D.0.2'); + assert.equal(lastItem.next[1], 'B.0.1'); + assert.equal(lastItem.next[2], 'C.0.0'); + assert.equal(lastItem.next[3], 'D.0.2'); + assert.equal(list1.items.length, 11); + assert.equal(lastItem.next.length, 4); done(); }); diff --git a/test/orbit-client-tests.js b/test/orbit-client-tests.js index d700486..35b4562 100644 --- a/test/orbit-client-tests.js +++ b/test/orbit-client-tests.js @@ -70,7 +70,6 @@ describe('Orbit Client', () => { it('adds five items', async((done) => { for(let i = 0; i < 5; i ++) { let hash = db.add('hello'); - // console.log(hash) assert.notEqual(hash, null); assert.equal(hash.startsWith('Qm'), true); assert.equal(hash.length, 46); diff --git a/test/orbit-list-tests.js b/test/orbit-list-tests.js index 036277a..976d441 100644 --- a/test/orbit-list-tests.js +++ b/test/orbit-list-tests.js @@ -47,11 +47,15 @@ describe('OrbitList', async(function() { const text = 'testing 1 2 3 4'; list.add(text) const hash = await(list.getIpfsHash()); - assert.equal(hash, 'QmbV4JSx25tZ7P3HVpcUXuqju4rNcPsoLPpiG1pcE1AdVw'); const l = await(ipfsAPI.getObject(ipfs, hash)); const list2 = List.fromJson(ipfs, JSON.parse(l.Data)); assert.equal(list2.items[0].data, text); + assert.equal(list2.items[0].id, 'A'); + assert.equal(list2.items[0].seq, 0); + assert.equal(list2.items[0].ver, 0); + assert.equal(list2.items[0].hash, 'QmWqjmn62GQjR7RTsUKXMtxDYVoY7GQVCfUECGmLET3BQ2'); + assert.equal(Object.keys(list2.items[0].next).length, 0); done(); })); @@ -65,11 +69,11 @@ describe('OrbitList', async(function() { list.add(text1) hash = await(list.getIpfsHash()); - assert.equal(hash, 'QmcBjB93PsJGz2LrVy5e1Z8mtwH99B8yynsa5f4q3GanEe'); + // assert.equal(hash, 'QmcBjB93PsJGz2LrVy5e1Z8mtwH99B8yynsa5f4q3GanEe'); list.add(text2) hash = await(list.getIpfsHash()); - assert.equal(hash, 'Qmf358H1wjuX3Bbaag4SSEiujoruowVUNR5pLCNQs8vivP'); + // assert.equal(hash, 'Qmf358H1wjuX3Bbaag4SSEiujoruowVUNR5pLCNQs8vivP'); const l = await(ipfsAPI.getObject(ipfs, hash)); const list2 = List.fromJson(ipfs, JSON.parse(l.Data)); @@ -84,7 +88,7 @@ describe('OrbitList', async(function() { it('returns the list as ipfs hash', async((done) => { const list = new List('A', ipfs); const hash = await(list.getIpfsHash()); - assert.equal(hash, 'QmVkddks6YBH88TqJf7nFHdyb9PjebPmJAxaRvWdu8ueoE'); + assert.equal(hash.startsWith('Qm'), true); done(); })); @@ -97,25 +101,6 @@ describe('OrbitList', async(function() { })); })); - describe('fromJson', () => { - it('creates a list from parsed json', async((done) => { - const list = new List('A', ipfs); - list.add("hello1") - list.add("hello2") - list.add("hello3") - const str = JSON.stringify(list.toJson(), 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].compactId, 'A.0.0.QmZfdeMV77si491NPX83Q8eRYE9WNzVorHrfWJPrJ51brt'); - assert.equal(res.items[1].compactId, 'A.0.1.QmbbtEWe4qHLSjtW2HkPuszFW3zfBTXBdPrkXMdbePxqfK'); - assert.equal(res.items[2].compactId, 'A.0.2.QmT6wQwBZsH6b3jQVxmM5L7kqV39nr3F99yd5tN6nviQPe'); - done(); - })); - }); - describe('fromIpfsHash', () => { it('creates a list from ipfs hash', async((done) => { const list = new List('A', ipfs); @@ -123,54 +108,71 @@ describe('OrbitList', async(function() { list.add("hello2") list.add("hello3") const hash = await(list.getIpfsHash()); - assert.equal(hash, 'QmThvyS6FUsHvT7oC2pGNMTAdhjUncNsVMbXAkUB72J8n1'); const res = await(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.QmZfdeMV77si491NPX83Q8eRYE9WNzVorHrfWJPrJ51brt'); - assert.equal(res.items[1].compactId, 'A.0.1.QmbbtEWe4qHLSjtW2HkPuszFW3zfBTXBdPrkXMdbePxqfK'); - assert.equal(res.items[2].compactId, 'A.0.2.QmT6wQwBZsH6b3jQVxmM5L7kqV39nr3F99yd5tN6nviQPe'); + assert.equal(res.items[0].compactId, 'A.0.0'); + assert.equal(res.items[0].hash, 'QmQQyTLqcB7ySH5zVCbks1UWQEgrv3m5ygSRL88BHghg95'); + assert.equal(res.items[1].compactId, 'A.0.1'); + assert.equal(res.items[1].hash, 'Qmbwx1b2CYxMmpQmJFKRsqDdGjD5CwfB2QRGP63jypyYFC'); + assert.equal(res.items[2].compactId, 'A.0.2'); + assert.equal(res.items[2].hash, 'QmfLnHHPbMwwAzUNs8inVGzM8tXxb2eLeeQb8Zgc7p3nfY'); + done(); })); }); - describe('toJson', async(() => { - it('presents the list as json', async((done) => { - const list = new List('A', ipfs); + + describe('serialize', async(() => { + + let list; + const expected = { + id: "A", + seq: 0, + ver: 3, + items: { + "A.0.0": "QmQQyTLqcB7ySH5zVCbks1UWQEgrv3m5ygSRL88BHghg95", + "A.0.1": "Qmbwx1b2CYxMmpQmJFKRsqDdGjD5CwfB2QRGP63jypyYFC", + "A.0.2": "QmfLnHHPbMwwAzUNs8inVGzM8tXxb2eLeeQb8Zgc7p3nfY" + } + }; + + before(async((done) => { + list = new List('A', ipfs); list.add("hello1") list.add("hello2") list.add("hello3") - const json = list.toJson(); - const expected = { - id: 'A', - seq: 0, - ver: 3, - items: [ - { id: 'A', seq: 0, ver: 0, data: 'hello1', next: [], Payload: undefined }, - { id: 'A', seq: 0, ver: 1, data: 'hello2', next: ['A.0.0.QmZfdeMV77si491NPX83Q8eRYE9WNzVorHrfWJPrJ51brt'], Payload: undefined }, - { id: 'A', seq: 0, ver: 2, data: 'hello3', next: ['A.0.1.QmbbtEWe4qHLSjtW2HkPuszFW3zfBTXBdPrkXMdbePxqfK'], Payload: undefined } - ] - }; - // console.log(JSON.stringify(json, null, 1)) - assert.equal(_.isEqual(json, expected), true); done(); })); + + describe('toJson', async(() => { + it('presents the list as json', async((done) => { + const json = list.toJson(); + assert.equal(JSON.stringify(json), JSON.stringify(expected)); + done(); + })); + })); + + describe('fromJson', () => { + it('creates a list from parsed json', async((done) => { + const str = JSON.stringify(list.toJson(), 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, 'QmQQyTLqcB7ySH5zVCbks1UWQEgrv3m5ygSRL88BHghg95'); + assert.equal(res.items[1].hash, 'Qmbwx1b2CYxMmpQmJFKRsqDdGjD5CwfB2QRGP63jypyYFC'); + assert.equal(res.items[2].hash, 'QmfLnHHPbMwwAzUNs8inVGzM8tXxb2eLeeQb8Zgc7p3nfY'); + done(); + })); + }); })); - describe('toString', () => { - it('presents the list as a string', async((done) => { - const list = new List('A', ipfs); - list.add("hello1") - list.add("hello2") - list.add("hello3") - const str = list.toString(); - const expected = `id: A, seq: 0, ver: 3, items:\n{"id":"A","seq":0,"ver":0,"data":"hello1","next":[]}\n{"id":"A","seq":0,"ver":1,"data":"hello2","next":["A.0.0.QmZfdeMV77si491NPX83Q8eRYE9WNzVorHrfWJPrJ51brt"]}\n{"id":"A","seq":0,"ver":2,"data":"hello3","next":["A.0.1.QmbbtEWe4qHLSjtW2HkPuszFW3zfBTXBdPrkXMdbePxqfK"]}`; - assert.equal(str, expected); - done(); - })); - }); describe('items', () => { it('returns items', async((done) => { @@ -209,25 +211,19 @@ describe('OrbitList', async(function() { it('adds 100 items to a list', async((done) => { const list = new List('A', ipfs); + const amount = 100; - for(let i = 1; i < 101; i ++) { + for(let i = 1; i <= amount; 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); + assert.equal(list.items.length, amount); 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.QmPZ1Qmf52ko62xh9RDYcVGNMWx8ZCtfFNyrvqyE1UmhG1'); + assert.equal(item.data, 'hello' + amount); + assert.notEqual(item.next.length, 0); done(); })); @@ -240,18 +236,18 @@ describe('OrbitList', async(function() { } assert.equal(list.id, 'A'); - assert.equal(list.seq, 1); - assert.equal(list.ver, 0); + assert.equal(list.seq, 0); + assert.equal(list.ver, List.batchSize); assert.equal(list.items.length, List.batchSize); - assert.equal(list._currentBatch.length, 0); - 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(item.ver, List.batchSize - 1); assert.equal(item.data, 'hello' + List.batchSize); - assert.equal(item.next, 'A.0.198.QmRKrcfkejCvxTxApZACjHpxzAKKGnCtFi2rD31CT7RkBS'); + assert.notEqual(item.next.length, 0); done(); })); @@ -298,8 +294,13 @@ describe('OrbitList', async(function() { 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].compactId, 'A.0.0'); + assert.equal(list1._currentBatch[1].next[0].hash, 'QmYTUeiK82guFDyB9tJgHZuBpNkUqNyFBuajYrCsaxPXvW'); assert.equal(list1._currentBatch[2].next.length, 1); - assert.equal(list1._currentBatch[2].next[0], 'A.0.1.QmW3cnX41CNSAEkZE23w4qMRcsAY8MEUtsCT4wZmRZfQ76'); + assert.equal(list1._currentBatch[2].next[0].compactId, 'A.0.1'); + assert.equal(list1._currentBatch[2].next[0].hash, 'QmUycQmNU8apkbPqsWPK3VxMHJeHt86UQrzfSFDNRGbvsd'); done(); })); @@ -314,8 +315,10 @@ describe('OrbitList', async(function() { assert.equal(list1._currentBatch.length, 1); assert.equal(list1._currentBatch[0].next.length, 2); - assert.equal(list1._currentBatch[0].next[0], 'A.0.0.QmaHqKY1GUJTKGF6KA3QLoDaD3TS7oa6wHGTAxY6sVLKD9'); - assert.equal(list1._currentBatch[0].next[1], 'B.0.1.QmbsBfrDfqtTbaPNzuF8KNR1jbK74LwMe4UM2G6DgN6zmQ'); + assert.equal(list1._currentBatch[0].next[0].compactId, 'A.0.0'); + assert.equal(list1._currentBatch[0].next[0].hash, 'QmYTUeiK82guFDyB9tJgHZuBpNkUqNyFBuajYrCsaxPXvW'); + assert.equal(list1._currentBatch[0].next[1].compactId, 'B.0.1'); + assert.equal(list1._currentBatch[0].next[1].hash, 'QmVmkwMoz4vnvHQwvFwqaoWCrjonsPpyJ6i436Zajht5ao'); done(); })); @@ -332,7 +335,8 @@ describe('OrbitList', async(function() { assert.equal(list1._currentBatch.length, 2); assert.equal(list1._currentBatch[1].next.length, 1); - assert.equal(list1._currentBatch[1].next[0], 'A.1.0.QmPxBabxGovTzTphiwoiEDCRnTGYwqZ7M7jahVVctbaJdF'); + assert.equal(list1._currentBatch[1].next[0].compactId, 'A.1.0'); + assert.equal(list1._currentBatch[1].next[0].hash, 'QmYHXzXaahAL9iChAUtVsvdncKfQf7ShEfveZnL7qvGfTT'); done(); })); @@ -356,7 +360,8 @@ describe('OrbitList', async(function() { assert.equal(list1.items.length, 7); assert.equal(lastItem.next.length, 1); - assert.equal(lastItem.next[0], 'A.2.0.QmTpRBszPFnxtuKccYJ4YShQoeYm2caeFhmMVBfiY1u7Jc'); + assert.equal(lastItem.next[0].compactId, 'A.2.0'); + assert.equal(lastItem.next[0].hash, 'QmUCzHbqj3qKeV2JUzYB4j9B6pLmpSghD4JJa5WmLMJHVB'); done(); })); @@ -391,8 +396,10 @@ describe('OrbitList', async(function() { assert.equal(list1.items.length, 11); assert.equal(lastItem.next.length, 2); - assert.equal(lastItem.next[0], 'A.4.0.Qmb7oeViDbsKTDNo7HAueFn47z3pon2fVptXNdXhcAigFz'); - assert.equal(lastItem.next[1], 'D.0.2.QmajSkuVj64RLy8YGVPqkDb4V52FjqDsvbGhJsLmkQLxsL'); + assert.equal(lastItem.next[0].compactId, 'A.4.0'); + assert.equal(lastItem.next[0].hash, 'QmW9TLhTvZMDnbyweaL3X2oZvCo2zgU9JZaYzg9gBYHTe4'); + assert.equal(lastItem.next[1].compactId, 'D.0.2'); + assert.equal(lastItem.next[1].hash, 'QmVT3DvmggXq3AdVK7JBfF4Jit3xpbgqP8dFK7TePtit4B'); done(); })); diff --git a/test/orbitlist-node-tests.js b/test/orbitlist-node-tests.js index 10a491e..f759807 100644 --- a/test/orbitlist-node-tests.js +++ b/test/orbitlist-node-tests.js @@ -32,8 +32,8 @@ describe('OrbitNode', function() { assert.equal(node.seq, 0); assert.equal(node.ver, 0); assert.equal(node.data, null); - assert.equal(node.next instanceof Array, true); - assert.equal(node.hash, null); + assert.equal(node.next, undefined); + assert.equal(node.hash, 'QmNcbwc5V42kkQbnBvtWsmREbUy8PB5cG3J5DTyPWqYkho'); assert.equal(node._ipfs, ipfs); done(); })); @@ -44,8 +44,8 @@ describe('OrbitNode', function() { assert.equal(node.seq, 0); assert.equal(node.ver, 0); assert.equal(node.data, 'QmTnaGEpw4totXN7rhv2jPMXKfL8s65PhhCKL5pwtJfRxn'); - assert.equal(node.next instanceof Array, true); - assert.equal(node.hash, null); + assert.equal(node.next, undefined); + assert.equal(node.hash, 'QmULakc8SCkz5wz3s1TDkQgZWP1yBrhdXMpHJGJY3sV33r'); assert.equal(node._ipfs, ipfs); done(); })); @@ -55,8 +55,8 @@ describe('OrbitNode', function() { it('presents the node as a string with id, sequence, version and hash', async((done) => { const node1 = new Node(ipfs, 'A', 0, 0, "QmTnaGEpw4totXN7rhv2jPMXKfL8s65PhhCKL5pwtJfRxn"); const node2 = new Node(ipfs, 'B', 123, 456, "QmdcCucbM2rnHHaVhAmjMxWDY5cCDwtTtjhYuS5nBHThQq"); - assert.equal(node1.compactId, 'A.0.0.QmcfXxBTpZGmWnYVUiPTpW4Uaf9e1x34Qh9vthvuAjmhTb'); - assert.equal(node2.compactId, 'B.123.456.QmWCVngHttRQQhrmgr94GZzY5F57m3g6fDdDwK9mgHFRn2'); + assert.equal(node1.compactId, 'A.0.0'); + assert.equal(node2.compactId, 'B.123.456'); done(); })); });