diff --git a/fabric-client/test/impl/BasicCommitHandler.js b/fabric-client/test/impl/BasicCommitHandler.js new file mode 100644 index 0000000000..79eb103b54 --- /dev/null +++ b/fabric-client/test/impl/BasicCommitHandler.js @@ -0,0 +1,257 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +'use strict'; + +const rewire = require('rewire'); +const BasicCommitHandler = rewire('../../lib/impl/BasicCommitHandler'); +const Channel = require('../../lib/Channel'); +const Orderer = require('../../lib/Orderer'); + +const chai = require('chai'); +const chaiAsPromised = require('chai-as-promised'); +const sinon = require('sinon'); +const sinonChai = require('sinon-chai'); +chai.should(); +chai.use(chaiAsPromised); +chai.use(sinonChai); + +describe('BasicCommitHandler', () => { + + let revert; + let FakeLogger; + let debugStub; + let errorStub; + let channelStub; + let handler; + + beforeEach(() => { + revert = []; + FakeLogger = { + debug: () => {}, + error: () => {} + }; + debugStub = sinon.stub(FakeLogger, 'debug'); + errorStub = sinon.stub(FakeLogger, 'error'); + revert.push(BasicCommitHandler.__set__('logger', FakeLogger)); + + channelStub = sinon.createStubInstance(Channel); + handler = new BasicCommitHandler(channelStub); + }); + + afterEach(() => { + if (revert.length) { + revert.forEach(Function.prototype.call, Function.prototype.call); + } + sinon.restore(); + }); + + describe('#constructor', () => { + it('should set `_channel` with a passed parameter', () => { + const basicCommitHandler = new BasicCommitHandler(channelStub); + basicCommitHandler._channel.should.equal(channelStub); + }); + }); + + describe('#create', () => { + it('should create an instance of a basic commit handler', () => { + const basicCommitHandler = BasicCommitHandler.create(channelStub); + basicCommitHandler._channel.should.equal(channelStub); + }); + }); + + describe('#initialize', () => { + it('should log on entry', () => { + handler.initialize(); + sinon.assert.calledWith(debugStub, 'initialize - start'); + }); + }); + + describe('#commit', () => { + + let _commitStub; + let ordererStub; + let getConfigSettingStub; + + beforeEach(() => { + getConfigSettingStub = sinon.stub().withArgs('request-timeout').returns(1000); + revert.push(BasicCommitHandler.__set__('utils.getConfigSetting', getConfigSettingStub)); + _commitStub = sinon.stub(); + handler._commit = _commitStub; + ordererStub = sinon.createStubInstance(Orderer); + channelStub._getOrderer.returns(ordererStub); + }); + + it('should throw an error if not params given', async () => { + await handler.commit() + .should.be.rejectedWith(/Missing all required input parameters/); + sinon.assert.calledWith(errorStub, 'Commit Handler error:Missing all required input parameters'); + }); + + it('should throw an error if request not specified', async () => { + await handler.commit({}) + .should.be.rejectedWith(/Missing "request" input parameter/); + }); + + it('should throw an error if signed_proposal not specified', async () => { + await handler.commit({request: {}}) + .should.be.rejectedWith(/Missing "signed_envelope" input parameter/); + }); + + it('should log on debug message if getDiscoveryResults throw an error', async () => { + channelStub.getDiscoveryResults.throws('dummy error'); + + await handler.commit({request: {}, signed_envelope: 'envelope'}); + sinon.assert.calledWith(debugStub, '%s - no discovery results %s'); + sinon.assert.calledOnce(channelStub.getDiscoveryResults); + sinon.assert.calledWith(_commitStub, 'envelope', 1000); + }); + + it('should call _commit', async () => { + channelStub.getDiscoveryResults.returns('discovery results'); + + await handler.commit({request: {}, signed_envelope: 'envelope'}); + sinon.assert.calledOnce(channelStub.getDiscoveryResults); + sinon.assert.calledWith(_commitStub, 'envelope', 1000); + }); + + it('should throw an error if sendBroadcast fails', async () => { + ordererStub.sendBroadcast.throws('dummy error'); + + await handler.commit({request: {orderer: 'orderer.com'}, signed_envelope: 'envelope', timeout: 2000}) + .should.be.rejectedWith(/Failed to send to the orderer/); + sinon.assert.calledWith(debugStub, '%s - using single orderer', 'commit'); + sinon.assert.calledWith(channelStub._getOrderer, 'orderer.com', 'endorsingPeer'); + sinon.assert.calledWith(ordererStub.sendBroadcast, 'envelope', 2000); + }); + + it('should return response if a message is broadcasted', async () => { + ordererStub.sendBroadcast.returns('response'); + + const response = await handler.commit({request: {orderer: 'orderer.com'}, signed_envelope: 'envelope', timeout: 2000}); + sinon.assert.calledWith(debugStub, '%s - using single orderer', 'commit'); + sinon.assert.calledWith(channelStub._getOrderer, 'orderer.com', 'endorsingPeer'); + sinon.assert.calledWith(ordererStub.sendBroadcast, 'envelope', 2000); + response.should.equal('response'); + }); + + }); + + describe('#_commit', () => { + + let orderer1Stub; + let orderer2Stub; + let orderers; + + beforeEach(() => { + orderer1Stub = sinon.createStubInstance(Orderer); + orderer1Stub.getName.returns('Orderer1'); + orderer2Stub = sinon.createStubInstance(Orderer); + orderer2Stub.getName.returns('Orderer2'); + orderers = [orderer1Stub, orderer2Stub]; + }); + + it('should throw an error if no orderers assigned', async () => { + channelStub.getOrderers = sinon.stub().returns(undefined); + + await handler._commit('envelope', 1000) + .should.be.rejectedWith(/No orderers assigned to the channel/); + }); + + it('should return results if the first orderer returns SUCCESS', async () => { + channelStub.getOrderers = sinon.stub().returns(orderers); + orderer1Stub.sendBroadcast.returns({status: 'SUCCESS'}); + orderer2Stub.sendBroadcast.returns({status: 'SUCCESS'}); + + const results = await handler._commit('envelope', 1000); + debugStub.getCall(0).args.should.deep.equal(['%s - found %s orderers assigned to channel', '_commit', 2]); + debugStub.getCall(1).args.should.deep.equal(['%s - starting orderer %s', '_commit', 'Orderer1']); + sinon.assert.calledWith(orderer1Stub.sendBroadcast, 'envelope', 1000); + debugStub.getCall(2).args.should.deep.equal(['%s - Successfully sent transaction to the orderer %s', '_commit', 'Orderer1']); + results.should.deep.equal({status: 'SUCCESS'}); + }); + + it('should return results if the second orderer returns SUCCESS', async () => { + channelStub.getOrderers = sinon.stub().returns(orderers); + orderer1Stub.sendBroadcast.returns({status: 'ERROR'}); + orderer2Stub.sendBroadcast.returns({status: 'SUCCESS'}); + + const results = await handler._commit('envelope', 1000); + debugStub.getCall(0).args.should.deep.equal(['%s - found %s orderers assigned to channel', '_commit', 2]); + debugStub.getCall(1).args.should.deep.equal(['%s - starting orderer %s', '_commit', 'Orderer1']); + sinon.assert.calledWith(orderer1Stub.sendBroadcast, 'envelope', 1000); + debugStub.getCall(2).args.should.deep.equal(['%s - Failed to send transaction successfully to the orderer status:%s', '_commit', 'ERROR']); + debugStub.getCall(3).args.should.deep.equal(['%s - finished orderer %s ', '_commit', 'Orderer1']); + debugStub.getCall(4).args.should.deep.equal(['%s - starting orderer %s', '_commit', 'Orderer2']); + sinon.assert.calledWith(orderer2Stub.sendBroadcast, 'envelope', 1000); + debugStub.getCall(5).args.should.deep.equal(['%s - Successfully sent transaction to the orderer %s', '_commit', 'Orderer2']); + results.should.deep.equal({status: 'SUCCESS'}); + }); + + it('should throw an error if all orderer returns an error', async () => { + channelStub.getOrderers = sinon.stub().returns(orderers); + orderer1Stub.sendBroadcast.returns({status: 'ERROR'}); + orderer2Stub.sendBroadcast.returns({status: 'ERROR'}); + + await handler._commit('envelope', 1000) + .should.be.rejectedWith(/Failed to send transaction successfully to the orderer status:ERROR/); + debugStub.getCall(0).args.should.deep.equal(['%s - found %s orderers assigned to channel', '_commit', 2]); + debugStub.getCall(1).args.should.deep.equal(['%s - starting orderer %s', '_commit', 'Orderer1']); + sinon.assert.calledWith(orderer1Stub.sendBroadcast, 'envelope', 1000); + debugStub.getCall(2).args.should.deep.equal(['%s - Failed to send transaction successfully to the orderer status:%s', '_commit', 'ERROR']); + debugStub.getCall(3).args.should.deep.equal(['%s - finished orderer %s ', '_commit', 'Orderer1']); + debugStub.getCall(4).args.should.deep.equal(['%s - starting orderer %s', '_commit', 'Orderer2']); + sinon.assert.calledWith(orderer2Stub.sendBroadcast, 'envelope', 1000); + debugStub.getCall(5).args.should.deep.equal(['%s - Failed to send transaction successfully to the orderer status:%s', '_commit', 'ERROR']); + debugStub.getCall(6).args.should.deep.equal(['%s - finished orderer %s ', '_commit', 'Orderer2']); + }); + + it('should throw an error if all orderers return nothing', async () => { + channelStub.getOrderers = sinon.stub().returns(orderers); + orderer1Stub.sendBroadcast.returns(); + orderer2Stub.sendBroadcast.returns(); + + await handler._commit('envelope', 1000) + .should.be.rejectedWith(/Failed to send transaction to the orderer/); + debugStub.getCall(0).args.should.deep.equal(['%s - found %s orderers assigned to channel', '_commit', 2]); + debugStub.getCall(1).args.should.deep.equal(['%s - starting orderer %s', '_commit', 'Orderer1']); + sinon.assert.calledWith(orderer1Stub.sendBroadcast, 'envelope', 1000); + debugStub.getCall(2).args.should.deep.equal(['%s - Failed to send transaction to the orderer %s', '_commit', 'Orderer1']); + debugStub.getCall(3).args.should.deep.equal(['%s - finished orderer %s ', '_commit', 'Orderer1']); + debugStub.getCall(4).args.should.deep.equal(['%s - starting orderer %s', '_commit', 'Orderer2']); + sinon.assert.calledWith(orderer2Stub.sendBroadcast, 'envelope', 1000); + debugStub.getCall(5).args.should.deep.equal(['%s - Failed to send transaction to the orderer %s', '_commit', 'Orderer2']); + debugStub.getCall(6).args.should.deep.equal(['%s - finished orderer %s ', '_commit', 'Orderer2']); + }); + + it('should throw an error if all orderers throw an exception error', async () => { + channelStub.getOrderers = sinon.stub().returns(orderers); + orderer1Stub.sendBroadcast.throws(new Error('dummy error')); + orderer2Stub.sendBroadcast.throws(new Error('dummy error')); + + await handler._commit('envelope', 1000) + .should.be.rejectedWith(/dummy error/); + debugStub.getCall(0).args.should.deep.equal(['%s - found %s orderers assigned to channel', '_commit', 2]); + debugStub.getCall(1).args.should.deep.equal(['%s - starting orderer %s', '_commit', 'Orderer1']); + sinon.assert.calledWith(orderer1Stub.sendBroadcast, 'envelope', 1000); + debugStub.getCall(2).args.should.deep.equal(['%s - Caught: %s', '_commit', 'Error: dummy error']); + debugStub.getCall(3).args.should.deep.equal(['%s - finished orderer %s ', '_commit', 'Orderer1']); + debugStub.getCall(4).args.should.deep.equal(['%s - starting orderer %s', '_commit', 'Orderer2']); + sinon.assert.calledWith(orderer2Stub.sendBroadcast, 'envelope', 1000); + debugStub.getCall(5).args.should.deep.equal(['%s - Caught: %s', '_commit', 'Error: dummy error']); + debugStub.getCall(6).args.should.deep.equal(['%s - finished orderer %s ', '_commit', 'Orderer2']); + }); + }); + +}); \ No newline at end of file diff --git a/test/unit/commit-handler.js b/test/unit/commit-handler.js deleted file mode 100644 index efe71df392..0000000000 --- a/test/unit/commit-handler.js +++ /dev/null @@ -1,143 +0,0 @@ -/** - * Copyright 2016-2017 IBM All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the 'License'); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an 'AS IS' BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -'use strict'; - -const tape = require('tape'); -const _test = require('tape-promise').default; -const test = _test(tape); -const Client = require('fabric-client'); -const TestUtil = require('./util.js'); - -test('\n\n ** BasicCommitHandler - test **\n\n', async (t) => { - - const temp = Client.getConfigSetting('commit-handler'); - Client.setConfigSetting('endorsement-handler', 'fabric-client/lib/impl/BasicCommitHandler.js'); - - const client = new Client(); - const store = await Client.newDefaultKeyValueStore({path: TestUtil.storePathForOrg('org1')}); - client.setStateStore(store); - await TestUtil.setAdmin(client, 'org1'); - const channel = client.newChannel('handlertest'); - try { - await channel.initialize(); - } catch (error) { - // going to get an error - } - - const handler = channel._commit_handler; - if (handler && handler.commit) { - t.pass('Able to have the channel create the handler'); - } else { - t.fail('Channel was not able to create the handler'); - t.end(); - return; - } - - try { - await channel.initialize({commitHandler:'bad.js'}); - t.fail('Should not be here - commiHandler name is bad '); - } catch (error) { - if (error.toString().includes('find module')) { - t.pass('Successfully failed to initialize using the commitHandler file name ::' + error); - } else { - t.fail('Received an unknown error ::' + error); - } - } - - let parameters = null; - await errorChecker(t, handler, parameters, 'Missing all'); - parameters = {}; - await errorChecker(t, handler, parameters, 'Missing "request"'); - parameters.request = {}; - await errorChecker(t, handler, parameters, 'Missing "signed_envolope"'); - - const orderer = client.newOrderer('grpc://somehost.com:7777'); - try { - await handler.commit({request: {orderer}, signed_envelope: {}}, 1000); - t.fail('Should not be here - looking for orderers in request'); - } catch (error) { - if (error instanceof Error) { - if (error.toString().indexOf('Failed to connect before the deadline') > -1) { - t.pass('This should fail with ' + error.toString()); - } else { - t.fail('Did not get Failed to connect before the deadline - got ' + error.toString()); - } - } else { - t.fail('Unknown commit results returned'); - } - } - - const request = {}; - const envelope = {}; - - try { - await handler._commit(request, envelope, 5000); - t.fail('Should not be here - looking for no orderers assigned'); - } catch (error) { - if (error instanceof Error) { - if (error.toString().indexOf('No orderers assigned to the channel') > -1) { - t.pass('This should fail with ' + error.toString()); - } else { - t.fail('Did not get No orderers assigned to the channel - got ' + error.toString()); - } - } else { - t.fail('Unknown commit results returned'); - } - } - - channel.addOrderer(client.newOrderer('grpc://somehost.com:1111')); - channel.addOrderer(client.newOrderer('grpc://somehost.com:2222')); - channel.addOrderer(client.newOrderer('grpc://somehost.com:3333')); - channel.addOrderer(client.newOrderer('grpc://somehost.com:4444')); - channel.addOrderer(client.newOrderer('grpc://somehost.com:5555')); - channel.addOrderer(client.newOrderer('grpc://somehost.com:6666')); - - try { - await handler._commit(request, envelope, 5000); - t.fail('Should not be here - looking for connect deadline'); - } catch (error) { - if (error instanceof Error) { - if (error.toString().indexOf('Failed to connect before the deadline') > -1) { - t.pass('This should fail with ' + error.toString()); - } else { - t.fail('Did not get deadline error - got ' + error.toString()); - } - } else { - t.fail('Unknown commit results returned'); - } - } - - t.pass('Completed the testing'); - - if (temp) { - Client.setConfigSetting('endorsement-handler-path', temp); - } - t.end(); -}); - -async function errorChecker(t, handler, parameters, error_text) { - try { - await handler.commit(parameters); - t.fail('Should not be here - error checker has failed'); - } catch (error) { - if (error.toString().indexOf(error_text)) { - t.pass('Check for :' + error_text); - } else { - t.fail('Check for :' + error_text); - } - } -}