From ee4225fef85cb014bddfa9d0759363f49c6fc2ad Mon Sep 17 00:00:00 2001 From: harrisob Date: Wed, 4 Dec 2019 09:14:57 -0500 Subject: [PATCH] FABN-1347 NodeSDK update low level (#59) Update fabric-common with changes to help with the integration with the high level SDK Signed-off-by: Bret Harrison --- build/tasks/test.js | 2 +- fabric-common/lib/Channel.js | 42 +-- fabric-common/lib/Client.js | 2 +- fabric-common/lib/Commit.js | 20 +- fabric-common/lib/Discoverer.js | 12 +- fabric-common/lib/DiscoveryService.js | 18 +- fabric-common/lib/Endorsement.js | 12 +- fabric-common/lib/Endorser.js | 16 +- fabric-common/lib/EventListener.js | 11 +- fabric-common/lib/EventService.js | 128 +++++--- fabric-common/lib/Proposal.js | 62 ++-- fabric-common/lib/Query.js | 10 +- fabric-common/lib/ServiceAction.js | 4 +- fabric-common/lib/ServiceEndpoint.js | 102 ++++-- fabric-common/lib/ServiceHandler.js | 4 +- fabric-common/test/BaseClient.js | 354 +++++++++++++++++++++ fabric-common/test/Channel.js | 12 +- fabric-common/test/Commit.js | 6 +- fabric-common/test/DiscoveryService.js | 14 +- fabric-common/test/Endorsement.js | 4 +- fabric-common/test/EventListener.js | 49 ++- fabric-common/test/EventService.js | 86 +++-- fabric-common/test/Proposal.js | 6 +- fabric-common/test/Query.js | 4 +- fabric-common/test/ServiceEndpoint.js | 81 ++++- fabric-common/test/User.js | 2 +- test/scenario/features/steps/base_steps.js | 7 +- 27 files changed, 839 insertions(+), 231 deletions(-) create mode 100644 fabric-common/test/BaseClient.js diff --git a/build/tasks/test.js b/build/tasks/test.js index ea357c86a1..a30d06d30a 100644 --- a/build/tasks/test.js +++ b/build/tasks/test.js @@ -178,7 +178,7 @@ gulp.task('test-fv-scenario', shell.task('npx nyc gulp run-test-fv-scenario')); // run fv only with code coverage // override the global nyc configuration -gulp.task('test-fv-only', shell.task('npx nyc --check-coverage --statements 53 --branches 42 --functions 53 --lines 53 gulp run-tape-e2e')); +gulp.task('test-fv-only', shell.task('npx nyc --check-coverage --statements 50 --branches 42 --functions 50 --lines 50 gulp run-tape-e2e')); gulp.task('run-test-fv-scenario', (done) => { const tasks = ['run-tape-e2e', 'docker-clean', 'run-test:cucumber', 'docker-clean', 'run-test:ts-cucumber']; diff --git a/fabric-common/lib/Channel.js b/fabric-common/lib/Channel.js index c22cd30daa..9c4bab9211 100644 --- a/fabric-common/lib/Channel.js +++ b/fabric-common/lib/Channel.js @@ -82,60 +82,62 @@ const Channel = class { /** * Gets an Endorsement instance for this channel. - * @param {string} chaincodeName + * @param {string} chaincodeId */ - newEndorsement(chaincodeName = checkParameter('chaincodeName')) { + newEndorsement(chaincodeId = checkParameter('chaincodeId')) { const method = `newEndorsement[${this.name}]`; logger.debug(`${method} - start`); - return new Endorsement(chaincodeName, this); + return new Endorsement(chaincodeId, this); } /** * Gets a Query instance for this channel. - * @param {string} chaincodeName + * @param {string} chaincodeId */ - newQuery(chaincodeName = checkParameter('chaincodeName')) { + newQuery(chaincodeId = checkParameter('chaincodeId')) { const method = `newQuery[${this.name}]`; logger.debug(`${method} - start`); - return new Query(chaincodeName, this); + return new Query(chaincodeId, this); } /** * Gets a Commit instance for this channel. - * @param {string} chaincodeName + * @param {string} chaincodeId */ - newCommit(chaincodeName = checkParameter('chaincodeName')) { + newCommit(chaincodeId = checkParameter('chaincodeId')) { const method = `newCommit[${this.name}]`; logger.debug(`${method} - start`); - return new Commit(chaincodeName, this); + return new Commit(chaincodeId, this); } /** - * Returns a new {@link EventService} object on each call. + * Returns a new {@link EventService} instance * - * @param {string} name - The name for this EventService - * @returns {EventService} The EventService instance + * @param {string} name - The name of this event service. */ newEventService(name = checkParameter('name')) { const method = `newEventService[${this.name}]`; logger.debug(`${method} - start`); - return new EventService(name, this); + + const eventService = new EventService(name, this); + + return eventService; } /** - * Returns a {@link DiscoveryService} instance with the given name. - * Will return a new instance. + * Returns a new {@link DiscoveryService} instance * - * @param {string} name The name of this discovery instance. - * @returns {DiscoveryService} The discovery instance. + * @param {string} name - The name of this discovery service. */ newDiscoveryService(name = checkParameter('name')) { - const method = `newDiscovery[${this.name}]`; - logger.debug(`${method} - start - create new DiscoveryService name:${name} for channel:${this.name}`); - return new DiscoveryService(name, this); + const method = `newDiscoveryService[${this.name}]`; + logger.debug(`${method} - start`); + const discoveryService = new DiscoveryService(name, this); + + return discoveryService; } /** diff --git a/fabric-common/lib/Client.js b/fabric-common/lib/Client.js index a40d2c62b5..75c5cd0319 100644 --- a/fabric-common/lib/Client.js +++ b/fabric-common/lib/Client.js @@ -222,7 +222,7 @@ const Client = class { /** * Will return an array of {@link Endorser} instances that have been - * assigned to this channel instance. Include a MSPID to only return endorsers + * created by this client instance. Include a MSPID to only return endorsers * in a specific organization. * * @param {string} [mspid] - Optional. The mspid of the endorsers to return diff --git a/fabric-common/lib/Commit.js b/fabric-common/lib/Commit.js index baf3d2fef9..a0ee26040e 100644 --- a/fabric-common/lib/Commit.js +++ b/fabric-common/lib/Commit.js @@ -26,13 +26,13 @@ class Commit extends Proposal { /** * Construct a Proposal object. * - * @param {string} chaincodeName - The chaincode this proposal will execute + * @param {string} chaincodeId - The chaincode this proposal will execute * @param {Channel} channel - The channel of this proposal * @returns {Proposal} The Proposal instance. */ - constructor(chaincodeName = checkParameter('chaincodeName'), channel = checkParameter('channel'), endorsement) { - super(chaincodeName, channel); - const method = `constructor[${chaincodeName}]`; + constructor(chaincodeId = checkParameter('chaincodeId'), channel = checkParameter('channel'), endorsement) { + super(chaincodeId, channel); + const method = `constructor[${chaincodeId}]`; logger.debug('%s - start', method); this.type = TYPE; this._endorsement = endorsement; @@ -53,7 +53,7 @@ class Commit extends Proposal { * @returns {byte[]} The commits payload bytes to be signed. */ build(idContext = checkParameter('idContext'), request = {}) { - const method = `build[${this.chaincodeName}]`; + const method = `build[${this.chaincodeId}]`; logger.debug('%s - start - %s', method, idContext.name); if (request.endorsement) { @@ -144,7 +144,7 @@ class Commit extends Proposal { * @returns commit results */ async send(request = {}) { - const method = `send[${this.chaincodeName}]`; + const method = `send[${this.chaincodeId}]`; logger.debug('%s - start', method); const {handler, targets, requestTimeout} = request; @@ -152,12 +152,12 @@ class Commit extends Proposal { const envelope = this.getSignedEnvelope(); if (handler) { - logger.debug('%s - calling the handler'); + logger.debug('%s - calling the handler', method); const result = await handler.commit(envelope, request); return result; } else if (targets) { - logger.debug('%s - sending to the targets'); + logger.debug('%s - sending to the targets', method); const committers = this.channel.getTargetCommitters(targets); let bad_result = {}; bad_result.status = 'UNKNOWN'; @@ -173,7 +173,7 @@ class Commit extends Proposal { return bad_result; } else { - throw Error('Missing targets parameter'); + throw checkParameter('targets'); } } @@ -182,7 +182,7 @@ class Commit extends Proposal { */ toString() { - return `Commit: {chaincodeName: ${this.chaincodeName}, channel: ${this.channel.name}}`; + return `Commit: {chaincodeId: ${this.chaincodeId}, channel: ${this.channel.name}}`; } } diff --git a/fabric-common/lib/Discoverer.js b/fabric-common/lib/Discoverer.js index 214e8f24c7..7bd31fdadd 100644 --- a/fabric-common/lib/Discoverer.js +++ b/fabric-common/lib/Discoverer.js @@ -66,7 +66,9 @@ class Discoverer extends ServiceEndpoint { const send_timeout = setTimeout(() => { clearTimeout(send_timeout); logger.error(`${method} - timed out after:${rto}`); - return reject(new Error('REQUEST TIMEOUT')); + const return_error = new Error('REQUEST TIMEOUT'); + this.getCharacteristics(return_error); + return reject(return_error); }, rto); this.service.discover(signedEnvelope, (err, response) => { @@ -74,21 +76,21 @@ class Discoverer extends ServiceEndpoint { if (err) { logger.debug(`${method} - Received discovery response from: ${this.endpoint.url} status: ${err}`); if (err instanceof Error) { - err.peer = this.getCharacteristics(); + this.getCharacteristics(err); reject(err); } else { const return_error = new Error(err); - return_error.connection = this.getCharacteristics(); + this.getCharacteristics(return_error); reject(return_error); } } else { if (response) { logger.debug(`${method} - Received discovery response from peer "${this.endpoint.url}"`); - response.connection = this.getCharacteristics(); + this.getCharacteristics(response); resolve(response); } else { const return_error = new Error(`GRPC service failed to get a proper response from the peer ${this.endpoint.url}.`); - return_error.connection = this.getCharacteristics(); + this.getCharacteristics(return_error); logger.error(`${method} - rejecting with:${return_error}`); reject(return_error); } diff --git a/fabric-common/lib/DiscoveryService.js b/fabric-common/lib/DiscoveryService.js index 2898ea1f8c..fd09e1613c 100644 --- a/fabric-common/lib/DiscoveryService.js +++ b/fabric-common/lib/DiscoveryService.js @@ -45,7 +45,7 @@ class DiscoveryService extends ServiceAction { this.discoveryResults = null; this.asLocalhost = false; - this._current_target = null; + this.currentTarget = null; this.targets = null; // will be used when targets are not provided } @@ -65,9 +65,13 @@ class DiscoveryService extends ServiceAction { throw Error('targets parameter is not an array'); } + if (targets.length < 1) { + throw Error('No targets provided'); + } + for (const discoverer of targets) { - if (discoverer.connected) { - logger.debug('%s - target is connected %s', method, discoverer.name); + if (discoverer.connected || discoverer.isConnectable()) { + logger.debug('%s - target is or could be connected %s', method, discoverer.name); } else { throw Error(`Discoverer ${discoverer.name} is not connected`); } @@ -286,7 +290,7 @@ class DiscoveryService extends ServiceAction { logger.debug(`${method} - about to discover on ${target.endpoint.url}`); try { response = await target.sendDiscovery(signedEnvelope, this.requestTimeout); - this._current_target = target; + this.currentTarget = target; break; } catch (error) { response = error; @@ -610,7 +614,7 @@ class DiscoveryService extends ServiceAction { const host_port = address.split(':'); const url = this._buildUrl(host_port[0], host_port[1]); logger.debug(`${method} - create a new endorser ${url}`); - const peer = this.client.getEndorser(address, msp_id); + const peer = this.client.newEndorser(address, msp_id); const end_point = this.client.newEndpoint(this._buildOptions(address, url, host_port[0], msp_id)); try { logger.debug(`${method} - about to connect to endorser ${address} url:${url}`); @@ -638,8 +642,8 @@ class DiscoveryService extends ServiceAction { // discovery should also use TLS. let protocol = null; let isTLS = true; - if (this._current_target) { - isTLS = this._current_target.endpoint.isTLS(); + if (this.currentTarget) { + isTLS = this.currentTarget.endpoint.isTLS(); } protocol = isTLS ? 'grpcs' : 'grpc'; // but if not, use the following to override diff --git a/fabric-common/lib/Endorsement.js b/fabric-common/lib/Endorsement.js index da9144e6ae..c520bf6add 100644 --- a/fabric-common/lib/Endorsement.js +++ b/fabric-common/lib/Endorsement.js @@ -25,13 +25,13 @@ class Endorsement extends Proposal { /** * Construct a Proposal object. * - * @param {string} chaincodeName - The chaincode this proposal will execute + * @param {string} chaincodeId - The chaincode this proposal will execute * @param {Channel} channel - The channel of this proposal * @returns {Proposal} The Proposal instance. */ - constructor(chaincodeName = checkParameter('chaincodeName'), channel = checkParameter('channel')) { - super(chaincodeName, channel); - const method = `constructor[${chaincodeName}]`; + constructor(chaincodeId = checkParameter('chaincodeId'), channel = checkParameter('channel')) { + super(chaincodeId, channel); + const method = `constructor[${chaincodeId}]`; logger.debug('%s - start', method); this.type = TYPE; } @@ -43,7 +43,7 @@ class Endorsement extends Proposal { const method = `newCommit[${this.name}]`; logger.debug(`${method} - start`); - return new Commit(this.chaincodeName, this.channel, this); + return new Commit(this.chaincodeId, this.channel, this); } /** @@ -51,7 +51,7 @@ class Endorsement extends Proposal { */ toString() { - return `Endorsement: {chaincodeName: ${this.chaincodeName}, channel: ${this.channel.name}}`; + return `Endorsement: {chaincodeId: ${this.chaincodeId}, channel: ${this.channel.name}}`; } } diff --git a/fabric-common/lib/Endorser.js b/fabric-common/lib/Endorser.js index 3f3dce2d8b..ddb9856c5b 100644 --- a/fabric-common/lib/Endorser.js +++ b/fabric-common/lib/Endorser.js @@ -61,7 +61,7 @@ class Endorser extends ServiceEndpoint { */ sendProposal(signedProposal, timeout) { const method = `sendProposal[${this.name}]`; - logger.debug(`${method} - Start ----${this.name} ${this.endpoint.url}`); + logger.debug(`${method} - Start ----${this.name} ${this.endpoint.url} timeout:${timeout}`); return new Promise((resolve, reject) => { if (!signedProposal) { @@ -77,7 +77,9 @@ class Endorser extends ServiceEndpoint { const send_timeout = setTimeout(() => { clearTimeout(send_timeout); logger.error(`${method} - ${this.name} timed out after:${rto}`); - return reject(new Error('REQUEST TIMEOUT')); + const return_error = new Error('REQUEST TIMEOUT'); + this.getCharacteristics(return_error); + return reject(return_error); }, rto); this.service.processProposal(signedProposal, (err, proposalResponse) => { @@ -85,28 +87,28 @@ class Endorser extends ServiceEndpoint { if (err) { logger.error(`${method} - Received error response from: ${this.endpoint.url} error: ${err}`); if (err instanceof Error) { - err.connection = this.getCharacteristics(); + this.getCharacteristics(err); reject(err); } else { const out_error = new Error(err); - out_error.connection = this.getCharacteristics(); + this.getCharacteristics(out_error); reject(out_error); } } else { if (proposalResponse) { logger.debug(`${method} - Received proposal response from peer "${this.endpoint.url}": status - ${proposalResponse.response && proposalResponse.response.status}`); if (proposalResponse.response && proposalResponse.response.status) { - proposalResponse.connection = this.getCharacteristics(); + this.getCharacteristics(proposalResponse); resolve(proposalResponse); } else { const return_error = new Error(`GRPC service failed to get a proper response from the peer "${this.endpoint.url}".`); - return_error.connection = this.getCharacteristics(); + this.getCharacteristics(return_error); logger.error(`${method} - rejecting with:${return_error}`); reject(return_error); } } else { const return_error = new Error(`GRPC service got a null or undefined response from the peer "${this.endpoint.url}".`); - return_error.connection = this.getCharacteristics(); + this.getCharacteristics(return_error); logger.error(`${method} - rejecting with:${return_error}`); reject(return_error); } diff --git a/fabric-common/lib/EventListener.js b/fabric-common/lib/EventListener.js index 0dad63dd47..d9b649c9ce 100644 --- a/fabric-common/lib/EventListener.js +++ b/fabric-common/lib/EventListener.js @@ -23,6 +23,7 @@ class EventListener { /* * Constructs a Event Listener * + * @param {EventService} eventService - The EventService where this listener is registered * @param {string} listenerType - a string to indicate the type of event registration * "block", "tx", or "chaincode". * @param {function} callback - Callback for event matches @@ -43,7 +44,8 @@ class EventListener { * @param {string} [chaincodeId] - optional - used to isolate chaincode events * to a specific chaincode. */ - constructor(listenerType = checkParameter('listenerType'), callback = checkParameter('callback'), options, event, chaincodeId) { + constructor(eventService = checkParameter('eventService'), listenerType = checkParameter('listenerType'), callback = checkParameter('callback'), options, event, chaincodeId) { + this.eventService = eventService; this.type = TYPE; this.listenerType = listenerType; if (listenerType === EventListener.TX && !event) { @@ -97,6 +99,13 @@ class EventListener { } } + /** + * Convenience method to for users to unregister this listener + */ + unregisterEventListener() { + this.eventService.unregisterEventListener(this); + } + toString() { return `EventListener: { listenerType: ${this.listenerType}, startBlock: ${ this.startBlock}, endBlock: ${this.endBlock}, unregister: ${ diff --git a/fabric-common/lib/EventService.js b/fabric-common/lib/EventService.js index 08d342d7af..f24f01780e 100644 --- a/fabric-common/lib/EventService.js +++ b/fabric-common/lib/EventService.js @@ -65,6 +65,7 @@ class EventService extends ServiceAction { logger.debug(`${TYPE}.constructor[${name}] - start `); super(name); this.type = TYPE; + this.channel = channel; // the last block number received this.lastBlockNumber = null; @@ -84,11 +85,11 @@ class EventService extends ServiceAction { this._current_eventer = null; // closing state to case of multiple calls this._close_running = false; + // remember the blockType this EventService is listening // will be set during the .build call this.blockType = null; - - this.channel = channel; + this.replay = false; } /** @@ -106,11 +107,15 @@ class EventService extends ServiceAction { throw Error('targets parameter is not an array'); } + if (targets.length < 1) { + throw Error('No targets provided'); + } + for (const eventer of targets) { - if (eventer.connected) { - logger.debug('%s - target is connected %s', method, eventer.name); + if (eventer.connected || eventer.isConnectable()) { + logger.debug('%s - target is or could be connected %s', method, eventer.name); } else { - throw Error(`Eventer ${eventer.name} is not connected`); + throw Error(`Eventer ${eventer.name} is not connectable`); } } // must be all targets are connected @@ -216,6 +221,8 @@ class EventService extends ServiceAction { const {startBlock, endBlock, blockType = FILTERED_BLOCK} = options; this.startBlock = this._checkBlockNum(startBlock); this.endBlock = this._checkBlockNum(endBlock); + + // when they are both Longs if (this.startBlock && this.endBlock && this.endBlock.greaterThan && this.startBlock.greaterThan) { if (this.startBlock.greaterThan(this.endBlock)) { throw Error('"startBlock" must not be greater than "endBlock"'); @@ -248,10 +255,12 @@ class EventService extends ServiceAction { } else if (this.startBlock === OLDEST) { const seekOldest = new fabprotos.orderer.SeekOldest(); seekStart.setOldest(seekOldest); + this.replay = true; } else if (this.startBlock) { const seekSpecifiedStart = new fabprotos.orderer.SeekSpecified(); seekSpecifiedStart.setNumber(this.startBlock); seekStart.setSpecified(seekSpecifiedStart); + this.replay = true; } // build stop proto @@ -260,16 +269,19 @@ class EventService extends ServiceAction { const seekNewest = new fabprotos.orderer.SeekNewest(); seekStop.setNewest(seekNewest); behavior = fabprotos.orderer.SeekInfo.SeekBehavior.FAIL_IF_NOT_READY; + this.replay = true; } else if (this.endBlock === OLDEST) { const seekOldest = new fabprotos.orderer.SeekOldest(); seekStop.setOldest(seekOldest); behavior = fabprotos.orderer.SeekInfo.SeekBehavior.FAIL_IF_NOT_READY; + this.replay = true; } else { const seekSpecifiedStop = new fabprotos.orderer.SeekSpecified(); if (this.endBlock) { seekSpecifiedStop.setNumber(this.endBlock); // user should be told that the block does not exist behavior = fabprotos.orderer.SeekInfo.SeekBehavior.FAIL_IF_NOT_READY; + this.replay = true; } else { seekSpecifiedStop.setNumber(Long.MAX_VALUE); } @@ -321,32 +333,50 @@ class EventService extends ServiceAction { const method = `send[${this.name}]`; logger.debug(`${method} - start`); - const {targets = checkParameter('targets'), requestTimeout} = request; - const envelope = this.getSignedEnvelope(); - this._current_eventer = null; - let start_error = null; - this._end_block_seen = false; - - if (targets) { + const {targets, requestTimeout} = request; + if (targets && Array.isArray(targets) && targets.length > 0) { this.targets = targets; + logger.debug('%s - using user assigned targets', method); } else if (this.targets) { logger.debug('%s - using preassigned targets', method); } else { checkParameter('targets'); } + const envelope = this.getSignedEnvelope(); + this._current_eventer = null; + let start_error = null; + this._end_block_seen = false; - for (const target of targets) { + for (const target of this.targets) { try { - this._current_eventer = await this._startService(target, envelope, requestTimeout); + if (target.stream) { + start_error = Error(`Event service ${target.name} is currently listening`); + } else { + if (target.isConnectable()) { + await target.connect(); // target endpoint has been previously assigned, but not connected yet + } + const isConnected = await target.checkConnection(); + if (!isConnected) { + start_error = Error(`Event service ${target.name} is not connected`); + } else { + this._current_eventer = await this._startService(target, envelope, requestTimeout); + } + } } catch (error) { logger.error('%s - Starting stream to %s failed', method, target.name); start_error = error; } + + // let see how we did with this target if (this._current_eventer) { + // great, it will be the one we use, stop looking start_error = null; break; } } + + // if we ran through the all targets and have start_error then we + // have not found a working target endpoint, so tell user error if (start_error) { throw start_error; } @@ -360,15 +390,6 @@ class EventService extends ServiceAction { const method = `_startService[${this.name}]`; return new Promise((resolve, reject) => { - if (eventer.stream) { - reject(Error(`Event service ${eventer.name} is currently listening`)); - return; - } - if (!eventer.checkConnection()) { - reject(Error(`Event service ${eventer.name} is not connected`)); - return; - } - if (!requestTimeout) { requestTimeout = eventer.endpoint.options.requestTimeout; } @@ -567,8 +588,8 @@ class EventService extends ServiceAction { * Use this method to indicate if this event service has event listeners * {@link EventListener} assigned and waiting for an event. */ - isListening() { - const method = `isListening[${this.name}]`; + hasListeners() { + const method = `hasListeners[${this.name}]`; logger.debug(`${method} - start`); if (this._eventListenerRegistrations.size > 0) { @@ -654,14 +675,22 @@ class EventService extends ServiceAction { * the register listener methods. * * @param {EventListener} eventListener - The registered listener. + * @param {boolean} [notThrow] - When the listener is not found an error + * will be thrown when not included or false */ - unregisterEventListener(eventListener = checkParameter('eventListener')) { + unregisterEventListener(eventListener = checkParameter('eventListener'), notThrow) { const method = `unregisterEventListener[${this.name}]`; logger.debug(`${method} - start - eventListener:${eventListener}`); if (this._eventListenerRegistrations.has(eventListener)) { this._eventListenerRegistrations.delete(eventListener); } else { - throw Error('eventListener not found'); + if (!notThrow) { + logger.error('%s - event listener was not found', method); + throw Error('eventListener not found'); + } else { + logger.debug('%s - event listener was not found', method); + return; // nothing to do + } } let found_block = false; @@ -714,7 +743,7 @@ class EventService extends ServiceAction { logger.debug(`${method} - start`); const event_name = new RegExp(eventName); - const event_reg = new EventListener(CHAINCODE, callback, options, event_name, chaincodeId); + const event_reg = new EventListener(this, CHAINCODE, callback, options, event_name, chaincodeId); this._eventListenerRegistrations.set(event_reg, event_reg); this._haveChaincodeListeners = true; @@ -742,8 +771,7 @@ class EventService extends ServiceAction { * has been seen. *
The Event will be the {@link Event} object. * @param {EventRegistrationOptions} options - Options on the registrations to allow - * for start and end block numbers, automatically unregister and - * automatically disconnect. + * for start and end block numbers or to automatically unregister * @returns {EventListener} The EventListener instance to be used to * remove this registration using {@link EventService#unregisterEvent}) */ @@ -751,7 +779,7 @@ class EventService extends ServiceAction { const method = `registerBlockListener[${this.name}]`; logger.debug(`${method} - start`); - const event_reg = new EventListener(BLOCK, callback, options, null); + const event_reg = new EventListener(this, BLOCK, callback, options, null); this._eventListenerRegistrations.set(event_reg, event_reg); this._haveBlockListeners = true; @@ -780,7 +808,7 @@ class EventService extends ServiceAction { * shutdown due to the last block being received if replaying and requesting * the endBlock to be 'newest' or a specific value. * @param {EventRegistrationOptions} options - Options on the registrations to allow - * for start and end block numbers, automatically unregister. + * for start and end block numbers or to automatically unregister. * @returns {EventListener} The EventListener instance to be used to * remove this registration using {@link EventService#unregisterEvent}) */ @@ -802,13 +830,39 @@ class EventService extends ServiceAction { } } - const event_reg = new EventListener(TX, callback, send_options, _txid); + const event_reg = new EventListener(this, TX, callback, send_options, _txid); this._eventListenerRegistrations.set(event_reg, event_reg); this._haveTxListeners = true; return event_reg; } + /** + * Utility method to find an event listener for a specific transaction ID + * + * @param {string} txid - the transaction ID of the event listener + * being searched. + * @return {EventListener} The EventListener for the transaction ID provided + */ + getTransactionListener(txid = checkParameter('txid')) { + const method = `getTransactionListener[${this.name}]`; + logger.debug('%s - start', method); + let result = null; + + for (const trans_reg of this._eventListenerRegistrations.values()) { + // check each listener to see if this transaction ID matches + if (trans_reg.listenerType === TX) { + if (trans_reg.event === txid) { + logger.debug(`${method} - found the listener for ${txid}`); + result = trans_reg; + break; + } + } + } + + return result; + } + /* * private internal method to check each registered listener * to see if it has requested to stop listening on a specific @@ -825,7 +879,7 @@ class EventService extends ServiceAction { event.endBlockReceived = true; event.blockNumber = block_num; listener.onEvent(null, event); - this.unregisterEventListener(listener); + this.unregisterEventListener(listener, true); logger.debug('%s - automatically unregister %s, end block: %s has been seen', method, listener, block_num); } else { logger.debug('%s - %s, end block: %s not seen', method, listener, block_num); @@ -863,8 +917,8 @@ class EventService extends ServiceAction { // check to see if we should automatically unregister if (block_reg.unregister) { - this.unregisterEventListener(block_reg); logger.debug(`${method} - automatically unregister block listener for ${block_reg}`); + this.unregisterEventListener(block_reg, true); } } } @@ -924,8 +978,8 @@ class EventService extends ServiceAction { // check to see if we should automatically unregister if (trans_reg.unregister) { - this.unregisterEventListener(trans_reg); logger.debug(`${method} - automatically unregister tx listener for ${tx_id}`); + this.unregisterEventListener(trans_reg, true); } } else { logger.debug('%s - tx listener for %s - not called', method, trans_reg.event); @@ -1011,8 +1065,8 @@ class EventService extends ServiceAction { // see if we should automatically unregister this event listener if (chaincode_reg.unregister) { - this.unregisterEventListener(chaincode_reg); logger.debug(`${method} - automatically unregister chaincode event listener setting`); + this.unregisterEventListener(chaincode_reg, true); } } diff --git a/fabric-common/lib/Proposal.js b/fabric-common/lib/Proposal.js index fc44cd4093..d9bc302c0e 100644 --- a/fabric-common/lib/Proposal.js +++ b/fabric-common/lib/Proposal.js @@ -14,7 +14,7 @@ const ServiceAction = require('./ServiceAction.js'); /** * @classdesc - * This is an abstract class represents a Proposal definition and the + * This is an base class represents a Proposal definition and the * base for actions on a proposal. * This class allows an application to contain all proposal attributes and * artifacts in one place during runtime. Use the {@link Endorsement} @@ -27,16 +27,16 @@ class Proposal extends ServiceAction { /** * Construct a Proposal object. * - * @param {string} chaincodeName - The chaincode this proposal will execute + * @param {string} chaincodeId - The chaincode this proposal will execute * @param {Channel} channel - The channel of this proposal * @returns {Proposal} The Proposal instance. */ - constructor(chaincodeName = checkParameter('chaincodeName'), channel = checkParameter('channel')) { - super(chaincodeName); - logger.debug(`${TYPE}.constructor[${chaincodeName}] - start `); + constructor(chaincodeId = checkParameter('chaincodeId'), channel = checkParameter('channel')) { + super(chaincodeId); + logger.debug(`${TYPE}.constructor[${chaincodeId}] - start `); this.type = TYPE; - this.chaincodeName = chaincodeName; + this.chaincodeId = chaincodeId; this.channel = channel; this.collectionsInterest = []; this.chaincodesCollectionsInterest = []; @@ -48,7 +48,7 @@ class Proposal extends ServiceAction { * @returns {string} The transaction ID of the proposal */ getTransactionId() { - const method = `getTransactionId[${this.chaincodeName}]`; + const method = `getTransactionId[${this.chaincodeId}]`; logger.debug('%s - start', method); if (!this._action || !this._action.transactionId) { throw Error('The proposal has not been built'); @@ -72,13 +72,13 @@ class Proposal extends ServiceAction { * ] */ buildProposalInterest() { - const method = `buildProposalInterest[${this.chaincodeName}]`; + const method = `buildProposalInterest[${this.chaincodeId}]`; logger.debug('%s - start', method); let interest = []; const chaincode = {}; interest.push(chaincode); - chaincode.name = this.chaincodeName; + chaincode.name = this.chaincodeId; if (this.collectionsInterest.length > 0) { chaincode.collectionNames = this.collectionsInterest; } @@ -96,7 +96,7 @@ class Proposal extends ServiceAction { * @param {string} collectionName - collection name */ addCollectionInterest(collectionName) { - const method = `addCollectionInterest[${this.chaincodeName}]`; + const method = `addCollectionInterest[${this.chaincodeId}]`; logger.debug('%s - start', method); if (typeof collectionName === 'string') { this.collectionsInterest.push(collectionName); @@ -111,21 +111,21 @@ class Proposal extends ServiceAction { * Use this method to add a chaincode name and collection names * that this proposal's chaincode will call. These will be used * to build a Discovery interest. {@link Proposal#buildProposalInterest} - * @param {string} chaincodeName - chaincode name + * @param {string} chaincodeId - chaincode name * @param {...string} collectionNames - one or more collection names */ - addChaincodeCollectionsInterest(chaincodeName, ...collectionNames) { - const method = `addChaincodeCollectionsInterest[${this.chaincodeName}]`; + addChaincodeCollectionsInterest(chaincodeId, ...collectionNames) { + const method = `addChaincodeCollectionsInterest[${this.chaincodeId}]`; logger.debug('%s - start', method); - if (typeof chaincodeName === 'string') { + if (typeof chaincodeId === 'string') { const added_chaincode = {}; - added_chaincode.name = chaincodeName; + added_chaincode.name = chaincodeId; if (collectionNames && collectionNames.length > 0) { added_chaincode.collectionNames = collectionNames; } this.chaincodesCollectionsInterest.push(added_chaincode); } else { - throw Error('Invalid chaincodeName parameter'); + throw Error('Invalid chaincodeId parameter'); } return this; @@ -160,7 +160,7 @@ class Proposal extends ServiceAction { * @param {BuildProposalRequest} request - The proposals values of the request. */ build(idContext = checkParameter('idContext'), request = {}) { - const method = `build[${this.chaincodeName}][${this.type}]`; + const method = `build[${this.chaincodeId}][${this.type}]`; logger.debug('%s - start', method); const {fcn, args = [], transientMap, init} = request; @@ -184,25 +184,28 @@ class Proposal extends ServiceAction { if (fcn) { this._action.fcn = fcn; this._action.args.push(Buffer.from(this._action.fcn, 'utf8')); - logger.debug('%s - adding function arg:%s', method, this._action.fcn); + logger.debug('%s - adding function %s', method, this._action.fcn); } else { - logger.debug('%s - not adding a function arg:%s', method); + logger.debug('%s - not adding function', method); } for (let i = 0; i < args.length; i++) { logger.debug('%s - adding arg %s', method, args[i]); - if (typeof args[i] === 'string') { - this._action.args.push(Buffer.from(args[i], 'utf8')); + let arg; + if (args[i] instanceof Buffer) { + arg = args[i]; } else { - this._action.args.push(args[i]); + const arg_as_string = args[i].toString(); + arg = Buffer.from(arg_as_string, 'utf8'); } + this._action.args.push(arg); } - logger.debug('%s - chaincode ID:%s', method, this._action.chaincodeName); + logger.debug('%s - chaincode ID:%s', method, this.chaincodeId); const chaincodeSpec = new fabprotos.protos.ChaincodeSpec(); chaincodeSpec.setType(fabprotos.protos.ChaincodeSpec.Type.GOLANG); const chaincode_id = new fabprotos.protos.ChaincodeID(); - chaincode_id.setName(this.chaincodeName); + chaincode_id.setName(this.chaincodeId); chaincodeSpec.setChaincodeId(chaincode_id); const input = new fabprotos.protos.ChaincodeInput(); input.setArgs(this._action.args); @@ -213,7 +216,7 @@ class Proposal extends ServiceAction { const channelHeader = this.channel.buildChannelHeader( fabprotos.common.HeaderType.ENDORSER_TRANSACTION, - this.chaincodeName, + this.chaincodeId, this._action.transactionId ); @@ -320,9 +323,10 @@ message Endorsement { * @returns {ProposalResponse} The results of sending */ async send(request = {}) { - const method = `send[${this.chaincodeName}]`; + const method = `send[${this.chaincodeId}]`; logger.debug('%s - start', method); const {handler, targets, requestTimeout} = request; + logger.debug('%s - requestTimeout %s', method, requestTimeout); const signedEnvelope = this.getSignedProposal(); this._proposalResponses = []; this._proposalErrors = []; @@ -419,7 +423,7 @@ message Endorsement { * the signature are valid, false otherwise. */ verifyProposalResponse(proposalResponse = checkParameter('proposalResponse')) { - const method = `verifyProposalResponse[${this.chaincodeName}]`; + const method = `verifyProposalResponse[${this.chaincodeId}]`; logger.debug('%s - start', method); if (proposalResponse instanceof Error) { @@ -496,7 +500,7 @@ message Endorsement { * @returns {boolean} True when all proposals compare equally, false otherwise. */ compareProposalResponseResults(proposalResponses = checkParameter('proposalResponses')) { - const method = `compareProposalResponseResults[${this.chaincodeName}]`; + const method = `compareProposalResponseResults[${this.chaincodeId}]`; logger.debug('%s - start', method); if (!Array.isArray(proposalResponses)) { @@ -531,7 +535,7 @@ message Endorsement { */ toString() { - return `Proposal: {chaincodeName: ${this.chaincodeName}, channel: ${this.channel.name}}`; + return `Proposal: {chaincodeId: ${this.chaincodeId}, channel: ${this.channel.name}}`; } } diff --git a/fabric-common/lib/Query.js b/fabric-common/lib/Query.js index f962c6bbc4..4ae17c5911 100644 --- a/fabric-common/lib/Query.js +++ b/fabric-common/lib/Query.js @@ -24,14 +24,14 @@ class Query extends Proposal { /** * Construct a Proposal object. * - * @param {string} chaincodeName - The chaincode this proposal will execute + * @param {string} chaincodeId - The chaincode this proposal will execute * @param {Channel} channel - The channel of this proposal * @returns {Proposal} The Proposal instance. */ - constructor(chaincodeName = checkParameter('chaincodeName'), channel = checkParameter('channel')) { - super(chaincodeName, channel); + constructor(chaincodeId = checkParameter('chaincodeId'), channel = checkParameter('channel')) { + super(chaincodeId, channel); - const method = `constructor[${chaincodeName}]`; + const method = `constructor[${chaincodeId}]`; logger.debug('%s - start', method); this.type = TYPE; } @@ -41,7 +41,7 @@ class Query extends Proposal { */ toString() { - return `Query: {chaincodeName: ${this.chaincodeName}, channel: ${this.channel.name}}`; + return `Query: {chaincodeId: ${this.chaincodeId}, channel: ${this.channel.name}}`; } } diff --git a/fabric-common/lib/ServiceAction.js b/fabric-common/lib/ServiceAction.js index 05936586e2..f27b198639 100644 --- a/fabric-common/lib/ServiceAction.js +++ b/fabric-common/lib/ServiceAction.js @@ -13,14 +13,14 @@ const fabprotos = require('fabric-protos'); /** * @classdesc - * This is an abstract class that represents an action on a fabric service. + * This is an base class that represents an action on a fabric service. * * @class */ const ServiceAction = class { /** - * Construct a ServiceAction abstract object. + * Construct a ServiceAction base object. * * @returns {ServiceAction} The ServiceAction instance. */ diff --git a/fabric-common/lib/ServiceEndpoint.js b/fabric-common/lib/ServiceEndpoint.js index f032b0daf7..ae96c3943e 100644 --- a/fabric-common/lib/ServiceEndpoint.js +++ b/fabric-common/lib/ServiceEndpoint.js @@ -21,6 +21,7 @@ class ServiceEndpoint { this.mspid = mspid; this.client = client; this.connected = false; + this.connectAttempted = false; this.endpoint = null; this.service = null; this.serviceClass = null; @@ -28,16 +29,68 @@ class ServiceEndpoint { } /** - * Connects to a ServiceEndpoint with the given url and opts. - * If a connect exist an error will be thrown. The application must - * disconnect the connection before re-connecting to the service. + * Use this method to give this service endpoint an endpoint and + * options that it may connect to at a later time. Use the {@link ServiceEndpoint#connect} + * method without a endpoint or options to connect using the setting provided here. * * @param {Endpoint} endpoint - Service connection options including the url. * @param {ConnectionOptions} options - Any specific options for this instance * of the connection to the peer. These will override options from the * endpoint service connection options. */ - async connect(endpoint = checkParameter('endpoint'), options = {}) { + setEndpoint(endpoint = checkParameter('endpoint'), options = {}) { + const method = `setEndpoint[${this.type}-${this.name}]`; + logger.debug(`${method} - start `); + + if (this.endpoint && this.connected) { + const message = `This service endpoint ${this.name}-${this.endpoint.url} is connected`; + logger.error(message); + throw Error(message); + } + + this.endpoint = endpoint; + this.connectAttempted = false; + this.options = Object.assign({}, endpoint.options, options); + logger.debug(`${method} - endpoint has been set for ${this.name}-${this.endpoint.url}`); + } + + /** + * Check that this ServiceEndpoint is not connected and has been assigned + * an endpoint so that it could be connected. If a previous attempt + * to conntect has been tried unsuccessfully it will be considered + * not to be connectable. + */ + isConnectable() { + const method = `isConnectable[${this.type}-${this.name}]`; + logger.debug(`${method} - start `); + + let result = false; + if (this.connected) { + logger.debug(`${method} - this servive endpoint has been connected`); + result = false; + } else if (this.endpoint && !this.connectAttempted) { + logger.debug(`${method} - this service endpoint has been assigned an endpoint, connect may be run`); + result = true; + } + + return result; + } + + /** + * Connects this ServiceEndpoint with the given url and opts. + * If a connect exist an error will be thrown. The application must + * disconnect the connection before re-connecting to the service. + * + * @param {Endpoint} [endpoint] - Service connection options including the url. + * When an endpoint is not provided, the setEndpoint() must have been called + * previously. If setEndpoint was previously call and a endpoint is provided + * here then it will replace the existing endpoint. + * @param {ConnectionOptions} [options] - Any specific options for this instance + * of the connection to the peer. These will override options from the + * endpoint service connection options. Endpoint options and option provided + * here will replace options from the setEndpoint() if previously called. + */ + async connect(endpoint, options = {}) { const method = `connect[${this.type}-${this.name}]`; logger.debug(`${method} - start `); @@ -48,18 +101,26 @@ class ServiceEndpoint { } if (this.service) { - const message = `This service endpoint ${this.name}-${this.endpoint.url} has an active service connection`; + const message = `This service endpoint ${this.name}-${this.endpoint.url} has an active grpc service connection`; logger.error(message); throw Error(message); } - this.endpoint = endpoint; - this.options = Object.assign({}, endpoint.options, options); + if (!endpoint && !this.endpoint) { + checkParameter('endpoint'); + } - logger.debug(`${method} - endorser service does not exist, will create service for this peer ${this.name}`); + if (endpoint) { + this.endpoint = endpoint; + } + + this.options = Object.assign({}, this.endpoint.options, options); + + this.connectAttempted = true; + logger.debug(`${method} - create the grpc service for ${this.name}`); this.service = new this.serviceClass(this.endpoint.addr, this.endpoint.creds, this.options); await this.waitForReady(this.service); - logger.debug(`${method} - completed the waitForReady for this peer ${this.name}`); + logger.debug(`${method} - completed the waitForReady for ${this.name}`); } /** @@ -70,7 +131,7 @@ class ServiceEndpoint { logger.debug(`${method} - start `); if (this.service) { - logger.debug(`${method} ${this.type} ${this.name} - closing service connection ${this.endpoint.addr}`); + logger.debug(`${method} ${this.type} ${this.name} - closing grpc service connection ${this.endpoint.addr}`); this.service.close(); this.service = null; this.connected = false; @@ -128,19 +189,18 @@ class ServiceEndpoint { /* * Get this remote endpoints characteristics */ - getCharacteristics() { - const characteristics = { - type: this.type, - name: this.name, - url: this.endpoint ? this.endpoint.url : '', - options: this.endpoint ? this.endpoint.options : {} - }; + getCharacteristics(results) { + results.connection = {}; + results.connection.type = this.type; + results.connection.name = this.name; + results.connection.url = this.endpoint ? this.endpoint.url : ''; + results.connection.options = this.endpoint ? this.endpoint.options : {}; + results.peer = this.name; + // remove private key - if (characteristics.options.clientKey) { - delete characteristics.options.clientKey; + if (results.connection.options.clientKey) { + delete results.connection.options.clientKey; } - - return characteristics; } /** diff --git a/fabric-common/lib/ServiceHandler.js b/fabric-common/lib/ServiceHandler.js index 2b098158a5..a3b2b7d825 100644 --- a/fabric-common/lib/ServiceHandler.js +++ b/fabric-common/lib/ServiceHandler.js @@ -11,14 +11,14 @@ const logger = getLogger(TYPE); /** * @classdesc - * This is an abstract class that represents an action on a fabric service. + * This is an base class that represents an action on a fabric service. * * @class */ const ServiceHandler = class { /** - * Construct a ServiceHandler abstract object. + * Construct a ServiceHandler base object. * * @returns {ServiceHandler} The ServiceHandler instance. */ diff --git a/fabric-common/test/BaseClient.js b/fabric-common/test/BaseClient.js new file mode 100644 index 0000000000..f2eedf0355 --- /dev/null +++ b/fabric-common/test/BaseClient.js @@ -0,0 +1,354 @@ +/** + * Copyright 2019 IBM All Rights Reserved. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +'use strict'; + +const rewire = require('rewire'); +const {BaseClient} = require('fabric-common'); +const BaseClientRewire = rewire('fabric-common/lib/BaseClient'); + +const chai = require('chai'); +const sinon = require('sinon'); +const should = chai.should(); + +describe('BaseClient', () => { + + describe('#constructor', () => { + + it('should set `_cryptoSuite` to null', () => { + const client = new BaseClient(); + should.equal(client._cryptoSuite, null); + }); + }); + + describe('#BaseClient.newCryptoSuite', () => { + + const sandbox = sinon.createSandbox(); + + afterEach(() => { + sandbox.restore(); + }); + + it('should call `sdkUtils.newCryptoSuite` with passed parameters and return result', () => { + const sdkUtilsStub = sandbox.stub(); + const newCryptoSuiteStub = sandbox.stub().returns('newCryptoSuite'); + sdkUtilsStub.newCryptoSuite = newCryptoSuiteStub; + BaseClientRewire.__set__('sdkUtils', sdkUtilsStub); + + const result = BaseClientRewire.newCryptoSuite('setting'); + + result.should.equal('newCryptoSuite'); + sinon.assert.calledOnce(newCryptoSuiteStub); + sinon.assert.calledWith(newCryptoSuiteStub, 'setting'); + }); + }); + + describe('#BaseClient.newCryptoKeyStore', () => { + + const sandbox = sinon.createSandbox(); + + afterEach(() => { + sandbox.restore(); + }); + + it('should call `sdkUtils.newCryptoKeyStore` with passed parameters and return result', () => { + const sdkUtilsStub = sandbox.stub(); + const newCryptoKeyStoreStub = sandbox.stub().returns('newCryptoKeyStore'); + sdkUtilsStub.newCryptoKeyStore = newCryptoKeyStoreStub; + BaseClientRewire.__set__('sdkUtils', sdkUtilsStub); + + const result = BaseClientRewire.newCryptoKeyStore('setting'); + + result.should.equal('newCryptoKeyStore'); + sinon.assert.calledOnce(newCryptoKeyStoreStub); + sinon.assert.calledWith(newCryptoKeyStoreStub, 'setting'); + }); + }); + + describe('#BaseClient.newDefaultKeyValueStore', () => { + + const sandbox = sinon.createSandbox(); + + afterEach(() => { + sandbox.restore(); + }); + + it('should call `sdkUtils.newKeyValueStore` with passed parameters and return result', async () => { + const sdkUtilsStub = sandbox.stub(); + const newDefaultKeyValueStoreStub = sandbox.stub().resolves('newDefaultKeyValueStore'); + sdkUtilsStub.newKeyValueStore = newDefaultKeyValueStoreStub; + BaseClientRewire.__set__('sdkUtils', sdkUtilsStub); + + const result = await BaseClientRewire.newDefaultKeyValueStore('setting'); + + result.should.equal('newDefaultKeyValueStore'); + sinon.assert.calledOnce(newDefaultKeyValueStoreStub); + sinon.assert.calledWith(newDefaultKeyValueStoreStub, 'setting'); + }); + }); + + describe('#BaseClient.setLogger', () => { + + const sandbox = sinon.createSandbox(); + + afterEach(() => { + sandbox.restore(); + }); + + it('should throw if passed logger does not implement any methods', () => { + (() => { + BaseClient.setLogger({}); + }).should.throw(/The "logger" parameter must be an object that implements the following methods, which are missing: debug\(\) info\(\) warn\(\) error\(\)/); + }); + + it('should throw if passed logger does not implement debug', () => { + (() => { + const fakelog = sinon.stub(); + fakelog.info = sinon.stub(); + fakelog.warn = sinon.stub(); + fakelog.error = sinon.stub(); + BaseClient.setLogger(fakelog); + }).should.throw(/The "logger" parameter must be an object that implements the following methods, which are missing: debug\(\)/); + }); + + it('should throw if passed logger does not implement info', () => { + (() => { + const fakelog = sinon.stub(); + fakelog.debug = sinon.stub(); + fakelog.warn = sinon.stub(); + fakelog.error = sinon.stub(); + BaseClient.setLogger(fakelog); + }).should.throw(/The "logger" parameter must be an object that implements the following methods, which are missing: info\(\)/); + }); + + it('should throw if passed logger does not implement warn', () => { + (() => { + const fakelog = sinon.stub(); + fakelog.debug = sinon.stub(); + fakelog.info = sinon.stub(); + fakelog.error = sinon.stub(); + BaseClient.setLogger(fakelog); + }).should.throw(/The "logger" parameter must be an object that implements the following methods, which are missing: warn\(\)/); + }); + + it('should throw if passed logger does not implement error', () => { + (() => { + const fakelog = sinon.stub(); + fakelog.debug = sinon.stub(); + fakelog.info = sinon.stub(); + fakelog.warn = sinon.stub(); + BaseClient.setLogger(fakelog); + }).should.throw(/The "logger" parameter must be an object that implements the following methods, which are missing: error\(\)/); + }); + + it('should overwrite the NodeJS.global logger if it already exists', () => { + const fakelog1 = sinon.stub(); + fakelog1.debug = sinon.stub().returns('original'); + + const fakelog = sinon.stub(); + fakelog.debug = sinon.stub().returns('replacement'); + fakelog.info = sinon.stub(); + fakelog.warn = sinon.stub(); + fakelog.error = sinon.stub(); + + global.hfc.logger = fakelog1; + BaseClient.setLogger(fakelog); + + global.hfc.logger.debug().should.equal('replacement'); + }); + + it('should set the NodeJS.global logger if it doesnt exist', () => { + + global.hfc = null; + + const fakelog = sinon.stub(); + fakelog.debug = sinon.stub().returns('new'); + fakelog.info = sinon.stub(); + fakelog.warn = sinon.stub(); + fakelog.error = sinon.stub(); + + BaseClient.setLogger(fakelog); + + global.hfc.logger.debug().should.equal('new'); + }); + }); + + describe('#BaseClient.getConfigSetting', () => { + + const sandbox = sinon.createSandbox(); + + afterEach(() => { + sandbox.restore(); + }); + + it('should call `sdkUtils.getConfigSetting` with passed parameters and return result', () => { + const sdkUtilsStub = sandbox.stub(); + const getConfigSettingStub = sandbox.stub().returns('getConfigSetting'); + sdkUtilsStub.getConfigSetting = getConfigSettingStub; + BaseClientRewire.__set__('sdkUtils', sdkUtilsStub); + + const result = BaseClientRewire.getConfigSetting('name', 'default_value'); + + result.should.equal('getConfigSetting'); + sinon.assert.calledOnce(getConfigSettingStub); + sinon.assert.calledWith(getConfigSettingStub, 'name', 'default_value'); + }); + }); + + describe('#getConfigSetting', () => { + + const sandbox = sinon.createSandbox(); + + afterEach(() => { + sandbox.restore(); + }); + + it('should call `BaseClient.getConfigSetting` with passed parameters and return result', () => { + const sdkUtilsStub = sandbox.stub(); + const getConfigSettingStub = sandbox.stub().returns('getConfigSetting'); + sdkUtilsStub.getConfigSetting = getConfigSettingStub; + BaseClientRewire.__set__('sdkUtils', sdkUtilsStub); + + + const client = new BaseClientRewire(); + const result = client.getConfigSetting('name', 'default_value'); + + result.should.equal('getConfigSetting'); + sinon.assert.calledOnce(getConfigSettingStub); + sinon.assert.calledWith(getConfigSettingStub, 'name', 'default_value'); + }); + }); + + describe('#BaseClient.addConfigFile', () => { + + const sandbox = sinon.createSandbox(); + + afterEach(() => { + sandbox.restore(); + }); + + it('should call `sdkUtils.addConfigFile` with passed parameters', () => { + const sdkUtilsStub = sandbox.stub(); + const addConfigFileStub = sandbox.stub(); + sdkUtilsStub.addConfigFile = addConfigFileStub; + BaseClientRewire.__set__('sdkUtils', sdkUtilsStub); + + BaseClientRewire.addConfigFile('path'); + sinon.assert.calledOnce(addConfigFileStub); + sinon.assert.calledWith(addConfigFileStub, 'path'); + }); + }); + + describe('#BaseClient.setConfigSetting', () => { + const sandbox = sinon.createSandbox(); + + afterEach(() => { + sandbox.restore(); + }); + + it('should call `sdkUtils.setConfigSetting` with passed parameters', () => { + const sdkUtilsStub = sandbox.stub(); + const setConfigSettingStub = sandbox.stub(); + sdkUtilsStub.setConfigSetting = setConfigSettingStub; + BaseClientRewire.__set__('sdkUtils', sdkUtilsStub); + + BaseClientRewire.setConfigSetting('name', 'value'); + sinon.assert.calledOnce(setConfigSettingStub); + sinon.assert.calledWith(setConfigSettingStub, 'name', 'value'); + }); + }); + + describe('#setConfigSetting', () => { + + const sandbox = sinon.createSandbox(); + + afterEach(() => { + sandbox.restore(); + }); + + it('should call `BaseClient.setConfigSetting` with passed parameters and return result', () => { + const sdkUtilsStub = sandbox.stub(); + const setConfigSettingStub = sandbox.stub(); + sdkUtilsStub.setConfigSetting = setConfigSettingStub; + BaseClientRewire.__set__('sdkUtils', sdkUtilsStub); + + const client = new BaseClientRewire(); + client.setConfigSetting('name', 'value'); + + sinon.assert.calledOnce(setConfigSettingStub); + sinon.assert.calledWith(setConfigSettingStub, 'name', 'value'); + }); + }); + + describe('#BaseClient.getLogger', () => { + + const sandbox = sinon.createSandbox(); + + afterEach(() => { + sandbox.restore(); + }); + + it('should call `sdkUtils.getLogger` with passed parameters and return result', () => { + const sdkUtilsStub = sandbox.stub(); + const getLoggerStub = sandbox.stub().returns('i am a logger'); + sdkUtilsStub.getLogger = getLoggerStub; + BaseClientRewire.__set__('sdkUtils', sdkUtilsStub); + + const result = BaseClientRewire.getLogger('name'); + + result.should.equal('i am a logger'); + sinon.assert.calledOnce(getLoggerStub); + sinon.assert.calledWith(getLoggerStub, 'name'); + }); + }); + + describe('#setCryptoSuite', () => { + + it('should set the CryptoSuite', () => { + const suite = 'theSuite'; + const client = new BaseClient(); + + client.setCryptoSuite(suite); + + client._cryptoSuite.should.equal(suite); + }); + }); + + describe('#getCryptoSuite', () => { + + it('should return the CryptoSuite', () => { + const suite = 'theSuite'; + const client = new BaseClient(); + + client._cryptoSuite = suite; + + client.getCryptoSuite().should.equal(suite); + }); + }); + + describe('#BaseClient.normalizeX509', () => { + + const sandbox = sinon.createSandbox(); + + afterEach(() => { + sandbox.restore(); + }); + + it('should call `sdkUtils.normalizeX509` with passed parameters and return result', () => { + const sdkUtilsStub = sandbox.stub(); + const normalizeX509Stub = sandbox.stub().returns('i am normal'); + sdkUtilsStub.normalizeX509 = normalizeX509Stub; + BaseClientRewire.__set__('sdkUtils', sdkUtilsStub); + + const result = BaseClientRewire.normalizeX509('raw'); + + result.should.equal('i am normal'); + sinon.assert.calledOnce(normalizeX509Stub); + sinon.assert.calledWith(normalizeX509Stub, 'raw'); + }); + }); + +}); diff --git a/fabric-common/test/Channel.js b/fabric-common/test/Channel.js index 97ec5023c3..b3359c7081 100644 --- a/fabric-common/test/Channel.js +++ b/fabric-common/test/Channel.js @@ -95,33 +95,33 @@ describe('Channel', () => { it('should require a chaincode name', () => { (() => { channel.newEndorsement(); - }).should.throw('Missing chaincodeName parameter'); + }).should.throw('Missing chaincodeId parameter'); }); it('should be able to create an endorsement', () => { - channel.newEndorsement('chaincodename'); + channel.newEndorsement('chaincodeId'); }); }); describe('#newQuery', () => { it('should require a chaincode name', () => { (() => { channel.newQuery(); - }).should.throw('Missing chaincodeName parameter'); + }).should.throw('Missing chaincodeId parameter'); }); it('should be able to create a query', () => { - channel.newQuery('chaincodename'); + channel.newQuery('chaincodeId'); }); }); describe('#newCommit', () => { it('should require a chaincode name', () => { (() => { channel.newCommit(); - }).should.throw('Missing chaincodeName parameter'); + }).should.throw('Missing chaincodeId parameter'); }); it('should be able to create a commit', () => { - channel.newCommit('chaincodename'); + channel.newCommit('chaincodeId'); }); }); describe('#newEventService', () => { diff --git a/fabric-common/test/Commit.js b/fabric-common/test/Commit.js index cff0cdf98f..b0c73238ca 100644 --- a/fabric-common/test/Commit.js +++ b/fabric-common/test/Commit.js @@ -47,10 +47,10 @@ describe('Commit', () => { }); describe('#constructor', () => { - it('should require a chaincodeName', () => { + it('should require a chaincodeId', () => { (() => { new Commit(); - }).should.throw('Missing chaincodeName parameter'); + }).should.throw('Missing chaincodeId parameter'); }); it('should require a Channel', () => { (() => { @@ -150,7 +150,7 @@ describe('Commit', () => { describe('#toString', () => { it('should return string', () => { const string = commit.toString(); - should.equal(string, 'Commit: {chaincodeName: chaincode, channel: mychannel}'); + should.equal(string, 'Commit: {chaincodeId: chaincode, channel: mychannel}'); }); }); }); diff --git a/fabric-common/test/DiscoveryService.js b/fabric-common/test/DiscoveryService.js index 61e0605cc2..9a19def57d 100644 --- a/fabric-common/test/DiscoveryService.js +++ b/fabric-common/test/DiscoveryService.js @@ -168,8 +168,14 @@ describe('DiscoveryService', () => { discovery.setTargets(discoverer); }).should.throw('targets parameter is not an array'); }); + it('should require targets when array is empty', () => { + (() => { + discovery.setTargets([]); + }).should.throw('No targets provided'); + }); it('should throw when target not connected', () => { (() => { + discoverer.endpoint = undefined; discovery.setTargets([discoverer]); }).should.throw('Discoverer mydiscoverer is not connected'); }); @@ -179,6 +185,12 @@ describe('DiscoveryService', () => { should.equal(discovery.targets[0].type, 'Discoverer'); should.equal(discovery.targets[0].name, 'mydiscoverer'); }); + it('should handle connectable target', () => { + discoverer.connected = false; + discovery.setTargets([discoverer]); + should.equal(discovery.targets[0].type, 'Discoverer'); + should.equal(discovery.targets[0].name, 'mydiscoverer'); + }); }); describe('#newHandler', () => { @@ -442,7 +454,7 @@ describe('DiscoveryService', () => { discovery._current_target = endorser; endorser.endpoint = endpoint; const results = discovery._buildUrl('hostname', 1000); - should.equal(results, 'grpc://hostname:1000'); + should.equal(results, 'grpcs://hostname:1000'); }); it('should handle override setting', () => { Client.setConfigSetting('discovery-override-protocol', 'grpcs'); diff --git a/fabric-common/test/Endorsement.js b/fabric-common/test/Endorsement.js index c56380333d..1918118c3b 100644 --- a/fabric-common/test/Endorsement.js +++ b/fabric-common/test/Endorsement.js @@ -32,7 +32,7 @@ describe('Endorsement', () => { it('should require a name', () => { (() => { new Endorsement(); - }).should.throw('Missing chaincodeName parameter'); + }).should.throw('Missing chaincodeId parameter'); }); it('should require a Channel', () => { (() => { @@ -55,7 +55,7 @@ describe('Endorsement', () => { describe('#toString', () => { it('should return string', () => { const string = endorsement.toString(); - should.equal(string, 'Endorsement: {chaincodeName: chaincode, channel: mychannel}'); + should.equal(string, 'Endorsement: {chaincodeId: chaincode, channel: mychannel}'); }); }); }); diff --git a/fabric-common/test/EventListener.js b/fabric-common/test/EventListener.js index dcbbadc1b4..13e35f68dc 100644 --- a/fabric-common/test/EventListener.js +++ b/fabric-common/test/EventListener.js @@ -9,6 +9,7 @@ const chai = require('chai'); const chaiAsPromised = require('chai-as-promised'); const should = chai.should(); chai.use(chaiAsPromised); +const sinon = require('sinon'); const {convertToLong} = require('../lib/Utils.js'); const EventListener = rewire('../lib/EventListener'); @@ -19,55 +20,60 @@ describe('EventListener', () => { it('should require a listenerType', () => { (() => { new EventListener(); + }).should.throw('Missing eventService parameter'); + }); + it('should require a listenerType', () => { + (() => { + new EventListener('eventService'); }).should.throw('Missing listenerType parameter'); }); it('should require a callback', () => { (() => { - new EventListener('block'); + new EventListener('eventService', 'block'); }).should.throw('Missing callback parameter'); }); it('should require an event', () => { (() => { - new EventListener('tx', {}); + new EventListener('eventService', 'tx', {}); }).should.throw('Missing event parameter'); }); it('should require an event', () => { (() => { - new EventListener('chaincode', {}); + new EventListener('eventService', 'chaincode', {}); }).should.throw('Missing event parameter'); }); it('should default block listener', () => { - const el = new EventListener('block', {}); + const el = new EventListener('eventService', 'block', {}); el.type.should.equal('EventListener'); el.unregister.should.be.false; }); it('should default tx listener', () => { - const el = new EventListener('tx', {}, {}, 'txid'); + const el = new EventListener('eventService', 'tx', {}, {}, 'txid'); el.type.should.equal('EventListener'); el.unregister.should.be.true; }); it('should default chaincode listener', () => { - const el = new EventListener('chaincode', {}, {}, 'event'); + const el = new EventListener('eventService', 'chaincode', {}, {}, 'event'); el.type.should.equal('EventListener'); el.unregister.should.be.false; }); it('should with option block listener', () => { - const el = new EventListener('block', {}, {unregister: true}); + const el = new EventListener('eventService', 'block', {}, {unregister: true}); el.type.should.equal('EventListener'); el.unregister.should.be.true; }); it('should with option tx listener', () => { - const el = new EventListener('tx', {}, {unregister: false}, 'txid'); + const el = new EventListener('eventService', 'tx', {}, {unregister: false}, 'txid'); el.type.should.equal('EventListener'); el.unregister.should.be.false; }); it('should with option chaincode listener', () => { - const el = new EventListener('chaincode', {}, {unregister: true}, 'event'); + const el = new EventListener('eventService', 'chaincode', {}, {unregister: true}, 'event'); el.type.should.equal('EventListener'); el.unregister.should.be.true; }); it('should set start and end of block listener', () => { - const el = new EventListener('block', {}, {startBlock: 33, endBlock: 44}); + const el = new EventListener('eventService', 'block', {}, {startBlock: 33, endBlock: 44}); el.type.should.equal('EventListener'); el.unregister.should.be.false; el.startBlock.should.be.deep.equal(convertToLong(33)); @@ -85,7 +91,7 @@ describe('EventListener', () => { let blockNumber = convertToLong(14); let transactionId = '1'; let transactionStatus = 'invalid'; - const eventListener = new EventListener('tx', (error, event) => { + const eventListener = new EventListener('eventService', 'tx', (error, event) => { blockNumber = event.blockNumber; transactionId = event.transactionId; transactionStatus = event.transactionStatus; @@ -112,7 +118,7 @@ describe('EventListener', () => { let blockNumber = convertToLong(9); let transactionId = '9'; let transactionStatus = 'invalid'; - const eventListener = new EventListener('tx', (error, event) => { + const eventListener = new EventListener('eventService', 'tx', (error, event) => { blockNumber = event.blockNumber; transactionId = event.transactionId; transactionStatus = event.transactionStatus; @@ -139,7 +145,7 @@ describe('EventListener', () => { let blockNumber = convertToLong(9); let transactionId = '9'; let transactionStatus = 'invalid'; - const eventListener = new EventListener('tx', (error, event) => { + const eventListener = new EventListener('eventService', 'tx', (error, event) => { blockNumber = event.blockNumber; transactionId = event.transactionId; transactionStatus = event.transactionStatus; @@ -159,7 +165,7 @@ describe('EventListener', () => { it('call the onEvent with an error', () => { let called_with_error = false; - const eventListener = new EventListener('tx', (error, event) => { + const eventListener = new EventListener('eventService', 'tx', (error, event) => { called_with_error = true; }, null, '12345'); @@ -169,7 +175,7 @@ describe('EventListener', () => { it('have the callback throw an error', () => { let called_with_error = false; - const eventListener = new EventListener('tx', (error, event) => { + const eventListener = new EventListener('eventService', 'tx', (error, event) => { called_with_error = true; throw Error('callback error'); }, null, '12345'); @@ -184,16 +190,25 @@ describe('EventListener', () => { }); }); + describe('#unregister', () => { + it('should run', () => { + const eventService = sinon.stub(); + eventService.unregisterEventListener = sinon.stub(); + const eventListener = new EventListener(eventService, 'tx', {}, {}, '1AB34'); + eventListener.unregisterEventListener(); + }); + }); + describe('#toString', () => { it('should return string', () => { - const eventListener = new EventListener('tx', {}, {}, '1AB34'); + const eventListener = new EventListener('eventService', 'tx', {}, {}, '1AB34'); const string = eventListener.toString(); should.equal(string, 'EventListener: { listenerType: tx, startBlock: null, endBlock: null, unregister: true, event: 1AB34}'); }); it('should return string', () => { - const eventListener = new EventListener('chaincode', {}, {}, 'event'); + const eventListener = new EventListener('eventService', 'chaincode', {}, {}, 'event'); const string = eventListener.toString(); should.equal(string, 'EventListener: { listenerType: chaincode, startBlock: null, endBlock: null, unregister: false, event: event}'); diff --git a/fabric-common/test/EventService.js b/fabric-common/test/EventService.js index 1bf82b852b..ee3c1d3dc9 100644 --- a/fabric-common/test/EventService.js +++ b/fabric-common/test/EventService.js @@ -176,10 +176,16 @@ describe('EventService', () => { eventService.setTargets(eventer); }).should.throw('targets parameter is not an array'); }); + it('should require targets as an empty array', () => { + (() => { + eventService.setTargets([]); + }).should.throw('No targets provided'); + }); it('should throw when target not connected', () => { (() => { + eventer.isConnectable = sinon.stub().returns(false); eventService.setTargets([eventer]); - }).should.throw('Eventer eventer1 is not connected'); + }).should.throw('Eventer eventer1 is not connectable'); }); it('should handle connected target', () => { eventer.connected = true; @@ -187,6 +193,13 @@ describe('EventService', () => { should.equal(eventService.targets[0].type, 'Eventer'); should.equal(eventService.targets[0].name, 'eventer1'); }); + it('should handle connectable target', () => { + eventer.connected = false; + eventer.isConnectable = sinon.stub().returns(true); + eventService.setTargets([eventer]); + should.equal(eventService.targets[0].type, 'Eventer'); + should.equal(eventService.targets[0].name, 'eventer1'); + }); }); describe('#getLastBlockNumber', () => { @@ -379,8 +392,26 @@ describe('EventService', () => { it('throws if targets is missing', async () => { await eventService.send().should.be.rejectedWith('Missing targets parameter'); }); + it('throws if eventer stream is running', async () => { + const eventer1 = client.newEventer('eventer1'); + eventer1.stream = sinon.stub(); + eventService._payload = Buffer.from('payload'); + eventService._signature = Buffer.from('signature'); + await eventService.send({targets: [eventer1]}).should.be.rejectedWith('Event service eventer1 is currently listening'); + }); + it('throws if eventer is not connected', async () => { + const eventer1 = client.newEventer('eventer1'); + eventer1.isConnectable = sinon.stub().returns(false); + eventer1.checkConnection = sinon.stub().returns(false); + eventService._payload = Buffer.from('payload'); + eventService._signature = Buffer.from('signature'); + await eventService.send({targets: [eventer1]}).should.be.rejectedWith('Event service eventer1 is not connected'); + }); it('runs ok', async () => { const eventer1 = client.newEventer('eventer1'); + eventer1.isConnectable = sinon.stub().returns(true); + eventer1.connect = sinon.stub().resolves(true); + eventer1.checkConnection = sinon.stub().resolves(true); sinon.stub(eventService, '_startService').resolves(eventer1); eventService._payload = Buffer.from('payload'); eventService._signature = Buffer.from('signature'); @@ -397,12 +428,15 @@ describe('EventService', () => { sinon.stub(eventService, '_startService').rejects(Error('failed')); eventService._payload = Buffer.from('payload'); eventService._signature = Buffer.from('signature'); + eventer1.isConnectable = sinon.stub().returns(true); + eventer1.connect = sinon.stub().resolves(true); + eventer1.checkConnection = sinon.stub().resolves(true); await eventService.send({targets: [eventer1]}).should.be.rejectedWith('failed'); sinon.assert.calledWith(FakeLogger.error, '%s - Starting stream to %s failed'); }); }); - describe('#_send', () => { + describe('#_startService', () => { let eventProtoDeliverStub; let deliverFilteredStub; let deliverStub; @@ -425,17 +459,6 @@ describe('EventService', () => { eventService = new EventService('myhub', channel); }); - - it('throws if eventer stream is running', async () => { - const eventer1 = client.newEventer('eventer1'); - eventer1.stream = sinon.stub(); - await eventService._startService(eventer1).should.be.rejectedWith('Event service eventer1 is currently listening'); - }); - it('throws if eventer is not connected', async () => { - const eventer1 = client.newEventer('eventer1'); - eventer1.checkConnection = sinon.stub().returns(false); - await eventService._startService(eventer1).should.be.rejectedWith('Event service eventer1 is not connected'); - }); it('throws timeout on stream', async () => { const eventer1 = client.newEventer('eventer1'); eventer1.endpoint = endpoint; @@ -664,14 +687,14 @@ describe('EventService', () => { }); }); - describe('#isListening', () => { + describe('#hasListeners', () => { it('should return true', () => { eventService._eventListenerRegistrations.set('something', 'else'); - const results = eventService.isListening(); + const results = eventService.hasListeners(); results.should.be.true; }); it('should return false', () => { - const results = eventService.isListening(); + const results = eventService.hasListeners(); results.should.be.false; }); }); @@ -727,13 +750,16 @@ describe('EventService', () => { eventService.unregisterEventListener(); }).should.throw(/Missing eventListener parameter/); }); - it('should throw if eventListener not given', () => { + it('should throw if eventListener not found', () => { (() => { eventService.unregisterEventListener('bad'); }).should.throw(/eventListener not found/); }); - it('should run when not found', () => { - const eventListener = new EventListener('block', sinon.stub()); + it('should not throw if eventListener not found', () => { + eventService.unregisterEventListener('bad', true); + }); + it('should run when found', () => { + const eventListener = new EventListener('eventService', 'block', sinon.stub()); const registrations = new Map(); registrations.set(eventListener, eventListener); eventService._eventListenerRegistrations = registrations; @@ -743,13 +769,13 @@ describe('EventService', () => { }); it('should set the have flags', () => { const registrations = new Map(); - const eventListener = new EventListener('block', sinon.stub()); + const eventListener = new EventListener('eventService', 'block', sinon.stub()); registrations.set(eventListener, eventListener); - const eventListener1 = new EventListener('block', sinon.stub()); + const eventListener1 = new EventListener('eventService', 'block', sinon.stub()); registrations.set(eventListener1, eventListener1); - const eventListener2 = new EventListener('tx', sinon.stub(), null, 'txid'); + const eventListener2 = new EventListener('eventService', 'tx', sinon.stub(), null, 'txid'); registrations.set(eventListener2, eventListener2); - const eventListener3 = new EventListener('chaincode', sinon.stub(), null, 'eventname', 'chaincode'); + const eventListener3 = new EventListener('eventService', 'chaincode', sinon.stub(), null, 'eventname', 'chaincode'); registrations.set(eventListener3, eventListener3); eventService._eventListenerRegistrations = registrations; const results = eventService.unregisterEventListener(eventListener); @@ -1121,4 +1147,18 @@ describe('EventService', () => { sinon.assert.calledWith(FakeLogger.debug, '_queueChaincodeEvent[myhub] - queuing chaincode event: event1'); }); }); + + describe('#getTransactionListener', () => { + it('should not find listener', () => { + eventService.registerTransactionListener('tx1', sinon.stub()); + const listener = eventService.getTransactionListener('tx2'); + should.equal(listener, null); + }); + + it('should find listener', () => { + const tx1 = eventService.registerTransactionListener('tx1', sinon.stub()); + const listener = eventService.getTransactionListener('tx1'); + should.equal(listener, tx1); + }); + }); }); diff --git a/fabric-common/test/Proposal.js b/fabric-common/test/Proposal.js index 5e7ae3055c..953b56df35 100644 --- a/fabric-common/test/Proposal.js +++ b/fabric-common/test/Proposal.js @@ -48,7 +48,7 @@ describe('Proposal', () => { it('should require a name', () => { (() => { new Proposal(); - }).should.throw('Missing chaincodeName parameter'); + }).should.throw('Missing chaincodeId parameter'); }); it('should require a Channel', () => { (() => { @@ -132,7 +132,7 @@ describe('Proposal', () => { it('should require a string chaincode name', () => { (() => { proposal.addChaincodeCollectionsInterest({}); - }).should.throw('Invalid chaincodeName parameter'); + }).should.throw('Invalid chaincodeId parameter'); }); }); @@ -371,7 +371,7 @@ describe('Proposal', () => { describe('#toString', () => { it('should return string', () => { const string = proposal.toString(); - should.equal(string, 'Proposal: {chaincodeName: chaincode, channel: mychannel}'); + should.equal(string, 'Proposal: {chaincodeId: chaincode, channel: mychannel}'); }); }); }); diff --git a/fabric-common/test/Query.js b/fabric-common/test/Query.js index 3ffaba2e79..fa7d711111 100644 --- a/fabric-common/test/Query.js +++ b/fabric-common/test/Query.js @@ -32,7 +32,7 @@ describe('Query', () => { it('should require a name', () => { (() => { new Query(); - }).should.throw('Missing chaincodeName parameter'); + }).should.throw('Missing chaincodeId parameter'); }); it('should require a Channel', () => { (() => { @@ -48,7 +48,7 @@ describe('Query', () => { describe('#toString', () => { it('should return string', () => { const string = endorsement.toString(); - should.equal(string, 'Query: {chaincodeName: chaincode, channel: mychannel}'); + should.equal(string, 'Query: {chaincodeId: chaincode, channel: mychannel}'); }); }); }); diff --git a/fabric-common/test/ServiceEndpoint.js b/fabric-common/test/ServiceEndpoint.js index 079ace8c98..26a7972293 100644 --- a/fabric-common/test/ServiceEndpoint.js +++ b/fabric-common/test/ServiceEndpoint.js @@ -44,17 +44,62 @@ describe('ServiceEndpoint', () => { }); }); + describe('#setEndpoint', () => { + it('should run', () => { + serviceEndpoint.endpoint = undefined; + serviceEndpoint.setEndpoint(endpoint, {key1: 'value1'}); + should.equal(serviceEndpoint.endpoint, endpoint); + should.equal(serviceEndpoint.options.key1, 'value1'); + }); + it('should throw', () => { + (() => { + serviceEndpoint.setEndpoint(endpoint, {key1: 'value1'}); + }).should.throw('This service endpoint myserviceEndpoint-grpc://host:2700 is connected'); + }); + }); + + describe('#isConnectable', () => { + it('should be true if connected', () => { + serviceEndpoint.connected = true; + const result = serviceEndpoint.isConnectable(); + should.equal(result, false); + }); + it('should be true if not connected and have endpoint assigned', () => { + serviceEndpoint.connected = false; + serviceEndpoint.endpoint = endpoint; + const result = serviceEndpoint.isConnectable(); + should.equal(result, true); + }); + it('should be false if not connected and have endpoint assigned but already tried to connect', () => { + serviceEndpoint.connected = false; + serviceEndpoint.endpoint = endpoint; + serviceEndpoint.connectAttempted = true; + const result = serviceEndpoint.isConnectable(); + should.equal(result, false); + }); + it('should be false if not connected and no endpoint assigned', () => { + serviceEndpoint.connected = false; + serviceEndpoint.endpoint = undefined; + const result = serviceEndpoint.isConnectable(); + should.equal(result, false); + }); + }); + describe('#connect', () => { it('should reject if no endpoint', async () => { - await serviceEndpoint.connect().should.be.rejectedWith(/Missing endpoint parameter/); + serviceEndpoint.connected = false; + serviceEndpoint.service = null; + serviceEndpoint.endpoint = null; + await serviceEndpoint.connect().should.be.rejectedWith('Missing endpoint parameter'); }); it('should reject if connected', async () => { + await serviceEndpoint.connect('send').should.be.rejectedWith(/is connected/); }); it('should reject if service exist', async () => { serviceEndpoint.connected = false; - await serviceEndpoint.connect('send').should.be.rejectedWith(/has an active service connection/); + await serviceEndpoint.connect('send').should.be.rejectedWith('This service endpoint myserviceEndpoint-grpc://host:2700 has an active grpc service connection'); }); it('should reject if timeout', async () => { serviceEndpoint.endpoint = null; @@ -138,28 +183,30 @@ describe('ServiceEndpoint', () => { describe('#getCharacteristics', () => { it('should get a good characteristics object', async () => { - const characteristics = serviceEndpoint.getCharacteristics(); - characteristics.type.should.be.equal('ServiceEndpoint'); - characteristics.name.should.be.equal('myserviceEndpoint'); - characteristics.url.should.be.equal('grpc://host:2700'); + const something = sinon.stub(); + serviceEndpoint.getCharacteristics(something); + something.connection.type.should.be.equal('ServiceEndpoint'); + something.connection.name.should.be.equal('myserviceEndpoint'); + something.connection.url.should.be.equal('grpc://host:2700'); + something.peer.should.be.equal('myserviceEndpoint'); }); it('should get a good characteristics object without endpoint', async () => { serviceEndpoint.endpoint = null; - const characteristics = serviceEndpoint.getCharacteristics(); - characteristics.type.should.be.equal('ServiceEndpoint'); - characteristics.name.should.be.equal('myserviceEndpoint'); - characteristics.url.should.be.equal(''); - characteristics.options.should.be.deep.equal({}); + const something = sinon.stub(); + serviceEndpoint.getCharacteristics(something); + something.connection.type.should.be.equal('ServiceEndpoint'); + something.connection.name.should.be.equal('myserviceEndpoint'); + something.connection.url.should.be.equal(''); + something.connection.options.should.be.deep.equal({}); + something.peer.should.be.equal('myserviceEndpoint'); }); it('should get a good characteristics object without clientKey', async () => { serviceEndpoint.endpoint.options.clientKey = 'clientKey'; serviceEndpoint.endpoint.options.clientCert = 'clientCert'; - const characteristics = serviceEndpoint.getCharacteristics(); - characteristics.type.should.be.equal('ServiceEndpoint'); - characteristics.name.should.be.equal('myserviceEndpoint'); - characteristics.url.should.be.equal('grpc://host:2700'); - characteristics.options.clientCert.should.be.equal('clientCert'); - should.equal(characteristics.options.clientKey, undefined); + const something = sinon.stub(); + serviceEndpoint.getCharacteristics(something); + something.connection.options.clientCert.should.be.equal('clientCert'); + should.equal(something.connection.options.clientKey, undefined); }); }); diff --git a/fabric-common/test/User.js b/fabric-common/test/User.js index ad85f7891b..5231a2c74f 100644 --- a/fabric-common/test/User.js +++ b/fabric-common/test/User.js @@ -99,7 +99,7 @@ describe('User', () => { }); describe('#fromString', () => { - it('should require a chaincodeName', async () => { + it('should require a chaincodeId', async () => { const user = User.createUser('user', 'password', 'mspid', cert, key); const string = user.toString(); const f_user = new User('fake'); diff --git a/test/scenario/features/steps/base_steps.js b/test/scenario/features/steps/base_steps.js index 4c3110bc40..ad49540489 100644 --- a/test/scenario/features/steps/base_steps.js +++ b/test/scenario/features/steps/base_steps.js @@ -155,6 +155,7 @@ module.exports = function () { if (error) { testUtil.logError(`Failed to receive transaction event for ${endorsement.getTransactionId()}`); reject(error); + return; } testUtil.logMsg(`Successfully received the transaction event for ${event.transactionId} with status of ${event.status} in block number ${event.blockNumber}`); resolve('Commit success'); @@ -439,7 +440,7 @@ module.exports = function () { const found_peers = channel.getEndorsers('Org1MSP'); for (const found_peer of found_peers) { const eventer = client.newEventer(found_peer.name + '-eventer'); - eventer.connect(found_peer.endpoint); + await eventer.connect(found_peer.endpoint); eventers.push(eventer); testUtil.logMsg(`Successfully created an event hub based on a discovered peer: ${eventer.name}`); } @@ -453,7 +454,7 @@ module.exports = function () { const handle = setTimeout(() => { reject(new Error('Test application has timed out waiting for tx event')); // may want to close the event hub or unregister the tx event listener - }, 10000); + }, 20000); event_service.registerTransactionListener( endorsement.getTransactionId(), @@ -461,6 +462,7 @@ module.exports = function () { clearTimeout(handle); if (error) { reject(error); + return; } testUtil.logMsg(`Successfully received the transaction event for ${event.transactionId} with status of ${event.status} in block number ${event.blockNumber}`); resolve(`Success ${event.transactionId}`); @@ -823,6 +825,7 @@ module.exports = function () { if (error) { testUtil.logError(`Failed to receive transaction event for ${endorsement.getTransactionId()}`); reject(error); + return; } // all chaincode events matches from this block // will be in an array for this listener