'use strict' /* eslint-env mocha */ const { expect } = require('aegir/utils/chai') const { fromString: uint8ArrayFromString } = require('uint8arrays/from-string') const { MemoryDatastore } = require('datastore-core/memory') const pDefer = require('p-defer') const PeerStore = require('../../src/peer-store') const peerUtils = require('../utils/creators/peer') const { codes: { ERR_INVALID_PARAMETERS } } = require('../../src/errors') /** * @typedef {import('../../src/peer-store/types').PeerStore} PeerStore * @typedef {import('../../src/peer-store/types').MetadataBook} MetadataBook * @typedef {import('peer-id')} PeerId */ describe('metadataBook', () => { /** @type {PeerId} */ let peerId before(async () => { [peerId] = await peerUtils.createPeerId() }) describe('metadataBook.set', () => { /** @type {PeerStore} */ let peerStore /** @type {MetadataBook} */ let mb beforeEach(() => { peerStore = new PeerStore({ peerId, datastore: new MemoryDatastore() }) mb = peerStore.metadataBook }) afterEach(() => { peerStore.removeAllListeners() }) it('throws invalid parameters error if invalid PeerId is provided', async () => { try { await mb.set('invalid peerId') } catch (/** @type {any} */ err) { expect(err.code).to.equal(ERR_INVALID_PARAMETERS) return } throw new Error('invalid peerId should throw error') }) it('throws invalid parameters error if no metadata provided', async () => { try { await mb.set(peerId) } catch (/** @type {any} */ err) { expect(err.code).to.equal(ERR_INVALID_PARAMETERS) return } throw new Error('no key provided should throw error') }) it('throws invalid parameters error if no value provided', async () => { try { await mb.setValue(peerId, 'location') } catch (/** @type {any} */ err) { expect(err.code).to.equal(ERR_INVALID_PARAMETERS) return } throw new Error('no value provided should throw error') }) it('throws invalid parameters error if value is not a buffer', async () => { try { await mb.setValue(peerId, 'location', 'mars') } catch (/** @type {any} */ err) { expect(err.code).to.equal(ERR_INVALID_PARAMETERS) return } throw new Error('invalid value provided should throw error') }) it('stores the content and emit change event', async () => { const defer = pDefer() const metadataKey = 'location' const metadataValue = uint8ArrayFromString('mars') peerStore.once('change:metadata', ({ peerId, metadata }) => { expect(peerId).to.exist() expect(metadata.get(metadataKey)).to.equalBytes(metadataValue) defer.resolve() }) await mb.setValue(peerId, metadataKey, metadataValue) const value = await mb.getValue(peerId, metadataKey) expect(value).to.equalBytes(metadataValue) const peerMetadata = await mb.get(peerId) expect(peerMetadata).to.exist() expect(peerMetadata.get(metadataKey)).to.equalBytes(metadataValue) return defer.promise }) it('emits on set if not storing the exact same content', async () => { const defer = pDefer() const metadataKey = 'location' const metadataValue1 = uint8ArrayFromString('mars') const metadataValue2 = uint8ArrayFromString('saturn') let changeCounter = 0 peerStore.on('change:metadata', () => { changeCounter++ if (changeCounter > 1) { defer.resolve() } }) // set 1 await mb.setValue(peerId, metadataKey, metadataValue1) // set 2 (same content) await mb.setValue(peerId, metadataKey, metadataValue2) const value = await mb.getValue(peerId, metadataKey) expect(value).to.equalBytes(metadataValue2) const peerMetadata = await mb.get(peerId) expect(peerMetadata).to.exist() expect(peerMetadata.get(metadataKey)).to.equalBytes(metadataValue2) return defer.promise }) it('does not emit on set if it is storing the exact same content', async () => { const defer = pDefer() const metadataKey = 'location' const metadataValue = uint8ArrayFromString('mars') let changeCounter = 0 peerStore.on('change:metadata', () => { changeCounter++ if (changeCounter > 1) { defer.reject() } }) // set 1 await mb.setValue(peerId, metadataKey, metadataValue) // set 2 (same content) await mb.setValue(peerId, metadataKey, metadataValue) // Wait 50ms for incorrect second event setTimeout(() => { defer.resolve() }, 50) return defer.promise }) }) describe('metadataBook.get', () => { /** @type {PeerStore} */ let peerStore /** @type {MetadataBook} */ let mb beforeEach(() => { peerStore = new PeerStore({ peerId, datastore: new MemoryDatastore() }) mb = peerStore.metadataBook }) it('throws invalid parameters error if invalid PeerId is provided', async () => { try { await mb.get('invalid peerId') } catch (/** @type {any} */ err) { expect(err.code).to.equal(ERR_INVALID_PARAMETERS) return } throw new Error('invalid peerId should throw error') }) it('returns empty if no metadata is known for the provided peer', async () => { const metadata = await mb.get(peerId) expect(metadata).to.be.empty() }) it('returns the metadata stored', async () => { const metadataKey = 'location' const metadataValue = uint8ArrayFromString('mars') const metadata = new Map() metadata.set(metadataKey, metadataValue) await mb.set(peerId, metadata) const peerMetadata = await mb.get(peerId) expect(peerMetadata).to.exist() expect(peerMetadata.get(metadataKey)).to.equalBytes(metadataValue) }) }) describe('metadataBook.getValue', () => { /** @type {PeerStore} */ let peerStore /** @type {MetadataBook} */ let mb beforeEach(() => { peerStore = new PeerStore({ peerId, datastore: new MemoryDatastore() }) mb = peerStore.metadataBook }) it('throws invalid parameters error if invalid PeerId is provided', async () => { try { await mb.getValue('invalid peerId') } catch (/** @type {any} */ err) { expect(err.code).to.equal(ERR_INVALID_PARAMETERS) return } throw new Error('invalid peerId should throw error') }) it('returns undefined if no metadata is known for the provided peer', async () => { const metadataKey = 'location' const metadata = await mb.getValue(peerId, metadataKey) expect(metadata).to.not.exist() }) it('returns the metadata value stored for the given key', async () => { const metadataKey = 'location' const metadataValue = uint8ArrayFromString('mars') await mb.setValue(peerId, metadataKey, metadataValue) const value = await mb.getValue(peerId, metadataKey) expect(value).to.exist() expect(value).to.equalBytes(metadataValue) }) it('returns undefined if no metadata is known for the provided peer and key', async () => { const metadataKey = 'location' const metadataBadKey = 'nickname' const metadataValue = uint8ArrayFromString('mars') await mb.setValue(peerId, metadataKey, metadataValue) const metadata = await mb.getValue(peerId, metadataBadKey) expect(metadata).to.not.exist() }) }) describe('metadataBook.delete', () => { /** @type {PeerStore} */ let peerStore /** @type {MetadataBook} */ let mb beforeEach(() => { peerStore = new PeerStore({ peerId, datastore: new MemoryDatastore() }) mb = peerStore.metadataBook }) it('throws invalid parameters error if invalid PeerId is provided', async () => { try { await mb.delete('invalid peerId') } catch (/** @type {any} */ err) { expect(err.code).to.equal(ERR_INVALID_PARAMETERS) return } throw new Error('invalid peerId should throw error') }) it('should not emit event if no records exist for the peer', async () => { const defer = pDefer() peerStore.on('change:metadata', () => { defer.reject() }) await mb.delete(peerId) // Wait 50ms for incorrect invalid event setTimeout(() => { defer.resolve() }, 50) return defer.promise }) it('should emit an event if the record exists for the peer', async () => { const defer = pDefer() const metadataKey = 'location' const metadataValue = uint8ArrayFromString('mars') await mb.setValue(peerId, metadataKey, metadataValue) // Listen after set peerStore.on('change:metadata', () => { defer.resolve() }) await mb.delete(peerId) return defer.promise }) }) describe('metadataBook.deleteValue', () => { /** @type {PeerStore} */ let peerStore /** @type {MetadataBook} */ let mb beforeEach(() => { peerStore = new PeerStore({ peerId, datastore: new MemoryDatastore() }) mb = peerStore.metadataBook }) it('throws invalid parameters error if invalid PeerId is provided', async () => { try { await mb.deleteValue('invalid peerId') } catch (/** @type {any} */ err) { expect(err.code).to.equal(ERR_INVALID_PARAMETERS) return } throw new Error('invalid peerId should throw error') }) it('should not emit event if no records exist for the peer', async () => { const defer = pDefer() const metadataKey = 'location' peerStore.on('change:metadata', () => { defer.reject() }) await mb.deleteValue(peerId, metadataKey) // Wait 50ms for incorrect invalid event setTimeout(() => { defer.resolve() }, 50) return defer.promise }) it('should emit event if a record exists for the peer', async () => { const defer = pDefer() const metadataKey = 'location' const metadataValue = uint8ArrayFromString('mars') await mb.setValue(peerId, metadataKey, metadataValue) // Listen after set peerStore.on('change:metadata', () => { defer.resolve() }) await mb.deleteValue(peerId, metadataKey) return defer.promise }) }) })