diff --git a/examples/pubsubKeyValue.js b/examples/pubsubKeyValue.js new file mode 100644 index 0000000..746daf9 --- /dev/null +++ b/examples/pubsubKeyValue.js @@ -0,0 +1,52 @@ +'use strict'; + +var async = require('asyncawait/async'); +var await = require('asyncawait/await'); +var OrbitClient = require('../src/OrbitClient'); +var Timer = require('./Timer'); + +// var host = '178.62.229.175'; +var host = 'localhost'; +var port = 6379; + +var username = 'LambOfGod'; +var password = ''; + +let run = (async(() => { + try { + var orbit = OrbitClient.connect(host, port, username, password); + const c1 = 'c1'; + const channel = orbit.channel(c1); + + let count = 1; + let id = 'Log: Query ' + let running = false; + + setInterval(async(() => { + if(!running) { + running = true; + + // let timer = new Timer(true); + channel.put("lamb", "of god" + count); + // console.log(`Query #${count} took ${timer.stop(true)} ms\n`); + let v = channel.get("lamb"); + + console.log("---------------------------------------------------") + console.log("Id | Seq | Ver | Data") + console.log("---------------------------------------------------") + console.log(v); + console.log("---------------------------------------------------") + + running = false; + count ++; + } + }), 500); + + } catch(e) { + console.error("error:", e); + console.error(e.stack); + process.exit(1); + } +}))(); + +module.exports = run; diff --git a/examples/pubsubReader.js b/examples/pubsubReader.js index d83641b..3653ba4 100644 --- a/examples/pubsubReader.js +++ b/examples/pubsubReader.js @@ -27,16 +27,18 @@ let run = (async(() => { running = true; // let timer = new Timer(true); - channel.add(id + count); + channel.add("Hello " + count); // console.log(`Query #${count} took ${timer.stop(true)} ms\n`); - // console.log("Query..."); const c = channel.iterator({ limit: -1 }).collect().length; let items = channel.iterator({ limit: 5 }).collect(); + // console.log(items); console.log("---------------------------------------------------") - console.log("Id | Seq | Ver | Data") + // console.log("Id | Seq | Ver | Data") + console.log("Key | Value") console.log("---------------------------------------------------") - console.log(items.map((e) => `${e.id} | ${e.seq} | ${e.ver} | ${e.data}`).join("\n")); + // console.log(items.map((e) => `${e.id} | ${e.seq} | ${e.ver} | ${e.data}`).join("\n")); + console.log(items.map((e) => `${e.payload.key} | ${e.payload.value}`).join("\n")); console.log("---------------------------------------------------") console.log(`Found ${items.length} items from ${c}\n`); diff --git a/examples/pubsubWriter.js b/examples/pubsubWriter.js index 067234a..6270e0c 100644 --- a/examples/pubsubWriter.js +++ b/examples/pubsubWriter.js @@ -16,14 +16,14 @@ let run = (async(() => { try { var orbit = OrbitClient.connect(host, port, username, password); const c1 = 'c1'; + let channel; let count = 1; let id = 'Log: Query ' - let channel; setInterval(async(() => { if(channel) { - channel.add(id + count); + channel.add(username + " " + count); count ++; } }), process.argv[3] ? process.argv[3] : 1000); diff --git a/src/DataStore.js b/src/DataStore.js new file mode 100644 index 0000000..28ccacf --- /dev/null +++ b/src/DataStore.js @@ -0,0 +1,102 @@ +'use strict'; + +const _ = require('lodash'); +const async = require('asyncawait/async'); +const await = require('asyncawait/await'); +const ipfsAPI = require('orbit-common/lib/ipfs-api-promised'); +const OrbitList = require('./list/OrbitList'); +const HashCacheOps = require('./HashCacheOps'); + +const DefaultAmount = 1; + +class DataStore { + constructor(id, ipfs) { + this._ipfs = ipfs; + this.list = new OrbitList(id, this._ipfs); + } + + add(hash) { + this.list.add(hash); + } + + join(other) { + this.list.join(other); + } + + clear() { + this.list.clear(); + } + + get(options) { + return this._fetchRecursive(options); + } + + _fetchOne(index) { + const item = this.list.items[this.list.items.length - index - 1]; + if(item) { + await(item.getPayload()); + const f = item.compact(); + return { hash: f.data, payload: f.Payload }; + } + return null; + } + + _fetchRecursive(options, currentAmount, deleted, res) { + // console.log("-->") + // console.log("opts:", options, currentAmount) + const opts = { + amount: options && options.amount ? options.amount : DefaultAmount, + first: options && options.first ? options.first : null, + last: options && options.last ? options.last : null, + key: options && options.key ? options.key : null + }; + + let result = res ? res : []; + let handledItems = deleted ? deleted : []; + + if(!currentAmount) currentAmount = 0; + + const item = this._fetchOne(currentAmount); + // console.log("ITEM", item) + + if(item && item.payload) { + const wasHandled = _.includes(handledItems, item.payload.key); + 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)))) + { + // console.log("PUSH!", item, currentAmount, result.length); + result.push(item); + handledItems.push(item.payload.key); + } + } else if(item.payload.op === HashCacheOps.Delete) { + // console.log("DELETE!", item); + handledItems.push(item.payload.key); + } + + currentAmount ++; + + if(opts.key && item.payload.key === opts.key) + return result; + + // console.log("ITEM", item.payload.key, opts.last) + if(opts.last && item.payload.key === opts.last) + return result; + + if(!opts.last && opts.amount > -1 && result.length >= opts.amount) + return result; + + if(currentAmount >= this.list.items.length) + return result; + + // console.log("RES!", result) + result = this._fetchRecursive(opts, currentAmount, handledItems, result); + } + + return result; + + } +} + +module.exports = DataStore; diff --git a/src/HashCacheItem.js b/src/HashCacheItem.js index d4c63e1..838091f 100644 --- a/src/HashCacheItem.js +++ b/src/HashCacheItem.js @@ -2,6 +2,15 @@ const Encryption = require('orbit-common/lib/Encryption'); +class OrbitDBItem { + constructor(operation, key, value, metaInfo) { + this.op = operation; + this.key = key; + this.value = value; + this.meta = metaInfo; + } +} + class HashCacheItem { constructor(operation, key, sequenceNumber, targetHash, metaInfo, next) { this.op = operation; @@ -55,6 +64,7 @@ class EncryptedHashCacheItem extends HashCacheItem { } module.exports = { + OrbitDBItem: OrbitDBItem, HashCacheItem: HashCacheItem, EncryptedHashCacheItem: EncryptedHashCacheItem }; diff --git a/src/OrbitClient.js b/src/OrbitClient.js index 689db56..ee7470a 100644 --- a/src/OrbitClient.js +++ b/src/OrbitClient.js @@ -6,56 +6,54 @@ var Keystore = require('orbit-common/lib/Keystore'); var Encryption = require('orbit-common/lib/Encryption'); var ipfsDaemon = require('orbit-common/lib/ipfs-daemon'); var ipfsAPI = require('orbit-common/lib/ipfs-api-promised'); -var HashCache = require('./HashCacheClient'); -var HashCacheItem = require('./HashCacheItem').EncryptedHashCacheItem; +var HashCacheItem = require('./HashCacheItem').HashCacheItem; +var OrbitDBItem = require('./HashCacheItem').OrbitDBItem; var HashCacheOps = require('./HashCacheOps'); var ItemTypes = require('./ItemTypes'); var MetaInfo = require('./MetaInfo'); var Post = require('./Post'); var Aggregator = require('./Aggregator'); var PubSub = require('./PubSub'); -const List = require('./list/OrbitList'); var Timer = require('../examples/Timer'); +const List = require('./list/OrbitList'); +const DataStore = require('./DataStore'); var pubkey = Keystore.getKeys().publicKey; var privkey = Keystore.getKeys().privateKey; -let vvv = {}; - class OrbitClient { constructor(ipfs) { - this.ipfs = ipfs; + this._ipfs = ipfs; this.network = {}; this.user = null; - this.list = null; // TODO move to DataStore } channel(hash, password) { if(password === undefined) password = ''; this._pubsub.subscribe(hash, password, async((hash, message) => { - const other = await(List.fromIpfsHash(this.ipfs, message)); + const other = await(List.fromIpfsHash(this._ipfs, message)); // console.log(">", other.id, other.seq, other.ver); if(other.id !== this.user.username) { - let timer = new Timer(true); - this.list.join(other); // TODO: move to DataStore - console.log(`Join took ${timer.stop(true)} ms`); + // let timer = new Timer(true); + this._store.join(other); + // console.log(`Join took ${timer.stop(true)} ms`); } })); return { - info: (options) => this._info(hash, password), + // info: (options) => this._info(hash, password), delete: () => this._deleteChannel(hash, password), iterator: (options) => this._iterator(hash, password, options), setMode: (mode) => this._setMode(hash, password, mode), add: (data) => this._add(hash, password, data), - //TODO: tests - del: (options) => this._remove(hash, password, options), + del: (key) => this._remove(hash, password, key), put: (key, data) => this._put(hash, password, key, data), get: (key, options) => { const items = this._iterator(hash, password, { key: key }).collect(); - return items[0] ? items[0].item.Payload : null; + return items[0] ? items[0].payload.value : null; }, + //TODO: tests leave: () => this._pubsub.unsubscribe(hash) } } @@ -95,72 +93,45 @@ class OrbitClient { const reverse = options.reverse ? options.reverse : false; const key = options.key ? options.key : null; - let startFromHash; - if(lte || lt) { - startFromHash = lte ? lte : lt; - } else { - var channel = this._info(channel, password); - startFromHash = channel.head ? channel.head : null; - } - if((gt || lt) && limit > -1) limit += 1; - if(startFromHash) { - const opts = { - amount: limit, - last: gte ? gte : gt, - key: key - }; + const opts = { + amount: limit, + first: lte ? lte : lt, + last: gte ? gte : gt, + key: key + }; - // Get messages - // messages = Aggregator.fetchRecursive(this.ipfs, startFromHash, password, opts); - messages = this.list.items.map((f) => f.compact()); // TODO: move to DataStore + // Get messages + messages = await(this._store.get(opts)); + // console.log("M", messages) - // Slice the array - let startIndex = 0; - let endIndex = messages.length; - if(limit < 0) { - endIndex = messages.length - (gt ? 1 : 0); - } else { - startIndex = Math.max(0, messages.length - limit); - endIndex = messages.length - ((gt || lt) ? 1 : 0); - } + // Remove the first/last item if greater/lesser than is set + let startIndex = lt ? 1 : 0; + let endIndex = gt ? messages.length - 1 : messages.length; + messages = messages.slice(startIndex, endIndex) + // console.log("M2", messages) - messages = messages.slice(startIndex, endIndex) - } - - if(reverse) messages.reverse(); + if(!reverse) messages.reverse(); return messages; } _publish(data) { let post = new Post(data); - post.encrypt(privkey, pubkey); - return await (ipfsAPI.putObject(this.ipfs, JSON.stringify(post))); + // post.encrypt(privkey, pubkey); + return await (ipfsAPI.putObject(this._ipfs, JSON.stringify(post))); } - _createMessage(channel, password, operation, key, target) { + _createMessage(channel, password, operation, key, value) { // Create meta info const size = -1; const metaInfo = new MetaInfo(ItemTypes.Message, size, this.user.id, new Date().getTime()); - - // Get the current channel head and bump the sequence number - let seq = this._info(channel, password).seq + 1; - let head = this._info(channel, password).head; - // Create the hash cache item - const hcItem = new HashCacheItem(operation, key, seq, target, metaInfo, null, pubkey, privkey, password); - + const item = new OrbitDBItem(operation, key, value, metaInfo); // Save the item to ipfs - const data = await (ipfsAPI.putObject(this.ipfs, JSON.stringify(hcItem))); - let newHead = { Hash: data.Hash }; - - // If this is not the first item in the channel, patch with the previous (ie. link as next) - if(seq > 0) - newHead = await (ipfsAPI.patchObject(this.ipfs, data.Hash, head)); - - return { hash: newHead, seq: seq }; + const data = await (ipfsAPI.putObject(this._ipfs, JSON.stringify(item))); + return data.Hash; } /* DB Operations */ @@ -175,48 +146,46 @@ class OrbitClient { return await(this._createOperation(channel, password, HashCacheOps.Put, key, post.Hash)); } - _remove(channel, password, options) { - const key = null; - const target = options.key ? options.key : (options.hash ? options.hash : null); - return await(this._createOperation(channel, password, HashCacheOps.Delete, key, target)); + _remove(channel, password, hash) { + return await(this._createOperation(channel, password, HashCacheOps.Delete, hash, null)); } _createOperation(channel, password, operation, key, value, data) { - let message = this._createMessage(channel, password, operation, key, value); - this.list.add(message.hash.Hash); // TODO: move to DataStore - const listHash = await(this.list.getIpfsHash()); + let hash = this._createMessage(channel, password, operation, key, value); + this._store.add(hash); + const listHash = await(this._store.list.getIpfsHash()); await(this._pubsub.publish(channel, listHash)); - return message.hash.Hash; + return key; } _deleteChannel(channel, password) { - this._pubsub.delete(channel, password); + this._store.clear(); return true; } - _setMode(channel, password, modes) { - let m = []; - if(typeof modes !== 'Array') - m.push(modes); - else - m = modes; - // const res = await(this.client.linkedList(channel, password).setMode(m)); - // return res.modes; - return { todo: 'TODO!' } - } + // _setMode(channel, password, modes) { + // let m = []; + // if(typeof modes !== 'Array') + // m.push(modes); + // else + // m = modes; + // // const res = await(this.client.linkedList(channel, password).setMode(m)); + // // return res.modes; + // return { todo: 'TODO!' } + // } - _info(channel, password) { - var l = this._pubsub.latest(channel); - return l; - } + // _info(channel, password) { + // var l = this._pubsub.latest(channel); + // return l; + // } _connect(host, port, username, password) { return new Promise((resolve, reject) => { - this._pubsub = new PubSub(this.ipfs, host, port, username, password); + this._pubsub = new PubSub(this._ipfs, host, port, username, password); // this.client = this._pubsub._client; // this.user = this.client.info.user; this.user = { id: 'hello-todo', username: username } - this.list = new List(username, this.ipfs); // TODO: move to DataStore + this._store = new DataStore(username, this._ipfs); resolve(); // this.network = { // id: this.client.info.networkId, diff --git a/src/PubSub.js b/src/PubSub.js index 2625f7f..5cfe961 100644 --- a/src/PubSub.js +++ b/src/PubSub.js @@ -1,9 +1,6 @@ 'use strict'; -var async = require('asyncawait/async'); -var await = require('asyncawait/await'); -var redis = require("redis"); -var Aggregator = require('./Aggregator'); +const redis = require("redis"); const List = require('./list/OrbitList'); class Pubsub2 { @@ -13,17 +10,13 @@ class Pubsub2 { this.client1 = redis.createClient({ host: host, port: port }); this.client2 = redis.createClient({ host: host, port: port }); this.client1.on("message", this._handleMessage.bind(this)); - this.client1.on('connect', () => { - console.log('redis connected'); - }); - // this.client1.on("subscribe", (channel, count) => { - // }); + // this.client1.on('connect', () => console.log('redis connected')); + // this.client1.on("subscribe", (channel, count) => console.log(`subscribed to ${channel}`)); } subscribe(hash, password, callback) { if(!this._subscriptions[hash] || this._subscriptions[hash].password !== password) { this._subscriptions[hash] = { - topic: hash, password: password, head: null, callback: callback @@ -46,10 +39,6 @@ class Pubsub2 { return { head: this._subscriptions[hash] ? this._subscriptions[hash].head : null }; } - delete(hash, password) { - delete this._subscriptions[hash]; - } - _handleMessage(hash, message) { if(this._subscriptions[hash]) { this._subscriptions[hash].head = message; @@ -60,127 +49,4 @@ class Pubsub2 { } } -/* -class PubSub { - constructor(ipfs, host, port, username, password, resolve) { - this.ipfs = ipfs; - this._subscriptions = {}; - this.client1 = redis.createClient({ host: host, port: port }); - this.client2 = redis.createClient({ host: host, port: port }); - this.client3 = redis.createClient({ host: host, port: port }); - this.client1.on("message", this._handleMessage.bind(this)); - this.publishQueue = []; - - this.client1.on('connect', function() { - console.log('redis connected'); - resolve(); - }); - - this.client1.on("subscribe", function (channel, count) { - console.log("subscribed to pubsub topic '" + channel + "' (" + count + " peers)"); - }); - } - - subscribe(hash, password, head, callback) { - if(!this._subscriptions[hash] || this._subscriptions[hash].password !== password) { - this._subscriptions[hash] = { - topic: hash, - password: password, - head: null, - callback: callback, - seq: -1 - }; - // this.client3.get("orbit." + hash, (err, reply) => { - // if(reply) { - // let d = JSON.parse(reply); - // this._subscriptions[hash].seq = d.seq; - // this._subscriptions[hash].head = d.head; - // if(err) console.log(err); - // console.log(`head of '${hash}' is`, this._subscriptions[hash].head, "seq:", this._subscriptions[hash].seq); - // } - // }); - this.client1.subscribe(hash); - this.client2.publish(hash, JSON.stringify({ r: "HEAD" })); - } - - return new Promise((resolve, reject) => { - setTimeout(() => { - console.log("pubsub initialized") - resolve(); - }, 1000); - }); - } - - unsubscribe(hash) { - delete this._subscriptions[hash]; - this.client1.unsubscribe(); - this.client2.unsubscribe(); - } - - publish(hash, message, seq, callback) { - return new Promise((resolve, reject) => { - if(this.publishQueue.length === 0) { - this.publishQueue.splice(0, 0, { hash: message.Hash, callback: resolve }); - console.log("...") - this.client2.publish(hash, JSON.stringify({ hash: message.Hash, seq: seq })); - console.log("published") - } else { - console.log("queue full!") - resolve(false); - } - }); - } - - latest(hash) { - return { head: this._subscriptions[hash].head, modes: {}, seq: this._subscriptions[hash].seq }; - } - - delete(hash, password) { - delete this._subscriptions[hash]; - } - - _handleMessage(hash, event) { - if(this._subscriptions[hash]) { - var message = JSON.parse(event) - if(message.hash) { - var newHead = message.hash; - var seq = message.seq; - var isNewer = seq > this._subscriptions[hash].seq; - var item = this.publishQueue[this.publishQueue.length - 1]; - - // console.log(".", newHead, item ? item.hash : '', seq, this._subscriptions[hash].seq, isNewer) - - if(item) { - this.publishQueue.pop(); - item.callback(isNewer && newHead === item.hash); - } - - if(isNewer) - this._updateSubscription(hash, newHead, seq); - } else if(message.r === 'HEAD') { - console.log("SEND HEAD!") - this.client2.publish(hash, JSON.stringify(this.latest(hash))); - } else { - console.log("GOT HEAD!", message) - var isNewer = message.seq > this._subscriptions[hash].seq; - if(isNewer) { - console.log("NEW HEAD!") - this.publishQueue.pop(); - this._updateSubscription(hash, message.head, message.seq); - } - } - } - } - - _updateSubscription(hash, message, seq) { - // this.client3.set("orbit." + hash, JSON.stringify({ head: message, seq: seq })); - this._subscriptions[hash].seq = seq; - this._subscriptions[hash].head = message; - - if(this._subscriptions[hash].callback) - this._subscriptions[hash].callback(hash, message, seq); - } -} -*/ - module.exports = Pubsub2; diff --git a/src/list/OrbitList.js b/src/list/OrbitList.js index 40544ea..627beb3 100644 --- a/src/list/OrbitList.js +++ b/src/list/OrbitList.js @@ -21,6 +21,11 @@ class OrbitList extends List { this.ver ++; } + clear() { + this._items = []; + this._currentBatch = []; + } + getIpfsHash() { return new Promise(async((resolve, reject) => { const list = await(ipfsAPI.putObject(this._ipfs, JSON.stringify(this.toJson()))); @@ -28,6 +33,14 @@ class OrbitList extends List { })); } + static fromIpfsHash(ipfs, hash) { + return new Promise(async((resolve, reject) => { + const l = await(ipfsAPI.getObject(ipfs, hash)); + const list = OrbitList.fromJson(ipfs, JSON.parse(l.Data)); + resolve(list); + })); + } + static fromJson(ipfs, json) { let list = new List(json.id); list.seq = json.seq; @@ -35,14 +48,6 @@ class OrbitList extends List { list._items = _.uniqWith(json.items.map((f) => new Node(ipfs, f.id, f.seq, f.ver, f.data, f.next)), _.isEqual); return list; } - - static fromIpfsHash(ipfs, hash) { - return new Promise(async((resolve, reject) => { - const l = await(ipfsAPI.getObject(ipfs, hash)); - const list = OrbitList.fromJson(ipfs, JSON.parse(l.Data)); - resolve(list); - })); - } } module.exports = OrbitList; diff --git a/src/list/OrbitNode.js b/src/list/OrbitNode.js index 0350e7b..38cfc5d 100644 --- a/src/list/OrbitNode.js +++ b/src/list/OrbitNode.js @@ -1,7 +1,8 @@ 'use strict'; -const ipfsAPI = require('orbit-common/lib/ipfs-api-promised'); +const async = require('asyncawait/async'); const await = require('asyncawait/await'); +const ipfsAPI = require('orbit-common/lib/ipfs-api-promised'); const Node = require('./Node'); class OrbitNode extends Node { @@ -20,8 +21,24 @@ class OrbitNode extends Node { return "" + this.id + "." + this.seq + "." + this.ver + "." + this.hash; } + getPayload() { + if(!this.Payload) { + return new Promise(async((resolve, reject) => { + const payload = await(ipfsAPI.getObject(this._ipfs, this.data)); + this.Payload = JSON.parse(payload.Data); + if(this.Payload.value) { + const value = await(ipfsAPI.getObject(this._ipfs, this.Payload.value)); + this.Payload.value = JSON.parse(value.Data)["content"]; + } + resolve(this); + })); + } else { + return this; + } + } + compact() { - return { id: this.id, seq: this.seq, ver: this.ver, data: this.data, next: this.next } + return { id: this.id, seq: this.seq, ver: this.ver, data: this.data, next: this.next, Payload: this.Payload } } } diff --git a/test/orbit-client-tests.js b/test/orbit-client-tests.js index ae3d8df..9437013 100644 --- a/test/orbit-client-tests.js +++ b/test/orbit-client-tests.js @@ -1,580 +1,612 @@ -// 'use strict'; - -// var fs = require('fs'); -// var path = require('path'); -// var assert = require('assert'); -// var async = require('asyncawait/async'); -// var await = require('asyncawait/await'); -// var ipfsDaemon = require('orbit-common/lib/ipfs-daemon'); -// var logger = require('orbit-common/lib/logger'); -// var Server = require('orbit-server/src/server'); -// var OrbitClient = require('../src/OrbitClient'); - -// var serverConfig = { -// networkId: "orbitdb-test", -// networkName: "OrbitDB Test Network", -// salt: "hellothisisdog", -// userDataPath: "/tmp/orbitdb-tests", -// verifyMessages: true -// } - -// // Orbit -// const host = 'localhost'; -// const port = 6379; -// const username = 'testrunner'; -// const password = ''; - -// const startServer = async (() => { -// return new Promise(async((resolve, reject) => { -// logger.setLevel('ERROR'); -// const ipfsd = await(ipfsDaemon()); -// const server = Server(ipfsd.daemon, ipfsd.nodeInfo, serverConfig); -// server.app.listen(port, () => { -// resolve(server); -// }).on('error', (err) => { -// resolve(server); -// }); -// })); -// }); - - -// describe('Orbit Client', () => { -// let server, orbit; - -// let head = ''; -// let items = []; -// let channel = 'abcdefgh'; - -// before(async((done) => { -// var initialize = () => new Promise(async((resolve, reject) => { -// orbit = OrbitClient.connect(host, port, username, password); -// orbit.channel(channel, '').delete(); -// resolve(); -// })); -// server = await(startServer()); -// await(initialize()); -// done(); -// })); - -// after(function(done) { -// var deleteChannel = () => new Promise(async((resolve, reject) => { -// if(orbit) orbit.channel(channel, '').delete(); -// resolve(); -// })); -// server.shutdown(); -// server = null; -// deleteChannel().then(done); -// }); - -// /* TESTS */ -// describe('Connect', function() { -// it('connects to hash-cache-server', async((done) => { -// assert.notEqual(orbit, null); -// // assert.notEqual(orbit.client, null); -// // assert.equal(orbit.user.id, 'hello'); -// // assert.equal(orbit.network.id, serverConfig.networkId); -// // assert.equal(orbit.network.name, serverConfig.networkName); -// // assert.notEqual(orbit.network.config.SupernodeRouting, null); -// // assert.notEqual(orbit.network.config.Bootstrap.length, 0); -// done(); -// })); -// }); - -// describe('Info', function() { -// it('gets channel info on empty channel', async((done) => { -// var info = orbit.channel(channel, '').info(); -// assert.notEqual(info, null); -// assert.equal(info.head, null); -// assert.notEqual(info.modes, null); -// done(); -// })); - -// it('gets channel info on an existing channel', async((done) => { -// var msg = orbit.channel(channel, '').add('hello'); -// var info = orbit.channel(channel, '').info(); -// assert.notEqual(info, null); -// assert.notEqual(info.head, null); -// assert.notEqual(info.modes, null); -// assert.equal(info.modes.r, null); -// done(); -// })); - -// // it('gets channel info when channel has modes set', async((done) => { -// // try { -// // orbit.channel(channel).delete(); -// // var mode = { -// // mode: "+r", -// // params: { -// // password: 'password' -// // } -// // }; -// // var res = orbit.channel(channel, '').setMode(mode) -// // var info = orbit.channel(channel, 'password').info(); -// // assert.notEqual(info, null); -// // assert.equal(info.head, null); -// // assert.equal(JSON.stringify(info.modes), JSON.stringify(res)); -// // orbit.channel(channel, 'password').delete(); -// // } catch(e) { -// // orbit.channel(channel, 'password').delete(); -// // assert.equal(e, null); -// // } -// // done(); -// // })); - -// }); - -// describe('Delete', function() { -// it('deletes a channel from the database', async((done) => { -// var result = orbit.channel(channel, '').delete(); -// assert.equal(result, true); -// var iter = orbit.channel(channel, '').iterator(); -// assert.equal(iter.next().value, null); -// done(); -// })); - -// it('deletes a channel with a password', async((done) => { -// done(); -// })); - -// it('doesn\'t delete a channel when password is wrong', async((done) => { -// done(); -// })); - -// it('doesn\'t delete a channel when user is not an op', async((done) => { -// done(); -// })); -// }); - -// describe('Add events', function() { -// it('adds an item to an empty channel', async((done) => { -// try { -// orbit.channel(channel, '').delete(); -// const head = orbit.channel(channel, '').add('hello'); -// assert.notEqual(head, null); -// assert.equal(head.startsWith('Qm'), true); -// assert.equal(head.length, 46); -// } catch(e) { -// assert.equal(e, null); -// } -// done(); -// })); - -// it('adds a new item to a channel with one item', async((done) => { -// try { -// const head = orbit.channel(channel, '').iterator().collect()[0]; -// const second = orbit.channel(channel, '').add('hello'); -// assert.notEqual(second, null); -// assert.notEqual(second, head); -// assert.equal(second.startsWith('Qm'), true); -// assert.equal(second.length, 46); -// } catch(e) { -// assert.equal(e, null); -// } -// done(); -// })); - -// it('adds five items', async((done) => { -// for(var i = 0; i < 5; i ++) { -// try { -// var s = orbit.channel(channel, '').add('hello'); -// assert.notEqual(s, null); -// assert.equal(s.startsWith('Qm'), true); -// assert.equal(s.length, 46); -// } catch(e) { -// assert.equal(e, null); -// } -// } -// done(); -// })); - -// it('adds an item that is > 256 bytes', async((done) => { -// try { -// var msg = new Buffer(512); -// msg.fill('a') -// var s = orbit.channel(channel, '').add(msg.toString()); -// assert.notEqual(s, null); -// assert.equal(s.startsWith('Qm'), true); -// assert.equal(s.length, 46); -// } catch(e) { -// assert.equal(e, null); -// } -// done(); -// })); -// }); - - -// describe('Iterator', function() { -// var items = []; -// var itemCount = 5; - -// before(function(done) { -// var addMessages = () => new Promise(async((resolve, reject) => { -// var result = orbit.channel(channel, '').delete(); -// var iter = orbit.channel(channel, '').iterator(); -// for(var i = 0; i < itemCount; i ++) { -// var s = orbit.channel(channel, '').add('hello' + i); -// items.push(s); -// } -// resolve(); -// })); -// addMessages().then(done); -// }); - -// describe('Defaults', function() { -// it('returns an iterator', async((done) => { -// var iter = orbit.channel(channel, '').iterator(); -// var next = iter.next().value; -// assert.notEqual(iter, null); -// assert.notEqual(next, null); -// assert.notEqual(next.item, null); -// assert.notEqual(next.item.op, null); -// assert.equal(next.item.seq, 4); -// assert.notEqual(next.item.target, null); -// assert.notEqual(next.item.next, null); -// assert.notEqual(next.item.Payload, null); -// assert.equal(next.item.Payload, 'hello4'); -// done(); -// })); - -// it('implements Iterator interface', async((done) => { -// var iter = orbit.channel(channel, '').iterator({ limit: -1 }); -// var messages = []; - -// for(let i of iter) -// messages.push(i.hash); - -// assert.equal(messages.length, items.length); -// done(); -// })); - -// it('returns 1 item as default', async((done) => { -// var iter = orbit.channel(channel, '').iterator(); -// var first = iter.next().value; -// var second = iter.next().value; -// assert.equal(first.item.key, items[items.length - 1]); -// assert.equal(second, null); -// assert.equal(first.item.Payload, 'hello4'); -// done(); -// })); -// }); - -// describe('Collect', function() { -// it('returns all items', async((done) => { -// var iter = orbit.channel(channel, '').iterator({ limit: -1 }); -// var messages = iter.collect(); -// assert.equal(messages.length, items.length); -// assert.equal(messages[messages.length - 1].item.Payload, 'hello0'); -// assert.equal(messages[0].item.Payload, 'hello4'); -// done(); -// })); - -// it('returns 1 item', async((done) => { -// var iter = orbit.channel(channel, '').iterator(); -// var messages = iter.collect(); -// assert.equal(messages.length, 1); -// done(); -// })); - -// it('returns 3 items', async((done) => { -// var iter = orbit.channel(channel, '').iterator({ limit: 3 }); -// var messages = iter.collect(); -// assert.equal(messages.length, 3); -// done(); -// })); -// }); - -// describe('Options: limit', function() { -// it('returns 1 item when limit is 0', async((done) => { -// var iter = orbit.channel(channel, '').iterator({ limit: 0 }); -// var first = iter.next().value; -// var second = iter.next().value; -// assert.equal(first.item.key, items[items.length - 1]); -// assert.equal(second, null); -// done(); -// })); - -// it('returns 1 item when limit is 1', async((done) => { -// var iter = orbit.channel(channel, '').iterator({ limit: 1 }); -// var first = iter.next().value; -// var second = iter.next().value; -// assert.equal(first.item.key, items[items.length - 1]); -// assert.equal(second, null); -// done(); -// })); - -// it('returns 3 items', async((done) => { -// var iter = orbit.channel(channel, '').iterator({ limit: 3 }); -// var first = iter.next().value; -// var second = iter.next().value; -// var third = iter.next().value; -// var fourth = iter.next().value; -// assert.equal(first.item.key, items[items.length - 1]); -// assert.equal(second.item.key, items[items.length - 2]); -// assert.equal(third.item.key, items[items.length - 3]); -// assert.equal(fourth, null); -// done(); -// })); - -// it('returns all items', async((done) => { -// var iter = orbit.channel(channel, '').iterator({ limit: -1 }); -// var messages = iter.collect().map((e) => e.item.key); - -// messages.reverse(); -// assert.equal(messages.length, items.length); -// assert.equal(messages[0], items[0]); -// done(); -// })); - -// it('returns all items when limit is bigger than -1', async((done) => { -// var iter = orbit.channel(channel, '').iterator({ limit: -300 }); -// var messages = iter.collect().map((e) => e.item.key); - -// assert.equal(messages.length, items.length); -// assert.equal(messages[0], items[items.length - 1]); -// done(); -// })); - -// it('returns all items when limit is bigger than number of items', async((done) => { -// var iter = orbit.channel(channel, '').iterator({ limit: 300 }); -// var messages = iter.collect().map((e) => e.item.key); - -// assert.equal(messages.length, items.length); -// assert.equal(messages[0], items[items.length - 1]); -// done(); -// })); -// }); - -// describe('Options: reverse', function() { -// it('returns all items reversed', async((done) => { -// var iter = orbit.channel(channel, '').iterator({ limit: -1, reverse: true }); -// var messages = iter.collect().map((e) => e.item.key); - -// assert.equal(messages.length, items.length); -// assert.equal(messages[0], items[0]); -// done(); -// })); -// }); - -// describe('Options: ranges', function() { -// var all = []; -// var head; - -// before((done) => { -// var fetchAll = () => new Promise(async((resolve, reject) => { -// all = orbit.channel(channel, '').iterator({ limit: -1 }).collect(); -// head = all[0]; -// resolve(); -// })); -// fetchAll().then(done); -// }); - -// describe('gt & gte', function() { -// it('returns 0 items when gt is the head', async((done) => { -// var messages = orbit.channel(channel, '').iterator({ gt: head.hash }).collect(); -// assert.equal(messages.length, 0); -// done(); -// })); - -// it('returns 1 item when gte is the head', async((done) => { -// var iter2 = orbit.channel(channel, '').iterator({ gte: head.hash, limit: -1 }); -// var messages = iter2.collect().map((e) => e.item.key); - -// assert.equal(messages.length, 1); -// assert.equal(messages[0], items[items.length -1]); -// done(); -// })); - -// it('returns 2 item when gte is defined', async((done) => { -// var gte = all[1].hash; -// var iter = orbit.channel(channel, '').iterator({ gte: gte, limit: -1 }); -// var messages = iter.collect().map((e) => e.hash); - -// // console.log(messages, all) -// assert.equal(messages.length, 2); -// assert.equal(messages[0], all[0].hash); -// assert.equal(messages[1], all[1].hash); -// done(); -// })); - -// it('returns all items when gte is the root item', async((done) => { -// var iter = orbit.channel(channel, '').iterator({ gte: all[all.length -1], limit: -1 }); -// var messages = iter.collect().map((e) => e.item.key); - -// assert.equal(messages.length, itemCount); -// assert.equal(messages[0], items[items.length - 1]); -// assert.equal(messages[messages.length - 1], items[0]); -// done(); -// })); - -// it('returns items when gt is the root item', async((done) => { -// var iter = orbit.channel(channel, '').iterator({ gt: all[all.length - 1], limit: -1 }); -// var messages = iter.collect().map((e) => e.item.key); - -// assert.equal(messages.length, itemCount - 1); -// assert.equal(messages[0], items[items.length - 1]); -// assert.equal(messages[3], items[1]); -// done(); -// })); - -// it('returns items when gt is defined', async((done) => { -// var iter = orbit.channel(channel, '').iterator({ limit: -1}); -// var messages = iter.collect().map((e) => e.hash); - -// var gt = messages[2]; -// var iter2 = orbit.channel(channel, '').iterator({ gt: gt, limit: 100 }); -// var messages2 = iter2.collect().map((e) => e.hash); - -// assert.equal(messages2.length, 2); -// assert.equal(messages2[0], messages[0]); -// assert.equal(messages2[1], messages[1]); -// done(); -// })); -// }); - -// describe('lt & lte', function() { -// it('returns one item when lt is the head', async((done) => { -// var iter2 = orbit.channel(channel, '').iterator({ lt: head.hash }); -// var messages = iter2.collect().map((e) => e.hash); - -// assert.equal(messages.length, 1); -// assert.equal(messages[0], head.hash); -// done(); -// })); - -// it('returns all items when lt is head and limit is -1', async((done) => { -// var iter2 = orbit.channel(channel, '').iterator({ lt: head.hash, limit: -1 }); -// var messages = iter2.collect().map((e) => e.hash); - -// assert.equal(messages.length, itemCount); -// assert.equal(messages[0], head.hash); -// assert.equal(messages[4], all[all.length - 1].hash); -// done(); -// })); - -// it('returns 3 items when lt is head and limit is 3', async((done) => { -// var iter2 = orbit.channel(channel, '').iterator({ lt: head.hash, limit: 3 }); -// var messages = iter2.collect().map((e) => e.hash); - -// assert.equal(messages.length, 3); -// assert.equal(messages[0], head.hash); -// assert.equal(messages[2], all[2].hash); -// done(); -// })); - -// it('returns null when lt is the root item', async((done) => { -// var messages = orbit.channel(channel, '').iterator({ lt: all[all.length - 1].hash }).collect(); -// assert.equal(messages.length, 0); -// done(); -// })); - -// it('returns one item when lte is the root item', async((done) => { -// var iter = orbit.channel(channel, '').iterator({ lte: all[all.length - 1].hash }); -// var messages = iter.collect().map((e) => e.hash); - -// assert.equal(messages.length, 1); -// assert.equal(messages[0], all[all.length - 1].hash); -// done(); -// })); - -// it('returns all items when lte is the head', async((done) => { -// var iter2 = orbit.channel(channel, '').iterator({ lte: head.hash, limit: -1 }); -// var messages = iter2.collect().map((e) => e.hash); - -// assert.equal(messages.length, itemCount); -// assert.equal(messages[0], all[0].hash); -// assert.equal(messages[4], all[all.length - 1].hash); -// done(); -// })); - -// it('returns 3 items when lte is the head', async((done) => { -// var iter2 = orbit.channel(channel, '').iterator({ lte: head.hash, limit: 3 }); -// var messages = iter2.collect().map((e) => e.hash); - -// assert.equal(messages.length, 3); -// assert.equal(messages[0], all[0].hash); -// assert.equal(messages[1], all[1].hash); -// assert.equal(messages[2], all[2].hash); -// done(); -// })); -// }); -// }); - -// }); - - -// /* -// describe('Modes', function() { -// var password = 'hello'; - -// it('sets read mode', async((done) => { -// try { -// var mode = { -// mode: "+r", -// params: { -// password: password -// } -// }; -// var modes = orbit.channel(channel, '').setMode(mode) -// assert.notEqual(modes.r, null); -// assert.equal(modes.r.password, password); -// } catch(e) { -// assert.equal(e, null); -// } -// done(); -// })); - -// it('can\'t read with wrong password', async((done) => { -// try { -// var modes = orbit.channel(channel, 'invalidpassword').iterator(); -// assert.equal(true, false); -// } catch(e) { -// assert.equal(e, 'Unauthorized'); -// } -// done(); -// })); - -// it('sets write mode', async((done) => { -// try { -// var mode = { -// mode: "+w", -// params: { -// ops: [orbit.user.id] -// } -// }; -// var modes = orbit.channel(channel, password).setMode(mode); -// assert.notEqual(modes.w, null); -// assert.equal(modes.w.ops[0], orbit.user.id); -// } catch(e) { -// assert.equal(e, null); -// } -// done(); -// })); - -// it('can\'t write when user not an op', async((done) => { -// // TODO -// done(); -// })); - -// it('removes write mode', async((done) => { -// try { -// var modes = orbit.channel(channel, password).setMode({ mode: "-w" }); -// assert.equal(modes.w, null); -// } catch(e) { -// assert.equal(e, null); -// } -// done(); -// })); - -// it('removes read mode', async((done) => { -// try { -// var modes = orbit.channel(channel, password).setMode({ mode: "-r" }); -// assert.equal(modes.r, null); -// } catch(e) { -// assert.equal(e, null); -// } -// done(); -// })); - -// }); -// */ -// }); +'use strict'; + +const _ = require('lodash'); +const assert = require('assert'); +const async = require('asyncawait/async'); +const await = require('asyncawait/await'); +const OrbitClient = require('../src/OrbitClient'); + +// Orbit +const host = 'localhost'; +const port = 6379; +const username = 'testrunner'; +const password = ''; + +describe('Orbit Client', () => { + let client, db; + let items = []; + + let channel = 'abcdefgh'; + + before(async((done) => { + client = OrbitClient.connect(host, port, username, password); + db = client.channel(channel); + db.delete(); + done(); + })); + + after(async((done) => { + if(db) db.delete(); + done(); + })); + +/* + describe('Info', function() { // } + // }; + // var res = db.setMode(mode) + // var info = orbit.channel(channel, 'password').info(); + // assert.notEqual(info, null); + // assert.equal(info.head, null); + // assert.equal(JSON.stringify(info.modes), JSON.stringify(res)); + // orbit.channel(channel, 'password').delete(); + // } catch(e) { + // orbit.channel(channel, 'password').delete(); + // assert.equal(e, null); + // } + // done(); + // })); + }); +*/ + + describe('Add events', function() { + it('adds an item to an empty channel', async((done) => { + const head = db.add('hello'); + assert.notEqual(head, null); + assert.equal(head.startsWith('Qm'), true); + assert.equal(head.length, 46); + done(); + })); + + it('adds a new item to a channel with one item', async((done) => { + const head = db.iterator().collect()[0]; + const second = db.add('hello'); + assert.notEqual(second, null); + assert.notEqual(second, head); + assert.equal(second.startsWith('Qm'), true); + assert.equal(second.length, 46); + done(); + })); + + it('adds five items', async((done) => { + for(let i = 0; i < 5; i ++) { + let hash = db.add('hello'); + assert.notEqual(hash, null); + assert.equal(hash.startsWith('Qm'), true); + assert.equal(hash.length, 46); + } + done(); + })); + + it('adds an item that is > 256 bytes', async((done) => { + let msg = new Buffer(512); + msg.fill('a') + const hash = db.add(msg.toString()); + assert.notEqual(hash, null); + assert.equal(hash.startsWith('Qm'), true); + assert.equal(hash.length, 46); + done(); + })); + }); + + describe('Delete events', function() { + it('deletes an item when only one item in the database', async((done) => { + db.delete(); + const head = db.add('hello1'); + let item = db.iterator().collect(); + const delop = db.del(head); + const items = db.iterator().collect(); + assert.equal(delop.startsWith('Qm'), true); + assert.equal(items.length, 0); + done(); + })); + + it('deletes an item when two items in the database', async((done) => { + db.add('hello1'); + const head = db.add('hello2'); + db.del(head); + const items = db.iterator().collect(); + assert.equal(items.length, 1); + assert.equal(items[0].hash.startsWith('Qm'), true); + assert.equal(items[0].payload.op, 'ADD'); + assert.equal(items[0].payload.value, 'hello1'); + assert.notEqual(items[0].payload.meta, null); + done(); + })); + + it('deletes an item between adds', async((done) => { + const head = db.add('hello1'); + db.add('hello2'); + db.del(head); + db.add('hello3'); + const items = db.iterator().collect(); + assert.equal(items.length, 1); + assert.equal(items[0].hash.startsWith('Qm'), true); + assert.equal(items[0].payload.op, 'ADD'); + assert.equal(items[0].payload.value, 'hello3'); + assert.notEqual(items[0].payload.meta, null); + done(); + })); + }); + + describe('Iterator', function() { + let items = []; + const itemCount = 5; + + before(async((done) => { + db.delete(); + for(let i = 0; i < itemCount; i ++) { + const hash = db.add('hello' + i); + items.push(hash); + } + done(); + })); + + describe('Defaults', function() { + it('returns an iterator', async((done) => { + const iter = db.iterator(); + const next = iter.next().value; + assert.notEqual(iter, null); + assert.notEqual(next, null); + done(); + })); + + it('returns an item with the correct structure', async((done) => { + const iter = db.iterator(); + const next = iter.next().value; + + assert.notEqual(next, null); + assert.notEqual(next.hash, null); + assert.equal(next.hash.startsWith('Qm'), true); + assert.notEqual(next.payload, null); + assert.equal(next.payload.op, 'ADD'); + assert.equal(next.payload.key.startsWith('Qm'), true); + assert.equal(next.payload.value, 'hello4'); + assert.notEqual(next.payload.meta, null); + done(); + })); + + it('implements Iterator interface', async((done) => { + const iter = db.iterator({ limit: -1 }); + let messages = []; + + for(let i of iter) + messages.push(i.hash); + + assert.equal(messages.length, items.length); + done(); + })); + + it('returns 1 item as default', async((done) => { + const iter = db.iterator(); + const first = iter.next().value; + const second = iter.next().value; + assert.equal(first.payload.key, items[items.length - 1]); + assert.equal(second, null); + assert.equal(first.payload.value, 'hello4'); + done(); + })); + }); + + describe('Collect', function() { + it('returns all items', async((done) => { + const messages = db.iterator({ limit: -1 }).collect(); + assert.equal(messages.length, items.length); + assert.equal(messages[0].payload.value, 'hello0'); + assert.equal(messages[messages.length - 1].payload.value, 'hello4'); + done(); + })); + + it('returns 1 item', async((done) => { + const messages = db.iterator().collect(); + assert.equal(messages.length, 1); + done(); + })); + + it('returns 3 items', async((done) => { + const messages = db.iterator({ limit: 3 }).collect(); + assert.equal(messages.length, 3); + done(); + })); + }); + + describe('Options: limit', function() { + it('returns 1 item when limit is 0', async((done) => { + const iter = db.iterator({ limit: 0 }); + const first = iter.next().value; + const second = iter.next().value; + assert.equal(first.payload.key, items[items.length - 1]); + assert.equal(second, null); + done(); + })); + + it('returns 1 item when limit is 1', async((done) => { + const iter = db.iterator({ limit: 1 }); + const first = iter.next().value; + const second = iter.next().value; + assert.equal(first.payload.key, items[items.length - 1]); + assert.equal(second, null); + done(); + })); + + it('returns 3 items', async((done) => { + const iter = db.iterator({ limit: 3 }); + const first = iter.next().value; + const second = iter.next().value; + const third = iter.next().value; + const fourth = iter.next().value; + assert.equal(first.payload.key, items[items.length - 3]); + assert.equal(second.payload.key, items[items.length - 2]); + assert.equal(third.payload.key, items[items.length - 1]); + assert.equal(fourth, null); + done(); + })); + + it('returns all items', async((done) => { + const messages = db.iterator({ limit: -1 }) + .collect() + .map((e) => e.payload.key); + + messages.reverse(); + assert.equal(messages.length, items.length); + assert.equal(messages[0], items[items.length - 1]); + done(); + })); + + it('returns all items when limit is bigger than -1', async((done) => { + const messages = db.iterator({ limit: -300 }) + .collect() + .map((e) => e.payload.key); + + assert.equal(messages.length, items.length); + assert.equal(messages[0], items[0]); + done(); + })); + + it('returns all items when limit is bigger than number of items', async((done) => { + const messages = db.iterator({ limit: 300 }) + .collect() + .map((e) => e.payload.key); + + assert.equal(messages.length, items.length); + assert.equal(messages[0], items[0]); + done(); + })); + }); + + describe('Options: reverse', function() { + it('returns all items reversed', async((done) => { + const messages = db.iterator({ limit: -1, reverse: true }) + .collect() + .map((e) => e.payload.key); + + assert.equal(messages.length, items.length); + assert.equal(messages[0], items[items.length - 1]); + done(); + })); + }); + + describe('Options: ranges', function() { + + describe('gt & gte', function() { + it('returns 1 item when gte is the head', async((done) => { + const messages = db.iterator({ gte: _.last(items), limit: -1 }) + .collect() + .map((e) => e.payload.key); + + assert.equal(messages.length, 1); + assert.equal(messages[0], items[items.length -1]); + done(); + })); + + it('returns 0 items when gt is the head', async((done) => { + const messages = db.iterator({ gt: _.last(items) }).collect(); + assert.equal(messages.length, 0); + done(); + })); + + it('returns 2 item when gte is defined', async((done) => { + const gte = items[items.length - 2]; + const messages = db.iterator({ gte: gte, limit: -1 }) + .collect() + .map((e) => e.payload.key); + + assert.equal(messages.length, 2); + assert.equal(messages[0], items[items.length - 2]); + assert.equal(messages[1], items[items.length - 1]); + done(); + })); + + it('returns all items when gte is the root item', async((done) => { + const messages = db.iterator({ gte: items[0], limit: -1 }) + .collect() + .map((e) => e.payload.key); + + assert.equal(messages.length, items.length); + assert.equal(messages[0], items[0]); + assert.equal(messages[messages.length - 1], items[items.length - 1]); + done(); + })); + + it('returns items when gt is the root item', async((done) => { + const messages = db.iterator({ gt: items[0], limit: -1 }) + .collect() + .map((e) => e.payload.key); + + assert.equal(messages.length, itemCount - 1); + assert.equal(messages[0], items[1]); + assert.equal(messages[3], items[items.length - 1]); + done(); + })); + + it('returns items when gt is defined', async((done) => { + const messages = db.iterator({ limit: -1}) + .collect() + .map((e) => e.payload.key); + + const gt = messages[2]; + + const messages2 = db.iterator({ gt: gt, limit: 100 }) + .collect() + .map((e) => e.payload.key); + + assert.equal(messages2.length, 2); + assert.equal(messages2[0], messages[messages.length - 2]); + assert.equal(messages2[1], messages[messages.length - 1]); + done(); + })); + }); + + describe('lt & lte', function() { + it('returns one item after head when lt is the head', async((done) => { + const messages = db.iterator({ lt: _.last(items) }) + .collect() + .map((e) => e.payload.key); + + assert.equal(messages.length, 1); + assert.equal(messages[0], items[items.length - 2]); + done(); + })); + + it('returns all items when lt is head and limit is -1', async((done) => { + const messages = db.iterator({ lt: _.last(items), limit: -1 }) + .collect() + .map((e) => e.payload.key); + + assert.equal(messages.length, items.length - 1); + assert.equal(messages[0], items[0]); + assert.equal(messages[messages.length - 1], items[items.length - 2]); + done(); + })); + + it('returns 3 items when lt is head and limit is 3', async((done) => { + const messages = db.iterator({ lt: _.last(items), limit: 3 }) + .collect() + .map((e) => e.payload.key); + + assert.equal(messages.length, 3); + assert.equal(messages[0], items[items.length - 4]); + assert.equal(messages[2], items[items.length - 2]); + done(); + })); + + it('returns null when lt is the root item', async((done) => { + const messages = db.iterator({ lt: items[0] }).collect(); + assert.equal(messages.length, 0); + done(); + })); + + it('returns one item when lte is the root item', async((done) => { + const messages = db.iterator({ lte: items[0] }) + .collect() + .map((e) => e.payload.key); + + assert.equal(messages.length, 1); + assert.equal(messages[0], items[0]); + done(); + })); + + it('returns all items when lte is the head', async((done) => { + const messages = db.iterator({ lte: _.last(items), limit: -1 }) + .collect() + .map((e) => e.payload.key); + + assert.equal(messages.length, itemCount); + assert.equal(messages[0], items[0]); + assert.equal(messages[4], _.last(items)); + done(); + })); + + it('returns 3 items when lte is the head', async((done) => { + const messages = db.iterator({ lte: _.last(items), limit: 3 }) + .collect() + .map((e) => e.payload.key); + + assert.equal(messages.length, 3); + assert.equal(messages[0], items[items.length - 3]); + assert.equal(messages[1], items[items.length - 2]); + assert.equal(messages[2], _.last(items)); + done(); + })); + }); + }); + }); + + +/* + describe('Modes', function() { + var password = 'hello'; + + it('sets read mode', async((done) => { + try { + var mode = { + mode: "+r", + params: { + password: password + } + }; + var modes = db.setMode(mode) + assert.notEqual(modes.r, null); + assert.equal(modes.r.password, password); + } catch(e) { + assert.equal(e, null); + } + done(); + })); + + it('can\'t read with wrong password', async((done) => { + try { + var modes = orbit.channel(channel, 'invalidpassword').iterator(); + assert.equal(true, false); + } catch(e) { + assert.equal(e, 'Unauthorized'); + } + done(); + })); + + it('sets write mode', async((done) => { + try { + var mode = { + mode: "+w", + params: { + ops: [orbit.user.id] + } + }; + var modes = orbit.channel(channel, password).setMode(mode); + assert.notEqual(modes.w, null); + assert.equal(modes.w.ops[0], orbit.user.id); + } catch(e) { + assert.equal(e, null); + } + done(); + })); + + it('can\'t write when user not an op', async((done) => { + // TODO + done(); + })); + + it('removes write mode', async((done) => { + try { + var modes = orbit.channel(channel, password).setMode({ mode: "-w" }); + assert.equal(modes.w, null); + } catch(e) { + assert.equal(e, null); + } + done(); + })); + + it('removes read mode', async((done) => { + try { + var modes = orbit.channel(channel, password).setMode({ mode: "-r" }); + assert.equal(modes.r, null); + } catch(e) { + assert.equal(e, null); + } + done(); + })); + + }); +*/ + + describe('Delete', function() { + it('deletes a channel from the database', async((done) => { + const result = db.delete(); + assert.equal(result, true); + const iter = db.iterator(); + assert.equal(iter.next().value, null); + done(); + })); + }); + + describe('Key-Value Store', function() { + it('put', async((done) => { + db.put('key1', 'hello!'); + let all = db.iterator().collect(); + assert.equal(all.length, 1); + assert.equal(all[0].hash.startsWith('Qm'), true); + assert.equal(all[0].payload.key, 'key1'); + assert.equal(all[0].payload.op, 'PUT'); + assert.notEqual(all[0].payload.meta, null); + done(); + })); + + it('get', async((done) => { + db.put('key1', 'hello!'); + const value = db.get('key1'); + assert.equal(value, 'hello!'); + done(); + })); + + it('put updates a value', async((done) => { + db.put('key1', 'hello!'); + db.put('key1', 'hello again'); + const value = db.get('key1'); + assert.equal(value, 'hello again'); + done(); + })); + + it('deletes a key', async((done) => { + db.put('key1', 'hello!'); + db.del('key1'); + const value = db.get('key1'); + assert.equal(value, null); + done(); + })); + + it('deletes a key after multiple updates', async((done) => { + db.put('key1', 'hello1'); + db.put('key1', 'hello2'); + db.put('key1', 'hello3'); + db.del('key1'); + const value = db.get('key1'); + assert.equal(value, null); + done(); + })); + + it('put - multiple keys', async((done) => { + db.put('key1', 'hello1'); + db.put('key2', 'hello2'); + db.put('key3', 'hello3'); + const all = db.iterator().collect(); + assert.equal(all.length, 1); + done(); + })); + + it('get - multiple keys', async((done) => { + db.put('key1', 'hello1'); + db.put('key2', 'hello2'); + db.put('key3', 'hello3'); + const v1 = db.get('key1'); + const v2 = db.get('key2'); + const v3 = db.get('key3'); + assert.equal(v1, 'hello1'); + assert.equal(v2, 'hello2'); + assert.equal(v3, 'hello3'); + done(); + })); + + it('get - integer value', async((done) => { + db.put('key1', 123); + const v1 = db.get('key1'); + assert.equal(v1, 123); + done(); + })); + + it('get - object value', async((done) => { + const val = { one: 'first', two: 2 }; + db.put('key1', val); + const v1 = db.get('key1'); + assert.equal(_.isEqual(v1, val), true); + done(); + })); + + it('get - array value', async((done) => { + const val = [1, 2, 3, 4, 5]; + db.put('key1', val); + const v1 = db.get('key1'); + assert.equal(_.isEqual(v1, val), true); + done(); + })); + }); + +}); diff --git a/test/orbit-list-tests.js b/test/orbit-list-tests.js index a42a923..af905f2 100644 --- a/test/orbit-list-tests.js +++ b/test/orbit-list-tests.js @@ -148,9 +148,9 @@ describe('OrbitList', async(function() { 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.QmZfdeMV77si491NPX83Q8eRYE9WNzVorHrfWJPrJ51brt'] }, - { id: 'A', seq: 0, ver: 2, data: 'hello3', next: ['A.0.1.QmbbtEWe4qHLSjtW2HkPuszFW3zfBTXBdPrkXMdbePxqfK'] } + { 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))