import { strictEqual, deepStrictEqual } from 'assert' import rimraf from 'rimraf' import { copy } from 'fs-extra' import { Log, MemoryStorage } from '../src/log.js' import IdentityProvider from 'orbit-db-identity-provider' import Keystore from '../src/Keystore.js' import LogCreator from './utils/log-creator.js' import all from 'it-all' // Test utils import { config, testAPIs, startIpfs, stopIpfs } from 'orbit-db-test-utils' const { sync: rmrf } = rimraf const { createIdentity } = IdentityProvider const { createLogWithSixteenEntries } = LogCreator let ipfsd, ipfs, testIdentity, testIdentity2, testIdentity3 Object.keys(testAPIs).forEach((IPFS) => { describe('Log - Iterator (' + IPFS + ')', function () { this.timeout(config.timeout) const { identityKeyFixtures, signingKeyFixtures, identityKeysPath, signingKeysPath } = config let keystore, signingKeystore before(async () => { rmrf(identityKeysPath) rmrf(signingKeysPath) await copy(identityKeyFixtures, identityKeysPath) await copy(signingKeyFixtures, signingKeysPath) keystore = new Keystore(identityKeysPath) signingKeystore = new Keystore(signingKeysPath) testIdentity = await createIdentity({ id: 'userA', keystore, signingKeystore }) testIdentity2 = await createIdentity({ id: 'userB', keystore, signingKeystore }) testIdentity3 = await createIdentity({ id: 'userC', keystore, signingKeystore }) ipfsd = await startIpfs(IPFS, config.defaultIpfsConfig) ipfs = ipfsd.api }) after(async () => { await stopIpfs(ipfsd) rmrf(identityKeysPath) rmrf(signingKeysPath) await keystore.close() await signingKeystore.close() }) describe('Basic iterator functionality', () => { let log1 let startHash const hashes = [] const logSize = 100 const storage = MemoryStorage() beforeEach(async () => { log1 = Log(testIdentity, { logId: 'X', storage }) 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) }) 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' + (73 - 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) }) 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' + (79 - 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', () => { let fixture, identities before(async () => { identities = [testIdentity3, testIdentity2, testIdentity3, testIdentity] fixture = await createLogWithSixteenEntries(Log, ipfs, identities) }) it('returns the full length from all heads', async () => { const it = fixture.log.iterator({ lte: fixture.log.heads() }) const result = await all(it) strictEqual([...result].length, 16) }) it('returns partial entries from all heads', async () => { const it = fixture.log.iterator({ lte: fixture.log.heads(), amount: 6 }) 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: [fixture.log.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: [fixture.log.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') }) }) }) })