mirror of
https://github.com/orbitdb/orbitdb.git
synced 2025-06-24 15:02:30 +00:00
Merge pull request #18 from orbitdb/dev/fix-oplog-tests
Clean up oplog tests
This commit is contained in:
commit
a4fb12c65b
@ -458,4 +458,4 @@ const Log = async (identity, { logId, logHeads, access, entryStorage, headsStora
|
||||
}
|
||||
}
|
||||
|
||||
export { Log as default, DefaultAccessController }
|
||||
export { Log as default, DefaultAccessController, Clock }
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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'
|
||||
|
@ -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'
|
||||
|
@ -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'
|
||||
|
@ -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)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
@ -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)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
@ -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)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
@ -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)
|
||||
})
|
||||
})
|
||||
|
@ -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')
|
||||
})
|
||||
})
|
||||
})
|
||||
|
@ -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))
|
||||
})
|
||||
})
|
||||
})
|
||||
|
@ -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)
|
||||
})
|
||||
})
|
||||
|
@ -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'
|
||||
|
@ -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')
|
||||
})
|
||||
})
|
||||
})
|
||||
|
@ -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)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
@ -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')
|
||||
})
|
||||
})
|
||||
})
|
||||
|
@ -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`)
|
||||
})
|
||||
})
|
||||
|
Loading…
x
Reference in New Issue
Block a user