From 127745ea90bbeecc80c0bd1f58d24660d832ba14 Mon Sep 17 00:00:00 2001 From: achingbrain Date: Fri, 11 Dec 2020 14:03:20 +0000 Subject: [PATCH] fix: allow CIDs to be compared through deep equality The changes in #131 broke the tests of IPFS and probably quite a few other modules. This sort of thing used to work, now does not: ```js expect(ipfs.bitswap.unwant.calledWith(new CID(cidStr), defaultOptions)).to.be.true() ``` The reason it breaks is because internally `calledWith` does a `deepEqual` on the args which compares (among other things) the properties of the passed objects. We used to use `Object.defineProperty` to create cached versions of expensive to calculate fields which makes fields non-enumerable by default so they are skipped during the `deepEqual` check. Now we just set the fields on the object which means instances have different fields depending on which constructor branch was hit or worse, if the instances properties have been accessed. --- package.json | 1 + src/index.js | 9 ++++++--- test/index.spec.js | 44 ++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 51 insertions(+), 3 deletions(-) diff --git a/package.json b/package.json index 877a7f1..1076465 100644 --- a/package.json +++ b/package.json @@ -45,6 +45,7 @@ "uint8arrays": "^1.1.0" }, "devDependencies": { + "@sinonjs/samsam": "^5.3.0", "aegir": "^29.0.1", "multihashing-async": "^2.0.1" }, diff --git a/src/index.js b/src/index.js index eed3c06..2132540 100644 --- a/src/index.js +++ b/src/index.js @@ -95,7 +95,7 @@ class CID { this.multibaseName = 'base58btc' } CID.validateCID(this) - this.string = version + Object.defineProperty(this, 'string', { value: version }) return } @@ -164,6 +164,7 @@ class CID { * */ get bytes () { + // @ts-ignore let bytes = this._bytes if (!bytes) { @@ -179,7 +180,7 @@ class CID { } // Cache this Uint8Array so it doesn't have to be recreated - this._bytes = bytes + Object.defineProperty(this, '_bytes', { value: bytes }) } return bytes @@ -248,7 +249,9 @@ class CID { * @returns {string} */ toBaseEncodedString (base = this.multibaseName) { + // @ts-ignore non enumerable cache property if (this.string && this.string.length !== 0 && base === this.multibaseName) { + // @ts-ignore non enumerable cache property return this.string } let str @@ -264,7 +267,7 @@ class CID { } if (base === this.multibaseName) { // cache the string value - this.string = str + Object.defineProperty(this, 'string', { value: str }) } return str } diff --git a/test/index.spec.js b/test/index.spec.js index 912008e..5fcf62e 100644 --- a/test/index.spec.js +++ b/test/index.spec.js @@ -8,6 +8,7 @@ const multihashing = require('multihashing-async') const uint8ArrayFromString = require('uint8arrays/from-string') const uint8ArrayToString = require('uint8arrays/to-string') const CID = require('../src') +const { deepEqual } = require('@sinonjs/samsam') describe('CID', () => { let hash @@ -448,4 +449,47 @@ describe('CID', () => { expect(cid.toBaseEncodedString()).to.equal(base32String) }) }) + + describe('equality', () => { + it('should be deeply equal', () => { + const cid1 = new CID('QmdfTbBqBPQ7VNxZEYEj14VmRuZBkqFbiwReogJgS1zR1n') + const cid2 = new CID('QmdfTbBqBPQ7VNxZEYEj14VmRuZBkqFbiwReogJgS1zR1n') + + expect(deepEqual(cid1, cid2)).to.be.true() + }) + + it('should be deeply equal when constructed from another CID', () => { + const cid1 = new CID('QmdfTbBqBPQ7VNxZEYEj14VmRuZBkqFbiwReogJgS1zR1n') + const cid2 = new CID(cid1) + + expect(deepEqual(cid1, cid2)).to.be.true() + }) + + it('should be deeply equal when constructed from an Uint8Array', () => { + const cid1 = new CID('QmdfTbBqBPQ7VNxZEYEj14VmRuZBkqFbiwReogJgS1zR1n') + const cid2 = new CID(cid1.multihash) + + expect(deepEqual(cid1, cid2)).to.be.true() + }) + + it('should still be deeply equal after turning one into a base encoded string', () => { + const cid1 = new CID('QmdfTbBqBPQ7VNxZEYEj14VmRuZBkqFbiwReogJgS1zR1n') + const cid2 = new CID(cid1.multihash) + const cid3 = new CID(cid1.multihash) + + cid3.toBaseEncodedString() + + expect(deepEqual(cid2, cid3)).to.be.true() + }) + + it('should still be deeply equal after turning one into bytes', () => { + const cid1 = new CID('QmdfTbBqBPQ7VNxZEYEj14VmRuZBkqFbiwReogJgS1zR1n') + const cid2 = new CID(cid1.multihash) + const cid3 = new CID(cid1.multihash) + + cid3.bytes // eslint-disable-line no-unused-expressions + + expect(deepEqual(cid2, cid3)).to.be.true() + }) + }) })