diff --git a/src/rpc/index.js b/src/rpc/index.js index 925a3d6..e4b6d35 100644 --- a/src/rpc/index.js +++ b/src/rpc/index.js @@ -7,6 +7,7 @@ const jsonrpc = require('jsonrpc-lite') // Local support libraries const UserController = require('./users') +const wlogger = require('../lib/wlogger') let _this @@ -26,11 +27,19 @@ class JSONRPC { console.log('router str: ', str) console.log('router from: ', from) + // Exit if from is not specified. + if (!from || typeof from !== 'string') { + console.warn( + 'Warning: Can not send JSON RPC response. Can not determine which peer this message came from.' + ) + return + } + + // Attempt to parse the incoming data as a JSON RPC string. const parsedData = _this.jsonrpc.parse(str) console.log('parsedData: ', parsedData) - // console.log('parsedData.type: ', parsedData.type) - // Exit quietly if the incoming string is invalid + // Exit quietly if the incoming string is an invalid JSON RPC string. if (parsedData.type === 'invalid') { return } @@ -39,14 +48,29 @@ class JSONRPC { case 'users': _this.userController.userRouter(parsedData) break - // case default: - // TODO: Return an error + default: + return this.defaultResponse() } } catch (err) { - console.error('Error in rpc router(): ', err) + wlogger.error('Error in rpc router(): ', err) // Do not throw error. This is a top-level function. } } + + // The default JSON RPC response if the incoming command could not be routed. + defaultResponse () { + try { + const errorObj = this.jsonrpc.error( + 'Can not route', + new jsonrpc.JsonRpcError('Input does not match routing rules', 422) + ) + const errorStr = JSON.stringify(errorObj) + return errorStr + } catch (err) { + console.error('Error in defaultResponse()') + throw err + } + } } module.exports = JSONRPC diff --git a/test/unit/json-rpc/a10-rpc.unit.js b/test/unit/json-rpc/a10-rpc.unit.js index 84f36dc..cbb2663 100644 --- a/test/unit/json-rpc/a10-rpc.unit.js +++ b/test/unit/json-rpc/a10-rpc.unit.js @@ -3,29 +3,81 @@ */ // Public npm libraries +const assert = require('chai').assert const jsonrpc = require('jsonrpc-lite') +const sinon = require('sinon') const JSONRPC = require('../../../src/rpc') describe('#JSON RPC', () => { let uut + let sandbox beforeEach(() => { + sandbox = sinon.createSandbox() + uut = new JSONRPC() }) + afterEach(() => sandbox.restore()) + describe('#router', () => { - it('should do something', async () => { + it('should exit quietly if given a random string', async () => { + const str = 'random string message' + await uut.router(str) + + assert.isOk('Not throwing an error is a pass.') + }) + + it('should exit quietly if invalid JSON RPC message received', async () => { + const malformedRpc = '{"jsonrpc":"2.0"}' + + await uut.router(malformedRpc, 'peerA') + }) + + it('should return default response if routing is not possible', async () => { // const request = { // 'users', // id // 'getAll', // method // {} // } - const json = jsonrpc.request('users', 'getAll', {}) + const json = jsonrpc.request('unknownId', 'unknownMethod', {}) const str = JSON.stringify(json) - await uut.router(str) + const result = await uut.router(str, 'peerA') + // console.log('result: ', result) + + const jsonObj = jsonrpc.parse(result) + // console.log(`jsonObj: ${JSON.stringify(jsonObj, null, 2)}`) + + // Assert the expected properties exist on the returned object. + assert.property(jsonObj, 'payload') + assert.property(jsonObj, 'type') + assert.property(jsonObj.payload, 'jsonrpc') + assert.property(jsonObj.payload, 'id') + assert.property(jsonObj.payload, 'error') + assert.property(jsonObj.payload.error, 'message') + assert.property(jsonObj.payload.error, 'code') + + // Assert the expected values exist. + assert.equal(jsonObj.payload.id, 'Can not route') + assert.equal( + jsonObj.payload.error.message, + 'Input does not match routing rules' + ) + assert.equal(jsonObj.payload.error.code, 422) + }) + + it('should catch and handle errors', async () => { + // Force an error + sandbox.stub(uut.jsonrpc, 'parse').throws(new Error('test error')) + + const malformedRpc = '{"jsonrpc":"2.0"}' + + await uut.router(malformedRpc, 'peerA') + + assert.isOk('Not throwing an error is a pass.') }) }) })