From 61376fed8b9cad89cdb4b72879a31a7740388ebd Mon Sep 17 00:00:00 2001 From: Liam Grace Date: Fri, 26 Oct 2018 14:11:57 +0100 Subject: [PATCH] [FABN-599] Mocha unit tests for Channel Change-Id: I7963d5ebac20bab8d5021fb4b24e6f67b72717a3 Signed-off-by: Liam Grace --- fabric-client/package.json | 9 +- fabric-client/test/Channel.js | 473 ++++++++++++++++++++++++++++++++++ 2 files changed, 479 insertions(+), 3 deletions(-) diff --git a/fabric-client/package.json b/fabric-client/package.json index 0b60db40c9..58662a6cfb 100644 --- a/fabric-client/package.json +++ b/fabric-client/package.json @@ -1,7 +1,10 @@ { "name": "fabric-client", "description": "SDK for writing node.js applications to interact with Hyperledger Fabric. This package encapsulates the APIs to interact with Peers and Orderers of the Fabric network to install and instantiate chaincodes, send transaction invocations and perform chaincode queries.", - "keywords" : ["hyperledger", "blockchain"], + "keywords": [ + "hyperledger", + "blockchain" + ], "version": "1.4.0-snapshot", "tag": "unstable", "main": "index.js", @@ -40,8 +43,8 @@ "nconf": "^0.10.0", "nyc": "^12.0.2", "path": "^0.12.7", - "pkcs11js": "^1.0.6", - "protobufjs": "5.0.3", + "pkcs11js": "^1.0.6", + "protobufjs": "5.0.3", "promise-settle": "^0.3.0", "restify": "7.2.0", "rewire": "^4.0.1", diff --git a/fabric-client/test/Channel.js b/fabric-client/test/Channel.js index 093cf87d28..cbf43e9d07 100644 --- a/fabric-client/test/Channel.js +++ b/fabric-client/test/Channel.js @@ -16,11 +16,13 @@ const sinon = require('sinon'); const chai = require('chai'); +const rewire = require('rewire'); const chaiAsPromised = require('chai-as-promised'); chai.use(chaiAsPromised); const expect = chai.expect; const Channel = require('fabric-client/lib/Channel'); +const ChannelRewire = rewire('fabric-client/lib/Channel'); const ChannelEventHub = require('fabric-client/lib/ChannelEventHub'); const Client = require('fabric-client/lib/Client'); const { Identity, SigningIdentity } = require('fabric-client/lib/msp/identity'); @@ -894,4 +896,475 @@ describe('Channel', () => { return expect(channel.initialize(request)).to.be.fulfilled; }); }); + + describe('#_initialize', () => {}); + + describe('_buildDiscoveryMSPs', () => {}); + + describe('#_buildDiscoveryOrderers', () => {}); + + describe('#_buildDiscoveryPeers', () => {}); + + describe('#_buildDiscoveryEndorsementPlan', () => {}); + + describe('#getDiscoveryResults', () => {}); + + describe('#getEndorsementPlan', () => {}); + + describe('#refresh', () => { + it('should call initialize and return the result', async () => { + sinon.stub(channel, '_initialize').resolves('initialize-result'); + const result = await channel.refresh(); + sinon.assert.calledWith(channel._initialize, channel._last_refresh_request); + result.should.equal('initialize-result'); + }); + + it('should log if Channel._initialize throws an error', async () => { + sinon.stub(channel, '_initialize').rejects(new Error('initialize-failed')); + try { + await channel.refresh(); + } catch (err) { + err.message.should.equal('initialize-failed'); + } + }); + }); + + describe('#getOrganizations', () => {}); + + describe('#setMSPManager', () => {}); + + describe('#getMSPManager', () => {}); + + describe('#addPeer', () => {}); + + describe('#remoePeer', () => {}); + + describe('#gePeer', () => {}); + + describe('#getChannelPeers', () => {}); + + describe('#addOrderer', () => {}); + + describe('#removeOrderer', () => {}); + + describe('#getOrderer', () => {}); + + describe('#getOrderers', () => {}); + + describe('#newChannelEventHub', () => {}); + + describe('#getChannelEventHub', () => {}); + + describe('#getChannelEventHubsForOrg', () => {}); + + describe('#getPeersForOrg', () => {}); + + describe('#getGenesisBlock', () => {}); + + describe('#_discover', () => {}); + + describe('#_processDiscoveryChaincodeResults', () => {}); + + describe('#_processDiscoveryResults', () => {}); + + describe('#_processDiscoveryMembershipResults', () => {}); + + describe('#_processPeers', () => {}); + + describe('#_processPeers', () => {}); + + describe('#_buildOrdererName', () => { + it('should match an existing orderer', () => { + const orderer = sinon.createStubInstance(Orderer); + orderer.getUrl.returns('grpcs://peer:7051'); + orderer.getName.returns('orderer'); + sinon.stub(channel, '_buildUrl').returns('grpcs://peer:7051'); + channel._orderers = [orderer]; + const mspid = 'mspid'; + const host = 'peer'; + const port = '7051'; + const request = {}; + const name = channel._buildOrdererName(mspid, host, port, {}, request); + name.should.equal('orderer'); + }); + + it('should throw an error if orderer is not found and there is missing information', () => { + const orderer = sinon.createStubInstance(Orderer); + sinon.stub(channel, '_buildUrl').returns('grpcs://peer:7051'); + channel._orderers = [orderer]; + const mspid = 'mspid'; + const host = 'peer'; + const port = '7051'; + const request = {}; + (() => { + channel._buildOrdererName(mspid, host, port, {}, request); + }).should.throw(Error, 'No TLS cert information available'); + }); + + it('should add a new orderer if all information is available', () => { + const mspid = 'mspid'; + const host = 'peer'; + const port = '7051'; + const request = {}; + sinon.stub(channel, 'addOrderer'); + const name = channel._buildOrdererName(mspid, host, port, {mspid}, request); + sinon.assert.called(channel.addOrderer); + name.should.equal('peer:7051'); + }); + }); + + describe('#_buildPeerName', () => { + it('should match an existing peer', () => { + const peer = sinon.createStubInstance(Peer); + sinon.stub(channel, '_buildUrl').returns('grpcs://peer:7051'); + peer.getUrl.returns('grpcs://peer:7051'); + peer.getName.returns('peerName'); + channel._channel_peers = [peer]; + const request = {asLocalHost: true}; + const name = channel._buildPeerName('peer:7051', 'mspid', null, request); + sinon.assert.calledWith(channel._buildUrl, 'peer', '7051', request); + name.should.equal('peerName'); + }); + + it('should throw an error if peer is not found and there is information missing', () => { + const peer = sinon.createStubInstance(Peer); + sinon.stub(channel, '_buildUrl').returns('grpcs://peer:7053'); + channel._channel_peers = [peer]; + (() => { + channel._buildPeerName('peer:7051', 'mspid', null, 'request'); + }).should.throw(Error, 'No TLS cert information available'); + }); + + it('should add a new peer if all information is available', () => { + const mspid = 'mspid'; + const name = channel._buildPeerName('peer:7051', mspid, {mspid}, 'request'); + name.should.equal('peer:7051'); + }); + }); + + describe('#_buildUrl', () => {}); + + describe('#_buildOptions', () => { + it('should return the build options', () => { + const client = sinon.createStubInstance(Client); + channel._clientContext = client; + const msp = {tls_root_certs: 'ROOT_CERT'}; + const pem = 'ROOT_CERT'; + const name = 'name'; + sinon.stub(channel, '_buildTlsRootCerts').returns('ROOT_CERT'); + const buildOptions = channel._buildOptions(name, 'url', 'host', msp); + sinon.assert.calledWith(channel._buildTlsRootCerts, msp); + sinon.assert.calledWith(client.addTlsClientCertAndKey, {name, pem, 'ssl-target-name-override': 'host'}); + buildOptions.should.deep.equal({pem, name, 'ssl-target-name-override': 'host'}); + }); + }); + + describe('#_buildTlsRootCerts', () => { + it('should return the root certs if they are in the msp', () => { + const msp = {tls_root_certs: 'ROOT_CERT'}; + channel._buildTlsRootCerts(msp).should.deep.equal('ROOT_CERT'); + }); + + it('should return the intermediate certs if they are in the msp', () => { + const msp = {tls_intermediate_certs: 'INTERMEDIATE_CERT'}; + channel._buildTlsRootCerts(msp).should.deep.equal('INTERMEDIATE_CERT'); + }); + + it('should return both root and intermediate certs if they are in the msp', () => { + const msp = {tls_root_certs: 'ROOT_CERT', tls_intermediate_certs: 'INTERMEDIATE_CERT'}; + channel._buildTlsRootCerts(msp).should.deep.equal('ROOT_CERTINTERMEDIATE_CERT'); + }); + }); + + describe('#_buildProtoChaincodeInterest', () => {}); + + describe('#_merge_hints', () => { + it('should return false if no hints are given', () => { + channel._merge_hints().should.be.false; + }); + + it('should convert non-array hints to arrays and add it to the hints', () => { + const hint = {hint: 'hint'}; + channel._merge_hints(hint).should.be.true; + channel._discovery_interests.get(JSON.stringify(hint)).should.deep.equal(hint); + }); + + it('should should return true if hint exists', () => { + const hint = {hint: 'hint'}; + const hints = [hint]; + channel._discovery_interests.set(JSON.stringify(hint), hint); + channel._merge_hints(hints).should.be.false; + }); + }); + + describe('#buildDiscoveryInterest', () => { + it('should return a discovery interest and call _buildDiscoveryInterest', () => { + const name = 'chaincodeName'; + const collection_names = ['cc']; + const chaincodeCall = {name, collection_names}; + sinon.stub(channel, '_buildDiscoveryChaincodeCall').returns(chaincodeCall); + channel._buildDiscoveryInterest('name', collection_names).should.deep.equal({chaincodes: [chaincodeCall]}); + }); + }); + + describe('#_buildDiscoveryChaincodeCall', () => { + it('should throw an error if name is not a string', () => { + (() => { + channel._buildDiscoveryChaincodeCall(null, []); + }).should.throw(Error, 'Chaincode name must be a string'); + }); + + it('should throw an error if collection_names is not an array', () => { + (() => { + channel._buildDiscoveryChaincodeCall('chaincodeName', {}); + }).should.throw(Error, 'Collections names must be an array of strings'); + }); + + it('should throw an error if collection_names contains non-string values', () => { + (() => { + channel._buildDiscoveryChaincodeCall('chaincodeName', [null]); + }).should.throw(Error, 'The collection name must be a string'); + }); + + it('should return the chaincode calls if collection_names are not set', () => { + channel._buildDiscoveryChaincodeCall('chaincodeName', null).should.deep.equal({name: 'chaincodeName'}); + }); + + it('should return the chaincode call when correct parameters are given', () => { + const name = 'chaincodeName'; + const collection_names = ['collection']; + channel._buildDiscoveryChaincodeCall('chaincodeName', collection_names).should.deep.equal({name, collection_names}); + }); + }); + + describe('#joinChannel', () => {}); + + describe('#getChannelConfig', () => {}); + + describe('#getChannelConfigFromOrderer', () => {}); + + describe('#loadConfigUpdate', () => {}); + + describe('#loadConfigEnvelope', () => {}); + + describe('#queryInfo', () => {}); + + describe('#queryByBlockId', () => {}); + + describe('#queryBlockByHash', () => {}); + + describe('#queryBlock', () => {}); + + describe('#queryTransaction', () => {}); + + describe('#queryInstantiatedChaincodes', () => {}); + + describe('#queryCollectionsConfig', () => {}); + + describe('#sendInstantiateProposal', () => { + it('should call _sendChaincodeProposal', () => { + sinon.stub(channel, '_sendChaincodeProposal'); + channel.sendInstantiateProposal('request', 10); + sinon.assert.calledWith(channel._sendChaincodeProposal, 'request', 'deploy', 10); + }); + }); + + describe('#sendUpgradeProposal', () => { + it('should call _sendChaincodeProposal', () => { + sinon.stub(channel, '_sendChaincodeProposal'); + channel.sendUpgradeProposal('request', 10); + sinon.assert.calledWith(channel._sendChaincodeProposal, 'request', 'upgrade', 10); + }); + }); + + describe('#_sendChaincodeProposal', () => {}); + + describe('#sendTransactionProposal', () => {}); + + describe('sendTransactionProposal', () => {}); + + describe('#sendTransaction', () => {}); + + describe('#sendSignedTransation', () => {}); + + describe('#buildEnvelope', () => {}); + + describe('#queryByChaincode', () => {}); + + describe('#_getTargetForQuery', () => {}); + + describe('#_getTargetForDiscovery', () => {}); + + describe('#_getTargets', () => {}); + + describe('#_getOrderer', () => {}); + + describe('#_buildEndorsementPolicy', () => {}); + + describe('#_getProposalResponseResults', () => {}); + + describe('#loadConfigGroup', () => {}); + + describe('#loadConfigValue', () => {}); +}); + +describe('ChannelPeer', () => { + let ChannelPeer; + let peer; + let channel; + let eventHub; + let instance; + beforeEach(() => { + ChannelPeer = ChannelRewire.__get__('ChannelPeer'); + peer = sinon.createStubInstance(Peer); + peer.getName.returns('peerName'); + peer.getUrl.returns('http://someurl'); + channel = sinon.createStubInstance(Channel); + eventHub = sinon.createStubInstance(ChannelEventHub); + instance = new ChannelPeer('mspId', channel, peer); + instance._channel_event_hub = eventHub; + }); + + describe('#constructor', () => { + it('should throw an error if the channel parameter is missing', () => { + (() => { + new ChannelPeer('mspid'); + }).should.throw(Error, 'Missing Channel parameter'); + }); + + it('should throw an error if the peer parameter is missing', () => { + (() => { + new ChannelPeer('mspid', sinon.createStubInstance(Channel)); + }).should.throw(Error, 'Missing Peer parameter'); + }); + + it('should set the correct class properties', () => { + const channelStub = sinon.createStubInstance(Channel); + const peerStub = sinon.createStubInstance(Peer); + peerStub.getName.returns('peerName'); + const channelPeer = new ChannelPeer('mspId', channelStub, peerStub); + channelPeer._mspid.should.equal('mspId'); + channelPeer._name.should.equal('peerName'); + channelPeer._channel.should.equal(channelStub); + channelPeer._peer.should.equal(peerStub); + channelPeer._roles.should.deep.equal({}); + }); + + it('should set the correct roles', () => { + const channelStub = sinon.createStubInstance(Channel); + const peerStub = sinon.createStubInstance(Peer); + peerStub.getName.returns('peerName'); + const channelPeer = new ChannelPeer('mspId', channelStub, peerStub, {'role1': 'role1'}); + channelPeer._roles.should.deep.equal({role1: 'role1'}); + }); + }); + + describe('#close' , () => { + it('should close the peer connection', () => { + instance._channel_event_hub = null; + instance.close(); + sinon.assert.called(peer.close); + }); + + it('should close the connection to the event hub', () => { + instance.close(); + sinon.assert.called(instance._channel_event_hub.close); + }); + }); + + describe('#getMspId', () => { + it('should return the mspid', () => { + instance.getMspid().should.equal('mspId'); + }); + }); + + describe('#getName', () => { + it('should return the name', () => { + instance.getName().should.equal('peerName'); + }); + }); + + describe('#getUrl', () => { + it('should return the peer url', () => { + instance.getUrl().should.equal('http://someurl'); + }); + }); + + describe('#setRole', () => { + it('should set a role', () => { + instance.setRole('aSetRole', 'theRole'); + instance._roles.should.deep.equal({aSetRole: 'theRole'}); + }); + }); + + describe('#isInRole', () => { + it('should throw an error if no role is given', () => { + (() => { + instance.isInRole(); + }).should.throw(Error, 'Missing "role" parameter'); + }); + + it('should return true if role not found', () => { + instance.isInRole('someRole').should.be.true; + }); + + it('should return the role if found', () => { + instance._roles = {someRole: 'theRole'}; + instance.isInRole('someRole').should.equal('theRole'); + }); + }); + + describe('#isInOrg', () => { + it('should return true if no mspId is given', () => { + instance._mspid = null; + instance.isInOrg().should.be.true; + }); + + it('should check if the mspid matches', () => { + instance.isInOrg('mspId').should.be.true; + }); + }); + + describe('#getChannelEventHub', () => { + it('should return the event hub if it is set', () => { + instance.getChannelEventHub().should.equal(eventHub); + }); + + it('should create a new event hub if one is not set', () => { + instance._channel_event_hub = null; + const newEventHub = instance.getChannelEventHub(); + newEventHub.should.be.instanceof(ChannelEventHub); + newEventHub._channel.should.equal(channel); + newEventHub._peer.should.equal(peer); + }); + }); + + describe('#getPeer', () => { + it('should return the peer', () => { + instance.getPeer().should.equal(peer); + }); + }); + + describe('#sendProposal', () => { + it('should return the proposal request', () => { + peer.sendProposal.returns('proposal'); + instance.sendProposal('proposal', 'request').should.equal('proposal'); + sinon.assert.calledWith(peer.sendProposal, 'proposal', 'request'); + }); + }); + + describe('#sendDiscovery', () => { + it('should return the discovery request', () => { + peer.sendDiscovery.returns('discovery'); + instance.sendDiscovery('request', 'timeout').should.equal('discovery'); + sinon.assert.calledWith(peer.sendDiscovery, 'request', 'timeout'); + }); + }); + + describe('#toString', () => { + it('should call peer.toString', () => { + instance.toString(); + sinon.assert.called(peer.toString); + }); + }); });