From f252b3ad2a86c2d702cdcb8f012c437b61a57c38 Mon Sep 17 00:00:00 2001 From: mistakia <1823355+mistakia@users.noreply.github.com> Date: Thu, 14 Jan 2021 18:03:38 -0500 Subject: [PATCH] feat: adjustable in-memory entry storage --- API.md | 42 ++++--- README.md | 6 +- benchmarks/tail-hashes.js | 2 +- benchmarks/tails.js | 2 +- benchmarks/utils/create-log.js | 2 +- benchmarks/values.js | 2 +- lib/es5/entry-index.js | 22 ++-- lib/es5/log-io.js | 14 ++- package.json | 1 + src/entry-index.js | 21 ++-- src/log-io.js | 4 +- src/log.js | 193 ++++++++++++++++++------------- test/entry-io.spec.js | 33 ++++-- test/log-append.spec.js | 21 ++-- test/log-crdt.spec.js | 32 ++--- test/log-heads-tails.spec.js | 71 +++++++----- test/log-iterator.spec.js | 77 +++++++----- test/log-join-concurrent.spec.js | 15 ++- test/log-join.spec.js | 52 ++++++--- test/log-load.spec.js | 179 +++++++++++++++++----------- test/log-references.spec.js | 40 ++++--- test/log.spec.js | 103 +++++++++-------- test/replicate.spec.js | 18 +-- test/signed-log.spec.js | 29 +++-- 24 files changed, 601 insertions(+), 380 deletions(-) diff --git a/API.md b/API.md index 0fc2bdcd..54dce08b 100644 --- a/API.md +++ b/API.md @@ -10,7 +10,7 @@ const Log = require('ipfs-log') ### Constructor -#### new Log(ipfs, identity, [{ logId, access, entries, heads, clock, sortFn }]) +#### new Log(ipfs, identity, [{ logId, access, entries, heads, clock, sortFn, hashIndex }]) Create a log. Each log gets a unique ID, which can be passed in the `options` as `logId`. Returns a `Log` instance. @@ -32,15 +32,6 @@ console.log(log.id) Returns the ID of the log. -#### values - -Returns an `Array` of [entries](https://github.com/orbitdb/ipfs-log/blob/master/src/entry.js) in the log. The values are in linearized order according to their [Lamport clocks](https://en.wikipedia.org/wiki/Lamport_timestamps). - -```javascript -const values = log.values -// TODO: output example -``` - #### length Returns the number of entries in the log. @@ -58,16 +49,25 @@ const heads = log.heads // TODO: output example ``` -#### tails +### Methods + +#### values -Return the tails of the log. Tails are the entries that reference other entries that are not in the log. +Returns a *Promise* that resolves to an `Array` of [entries](https://github.com/orbitdb/ipfs-log/blob/master/src/entry.js) in the log. The values are in linearized order according to their [Lamport clocks](https://en.wikipedia.org/wiki/Lamport_timestamps). ```javascript -const tails = log.tails +const values = await log.values() // TODO: output example ``` -### Methods +#### tails + +Returns a *Promise* that resolves to the tails of the log. Tails are the entries that reference other entries that are not in the log. + +```javascript +const tails = await log.tails() +// TODO: output example +``` #### append(data) @@ -115,6 +115,18 @@ console.log(log.values) // ] ``` +#### get(hash) + +Returns a *Promise* that resolves to an entry if it exists, otherwise undefined. + +```javascript +const entry = await get(hash) +``` + +#### has(hash) + +Returns true if the hash exists in the log, otherwise false. + #### join(log, [length]) Join the log with another log. Returns a Promise that resolves to a `Log` instance. The size of the joined log can be specified by giving `length` argument. @@ -158,7 +170,7 @@ const buffer = log1.toBuffer() ### toString -Returns the log values as a nicely formatted string. +Returns a *Promise* that resolves to the log values as a nicely formatted string. ```javascript console.log(log.toString()) diff --git a/README.md b/README.md index f4e36156..d7b0ff10 100644 --- a/README.md +++ b/README.md @@ -137,12 +137,14 @@ See [API Documentation](https://github.com/orbitdb/ipfs-log/tree/master/API.md) - [new Log(ipfs, identity, [{ logId, access, entries, heads, clock, sortFn }])](https://github.com/orbitdb/ipfs-log/tree/master/API.md##new-log-ipfs-id) - [Properties](https://github.com/orbitdb/ipfs-log/tree/master/API.md##properties) - [id](https://github.com/orbitdb/ipfs-log/tree/master/API.md##id) - - [values](https://github.com/orbitdb/ipfs-log/tree/master/API.md##values) - [length](https://github.com/orbitdb/ipfs-log/tree/master/API.md##length) - [clock](https://github.com/orbitdb/ipfs-log/tree/master/API.md##length) - [heads](https://github.com/orbitdb/ipfs-log/tree/master/API.md##heads) - - [tails](https://github.com/orbitdb/ipfs-log/tree/master/API.md##tails) - [Methods](https://github.com/orbitdb/ipfs-log/tree/master/API.md##methods) + - [values](https://github.com/orbitdb/ipfs-log/tree/master/API.md##values) + - [tails](https://github.com/orbitdb/ipfs-log/tree/master/API.md##tails) + - [get(hash)](https://github.com/orbitdb/ipfs-log/tree/master/API.md##get) + - [has(hash)](https://github.com/orbitdb/ipfs-log/tree/master/API.md##has) - [append(data)](https://github.com/orbitdb/ipfs-log/tree/master/API.md##appenddata) - [join(log)](https://github.com/orbitdb/ipfs-log/tree/master/API.md##joinlog) - [toMultihash()](https://github.com/orbitdb/ipfs-log/tree/master/API.md##tomultihash) diff --git a/benchmarks/tail-hashes.js b/benchmarks/tail-hashes.js index c3bcc382..f127e05f 100644 --- a/benchmarks/tail-hashes.js +++ b/benchmarks/tail-hashes.js @@ -15,7 +15,7 @@ const base = { return { log, ipfsd } }, cycle: async function ({ log }) { - return log.tailHashes + return log.tailHashes() }, teardown: async function ({ ipfsd }) { await stopIpfs(ipfsd) diff --git a/benchmarks/tails.js b/benchmarks/tails.js index d0079d00..ca5612ef 100644 --- a/benchmarks/tails.js +++ b/benchmarks/tails.js @@ -15,7 +15,7 @@ const base = { return { log, ipfsd } }, cycle: async function ({ log }) { - return log.tails + return log.tails() }, teardown: async function ({ ipfsd }) { await stopIpfs(ipfsd) diff --git a/benchmarks/utils/create-log.js b/benchmarks/utils/create-log.js index 9fc1763f..233ad013 100644 --- a/benchmarks/utils/create-log.js +++ b/benchmarks/utils/create-log.js @@ -17,7 +17,7 @@ const createLog = async (ipfs, logId) => { const access = new AccessController() const keystore = new Keystore(store) const identity = await IdentityProvider.createIdentity({ id: 'userA', keystore }) - const log = new Log(ipfs, identity, { logId: 'A', access }) + const log = new Log(ipfs, identity, { logId: 'A', access, cacheSize: 5 }) return { log, access, identity } } diff --git a/benchmarks/values.js b/benchmarks/values.js index f5a0a7ae..0b8f8e94 100644 --- a/benchmarks/values.js +++ b/benchmarks/values.js @@ -15,7 +15,7 @@ const base = { return { log, ipfsd } }, cycle: async function ({ log }) { - return log.values + return log.values() }, teardown: async function ({ ipfsd }) { await stopIpfs(ipfsd) diff --git a/lib/es5/entry-index.js b/lib/es5/entry-index.js index 02861431..e4f8088c 100644 --- a/lib/es5/entry-index.js +++ b/lib/es5/entry-index.js @@ -6,37 +6,45 @@ var _classCallCheck2 = _interopRequireDefault(require("@babel/runtime/helpers/cl var _createClass2 = _interopRequireDefault(require("@babel/runtime/helpers/createClass")); +var LRU = require('lru-cache'); + var EntryIndex = /*#__PURE__*/function () { function EntryIndex() { var entries = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; + var cacheSize = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : Infinity; (0, _classCallCheck2["default"])(this, EntryIndex); - this._cache = entries; + this._cache = new LRU({ + max: cacheSize + }); + this.add(entries); } (0, _createClass2["default"])(EntryIndex, [{ key: "set", value: function set(k, v) { - this._cache[k] = v; + this._cache.set(k, v); } }, { key: "get", value: function get(k) { - return this._cache[k]; + return this._cache.get(k); } }, { key: "delete", value: function _delete(k) { - return delete this._cache[k]; + this._cache.del(k); } }, { key: "add", - value: function add(newItems) { - this._cache = Object.assign(this._cache, newItems); + value: function add(items) { + for (var k in items) { + this._cache.set(k, items[k]); + } } }, { key: "length", get: function get() { - return Object.values(this._cache).length; + return this._cache.length; } }]); return EntryIndex; diff --git a/lib/es5/log-io.js b/lib/es5/log-io.js index 010073e7..b67a3427 100644 --- a/lib/es5/log-io.js +++ b/lib/es5/log-io.js @@ -53,6 +53,7 @@ var LogIO = /*#__PURE__*/function () { var _toMultihash = (0, _asyncToGenerator2["default"])( /*#__PURE__*/_regenerator["default"].mark(function _callee(ipfs, log) { var _ref, format, + values, _args = arguments; return _regenerator["default"].wrap(function _callee$(_context) { @@ -78,20 +79,25 @@ var LogIO = /*#__PURE__*/function () { case 5: if (!isDefined(format)) format = 'dag-cbor'; + _context.next = 8; + return log.values(); - if (!(log.values.length < 1)) { - _context.next = 8; + case 8: + values = _context.sent; + + if (!(values.length < 1)) { + _context.next = 11; break; } throw new Error('Can\'t serialize an empty log'); - case 8: + case 11: return _context.abrupt("return", io.write(ipfs, format, log.toJSON(), { links: IPLD_LINKS })); - case 9: + case 12: case "end": return _context.stop(); } diff --git a/package.json b/package.json index 01bec4bc..bb5d4162 100644 --- a/package.json +++ b/package.json @@ -20,6 +20,7 @@ ], "dependencies": { "json-stringify-deterministic": "^1.0.1", + "lru-cache": "^6.0.0", "multihashing-async": "^2.0.1", "orbit-db-identity-provider": "^0.3.1", "orbit-db-io": "^1.0.1", diff --git a/src/entry-index.js b/src/entry-index.js index 1568729e..066da7a9 100644 --- a/src/entry-index.js +++ b/src/entry-index.js @@ -1,28 +1,33 @@ 'use strict' +const LRU = require('lru-cache') + class EntryIndex { - constructor (entries = {}) { - this._cache = entries + constructor (entries = {}, cacheSize = Infinity) { + this._cache = new LRU({ max: cacheSize }) + this.add(entries) } set (k, v) { - this._cache[k] = v + this._cache.set(k, v) } get (k) { - return this._cache[k] + return this._cache.get(k) } delete (k) { - return delete this._cache[k] + this._cache.del(k) } - add (newItems) { - this._cache = Object.assign(this._cache, newItems) + add (items) { + for (const k in items) { + this._cache.set(k, items[k]) + } } get length () { - return Object.values(this._cache).length + return this._cache.length } } diff --git a/src/log-io.js b/src/log-io.js index 5471954b..b8a4cc12 100644 --- a/src/log-io.js +++ b/src/log-io.js @@ -23,7 +23,9 @@ class LogIO { if (!isDefined(ipfs)) throw LogError.IPFSNotDefinedError() if (!isDefined(log)) throw LogError.LogNotDefinedError() if (!isDefined(format)) format = 'dag-cbor' - if (log.values.length < 1) throw new Error('Can\'t serialize an empty log') + + const values = await log.values() + if (values.length < 1) throw new Error('Can\'t serialize an empty log') return io.write(ipfs, format, log.toJSON(), { links: IPLD_LINKS }) } diff --git a/src/log.js b/src/log.js index 159673f5..9b8cbbbb 100644 --- a/src/log.js +++ b/src/log.js @@ -41,9 +41,10 @@ class Log extends GSet { * @param {Array} options.heads Set the heads of the log * @param {Clock} options.clock Set the clock of the log * @param {Function} options.sortFn The sort function - by default LastWriteWins + * @param {Map} options.hashIndex A Map of entry hashes to next pointers * @return {Log} The log instance */ - constructor (ipfs, identity, { logId, access, entries, heads, clock, sortFn, concurrency } = {}) { + constructor (ipfs, identity, { logId, access, entries, heads, clock, sortFn, concurrency, hashIndex = new Map(), cacheSize } = {}) { if (!isDefined(ipfs)) { throw LogError.IPFSNotDefinedError() } @@ -82,20 +83,22 @@ class Log extends GSet { // Add entries to the internal cache const uniqueEntries = (entries || []).reduce(uniqueEntriesReducer, {}) - this._entryIndex = new EntryIndex(uniqueEntries) + this._entryIndex = new EntryIndex(uniqueEntries, cacheSize) entries = Object.values(uniqueEntries) || [] // Set heads if not passed as an argument heads = heads || Log.findHeads(entries) this._headsIndex = heads.reduce(uniqueEntriesReducer, {}) + // Index of log hashes + this._hashIndex = hashIndex + entries.forEach(e => this._hashIndex.set(e.hash, e.next)) + // Index of all next pointers in this log this._nextsIndex = {} const addToNextsIndex = e => e.next.forEach(a => (this._nextsIndex[a] = e.hash)) entries.forEach(addToNextsIndex) - - // Set the length, we calculate the length manually internally - this._length = entries.length + hashIndex.forEach((nexts, hash) => nexts.forEach(a => (this._nextsIndex[a] = hash))) // Set the clock const maxTime = Math.max(clock ? clock.time : 0, this.heads.reduce(maxClockTimeReducer, 0)) @@ -128,15 +131,15 @@ class Log extends GSet { * @return {number} Length */ get length () { - return this._length + return this._hashIndex.size } /** * Returns the values in the log. - * @returns {Array} + * @returns {Promise>} */ - get values () { - return Object.values(this.traverse(this.heads)).reverse() + async values () { + return Object.values(await this.traverse(this.heads)).reverse() } /** @@ -150,19 +153,21 @@ class Log extends GSet { /** * Returns an array of Entry objects that reference entries which * are not in the log currently. - * @returns {Array} + * @returns {Promise>} */ - get tails () { - return Log.findTails(this.values) + async tails () { + const values = await this.values() + return Log.findTails(values) } /** * Returns an array of hashes that are referenced by entries which * are not in the log currently. - * @returns {Array} Array of hashes + * @returns {Promise>} Array of hashes */ - get tailHashes () { - return Log.findTailHashes(this.values) + async tailHashes () { + const values = await this.values() + return Log.findTailHashes(values) } /** @@ -179,10 +184,27 @@ class Log extends GSet { /** * Find an entry. * @param {string} [hash] The hashes of the entry - * @returns {Entry|undefined} + * @returns {Promise} */ - get (hash) { - return this._entryIndex.get(hash) + async get (hash) { + if (!this.has(hash)) { + return undefined + } + + const haveCache = this._entryIndex.get(hash) + if (haveCache) { + return haveCache + } + + let entry + try { + entry = await Entry.fromMultihash(this._storage, hash) + this._entryIndex.set(entry.hash, entry) + } catch (error) { + // TODO + } + + return entry } /** @@ -191,10 +213,10 @@ class Log extends GSet { * @returns {boolean} */ has (entry) { - return this._entryIndex.get(entry.hash || entry) !== undefined + return this._hashIndex.has(entry.hash || entry) } - traverse (rootEntries, amount = -1, endHash) { + async traverse (rootEntries, amount = -1, endHash) { // Sort the given given root entries and use as the starting stack let stack = rootEntries.sort(this._sortFn).reverse() @@ -204,7 +226,7 @@ class Log extends GSet { const result = {} let count = 0 // Named function for getting an entry from the log - const getEntry = e => this.get(e) + const getEntry = async e => this.get(e) // Add an entry to the stack and traversed nodes index const addToStack = entry => { @@ -240,7 +262,7 @@ class Log extends GSet { if (endHash && endHash === entry.hash) break // Add entry's next references to the stack - const entries = entry.next.map(getEntry) + const entries = await pMap(entry.next, getEntry) const defined = entries.filter(isDefined) defined.forEach(addToStack) } @@ -254,14 +276,14 @@ class Log extends GSet { /** * Append an entry to the log. * @param {Entry} entry Entry to add - * @return {Log} New Log containing the appended value + * @return {Promise} New Log containing the appended value */ async append (data, pointerCount = 1, pin = false) { // Update the clock (find the latest clock) const newTime = Math.max(this.clock.time, this.heads.reduce(maxClockTimeReducer, 0)) + 1 this._clock = new Clock(this.clock.id, newTime) - const all = Object.values(this.traverse(this.heads, Math.max(pointerCount, this.heads.length))) + const all = Object.values(await this.traverse(this.heads, Math.max(pointerCount, this.heads.length))) // If pointer count is 4, returns 2 // If pointer count is 8, returns 3 references @@ -309,8 +331,7 @@ class Log extends GSet { nexts.forEach(e => (this._nextsIndex[e] = entry.hash)) this._headsIndex = {} this._headsIndex[entry.hash] = entry - // Update the length - this._length++ + this._hashIndex.set(entry.hash, nexts) return entry } @@ -323,7 +344,7 @@ class Log extends GSet { * @param {string|Array} options.lt Ending hash of the iterator, non-inclusive * @param {string|Array} options.lte Ending hash of the iterator, inclusive * @param {amount} options.amount Number of entried to return to / from the gte / lte hash - * @returns {Symbol.Iterator} Iterator object containing log entries + * @returns {Symbol.asyncIterator} asyncIterator object containing log entries * * @examples * @@ -346,31 +367,50 @@ class Log extends GSet { */ iterator ({ gt = undefined, gte = undefined, lt = undefined, lte = undefined, amount = -1 } = {}) { - if (amount === 0) return (function * () {})() - if (typeof lte === 'string') lte = [this.get(lte)] - if (typeof lt === 'string') lt = [this.get(this.get(lt).next[0])] + if (amount === 0) return (async function * () {})() + if (lte && (typeof lte !== 'string' && !Array.isArray(lte))) { + throw LogError.LtOrLteMustBeStringOrArray() + } + if (lt && (typeof lt !== 'string' && !Array.isArray(lt))) { + throw LogError.LtOrLteMustBeStringOrArray() + } - if (lte && !Array.isArray(lte)) throw LogError.LtOrLteMustBeStringOrArray() - if (lt && !Array.isArray(lt)) throw LogError.LtOrLteMustBeStringOrArray() + const self = this + return (async function * () { + if (typeof lte === 'string') { + const value = await self.get(lte) + lte = [value] + } + if (typeof lt === 'string') { + const value = await self.get(lt) + const next = await self.get(value.next[0]) + lt = [next] + } - const start = (lte || (lt || this.heads)).filter(isDefined) - const endHash = gte ? this.get(gte).hash : gt ? this.get(gt).hash : null - const count = endHash ? -1 : amount || -1 + const start = (lte || (lt || self.heads)).filter(isDefined) + let endHash = null + if (gte) { + const value = await self.get(gte) + endHash = value.hash + } else if (gt) { + const value = await self.get(gt) + endHash = value.hash + } + const count = endHash ? -1 : amount || -1 - const entries = this.traverse(start, count, endHash) - let entryValues = Object.values(entries) + const entries = await self.traverse(start, count, endHash) + let entryValues = Object.values(entries) - // Strip off last entry if gt is non-inclusive - if (gt) entryValues.pop() + // Strip off last entry if gt is non-inclusive + if (gt) entryValues.pop() - // Deal with the amount argument working backwards from gt/gte - if ((gt || gte) && amount > -1) { - entryValues = entryValues.slice(entryValues.length - amount, entryValues.length) - } + // Deal with the amount argument working backwards from gt/gte + if ((gt || gte) && amount > -1) { + entryValues = entryValues.slice(entryValues.length - amount, entryValues.length) + } - return (function * () { - for (const i in entryValues) { - yield entryValues[i] + for (const value of entryValues) { + yield value } })() } @@ -392,7 +432,7 @@ class Log extends GSet { if (this.id !== log.id) return // Get the difference of the logs - const newItems = Log.difference(log, this) + const newItems = await Log.difference(log, this) const identityProvider = this._identity.provider // Verify if entries are allowed to be added to the log and throws if @@ -417,13 +457,12 @@ class Log extends GSet { await verify(e) }, { concurrency: this.joinConcurrency }) - // Update the internal next pointers index - const addToNextsIndex = e => { - const entry = this.get(e.hash) - if (!entry) this._length++ /* istanbul ignore else */ + // Update internal indexes + const addToIndexes = e => { e.next.forEach(a => (this._nextsIndex[a] = e.hash)) + this._hashIndex.set(e.hash, e.next) } - Object.values(newItems).forEach(addToNextsIndex) + entriesToJoin.forEach(addToIndexes) // Update the internal entry index this._entryIndex.add(newItems) @@ -441,12 +480,16 @@ class Log extends GSet { // Slice to the requested size if (size > -1) { - let tmp = this.values + let tmp = await this.values() tmp = tmp.slice(-size) this._entryIndex = null this._entryIndex = new EntryIndex(tmp.reduce(uniqueEntriesReducer, {})) + this._hashIndex = new Map() + tmp.forEach(e => this._hashIndex.set(e.hash, e.next)) + this._nextsIndex = {} + const addToNextsIndex = e => e.next.forEach(a => (this._nextsIndex[a] = e.hash)) + tmp.forEach(addToNextsIndex) this._headsIndex = Log.findHeads(tmp).reduce(uniqueEntriesReducer, {}) - this._length = this._entryIndex.length } // Find the latest clock from the heads @@ -473,11 +516,12 @@ class Log extends GSet { * Get the log in JSON format as a snapshot. * @returns {Object} An object with the id, heads and value properties */ - toSnapshot () { + async toSnapshot () { + const values = await this.values() return { id: this.id, heads: this.heads, - values: this.values + values: values } } @@ -497,12 +541,13 @@ class Log extends GSet { * └─one * └─three */ - toString (payloadMapper) { - return this.values + async toString (payloadMapper) { + const values = await this.values() + return values .slice() .reverse() .map((e, idx) => { - const parents = Entry.findChildren(e, this.values) + const parents = Entry.findChildren(e, values) const len = parents.length let padding = new Array(Math.max(len - 1, 0)) padding = len > 1 ? padding.fill(' ') : padding @@ -520,8 +565,7 @@ class Log extends GSet { */ static isLog (log) { return log.id !== undefined && - log.heads !== undefined && - log._entryIndex !== undefined + log.heads !== undefined } /** @@ -707,31 +751,18 @@ class Log extends GSet { return entries.reduce(reduceTailHashes, []) } - static difference (a, b) { - const stack = Object.keys(a._headsIndex) - const traversed = {} + static async difference (a, b) { const res = {} - - const pushToStack = hash => { - if (!traversed[hash] && !b.get(hash)) { - stack.push(hash) - traversed[hash] = true - } - } - - while (stack.length > 0) { - const hash = stack.shift() - const entry = a.get(hash) - if (entry && !b.get(hash) && entry.id === b.id) { - res[entry.hash] = entry - traversed[entry.hash] = true - entry.next.concat(entry.refs).forEach(pushToStack) - } - } + const hashesFromA = Array.from(a._hashIndex.keys()) + const diff = hashesFromA.filter(hash => !b.has(hash)) + const entries = await pMap(diff, hash => a.get(hash)) + entries.forEach(e => { res[e.hash] = e }) return res } } +Log.Entry = Entry + module.exports = Log module.exports.Sorting = Sorting module.exports.Entry = Entry diff --git a/test/entry-io.spec.js b/test/entry-io.spec.js index 65da3a09..e346853a 100644 --- a/test/entry-io.spec.js +++ b/test/entry-io.spec.js @@ -63,7 +63,8 @@ Object.keys(testAPIs).forEach((IPFS) => { it('log with one entry', async () => { const log = new Log(ipfs, testIdentity, { logId: 'X' }) await log.append('one') - const hash = log.values[0].hash + const values = await log.values() + const hash = values[0].hash const res = await EntryIO.fetchAll(ipfs, hash, { length: 1 }) assert.strictEqual(res.length, 1) }) @@ -72,7 +73,8 @@ Object.keys(testAPIs).forEach((IPFS) => { const log = new Log(ipfs, testIdentity, { logId: 'X' }) await log.append('one') await log.append('two') - const hash = last(log.values).hash + const values = await log.values() + const hash = last(values).hash const res = await EntryIO.fetchAll(ipfs, hash, { length: 2 }) assert.strictEqual(res.length, 2) }) @@ -81,7 +83,8 @@ Object.keys(testAPIs).forEach((IPFS) => { const log = new Log(ipfs, testIdentity, { logId: 'X' }) await log.append('one') await log.append('two') - const hash = last(log.values).hash + const values = await log.values() + const hash = last(values).hash const res = await EntryIO.fetchAll(ipfs, hash, { length: 1 }) assert.strictEqual(res.length, 1) }) @@ -103,9 +106,10 @@ Object.keys(testAPIs).forEach((IPFS) => { let log2 = new Log(ipfs, testIdentity, { logId: 'X' }) for (let i = 1; i <= count; i++) { await log.append('hello' + i) + const values2 = await log2.values() if (i % 10 === 0) { log2 = new Log(ipfs, testIdentity, - { logId: log2.id, entries: log2.values, heads: log2.heads.concat(log.heads) }) + { logId: log2.id, entries: values2, heads: log2.heads.concat(log.heads) }) await log2.append('hi' + i) } } @@ -122,7 +126,8 @@ Object.keys(testAPIs).forEach((IPFS) => { for (let i = 1; i <= count; i++) { await log.append('hello' + i) if (i % 10 === 0) { - log2 = new Log(ipfs, testIdentity, { logId: log2.id, entries: log2.values }) + const values2 = await log2.values() + log2 = new Log(ipfs, testIdentity, { logId: log2.id, entries: values2 }) await log2.append('hi' + i) await log2.join(log) } @@ -141,14 +146,16 @@ Object.keys(testAPIs).forEach((IPFS) => { for (let i = 1; i <= count; i++) { await log.append('hello' + i) if (i % 10 === 0) { + const values2 = await log2.values() log2 = new Log(ipfs, testIdentity, - { logId: log2.id, entries: log2.values, heads: log2.heads }) + { logId: log2.id, entries: values2, heads: log2.heads }) await log2.append('hi' + i) await log2.join(log) } if (i % 25 === 0) { + const values3 = await log3.values() log3 = new Log(ipfs, testIdentity, - { logId: log3.id, entries: log3.values, heads: log3.heads.concat(log2.heads) }) + { logId: log3.id, entries: values3, heads: log3.heads.concat(log2.heads) }) await log3.append('--' + i) } } @@ -172,8 +179,9 @@ Object.keys(testAPIs).forEach((IPFS) => { await log2.join(log) } if (i % 25 === 0) { + const values3 = await log3.values() log3 = new Log(ipfs, testIdentity3, - { logId: log3.id, entries: log3.values, heads: log3.heads.concat(log2.heads) }) + { logId: log3.id, entries: values3, heads: log3.heads.concat(log2.heads) }) await log3.append('--' + i) } } @@ -184,10 +192,13 @@ Object.keys(testAPIs).forEach((IPFS) => { await log4.join(log2) await log4.join(log3) - const values3 = log3.values.map((e) => e.payload) - const values4 = log4.values.map((e) => e.payload) + const values3 = await log3.values() + const values4 = await log4.values() - assert.deepStrictEqual(values3, values4) + const entryPayloads3 = values3.map((e) => e.payload) + const entryPayloads4 = values4.map((e) => e.payload) + + assert.deepStrictEqual(entryPayloads3, entryPayloads4) }) }) }) diff --git a/test/log-append.spec.js b/test/log-append.spec.js index 86c67a95..04a8295e 100644 --- a/test/log-append.spec.js +++ b/test/log-append.spec.js @@ -50,11 +50,12 @@ Object.keys(testAPIs).forEach((IPFS) => { describe('append', () => { describe('append one', async () => { - let log + let log, values before(async () => { log = new Log(ipfs, testIdentity, 'A') await log.append('hello1') + values = await log.values() }) it('added the correct amount of items', () => { @@ -62,25 +63,25 @@ Object.keys(testAPIs).forEach((IPFS) => { }) it('added the correct values', async () => { - log.values.forEach((entry) => { + values.forEach((entry) => { assert.strictEqual(entry.payload, 'hello1') }) }) it('added the correct amount of next pointers', async () => { - log.values.forEach((entry) => { + values.forEach((entry) => { assert.strictEqual(entry.next.length, 0) }) }) it('has the correct heads', async () => { log.heads.forEach((head) => { - assert.strictEqual(head.hash, log.values[0].hash) + assert.strictEqual(head.hash, values[0].hash) }) }) it('updated the clocks correctly', async () => { - log.values.forEach((entry) => { + values.forEach((entry) => { assert.strictEqual(entry.clock.id, testIdentity.publicKey) assert.strictEqual(entry.clock.time, 1) }) @@ -91,14 +92,14 @@ Object.keys(testAPIs).forEach((IPFS) => { const amount = 100 const nextPointerAmount = 64 - let log + let log, values before(async () => { log = new Log(ipfs, testIdentity, 'A') for (let i = 0; i < amount; i++) { await log.append('hello' + i, nextPointerAmount) // Make sure the log has the right heads after each append - const values = log.values + values = await log.values() assert.strictEqual(log.heads.length, 1) assert.strictEqual(log.heads[0].hash, values[values.length - 1].hash) } @@ -109,20 +110,20 @@ Object.keys(testAPIs).forEach((IPFS) => { }) it('added the correct values', async () => { - log.values.forEach((entry, index) => { + values.forEach((entry, index) => { assert.strictEqual(entry.payload, 'hello' + index) }) }) it('updated the clocks correctly', async () => { - log.values.forEach((entry, index) => { + values.forEach((entry, index) => { assert.strictEqual(entry.clock.time, index + 1) assert.strictEqual(entry.clock.id, testIdentity.publicKey) }) }) it('added the correct amount of refs pointers', async () => { - log.values.forEach((entry, index) => { + values.forEach((entry, index) => { assert.strictEqual(entry.refs.length, index > 0 ? Math.ceil(Math.log2(Math.min(nextPointerAmount, index))) : 0) }) }) diff --git a/test/log-crdt.spec.js b/test/log-crdt.spec.js index bed61bde..e6988e6c 100644 --- a/test/log-crdt.spec.js +++ b/test/log-crdt.spec.js @@ -73,7 +73,8 @@ Object.keys(testAPIs).forEach((IPFS) => { await log2.join(log3) await log1.join(log2) - const res1 = log1.values.slice() + const values1 = await log1.values() + const res1 = values1.slice() log1 = new Log(ipfs, testIdentity, { logId: 'X' }) log2 = new Log(ipfs, testIdentity2, { logId: 'X' }) @@ -89,7 +90,8 @@ Object.keys(testAPIs).forEach((IPFS) => { await log1.join(log2) await log3.join(log1) - const res2 = log3.values.slice() + const values3 = await log3.values() + const res2 = values3.slice() // associativity: a + (b + c) == (a + b) + c assert.strictEqual(res1.length, expectedElementsCount) @@ -107,7 +109,8 @@ Object.keys(testAPIs).forEach((IPFS) => { // b + a await log2.join(log1) - const res1 = log2.values.slice() + const values2 = await log2.values() + const res1 = values2.slice() log1 = new Log(ipfs, testIdentity, { logId: 'X' }) log2 = new Log(ipfs, testIdentity2, { logId: 'X' }) @@ -118,7 +121,8 @@ Object.keys(testAPIs).forEach((IPFS) => { // a + b await log1.join(log2) - const res2 = log1.values.slice() + const values1 = await log1.values() + const res2 = values1.slice() // commutativity: a + b == b + a assert.strictEqual(res1.length, expectedElementsCount) @@ -135,7 +139,7 @@ Object.keys(testAPIs).forEach((IPFS) => { await log2.append('helloB1') await log2.append('helloB2') await log2.join(log1) - const resA1 = log2.toString() + const resA1 = await log2.toString() log1 = new Log(ipfs, testIdentity, { logId: 'X' }) log2 = new Log(ipfs, testIdentity2, { logId: 'X' }) @@ -144,7 +148,7 @@ Object.keys(testAPIs).forEach((IPFS) => { await log2.append('helloB1') await log2.append('helloB2') await log1.join(log2) - const resA2 = log1.toString() + const resA2 = await log1.toString() assert.strictEqual(resA1, resA2) @@ -156,7 +160,7 @@ Object.keys(testAPIs).forEach((IPFS) => { await log2.append('helloB1') await log2.append('helloB2') await log1.join(log2) - const resB1 = log1.toString() + const resB1 = await log1.toString() log1 = new Log(ipfs, testIdentity, { logId: 'X' }) log2 = new Log(ipfs, testIdentity2, { logId: 'X' }) @@ -165,7 +169,7 @@ Object.keys(testAPIs).forEach((IPFS) => { await log2.append('helloB1') await log2.append('helloB2') await log2.join(log1) - const resB2 = log2.toString() + const resB2 = await log2.toString() assert.strictEqual(resB1, resB2) @@ -177,7 +181,7 @@ Object.keys(testAPIs).forEach((IPFS) => { await log3.append('helloC1') await log3.append('helloC2') await log3.join(log1) - const resC1 = log3.toString() + const resC1 = await log3.toString() log1 = new Log(ipfs, testIdentity, { logId: 'X' }) log3 = new Log(ipfs, testIdentity3, { logId: 'X' }) @@ -186,7 +190,7 @@ Object.keys(testAPIs).forEach((IPFS) => { await log3.append('helloC1') await log3.append('helloC2') await log1.join(log3) - const resC2 = log1.toString() + const resC2 = await log1.toString() assert.strictEqual(resC1, resC2) @@ -199,7 +203,7 @@ Object.keys(testAPIs).forEach((IPFS) => { await log3.append('helloC1') await log3.append('helloC2') await log3.join(log2) - const resD1 = log3.toString() + const resD1 = await log3.toString() log2 = new Log(ipfs, testIdentity2, { logId: 'X' }) log3 = new Log(ipfs, testIdentity3, { logId: 'X' }) @@ -208,7 +212,7 @@ Object.keys(testAPIs).forEach((IPFS) => { await log3.append('helloC1') await log3.append('helloC2') await log2.join(log3) - const resD2 = log2.toString() + const resD2 = await log2.toString() assert.strictEqual(resD1, resD2) @@ -224,7 +228,7 @@ Object.keys(testAPIs).forEach((IPFS) => { await log3.append('helloC2') await log1.join(log2) await log1.join(log3) - const logLeft = log1.toString() + const logLeft = await log1.toString() log1 = new Log(ipfs, testIdentity, { logId: 'X' }) log2 = new Log(ipfs, testIdentity2, { logId: 'X' }) @@ -237,7 +241,7 @@ Object.keys(testAPIs).forEach((IPFS) => { await log3.append('helloC2') await log3.join(log2) await log3.join(log1) - const logRight = log3.toString() + const logRight = await log3.toString() assert.strictEqual(logLeft, logRight) }) diff --git a/test/log-heads-tails.spec.js b/test/log-heads-tails.spec.js index 46dda4f4..84340562 100644 --- a/test/log-heads-tails.spec.js +++ b/test/log-heads-tails.spec.js @@ -74,7 +74,8 @@ Object.keys(testAPIs).forEach((IPFS) => { const log1 = new Log(ipfs, testIdentity, { logId: 'A' }) await log1.append('helloA1') await log1.append('helloA2') - assert.deepStrictEqual(log1.get(log1.heads[0].hash), log1.heads[0]) + const entry = await log1.get(log1.heads[0].hash) + assert.deepStrictEqual(entry, log1.heads[0]) }) it('finds head after a join and append', async () => { @@ -87,7 +88,8 @@ Object.keys(testAPIs).forEach((IPFS) => { await log2.join(log1) await log2.append('helloB2') - const expectedHead = last(log2.values) + const values2 = await log2.values() + const expectedHead = last(values2) assert.strictEqual(log2.heads.length, 1) assert.deepStrictEqual(log2.heads[0].hash, expectedHead.hash) @@ -99,11 +101,13 @@ Object.keys(testAPIs).forEach((IPFS) => { await log1.append('helloA1') await log1.append('helloA2') - const expectedHead1 = last(log1.values) + const values1 = await log1.values() + const expectedHead1 = last(values1) await log2.append('helloB1') await log2.append('helloB2') - const expectedHead2 = last(log2.values) + const values2 = await log2.values() + const expectedHead2 = last(values2) await log1.join(log2) @@ -129,8 +133,10 @@ Object.keys(testAPIs).forEach((IPFS) => { await log1.append('helloA3') await log1.append('helloA4') - const expectedHead2 = last(log2.values) - const expectedHead1 = last(log1.values) + const values1 = await log1.values() + const values2 = await log2.values() + const expectedHead2 = last(values2) + const expectedHead1 = last(values1) await log1.join(log2) @@ -152,12 +158,14 @@ Object.keys(testAPIs).forEach((IPFS) => { await log1.join(log2) await log1.append('helloA3') await log1.append('helloA4') - const expectedHead1 = last(log1.values) + const values1 = await log1.values() + const expectedHead1 = last(values1) await log3.append('helloC1') await log3.append('helloC2') await log2.join(log3) await log2.append('helloB3') - const expectedHead2 = last(log2.values) + const values2 = await log2.values() + const expectedHead2 = last(values2) await log1.join(log2) const heads = log1.heads @@ -178,12 +186,15 @@ Object.keys(testAPIs).forEach((IPFS) => { await log1.join(log2) await log1.append('helloA3') await log1.append('helloA4') - const expectedHead1 = last(log1.values) + const values1 = await log1.values() + const expectedHead1 = last(values1) await log3.append('helloC1') await log2.append('helloB3') await log3.append('helloC2') - const expectedHead2 = last(log2.values) - const expectedHead3 = last(log3.values) + const values2 = await log2.values() + const values3 = await log3.values() + const expectedHead2 = last(values2) + const expectedHead3 = last(values3) await log1.join(log2) await log1.join(log3) @@ -199,13 +210,15 @@ Object.keys(testAPIs).forEach((IPFS) => { it('returns a tail', async () => { const log1 = new Log(ipfs, testIdentity, { logId: 'A' }) await log1.append('helloA1') - assert.strictEqual(log1.tails.length, 1) + const tails1 = await log1.tails() + assert.strictEqual(tails1.length, 1) }) it('tail is a Entry', async () => { const log1 = new Log(ipfs, testIdentity, { logId: 'A' }) await log1.append('helloA1') - assert.strictEqual(Entry.isEntry(log1.tails[0]), true) + const tails1 = await log1.tails() + assert.strictEqual(Entry.isEntry(tails1[0]), true) }) it('returns tail entries', async () => { @@ -214,9 +227,10 @@ Object.keys(testAPIs).forEach((IPFS) => { await log1.append('helloA1') await log2.append('helloB1') await log1.join(log2) - assert.strictEqual(log1.tails.length, 2) - assert.strictEqual(Entry.isEntry(log1.tails[0]), true) - assert.strictEqual(Entry.isEntry(log1.tails[1]), true) + const tails1 = await log1.tails() + assert.strictEqual(tails1.length, 2) + assert.strictEqual(Entry.isEntry(tails1[0]), true) + assert.strictEqual(Entry.isEntry(tails1[1]), true) }) it('returns tail hashes', async () => { @@ -227,7 +241,8 @@ Object.keys(testAPIs).forEach((IPFS) => { await log2.append('helloB1') await log2.append('helloB2') await log1.join(log2, 2) - assert.strictEqual(log1.tailHashes.length, 2) + const tailHashes = await log1.tailHashes() + assert.strictEqual(tailHashes.length, 2) }) it('returns no tail hashes if all entries point to empty nexts', async () => { @@ -236,7 +251,8 @@ Object.keys(testAPIs).forEach((IPFS) => { await log1.append('helloA1') await log2.append('helloB1') await log1.join(log2) - assert.strictEqual(log1.tailHashes.length, 0) + const tailHashes = await log1.tailHashes() + assert.strictEqual(tailHashes.length, 0) }) it('returns tails after loading a partial log', async () => { @@ -249,9 +265,11 @@ Object.keys(testAPIs).forEach((IPFS) => { await log1.join(log2) const log4 = await Log.fromEntry(ipfs, testIdentity, log1.heads, { length: 2 }) assert.strictEqual(log4.length, 2) - assert.strictEqual(log4.tails.length, 2) - assert.strictEqual(log4.tails[0].hash, log4.values[0].hash) - assert.strictEqual(log4.tails[1].hash, log4.values[1].hash) + const tails4 = await log4.tails() + assert.strictEqual(tails4.length, 2) + const values4 = await log4.values() + assert.strictEqual(tails4[0].hash, values4[0].hash) + assert.strictEqual(tails4[1].hash, values4[1].hash) }) it('returns tails sorted by public key', async () => { @@ -265,11 +283,12 @@ Object.keys(testAPIs).forEach((IPFS) => { await log3.join(log1) await log3.join(log2) await log4.join(log3) - assert.strictEqual(log4.tails.length, 3) - assert.strictEqual(log4.tails[0].id, 'XX') - assert.strictEqual(log4.tails[0].clock.id, testIdentity3.publicKey) - assert.strictEqual(log4.tails[1].clock.id, testIdentity2.publicKey) - assert.strictEqual(log4.tails[2].clock.id, testIdentity.publicKey) + const tails4 = await log4.tails() + assert.strictEqual(tails4.length, 3) + assert.strictEqual(tails4[0].id, 'XX') + assert.strictEqual(tails4[0].clock.id, testIdentity3.publicKey) + assert.strictEqual(tails4[1].clock.id, testIdentity2.publicKey) + assert.strictEqual(tails4[2].clock.id, testIdentity.publicKey) assert.strictEqual(log4.clock.id, testIdentity4.publicKey) }) }) diff --git a/test/log-iterator.spec.js b/test/log-iterator.spec.js index 3f482328..31dc90aa 100644 --- a/test/log-iterator.spec.js +++ b/test/log-iterator.spec.js @@ -16,6 +16,16 @@ const { stopIpfs } = require('orbit-db-test-utils') +const toArray = async (iterator) => { + const arr = [] + + for await (const entry of iterator) { + arr.push(entry) + } + + return arr +} + let ipfsd, ipfs, testIdentity, testIdentity2, testIdentity3 Object.keys(testAPIs).forEach((IPFS) => { @@ -62,14 +72,14 @@ Object.keys(testAPIs).forEach((IPFS) => { } }) - it('returns a Symbol.iterator object', async () => { + it('returns a Symbol.asyncIterator object', async () => { const it = log1.iterator({ lte: 'zdpuAuNuQ4YBeXY5YStfrsJx6ykz4yBV2XnNcBR4uGmiojQde', amount: 0 }) - assert.strictEqual(typeof it[Symbol.iterator], 'function') - assert.deepStrictEqual(it.next(), { value: undefined, done: true }) + assert.strictEqual(typeof it[Symbol.asyncIterator], 'function') + assert.deepStrictEqual(await it.next(), { value: undefined, done: true }) }) it('returns length with lte and amount', async () => { @@ -79,7 +89,8 @@ Object.keys(testAPIs).forEach((IPFS) => { amount: amount }) - assert.strictEqual([...it].length, 10) + const values = await toArray(it) + assert.strictEqual(values.length, 10) }) it('returns entries with lte and amount', async () => { @@ -91,7 +102,7 @@ Object.keys(testAPIs).forEach((IPFS) => { }) let i = 0 - for (const entry of it) { + for await (const entry of it) { assert.strictEqual(entry.payload, 'entry' + (67 - i++)) } }) @@ -104,7 +115,8 @@ Object.keys(testAPIs).forEach((IPFS) => { amount: amount }) - assert.strictEqual([...it].length, amount) + const values = await toArray(it) + assert.strictEqual(values.length, amount) }) it('returns entries with lt and amount', async () => { @@ -116,7 +128,7 @@ Object.keys(testAPIs).forEach((IPFS) => { }) let i = 1 - for (const entry of it) { + for await (const entry of it) { assert.strictEqual(entry.payload, 'entry' + (67 - i++)) } }) @@ -130,7 +142,7 @@ Object.keys(testAPIs).forEach((IPFS) => { let i = 0 let count = 0 - for (const entry of it) { + for await (const entry of it) { assert.strictEqual(entry.payload, 'entry' + (72 - i++)) count++ } @@ -145,7 +157,8 @@ Object.keys(testAPIs).forEach((IPFS) => { amount: amount }) - assert.strictEqual([...it].length, amount) + const values = await toArray(it) + assert.strictEqual(values.length, amount) }) it('returns entries with gte and amount', async () => { @@ -157,7 +170,7 @@ Object.keys(testAPIs).forEach((IPFS) => { }) let i = 0 - for (const entry of it) { + for await (const entry of it) { assert.strictEqual(entry.payload, 'entry' + (79 - i++)) } }) @@ -168,7 +181,8 @@ Object.keys(testAPIs).forEach((IPFS) => { gt: 'zdpuAymZUrYbHgwfYK76xXYhzxNqwaXRWWrn5kmRsZJFdqBEz', lt: 'zdpuAoDcWRiChLXnGskymcGrM1VdAjsaFrsXvNZmcDattA7AF' }) - const hashes = [...it].map(e => e.hash) + const values = await toArray(it) + const hashes = values.map(e => e.hash) // neither hash should appear in the array assert.strictEqual(hashes.indexOf('zdpuAymZUrYbHgwfYK76xXYhzxNqwaXRWWrn5kmRsZJFdqBEz'), -1) @@ -181,7 +195,8 @@ Object.keys(testAPIs).forEach((IPFS) => { gte: 'zdpuAt7YtNE1i9APJitGyKomcmxjc2BDHa57wkrjq4onqBNaR', lt: 'zdpuAr8N4vzqcB5sh5JLcr6Eszo4HnYefBWDbBBwwrTPo6kU6' }) - const hashes = [...it].map(e => e.hash) + const values = await toArray(it) + const hashes = values.map(e => e.hash) // only the gte hash should appear in the array assert.strictEqual(hashes.indexOf('zdpuAt7YtNE1i9APJitGyKomcmxjc2BDHa57wkrjq4onqBNaR'), 24) @@ -194,7 +209,8 @@ Object.keys(testAPIs).forEach((IPFS) => { gt: 'zdpuAqUrGrPa4AaZAQbCH4yxQfEjB32rdFY743XCgyGW8iAuU', lte: 'zdpuAwkagwE9D2jUtLnDiCPqBGh9xhpnaX8iEDQ3K7HRmjggi' }) - const hashes = [...it].map(e => e.hash) + const values = await toArray(it) + const hashes = values.map(e => e.hash) // only the lte hash should appear in the array assert.strictEqual(hashes.indexOf('zdpuAqUrGrPa4AaZAQbCH4yxQfEjB32rdFY743XCgyGW8iAuU'), -1) @@ -207,7 +223,8 @@ Object.keys(testAPIs).forEach((IPFS) => { gte: 'zdpuAzG5AD1GdeNffSskTErjjPbAb95QiNyoaQSrbB62eqYSD', lte: 'zdpuAuujURnUUxVw338Xwh47zGEFjjbaZXXARHPik6KYUcUVk' }) - const hashes = [...it].map(e => e.hash) + const values = await toArray(it) + const hashes = values.map(e => e.hash) // neither hash should appear in the array assert.strictEqual(hashes.indexOf('zdpuAzG5AD1GdeNffSskTErjjPbAb95QiNyoaQSrbB62eqYSD'), 9) @@ -220,7 +237,8 @@ Object.keys(testAPIs).forEach((IPFS) => { gt: 'zdpuAuNuQ4YBeXY5YStfrsJx6ykz4yBV2XnNcBR4uGmiojQde' }) - assert.strictEqual([...it].length, 33) + const values = await toArray(it) + assert.strictEqual(values.length, 33) }) it('returns entries with gt and default amount', async () => { @@ -229,7 +247,7 @@ Object.keys(testAPIs).forEach((IPFS) => { }) let i = 0 - for (const entry of it) { + for await (const entry of it) { assert.strictEqual(entry.payload, 'entry' + (100 - i++)) } }) @@ -239,7 +257,8 @@ Object.keys(testAPIs).forEach((IPFS) => { gte: 'zdpuAuNuQ4YBeXY5YStfrsJx6ykz4yBV2XnNcBR4uGmiojQde' }) - assert.strictEqual([...it].length, 34) + const values = await toArray(it) + assert.strictEqual(values.length, 34) }) it('returns entries with gte and default amount', async () => { @@ -248,7 +267,7 @@ Object.keys(testAPIs).forEach((IPFS) => { }) let i = 0 - for (const entry of it) { + for await (const entry of it) { assert.strictEqual(entry.payload, 'entry' + (100 - i++)) } }) @@ -258,7 +277,8 @@ Object.keys(testAPIs).forEach((IPFS) => { lt: 'zdpuAuNuQ4YBeXY5YStfrsJx6ykz4yBV2XnNcBR4uGmiojQde' }) - assert.strictEqual([...it].length, 67) + const values = await toArray(it) + assert.strictEqual(values.length, 67) }) it('returns entries with lt and default amount value', async () => { @@ -267,7 +287,7 @@ Object.keys(testAPIs).forEach((IPFS) => { }) let i = 0 - for (const entry of it) { + for await (const entry of it) { assert.strictEqual(entry.payload, 'entry' + (66 - i++)) } }) @@ -277,7 +297,8 @@ Object.keys(testAPIs).forEach((IPFS) => { lte: 'zdpuAuNuQ4YBeXY5YStfrsJx6ykz4yBV2XnNcBR4uGmiojQde' }) - assert.strictEqual([...it].length, 68) + const values = await toArray(it) + assert.strictEqual(values.length, 68) }) it('returns entries with lte and default amount value', async () => { @@ -286,7 +307,7 @@ Object.keys(testAPIs).forEach((IPFS) => { }) let i = 0 - for (const entry of it) { + for await (const entry of it) { assert.strictEqual(entry.payload, 'entry' + (67 - i++)) } }) @@ -305,7 +326,8 @@ Object.keys(testAPIs).forEach((IPFS) => { lte: fixture.log.heads }) - assert.strictEqual([...it].length, 16) + const values = await toArray(it) + assert.strictEqual(values.length, 16) }) it('returns partial entries from all heads', async () => { @@ -314,7 +336,8 @@ Object.keys(testAPIs).forEach((IPFS) => { amount: 6 }) - assert.deepStrictEqual([...it].map(e => e.payload), + const values = await toArray(it) + assert.deepStrictEqual(values.map(e => e.payload), ['entryA10', 'entryA9', 'entryA8', 'entryA7', 'entryC0', 'entryA6']) }) @@ -323,7 +346,8 @@ Object.keys(testAPIs).forEach((IPFS) => { lte: [fixture.log.heads[0]] }) - assert.strictEqual([...it].length, 10) + const values = await toArray(it) + assert.strictEqual(values.length, 10) }) it('returns partial logs from single heads #2', async () => { @@ -331,7 +355,8 @@ Object.keys(testAPIs).forEach((IPFS) => { lte: [fixture.log.heads[1]] }) - assert.strictEqual([...it].length, 11) + const values = await toArray(it) + assert.strictEqual(values.length, 11) }) it('throws error if lt/lte not a string or array of entries', async () => { diff --git a/test/log-join-concurrent.spec.js b/test/log-join-concurrent.spec.js index 0f257c0f..55f943d1 100644 --- a/test/log-join-concurrent.spec.js +++ b/test/log-join-concurrent.spec.js @@ -60,9 +60,12 @@ Object.keys(testAPIs).forEach(IPFS => { const hash1 = await log1.toMultihash() const hash2 = await log2.toMultihash() + const values1 = await log1.values() + const values2 = await log2.values() + assert.strictEqual(hash1, hash2) assert.strictEqual(log1.length, 20) - assert.deepStrictEqual(log1.values.map(e => e.payload), log2.values.map(e => e.payload)) + assert.deepStrictEqual(values1.map(e => e.payload), values2.map(e => e.payload)) }) it('Concurrently appending same payload after join results in same state', async () => { @@ -80,19 +83,25 @@ Object.keys(testAPIs).forEach(IPFS => { const hash1 = await log1.toMultihash() const hash2 = await log2.toMultihash() + const values1 = await log1.values() + const values2 = await log2.values() + assert.strictEqual(hash1, hash2) assert.strictEqual(log1.length, 41) assert.strictEqual(log2.length, 41) - assert.deepStrictEqual(log1.values.map(e => e.payload), log2.values.map(e => e.payload)) + assert.deepStrictEqual(values1.map(e => e.payload), values2.map(e => e.payload)) }) 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() + assert.strictEqual(log1.length, log2.length) assert.strictEqual(log1.length, 41) - assert.deepStrictEqual(log1.values.map(e => e.payload), log2.values.map(e => e.payload)) + assert.deepStrictEqual(values1.map(e => e.payload), values2.map(e => e.payload)) }) }) }) diff --git a/test/log-join.spec.js b/test/log-join.spec.js index c6a6766e..fca7afe0 100644 --- a/test/log-join.spec.js +++ b/test/log-join.spec.js @@ -136,10 +136,11 @@ Object.keys(testAPIs).forEach((IPFS) => { 'helloA1', 'helloB1', 'helloA2', 'helloB2' ] + const values1 = await log1.values() assert.strictEqual(log1.length, 4) - assert.deepStrictEqual(log1.values.map((e) => e.payload), expectedData) + assert.deepStrictEqual(values1.map((e) => e.payload), expectedData) - const item = last(log1.values) + const item = last(values1) assert.strictEqual(item.next.length, 1) }) @@ -155,9 +156,11 @@ Object.keys(testAPIs).forEach((IPFS) => { 'helloA1', 'helloB1', 'helloA2', 'helloB2' ] - assert.deepStrictEqual(log1.values.map((e) => e.hash), log2.values.map((e) => e.hash)) - assert.deepStrictEqual(log1.values.map((e) => e.payload), expectedData) - assert.deepStrictEqual(log2.values.map((e) => e.payload), expectedData) + const values1 = await log1.values() + const values2 = await log2.values() + assert.deepStrictEqual(values1.map((e) => e.hash), values2.map((e) => e.hash)) + assert.deepStrictEqual(values1.map((e) => e.payload), expectedData) + assert.deepStrictEqual(values2.map((e) => e.payload), expectedData) }) it('joins logs twice', async () => { @@ -174,7 +177,8 @@ Object.keys(testAPIs).forEach((IPFS) => { ] assert.strictEqual(log2.length, 4) - assert.deepStrictEqual(log2.values.map((e) => e.payload), expectedData) + const values2 = await log2.values() + assert.deepStrictEqual(values2.map((e) => e.payload), expectedData) }) it('joins 2 logs two ways', async () => { @@ -191,7 +195,8 @@ Object.keys(testAPIs).forEach((IPFS) => { ] assert.strictEqual(log2.length, 4) - assert.deepStrictEqual(log2.values.map((e) => e.payload), expectedData) + const values2 = await log2.values() + assert.deepStrictEqual(values2.map((e) => e.payload), expectedData) }) it('joins 2 logs two ways and has the right heads at every step', async () => { @@ -256,7 +261,8 @@ Object.keys(testAPIs).forEach((IPFS) => { ] assert.strictEqual(log1.length, 8) - assert.deepStrictEqual(log1.values.map(e => e.payload), expectedData) + const values1 = await log1.values() + assert.deepStrictEqual(values1.map(e => e.payload), expectedData) }) it('joins 4 logs to one is commutative', async () => { @@ -276,7 +282,9 @@ Object.keys(testAPIs).forEach((IPFS) => { await log2.join(log4) assert.strictEqual(log1.length, 8) - assert.deepStrictEqual(log1.values.map(e => e.payload), log2.values.map(e => e.payload)) + const values1 = await log1.values() + const values2 = await log2.values() + assert.deepStrictEqual(values1.map(e => e.payload), values2.map(e => e.payload)) }) it('joins logs and updates clocks', async () => { @@ -335,7 +343,8 @@ Object.keys(testAPIs).forEach((IPFS) => { { payload: 'helloD6', id: 'X', clock: new Clock(testIdentity4.publicKey, 8) } ] - const transformed = log4.values.map((e) => { + const values4 = await log4.values() + const transformed = values4.map((e) => { return { payload: e.payload, id: e.id, clock: e.clock } }) @@ -390,7 +399,8 @@ Object.keys(testAPIs).forEach((IPFS) => { ] assert.strictEqual(log4.length, 10) - assert.deepStrictEqual(log4.values.map((e) => e.payload), expectedData) + const values4 = await log4.values() + assert.deepStrictEqual(values4.map((e) => e.payload), expectedData) }) describe('takes length as an argument', async () => { @@ -407,10 +417,11 @@ Object.keys(testAPIs).forEach((IPFS) => { const expectedData = [ 'helloB2' ] - const lastEntry = last(log1.values) + const values1 = await log1.values() + const lastEntry = last(values1) assert.strictEqual(log1.length, 1) - assert.deepStrictEqual(log1.values.map((e) => e.payload), expectedData) + assert.deepStrictEqual(values1.map((e) => e.payload), expectedData) assert.strictEqual(lastEntry.next.length, 1) }) @@ -420,10 +431,11 @@ Object.keys(testAPIs).forEach((IPFS) => { const expectedData = [ 'helloA2', 'helloB2' ] - const lastEntry = last(log1.values) + const values1 = await log1.values() + const lastEntry = last(values1) assert.strictEqual(log1.length, 2) - assert.deepStrictEqual(log1.values.map((e) => e.payload), expectedData) + assert.deepStrictEqual(values1.map((e) => e.payload), expectedData) assert.strictEqual(lastEntry.next.length, 1) }) @@ -433,10 +445,11 @@ Object.keys(testAPIs).forEach((IPFS) => { const expectedData = [ 'helloB1', 'helloA2', 'helloB2' ] - const lastEntry = last(log1.values) + const values1 = await log1.values() + const lastEntry = last(values1) assert.strictEqual(log1.length, 3) - assert.deepStrictEqual(log1.values.map((e) => e.payload), expectedData) + assert.deepStrictEqual(values1.map((e) => e.payload), expectedData) assert.strictEqual(lastEntry.next.length, 1) }) @@ -446,10 +459,11 @@ Object.keys(testAPIs).forEach((IPFS) => { const expectedData = [ 'helloA1', 'helloB1', 'helloA2', 'helloB2' ] - const lastEntry = last(log1.values) + const values1 = await log1.values() + const lastEntry = last(values1) assert.strictEqual(log1.length, 4) - assert.deepStrictEqual(log1.values.map((e) => e.payload), expectedData) + assert.deepStrictEqual(values1.map((e) => e.payload), expectedData) assert.strictEqual(lastEntry.next.length, 1) }) }) diff --git a/test/log-load.spec.js b/test/log-load.spec.js index 896d85c6..a5ffa75d 100644 --- a/test/log-load.spec.js +++ b/test/log-load.spec.js @@ -93,10 +93,10 @@ Object.keys(testAPIs).forEach((IPFS) => { json.heads = await Promise.all(json.heads.map(headHash => Entry.fromMultihash(ipfs, headHash))) const log = await Log.fromJSON(ipfs, testIdentity, json, { logId: 'X' }) - + const values = await log.values() assert.strictEqual(log.id, data.heads[0].id) assert.strictEqual(log.length, 16) - assert.deepStrictEqual(log.values.map(e => e.payload), fixture.expectedData) + assert.deepStrictEqual(values.map(e => e.payload), fixture.expectedData) }) it('creates a log from an entry with custom tiebreaker', async () => { @@ -109,9 +109,10 @@ Object.keys(testAPIs).forEach((IPFS) => { const log = await Log.fromJSON(ipfs, testIdentity, json, { length: -1, logId: 'X', sortFn: FirstWriteWins }) + const values = await log.values() assert.strictEqual(log.id, data.heads[0].id) assert.strictEqual(log.length, 16) - assert.deepStrictEqual(log.values.map(e => e.payload), firstWriteExpectedData) + assert.deepStrictEqual(values.map(e => e.payload), firstWriteExpectedData) }) it('respects timeout parameter', async () => { @@ -125,8 +126,9 @@ Object.keys(testAPIs).forEach((IPFS) => { const et = new Date().getTime() // Allow for a few millseconds of skew assert.strictEqual((et - st) >= (timeout - 10), true, '' + (et - st) + ' should be greater than timeout ' + timeout) + const values = await log.values() assert.strictEqual(log.length, 0) - assert.deepStrictEqual(log.values.map(e => e.payload), []) + assert.deepStrictEqual(values.map(e => e.payload), []) }) }) @@ -148,10 +150,10 @@ Object.keys(testAPIs).forEach((IPFS) => { { logId: 'X' }) await log1.join(log2) - + const values1 = await log1.values() assert.strictEqual(log1.id, data.heads[0].id) assert.strictEqual(log1.length, 16) - assert.deepStrictEqual(log1.values.map(e => e.payload), fixture.expectedData) + assert.deepStrictEqual(values1.map(e => e.payload), fixture.expectedData) }) it('creates a log from an entry hash with custom tiebreaker', async () => { @@ -164,20 +166,21 @@ Object.keys(testAPIs).forEach((IPFS) => { { logId: 'X', sortFn: FirstWriteWins }) await log1.join(log2) - + const values1 = await log1.values() assert.strictEqual(log1.id, data.heads[0].id) assert.strictEqual(log1.length, 16) - assert.deepStrictEqual(log1.values.map(e => e.payload), firstWriteExpectedData) + assert.deepStrictEqual(values1.map(e => e.payload), firstWriteExpectedData) }) it('respects timeout parameter', async () => { const timeout = 500 const st = new Date().getTime() - const log = await Log.fromEntryHash(ipfs, testIdentity, 'zdpuAwNuRc2Kc1aNDdcdSWuxfNpHRJQw8L8APBNHCEFXbogus', { logId: 'X', timeout }) + const log1 = await Log.fromEntryHash(ipfs, testIdentity, 'zdpuAwNuRc2Kc1aNDdcdSWuxfNpHRJQw8L8APBNHCEFXbogus', { logId: 'X', timeout }) const et = new Date().getTime() + const values1 = await log1.values() assert.strictEqual((et - st) >= timeout, true, '' + (et - st) + ' should be greater than timeout ' + timeout) - assert.strictEqual(log.length, 0) - assert.deepStrictEqual(log.values.map(e => e.payload), []) + assert.strictEqual(log1.length, 0) + assert.deepStrictEqual(values1.map(e => e.payload), []) }) }) @@ -192,21 +195,24 @@ Object.keys(testAPIs).forEach((IPFS) => { const fixture = await LogCreator.createLogWithSixteenEntries(Log, ipfs, identities) const data = fixture.log - const log = await Log.fromEntry(ipfs, testIdentity, data.heads, { length: -1 }) - assert.strictEqual(log.id, data.heads[0].id) - assert.strictEqual(log.length, 16) - assert.deepStrictEqual(log.values.map(e => e.payload), fixture.expectedData) + const log1 = await Log.fromEntry(ipfs, testIdentity, data.heads, { length: -1 }) + const values1 = await log1.values() + assert.strictEqual(log1.id, data.heads[0].id) + assert.strictEqual(log1.length, 16) + assert.deepStrictEqual(values1.map(e => e.payload), fixture.expectedData) }) it('creates a log from an entry with custom tiebreaker', async () => { const fixture = await LogCreator.createLogWithSixteenEntries(Log, ipfs, identities) const data = fixture.log - const log = await Log.fromEntry(ipfs, testIdentity, data.heads, + const log1 = await Log.fromEntry(ipfs, testIdentity, data.heads, { length: -1, sortFn: FirstWriteWins }) - assert.strictEqual(log.id, data.heads[0].id) - assert.strictEqual(log.length, 16) - assert.deepStrictEqual(log.values.map(e => e.payload), firstWriteExpectedData) + + const values1 = await log1.values() + assert.strictEqual(log1.id, data.heads[0].id) + assert.strictEqual(log1.length, 16) + assert.deepStrictEqual(values1.map(e => e.payload), firstWriteExpectedData) }) it('keeps the original heads', async () => { @@ -215,29 +221,33 @@ Object.keys(testAPIs).forEach((IPFS) => { const log1 = await Log.fromEntry(ipfs, testIdentity, data.heads, { length: data.heads.length }) + + const values1 = await log1.values() assert.strictEqual(log1.id, data.heads[0].id) assert.strictEqual(log1.length, data.heads.length) - assert.strictEqual(log1.values[0].payload, 'entryC0') - assert.strictEqual(log1.values[1].payload, 'entryA10') + assert.strictEqual(values1[0].payload, 'entryC0') + assert.strictEqual(values1[1].payload, 'entryA10') const log2 = await Log.fromEntry(ipfs, testIdentity, data.heads, { length: 4 }) + const values2 = await log2.values() assert.strictEqual(log2.id, data.heads[0].id) assert.strictEqual(log2.length, 4) - assert.strictEqual(log2.values[0].payload, 'entryC0') - assert.strictEqual(log2.values[1].payload, 'entryA8') - assert.strictEqual(log2.values[2].payload, 'entryA9') - assert.strictEqual(log2.values[3].payload, 'entryA10') + assert.strictEqual(values2[0].payload, 'entryC0') + assert.strictEqual(values2[1].payload, 'entryA8') + assert.strictEqual(values2[2].payload, 'entryA9') + assert.strictEqual(values2[3].payload, 'entryA10') const log3 = await Log.fromEntry(ipfs, testIdentity, data.heads, { length: 7 }) + const values3 = await log3.values() assert.strictEqual(log3.id, data.heads[0].id) assert.strictEqual(log3.length, 7) - assert.strictEqual(log3.values[0].payload, 'entryB5') - assert.strictEqual(log3.values[1].payload, 'entryA6') - assert.strictEqual(log3.values[2].payload, 'entryC0') - assert.strictEqual(log3.values[3].payload, 'entryA7') - assert.strictEqual(log3.values[4].payload, 'entryA8') - assert.strictEqual(log3.values[5].payload, 'entryA9') - assert.strictEqual(log3.values[6].payload, 'entryA10') + assert.strictEqual(values3[0].payload, 'entryB5') + assert.strictEqual(values3[1].payload, 'entryA6') + assert.strictEqual(values3[2].payload, 'entryC0') + assert.strictEqual(values3[3].payload, 'entryA7') + assert.strictEqual(values3[4].payload, 'entryA8') + assert.strictEqual(values3[5].payload, 'entryA9') + assert.strictEqual(values3[6].payload, 'entryA10') }) it('onProgress callback is fired for each entry', async () => { @@ -432,8 +442,9 @@ Object.keys(testAPIs).forEach((IPFS) => { const b = await Log.fromEntry(ipfs, testIdentity2, last(items2), { length: amount * 2 }) + const bValues = await b.values() assert.strictEqual(b.length, amount * 2) - assert.deepStrictEqual(b.values.map((e) => e.payload), itemsInB) + assert.deepStrictEqual(bValues.map((e) => e.payload), itemsInB) const c = await Log.fromEntry(ipfs, testIdentity4, last(items3), { length: amount * 3 }) @@ -473,14 +484,16 @@ Object.keys(testAPIs).forEach((IPFS) => { 'entryC10', 'EOF' ] - assert.deepStrictEqual(c.values.map(e => e.payload), tmp) + let cValues = await c.values() + assert.deepStrictEqual(cValues.map(e => e.payload), tmp) // make sure logX comes after A, B and C const logX = new Log(ipfs, testIdentity4, { logId: 'X' }) await logX.append('1') await logX.append('2') await logX.append('3') - const d = await Log.fromEntry(ipfs, testIdentity3, last(logX.values), + const xValues = await logX.values() + const d = await Log.fromEntry(ipfs, testIdentity3, last(xValues), { length: -1 }) await c.join(d) @@ -488,13 +501,17 @@ Object.keys(testAPIs).forEach((IPFS) => { await c.append('DONE') await d.append('DONE') - const f = await Log.fromEntry(ipfs, testIdentity3, last(c.values), + cValues = await c.values() + const f = await Log.fromEntry(ipfs, testIdentity3, last(cValues), { amount: -1, exclude: [] }) - const g = await Log.fromEntry(ipfs, testIdentity3, last(d.values), + const dValues = await d.values() + const g = await Log.fromEntry(ipfs, testIdentity3, last(dValues), { length: -1, exclude: [] }) - assert.strictEqual(f.toString(), bigLogString) - assert.strictEqual(g.toString(), bigLogString) + const fString = await f.toString() + const gString = await g.toString() + assert.strictEqual(fString, bigLogString) + assert.strictEqual(gString, bigLogString) }) it('retrieves full log of randomly joined log', async () => { @@ -531,7 +548,8 @@ Object.keys(testAPIs).forEach((IPFS) => { 'entryA11', 'entryA12', 'entryA13', 'entryA14', 'entryA15' ] - assert.deepStrictEqual(log1.values.map(e => e.payload), expectedData) + const values1 = await log1.values() + assert.deepStrictEqual(values1.map(e => e.payload), expectedData) }) it('retrieves randomly joined log deterministically', async () => { @@ -567,7 +585,8 @@ Object.keys(testAPIs).forEach((IPFS) => { 'entryC0', 'entryA7', 'entryA8', 'entryA9', 'entryA10' ] - assert.deepStrictEqual(log.values.map(e => e.payload), expectedData) + const values = await log.values() + assert.deepStrictEqual(values.map(e => e.payload), expectedData) }) it('sorts', async () => { @@ -596,26 +615,27 @@ Object.keys(testAPIs).forEach((IPFS) => { 'entryA8', 'entryA9', 'entryA10' ] - const fetchOrder = log.values.slice().sort(Entry.compare) + const values = await log.values() + const fetchOrder = values.slice().sort(Entry.compare) assert.deepStrictEqual(fetchOrder.map(e => e.payload), expectedData) - const reverseOrder = log.values.slice().reverse().sort(Entry.compare) + const reverseOrder = values.slice().reverse().sort(Entry.compare) assert.deepStrictEqual(fetchOrder, reverseOrder) - const hashOrder = log.values.slice().sort((a, b) => a.hash > b.hash).sort(Entry.compare) + const hashOrder = values.slice().sort((a, b) => a.hash > b.hash).sort(Entry.compare) assert.deepStrictEqual(fetchOrder, hashOrder) - const randomOrder2 = log.values.slice().sort((a, b) => 0.5 - Math.random()).sort(Entry.compare) + const randomOrder2 = values.slice().sort((a, b) => 0.5 - Math.random()).sort(Entry.compare) assert.deepStrictEqual(fetchOrder, randomOrder2) // partial data - const partialLog = log.values.filter(e => e.payload !== 'entryC0').sort(Entry.compare) + const partialLog = values.filter(e => e.payload !== 'entryC0').sort(Entry.compare) assert.deepStrictEqual(partialLog.map(e => e.payload), expectedData2) - const partialLog2 = log.values.filter(e => e.payload !== 'entryA10').sort(Entry.compare) + const partialLog2 = values.filter(e => e.payload !== 'entryA10').sort(Entry.compare) assert.deepStrictEqual(partialLog2.map(e => e.payload), expectedData3) - const partialLog3 = log.values.filter(e => e.payload !== 'entryB5').sort(Entry.compare) + const partialLog3 = values.filter(e => e.payload !== 'entryB5').sort(Entry.compare) assert.deepStrictEqual(partialLog3.map(e => e.payload), expectedData4) }) @@ -624,12 +644,14 @@ Object.keys(testAPIs).forEach((IPFS) => { const log = testLog.log const expectedData = testLog.expectedData - const fetchOrder = log.values.slice().sort(Entry.compare) + const values = await log.values() + const fetchOrder = values.slice().sort(Entry.compare) assert.deepStrictEqual(fetchOrder.map(e => e.payload), expectedData) let sorted for (let i = 0; i < 1000; i++) { - const randomOrder = log.values.slice().sort((a, b) => 0.5 - Math.random()) + const values = await log.values() + const randomOrder = values.slice().sort((a, b) => 0.5 - Math.random()) sorted = randomOrder.sort(Entry.compare) assert.deepStrictEqual(sorted.map(e => e.payload), expectedData) } @@ -639,7 +661,8 @@ Object.keys(testAPIs).forEach((IPFS) => { const testLog = await LogCreator.createLogWithTwoHundredEntries(Log, ipfs, identities) const log = testLog.log const expectedData = testLog.expectedData - assert.deepStrictEqual(log.values.map(e => e.payload), expectedData) + const values = await log.values() + assert.deepStrictEqual(values.map(e => e.payload), expectedData) }) it('sorts entries according to custom tiebreaker function', async () => { @@ -648,7 +671,8 @@ Object.keys(testAPIs).forEach((IPFS) => { const firstWriteWinsLog = new Log(ipfs, identities[0], { logId: 'X', sortFn: FirstWriteWins }) await firstWriteWinsLog.join(testLog.log) - assert.deepStrictEqual(firstWriteWinsLog.values.map(e => e.payload), + const firstWriteWinsLogValues = await firstWriteWinsLog.values() + assert.deepStrictEqual(firstWriteWinsLogValues.map(e => e.payload), firstWriteExpectedData) }) @@ -657,7 +681,14 @@ Object.keys(testAPIs).forEach((IPFS) => { const firstWriteWinsLog = new Log(ipfs, identities[0], { logId: 'X', sortFn: BadComparatorReturnsZero }) await firstWriteWinsLog.join(testLog.log) - assert.throws(() => firstWriteWinsLog.values, Error, 'Error Thrown') + + let err + try { + await firstWriteWinsLog.values() + } catch (e) { + err = e + } + assert.notStrictEqual(err, undefined) }) it('retrieves partially joined log deterministically - single next pointer', async () => { @@ -697,7 +728,8 @@ Object.keys(testAPIs).forEach((IPFS) => { 'entryC0', 'entryA7', 'entryA8', 'entryA9', 'entryA10' ] - assert.deepStrictEqual(res.values.map(e => e.payload), first5) + const values = await res.values() + assert.deepStrictEqual(values.map(e => e.payload), first5) // First 11 res = await Log.fromMultihash(ipfs, testIdentity2, hash, { length: 11 }) @@ -709,7 +741,8 @@ Object.keys(testAPIs).forEach((IPFS) => { 'entryC0', 'entryA7', 'entryA8', 'entryA9', 'entryA10' ] - assert.deepStrictEqual(res.values.map(e => e.payload), first11) + const values2 = await res.values() + assert.deepStrictEqual(values2.map(e => e.payload), first11) // All but one res = await Log.fromMultihash(ipfs, testIdentity2, hash, { length: 16 - 1 }) @@ -721,7 +754,8 @@ Object.keys(testAPIs).forEach((IPFS) => { 'entryC0', 'entryA7', 'entryA8', 'entryA9', 'entryA10' ] - assert.deepStrictEqual(res.values.map(e => e.payload), all) + const values3 = await res.values() + assert.deepStrictEqual(values3.map(e => e.payload), all) }) it('retrieves partially joined log deterministically - multiple next pointers', async () => { @@ -761,7 +795,8 @@ Object.keys(testAPIs).forEach((IPFS) => { 'entryC0', 'entryA7', 'entryA8', 'entryA9', 'entryA10' ] - assert.deepStrictEqual(res.values.map(e => e.payload), first5) + const values = await res.values() + assert.deepStrictEqual(values.map(e => e.payload), first5) // First 11 res = await Log.fromMultihash(ipfs, testIdentity2, hash, { length: 11 }) @@ -773,7 +808,8 @@ Object.keys(testAPIs).forEach((IPFS) => { 'entryA7', 'entryA8', 'entryA9', 'entryA10' ] - assert.deepStrictEqual(res.values.map(e => e.payload), first11) + const values1 = await res.values() + assert.deepStrictEqual(values1.map(e => e.payload), first11) // All but one res = await Log.fromMultihash(ipfs, testIdentity2, hash, { length: 16 - 1 }) @@ -785,7 +821,8 @@ Object.keys(testAPIs).forEach((IPFS) => { 'entryC0', 'entryA7', 'entryA8', 'entryA9', 'entryA10' ] - assert.deepStrictEqual(res.values.map(e => e.payload), all) + const values2 = await res.values() + assert.deepStrictEqual(values2.map(e => e.payload), all) }) it('throws an error if ipfs is not defined', async () => { @@ -840,21 +877,24 @@ Object.keys(testAPIs).forEach((IPFS) => { const a = await Log.fromEntry(ipfs, testIdentity, last(items1), { length: -1 }) assert.strictEqual(a.length, amount) - assert.strictEqual(a.values[0].hash, items1[0].hash) + const values = await a.values() + assert.strictEqual(values[0].hash, items1[0].hash) }) it('returns all entries - including excluded entries', async () => { // One entry const a = await Log.fromEntry(ipfs, testIdentity, last(items1), { length: -1, exclude: [items1[0]] }) + const aValues = await a.values() assert.strictEqual(a.length, amount) - assert.strictEqual(a.values[0].hash, items1[0].hash) + assert.strictEqual(aValues[0].hash, items1[0].hash) // All entries const b = await Log.fromEntry(ipfs, testIdentity, last(items1), { length: -1, exclude: items1 }) + const bValues = await b.values() assert.strictEqual(b.length, amount) - assert.strictEqual(b.values[0].hash, items1[0].hash) + assert.strictEqual(bValues[0].hash, items1[0].hash) }) it('respects timeout parameter', async () => { @@ -866,7 +906,8 @@ Object.keys(testAPIs).forEach((IPFS) => { const et = new Date().getTime() assert.strictEqual((et - st) >= (timeout - 10), true, '' + (et - st) + ' should be greater than timeout ' + timeout) assert.strictEqual(log.length, 1) - assert.deepStrictEqual(log.values.map(e => e.payload), [e.payload]) + const values = await log.values() + assert.deepStrictEqual(values.map(e => e.payload), [e.payload]) }) }) }) @@ -915,19 +956,22 @@ Object.keys(testAPIs).forEach((IPFS) => { json.heads = await Promise.all(json.heads.map(headHash => Entry.fromMultihash(ipfs, headHash))) const log = await Log.fromJSON(ipfs, testIdentity, json, { logId: 'A' }) assert.strictEqual(log.length, 5) - assert.deepStrictEqual(log.values, v1Entries.map(e => Entry.toEntry(e, { includeHash: true }))) + const values = await log.values() + assert.deepStrictEqual(values, v1Entries.map(e => Entry.toEntry(e, { includeHash: true }))) }) it('creates a log from v1 entry', async () => { const log = await Log.fromEntry(ipfs, testIdentity, v1Entries[v1Entries.length - 1], { logId: 'A' }) assert.strictEqual(log.length, 5) - assert.deepStrictEqual(log.values, v1Entries.map(e => Entry.toEntry(e, { includeHash: true }))) + const values = await log.values() + assert.deepStrictEqual(values, v1Entries.map(e => Entry.toEntry(e, { includeHash: true }))) }) it('creates a log from v1 entry hash', async () => { const log = await Log.fromEntryHash(ipfs, testIdentity, v1Entries[v1Entries.length - 1].hash, { logId: 'A' }) assert.strictEqual(log.length, 5) - assert.deepStrictEqual(log.values, v1Entries.map(e => Entry.toEntry(e, { includeHash: true }))) + const values = await log.values() + assert.deepStrictEqual(values, v1Entries.map(e => Entry.toEntry(e, { includeHash: true }))) }) it('creates a log from log hash of v1 entries', async () => { @@ -935,7 +979,8 @@ Object.keys(testAPIs).forEach((IPFS) => { const hash = await log1.toMultihash() const log = await Log.fromMultihash(ipfs, testIdentity, hash, { logId: 'A' }) assert.strictEqual(log.length, 5) - assert.deepStrictEqual(log.values, v1Entries.map(e => Entry.toEntry(e, { includeHash: true }))) + const values = await log.values() + assert.deepStrictEqual(values, v1Entries.map(e => Entry.toEntry(e, { includeHash: true }))) }) }) }) diff --git a/test/log-references.spec.js b/test/log-references.spec.js index 3091677b..69f7fc2e 100644 --- a/test/log-references.spec.js +++ b/test/log-references.spec.js @@ -72,14 +72,19 @@ Object.keys(testAPIs).forEach((IPFS) => { await log4.append(i.toString(), Math.pow(maxReferenceDistance, 4)) } - assert.strict.equal(log1.values[log1.length - 1].next.length, 1) - assert.strict.equal(log2.values[log2.length - 1].next.length, 1) - assert.strict.equal(log3.values[log3.length - 1].next.length, 1) - assert.strict.equal(log4.values[log4.length - 1].next.length, 1) - assert.strict.equal(log1.values[log1.length - 1].refs.length, 1) - assert.strict.equal(log2.values[log2.length - 1].refs.length, 2) - assert.strict.equal(log3.values[log3.length - 1].refs.length, 3) - assert.strict.equal(log4.values[log4.length - 1].refs.length, 4) + const values1 = await log1.values() + const values2 = await log2.values() + const values3 = await log3.values() + const values4 = await log4.values() + + assert.strict.equal(values1[log1.length - 1].next.length, 1) + assert.strict.equal(values2[log2.length - 1].next.length, 1) + assert.strict.equal(values3[log3.length - 1].next.length, 1) + assert.strict.equal(values4[log4.length - 1].next.length, 1) + assert.strict.equal(values1[log1.length - 1].refs.length, 1) + assert.strict.equal(values2[log2.length - 1].refs.length, 2) + assert.strict.equal(values3[log3.length - 1].refs.length, 3) + assert.strict.equal(values4[log4.length - 1].refs.length, 4) }) const inputs = [ @@ -114,31 +119,32 @@ Object.keys(testAPIs).forEach((IPFS) => { await log1.append((i + 1).toString(), referenceCount) } - assert.strict.equal(log1.values.length, input.amount) - assert.strict.equal(log1.values[log1.length - 1].clock.time, input.amount) + const values1 = await log1.values() + assert.strict.equal(values1.length, input.amount) + assert.strict.equal(values1[log1.length - 1].clock.time, input.amount) for (let k = 0; k < input.amount; k++) { const idx = log1.length - k - 1 - assert.strict.equal(log1.values[idx].clock.time, idx + 1) + assert.strict.equal(values1[idx].clock.time, idx + 1) // Check the first ref (distance 2) - if (log1.values[idx].refs.length > 0) { assert.strict.equal(log1.values[idx].refs[0], log1.values[idx - 2].hash) } + if (values1[idx].refs.length > 0) { assert.strict.equal(values1[idx].refs[0], values1[idx - 2].hash) } // Check the second ref (distance 2) - if (log1.values[idx].refs.length > 1 && idx > referenceCount) { assert.strict.equal(log1.values[idx].refs[1], log1.values[idx - 4].hash) } + if (values1[idx].refs.length > 1 && idx > referenceCount) { assert.strict.equal(values1[idx].refs[1], values1[idx - 4].hash) } // Check the third ref (distance 4) - if (log1.values[idx].refs.length > 2 && idx > referenceCount) { assert.strict.equal(log1.values[idx].refs[2], log1.values[idx - 8].hash) } + if (values1[idx].refs.length > 2 && idx > referenceCount) { assert.strict.equal(values1[idx].refs[2], values1[idx - 8].hash) } // Check the fourth ref (distance 8) - if (log1.values[idx].refs.length > 3 && idx > referenceCount) { assert.strict.equal(log1.values[idx].refs[3], log1.values[idx - 16].hash) } + if (values1[idx].refs.length > 3 && idx > referenceCount) { assert.strict.equal(values1[idx].refs[3], values1[idx - 16].hash) } // Check the fifth ref (distance 16) - if (log1.values[idx].refs.length > 4 && idx > referenceCount) { assert.strict.equal(log1.values[idx].refs[4], log1.values[idx - 32].hash) } + if (values1[idx].refs.length > 4 && idx > referenceCount) { assert.strict.equal(values1[idx].refs[4], values1[idx - 32].hash) } // Check the reference of each entry - if (idx > referenceCount) { assert.strict.equal(log1.values[idx].refs.length, refLength) } + if (idx > referenceCount) { assert.strict.equal(values1[idx].refs.length, refLength) } } } diff --git a/test/log.spec.js b/test/log.spec.js index cd3d8167..3b75f5f7 100644 --- a/test/log.spec.js +++ b/test/log.spec.js @@ -62,16 +62,11 @@ Object.keys(testAPIs).forEach((IPFS) => { const log = new Log(ipfs, testIdentity) assert.notStrictEqual(log._entryIndex, null) assert.notStrictEqual(log._headsIndex, null) + assert.notStrictEqual(log._hashIndex, new Map()) assert.notStrictEqual(log._id, null) assert.notStrictEqual(log.id, null) assert.notStrictEqual(log.clock, null) - assert.notStrictEqual(log.values, null) assert.notStrictEqual(log.heads, null) - assert.notStrictEqual(log.tails, null) - assert.notStrictEqual(log.tailCids, null) - assert.deepStrictEqual(log.values, []) - assert.deepStrictEqual(log.heads, []) - assert.deepStrictEqual(log.tails, []) }) it('throws an error if IPFS instance is not passed as an argument', () => { @@ -106,10 +101,11 @@ Object.keys(testAPIs).forEach((IPFS) => { const three = await Entry.create(ipfs, testIdentity, 'A', 'entryC', [], new Clock('C', 0)) const log = new Log(ipfs, testIdentity, { logId: 'A', entries: [one, two, three] }) + const values = await log.values() assert.strictEqual(log.length, 3) - assert.strictEqual(log.values[0].payload, 'entryA') - assert.strictEqual(log.values[1].payload, 'entryB') - assert.strictEqual(log.values[2].payload, 'entryC') + assert.strictEqual(values[0].payload, 'entryA') + assert.strictEqual(values[1].payload, 'entryB') + assert.strictEqual(values[2].payload, 'entryC') }) it('sets heads if given as params', async () => { @@ -188,8 +184,9 @@ Object.keys(testAPIs).forEach((IPFS) => { await log.append('five') }) - it('returns a nicely formatted string', () => { - assert.strictEqual(log.toString(), expectedData) + it('returns a nicely formatted string', async () => { + const logString = await log.toString() + assert.strictEqual(logString, expectedData) }) }) @@ -201,13 +198,14 @@ Object.keys(testAPIs).forEach((IPFS) => { await log.append('one') }) - it('returns an Entry', () => { - const entry = log.get(log.values[0].hash) + it('returns an Entry', async () => { + const values = await log.values() + const entry = await log.get(values[0].hash) assert.deepStrictEqual(entry.hash, 'zdpuAoFzNYcuuQHk1gLcB8fomHGrqT9k1uQeAvewZJ1cSYrms') }) - it('returns undefined when Entry is not in the log', () => { - const entry = log.get('QmFoo') + it('returns undefined when Entry is not in the log', async () => { + const entry = await log.get('QmFoo') assert.deepStrictEqual(entry, undefined) }) }) @@ -221,16 +219,19 @@ Object.keys(testAPIs).forEach((IPFS) => { }) it('changes identity', async () => { - assert.strictEqual(log.values[0].clock.id, testIdentity.publicKey) - assert.strictEqual(log.values[0].clock.time, 1) + let values = await log.values() + assert.strictEqual(values[0].clock.id, testIdentity.publicKey) + assert.strictEqual(values[0].clock.time, 1) log.setIdentity(testIdentity2) await log.append('two') - assert.strictEqual(log.values[1].clock.id, testIdentity2.publicKey) - assert.strictEqual(log.values[1].clock.time, 2) + values = await log.values() + assert.strictEqual(values[1].clock.id, testIdentity2.publicKey) + assert.strictEqual(values[1].clock.time, 2) log.setIdentity(testIdentity3) await log.append('three') - assert.strictEqual(log.values[2].clock.id, testIdentity3.publicKey) - assert.strictEqual(log.values[2].clock.time, 3) + values = await log.values() + assert.strictEqual(values[2].clock.id, testIdentity3.publicKey) + assert.strictEqual(values[2].clock.time, 3) }) }) @@ -301,8 +302,8 @@ Object.keys(testAPIs).forEach((IPFS) => { ] } - it('returns the log snapshot', () => { - const snapshot = log.toSnapshot() + it('returns the log snapshot', async () => { + const snapshot = await log.toSnapshot() assert.strictEqual(snapshot.id, expectedData.id) assert.strictEqual(snapshot.heads.length, expectedData.heads.length) assert.strictEqual(snapshot.heads[0].hash, expectedData.heads[0]) @@ -403,23 +404,25 @@ Object.keys(testAPIs).forEach((IPFS) => { await log.append('one') const hash = await log.toMultihash() const res = await Log.fromMultihash(ipfs, testIdentity, hash, -1) + const values = await res.values() assert.strictEqual(JSON.stringify(res.toJSON()), JSON.stringify(expectedData)) assert.strictEqual(res.length, 1) - assert.strictEqual(res.values[0].payload, 'one') - assert.strictEqual(res.values[0].clock.id, testIdentity.publicKey) - assert.strictEqual(res.values[0].clock.time, 1) + assert.strictEqual(values[0].payload, 'one') + assert.strictEqual(values[0].clock.id, testIdentity.publicKey) + assert.strictEqual(values[0].clock.time, 1) }) it('creates a log from ipfs CID - three entries', async () => { const hash = await log.toMultihash() const res = await Log.fromMultihash(ipfs, testIdentity, hash, -1) + const values = await res.values() assert.strictEqual(res.length, 3) - assert.strictEqual(res.values[0].payload, 'one') - assert.strictEqual(res.values[0].clock.time, 1) - assert.strictEqual(res.values[1].payload, 'two') - assert.strictEqual(res.values[1].clock.time, 2) - assert.strictEqual(res.values[2].payload, 'three') - assert.strictEqual(res.values[2].clock.time, 3) + assert.strictEqual(values[0].payload, 'one') + assert.strictEqual(values[0].clock.time, 1) + assert.strictEqual(values[1].payload, 'two') + assert.strictEqual(values[1].clock.time, 2) + assert.strictEqual(values[2].payload, 'three') + assert.strictEqual(values[2].clock.time, 3) }) it('creates a log from ipfs multihash (backwards compat)', async () => { @@ -431,21 +434,24 @@ Object.keys(testAPIs).forEach((IPFS) => { await log.append('one') const multihash = await log.toMultihash() const res = await Log.fromMultihash(ipfs, testIdentity, multihash, { length: -1 }) + const values = await res.values() assert.strictEqual(JSON.stringify(res.toJSON()), JSON.stringify(expectedData)) assert.strictEqual(res.length, 1) - assert.strictEqual(res.values[0].payload, 'one') - assert.strictEqual(res.values[0].clock.id, testIdentity.publicKey) - assert.strictEqual(res.values[0].clock.time, 1) + assert.strictEqual(values[0].payload, 'one') + assert.strictEqual(values[0].clock.id, testIdentity.publicKey) + assert.strictEqual(values[0].clock.time, 1) }) it('has the right sequence number after creation and appending', async () => { const hash = await log.toMultihash() const res = await Log.fromMultihash(ipfs, testIdentity, hash, { length: -1 }) + let values = await res.values() assert.strictEqual(res.length, 3) await res.append('four') + values = await res.values() assert.strictEqual(res.length, 4) - assert.strictEqual(res.values[3].payload, 'four') - assert.strictEqual(res.values[3].clock.time, 4) + assert.strictEqual(values[3].payload, 'four') + assert.strictEqual(values[3].clock.time, 4) }) it('creates a log from ipfs CID that has three heads', async () => { @@ -561,7 +567,7 @@ Object.keys(testAPIs).forEach((IPFS) => { await log.append(i.toString()) } - const items = log.values + const items = await log.values() let i = 0 const loadProgressCallback = (entry) => { assert.notStrictEqual(entry, null) @@ -574,13 +580,14 @@ Object.keys(testAPIs).forEach((IPFS) => { const result = await Log.fromMultihash(ipfs, testIdentity, hash, { length: -1, exclude: [], onProgressCallback: loadProgressCallback }) + const values = await result.values() // Make sure the onProgress callback was called for each entry assert.strictEqual(i, amount) // Make sure the log entries are correct ones - assert.strictEqual(result.values[0].clock.time, 1) - assert.strictEqual(result.values[0].payload, '0') - assert.strictEqual(result.values[result.length - 1].clock.time, 100) - assert.strictEqual(result.values[result.length - 1].payload, '99') + assert.strictEqual(values[0].clock.time, 1) + assert.strictEqual(values[0].payload, '0') + assert.strictEqual(values[result.length - 1].clock.time, 100) + assert.strictEqual(values[result.length - 1].payload, '99') }) }) @@ -641,16 +648,18 @@ Object.keys(testAPIs).forEach((IPFS) => { describe('values', () => { it('returns all entries in the log', async () => { const log = new Log(ipfs, testIdentity) - assert.strictEqual(log.values instanceof Array, true) + let values = await log.values() + assert.strictEqual(values instanceof Array, true) assert.strictEqual(log.length, 0) await log.append('hello1') await log.append('hello2') await log.append('hello3') - assert.strictEqual(log.values instanceof Array, true) + values = await log.values() + assert.strictEqual(values instanceof Array, true) assert.strictEqual(log.length, 3) - assert.strictEqual(log.values[0].payload, 'hello1') - assert.strictEqual(log.values[1].payload, 'hello2') - assert.strictEqual(log.values[2].payload, 'hello3') + assert.strictEqual(values[0].payload, 'hello1') + assert.strictEqual(values[1].payload, 'hello2') + assert.strictEqual(values[2].payload, 'hello3') }) }) }) diff --git a/test/replicate.spec.js b/test/replicate.spec.js index d70b8318..7aeaeecf 100644 --- a/test/replicate.spec.js +++ b/test/replicate.spec.js @@ -152,19 +152,21 @@ Object.keys(testAPIs).forEach((IPFS) => { await result.join(log1) await result.join(log2) + const values = await result.values() + assert.strictEqual(buffer1.length, amount) assert.strictEqual(buffer2.length, amount) assert.strictEqual(result.length, amount * 2) assert.strictEqual(log1.length, amount) assert.strictEqual(log2.length, amount) - assert.strictEqual(result.values[0].payload, 'A1') - assert.strictEqual(result.values[1].payload, 'B1') - assert.strictEqual(result.values[2].payload, 'A2') - assert.strictEqual(result.values[3].payload, 'B2') - assert.strictEqual(result.values[99].payload, 'B50') - assert.strictEqual(result.values[100].payload, 'A51') - assert.strictEqual(result.values[198].payload, 'A100') - assert.strictEqual(result.values[199].payload, 'B100') + assert.strictEqual(values[0].payload, 'A1') + assert.strictEqual(values[1].payload, 'B1') + assert.strictEqual(values[2].payload, 'A2') + assert.strictEqual(values[3].payload, 'B2') + assert.strictEqual(values[99].payload, 'B50') + assert.strictEqual(values[100].payload, 'A51') + assert.strictEqual(values[198].payload, 'A100') + assert.strictEqual(values[199].payload, 'B100') }) }) }) diff --git a/test/signed-log.spec.js b/test/signed-log.spec.js index 8f43b967..e7b2e16e 100644 --- a/test/signed-log.spec.js +++ b/test/signed-log.spec.js @@ -82,8 +82,9 @@ Object.keys(testAPIs).forEach((IPFS) => { it('entries contain an identity', async () => { const log = new Log(ipfs, testIdentity, { logId: 'A' }) await log.append('one') - assert.notStrictEqual(log.values[0].sig, null) - assert.deepStrictEqual(log.values[0].identity, testIdentity.toJSON()) + const values = await log.values() + assert.notStrictEqual(values[0].sig, null) + assert.deepStrictEqual(values[0].identity, testIdentity.toJSON()) }) it('doesn\'t sign entries when identity is not defined', async () => { @@ -111,10 +112,11 @@ Object.keys(testAPIs).forEach((IPFS) => { throw e } + const values1 = await log1.values() assert.strictEqual(err, undefined) assert.strictEqual(log1.id, 'A') - assert.strictEqual(log1.values.length, 1) - assert.strictEqual(log1.values[0].payload, 'one') + assert.strictEqual(values1.length, 1) + assert.strictEqual(values1[0].payload, 'one') }) it('throws an error if log is signed but trying to merge with an entry that doesn\'t have public signing key', async () => { @@ -125,7 +127,8 @@ Object.keys(testAPIs).forEach((IPFS) => { try { await log1.append('one') await log2.append('two') - delete log2.values[0].key + const values2 = await log2.values() + delete values2[0].key await log1.join(log2) } catch (e) { err = e.toString() @@ -141,7 +144,8 @@ Object.keys(testAPIs).forEach((IPFS) => { try { await log1.append('one') await log2.append('two') - delete log2.values[0].sig + const values2 = await log2.values() + delete values2[0].sig await log1.join(log2) } catch (e) { err = e.toString() @@ -157,16 +161,21 @@ Object.keys(testAPIs).forEach((IPFS) => { try { await log1.append('one') await log2.append('two') - log2.values[0].sig = log1.values[0].sig + const values1 = await log1.values() + const values2 = await log2.values() + values2[0].sig = values1[0].sig await log1.join(log2) } catch (e) { err = e.toString() } - const entry = log2.values[0] + const values1 = await log1.values() + const values2 = await log2.values() + + const entry = values2[0] assert.strictEqual(err, `Error: Could not validate signature "${entry.sig}" for entry "${entry.hash}" and key "${entry.key}"`) - assert.strictEqual(log1.values.length, 1) - assert.strictEqual(log1.values[0].payload, 'one') + assert.strictEqual(values1.length, 1) + assert.strictEqual(values1[0].payload, 'one') }) it('throws an error if entry doesn\'t have append access', async () => {