From 7e228b729eabac3c1efc81f21bbe946f49672f6c Mon Sep 17 00:00:00 2001 From: Chris Troutner Date: Thu, 30 Sep 2021 14:47:17 -0700 Subject: [PATCH 1/4] fix(ipfs adapter): Adding unit tests for lock-file error handling --- src/adapters/ipfs/index.js | 3 ++- test/unit/adapters/ipfs-index.adapter.unit.js | 18 ++++++++++++++++++ 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/src/adapters/ipfs/index.js b/src/adapters/ipfs/index.js index 320fb87..df12983 100644 --- a/src/adapters/ipfs/index.js +++ b/src/adapters/ipfs/index.js @@ -10,6 +10,7 @@ class IPFS { // Encapsulate dependencies this.ipfsAdapter = new IpfsAdapter() this.IpfsCoordAdapter = IpfsCoordAdapter + this.process = process this.ipfsCoordAdapter = {} // placeholder @@ -42,7 +43,7 @@ class IPFS { // If error is due to a lock file issue. Kill the process, so that // Docker or pm2 has a chance to restart the service. if (err.message.includes('Lock already being held')) { - process.exit(1) + this.process.exit(1) } throw err diff --git a/test/unit/adapters/ipfs-index.adapter.unit.js b/test/unit/adapters/ipfs-index.adapter.unit.js index 1d46622..3c36911 100644 --- a/test/unit/adapters/ipfs-index.adapter.unit.js +++ b/test/unit/adapters/ipfs-index.adapter.unit.js @@ -45,5 +45,23 @@ describe('#IPFS-adapter-index', () => { assert.include(err.message, 'test error') } }) + + it('should handle lock-file errors', async () => { + try { + // Force an error + sandbox + .stub(uut.ipfsAdapter, 'start') + .rejects(new Error('Lock already being held')) + + // Prevent process from exiting + sandbox.stub(uut.process, 'exit').returns() + + await uut.start() + + assert.fail('Unexpected code path.') + } catch (err) { + assert.include(err.message, 'Lock already being held') + } + }) }) }) From 5af068e0d64ab25a0bd0f5c49dddb4507bc6382c Mon Sep 17 00:00:00 2001 From: Chris Troutner Date: Thu, 30 Sep 2021 15:12:47 -0700 Subject: [PATCH 2/4] fix(ipfs-coord): Got adapter back to 100% test coverage --- src/adapters/ipfs/ipfs-coord.js | 4 +- test/unit/adapters/ipfs-coord.adapter.unit.js | 39 +++++++++++++------ 2 files changed, 30 insertions(+), 13 deletions(-) diff --git a/src/adapters/ipfs/ipfs-coord.js b/src/adapters/ipfs/ipfs-coord.js index cbb546f..142319b 100644 --- a/src/adapters/ipfs/ipfs-coord.js +++ b/src/adapters/ipfs/ipfs-coord.js @@ -29,8 +29,8 @@ class IpfsCoordAdapter { this.IpfsCoord = IpfsCoord this.ipfsCoord = {} this.bchjs = new BCHJS() - // this.rpc = new JSONRPC() this.config = config + this.publicIp = publicIp // Properties of this class instance. this.isReady = false @@ -44,7 +44,7 @@ class IpfsCoordAdapter { // If configured as a Circuit Relay, get the public IP addresses for this node. if (this.config.isCircuitRelay) { try { - const ip4 = await publicIp.v4() + const ip4 = await this.publicIp.v4() // const ip6 = await publicIp.v6() circuitRelayInfo.ip4 = ip4 diff --git a/test/unit/adapters/ipfs-coord.adapter.unit.js b/test/unit/adapters/ipfs-coord.adapter.unit.js index c9b5003..40c8516 100644 --- a/test/unit/adapters/ipfs-coord.adapter.unit.js +++ b/test/unit/adapters/ipfs-coord.adapter.unit.js @@ -47,6 +47,20 @@ describe('#IPFS', () => { assert.equal(result, true) }) + + it('should get the public IP address if this node is a Circuit Relay', async () => { + // Mock dependencies. + uut.IpfsCoord = IPFSCoordMock + sandbox.stub(uut.publicIp, 'v4').returns('123') + + // Force Circuit Relay + uut.config.isCircuitRelay = true + + const result = await uut.start() + // console.log('result: ', result) + + assert.equal(result, true) + }) }) describe('#attachRPCRouter', () => { @@ -71,16 +85,19 @@ describe('#IPFS', () => { uut.attachRPCRouter(router) }) - // it('should throw an error if ipfs-coord has not been instantiated', () => { - // try { - // const router = console.log - // - // uut.attachRPCRouter(router) - // - // assert.fail('Unexpected code path') - // } catch (err) { - // assert.include(err.message, 'Cannot read property') - // } - // }) + it('should catch and throw an error', () => { + try { + // Force an error + delete uut.ipfsCoord.adapters + + const router = console.log + + uut.attachRPCRouter(router) + + assert.fail('Unexpected code path') + } catch (err) { + assert.include(err.message, 'Cannot read property') + } + }) }) }) From ee90bc07309e2dedf5fa08b70a868e3ffd56c082 Mon Sep 17 00:00:00 2001 From: Chris Troutner Date: Thu, 30 Sep 2021 15:20:51 -0700 Subject: [PATCH 3/4] fix(ipfs): Got adapter back to 100% unit test coverage --- src/adapters/ipfs/ipfs.js | 2 ++ test/unit/adapters/ipfs.adapter.unit.js | 13 +++++++++++++ 2 files changed, 15 insertions(+) diff --git a/src/adapters/ipfs/ipfs.js b/src/adapters/ipfs/ipfs.js index 7739b9a..8928bdf 100644 --- a/src/adapters/ipfs/ipfs.js +++ b/src/adapters/ipfs/ipfs.js @@ -77,6 +77,8 @@ class IpfsAdapter { async stop () { await this.ipfs.stop() + + return true } } diff --git a/test/unit/adapters/ipfs.adapter.unit.js b/test/unit/adapters/ipfs.adapter.unit.js index 05b10fc..b9e61bd 100644 --- a/test/unit/adapters/ipfs.adapter.unit.js +++ b/test/unit/adapters/ipfs.adapter.unit.js @@ -47,4 +47,17 @@ describe('#IPFS-adapter', () => { } }) }) + + describe('#stop', () => { + it('should stop the IPFS node', async () => { + // Mock dependencies + uut.ipfs = { + stop: () => {} + } + + const result = await uut.stop() + + assert.equal(result, true) + }) + }) }) From 5eb5ef204e9d78a80d16dbd17835b8e05edbb969 Mon Sep 17 00:00:00 2001 From: Chris Troutner Date: Fri, 1 Oct 2021 14:01:08 -0700 Subject: [PATCH 4/4] fix(json rpc): Increasing to 100% test coverage --- src/controllers/json-rpc/index.js | 16 ++- .../unit/controllers/json-rpc/a10-rpc.unit.js | 115 ++++++++++++++++++ test/unit/mocks/adapters/index.js | 8 +- 3 files changed, 132 insertions(+), 7 deletions(-) diff --git a/src/controllers/json-rpc/index.js b/src/controllers/json-rpc/index.js index ab54cea..ba90db0 100644 --- a/src/controllers/json-rpc/index.js +++ b/src/controllers/json-rpc/index.js @@ -56,7 +56,7 @@ class JSONRPC { wlogger.info( 'Warning: Can not send JSON RPC response. Can not determine which peer this message came from.' ) - return + return false } // Attempt to parse the incoming data as a JSON RPC string. @@ -66,22 +66,24 @@ class JSONRPC { // Exit quietly if the incoming string is an invalid JSON RPC string. if (parsedData.type === 'invalid') { wlogger.info('Rejecting invalid JSON RPC command.') - return + return false } // Check for duplicate entries with same 'id' value. const alreadyProcessed = _this._checkIfAlreadyProcessed(parsedData) if (alreadyProcessed) { - return + return false } else { + // console.log(`parsedData: ${JSON.stringify(parsedData, null, 2)}`) + // This node will regularly ping known circuit relays with an /about // JSON RPC call. These will be handled by ipfs-coord, but will percolate - // up to ipfs-coord. Ignore these messages. + // up to this library. Ignore these messages. if ( parsedData.type.includes('success') && parsedData.payload.method === undefined ) { - return + return false } // Log the incoming JSON RPC command. @@ -151,6 +153,8 @@ class JSONRPC { // If the ID is new, the function adds it to the cache and return false. _checkIfAlreadyProcessed (data) { try { + // console.log('data: ', data) + const id = data.payload.id // Check if the hash is in the array of already processed message. @@ -170,7 +174,7 @@ class JSONRPC { return alreadyProcessed } catch (err) { console.error('Error in _checkIfAlreadyProcessed: ', err) - return false + return true } } diff --git a/test/unit/controllers/json-rpc/a10-rpc.unit.js b/test/unit/controllers/json-rpc/a10-rpc.unit.js index ce63523..efe6bfb 100644 --- a/test/unit/controllers/json-rpc/a10-rpc.unit.js +++ b/test/unit/controllers/json-rpc/a10-rpc.unit.js @@ -179,5 +179,120 @@ describe('#JSON RPC', () => { assert.equal(obj.result.method, 'about') assert.equal(obj.id, id) }) + + it('should exit quietly for duplicate RPC message', async () => { + const id = uid() + const json = jsonrpc.request(id, 'unknownMethod', {}) + + const str = JSON.stringify(json) + + // Call router once. + await uut.router(str, 'peerA') + + // Call the router again with the same input. + const result = await uut.router(str, 'peerA') + + assert.equal(result, false) + }) + + it('should ignore metric queries from ipfs-coord', async () => { + const id = uid() + const json = jsonrpc.request(id, 'unknownMethod', {}) + + const str = JSON.stringify(json) + + // Force the RPC type to be 'success', to indicate an RPC that was + // processed internal to ipfs-coord. + sandbox.stub(uut.jsonrpc, 'parse').returns({ + payload: {}, + type: 'success' + }) + + // Call the router again with the same input. + const result = await uut.router(str, 'peerA') + + assert.equal(result, false) + }) + + it('should report errors when trying to send messages to peers', async () => { + const id = uid() + const json = jsonrpc.request(id, 'unknownMethod', {}) + + const str = JSON.stringify(json) + + // Force issue with sendPrivateMessage() + sandbox + .stub(uut.ipfsCoord.useCases.peer, 'sendPrivateMessage') + .rejects('test error') + + // Call the router again with the same input. + const result = await uut.router(str, 'peerA') + // console.log('result: ', result) + + assert.property(result, 'from') + assert.property(result, 'retStr') + }) + }) + + describe('#_checkIfAlreadyProcessed', () => { + it('should return false the first time an RPC command is seen', () => { + const data = { + payload: { + jsonrpc: '2.0', + id: '6c515f3c-cf8a-42ec-870e-e416edd4923f', + method: 'unknownMethod', + params: {} + }, + type: 'request' + } + + const result = uut._checkIfAlreadyProcessed(data) + + assert.equal(result, false) + }) + + it('should return true the second time an RPC command is seen', () => { + const data = { + payload: { + jsonrpc: '2.0', + id: '6c515f3c-cf8a-42ec-870e-e416edd4923f', + method: 'unknownMethod', + params: {} + }, + type: 'request' + } + + // First call. + uut._checkIfAlreadyProcessed(data) + + // Second call. + const result = uut._checkIfAlreadyProcessed(data) + + assert.equal(result, true) + }) + + it('should push out old data from the cache for new data', () => { + const data = { + payload: { + jsonrpc: '2.0', + id: '6c515f3c-cf8a-42ec-870e-e416edd4923f', + method: 'unknownMethod', + params: {} + }, + type: 'request' + } + + uut.MSG_CACHE_SIZE = 0 + + const result = uut._checkIfAlreadyProcessed(data) + + assert.equal(result, false) + }) + + it('should return true on error', () => { + const result = uut._checkIfAlreadyProcessed() + + assert.equal(result, true) + }) }) }) diff --git a/test/unit/mocks/adapters/index.js b/test/unit/mocks/adapters/index.js index aa84c05..a236014 100644 --- a/test/unit/mocks/adapters/index.js +++ b/test/unit/mocks/adapters/index.js @@ -7,7 +7,13 @@ const ipfs = { ipfs: {} }, ipfsCoordAdapter: { - ipfsCoord: {} + ipfsCoord: { + useCases: { + peer: { + sendPrivateMessage: () => {} + } + } + } } }