diff --git a/src/oplog/log.js b/src/oplog/log.js index 1a82de4..354140b 100644 --- a/src/oplog/log.js +++ b/src/oplog/log.js @@ -458,4 +458,4 @@ const Log = async (identity, { logId, logHeads, access, entryStorage, headsStora } } -export { Log as default, DefaultAccessController } +export { Log as default, DefaultAccessController, Clock } diff --git a/test/db/document-store.test.js b/test/db/document-store.test.js index 79728e7..2b3c935 100644 --- a/test/db/document-store.test.js +++ b/test/db/document-store.test.js @@ -11,7 +11,6 @@ Object.keys(testAPIs).forEach((IPFS) => { describe('DocumentStore Database (' + IPFS + ')', function () { let ipfsd let ipfs - let keystore, signingKeyStore let accessController let identities1 let testIdentity1 diff --git a/test/db/event-store.test.js b/test/db/event-store.test.js index f07b9ab..50532c7 100644 --- a/test/db/event-store.test.js +++ b/test/db/event-store.test.js @@ -13,7 +13,6 @@ Object.keys(testAPIs).forEach((IPFS) => { describe('EventStore Database (' + IPFS + ')', function () { let ipfsd let ipfs - let keystore, signingKeyStore let accessController let identities1 let testIdentity1 diff --git a/test/db/replication/document-store.test.js b/test/db/replication/document-store.test.js index 99c01f5..948edfb 100644 --- a/test/db/replication/document-store.test.js +++ b/test/db/replication/document-store.test.js @@ -1,8 +1,7 @@ import { deepStrictEqual } from 'assert' import rmrf from 'rimraf' -import { Log, Entry } from '../../../src/index.js' +import { Log, Entry, Database } from '../../../src/index.js' import { DocumentStore } from '../../../src/db/index.js' -import { Database } from '../../../src/index.js' import { config, startIpfs, stopIpfs } from 'orbit-db-test-utils' import connectPeers from '../../utils/connect-nodes.js' import { createTestIdentities, cleanUpTestIdentities } from '../../fixtures/orbit-db-identity-keys.js' diff --git a/test/db/replication/event-store.test.js b/test/db/replication/event-store.test.js index d71af41..ef7f3af 100644 --- a/test/db/replication/event-store.test.js +++ b/test/db/replication/event-store.test.js @@ -1,8 +1,7 @@ import { deepStrictEqual } from 'assert' import rmrf from 'rimraf' -import { Log, Entry } from '../../../src/index.js' +import { Log, Entry, Database } from '../../../src/index.js' import { EventStore } from '../../../src/db/index.js' -import { Database } from '../../../src/index.js' import { config, startIpfs, stopIpfs } from 'orbit-db-test-utils' import connectPeers from '../../utils/connect-nodes.js' import waitFor from '../../utils/wait-for.js' diff --git a/test/db/replication/keyvalue.test.js b/test/db/replication/keyvalue.test.js index a95b212..eb0c8dc 100644 --- a/test/db/replication/keyvalue.test.js +++ b/test/db/replication/keyvalue.test.js @@ -1,8 +1,7 @@ import { deepStrictEqual } from 'assert' import rmrf from 'rimraf' -import { Log, Entry } from '../../../src/index.js' +import { Log, Entry, Database } from '../../../src/index.js' import { KeyValue, KeyValuePersisted } from '../../../src/db/index.js' -import { Database } from '../../../src/index.js' import { config, startIpfs, stopIpfs } from 'orbit-db-test-utils' import connectPeers from '../../utils/connect-nodes.js' import waitFor from '../../utils/wait-for.js' diff --git a/test/oplog/append.test.js b/test/oplog/append.test.js index 05e8676..159dfe1 100644 --- a/test/oplog/append.test.js +++ b/test/oplog/append.test.js @@ -1,132 +1,117 @@ import { strictEqual, deepStrictEqual } from 'assert' -import rimraf from 'rimraf' +import rmrf from 'rimraf' import { copy } from 'fs-extra' -import { Log } from '../../src/oplog/index.js' -import MemoryStorage from '../../src/storage/memory.js' -import LevelStorage from '../../src/storage/level.js' -import { Identities } from '../../src/identities/index.js' -import KeyStore from '../../src/key-store.js' +import { Log, Identities, KeyStore } from '../../src/index.js' +import testKeysPath from '../fixtures/test-keys-path.js ' -// Test utils -import { config, testAPIs } from 'orbit-db-test-utils' +const keysPath = './testkeys' -const { sync: rmrf } = rimraf +describe('Log - Append', function () { + this.timeout(5000) -let testIdentity + let keystore + let identities + let testIdentity -Object.keys(testAPIs).forEach((IPFS) => { - describe('Log - Append (' + IPFS + ')', function () { - this.timeout(config.timeout) + before(async () => { + await copy(testKeysPath, keysPath) + keystore = await KeyStore({ path: keysPath }) + identities = await Identities({ keystore }) + testIdentity = await identities.createIdentity({ id: 'userA' }) + }) - const { identityKeyFixtures, signingKeyFixtures, identityKeysPath } = config - - let keystore - let identities - - before(async () => { - rmrf(identityKeysPath) - await copy(identityKeyFixtures, identityKeysPath) - await copy(signingKeyFixtures, identityKeysPath) - - keystore = await KeyStore({ storage: await LevelStorage({ path: identityKeysPath }) }) - - const storage = await MemoryStorage() - - identities = await Identities({ keystore, storage }) - testIdentity = await identities.createIdentity({ id: 'userA' }) - }) - - after(async () => { + after(async () => { + if (keystore) { await keystore.close() - rmrf(identityKeysPath) - }) + } + await rmrf(keysPath) + }) - describe('append', async () => { - describe('append one', async () => { - let log - let values = [] - let heads = [] + describe('append', async () => { + describe('append one', async () => { + let log + let values = [] + let heads = [] - before(async () => { - log = await Log(testIdentity, { logId: 'A' }) - await log.append('hello1') - values = await log.values() - heads = await log.heads() - }) + before(async () => { + log = await Log(testIdentity, { logId: 'A' }) + await log.append('hello1') + values = await log.values() + heads = await log.heads() + }) - it('added the correct amount of items', () => { - strictEqual(values.length, 1) - }) + it('added the correct amount of items', () => { + strictEqual(values.length, 1) + }) - it('added the correct values', async () => { - values.forEach((entry) => { - strictEqual(entry.payload, 'hello1') - }) - }) - - it('added the correct amount of next pointers', async () => { - values.forEach((entry) => { - strictEqual(entry.next.length, 0) - }) - }) - - it('has the correct heads', async () => { - heads.forEach((head) => { - strictEqual(head.hash, values[0].hash) - }) - }) - - it('updated the clocks correctly', async () => { - values.forEach((entry) => { - strictEqual(entry.clock.id, testIdentity.publicKey) - strictEqual(entry.clock.time, 1) - }) + it('added the correct values', async () => { + values.forEach((entry) => { + strictEqual(entry.payload, 'hello1') }) }) - describe('append 100 items to a log', async () => { - const amount = 100 - const nextPointerAmount = 64 - - let log - let values = [] - let heads = [] - - before(async () => { - log = await Log(testIdentity, { logId: 'A' }) - for (let i = 0; i < amount; i++) { - await log.append('hello' + i, { pointerCount: nextPointerAmount }) - } - values = await log.values() - heads = await log.heads() + it('added the correct amount of next pointers', async () => { + values.forEach((entry) => { + strictEqual(entry.next.length, 0) }) + }) - it('set the correct heads', () => { - strictEqual(heads.length, 1) - deepStrictEqual(heads[0], values[values.length - 1]) + it('has the correct heads', async () => { + heads.forEach((head) => { + strictEqual(head.hash, values[0].hash) }) + }) - it('added the correct amount of items', () => { - strictEqual(values.length, amount) + it('updated the clocks correctly', async () => { + values.forEach((entry) => { + strictEqual(entry.clock.id, testIdentity.publicKey) + strictEqual(entry.clock.time, 1) }) + }) + }) - it('added the correct values', async () => { - values.forEach((entry, index) => { - strictEqual(entry.payload, 'hello' + index) - }) + describe('append 100 items to a log', async () => { + const amount = 100 + const nextPointerAmount = 64 + + let log + let values = [] + let heads = [] + + before(async () => { + log = await Log(testIdentity, { logId: 'A' }) + for (let i = 0; i < amount; i++) { + await log.append('hello' + i, { pointerCount: nextPointerAmount }) + } + values = await log.values() + heads = await log.heads() + }) + + it('set the correct heads', () => { + strictEqual(heads.length, 1) + deepStrictEqual(heads[0], values[values.length - 1]) + }) + + it('added the correct amount of items', () => { + strictEqual(values.length, amount) + }) + + it('added the correct values', async () => { + values.forEach((entry, index) => { + strictEqual(entry.payload, 'hello' + index) }) + }) - it('updated the clocks correctly', async () => { - values.forEach((entry, index) => { - strictEqual(entry.clock.time, index + 1) - strictEqual(entry.clock.id, testIdentity.publicKey) - }) + it('updated the clocks correctly', async () => { + values.forEach((entry, index) => { + strictEqual(entry.clock.time, index + 1) + strictEqual(entry.clock.id, testIdentity.publicKey) }) + }) - it('added the correct amount of refs pointers', async () => { - values.forEach((entry, index) => { - strictEqual(entry.refs.length, index > 0 ? Math.floor(Math.log2(Math.min(nextPointerAmount, index))) : 0) - }) + it('added the correct amount of refs pointers', async () => { + values.forEach((entry, index) => { + strictEqual(entry.refs.length, index > 0 ? Math.floor(Math.log2(Math.min(nextPointerAmount, index))) : 0) }) }) }) diff --git a/test/oplog/crdt.test.js b/test/oplog/crdt.test.js index 6208403..a80c79c 100644 --- a/test/oplog/crdt.test.js +++ b/test/oplog/crdt.test.js @@ -1,246 +1,242 @@ import { strictEqual, deepStrictEqual } from 'assert' -import rimraf from 'rimraf' -import { Log } from '../../src/oplog/index.js' -import { Identities } from '../../src/identities/index.js' -import KeyStore from '../../src/key-store.js' -import { config, testAPIs } from 'orbit-db-test-utils' +import rmrf from 'rimraf' +import { copy } from 'fs-extra' +import { Log, Identities, KeyStore } from '../../src/index.js' import testKeysPath from '../fixtures/test-keys-path.js ' -const { sync: rmrf } = rimraf +const keysPath = './testkeys' let testIdentity, testIdentity2, testIdentity3 -Object.keys(testAPIs).forEach((IPFS) => { - describe('Log - CRDT (' + IPFS + ')', function () { - this.timeout(config.timeout) +describe('Log - CRDT', function () { + this.timeout(5000) - const { identityKeysPath } = config + let keystore + let identities1 - let keystore - let identities1 + before(async () => { + await copy(testKeysPath, keysPath) + keystore = await KeyStore({ path: keysPath }) + identities1 = await Identities({ keystore }) + testIdentity = await identities1.createIdentity({ id: 'userA' }) + testIdentity2 = await identities1.createIdentity({ id: 'userB' }) + testIdentity3 = await identities1.createIdentity({ id: 'userC' }) + }) - before(async () => { - keystore = await KeyStore({ path: testKeysPath }) - - identities1 = await Identities({ keystore }) - testIdentity = await identities1.createIdentity({ id: 'userA' }) - testIdentity2 = await identities1.createIdentity({ id: 'userB' }) - testIdentity3 = await identities1.createIdentity({ id: 'userC' }) - }) - - after(async () => { + after(async () => { + if (keystore) { await keystore.close() - rmrf(identityKeysPath) + } + await rmrf(keysPath) + }) + + describe('is a CRDT', async () => { + const logId = 'X' + + let log1, log2, log3 + + beforeEach(async () => { + log1 = await Log(testIdentity, { logId }) + log2 = await Log(testIdentity2, { logId }) + log3 = await Log(testIdentity3, { logId }) }) - describe('is a CRDT', async () => { - const logId = 'X' + it('join is associative', async () => { + const expectedElementsCount = 6 - let log1, log2, log3 + await log1.append('helloA1') + await log1.append('helloA2') + await log2.append('helloB1') + await log2.append('helloB2') + await log3.append('helloC1') + await log3.append('helloC2') - beforeEach(async () => { - log1 = await Log(testIdentity, { logId }) - log2 = await Log(testIdentity2, { logId }) - log3 = await Log(testIdentity3, { logId }) - }) + // a + (b + c) + await log2.join(log3) + await log1.join(log2) - it('join is associative', async () => { - const expectedElementsCount = 6 + const res1 = await log1.values() - await log1.append('helloA1') - await log1.append('helloA2') - await log2.append('helloB1') - await log2.append('helloB2') - await log3.append('helloC1') - await log3.append('helloC2') + log1 = await Log(testIdentity, { logId }) + log2 = await Log(testIdentity2, { logId }) + log3 = await Log(testIdentity3, { logId }) + await log1.append('helloA1') + await log1.append('helloA2') + await log2.append('helloB1') + await log2.append('helloB2') + await log3.append('helloC1') + await log3.append('helloC2') - // a + (b + c) - await log2.join(log3) - await log1.join(log2) + // (a + b) + c + await log1.join(log2) + await log3.join(log1) - const res1 = await log1.values() + const res2 = await log3.values() - log1 = await Log(testIdentity, { logId }) - log2 = await Log(testIdentity2, { logId }) - log3 = await Log(testIdentity3, { logId }) - await log1.append('helloA1') - await log1.append('helloA2') - await log2.append('helloB1') - await log2.append('helloB2') - await log3.append('helloC1') - await log3.append('helloC2') + // associativity: a + (b + c) == (a + b) + c + strictEqual(res1.length, expectedElementsCount) + strictEqual(res2.length, expectedElementsCount) + deepStrictEqual(res1.map(e => e.hash), res2.map(e => e.hash)) + }) - // (a + b) + c - await log1.join(log2) - await log3.join(log1) + it('join is commutative', async () => { + const expectedElementsCount = 4 - const res2 = await log3.values() + await log1.append('helloA1') + await log1.append('helloA2') + await log2.append('helloB1') + await log2.append('helloB2') - // associativity: a + (b + c) == (a + b) + c - strictEqual(res1.length, expectedElementsCount) - strictEqual(res2.length, expectedElementsCount) - deepStrictEqual(res1.map(e => e.hash), res2.map(e => e.hash)) - }) + // b + a + await log2.join(log1) + const res1 = await log2.values() - it('join is commutative', async () => { - const expectedElementsCount = 4 + log1 = await Log(testIdentity, { logId }) + log2 = await Log(testIdentity2, { logId }) + await log1.append('helloA1') + await log1.append('helloA2') + await log2.append('helloB1') + await log2.append('helloB2') - await log1.append('helloA1') - await log1.append('helloA2') - await log2.append('helloB1') - await log2.append('helloB2') + // a + b + await log1.join(log2) + const res2 = await log1.values() - // b + a - await log2.join(log1) - const res1 = await log2.values() + // commutativity: a + b == b + a + strictEqual(res1.length, expectedElementsCount) + strictEqual(res2.length, expectedElementsCount) + deepStrictEqual(res1.map(e => e.hash), res2.map(e => e.hash)) + }) - log1 = await Log(testIdentity, { logId }) - log2 = await Log(testIdentity2, { logId }) - await log1.append('helloA1') - await log1.append('helloA2') - await log2.append('helloB1') - await log2.append('helloB2') + it('multiple joins are commutative', async () => { + // b + a == a + b + log1 = await Log(testIdentity, { logId }) + log2 = await Log(testIdentity2, { logId }) + await log1.append('helloA1') + await log1.append('helloA2') + await log2.append('helloB1') + await log2.append('helloB2') + await log2.join(log1) + const resA1 = await log2.values() - // a + b - await log1.join(log2) - const res2 = await log1.values() + log1 = await Log(testIdentity, { logId }) + log2 = await Log(testIdentity2, { logId }) + await log1.append('helloA1') + await log1.append('helloA2') + await log2.append('helloB1') + await log2.append('helloB2') + await log1.join(log2) + const resA2 = await log1.values() - // commutativity: a + b == b + a - strictEqual(res1.length, expectedElementsCount) - strictEqual(res2.length, expectedElementsCount) - deepStrictEqual(res1.map(e => e.hash), res2.map(e => e.hash)) - }) + deepStrictEqual(resA1.map(e => e.hash), resA2.map(e => e.hash)) - it('multiple joins are commutative', async () => { - // b + a == a + b - log1 = await Log(testIdentity, { logId }) - log2 = await Log(testIdentity2, { logId }) - await log1.append('helloA1') - await log1.append('helloA2') - await log2.append('helloB1') - await log2.append('helloB2') - await log2.join(log1) - const resA1 = await log2.values() + // a + b == b + a + log1 = await Log(testIdentity, { logId }) + log2 = await Log(testIdentity2, { logId }) + await log1.append('helloA1') + await log1.append('helloA2') + await log2.append('helloB1') + await log2.append('helloB2') + await log1.join(log2) + const resB1 = await log1.values() - log1 = await Log(testIdentity, { logId }) - log2 = await Log(testIdentity2, { logId }) - await log1.append('helloA1') - await log1.append('helloA2') - await log2.append('helloB1') - await log2.append('helloB2') - await log1.join(log2) - const resA2 = await log1.values() + log1 = await Log(testIdentity, { logId }) + log2 = await Log(testIdentity2, { logId }) + await log1.append('helloA1') + await log1.append('helloA2') + await log2.append('helloB1') + await log2.append('helloB2') + await log2.join(log1) + const resB2 = await log2.values() - deepStrictEqual(resA1.map(e => e.hash), resA2.map(e => e.hash)) + deepStrictEqual(resB1.map(e => e.hash), resB2.map(e => e.hash)) - // a + b == b + a - log1 = await Log(testIdentity, { logId }) - log2 = await Log(testIdentity2, { logId }) - await log1.append('helloA1') - await log1.append('helloA2') - await log2.append('helloB1') - await log2.append('helloB2') - await log1.join(log2) - const resB1 = await log1.values() + // a + c == c + a + log1 = await Log(testIdentity, { logId }) + log3 = await Log(testIdentity3, { logId }) + await log1.append('helloA1') + await log1.append('helloA2') + await log3.append('helloC1') + await log3.append('helloC2') + await log3.join(log1) + const resC1 = await log3.values() - log1 = await Log(testIdentity, { logId }) - log2 = await Log(testIdentity2, { logId }) - await log1.append('helloA1') - await log1.append('helloA2') - await log2.append('helloB1') - await log2.append('helloB2') - await log2.join(log1) - const resB2 = await log2.values() + log1 = await Log(testIdentity, { logId }) + log3 = await Log(testIdentity3, { logId }) + await log1.append('helloA1') + await log1.append('helloA2') + await log3.append('helloC1') + await log3.append('helloC2') + await log1.join(log3) + const resC2 = await log1.values() - deepStrictEqual(resB1.map(e => e.hash), resB2.map(e => e.hash)) + deepStrictEqual(resC1.map(e => e.hash), resC2.map(e => e.hash)) - // a + c == c + a - log1 = await Log(testIdentity, { logId }) - log3 = await Log(testIdentity3, { logId }) - await log1.append('helloA1') - await log1.append('helloA2') - await log3.append('helloC1') - await log3.append('helloC2') - await log3.join(log1) - const resC1 = await log3.values() + // c + b == b + c + log2 = await Log(testIdentity2, { logId }) + log3 = await Log(testIdentity3, { logId }) - log1 = await Log(testIdentity, { logId }) - log3 = await Log(testIdentity3, { logId }) - await log1.append('helloA1') - await log1.append('helloA2') - await log3.append('helloC1') - await log3.append('helloC2') - await log1.join(log3) - const resC2 = await log1.values() + await log2.append('helloB1') + await log2.append('helloB2') + await log3.append('helloC1') + await log3.append('helloC2') + await log3.join(log2) + const resD1 = await log3.values() - deepStrictEqual(resC1.map(e => e.hash), resC2.map(e => e.hash)) + log2 = await Log(testIdentity2, { logId }) + log3 = await Log(testIdentity3, { logId }) + await log2.append('helloB1') + await log2.append('helloB2') + await log3.append('helloC1') + await log3.append('helloC2') + await log2.join(log3) + const resD2 = await log2.values() - // c + b == b + c - log2 = await Log(testIdentity2, { logId }) - log3 = await Log(testIdentity3, { logId }) + deepStrictEqual(resD1.map(e => e.hash), resD2.map(e => e.hash)) - await log2.append('helloB1') - await log2.append('helloB2') - await log3.append('helloC1') - await log3.append('helloC2') - await log3.join(log2) - const resD1 = await log3.values() + // a + b + c == c + b + a + log1 = await Log(testIdentity, { logId }) + log2 = await Log(testIdentity2, { logId }) + log3 = await Log(testIdentity3, { logId }) + await log1.append('helloA1') + await log1.append('helloA2') + await log2.append('helloB1') + await log2.append('helloB2') + await log3.append('helloC1') + await log3.append('helloC2') + await log1.join(log2) + await log1.join(log3) + const logLeft = await log1.values() - log2 = await Log(testIdentity2, { logId }) - log3 = await Log(testIdentity3, { logId }) - await log2.append('helloB1') - await log2.append('helloB2') - await log3.append('helloC1') - await log3.append('helloC2') - await log2.join(log3) - const resD2 = await log2.values() + log1 = await Log(testIdentity, { logId }) + log2 = await Log(testIdentity2, { logId }) + log3 = await Log(testIdentity3, { logId }) + await log1.append('helloA1') + await log1.append('helloA2') + await log2.append('helloB1') + await log2.append('helloB2') + await log3.append('helloC1') + await log3.append('helloC2') + await log3.join(log2) + await log3.join(log1) + const logRight = await log3.values() - deepStrictEqual(resD1.map(e => e.hash), resD2.map(e => e.hash)) + deepStrictEqual(logLeft.map(e => e.hash), logRight.map(e => e.hash)) + }) - // a + b + c == c + b + a - log1 = await Log(testIdentity, { logId }) - log2 = await Log(testIdentity2, { logId }) - log3 = await Log(testIdentity3, { logId }) - await log1.append('helloA1') - await log1.append('helloA2') - await log2.append('helloB1') - await log2.append('helloB2') - await log3.append('helloC1') - await log3.append('helloC2') - await log1.join(log2) - await log1.join(log3) - const logLeft = await log1.values() + it('join is idempotent', async () => { + const expectedElementsCount = 3 - log1 = await Log(testIdentity, { logId }) - log2 = await Log(testIdentity2, { logId }) - log3 = await Log(testIdentity3, { logId }) - await log1.append('helloA1') - await log1.append('helloA2') - await log2.append('helloB1') - await log2.append('helloB2') - await log3.append('helloC1') - await log3.append('helloC2') - await log3.join(log2) - await log3.join(log1) - const logRight = await log3.values() + const logA = await Log(testIdentity, { logId }) + await logA.append('helloA1') + await logA.append('helloA2') + await logA.append('helloA3') - deepStrictEqual(logLeft.map(e => e.hash), logRight.map(e => e.hash)) - }) + // idempotence: a + a = a + await logA.join(logA) + const values = await logA.values() - it('join is idempotent', async () => { - const expectedElementsCount = 3 - - const logA = await Log(testIdentity, { logId }) - await logA.append('helloA1') - await logA.append('helloA2') - await logA.append('helloA3') - - // idempotence: a + a = a - await logA.join(logA) - const values = await logA.values() - - strictEqual(values.length, expectedElementsCount) - }) + strictEqual(values.length, expectedElementsCount) }) }) }) diff --git a/test/oplog/entry.test.js b/test/oplog/entry.test.js index 2a57b7b..cdfaf77 100644 --- a/test/oplog/entry.test.js +++ b/test/oplog/entry.test.js @@ -1,201 +1,184 @@ import { strictEqual, deepStrictEqual } from 'assert' -import rimraf from 'rimraf' +import rmrf from 'rimraf' import { copy } from 'fs-extra' -import { Entry } from '../../src/oplog/index.js' -import { Identities } from '../../src/identities/index.js' -import KeyStore from '../../src/key-store.js' -import { config, testAPIs, startIpfs, stopIpfs } from 'orbit-db-test-utils' +import { Entry, Identities, KeyStore } from '../../src/index.js' import testKeysPath from '../fixtures/test-keys-path.js ' -const { sync: rmrf } = rimraf const { create, isEntry } = Entry +const keysPath = './testkeys' -Object.keys(testAPIs).forEach((IPFS) => { - describe('Entry (' + IPFS + ')', function () { - this.timeout(config.timeout) +describe('Entry', function () { + this.timeout(5000) - const { identityKeyFixtures, signingKeyFixtures, identityKeysPath } = config + let keystore + let identities + let testIdentity - let keystore - let identities - let testIdentity - let ipfsd, ipfs + before(async () => { + await copy(testKeysPath, keysPath) + keystore = await KeyStore({ path: keysPath }) + identities = await Identities({ keystore }) + testIdentity = await identities.createIdentity({ id: 'userA' }) + }) - before(async () => { - ipfsd = await startIpfs(IPFS, config.daemon1) - ipfs = ipfsd.api - - await copy(identityKeyFixtures, identityKeysPath) - await copy(signingKeyFixtures, identityKeysPath) - - keystore = await KeyStore({ path: testKeysPath }) - - identities = await Identities({ keystore, ipfs }) - testIdentity = await identities.createIdentity({ id: 'userA' }) - }) - - after(async () => { + after(async () => { + if (keystore) { await keystore.close() + } + await rmrf(keysPath) + }) - if (ipfsd) { - await stopIpfs(ipfsd) + describe('create', () => { + it('creates a an empty entry', async () => { + const expectedHash = 'zdpuApShn2wbu8aDWJhmzBtLWmoVF5VBbVVtuszMpscmiUgrH' + const entry = await create(testIdentity, 'A', 'hello') + strictEqual(entry.hash, expectedHash) + strictEqual(entry.id, 'A') + strictEqual(entry.clock.id, testIdentity.publicKey) + strictEqual(entry.clock.time, 0) + strictEqual(entry.v, 2) + strictEqual(entry.payload, 'hello') + strictEqual(entry.next.length, 0) + strictEqual(entry.refs.length, 0) + }) + + it('creates a entry with payload', async () => { + const expectedHash = 'zdpuApKrG9gBpxSqNRQ1Zq8zkkVTS1GQxYoCkXKtDvuNKv4WB' + const payload = 'hello world' + const entry = await create(testIdentity, 'A', payload) + strictEqual(entry.hash, expectedHash) + strictEqual(entry.payload, payload) + strictEqual(entry.id, 'A') + strictEqual(entry.clock.id, testIdentity.publicKey) + strictEqual(entry.clock.time, 0) + strictEqual(entry.v, 2) + strictEqual(entry.next.length, 0) + strictEqual(entry.refs.length, 0) + }) + + it('retrieves the identity from an entry', async () => { + const expected = { + id: testIdentity.id, + publicKey: testIdentity.publicKey, + signatures: testIdentity.signatures, + type: testIdentity.type, + hash: testIdentity.hash, + bytes: testIdentity.bytes, + sign: undefined, + verify: undefined } + const payload = 'hello world' + const entry = await create(testIdentity, 'A', payload) + const entryIdentity = await identities.getIdentity(entry.identity) - rmrf(identityKeysPath) + deepStrictEqual(entryIdentity, expected) }) - describe('create', () => { - it('creates a an empty entry', async () => { - // const expectedHash = 'zdpuAsqjGLA4aAGiSNYeTE5zH6e5ayRpgiZrfN2d3UpmzEF76' - const entry = await create(testIdentity, 'A', 'hello') - // strictEqual(entry.hash, expectedHash) - strictEqual(entry.id, 'A') - strictEqual(entry.clock.id, testIdentity.publicKey) - strictEqual(entry.clock.time, 0) - strictEqual(entry.v, 2) - strictEqual(entry.payload, 'hello') - strictEqual(entry.next.length, 0) - strictEqual(entry.refs.length, 0) - }) - - it('creates a entry with payload', async () => { - // const expectedHash = 'zdpuB2uuvoKD9cmBV8ET5R9KeytY1Jq72LNQrjEpuEyZURP5Q' - const payload = 'hello world' - const entry = await create(testIdentity, 'A', payload) - strictEqual(entry.payload, payload) - strictEqual(entry.id, 'A') - strictEqual(entry.clock.id, testIdentity.publicKey) - strictEqual(entry.clock.time, 0) - strictEqual(entry.v, 2) - strictEqual(entry.next.length, 0) - strictEqual(entry.refs.length, 0) - // strictEqual(entry.hash, expectedHash) - }) - - it('retrieves the identity from an entry', async () => { - const expected = { - id: testIdentity.id, - publicKey: testIdentity.publicKey, - signatures: testIdentity.signatures, - type: testIdentity.type, - hash: testIdentity.hash, - bytes: testIdentity.bytes, - sign: undefined, - verify: undefined - } - const payload = 'hello world' - const entry = await create(testIdentity, 'A', payload) - const entryIdentity = await identities.getIdentity(entry.identity) - - deepStrictEqual(entryIdentity, expected) - }) - - it('creates a entry with payload and next', async () => { - // const expectedHash = 'zdpuApstRF3DCyuuNhPks8sG2qXPf6BFbMA7EeaGrn9Y6ZEzQ' - const payload1 = 'hello world' - const payload2 = 'hello again' - const entry1 = await create(testIdentity, 'A', payload1) - entry1.clock.tick() - const entry2 = await create(testIdentity, 'A', payload2, entry1.clock, [entry1]) - strictEqual(entry2.payload, payload2) - strictEqual(entry2.next.length, 1) - // strictEqual(entry2.hash, expectedHash) - strictEqual(entry2.clock.id, testIdentity.publicKey) - strictEqual(entry2.clock.time, 1) - }) - - it('`next` parameter can be an array of strings', async () => { - const entry1 = await create(testIdentity, 'A', 'hello1') - const entry2 = await create(testIdentity, 'A', 'hello2', null, [entry1.hash]) - strictEqual(typeof entry2.next[0] === 'string', true) - }) - - it('throws an error if no params are defined', async () => { - let err - try { - await create() - } catch (e) { - err = e - } - strictEqual(err.message, 'Identity is required, cannot create entry') - }) - - it('throws an error if identity are not defined', async () => { - let err - try { - await create(null, 'A', 'hello2') - } catch (e) { - err = e - } - strictEqual(err.message, 'Identity is required, cannot create entry') - }) - - it('throws an error if id is not defined', async () => { - let err - try { - await create(testIdentity, null, 'hello') - } catch (e) { - err = e - } - strictEqual(err.message, 'Entry requires an id') - }) - - it('throws an error if payload is not defined', async () => { - let err - try { - await create(testIdentity, 'A', null) - } catch (e) { - err = e - } - strictEqual(err.message, 'Entry requires a payload') - }) - - it('throws an error if next is not an array', async () => { - let err - try { - await create(testIdentity, 'A', 'hello', null, {}) - } catch (e) { - err = e - } - strictEqual(err.message, '\'next\' argument is not an array') - }) + it('creates a entry with payload and next', async () => { + // const expectedHash = 'zdpuApstRF3DCyuuNhPks8sG2qXPf6BFbMA7EeaGrn9Y6ZEzQ' + const payload1 = 'hello world' + const payload2 = 'hello again' + const entry1 = await create(testIdentity, 'A', payload1) + entry1.clock.tick() + const entry2 = await create(testIdentity, 'A', payload2, entry1.clock, [entry1]) + strictEqual(entry2.payload, payload2) + strictEqual(entry2.next.length, 1) + // strictEqual(entry2.hash, expectedHash) + strictEqual(entry2.clock.id, testIdentity.publicKey) + strictEqual(entry2.clock.time, 1) }) - describe('isEntry', () => { - it('is an Entry', async () => { - const entry = await create(testIdentity, 'A', 'hello') - strictEqual(isEntry(entry), true) - }) + it('`next` parameter can be an array of strings', async () => { + const entry1 = await create(testIdentity, 'A', 'hello1') + const entry2 = await create(testIdentity, 'A', 'hello2', null, [entry1.hash]) + strictEqual(typeof entry2.next[0] === 'string', true) + }) - it('is not an Entry - no id', async () => { - const fakeEntry = { next: [], v: 1, hash: 'Foo', payload: 123, seq: 0 } - strictEqual(isEntry(fakeEntry), false) - }) + it('throws an error if no params are defined', async () => { + let err + try { + await create() + } catch (e) { + err = e + } + strictEqual(err.message, 'Identity is required, cannot create entry') + }) - it('is not an Entry - no seq', async () => { - const fakeEntry = { next: [], v: 1, hash: 'Foo', payload: 123 } - strictEqual(isEntry(fakeEntry), false) - }) + it('throws an error if identity are not defined', async () => { + let err + try { + await create(null, 'A', 'hello2') + } catch (e) { + err = e + } + strictEqual(err.message, 'Identity is required, cannot create entry') + }) - it('is not an Entry - no next', async () => { - const fakeEntry = { id: 'A', v: 1, hash: 'Foo', payload: 123, seq: 0 } - strictEqual(isEntry(fakeEntry), false) - }) + it('throws an error if id is not defined', async () => { + let err + try { + await create(testIdentity, null, 'hello') + } catch (e) { + err = e + } + strictEqual(err.message, 'Entry requires an id') + }) - it('is not an Entry - no version', async () => { - const fakeEntry = { id: 'A', next: [], payload: 123, seq: 0 } - strictEqual(isEntry(fakeEntry), false) - }) + it('throws an error if payload is not defined', async () => { + let err + try { + await create(testIdentity, 'A', null) + } catch (e) { + err = e + } + strictEqual(err.message, 'Entry requires a payload') + }) - it('is not an Entry - no hash', async () => { - const fakeEntry = { id: 'A', v: 1, next: [], payload: 123, seq: 0 } - strictEqual(isEntry(fakeEntry), false) - }) + it('throws an error if next is not an array', async () => { + let err + try { + await create(testIdentity, 'A', 'hello', null, {}) + } catch (e) { + err = e + } + strictEqual(err.message, '\'next\' argument is not an array') + }) + }) - it('is not an Entry - no payload', async () => { - const fakeEntry = { id: 'A', v: 1, next: [], hash: 'Foo', seq: 0 } - strictEqual(isEntry(fakeEntry), false) - }) + describe('isEntry', () => { + it('is an Entry', async () => { + const entry = await create(testIdentity, 'A', 'hello') + strictEqual(isEntry(entry), true) + }) + + it('is not an Entry - no id', async () => { + const fakeEntry = { next: [], v: 1, hash: 'Foo', payload: 123, seq: 0 } + strictEqual(isEntry(fakeEntry), false) + }) + + it('is not an Entry - no seq', async () => { + const fakeEntry = { next: [], v: 1, hash: 'Foo', payload: 123 } + strictEqual(isEntry(fakeEntry), false) + }) + + it('is not an Entry - no next', async () => { + const fakeEntry = { id: 'A', v: 1, hash: 'Foo', payload: 123, seq: 0 } + strictEqual(isEntry(fakeEntry), false) + }) + + it('is not an Entry - no version', async () => { + const fakeEntry = { id: 'A', next: [], payload: 123, seq: 0 } + strictEqual(isEntry(fakeEntry), false) + }) + + it('is not an Entry - no hash', async () => { + const fakeEntry = { id: 'A', v: 1, next: [], payload: 123, seq: 0 } + strictEqual(isEntry(fakeEntry), false) + }) + + it('is not an Entry - no payload', async () => { + const fakeEntry = { id: 'A', v: 1, next: [], hash: 'Foo', seq: 0 } + strictEqual(isEntry(fakeEntry), false) }) }) }) diff --git a/test/oplog/heads.test.js b/test/oplog/heads.test.js index d39ce1c..82eede9 100644 --- a/test/oplog/heads.test.js +++ b/test/oplog/heads.test.js @@ -1,184 +1,171 @@ import { strictEqual, deepStrictEqual } from 'assert' -import rimraf from 'rimraf' +import rmrf from 'rimraf' import { copy } from 'fs-extra' -import { Log } from '../../src/oplog/index.js' -import { Identities } from '../../src/identities/index.js' -import KeyStore from '../../src/key-store.js' -import MemoryStorage from '../../src/storage/memory.js' -import { config, testAPIs } from 'orbit-db-test-utils' +import { Log, Identities, KeyStore } from '../../src/index.js' import testKeysPath from '../fixtures/test-keys-path.js ' -const { sync: rmrf } = rimraf - -let testIdentity +const keysPath = './testkeys' const last = (arr) => { return arr[arr.length - 1] } -Object.keys(testAPIs).forEach((IPFS) => { - describe('Log - Heads (' + IPFS + ')', function () { - this.timeout(config.timeout) +describe('Log - Heads', function () { + this.timeout(5000) - const { identityKeyFixtures, signingKeyFixtures, identityKeysPath } = config + let keystore + let identities + let testIdentity - let keystore - let identities + before(async () => { + await copy(testKeysPath, keysPath) + keystore = await KeyStore({ path: keysPath }) + identities = await Identities({ keystore }) + testIdentity = await identities.createIdentity({ id: 'userA' }) + }) - before(async () => { - rmrf(identityKeysPath) - - await copy(identityKeyFixtures, identityKeysPath) - await copy(signingKeyFixtures, identityKeysPath) - - keystore = await KeyStore({ path: testKeysPath }) - const storage = await MemoryStorage() - - identities = await Identities({ keystore, storage }) - testIdentity = await identities.createIdentity({ id: 'userA' }) - }) - - after(async () => { + after(async () => { + if (keystore) { await keystore.close() - rmrf(identityKeysPath) - }) + } + await rmrf(keysPath) + }) - it('finds one head after one entry', async () => { - const log1 = await Log(testIdentity, { logId: 'A' }) - await log1.append('helloA1') - strictEqual((await log1.heads()).length, 1) - }) + it('finds one head after one entry', async () => { + const log1 = await Log(testIdentity, { logId: 'A' }) + await log1.append('helloA1') + strictEqual((await log1.heads()).length, 1) + }) - it('finds one head after two entries', async () => { - const log1 = await Log(testIdentity, { logId: 'A' }) - await log1.append('helloA1') - await log1.append('helloA2') - strictEqual((await log1.heads()).length, 1) - }) + it('finds one head after two entries', async () => { + const log1 = await Log(testIdentity, { logId: 'A' }) + await log1.append('helloA1') + await log1.append('helloA2') + strictEqual((await log1.heads()).length, 1) + }) - it('latest entry is the the head', async () => { - const log1 = await Log(testIdentity, { logId: 'A' }) - await log1.append('helloA1') - const entry = await log1.append('helloA2') - deepStrictEqual(entry.hash, (await log1.heads())[0].hash) - }) + it('latest entry is the the head', async () => { + const log1 = await Log(testIdentity, { logId: 'A' }) + await log1.append('helloA1') + const entry = await log1.append('helloA2') + deepStrictEqual(entry.hash, (await log1.heads())[0].hash) + }) - it('finds head after a join and append', async () => { - const log1 = await Log(testIdentity, { logId: 'A' }) - const log2 = await Log(testIdentity, { logId: 'A' }) + it('finds head after a join and append', async () => { + const log1 = await Log(testIdentity, { logId: 'A' }) + const log2 = await Log(testIdentity, { logId: 'A' }) - await log1.append('helloA1') - await log1.append('helloA2') - await log2.append('helloB1') + await log1.append('helloA1') + await log1.append('helloA2') + await log2.append('helloB1') - await log2.join(log1) - await log2.append('helloB2') - const expectedHead = last(await log2.values()) + await log2.join(log1) + await log2.append('helloB2') + const expectedHead = last(await log2.values()) - const heads = await log2.heads() - strictEqual(heads.length, 1) - deepStrictEqual(heads[0].hash, expectedHead.hash) - }) + const heads = await log2.heads() + strictEqual(heads.length, 1) + deepStrictEqual(heads[0].hash, expectedHead.hash) + }) - it('finds two heads after a join', async () => { - const log2 = await Log(testIdentity, { logId: 'A' }) - const log1 = await Log(testIdentity, { logId: 'A' }) + it('finds two heads after a join', async () => { + const log2 = await Log(testIdentity, { logId: 'A' }) + const log1 = await Log(testIdentity, { logId: 'A' }) - await log1.append('helloA1') - await log1.append('helloA2') - const expectedHead1 = last(await log1.values()) + await log1.append('helloA1') + await log1.append('helloA2') + const expectedHead1 = last(await log1.values()) - await log2.append('helloB1') - await log2.append('helloB2') - const expectedHead2 = last(await log2.values()) + await log2.append('helloB1') + await log2.append('helloB2') + const expectedHead2 = last(await log2.values()) - await log1.join(log2) + await log1.join(log2) - const heads = await log1.heads() - strictEqual(heads.length, 2) - strictEqual(heads[0].hash, expectedHead2.hash) - strictEqual(heads[1].hash, expectedHead1.hash) - }) + const heads = await log1.heads() + strictEqual(heads.length, 2) + strictEqual(heads[0].hash, expectedHead2.hash) + strictEqual(heads[1].hash, expectedHead1.hash) + }) - it('finds two heads after two joins', async () => { - const log1 = await Log(testIdentity, { logId: 'A' }) - const log2 = await Log(testIdentity, { logId: 'A' }) + it('finds two heads after two joins', async () => { + const log1 = await Log(testIdentity, { logId: 'A' }) + const log2 = await Log(testIdentity, { logId: 'A' }) - await log1.append('helloA1') - await log1.append('helloA2') + await log1.append('helloA1') + await log1.append('helloA2') - await log2.append('helloB1') - await log2.append('helloB2') + await log2.append('helloB1') + await log2.append('helloB2') - await log1.join(log2) + await log1.join(log2) - await log2.append('helloB3') + await log2.append('helloB3') - await log1.append('helloA3') - await log1.append('helloA4') - const expectedHead2 = last(await log2.values()) - const expectedHead1 = last(await log1.values()) + await log1.append('helloA3') + await log1.append('helloA4') + const expectedHead2 = last(await log2.values()) + const expectedHead1 = last(await log1.values()) - await log1.join(log2) + await log1.join(log2) - const heads = await log1.heads() - strictEqual(heads.length, 2) - strictEqual(heads[0].hash, expectedHead1.hash) - strictEqual(heads[1].hash, expectedHead2.hash) - }) + const heads = await log1.heads() + strictEqual(heads.length, 2) + strictEqual(heads[0].hash, expectedHead1.hash) + strictEqual(heads[1].hash, expectedHead2.hash) + }) - it('finds two heads after three joins', async () => { - const log1 = await Log(testIdentity, { logId: 'A' }) - const log2 = await Log(testIdentity, { logId: 'A' }) - const log3 = await Log(testIdentity, { logId: 'A' }) + it('finds two heads after three joins', async () => { + const log1 = await Log(testIdentity, { logId: 'A' }) + const log2 = await Log(testIdentity, { logId: 'A' }) + const log3 = await Log(testIdentity, { logId: 'A' }) - await log1.append('helloA1') - await log1.append('helloA2') - await log2.append('helloB1') - await log2.append('helloB2') - await log1.join(log2) - await log1.append('helloA3') - await log1.append('helloA4') - const expectedHead1 = last(await log1.values()) - await log3.append('helloC1') - await log3.append('helloC2') - await log2.join(log3) - await log2.append('helloB3') - const expectedHead2 = last(await log2.values()) - await log1.join(log2) + await log1.append('helloA1') + await log1.append('helloA2') + await log2.append('helloB1') + await log2.append('helloB2') + await log1.join(log2) + await log1.append('helloA3') + await log1.append('helloA4') + const expectedHead1 = last(await log1.values()) + await log3.append('helloC1') + await log3.append('helloC2') + await log2.join(log3) + await log2.append('helloB3') + const expectedHead2 = last(await log2.values()) + await log1.join(log2) - const heads = await log1.heads() - strictEqual(heads.length, 2) - strictEqual(heads[0].hash, expectedHead1.hash) - strictEqual(heads[1].hash, expectedHead2.hash) - }) + const heads = await log1.heads() + strictEqual(heads.length, 2) + strictEqual(heads[0].hash, expectedHead1.hash) + strictEqual(heads[1].hash, expectedHead2.hash) + }) - it('finds three heads after three joins', async () => { - const log1 = await Log(testIdentity, { logId: 'A' }) - const log2 = await Log(testIdentity, { logId: 'A' }) - const log3 = await Log(testIdentity, { logId: 'A' }) + it('finds three heads after three joins', async () => { + const log1 = await Log(testIdentity, { logId: 'A' }) + const log2 = await Log(testIdentity, { logId: 'A' }) + const log3 = await Log(testIdentity, { logId: 'A' }) - await log1.append('helloA1') - await log1.append('helloA2') - await log2.append('helloB1') - await log2.append('helloB2') - await log1.join(log2) - await log1.append('helloA3') - await log1.append('helloA4') - const expectedHead1 = last(await log1.values()) - await log3.append('helloC1') - await log2.append('helloB3') - await log3.append('helloC2') - const expectedHead2 = last(await log2.values()) - const expectedHead3 = last(await log3.values()) - await log1.join(log2) - await log1.join(log3) + await log1.append('helloA1') + await log1.append('helloA2') + await log2.append('helloB1') + await log2.append('helloB2') + await log1.join(log2) + await log1.append('helloA3') + await log1.append('helloA4') + const expectedHead1 = last(await log1.values()) + await log3.append('helloC1') + await log2.append('helloB3') + await log3.append('helloC2') + const expectedHead2 = last(await log2.values()) + const expectedHead3 = last(await log3.values()) + await log1.join(log2) + await log1.join(log3) - const heads = await log1.heads() - strictEqual(heads.length, 3) - deepStrictEqual(heads[0].hash, expectedHead1.hash) - deepStrictEqual(heads[1].hash, expectedHead2.hash) - deepStrictEqual(heads[2].hash, expectedHead3.hash) - }) + const heads = await log1.heads() + strictEqual(heads.length, 3) + deepStrictEqual(heads[0].hash, expectedHead1.hash) + deepStrictEqual(heads[1].hash, expectedHead2.hash) + deepStrictEqual(heads[2].hash, expectedHead3.hash) }) }) diff --git a/test/oplog/iterator.test.js b/test/oplog/iterator.test.js index 3905d8d..6568483 100644 --- a/test/oplog/iterator.test.js +++ b/test/oplog/iterator.test.js @@ -1,490 +1,466 @@ import { strictEqual, deepStrictEqual } from 'assert' -import rimraf from 'rimraf' -import { Log } from '../../src/oplog/index.js' -import { Identities } from '../../src/identities/index.js' -import KeyStore from '../../src/key-store.js' -import LogCreator from './utils/log-creator.js' import all from 'it-all' -import MemoryStorage from '../../src/storage/memory.js' -import LevelStorage from '../../src/storage/level.js' +import rmrf from 'rimraf' +import { copy } from 'fs-extra' +import { Log, Identities, KeyStore } from '../../src/index.js' +import LogCreator from './utils/log-creator.js' +import testKeysPath from '../fixtures/test-keys-path.js ' -// Test utils -import { config, testAPIs, startIpfs, stopIpfs } from 'orbit-db-test-utils' -import { identityKeys, signingKeys } from '../fixtures/orbit-db-identity-keys.js' - -const { sync: rmrf } = rimraf const { createLogWithSixteenEntries } = LogCreator -Object.keys(testAPIs).forEach((IPFS) => { - describe('Log - Iterator (' + IPFS + ')', function () { - this.timeout(config.timeout) +const keysPath = './testkeys' - let ipfs - let ipfsd - let keystore - let identities1, identities2, identities3 - let testIdentity, testIdentity2, testIdentity3 +describe('Log - Iterator', function () { + this.timeout(5000) + + let keystore + let identities1, identities2, identities3 + let testIdentity, testIdentity2, testIdentity3 + + before(async () => { + await copy(testKeysPath, keysPath) + keystore = await KeyStore({ path: keysPath }) + identities1 = await Identities({ keystore }) + identities2 = await Identities({ keystore }) + identities3 = await Identities({ keystore }) + testIdentity = await identities1.createIdentity({ id: 'userC' }) + testIdentity2 = await identities2.createIdentity({ id: 'userB' }) + testIdentity3 = await identities3.createIdentity({ id: 'userC' }) + }) + + after(async () => { + if (keystore) { + await keystore.close() + } + await rmrf(keysPath) + }) + + describe('Basic iterator functionality', async () => { + let log1 + let startHash + const hashes = [] + const logSize = 100 + + beforeEach(async () => { + log1 = await Log(testIdentity, { logId: 'X' }) + + for (let i = 0; i < logSize; i++) { + const entry = await log1.append('entry' + i) + hashes.push([entry.hash, hashes.length]) + } + + // entry67 + // startHash = 'zdpuAxCuaH2R7AYKZ6ZBeeA94v3FgmHZ8wCfDy7pLVcoc3zSo' + startHash = hashes[67][0] + strictEqual(startHash, hashes[67][0]) + }) + + it('returns length with lte and amount', async () => { + const amount = 10 + + const it = log1.iterator({ + lte: startHash, + amount + }) + + const result = await all(it) + + strictEqual([...result].length, 10) + strictEqual(result[0].hash, startHash) + }) + + it('returns entries with lte and amount', async () => { + const amount = 10 + + const it = log1.iterator({ + lte: startHash, + amount + }) + + let i = 0 + for await (const entry of it) { + strictEqual(entry.payload, 'entry' + (67 - i++)) + } + + strictEqual(i, amount) + }) + + it('returns length with lt and amount', async () => { + const amount = 10 + + const it = log1.iterator({ + lt: startHash, + amount + }) + + const result = await all(it) + + strictEqual([...result].length, amount) + }) + + it('returns entries with lt and amount', async () => { + const amount = 10 + + const it = log1.iterator({ + lt: startHash, + amount + }) + + let i = 0 + for await (const entry of it) { + strictEqual(entry.payload, 'entry' + (66 - i++)) + } + + strictEqual(i, amount) + }) + + it('returns correct length with gt and amount', async () => { + const amount = 5 + + const it = log1.iterator({ + gt: startHash, + amount + }) + + let i = 0 + for await (const entry of it) { + strictEqual(entry.payload, 'entry' + (72 - i++)) + } + + strictEqual(i, amount) + }) + + it('returns length with gte and amount', async () => { + const amount = 12 + + const it = log1.iterator({ + gte: startHash, + amount + }) + + const result = await all(it) + + strictEqual([...result].length, amount) + strictEqual(result[result.length - 1].hash, startHash) + }) + + it('returns entries with gte and amount', async () => { + const amount = 12 + + const it = log1.iterator({ + gte: startHash, + amount + }) + + let i = 0 + for await (const entry of it) { + strictEqual(entry.payload, 'entry' + (78 - i++)) + } + + strictEqual(i, amount) + }) + + it('iterates with lt and gt', async () => { + const expectedHashes = hashes.slice().slice(0, 12).map(e => e[0]) + const it = log1.iterator({ + gt: expectedHashes[0], + lt: expectedHashes[expectedHashes.length - 1] + }) + const result = await all(it) + const hashes_ = await Promise.all([...result.reverse()].map(e => e.hash)) + + strictEqual(hashes_.length, 10) + + let i = 0 + for (const h of hashes_) { + strictEqual(expectedHashes[i + 1], h) + i++ + } + strictEqual(i, 10) + }) + + it('iterates with lt and gte', async () => { + const expectedHashes = hashes.slice().slice(0, 26).map(e => e[0]) + const it = log1.iterator({ + gte: expectedHashes[0], + lt: expectedHashes[expectedHashes.length - 1] + }) + const result = await all(it) + const hashes_ = await Promise.all([...result].map(e => e.hash)) + + strictEqual(hashes_.length, 25) + strictEqual(hashes_.indexOf(expectedHashes[0]), 24) + strictEqual(hashes_.indexOf(expectedHashes[expectedHashes.length - 1]), -1) + strictEqual(hashes_.indexOf(expectedHashes[expectedHashes.length - 2]), 0) + + let i = 0 + for (const h of hashes_) { + strictEqual(expectedHashes[expectedHashes.length - 2 - i], h) + i++ + } + strictEqual(i, 25) + }) + + it('iterates with lte and gt', async () => { + const expectedHashes = hashes.slice().slice(0, 5).map(e => e[0]) + const it = log1.iterator({ + gt: expectedHashes[0], + lte: expectedHashes[expectedHashes.length - 1] + }) + const result = await all(it) + const hashes_ = await Promise.all([...result].map(e => e.hash)) + + strictEqual(hashes_.length, 4) + strictEqual(hashes_.indexOf(expectedHashes[0]), -1) + strictEqual(hashes_.indexOf(expectedHashes[expectedHashes.length - 1]), 0) + strictEqual(hashes_.indexOf(expectedHashes[expectedHashes.length - 2]), 1) + strictEqual(hashes_.indexOf(expectedHashes[expectedHashes.length - 3]), 2) + strictEqual(hashes_.indexOf(expectedHashes[expectedHashes.length - 4]), 3) + }) + + it('iterates with lte and gte', async () => { + const expectedHashes = hashes.slice().slice(0, 10).map(e => e[0]) + const it = log1.iterator({ + gte: expectedHashes[0], + lte: expectedHashes[expectedHashes.length - 1] + }) + const result = await all(it) + const hashes_ = await Promise.all([...result].map(e => e.hash)) + + strictEqual(hashes_.length, 10) + strictEqual(hashes_.indexOf(expectedHashes[0]), 9) + strictEqual(hashes_.indexOf(expectedHashes[expectedHashes.length - 1]), 0) + + let i = 0 + for (const h of hashes_) { + strictEqual(expectedHashes[expectedHashes.length - 1 - i], h) + i++ + } + strictEqual(i, 10) + }) + + it('iterates the full log by default', async () => { + const expectedHashes = hashes.slice().map(e => e[0]) + const it = log1.iterator({}) + const result = await all(it) + const hashes_ = await Promise.all([...result].map(e => e.hash)) + + strictEqual(hashes_.length, logSize) + + let i = 0 + for (const h of hashes_) { + strictEqual(expectedHashes[expectedHashes.length - 1 - i], h) + i++ + } + + strictEqual(i, logSize) + }) + + it('iterates the full log with gte and lte and amount', async () => { + const expectedHashes = hashes.slice().map(e => e[0]) + const it = log1.iterator({ + gte: expectedHashes[0], + lte: expectedHashes[expectedHashes.length - 1], + amount: logSize + }) + const result = await all(it) + const hashes_ = await Promise.all([...result].map(e => e.hash)) + + strictEqual(hashes_.length, logSize) + + let i = 0 + for (const h of hashes_) { + strictEqual(expectedHashes[expectedHashes.length - 1 - i], h) + i++ + } + + strictEqual(i, logSize) + }) + + it('returns length with gt and default amount', async () => { + const it = log1.iterator({ + gt: startHash + }) + + const result = await all(it) + + strictEqual([...result].length, 32) + }) + + it('returns entries with gt and default amount', async () => { + const it = log1.iterator({ + gt: startHash + }) + + let i = 0 + for await (const entry of it) { + strictEqual(entry.payload, 'entry' + (logSize - 1 - i++)) + } + + strictEqual(i, 32) + }) + + it('returns length with gte and default amount', async () => { + const it = await log1.iterator({ + gte: startHash + }) + + const result = await all(it) + + strictEqual([...result].length, 33) + }) + + it('returns entries with gte and default amount', async () => { + const it = log1.iterator({ + gte: startHash + }) + + let i = 0 + for await (const entry of it) { + strictEqual(entry.payload, 'entry' + (logSize - 1 - i++)) + } + + strictEqual(i, 33) + }) + + it('returns length with lt and default amount value', async () => { + const it = log1.iterator({ + lt: startHash + }) + + const result = await all(it) + + strictEqual([...result].length, 67) + }) + + it('returns entries with lt and default amount value', async () => { + const it = log1.iterator({ + lt: startHash + }) + + let i = 0 + for await (const entry of it) { + strictEqual(entry.payload, 'entry' + (66 - i++)) + } + strictEqual(i, 67) + }) + + it('returns length with lte and default amount value', async () => { + const it = log1.iterator({ + lte: startHash + }) + + const result = await all(it) + + strictEqual([...result].length, 68) + }) + + it('returns entries with lte and default amount value', async () => { + const it = log1.iterator({ + lte: startHash + }) + + let i = 0 + for await (const entry of it) { + strictEqual(entry.payload, 'entry' + (67 - i++)) + } + + strictEqual(i, 68) + }) + + it('returns zero entries when amount is 0', async () => { + const it = log1.iterator({ + amount: 0 + }) + + let i = 0 + for await (const entry of it) { // eslint-disable-line no-unused-vars + i++ + } + + strictEqual(i, 0) + }) + }) + + describe('Iteration over forked/joined logs', async () => { + let fixture, identities, heads before(async () => { - keystore = await KeyStore({ storage: await LevelStorage({ path: './keys_1', valueEncoding: 'json' }) }) - - for (const [key, value] of Object.entries(identityKeys)) { - await keystore.addKey(key, value) - } - for (const [key, value] of Object.entries(signingKeys)) { - await keystore.addKey(key, value) - } - - const storage = await MemoryStorage() - - identities1 = await Identities({ keystore, storage }) - identities2 = await Identities({ keystore, storage }) - identities3 = await Identities({ keystore, storage }) - testIdentity = await identities1.createIdentity({ id: 'userA' }) - testIdentity2 = await identities2.createIdentity({ id: 'userB' }) - testIdentity3 = await identities3.createIdentity({ id: 'userC' }) - - ipfsd = await startIpfs(IPFS, config.defaultIpfsConfig) - ipfs = ipfsd.api + identities = [testIdentity3, testIdentity2, testIdentity3, testIdentity] + fixture = await createLogWithSixteenEntries(Log, null, identities) + heads = await fixture.log.heads() }) - after(async () => { - if (ipfsd) { - await stopIpfs(ipfsd) - } - if (keystore) { - await keystore.close() - } - rmrf('./keys_1') + it('returns the full length from all heads', async () => { + const it = fixture.log.iterator({ + lte: heads + }) + + const result = await all(it) + + strictEqual([...result].length, 16) }) - describe('Basic iterator functionality', async () => { - let log1 - let startHash - const hashes = [] - const logSize = 100 - - beforeEach(async () => { - log1 = await Log(testIdentity, { logId: 'X' }) - - for (let i = 0; i < logSize; i++) { - const entry = await log1.append('entry' + i) - hashes.push([entry.hash, hashes.length]) - } - - // entry67 - // startHash = 'zdpuAxCuaH2R7AYKZ6ZBeeA94v3FgmHZ8wCfDy7pLVcoc3zSo' - startHash = hashes[67][0] - strictEqual(startHash, hashes[67][0]) + it('returns partial entries from all heads', async () => { + const it = fixture.log.iterator({ + lte: heads, + amount: 6 }) - it('returns length with lte and amount', async () => { - const amount = 10 + const result = await all(it) - const it = log1.iterator({ - lte: startHash, - amount - }) - - const result = await all(it) - - strictEqual([...result].length, 10) - strictEqual(result[0].hash, startHash) - }) - - it('returns entries with lte and amount', async () => { - const amount = 10 - - const it = log1.iterator({ - lte: startHash, - amount - }) - - let i = 0 - for await (const entry of it) { - strictEqual(entry.payload, 'entry' + (67 - i++)) - } - - strictEqual(i, amount) - }) - - it('returns length with lt and amount', async () => { - const amount = 10 - - const it = log1.iterator({ - lt: startHash, - amount - }) - - const result = await all(it) - - strictEqual([...result].length, amount) - }) - - it('returns entries with lt and amount', async () => { - const amount = 10 - - const it = log1.iterator({ - lt: startHash, - amount - }) - - let i = 0 - for await (const entry of it) { - strictEqual(entry.payload, 'entry' + (66 - i++)) - } - - strictEqual(i, amount) - }) - - it('returns correct length with gt and amount', async () => { - const amount = 5 - - const it = log1.iterator({ - gt: startHash, - amount - }) - - let i = 0 - for await (const entry of it) { - strictEqual(entry.payload, 'entry' + (72 - i++)) - } - - strictEqual(i, amount) - }) - - it('returns length with gte and amount', async () => { - const amount = 12 - - const it = log1.iterator({ - gte: startHash, - amount - }) - - const result = await all(it) - - strictEqual([...result].length, amount) - strictEqual(result[result.length - 1].hash, startHash) - }) - - it('returns entries with gte and amount', async () => { - const amount = 12 - - const it = log1.iterator({ - gte: startHash, - amount - }) - - let i = 0 - for await (const entry of it) { - strictEqual(entry.payload, 'entry' + (78 - i++)) - } - - strictEqual(i, amount) - }) - - it('iterates with lt and gt', async () => { - const expectedHashes = hashes.slice().slice(0, 12).map(e => e[0]) - const it = log1.iterator({ - gt: expectedHashes[0], - lt: expectedHashes[expectedHashes.length - 1] - }) - const result = await all(it) - const hashes_ = await Promise.all([...result.reverse()].map(e => e.hash)) - - strictEqual(hashes_.length, 10) - - let i = 0 - for (const h of hashes_) { - strictEqual(expectedHashes[i + 1], h) - i++ - } - strictEqual(i, 10) - }) - - it('iterates with lt and gte', async () => { - const expectedHashes = hashes.slice().slice(0, 26).map(e => e[0]) - const it = log1.iterator({ - gte: expectedHashes[0], - lt: expectedHashes[expectedHashes.length - 1] - }) - const result = await all(it) - const hashes_ = await Promise.all([...result].map(e => e.hash)) - - strictEqual(hashes_.length, 25) - strictEqual(hashes_.indexOf(expectedHashes[0]), 24) - strictEqual(hashes_.indexOf(expectedHashes[expectedHashes.length - 1]), -1) - strictEqual(hashes_.indexOf(expectedHashes[expectedHashes.length - 2]), 0) - - let i = 0 - for (const h of hashes_) { - strictEqual(expectedHashes[expectedHashes.length - 2 - i], h) - i++ - } - strictEqual(i, 25) - }) - - it('iterates with lte and gt', async () => { - const expectedHashes = hashes.slice().slice(0, 5).map(e => e[0]) - const it = log1.iterator({ - gt: expectedHashes[0], - lte: expectedHashes[expectedHashes.length - 1] - }) - const result = await all(it) - const hashes_ = await Promise.all([...result].map(e => e.hash)) - - strictEqual(hashes_.length, 4) - strictEqual(hashes_.indexOf(expectedHashes[0]), -1) - strictEqual(hashes_.indexOf(expectedHashes[expectedHashes.length - 1]), 0) - strictEqual(hashes_.indexOf(expectedHashes[expectedHashes.length - 2]), 1) - strictEqual(hashes_.indexOf(expectedHashes[expectedHashes.length - 3]), 2) - strictEqual(hashes_.indexOf(expectedHashes[expectedHashes.length - 4]), 3) - }) - - it('iterates with lte and gte', async () => { - const expectedHashes = hashes.slice().slice(0, 10).map(e => e[0]) - const it = log1.iterator({ - gte: expectedHashes[0], - lte: expectedHashes[expectedHashes.length - 1] - }) - const result = await all(it) - const hashes_ = await Promise.all([...result].map(e => e.hash)) - - strictEqual(hashes_.length, 10) - strictEqual(hashes_.indexOf(expectedHashes[0]), 9) - strictEqual(hashes_.indexOf(expectedHashes[expectedHashes.length - 1]), 0) - - let i = 0 - for (const h of hashes_) { - strictEqual(expectedHashes[expectedHashes.length - 1 - i], h) - i++ - } - strictEqual(i, 10) - }) - - it('iterates the full log by default', async () => { - const expectedHashes = hashes.slice().map(e => e[0]) - const it = log1.iterator({}) - const result = await all(it) - const hashes_ = await Promise.all([...result].map(e => e.hash)) - - strictEqual(hashes_.length, logSize) - - let i = 0 - for (const h of hashes_) { - strictEqual(expectedHashes[expectedHashes.length - 1 - i], h) - i++ - } - - strictEqual(i, logSize) - }) - - it('iterates the full log with gte and lte and amount', async () => { - const expectedHashes = hashes.slice().map(e => e[0]) - const it = log1.iterator({ - gte: expectedHashes[0], - lte: expectedHashes[expectedHashes.length - 1], - amount: logSize - }) - const result = await all(it) - const hashes_ = await Promise.all([...result].map(e => e.hash)) - - strictEqual(hashes_.length, logSize) - - let i = 0 - for (const h of hashes_) { - strictEqual(expectedHashes[expectedHashes.length - 1 - i], h) - i++ - } - - strictEqual(i, logSize) - }) - - it('returns length with gt and default amount', async () => { - const it = log1.iterator({ - gt: startHash - }) - - const result = await all(it) - - strictEqual([...result].length, 32) - }) - - it('returns entries with gt and default amount', async () => { - const it = log1.iterator({ - gt: startHash - }) - - let i = 0 - for await (const entry of it) { - strictEqual(entry.payload, 'entry' + (logSize - 1 - i++)) - } - - strictEqual(i, 32) - }) - - it('returns length with gte and default amount', async () => { - const it = await log1.iterator({ - gte: startHash - }) - - const result = await all(it) - - strictEqual([...result].length, 33) - }) - - it('returns entries with gte and default amount', async () => { - const it = log1.iterator({ - gte: startHash - }) - - let i = 0 - for await (const entry of it) { - strictEqual(entry.payload, 'entry' + (logSize - 1 - i++)) - } - - strictEqual(i, 33) - }) - - it('returns length with lt and default amount value', async () => { - const it = log1.iterator({ - lt: startHash - }) - - const result = await all(it) - - strictEqual([...result].length, 67) - }) - - it('returns entries with lt and default amount value', async () => { - const it = log1.iterator({ - lt: startHash - }) - - let i = 0 - for await (const entry of it) { - strictEqual(entry.payload, 'entry' + (66 - i++)) - } - strictEqual(i, 67) - }) - - it('returns length with lte and default amount value', async () => { - const it = log1.iterator({ - lte: startHash - }) - - const result = await all(it) - - strictEqual([...result].length, 68) - }) - - it('returns entries with lte and default amount value', async () => { - const it = log1.iterator({ - lte: startHash - }) - - let i = 0 - for await (const entry of it) { - strictEqual(entry.payload, 'entry' + (67 - i++)) - } - - strictEqual(i, 68) - }) - - it('returns zero entries when amount is 0', async () => { - const it = log1.iterator({ - amount: 0 - }) - - let i = 0 - for await (const entry of it) { // eslint-disable-line no-unused-vars - i++ - } - - strictEqual(i, 0) - }) + deepStrictEqual([...result].map(e => e.payload), + ['entryA10', 'entryA9', 'entryA8', 'entryA7', 'entryC0', 'entryA6']) }) - describe('Iteration over forked/joined logs', async () => { - let fixture, identities, heads - - before(async () => { - identities = [testIdentity3, testIdentity2, testIdentity3, testIdentity] - fixture = await createLogWithSixteenEntries(Log, ipfs, identities) - heads = await fixture.log.heads() + it('returns partial logs from single heads #1', async () => { + const it = fixture.log.iterator({ + lte: [heads[0]] }) - it('returns the full length from all heads', async () => { + const result = await all(it) + + strictEqual([...result].length, 10) + }) + + it('returns partial logs from single heads #2', async () => { + const it = fixture.log.iterator({ + lte: [heads[1]] + }) + + const result = await all(it) + + strictEqual([...result].length, 11) + }) + + it('throws error if lte not a string or array of entries', async () => { + let errMsg + + try { const it = fixture.log.iterator({ - lte: heads + lte: false }) + await all(it) + } catch (e) { + errMsg = e.message + } - const result = await all(it) + strictEqual(errMsg, 'lte must be a string or an array of Entries') + }) - strictEqual([...result].length, 16) - }) + it('throws error if lt not a string or array of entries', async () => { + let errMsg - it('returns partial entries from all heads', async () => { + try { const it = fixture.log.iterator({ - lte: heads, - amount: 6 + lt: {} }) + await all(it) + } catch (e) { + errMsg = e.message + } - const result = await all(it) - - deepStrictEqual([...result].map(e => e.payload), - ['entryA10', 'entryA9', 'entryA8', 'entryA7', 'entryC0', 'entryA6']) - }) - - it('returns partial logs from single heads #1', async () => { - const it = fixture.log.iterator({ - lte: [heads[0]] - }) - - const result = await all(it) - - strictEqual([...result].length, 10) - }) - - it('returns partial logs from single heads #2', async () => { - const it = fixture.log.iterator({ - lte: [heads[1]] - }) - - const result = await all(it) - - strictEqual([...result].length, 11) - }) - - it('throws error if lte not a string or array of entries', async () => { - let errMsg - - try { - const it = fixture.log.iterator({ - lte: false - }) - await all(it) - } catch (e) { - errMsg = e.message - } - - strictEqual(errMsg, 'lte must be a string or an array of Entries') - }) - - it('throws error if lt not a string or array of entries', async () => { - let errMsg - - try { - const it = fixture.log.iterator({ - lt: {} - }) - await all(it) - } catch (e) { - errMsg = e.message - } - - strictEqual(errMsg, 'lt must be a string or an array of Entries') - }) + strictEqual(errMsg, 'lt must be a string or an array of Entries') }) }) }) diff --git a/test/oplog/join-concurrent.test.js b/test/oplog/join-concurrent.test.js index 73ff5cd..76ac3aa 100644 --- a/test/oplog/join-concurrent.test.js +++ b/test/oplog/join-concurrent.test.js @@ -1,86 +1,87 @@ import { strictEqual, deepStrictEqual } from 'assert' -import { Log } from '../../src/oplog/index.js' -import { Identities } from '../../src/identities/index.js' -import KeyStore from '../../src/key-store.js' -import { config, testAPIs } from 'orbit-db-test-utils' +import rmrf from 'rimraf' +import { copy } from 'fs-extra' +import { Log, Identities, KeyStore } from '../../src/index.js' import testKeysPath from '../fixtures/test-keys-path.js ' -let testIdentity, testIdentity2 +const keysPath = './testkeys' -Object.keys(testAPIs).forEach(IPFS => { - describe('Log - Join Concurrent Entries (' + IPFS + ')', function () { - this.timeout(config.timeout) +describe('Log - Join Concurrent Entries', function () { + this.timeout(5000) - let keystore - let identities1 + let keystore + let identities1 + let testIdentity, testIdentity2 + + before(async () => { + await copy(testKeysPath, keysPath) + keystore = await KeyStore({ path: keysPath }) + identities1 = await Identities({ keystore }) + testIdentity = await identities1.createIdentity({ id: 'userA' }) + testIdentity2 = await identities1.createIdentity({ id: 'userB' }) + }) + + after(async () => { + if (keystore) { + await keystore.close() + } + await rmrf(keysPath) + }) + + describe('join ', async () => { + let log1, log2 before(async () => { - keystore = await KeyStore({ path: testKeysPath }) - - identities1 = await Identities({ keystore }) - testIdentity = await identities1.createIdentity({ id: 'userA' }) - testIdentity2 = await identities1.createIdentity({ id: 'userB' }) + log1 = await Log(testIdentity, { logId: 'A' }) + log2 = await Log(testIdentity2, { logId: 'A' }) }) - after(async () => { - await keystore.close() + it('joins consistently', async () => { + for (let i = 0; i < 10; i++) { + await log1.append('hello1-' + i) + await log2.append('hello2-' + i) + } + + await log1.join(log2) + await log2.join(log1) + + const values1 = await log1.values() + const values2 = await log2.values() + + strictEqual(values1.length, 20) + strictEqual(values2.length, 20) + deepStrictEqual(values1.map(e => e.payload && e.hash), values2.map(e => e.payload && e.hash)) }) - describe('join ', async () => { - let log1, log2 + it('Concurrently appending same payload after join results in same state', async () => { + for (let i = 10; i < 20; i++) { + await log1.append('hello1-' + i) + await log2.append('hello2-' + i) + } - before(async () => { - log1 = await Log(testIdentity, { logId: 'A' }) - log2 = await Log(testIdentity2, { logId: 'A' }) - }) + await log1.join(log2) + await log2.join(log1) - it('joins consistently', async () => { - for (let i = 0; i < 10; i++) { - await log1.append('hello1-' + i) - await log2.append('hello2-' + i) - } + await log1.append('same') + await log2.append('same') - await log1.join(log2) - await log2.join(log1) + const values1 = await log1.values() + const values2 = await log2.values() - const values1 = await log1.values() - const values2 = await log2.values() + strictEqual(values1.length, 41) + strictEqual(values2.length, 41) + }) - strictEqual(values1.length, 20) - strictEqual(values2.length, 20) - deepStrictEqual(values1.map(e => e.payload && e.hash), values2.map(e => e.payload && e.hash)) - }) + it('Joining after concurrently appending same payload joins entry once', async () => { + await log1.join(log2) + await log2.join(log1) - it('Concurrently appending same payload after join results in same state', async () => { - for (let i = 10; i < 20; i++) { - await log1.append('hello1-' + i) - await log2.append('hello2-' + i) - } + const values1 = await log1.values() + const values2 = await log2.values() - await log1.join(log2) - await log2.join(log1) - - await log1.append('same') - await log2.append('same') - - const values1 = await log1.values() - const values2 = await log2.values() - - strictEqual(values1.length, 41) - strictEqual(values2.length, 41) - }) - - it('Joining after concurrently appending same payload joins entry once', async () => { - await log1.join(log2) - await log2.join(log1) - - const values1 = await log1.values() - const values2 = await log2.values() - - strictEqual(values1.length, 42) - strictEqual(values2.length, 42) - deepStrictEqual(values1.map(e => e.payload && e.hash), values2.map(e => e.payload && e.hash)) - }) + strictEqual(values1.length, 42) + strictEqual(values2.length, 42) + deepStrictEqual(values1.map(e => e.payload && e.hash), values2.map(e => e.payload && e.hash)) }) }) }) diff --git a/test/oplog/join.test.js b/test/oplog/join.test.js index 9ef4b98..c3879cd 100644 --- a/test/oplog/join.test.js +++ b/test/oplog/join.test.js @@ -1,444 +1,428 @@ import { strictEqual, notStrictEqual, deepStrictEqual } from 'assert' -import rimraf from 'rimraf' -import { Log, Clock } from '../../src/oplog/index.js' -import { Identities } from '../../src/identities/index.js' -import KeyStore from '../../src/key-store.js' -import LevelStorage from '../../src/storage/level.js' -import MemoryStorage from '../../src/storage/memory.js' +import rmrf from 'rimraf' +import { copy } from 'fs-extra' +import { Log, Identities, KeyStore } from '../../src/index.js' +import { Clock } from '../../src/oplog/log.js' +import testKeysPath from '../fixtures/test-keys-path.js ' -// Test utils -import { config, testAPIs } from 'orbit-db-test-utils' -import { identityKeys, signingKeys } from '../fixtures/orbit-db-identity-keys.js' - -const { sync: rmrf } = rimraf +const keysPath = './testkeys' const last = (arr) => { return arr[arr.length - 1] } -Object.keys(testAPIs).forEach((IPFS) => { - describe('Log - Join (' + IPFS + ')', async function () { - this.timeout(config.timeout) +describe('Log - Join', async function () { + this.timeout(5000) - let keystore - let log1, log2, log3, log4 - let identities1, identities2, identities3, identities4 - let testIdentity, testIdentity2, testIdentity3, testIdentity4 + let keystore + let log1, log2, log3, log4 + let identities1, identities2, identities3, identities4 + let testIdentity, testIdentity2, testIdentity3, testIdentity4 - before(async () => { - keystore = await KeyStore({ storage: await LevelStorage({ path: './keys_1', valueEncoding: 'json' }) }) + before(async () => { + await copy(testKeysPath, keysPath) + keystore = await KeyStore({ path: keysPath }) + identities1 = await Identities({ keystore }) + identities2 = await Identities({ keystore }) + identities3 = await Identities({ keystore }) + identities4 = await Identities({ keystore }) + testIdentity = await identities1.createIdentity({ id: 'userX' }) + testIdentity2 = await identities2.createIdentity({ id: 'userA' }) + testIdentity3 = await identities3.createIdentity({ id: 'userB' }) + testIdentity4 = await identities4.createIdentity({ id: 'userC' }) + }) - for (const [key, value] of Object.entries(identityKeys)) { - await keystore.addKey(key, value) - } - for (const [key, value] of Object.entries(signingKeys)) { - await keystore.addKey(key, value) - } + after(async () => { + if (keystore) { + await keystore.close() + } + await rmrf(keysPath) + }) - const storage = await MemoryStorage() + beforeEach(async () => { + log1 = await Log(testIdentity, { logId: 'X' }) + log2 = await Log(testIdentity2, { logId: 'X' }) + log3 = await Log(testIdentity3, { logId: 'X' }) + log4 = await Log(testIdentity4, { logId: 'X' }) + }) - identities1 = await Identities({ keystore, storage }) - identities2 = await Identities({ keystore, storage }) - identities3 = await Identities({ keystore, storage }) - identities4 = await Identities({ keystore, storage }) - testIdentity = await identities1.createIdentity({ id: 'userC' }) - testIdentity2 = await identities2.createIdentity({ id: 'userB' }) - testIdentity3 = await identities3.createIdentity({ id: 'userD' }) - testIdentity4 = await identities4.createIdentity({ id: 'userA' }) + it('joins logs', async () => { + const items1 = [] + const items2 = [] + const amount = 20 + + for (let i = 1; i <= amount; i++) { + const n1 = await log1.append('entryA' + i) + const n2 = await log2.append('entryB' + i) + items1.push(n1) + items2.push(n2) + } + + strictEqual(items1.length, amount) + strictEqual(items2.length, amount) + + const valuesA = await log1.values() + const valuesB = await log2.values() + + strictEqual(valuesA.length, amount) + strictEqual(valuesB.length, amount) + + await log1.join(log2) + + const valuesC = await log1.values() + + strictEqual(valuesC.length, amount * 2) + strictEqual((await log1.heads()).length, 2) + }) + + it('throws an error if first log is not defined', async () => { + let err + try { + await log1.join() + } catch (e) { + err = e + } + notStrictEqual(err, null) + strictEqual(err.message, 'Log instance not defined') + }) + + it('throws an error if passed argument is not an instance of Log', async () => { + let err + try { + await log1.join({}) + } catch (e) { + err = e + } + notStrictEqual(err, null) + strictEqual(err.message, 'Given argument is not an instance of Log') + }) + + it('throws an error if trying to join an entry from a different log', async () => { + const logIdA = 'AAA' + const logIdB = 'BBB' + let err + try { + const logA = await Log(testIdentity, { logId: logIdA }) + await logA.append('entryA') + const logB = await Log(testIdentity, { logId: logIdB }) + await logB.append('entryB') + const valuesB = await logB.values() + await logA.joinEntry(last(valuesB)) + } catch (e) { + err = e + } + notStrictEqual(err, null) + strictEqual(err.message, `Entry's id (${logIdB}) doesn't match the log's id (${logIdA}).`) + }) + + it('throws an error if trying to join a different log', async () => { + const logIdA = 'AAA' + const logIdB = 'BBB' + let err + try { + const logA = await Log(testIdentity, { logId: logIdA }) + await logA.append('entryA') + const logB = await Log(testIdentity, { logId: logIdB }) + await logB.append('entryB') + await logA.join(logB) + } catch (e) { + err = e + } + notStrictEqual(err, null) + strictEqual(err.message, `Entry's id (${logIdB}) doesn't match the log's id (${logIdA}).`) + }) + + it('joins only unique items', async () => { + await log1.append('helloA1') + await log1.append('helloA2') + await log2.append('helloB1') + await log2.append('helloB2') + await log1.join(log2) + await log1.join(log2) + + const expectedData = [ + 'helloA1', 'helloB1', 'helloA2', 'helloB2' + ] + + const values = await log1.values() + strictEqual(values.length, 4) + deepStrictEqual(values.map((e) => e.payload), expectedData) + + const item = last(values) + strictEqual(item.next.length, 1) + }) + + it('joins logs two ways', async () => { + await log1.append('helloA1') + await log1.append('helloA2') + await log2.append('helloB1') + await log2.append('helloB2') + await log1.join(log2) + await log2.join(log1) + + const expectedData = [ + 'helloA1', 'helloB1', 'helloA2', 'helloB2' + ] + + const values1 = await log1.values() + const values2 = await log2.values() + + deepStrictEqual(values1.map((e) => e.hash), values2.map((e) => e.hash)) + deepStrictEqual(values1.map((e) => e.payload), expectedData) + deepStrictEqual(values2.map((e) => e.payload), expectedData) + }) + + it('joins logs twice', async () => { + await log1.append('helloA1') + await log2.append('helloB1') + await log2.join(log1) + + await log1.append('helloA2') + await log2.append('helloB2') + await log2.join(log1) + + const expectedData = [ + 'helloA1', 'helloB1', 'helloA2', 'helloB2' + ] + + const values = await log2.values() + + strictEqual(values.length, 4) + deepStrictEqual(values.map((e) => e.payload), expectedData) + }) + + it('joins 2 logs two ways', async () => { + await log1.append('helloA1') + await log2.append('helloB1') + await log2.join(log1) + await log1.join(log2) + await log1.append('helloA2') + await log2.append('helloB2') + await log2.join(log1) + + const expectedData = [ + 'helloA1', 'helloB1', 'helloA2', 'helloB2' + ] + + const values = await log2.values() + + strictEqual(values.length, 4) + deepStrictEqual(values.map((e) => e.payload), expectedData) + }) + + it('joins 2 logs two ways and has the right heads at every step', async () => { + await log1.append('helloA1') + const heads1 = await log1.heads() + strictEqual(heads1.length, 1) + strictEqual(heads1[0].payload, 'helloA1') + + await log2.append('helloB1') + const heads2 = await log2.heads() + strictEqual(heads2.length, 1) + strictEqual(heads2[0].payload, 'helloB1') + + await log2.join(log1) + const heads3 = await log2.heads() + strictEqual(heads3.length, 2) + strictEqual(heads3[0].payload, 'helloB1') + strictEqual(heads3[1].payload, 'helloA1') + + await log1.join(log2) + const heads4 = await log1.heads() + strictEqual(heads4.length, 2) + strictEqual(heads4[0].payload, 'helloB1') + strictEqual(heads4[1].payload, 'helloA1') + + await log1.append('helloA2') + const heads5 = await log1.heads() + strictEqual(heads5.length, 1) + strictEqual(heads5[0].payload, 'helloA2') + + await log2.append('helloB2') + const heads6 = await log2.heads() + strictEqual(heads6.length, 1) + strictEqual(heads6[0].payload, 'helloB2') + + await log2.join(log1) + const heads7 = await log2.heads() + strictEqual(heads7.length, 2) + strictEqual(heads7[0].payload, 'helloB2') + strictEqual(heads7[1].payload, 'helloA2') + }) + + it('joins 4 logs to one', async () => { + // order determined by identity's publicKey + await log1.append('helloA1') + await log1.append('helloA2') + + await log3.append('helloB1') + await log3.append('helloB2') + + await log2.append('helloC1') + await log2.append('helloC2') + + await log4.append('helloD1') + await log4.append('helloD2') + await log1.join(log2) + await log1.join(log3) + await log1.join(log4) + + const expectedData = [ + 'helloA1', + 'helloB1', + 'helloC1', + 'helloD1', + 'helloA2', + 'helloB2', + 'helloC2', + 'helloD2' + ] + + const values = await log1.values() + + strictEqual(values.length, 8) + deepStrictEqual(values.map(e => e.payload), expectedData) + }) + + it('joins 4 logs to one is commutative', async () => { + await log1.append('helloA1') + await log1.append('helloA2') + await log2.append('helloB1') + await log2.append('helloB2') + await log3.append('helloC1') + await log3.append('helloC2') + await log4.append('helloD1') + await log4.append('helloD2') + await log1.join(log2) + await log1.join(log3) + await log1.join(log4) + await log2.join(log1) + await log2.join(log3) + await log2.join(log4) + + const values1 = await log1.values() + const values2 = await log1.values() + + strictEqual(values1.length, 8) + strictEqual(values2.length, 8) + deepStrictEqual(values1.map(e => e.payload), values2.map(e => e.payload)) + }) + + it('joins logs and updates clocks', async () => { + await log1.append('helloA1') + await log2.append('helloB1') + await log2.join(log1) + await log1.append('helloA2') + await log2.append('helloB2') + + strictEqual((await log1.clock()).id, testIdentity.publicKey) + strictEqual((await log2.clock()).id, testIdentity2.publicKey) + strictEqual((await log1.clock()).time, 2) + strictEqual((await log2.clock()).time, 2) + + await log3.join(log1) + strictEqual(log3.id, 'X') + strictEqual((await log3.clock()).id, testIdentity3.publicKey) + strictEqual((await log3.clock()).time, 2) + + await log3.append('helloC1') + await log3.append('helloC2') + await log1.join(log3) + await log1.join(log2) + await log4.append('helloD1') + await log4.append('helloD2') + await log4.join(log2) + await log4.join(log1) + await log4.join(log3) + await log4.append('helloD3') + await log4.append('helloD4') + + await log1.join(log4) + await log4.join(log1) + await log4.append('helloD5') + await log1.append('helloA5') + await log4.join(log1) + strictEqual((await log4.clock()).id, testIdentity4.publicKey) + strictEqual((await log4.clock()).time, 7) + + await log4.append('helloD6') + strictEqual((await log4.clock()).time, 8) + + const expectedData = [ + { payload: 'helloA1', id: 'X', clock: new Clock(testIdentity.publicKey, 1) }, + { payload: 'helloB1', id: 'X', clock: new Clock(testIdentity2.publicKey, 1) }, + { payload: 'helloD1', id: 'X', clock: new Clock(testIdentity4.publicKey, 1) }, + { payload: 'helloA2', id: 'X', clock: new Clock(testIdentity.publicKey, 2) }, + { payload: 'helloB2', id: 'X', clock: new Clock(testIdentity2.publicKey, 2) }, + { payload: 'helloD2', id: 'X', clock: new Clock(testIdentity4.publicKey, 2) }, + { payload: 'helloC1', id: 'X', clock: new Clock(testIdentity3.publicKey, 3) }, + { payload: 'helloC2', id: 'X', clock: new Clock(testIdentity3.publicKey, 4) }, + { payload: 'helloD3', id: 'X', clock: new Clock(testIdentity4.publicKey, 5) }, + { payload: 'helloD4', id: 'X', clock: new Clock(testIdentity4.publicKey, 6) }, + { payload: 'helloA5', id: 'X', clock: new Clock(testIdentity.publicKey, 7) }, + { payload: 'helloD5', id: 'X', clock: new Clock(testIdentity4.publicKey, 7) }, + { payload: 'helloD6', id: 'X', clock: new Clock(testIdentity4.publicKey, 8) } + ] + + const values = await log4.values() + + const transformed = values.map((e) => { + return { payload: e.payload, id: e.id, clock: e.clock } }) - after(async () => { - if (keystore) { - await keystore.close() - } - rmrf('./keys_1') - }) - - beforeEach(async () => { - log1 = await Log(testIdentity, { logId: 'X' }) - log2 = await Log(testIdentity2, { logId: 'X' }) - log3 = await Log(testIdentity3, { logId: 'X' }) - log4 = await Log(testIdentity4, { logId: 'X' }) - }) - - it('joins logs', async () => { - const items1 = [] - const items2 = [] - const amount = 20 - - for (let i = 1; i <= amount; i++) { - const n1 = await log1.append('entryA' + i) - const n2 = await log2.append('entryB' + i) - items1.push(n1) - items2.push(n2) - } - - strictEqual(items1.length, amount) - strictEqual(items2.length, amount) - - const valuesA = await log1.values() - const valuesB = await log2.values() - - strictEqual(valuesA.length, amount) - strictEqual(valuesB.length, amount) - - await log1.join(log2) - - const valuesC = await log1.values() - - strictEqual(valuesC.length, amount * 2) - strictEqual((await log1.heads()).length, 2) - }) - - it('throws an error if first log is not defined', async () => { - let err - try { - await log1.join() - } catch (e) { - err = e - } - notStrictEqual(err, null) - strictEqual(err.message, 'Log instance not defined') - }) - - it('throws an error if passed argument is not an instance of Log', async () => { - let err - try { - await log1.join({}) - } catch (e) { - err = e - } - notStrictEqual(err, null) - strictEqual(err.message, 'Given argument is not an instance of Log') - }) - - it('throws an error if trying to join an entry from a different log', async () => { - const logIdA = 'AAA' - const logIdB = 'BBB' - let err - try { - const logA = await Log(testIdentity, { logId: logIdA }) - await logA.append('entryA') - const logB = await Log(testIdentity, { logId: logIdB }) - await logB.append('entryB') - const valuesB = await logB.values() - await logA.joinEntry(last(valuesB)) - } catch (e) { - err = e - } - notStrictEqual(err, null) - strictEqual(err.message, `Entry's id (${logIdB}) doesn't match the log's id (${logIdA}).`) - }) - - it('throws an error if trying to join a different log', async () => { - const logIdA = 'AAA' - const logIdB = 'BBB' - let err - try { - const logA = await Log(testIdentity, { logId: logIdA }) - await logA.append('entryA') - const logB = await Log(testIdentity, { logId: logIdB }) - await logB.append('entryB') - await logA.join(logB) - } catch (e) { - err = e - } - notStrictEqual(err, null) - strictEqual(err.message, `Entry's id (${logIdB}) doesn't match the log's id (${logIdA}).`) - }) - - it('joins only unique items', async () => { - await log1.append('helloA1') - await log1.append('helloA2') - await log2.append('helloB1') - await log2.append('helloB2') - await log1.join(log2) - await log1.join(log2) - - const expectedData = [ - 'helloA1', 'helloB1', 'helloA2', 'helloB2' - ] - - const values = await log1.values() - strictEqual(values.length, 4) - deepStrictEqual(values.map((e) => e.payload), expectedData) - - const item = last(values) - strictEqual(item.next.length, 1) - }) - - it('joins logs two ways', async () => { - await log1.append('helloA1') - await log1.append('helloA2') - await log2.append('helloB1') - await log2.append('helloB2') - await log1.join(log2) - await log2.join(log1) - - const expectedData = [ - 'helloA1', 'helloB1', 'helloA2', 'helloB2' - ] - - const values1 = await log1.values() - const values2 = await log2.values() - - deepStrictEqual(values1.map((e) => e.hash), values2.map((e) => e.hash)) - deepStrictEqual(values1.map((e) => e.payload), expectedData) - deepStrictEqual(values2.map((e) => e.payload), expectedData) - }) - - it('joins logs twice', async () => { - await log1.append('helloA1') - await log2.append('helloB1') - await log2.join(log1) - - await log1.append('helloA2') - await log2.append('helloB2') - await log2.join(log1) - - const expectedData = [ - 'helloA1', 'helloB1', 'helloA2', 'helloB2' - ] - - const values = await log2.values() - - strictEqual(values.length, 4) - deepStrictEqual(values.map((e) => e.payload), expectedData) - }) - - it('joins 2 logs two ways', async () => { - await log1.append('helloA1') - await log2.append('helloB1') - await log2.join(log1) - await log1.join(log2) - await log1.append('helloA2') - await log2.append('helloB2') - await log2.join(log1) - - const expectedData = [ - 'helloA1', 'helloB1', 'helloA2', 'helloB2' - ] - - const values = await log2.values() - - strictEqual(values.length, 4) - deepStrictEqual(values.map((e) => e.payload), expectedData) - }) - - it('joins 2 logs two ways and has the right heads at every step', async () => { - await log1.append('helloA1') - const heads1 = await log1.heads() - strictEqual(heads1.length, 1) - strictEqual(heads1[0].payload, 'helloA1') - - await log2.append('helloB1') - const heads2 = await log2.heads() - strictEqual(heads2.length, 1) - strictEqual(heads2[0].payload, 'helloB1') - - await log2.join(log1) - const heads3 = await log2.heads() - strictEqual(heads3.length, 2) - strictEqual(heads3[0].payload, 'helloB1') - strictEqual(heads3[1].payload, 'helloA1') - - await log1.join(log2) - const heads4 = await log1.heads() - strictEqual(heads4.length, 2) - strictEqual(heads4[0].payload, 'helloB1') - strictEqual(heads4[1].payload, 'helloA1') - - await log1.append('helloA2') - const heads5 = await log1.heads() - strictEqual(heads5.length, 1) - strictEqual(heads5[0].payload, 'helloA2') - - await log2.append('helloB2') - const heads6 = await log2.heads() - strictEqual(heads6.length, 1) - strictEqual(heads6[0].payload, 'helloB2') - - await log2.join(log1) - const heads7 = await log2.heads() - strictEqual(heads7.length, 2) - strictEqual(heads7[0].payload, 'helloB2') - strictEqual(heads7[1].payload, 'helloA2') - }) - - it('joins 4 logs to one', async () => { - // order determined by identity's publicKey - await log1.append('helloA1') - await log1.append('helloA2') - - await log3.append('helloB1') - await log3.append('helloB2') - - await log2.append('helloC1') - await log2.append('helloC2') - - await log4.append('helloD1') - await log4.append('helloD2') - await log1.join(log2) - await log1.join(log3) - await log1.join(log4) - - const expectedData = [ - 'helloA1', - 'helloB1', - 'helloC1', - 'helloD1', - 'helloA2', - 'helloB2', - 'helloC2', - 'helloD2' - ] - - const values = await log1.values() - - strictEqual(values.length, 8) - deepStrictEqual(values.map(e => e.payload), expectedData) - }) - - it('joins 4 logs to one is commutative', async () => { - await log1.append('helloA1') - await log1.append('helloA2') - await log2.append('helloB1') - await log2.append('helloB2') - await log3.append('helloC1') - await log3.append('helloC2') - await log4.append('helloD1') - await log4.append('helloD2') - await log1.join(log2) - await log1.join(log3) - await log1.join(log4) - await log2.join(log1) - await log2.join(log3) - await log2.join(log4) - - const values1 = await log1.values() - const values2 = await log1.values() - - strictEqual(values1.length, 8) - strictEqual(values2.length, 8) - deepStrictEqual(values1.map(e => e.payload), values2.map(e => e.payload)) - }) - - it('joins logs and updates clocks', async () => { - await log1.append('helloA1') - await log2.append('helloB1') - await log2.join(log1) - await log1.append('helloA2') - await log2.append('helloB2') - - strictEqual((await log1.clock()).id, testIdentity.publicKey) - strictEqual((await log2.clock()).id, testIdentity2.publicKey) - strictEqual((await log1.clock()).time, 2) - strictEqual((await log2.clock()).time, 2) - - await log3.join(log1) - strictEqual(log3.id, 'X') - strictEqual((await log3.clock()).id, testIdentity3.publicKey) - strictEqual((await log3.clock()).time, 2) - - await log3.append('helloC1') - await log3.append('helloC2') - await log1.join(log3) - await log1.join(log2) - await log4.append('helloD1') - await log4.append('helloD2') - await log4.join(log2) - await log4.join(log1) - await log4.join(log3) - await log4.append('helloD3') - await log4.append('helloD4') - - await log1.join(log4) - await log4.join(log1) - await log4.append('helloD5') - await log1.append('helloA5') - await log4.join(log1) - strictEqual((await log4.clock()).id, testIdentity4.publicKey) - strictEqual((await log4.clock()).time, 7) - - await log4.append('helloD6') - strictEqual((await log4.clock()).time, 8) - - const expectedData = [ - { payload: 'helloA1', id: 'X', clock: new Clock(testIdentity.publicKey, 1) }, - { payload: 'helloB1', id: 'X', clock: new Clock(testIdentity2.publicKey, 1) }, - { payload: 'helloD1', id: 'X', clock: new Clock(testIdentity4.publicKey, 1) }, - { payload: 'helloA2', id: 'X', clock: new Clock(testIdentity.publicKey, 2) }, - { payload: 'helloB2', id: 'X', clock: new Clock(testIdentity2.publicKey, 2) }, - { payload: 'helloD2', id: 'X', clock: new Clock(testIdentity4.publicKey, 2) }, - { payload: 'helloC1', id: 'X', clock: new Clock(testIdentity3.publicKey, 3) }, - { payload: 'helloC2', id: 'X', clock: new Clock(testIdentity3.publicKey, 4) }, - { payload: 'helloD3', id: 'X', clock: new Clock(testIdentity4.publicKey, 5) }, - { payload: 'helloD4', id: 'X', clock: new Clock(testIdentity4.publicKey, 6) }, - { payload: 'helloA5', id: 'X', clock: new Clock(testIdentity.publicKey, 7) }, - { payload: 'helloD5', id: 'X', clock: new Clock(testIdentity4.publicKey, 7) }, - { payload: 'helloD6', id: 'X', clock: new Clock(testIdentity4.publicKey, 8) } - ] - - const values = await log4.values() - - const transformed = values.map((e) => { - return { payload: e.payload, id: e.id, clock: e.clock } - }) - - strictEqual(values.length, 13) - deepStrictEqual(transformed, expectedData) - }) - - it('joins logs from 4 logs', async () => { - await log1.append('helloA1') - await log1.join(log2) - await log2.append('helloB1') - await log2.join(log1) - await log1.append('helloA2') - await log2.append('helloB2') - - await log1.join(log3) - strictEqual(log1.id, 'X') - strictEqual((await log1.clock()).id, testIdentity.publicKey) - strictEqual((await log1.clock()).time, 2) - - await log3.join(log1) - strictEqual(log3.id, 'X') - strictEqual((await log3.clock()).id, testIdentity3.publicKey) - strictEqual((await log3.clock()).time, 2) - - await log3.append('helloC1') - await log3.append('helloC2') - await log1.join(log3) - await log1.join(log2) - await log4.append('helloD1') - await log4.append('helloD2') - await log4.join(log2) - await log4.join(log1) - await log4.join(log3) - await log4.append('helloD3') - await log4.append('helloD4') - - strictEqual((await log4.clock()).id, testIdentity4.publicKey) - strictEqual((await log4.clock()).time, 6) - - const expectedData = [ - 'helloA1', - 'helloB1', - 'helloD1', - 'helloA2', - 'helloB2', - 'helloD2', - 'helloC1', - 'helloC2', - 'helloD3', - 'helloD4' - ] - - const values = await log4.values() - - strictEqual(values.length, 10) - deepStrictEqual(values.map((e) => e.payload), expectedData) - }) + strictEqual(values.length, 13) + deepStrictEqual(transformed, expectedData) + }) + + it('joins logs from 4 logs', async () => { + await log1.append('helloA1') + await log1.join(log2) + await log2.append('helloB1') + await log2.join(log1) + await log1.append('helloA2') + await log2.append('helloB2') + + await log1.join(log3) + strictEqual(log1.id, 'X') + strictEqual((await log1.clock()).id, testIdentity.publicKey) + strictEqual((await log1.clock()).time, 2) + + await log3.join(log1) + strictEqual(log3.id, 'X') + strictEqual((await log3.clock()).id, testIdentity3.publicKey) + strictEqual((await log3.clock()).time, 2) + + await log3.append('helloC1') + await log3.append('helloC2') + await log1.join(log3) + await log1.join(log2) + await log4.append('helloD1') + await log4.append('helloD2') + await log4.join(log2) + await log4.join(log1) + await log4.join(log3) + await log4.append('helloD3') + await log4.append('helloD4') + + strictEqual((await log4.clock()).id, testIdentity4.publicKey) + strictEqual((await log4.clock()).time, 6) + + const expectedData = [ + 'helloA1', + 'helloB1', + 'helloD1', + 'helloA2', + 'helloB2', + 'helloD2', + 'helloC1', + 'helloC2', + 'helloD3', + 'helloD4' + ] + + const values = await log4.values() + + strictEqual(values.length, 10) + deepStrictEqual(values.map((e) => e.payload), expectedData) }) }) diff --git a/test/oplog/load.test.js b/test/oplog/load.test.js index 6152642..32f3670 100644 --- a/test/oplog/load.test.js +++ b/test/oplog/load.test.js @@ -2,9 +2,8 @@ import { strictEqual, deepStrictEqual, notStrictEqual, throws } from 'assert' import rimraf from 'rimraf' import { copy } from 'fs-extra' import { Log, Entry, Sorting } from '../../src/oplog/index.js' +import { Identities, KeyStore } from '../../src/index.js' import bigLogString from '../fixtures/big-log.fixture.js' -import { Identities } from '../../src/identities/index.js' -import KeyStore from '../../src/key-store.js' import LogCreator from './utils/log-creator.js' import testKeysPath from '../fixtures/test-keys-path.js ' import { config, testAPIs, startIpfs, stopIpfs } from 'orbit-db-test-utils' diff --git a/test/oplog/log.test.js b/test/oplog/log.test.js index 95b916e..909e5fe 100644 --- a/test/oplog/log.test.js +++ b/test/oplog/log.test.js @@ -1,157 +1,146 @@ import { notStrictEqual, deepStrictEqual, strictEqual } from 'assert' -import rimraf from 'rimraf' -import { Log, Entry } from '../../src/oplog/index.js' -import { Identities } from '../../src/identities/index.js' -import KeyStore from '../../src/key-store.js' +import rmrf from 'rimraf' import { copy } from 'fs-extra' -import MemoryStorage from '../../src/storage/memory.js' -import { config, testAPIs } from 'orbit-db-test-utils' +import { Log, Entry, Identities, KeyStore, MemoryStorage } from '../../src/index.js' import testKeysPath from '../fixtures/test-keys-path.js ' -const { sync: rmrf } = rimraf const { create } = Entry -let testIdentity +const keysPath = './testkeys' -Object.keys(testAPIs).forEach((IPFS) => { - describe('Log (' + IPFS + ')', function () { - this.timeout(config.timeout) +describe('Log', function () { + this.timeout(5000) - const { identityKeyFixtures, signingKeyFixtures, identityKeysPath } = config + let keystore + let identities + let testIdentity - let keystore - let identities + before(async () => { + await copy(testKeysPath, keysPath) + keystore = await KeyStore({ path: keysPath }) + identities = await Identities({ keystore }) + testIdentity = await identities.createIdentity({ id: 'userA' }) + }) - before(async () => { - await copy(identityKeyFixtures, identityKeysPath) - await copy(signingKeyFixtures, identityKeysPath) - - keystore = await KeyStore({ path: testKeysPath }) - - const storage = await MemoryStorage() - - identities = await Identities({ keystore, storage }) - testIdentity = await identities.createIdentity({ id: 'userA' }) - }) - - after(async () => { + after(async () => { + if (keystore) { await keystore.close() - rmrf(identityKeysPath) + } + await rmrf(keysPath) + }) + + describe('create', async () => { + it('creates an empty log with default params', async () => { + const log = await Log(testIdentity) + notStrictEqual(log.heads, undefined) + notStrictEqual(log.id, undefined) + notStrictEqual(log.id, undefined) + notStrictEqual(log.clock(), undefined) + notStrictEqual(await log.heads(), undefined) + deepStrictEqual(await log.heads(), []) + + const values = await log.values() + deepStrictEqual(values, []) }) - describe('create', async () => { - it('creates an empty log with default params', async () => { - const log = await Log(testIdentity) - notStrictEqual(log.heads, undefined) - notStrictEqual(log.id, undefined) - notStrictEqual(log.id, undefined) - notStrictEqual(log.clock(), undefined) - notStrictEqual(await log.heads(), undefined) - deepStrictEqual(await log.heads(), []) - - const values = await log.values() - deepStrictEqual(values, []) - }) - - it('sets an id', async () => { - const log = await Log(testIdentity, { logId: 'ABC' }) - strictEqual(log.id, 'ABC') - }) - - it('sets the clock id', async () => { - const log = await Log(testIdentity, { logId: 'ABC' }) - strictEqual(log.id, 'ABC') - strictEqual((await log.clock()).id, testIdentity.publicKey) - }) - - it('generates id string if id is not passed as an argument', async () => { - const log = await Log(testIdentity) - strictEqual(typeof log.id === 'string', true) - }) - - it('sets one head if multiple are given as params', async () => { - const one = await create(testIdentity, 'A', 'entryA', null, []) - const two = await create(testIdentity, 'A', 'entryB', null, [one.hash]) - const three = await create(testIdentity, 'A', 'entryC', null, [two.hash]) - const four = await create(testIdentity, 'A', 'entryD', null, [two.hash]) - const entryStorage = await MemoryStorage() - await entryStorage.put(one.hash, one.bytes) - await entryStorage.put(two.hash, two.bytes) - await entryStorage.put(three.hash, three.bytes) - await entryStorage.put(four.hash, four.bytes) - const log = await Log(testIdentity, { logId: 'A', logHeads: [three, three, two, two], entryStorage }) - const values = await log.values() - const heads = await log.heads() - strictEqual(heads.length, 1) - strictEqual(heads[0].hash, three.hash) - strictEqual(values.length, 3) - }) - - it('sets two heads if two given as params', async () => { - const one = await create(testIdentity, 'A', 'entryA', null, []) - const two = await create(testIdentity, 'A', 'entryB', null, [one.hash]) - const three = await create(testIdentity, 'A', 'entryC', null, [two.hash]) - const four = await create(testIdentity, 'A', 'entryD', null, [two.hash]) - const entryStorage = await MemoryStorage() - await entryStorage.put(one.hash, one.bytes) - await entryStorage.put(two.hash, two.bytes) - await entryStorage.put(three.hash, three.bytes) - await entryStorage.put(four.hash, four.bytes) - const log = await Log(testIdentity, { logId: 'A', logHeads: [three, four, two], entryStorage }) - const values = await log.values() - const heads = await log.heads() - strictEqual(heads.length, 2) - strictEqual(heads[1].hash, three.hash) - strictEqual(heads[0].hash, four.hash) - strictEqual(values.length, 4) - }) - - it('throws an error if heads is not an array', async () => { - let err - try { - await Log(testIdentity, { logId: 'A', entries: [], logHeads: {} }) - } catch (e) { - err = e - } - notStrictEqual(err, undefined) - strictEqual(err.message, '\'logHeads\' argument must be an array') - }) - - it('creates default public AccessController if not defined', async () => { - const log = await Log(testIdentity) - const anyoneCanAppend = await log.access.canAppend('any') - notStrictEqual(log.access, undefined) - strictEqual(anyoneCanAppend, true) - }) - - it('throws an error if identity is not defined', async () => { - let err - try { - await Log() - } catch (e) { - err = e - } - notStrictEqual(err, undefined) - strictEqual(err.message, 'Identity is required') - }) + it('sets an id', async () => { + const log = await Log(testIdentity, { logId: 'ABC' }) + strictEqual(log.id, 'ABC') }) - describe('values', () => { - it('returns all entries in the log', async () => { - const log = await Log(testIdentity) - let values = await log.values() - strictEqual(values instanceof Array, true) - strictEqual(values.length, 0) - await log.append('hello1') - await log.append('hello2') - await log.append('hello3') - values = await log.values() - strictEqual(values instanceof Array, true) - strictEqual(values.length, 3) - strictEqual(values[0].payload, 'hello1') - strictEqual(values[1].payload, 'hello2') - strictEqual(values[2].payload, 'hello3') - }) + it('sets the clock id', async () => { + const log = await Log(testIdentity, { logId: 'ABC' }) + strictEqual(log.id, 'ABC') + strictEqual((await log.clock()).id, testIdentity.publicKey) + }) + + it('generates id string if id is not passed as an argument', async () => { + const log = await Log(testIdentity) + strictEqual(typeof log.id === 'string', true) + }) + + it('sets one head if multiple are given as params', async () => { + const one = await create(testIdentity, 'A', 'entryA', null, []) + const two = await create(testIdentity, 'A', 'entryB', null, [one.hash]) + const three = await create(testIdentity, 'A', 'entryC', null, [two.hash]) + const four = await create(testIdentity, 'A', 'entryD', null, [two.hash]) + const entryStorage = await MemoryStorage() + await entryStorage.put(one.hash, one.bytes) + await entryStorage.put(two.hash, two.bytes) + await entryStorage.put(three.hash, three.bytes) + await entryStorage.put(four.hash, four.bytes) + const log = await Log(testIdentity, { logId: 'A', logHeads: [three, three, two, two], entryStorage }) + const values = await log.values() + const heads = await log.heads() + strictEqual(heads.length, 1) + strictEqual(heads[0].hash, three.hash) + strictEqual(values.length, 3) + }) + + it('sets two heads if two given as params', async () => { + const one = await create(testIdentity, 'A', 'entryA', null, []) + const two = await create(testIdentity, 'A', 'entryB', null, [one.hash]) + const three = await create(testIdentity, 'A', 'entryC', null, [two.hash]) + const four = await create(testIdentity, 'A', 'entryD', null, [two.hash]) + const entryStorage = await MemoryStorage() + await entryStorage.put(one.hash, one.bytes) + await entryStorage.put(two.hash, two.bytes) + await entryStorage.put(three.hash, three.bytes) + await entryStorage.put(four.hash, four.bytes) + const log = await Log(testIdentity, { logId: 'A', logHeads: [three, four, two], entryStorage }) + const values = await log.values() + const heads = await log.heads() + strictEqual(heads.length, 2) + strictEqual(heads[1].hash, three.hash) + strictEqual(heads[0].hash, four.hash) + strictEqual(values.length, 4) + }) + + it('throws an error if heads is not an array', async () => { + let err + try { + await Log(testIdentity, { logId: 'A', entries: [], logHeads: {} }) + } catch (e) { + err = e + } + notStrictEqual(err, undefined) + strictEqual(err.message, '\'logHeads\' argument must be an array') + }) + + it('creates default public AccessController if not defined', async () => { + const log = await Log(testIdentity) + const anyoneCanAppend = await log.access.canAppend('any') + notStrictEqual(log.access, undefined) + strictEqual(anyoneCanAppend, true) + }) + + it('throws an error if identity is not defined', async () => { + let err + try { + await Log() + } catch (e) { + err = e + } + notStrictEqual(err, undefined) + strictEqual(err.message, 'Identity is required') + }) + }) + + describe('values', () => { + it('returns all entries in the log', async () => { + const log = await Log(testIdentity) + let values = await log.values() + strictEqual(values instanceof Array, true) + strictEqual(values.length, 0) + await log.append('hello1') + await log.append('hello2') + await log.append('hello3') + values = await log.values() + strictEqual(values instanceof Array, true) + strictEqual(values.length, 3) + strictEqual(values[0].payload, 'hello1') + strictEqual(values[1].payload, 'hello2') + strictEqual(values[2].payload, 'hello3') }) }) }) diff --git a/test/oplog/references.test.js b/test/oplog/references.test.js index f5b09bc..8763942 100644 --- a/test/oplog/references.test.js +++ b/test/oplog/references.test.js @@ -1,172 +1,159 @@ import { strictEqual } from 'assert' -import rimraf from 'rimraf' +import rmrf from 'rimraf' import { copy } from 'fs-extra' import { Log } from '../../src/oplog/index.js' -import { Identities } from '../../src/identities/index.js' -import KeyStore from '../../src/key-store.js' -import MemoryStorage from '../../src/storage/memory.js' +import { Identities, KeyStore, MemoryStorage } from '../../src/index.js' import testKeysPath from '../fixtures/test-keys-path.js ' -import { config, testAPIs } from 'orbit-db-test-utils' -const { sync: rmrf } = rimraf +const keysPath = './testkeys' -let testIdentity +describe('Log - References', function () { + this.timeout(60000) -Object.keys(testAPIs).forEach((IPFS) => { - describe('Log - References (' + IPFS + ')', function () { - this.timeout(config.timeout) + let keystore + let identities + let testIdentity - const { identityKeyFixtures, signingKeyFixtures, identityKeysPath } = config + before(async () => { + await copy(testKeysPath, keysPath) + keystore = await KeyStore({ path: keysPath }) + identities = await Identities({ keystore }) + testIdentity = await identities.createIdentity({ id: 'userA' }) + }) - let keystore - let identities - - before(async () => { - rmrf(identityKeysPath) - - await copy(identityKeyFixtures, identityKeysPath) - await copy(signingKeyFixtures, identityKeysPath) - - keystore = await KeyStore({ path: testKeysPath }) - - const storage = await MemoryStorage() - - identities = await Identities({ keystore, storage }) - testIdentity = await identities.createIdentity({ id: 'userA' }) - }) - - after(async () => { + after(async () => { + if (keystore) { await keystore.close() - rmrf(identityKeysPath) + } + await rmrf(keysPath) + }) + + describe('References', async () => { + const amount = 64 + + it('creates entries with 2 references', async () => { + const maxReferenceDistance = 2 + const log1 = await Log(testIdentity, { logId: 'A' }) + + for (let i = 0; i < amount; i++) { + await log1.append(i.toString(), { pointerCount: maxReferenceDistance }) + } + + const values1 = await log1.values() + + strictEqual(values1[values1.length - 1].refs.length, 1) }) - describe('References', async () => { - const amount = 64 + it('creates entries with 4 references', async () => { + const maxReferenceDistance = 2 + const log2 = await Log(testIdentity, { logId: 'B' }) - it('creates entries with 2 references', async () => { - const maxReferenceDistance = 2 - const log1 = await Log(testIdentity, { logId: 'A' }) + for (let i = 0; i < amount * 2; i++) { + await log2.append(i.toString(), { pointerCount: Math.pow(maxReferenceDistance, 2) }) + } - for (let i = 0; i < amount; i++) { - await log1.append(i.toString(), { pointerCount: maxReferenceDistance }) - } + const values2 = await log2.values() - const values1 = await log1.values() + strictEqual(values2[values2.length - 1].refs.length, 2) + }) - strictEqual(values1[values1.length - 1].refs.length, 1) - }) + it('creates entries with 8 references', async () => { + const maxReferenceDistance = 2 + const log3 = await Log(testIdentity, { logId: 'C' }) - it('creates entries with 4 references', async () => { - const maxReferenceDistance = 2 - const log2 = await Log(testIdentity, { logId: 'B' }) + for (let i = 0; i < amount * 3; i++) { + await log3.append(i.toString(), { pointerCount: Math.pow(maxReferenceDistance, 3) }) + } - for (let i = 0; i < amount * 2; i++) { - await log2.append(i.toString(), { pointerCount: Math.pow(maxReferenceDistance, 2) }) - } + const values3 = await log3.values() - const values2 = await log2.values() + strictEqual(values3[values3.length - 1].refs.length, 3) + }) - strictEqual(values2[values2.length - 1].refs.length, 2) - }) + it('creates entries with 16 references', async () => { + const maxReferenceDistance = 2 + const log4 = await Log(testIdentity, { logId: 'D' }) - it('creates entries with 8 references', async () => { - const maxReferenceDistance = 2 - const log3 = await Log(testIdentity, { logId: 'C' }) + for (let i = 0; i < amount * 4; i++) { + await log4.append(i.toString(), { pointerCount: Math.pow(maxReferenceDistance, 4) }) + } - for (let i = 0; i < amount * 3; i++) { - await log3.append(i.toString(), { pointerCount: Math.pow(maxReferenceDistance, 3) }) - } + const values4 = await log4.values() - const values3 = await log3.values() + strictEqual(values4[values4.length - 1].refs.length, 4) + }) - strictEqual(values3[values3.length - 1].refs.length, 3) - }) + const inputs = [ + { amount: 1, referenceCount: 1, refLength: 0 }, + { amount: 1, referenceCount: 2, refLength: 0 }, + { amount: 2, referenceCount: 1, refLength: 1 }, + { amount: 2, referenceCount: 2, refLength: 1 }, + { amount: 3, referenceCount: 2, refLength: 1 }, + { amount: 3, referenceCount: 4, refLength: 1 }, + { amount: 4, referenceCount: 4, refLength: 2 }, + { amount: 4, referenceCount: 4, refLength: 2 }, + { amount: 32, referenceCount: 4, refLength: 2 }, + { amount: 32, referenceCount: 8, refLength: 3 }, + { amount: 32, referenceCount: 16, refLength: 4 }, + { amount: 18, referenceCount: 32, refLength: 5 }, + { amount: 128, referenceCount: 32, refLength: 5 }, + { amount: 63, referenceCount: 64, refLength: 5 }, + { amount: 64, referenceCount: 64, refLength: 6 }, + { amount: 65, referenceCount: 64, refLength: 6 }, + { amount: 91, referenceCount: 64, refLength: 6 }, + { amount: 128, referenceCount: 64, refLength: 6 }, + { amount: 128, referenceCount: 1, refLength: 0 }, + { amount: 128, referenceCount: 2, refLength: 1 }, + { amount: 256, referenceCount: 1, refLength: 0 }, + { amount: 256, referenceCount: 4, refLength: 2 }, + { amount: 256, referenceCount: 8, refLength: 3 }, + { amount: 256, referenceCount: 16, refLength: 4 }, + { amount: 256, referenceCount: 32, refLength: 5 }, + { amount: 1024, referenceCount: 2, refLength: 1 } + ] - it('creates entries with 16 references', async () => { - const maxReferenceDistance = 2 - const log4 = await Log(testIdentity, { logId: 'D' }) - - for (let i = 0; i < amount * 4; i++) { - await log4.append(i.toString(), { pointerCount: Math.pow(maxReferenceDistance, 4) }) - } - - const values4 = await log4.values() - - strictEqual(values4[values4.length - 1].refs.length, 4) - }) - - const inputs = [ - { amount: 1, referenceCount: 1, refLength: 0 }, - { amount: 1, referenceCount: 2, refLength: 0 }, - { amount: 2, referenceCount: 1, refLength: 1 }, - { amount: 2, referenceCount: 2, refLength: 1 }, - { amount: 3, referenceCount: 2, refLength: 1 }, - { amount: 3, referenceCount: 4, refLength: 1 }, - { amount: 4, referenceCount: 4, refLength: 2 }, - { amount: 4, referenceCount: 4, refLength: 2 }, - { amount: 32, referenceCount: 4, refLength: 2 }, - { amount: 32, referenceCount: 8, refLength: 3 }, - { amount: 32, referenceCount: 16, refLength: 4 }, - { amount: 18, referenceCount: 32, refLength: 5 }, - { amount: 128, referenceCount: 32, refLength: 5 }, - { amount: 63, referenceCount: 64, refLength: 5 }, - { amount: 64, referenceCount: 64, refLength: 6 }, - { amount: 65, referenceCount: 64, refLength: 6 }, - { amount: 91, referenceCount: 64, refLength: 6 }, - { amount: 128, referenceCount: 64, refLength: 6 }, - { amount: 128, referenceCount: 1, refLength: 0 }, - { amount: 128, referenceCount: 2, refLength: 1 }, - { amount: 256, referenceCount: 1, refLength: 0 }, - { amount: 256, referenceCount: 4, refLength: 2 }, - { amount: 256, referenceCount: 8, refLength: 3 }, - { amount: 256, referenceCount: 16, refLength: 4 }, - { amount: 256, referenceCount: 32, refLength: 5 }, - { amount: 1024, referenceCount: 2, refLength: 1 } - ] - - inputs.forEach(input => { - it(`has ${input.refLength} references, max distance ${input.referenceCount}, total of ${input.amount} entries`, async () => { - const test = async (amount, referenceCount, refLength) => { - const storage = await MemoryStorage() - const log1 = await Log(testIdentity, { logId: 'A', storage }) - for (let i = 0; i < amount; i++) { - await log1.append((i + 1).toString(), { pointerCount: referenceCount }) - } - - const values = await log1.values() - - strictEqual(values.length, input.amount) - strictEqual(values[values.length - 1].clock.time, input.amount) - - for (let k = 0; k < input.amount; k++) { - const idx = values.length - k - 1 - strictEqual(values[idx].clock.time, idx + 1) - - // Check the first ref (distance 2) - if (values[idx].refs.length > 0) { strictEqual(values[idx].refs[0], values[idx - 2].hash) } - - // Check the second ref (distance 4) - if (values[idx].refs.length > 1 && idx > referenceCount) { strictEqual(values[idx].refs[1], values[idx - 4].hash) } - - // Check the third ref (distance 8) - if (values[idx].refs.length > 2 && idx > referenceCount) { strictEqual(values[idx].refs[2], values[idx - 8].hash) } - - // Check the fourth ref (distance 16) - if (values[idx].refs.length > 3 && idx > referenceCount) { strictEqual(values[idx].refs[3], values[idx - 16].hash) } - - // Check the fifth ref (distance 32) - if (values[idx].refs.length > 4 && idx > referenceCount) { strictEqual(values[idx].refs[4], values[idx - 32].hash) } - - // Check the fifth ref (distance 64) - if (values[idx].refs.length > 5 && idx > referenceCount) { strictEqual(values[idx].refs[5], values[idx - 64].hash) } - - // Check the reference of each entry - if (idx > referenceCount) { strictEqual(values[idx].refs.length, refLength) } - } + inputs.forEach(input => { + it(`has ${input.refLength} references, max distance ${input.referenceCount}, total of ${input.amount} entries`, async () => { + const test = async (amount, referenceCount, refLength) => { + const storage = await MemoryStorage() + const log1 = await Log(testIdentity, { logId: 'A', storage }) + for (let i = 0; i < amount; i++) { + await log1.append((i + 1).toString(), { pointerCount: referenceCount }) } - await test(input.amount, input.referenceCount, input.refLength) - }) + const values = await log1.values() + + strictEqual(values.length, input.amount) + strictEqual(values[values.length - 1].clock.time, input.amount) + + for (let k = 0; k < input.amount; k++) { + const idx = values.length - k - 1 + strictEqual(values[idx].clock.time, idx + 1) + + // Check the first ref (distance 2) + if (values[idx].refs.length > 0) { strictEqual(values[idx].refs[0], values[idx - 2].hash) } + + // Check the second ref (distance 4) + if (values[idx].refs.length > 1 && idx > referenceCount) { strictEqual(values[idx].refs[1], values[idx - 4].hash) } + + // Check the third ref (distance 8) + if (values[idx].refs.length > 2 && idx > referenceCount) { strictEqual(values[idx].refs[2], values[idx - 8].hash) } + + // Check the fourth ref (distance 16) + if (values[idx].refs.length > 3 && idx > referenceCount) { strictEqual(values[idx].refs[3], values[idx - 16].hash) } + + // Check the fifth ref (distance 32) + if (values[idx].refs.length > 4 && idx > referenceCount) { strictEqual(values[idx].refs[4], values[idx - 32].hash) } + + // Check the fifth ref (distance 64) + if (values[idx].refs.length > 5 && idx > referenceCount) { strictEqual(values[idx].refs[5], values[idx - 64].hash) } + + // Check the reference of each entry + if (idx > referenceCount) { strictEqual(values[idx].refs.length, refLength) } + } + } + + await test(input.amount, input.referenceCount, input.refLength) }) }) }) diff --git a/test/oplog/replicate.test.js b/test/oplog/replicate.test.js index d606fd6..e960dc0 100644 --- a/test/oplog/replicate.test.js +++ b/test/oplog/replicate.test.js @@ -1,153 +1,156 @@ import { strictEqual } from 'assert' -import { Log, Entry } from '../../src/index.js' -import { IPFSBlockStorage } from '../../src/storage/index.js' +import rmrf from 'rimraf' +import { copy } from 'fs-extra' +import { Log, Entry, Identities, KeyStore, IPFSBlockStorage } from '../../src/index.js' +import { config, startIpfs, stopIpfs, getIpfsPeerId, waitForPeers, connectPeers } from 'orbit-db-test-utils' +import testKeysPath from '../fixtures/test-keys-path.js ' -// Test utils -import { config, testAPIs, startIpfs, stopIpfs, getIpfsPeerId, waitForPeers, connectPeers } from 'orbit-db-test-utils' -import { createTestIdentities, cleanUpTestIdentities } from '../fixtures/orbit-db-identity-keys.js' +const keysPath = './testkeys' +const IPFS = 'js-ipfs' -Object.keys(testAPIs).forEach((IPFS) => { - describe('ipfs-log - Replication (' + IPFS + ')', function () { - this.timeout(config.timeout * 2) +describe('Log - Replication', function () { + this.timeout(60000) - let ipfsd1, ipfsd2 - let ipfs1, ipfs2 - let id1, id2 + let ipfsd1, ipfsd2 + let ipfs1, ipfs2 + let id1, id2 + let keystore + let identities1, identities2 + let testIdentity1, testIdentity2 + let storage1, storage2 - let identities1, identities2 - let testIdentity1, testIdentity2 - let storage1, storage2 + before(async () => { + ipfsd1 = await startIpfs(IPFS, config.daemon1) + ipfsd2 = await startIpfs(IPFS, config.daemon2) + ipfs1 = ipfsd1.api + ipfs2 = ipfsd2.api - before(async () => { - // Start two IPFS instances - ipfsd1 = await startIpfs(IPFS, config.daemon1) - ipfsd2 = await startIpfs(IPFS, config.daemon2) - ipfs1 = ipfsd1.api - ipfs2 = ipfsd2.api + await connectPeers(ipfs1, ipfs2) - await connectPeers(ipfs1, ipfs2) + id1 = await getIpfsPeerId(ipfs1) + id2 = await getIpfsPeerId(ipfs2) - // Get the peer IDs - id1 = await getIpfsPeerId(ipfs1) - id2 = await getIpfsPeerId(ipfs2) + await copy(testKeysPath, keysPath) + keystore = await KeyStore({ path: keysPath }) - const [identities, testIdentities] = await createTestIdentities(ipfs1, ipfs2) - identities1 = identities[0] - identities2 = identities[1] - testIdentity2 = testIdentities[0] - testIdentity1 = testIdentities[1] + identities1 = await Identities({ keystore, ipfs: ipfs1 }) + identities2 = await Identities({ keystore, ipfs: ipfs2 }) + testIdentity1 = await identities1.createIdentity({ id: 'userB' }) + testIdentity2 = await identities2.createIdentity({ id: 'userA' }) - storage1 = await IPFSBlockStorage({ ipfs: ipfs1 }) - storage2 = await IPFSBlockStorage({ ipfs: ipfs2 }) + storage1 = await IPFSBlockStorage({ ipfs: ipfs1 }) + storage2 = await IPFSBlockStorage({ ipfs: ipfs2 }) + }) + + after(async () => { + await stopIpfs(ipfsd1) + await stopIpfs(ipfsd2) + if (keystore) { + await keystore.close() + } + await storage1.close() + await storage2.close() + await rmrf(keysPath) + }) + + describe('replicates logs deterministically', async function () { + const amount = 128 + 1 + const logId = 'A' + + let log1, log2, input1, input2 + + const handleMessage1 = async (message) => { + const { id: peerId } = await ipfs1.id() + const messageIsFromMe = (message) => String(peerId) === String(message.from) + try { + if (!messageIsFromMe(message)) { + const entry = await Entry.decode(message.data) + await storage1.put(entry.hash, entry.bytes) + await log1.joinEntry(entry) + } + } catch (e) { + console.error(e) + } + } + + const handleMessage2 = async (message) => { + const { id: peerId } = await ipfs2.id() + const messageIsFromMe = (message) => String(peerId) === String(message.from) + try { + if (!messageIsFromMe(message)) { + const entry = await Entry.decode(message.data) + await storage2.put(entry.hash, entry.bytes) + await log2.joinEntry(entry) + } + } catch (e) { + console.error(e) + } + } + + beforeEach(async () => { + log1 = await Log(testIdentity1, { logId, storage: storage1 }) + log2 = await Log(testIdentity2, { logId, storage: storage2 }) + input1 = await Log(testIdentity1, { logId, storage: storage1 }) + input2 = await Log(testIdentity2, { logId, storage: storage2 }) + await ipfs1.pubsub.subscribe(logId, handleMessage1) + await ipfs2.pubsub.subscribe(logId, handleMessage2) }) - after(async () => { - await cleanUpTestIdentities([identities1, identities2]) - await stopIpfs(ipfsd1) - await stopIpfs(ipfsd2) - await storage1.close() - await storage2.close() + afterEach(async () => { + await ipfs1.pubsub.unsubscribe(logId, handleMessage1) + await ipfs2.pubsub.unsubscribe(logId, handleMessage2) }) - describe('replicates logs deterministically', async function () { - const amount = 128 + 1 - const logId = 'A' + it('replicates logs', async () => { + await waitForPeers(ipfs1, [id2], logId) + await waitForPeers(ipfs2, [id1], logId) - let log1, log2, input1, input2 - - const handleMessage1 = async (message) => { - const { id: peerId } = await ipfs1.id() - const messageIsFromMe = (message) => String(peerId) === String(message.from) - try { - if (!messageIsFromMe(message)) { - const entry = await Entry.decode(message.data) - await storage1.put(entry.hash, entry.bytes) - await log1.joinEntry(entry) - } - } catch (e) { - console.error(e) - } + for (let i = 1; i <= amount; i++) { + const entry1 = await input1.append('A' + i) + const entry2 = await input2.append('B' + i) + await ipfs1.pubsub.publish(logId, entry1.bytes) + await ipfs2.pubsub.publish(logId, entry2.bytes) } - const handleMessage2 = async (message) => { - const { id: peerId } = await ipfs2.id() - const messageIsFromMe = (message) => String(peerId) === String(message.from) - try { - if (!messageIsFromMe(message)) { - const entry = await Entry.decode(message.data) - await storage2.put(entry.hash, entry.bytes) - await log2.joinEntry(entry) - } - } catch (e) { - console.error(e) - } + console.log('Messages sent') + + const whileProcessingMessages = (timeoutMs) => { + return new Promise((resolve, reject) => { + const timeout = setTimeout(() => reject(new Error('timeout')), timeoutMs) + const timer = setInterval(async () => { + const valuesA = await log1.values() + const valuesB = await log2.values() + if (valuesA.length + valuesB.length === amount * 2) { + clearInterval(timer) + clearTimeout(timeout) + console.log('Messages received') + resolve() + } + }, 200) + }) } - beforeEach(async () => { - log1 = await Log(testIdentity1, { logId, storage: storage1 }) - log2 = await Log(testIdentity2, { logId, storage: storage2 }) - input1 = await Log(testIdentity1, { logId, storage: storage1 }) - input2 = await Log(testIdentity2, { logId, storage: storage2 }) - await ipfs1.pubsub.subscribe(logId, handleMessage1) - await ipfs2.pubsub.subscribe(logId, handleMessage2) - }) + await whileProcessingMessages(config.timeout) - afterEach(async () => { - await ipfs1.pubsub.unsubscribe(logId, handleMessage1) - await ipfs2.pubsub.unsubscribe(logId, handleMessage2) - }) + const result = await Log(testIdentity1, { logId, storage: storage1 }) + await result.join(log1) + await result.join(log2) - it('replicates logs', async () => { - await waitForPeers(ipfs1, [id2], logId) - await waitForPeers(ipfs2, [id1], logId) + const values1 = await log1.values() + const values2 = await log2.values() + const values3 = await result.values() - for (let i = 1; i <= amount; i++) { - const entry1 = await input1.append('A' + i) - const entry2 = await input2.append('B' + i) - await ipfs1.pubsub.publish(logId, entry1.bytes) - await ipfs2.pubsub.publish(logId, entry2.bytes) - } - - console.log('Messages sent') - - const whileProcessingMessages = (timeoutMs) => { - return new Promise((resolve, reject) => { - const timeout = setTimeout(() => reject(new Error('timeout')), timeoutMs) - const timer = setInterval(async () => { - const valuesA = await log1.values() - const valuesB = await log2.values() - if (valuesA.length + valuesB.length === amount * 2) { - clearInterval(timer) - clearTimeout(timeout) - console.log('Messages received') - resolve() - } - }, 200) - }) - } - - await whileProcessingMessages(config.timeout) - - const result = await Log(testIdentity1, { logId, storage: storage1 }) - await result.join(log1) - await result.join(log2) - - const values1 = await log1.values() - const values2 = await log2.values() - const values3 = await result.values() - - strictEqual(values1.length, amount) - strictEqual(values2.length, amount) - strictEqual(values3.length, amount * 2) - strictEqual(values3[0].payload, 'A1') - strictEqual(values3[1].payload, 'B1') - strictEqual(values3[2].payload, 'A2') - strictEqual(values3[3].payload, 'B2') - strictEqual(values3[99].payload, 'B50') - strictEqual(values3[100].payload, 'A51') - strictEqual(values3[198].payload, 'A100') - strictEqual(values3[199].payload, 'B100') - }) + strictEqual(values1.length, amount) + strictEqual(values2.length, amount) + strictEqual(values3.length, amount * 2) + strictEqual(values3[0].payload, 'A1') + strictEqual(values3[1].payload, 'B1') + strictEqual(values3[2].payload, 'A2') + strictEqual(values3[3].payload, 'B2') + strictEqual(values3[99].payload, 'B50') + strictEqual(values3[100].payload, 'A51') + strictEqual(values3[198].payload, 'A100') + strictEqual(values3[199].payload, 'B100') }) }) }) diff --git a/test/oplog/signed-log.test.js b/test/oplog/signed-log.test.js index 2281f93..a85f9ec 100644 --- a/test/oplog/signed-log.test.js +++ b/test/oplog/signed-log.test.js @@ -1,167 +1,173 @@ import { notStrictEqual, strictEqual } from 'assert' -import { Log } from '../../src/oplog/index.js' -import { config, testAPIs } from 'orbit-db-test-utils' -import { createTestIdentities, cleanUpTestIdentities } from '../fixtures/orbit-db-identity-keys.js' +import rmrf from 'rimraf' +import { copy } from 'fs-extra' +import { Log, Identities, KeyStore } from '../../src/index.js' +import testKeysPath from '../fixtures/test-keys-path.js ' -Object.keys(testAPIs).forEach((IPFS) => { - describe('Signed Log (' + IPFS + ')', function () { - this.timeout(config.timeout) +const keysPath = './testkeys' - let identities1, identities2 - let testIdentity, testIdentity2 +describe('Signed Log', function () { + this.timeout(5000) - before(async () => { - const [identities, testIdentities] = await createTestIdentities() - identities1 = identities[0] - identities2 = identities[1] - testIdentity = testIdentities[0] - testIdentity2 = testIdentities[1] - }) + let keystore + let identities + let testIdentity1, testIdentity2 - after(async () => { - await cleanUpTestIdentities([identities1, identities2]) - }) + before(async () => { + await copy(testKeysPath, keysPath) + keystore = await KeyStore({ path: keysPath }) - it('creates a signed log', async () => { - const logId = 'A' - const log = await Log(testIdentity, { logId }) - notStrictEqual(log.id, null) - strictEqual(log.id, logId) - }) + identities = await Identities({ keystore }) + testIdentity1 = await identities.createIdentity({ id: 'userB' }) + testIdentity2 = await identities.createIdentity({ id: 'userA' }) + }) - // it('has the correct identity', () => { - // const log = await Log(testIdentity, { logId: 'A' }) - // notStrictEqual(log.id, null) - // strictEqual(log.id, 'A') - // strictEqual(log.identity.id, '03e0480538c2a39951d054e17ff31fde487cb1031d0044a037b53ad2e028a3e77c') - // strictEqual(log.identity.publicKey, '048bef2231e64d5c7147bd4b8afb84abd4126ee8d8335e4b069ac0a65c7be711cea5c1b8d47bc20ebaecdca588600ddf2894675e78b2ef17cf49e7bbaf98080361') - // strictEqual(log.identity.signatures.id, '3045022100f5f6f10571d14347aaf34e526ce3419fd64d75ffa7aa73692cbb6aeb6fbc147102203a3e3fa41fa8fcbb9fc7c148af5b640e2f704b20b3a4e0b93fc3a6d44dffb41e') - // strictEqual(log.identity.signatures.publicKey, '3044022020982b8492be0c184dc29de0a3a3bd86a86ba997756b0bf41ddabd24b47c5acf02203745fda39d7df650a5a478e52bbe879f0cb45c074025a93471414a56077640a4') - // }) + after(async () => { + if (keystore) { + await keystore.close() + } + await rmrf(keysPath) + }) - it('has the correct public key', async () => { - const log = await Log(testIdentity, { logId: 'A' }) - strictEqual(log.identity.publicKey, testIdentity.publicKey) - }) + it('creates a signed log', async () => { + const logId = 'A' + const log = await Log(testIdentity1, { logId }) + notStrictEqual(log.id, null) + strictEqual(log.id, logId) + }) - it('has the correct pkSignature', async () => { - const log = await Log(testIdentity, { logId: 'A' }) - strictEqual(log.identity.signatures.id, testIdentity.signatures.id) - }) + // it('has the correct identity', () => { + // const log = await Log(testIdentity, { logId: 'A' }) + // notStrictEqual(log.id, null) + // strictEqual(log.id, 'A') + // strictEqual(log.identity.id, '03e0480538c2a39951d054e17ff31fde487cb1031d0044a037b53ad2e028a3e77c') + // strictEqual(log.identity.publicKey, '048bef2231e64d5c7147bd4b8afb84abd4126ee8d8335e4b069ac0a65c7be711cea5c1b8d47bc20ebaecdca588600ddf2894675e78b2ef17cf49e7bbaf98080361') + // strictEqual(log.identity.signatures.id, '3045022100f5f6f10571d14347aaf34e526ce3419fd64d75ffa7aa73692cbb6aeb6fbc147102203a3e3fa41fa8fcbb9fc7c148af5b640e2f704b20b3a4e0b93fc3a6d44dffb41e') + // strictEqual(log.identity.signatures.publicKey, '3044022020982b8492be0c184dc29de0a3a3bd86a86ba997756b0bf41ddabd24b47c5acf02203745fda39d7df650a5a478e52bbe879f0cb45c074025a93471414a56077640a4') + // }) - it('has the correct signature', async () => { - const log = await Log(testIdentity, { logId: 'A' }) - strictEqual(log.identity.signatures.publicKey, testIdentity.signatures.publicKey) - }) + it('has the correct public key', async () => { + const log = await Log(testIdentity1, { logId: 'A' }) + strictEqual(log.identity.publicKey, testIdentity1.publicKey) + }) - // it('entries contain an identity', async () => { - // const log = await Log(testIdentity, { logId: 'A' }) - // await log.append('one') - // const values = await log.values() - // notStrictEqual(values[0].sig, null) - // deepStrictEqual(values[0].identity, testIdentity.toJSON()) - // }) + it('has the correct pkSignature', async () => { + const log = await Log(testIdentity1, { logId: 'A' }) + strictEqual(log.identity.signatures.id, testIdentity1.signatures.id) + }) - it('doesn\'t sign entries when identity is not defined', async () => { - let err - try { - await Log(null) - } catch (e) { - err = e + it('has the correct signature', async () => { + const log = await Log(testIdentity1, { logId: 'A' }) + strictEqual(log.identity.signatures.publicKey, testIdentity1.signatures.publicKey) + }) + + // it('entries contain an identity', async () => { + // const log = await Log(testIdentity, { logId: 'A' }) + // await log.append('one') + // const values = await log.values() + // notStrictEqual(values[0].sig, null) + // deepStrictEqual(values[0].identity, testIdentity.toJSON()) + // }) + + it('doesn\'t sign entries when identity is not defined', async () => { + let err + try { + await Log(null) + } catch (e) { + err = e + } + strictEqual(err.message, 'Identity is required') + }) + + it('throws an error if log is signed but trying to merge with an entry that doesn\'t have public signing key', async () => { + const log1 = await Log(testIdentity1, { logId: 'A' }) + const log2 = await Log(testIdentity2, { logId: 'A' }) + + let err + try { + await log1.append('one') + const entry = await log2.append('two') + delete entry.key + await log1.joinEntry(entry) + } catch (e) { + err = e.toString() + } + strictEqual(err, 'Error: Entry doesn\'t have a key') + }) + + it('throws an error if log is signed but trying to merge an entry that doesn\'t have a signature', async () => { + const log1 = await Log(testIdentity1, { logId: 'A' }) + const log2 = await Log(testIdentity2, { logId: 'A' }) + + let err + try { + await log1.append('one') + const entry = await log2.append('two') + delete entry.sig + await log1.joinEntry(entry) + } catch (e) { + err = e.toString() + } + strictEqual(err, 'Error: Entry doesn\'t have a signature') + }) + + it('throws an error if log is signed but the signature doesn\'t verify', async () => { + const log1 = await Log(testIdentity1, { logId: 'A' }) + const log2 = await Log(testIdentity2, { logId: 'A' }) + let err + + try { + const entry1 = await log1.append('one') + const entry2 = await log2.append('two') + entry2.sig = entry1.sig + await log1.joinEntry(entry2) + } catch (e) { + err = e.toString() + } + + const entry = (await log2.values())[0] + const values = await log1.values() + strictEqual(err, `Error: Could not validate signature for entry "${entry.hash}"`) + strictEqual(values.length, 1) + strictEqual(values[0].payload, 'one') + }) + + it('throws an error if entry doesn\'t have append access', async () => { + const denyAccess = { canAppend: () => false } + const log1 = await Log(testIdentity1, { logId: 'A' }) + const log2 = await Log(testIdentity2, { logId: 'A', access: denyAccess }) + + let err + try { + await log1.append('one') + await log2.append('two') + await log1.join(log2) + } catch (e) { + err = e.toString() + } + + strictEqual(err, `Error: Could not append entry:\nKey "${testIdentity2.hash}" is not allowed to write to the log`) + }) + + it('throws an error upon join if entry doesn\'t have append access', async () => { + const testACL = { + canAppend: async (entry) => { + const identity = await identities.getIdentity(entry.identity) + return identity && identity.id !== testIdentity2.id } - strictEqual(err.message, 'Identity is required') - }) + } + const log1 = await Log(testIdentity1, { logId: 'A', access: testACL }) + const log2 = await Log(testIdentity2, { logId: 'A' }) - it('throws an error if log is signed but trying to merge with an entry that doesn\'t have public signing key', async () => { - const log1 = await Log(testIdentity, { logId: 'A' }) - const log2 = await Log(testIdentity2, { logId: 'A' }) + let err + try { + await log1.append('one') + await log2.append('two') + await log1.join(log2) + } catch (e) { + err = e.toString() + } - let err - try { - await log1.append('one') - const entry = await log2.append('two') - delete entry.key - await log1.joinEntry(entry) - } catch (e) { - err = e.toString() - } - strictEqual(err, 'Error: Entry doesn\'t have a key') - }) - - it('throws an error if log is signed but trying to merge an entry that doesn\'t have a signature', async () => { - const log1 = await Log(testIdentity, { logId: 'A' }) - const log2 = await Log(testIdentity2, { logId: 'A' }) - - let err - try { - await log1.append('one') - const entry = await log2.append('two') - delete entry.sig - await log1.joinEntry(entry) - } catch (e) { - err = e.toString() - } - strictEqual(err, 'Error: Entry doesn\'t have a signature') - }) - - it('throws an error if log is signed but the signature doesn\'t verify', async () => { - const log1 = await Log(testIdentity, { logId: 'A' }) - const log2 = await Log(testIdentity2, { logId: 'A' }) - let err - - try { - const entry1 = await log1.append('one') - const entry2 = await log2.append('two') - entry2.sig = entry1.sig - await log1.joinEntry(entry2) - } catch (e) { - err = e.toString() - } - - const entry = (await log2.values())[0] - const values = await log1.values() - strictEqual(err, `Error: Could not validate signature for entry "${entry.hash}"`) - strictEqual(values.length, 1) - strictEqual(values[0].payload, 'one') - }) - - it('throws an error if entry doesn\'t have append access', async () => { - const denyAccess = { canAppend: () => false } - const log1 = await Log(testIdentity, { logId: 'A' }) - const log2 = await Log(testIdentity2, { logId: 'A', access: denyAccess }) - - let err - try { - await log1.append('one') - await log2.append('two') - await log1.join(log2) - } catch (e) { - err = e.toString() - } - - strictEqual(err, `Error: Could not append entry:\nKey "${testIdentity2.hash}" is not allowed to write to the log`) - }) - - it('throws an error upon join if entry doesn\'t have append access', async () => { - const testACL = { - canAppend: async (entry) => { - const identity = await identities1.getIdentity(entry.identity) - return identity && identity.id !== testIdentity2.id - } - } - const log1 = await Log(testIdentity, { logId: 'A', access: testACL }) - const log2 = await Log(testIdentity2, { logId: 'A' }) - - let err - try { - await log1.append('one') - await log2.append('two') - await log1.join(log2) - } catch (e) { - err = e.toString() - } - - strictEqual(err, `Error: Could not append entry:\nKey "${testIdentity2.hash}" is not allowed to write to the log`) - }) + strictEqual(err, `Error: Could not append entry:\nKey "${testIdentity2.hash}" is not allowed to write to the log`) }) })