'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 startIpfs = async (() => { return new Promise(async((resolve, reject) => { const ipfsd = await(ipfsDaemon()); resolve(ipfsd.daemon); })); }); let ipfs; describe('OrbitList', async(function() { this.timeout(5000); before(async((done) => { ipfs = await(startIpfs()); done(); })); describe('Constructor', async(() => { it('initializes member variables', async((done) => { const list = new List('A', ipfs); 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('add', async(() => { it('saves the data to ipfs', async((done) => { const list = new List('A', ipfs); const text = 'testing 1 2 3 4'; list.add(text) const hash = await(list.getIpfsHash()); 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(); })); it('updates the data to ipfs', async((done) => { const list = new List('A', ipfs); const text1 = 'testing 1 2 3'; const text2 = 'testing 456'; let hash; list.add(text1) hash = await(list.getIpfsHash()); // assert.equal(hash, 'QmcBjB93PsJGz2LrVy5e1Z8mtwH99B8yynsa5f4q3GanEe'); list.add(text2) hash = await(list.getIpfsHash()); // assert.equal(hash, 'Qmf358H1wjuX3Bbaag4SSEiujoruowVUNR5pLCNQs8vivP'); const l = await(ipfsAPI.getObject(ipfs, hash)); const list2 = List.fromJson(ipfs, JSON.parse(l.Data)); assert.equal(list2.items[0].data, text1); assert.equal(list2.items[1].data, text2); done(); })); })); describe('getIpfsHash', async(() => { it('returns the list as ipfs hash', async((done) => { const list = new List('A', ipfs); const hash = await(list.getIpfsHash()); assert.equal(hash.startsWith('Qm'), true); done(); })); it('saves the list to ipfs', async((done) => { const list = new List('A', ipfs); const hash = await(list.getIpfsHash()); 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('A', ipfs); list.add("hello1") list.add("hello2") list.add("hello3") const hash = await(list.getIpfsHash()); 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'); 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('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") 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('items', () => { it('returns items', async((done) => { const list = new List('A', ipfs); 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('A', ipfs); 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('A', ipfs); 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('A', ipfs); 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(item.ver, List.batchSize - 1); assert.equal(item.data, 'hello' + List.batchSize); assert.notEqual(item.next.length, 0); done(); })); }); describe('join', () => { it('increases the sequence and resets the version if other list has the same or higher sequence', async((done) => { const list1 = new List('A', ipfs); const list2 = new List('B', ipfs); 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', async((done) => { const list1 = new List('A', ipfs); const list2 = new List('B', ipfs); 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', async((done) => { const list1 = new List('A', ipfs); 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].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].compactId, 'A.0.1'); assert.equal(list1._currentBatch[2].next[0].hash, 'QmUycQmNU8apkbPqsWPK3VxMHJeHt86UQrzfSFDNRGbvsd'); done(); })); it('finds the next heads (two) after a join', async((done) => { const list1 = new List('A', ipfs); const list2 = new List('B', ipfs); 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[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(); })); it('finds the next head (one) after a join', async((done) => { const list1 = new List('A', ipfs); const list2 = new List('B', ipfs); 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, 'QmYHXzXaahAL9iChAUtVsvdncKfQf7ShEfveZnL7qvGfTT'); done(); })); it('finds the next heads after two joins', async((done) => { const list1 = new List('A', ipfs); const list2 = new List('B', ipfs); 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, 'QmUCzHbqj3qKeV2JUzYB4j9B6pLmpSghD4JJa5WmLMJHVB'); done(); })); it('finds the next heads after multiple joins', async((done) => { const list1 = new List('A', ipfs); const list2 = new List('B', ipfs); const list3 = new List('C', ipfs); const list4 = new List('D', ipfs); 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[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(); })); it('joins list of one item with list of two items', async((done) => { const list1 = new List('A', ipfs); const list2 = new List('B', ipfs); 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('A', ipfs); const list2 = new List('B', ipfs); 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('A', ipfs); const list2 = new List('B', ipfs); 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('A', ipfs); const list2 = new List('B', ipfs); const list3 = new List('C', ipfs); const list4 = new List('D', ipfs); 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('A', ipfs); const list2 = new List('B', ipfs); const list3 = new List('C', ipfs); const list4 = new List('D', ipfs); 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(); })); }); describe('_findHeads', () => { it('TODO', (done) => { done(); }); }); describe('_isReferencedInChain', () => { it('TODO', (done) => { done(); }); }); }));