diff --git a/fabric-client/lib/User.js b/fabric-client/lib/User.js index 489bc4ef00..a5f7f86df4 100644 --- a/fabric-client/lib/User.js +++ b/fabric-client/lib/User.js @@ -1,9 +1,16 @@ /* - Copyright 2016, 2018 IBM All Rights Reserved. - - SPDX-License-Identifier: Apache-2.0 - -*/ + * 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'; diff --git a/fabric-client/lib/packager/BasePackager.js b/fabric-client/lib/packager/BasePackager.js index 73f99c3372..b03c94d0df 100644 --- a/fabric-client/lib/packager/BasePackager.js +++ b/fabric-client/lib/packager/BasePackager.js @@ -1,9 +1,17 @@ /* - Copyright 2017, 2018 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. + */ - SPDX-License-Identifier: Apache-2.0 - -*/ 'use strict'; @@ -69,17 +77,18 @@ const BasePackager = class { return new Promise((resolve, reject) => { logger.debug('findMetadataDescriptors : start'); const descriptors = []; - klaw(filePath).on('data', (entry) => { - if (entry.stats.isFile() && this.isMetadata(entry.path)) { - - const desc = { - name: path.join('META-INF', path.relative(filePath, entry.path).split('\\').join('/')), // for windows style paths - fqp: entry.path - }; - logger.debug(' findMetadataDescriptors :: %j', desc); - descriptors.push(desc); - } - }) + klaw(filePath) + .on('data', (entry) => { + if (entry.stats.isFile() && this.isMetadata(entry.path)) { + + const desc = { + name: path.join('META-INF', path.relative(filePath, entry.path).split('\\').join('/')), // for windows style paths + fqp: entry.path + }; + logger.debug(' findMetadataDescriptors :: %j', desc); + descriptors.push(desc); + } + }) .on('error', (error, item) => { logger.error('error while packaging item %j :: %s', item, error); reject(error); @@ -160,13 +169,14 @@ const BasePackager = class { generateTarGz (descriptors, dest) { return new Promise((resolve, reject) => { const pack = tar.pack(); - // Setup the pipeline to compress on the fly and resolve/reject the promise - pack.pipe(zlib.createGzip()).pipe(dest).on('finish', () => { - resolve(true); - }).on('error', (err) => { - reject(err); - }); + pack.pipe(zlib.createGzip()).pipe(dest) + .on('finish', () => { + resolve(true); + }) + .on('error', (err) => { + reject(err); + }); // Iterate through each descriptor in the order it was provided and resolve // the entry asynchronously. We will gather results below before diff --git a/fabric-client/lib/utils/ChannelHelper.js b/fabric-client/lib/utils/ChannelHelper.js index ff8da3c9f1..1bb4fede67 100644 --- a/fabric-client/lib/utils/ChannelHelper.js +++ b/fabric-client/lib/utils/ChannelHelper.js @@ -1,12 +1,17 @@ /* - Copyright 2018 IBM All Rights Reserved. - - SPDX-License-Identifier: Apache-2.0 + * 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. + */ -This helper is used for Channel util functions, most channel -util function should be removed here so that we can reduce the -file size of Channel.js -*/ const grpc = require('grpc'); const path = require('path'); diff --git a/fabric-client/test/User.js b/fabric-client/test/User.js new file mode 100644 index 0000000000..98b06b5e12 --- /dev/null +++ b/fabric-client/test/User.js @@ -0,0 +1,872 @@ +/* + * 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 UserRewire = rewire('../lib/User'); +const User = require('../lib/User'); + +const chai = require('chai'); +const should = chai.should(); +const sinon = require('sinon'); + +describe('User', () => { + + describe('#constructor', () => { + + const sandbox = sinon.createSandbox(); + + afterEach(() => { + sandbox.restore(); + }); + + it('should conditionally initialise parameters if cfg is a string', () => { + + const myUser = new User('my_cfg'); + myUser._name.should.equal('my_cfg'); + myUser._affiliation.should.equal(''); + should.not.exist(myUser._roles); + myUser._enrollmentSecret.should.equal(''); + myUser._mspId.should.equal(''); + should.not.exist(myUser._identity); + should.not.exist(myUser._signingIdentity); + should.not.exist(myUser._cryptoSuite); + }); + + it('should conditionally initialise parameters if cfg is an object with an enrollmentID and roles', () => { + const obj = new Object({enrollmentID: 'user_enrollmentID', name: 'user_name', roles: ['role_1', 'role_2'], affiliation: 'user_affiliation'}); + const myUser = new User(obj); + myUser._name.should.equal(obj.enrollmentID); + myUser._roles.should.equal(obj.roles); + myUser._affiliation.should.equal(obj.affiliation); + myUser._enrollmentSecret.should.equal(''); + myUser._mspId.should.equal(''); + should.not.exist(myUser._identity); + should.not.exist(myUser._signingIdentity); + should.not.exist(myUser._cryptoSuite); + }); + + it('should conditionally initialise parameters if cfg is an object without an enrollmentID or roles', () => { + const obj = new Object({name: 'user_name', affiliation: 'user_affiliation'}); + const myUser = new User(obj); + myUser._name.should.equal(obj.name); + myUser._roles.should.deep.equal(['fabric.user']); + myUser._affiliation.should.equal(obj.affiliation); + myUser._enrollmentSecret.should.equal(''); + myUser._mspId.should.equal(''); + should.not.exist(myUser._identity); + should.not.exist(myUser._signingIdentity); + should.not.exist(myUser._cryptoSuite); + }); + }); + + describe('#getName', () => { + it('should get the user name', () => { + const myUser = new User('my_cfg'); + myUser.getName().should.equal('my_cfg'); + }); + }); + + describe('#getRoles', () => { + it('should get the users roles', () => { + const obj = new Object({enrollmentID: 'user_enrollmentID', name: 'user_name', roles: ['role_1', 'role_2'], affiliation: 'user_affiliation'}); + const myUser = new User(obj); + myUser.getRoles().should.equal(obj.roles); + }); + }); + + describe('#setRoles', () => { + it('should set the users roles', () => { + const myUser = new User({enrollmentID: 'user_enrollmentID', name: 'user_name', roles: ['role_1', 'role_2'], affiliation: 'user_affiliation'}); + myUser.setRoles(['new_role_1', 'new_role_2']); + myUser._roles.should.deep.equal(['new_role_1', 'new_role_2']); + }); + }); + + describe('#getAffiliation', () => { + it('should get the users affiliation', () => { + const obj = new Object({enrollmentID: 'user_enrollmentID', name: 'user_name', roles: ['role_1', 'role_2'], affiliation: 'user_affiliation'}); + const myUser = new User(obj); + myUser.getAffiliation().should.equal(obj.affiliation); + }); + }); + + describe('#setAffiliation', () => { + it('should set the users affiliation', () => { + const myUser = new User({enrollmentID: 'user_enrollmentID', name: 'user_name', roles: ['role_1', 'role_2'], affiliation: 'user_affiliation'}); + myUser.setAffiliation('new_affiliation'); + myUser._affiliation.should.equal('new_affiliation'); + }); + }); + + describe('#getIdentity', () => { + it('should get the users identity', () => { + const myUser = new User('cfg'); + myUser._identity = 'test_identity'; + myUser.getIdentity().should.equal('test_identity'); + }); + }); + + describe('#getSigningIdentity', () => { + it('should get the users signing identity', () => { + const myUser = new User('cfg'); + myUser._signingIdentity = 'test_signingIdentity'; + myUser.getSigningIdentity().should.equal('test_signingIdentity'); + }); + }); + + describe('#getCryptoSuite', () => { + it('should get the users crypto suite', () => { + const myUser = new User('my_cfg'); + myUser._cryptoSuite = 'test_cryptoSuite'; + myUser.getCryptoSuite().should.equal('test_cryptoSuite'); + }); + }); + + describe('#setCryptoSuite', () => { + it('should set the users crypto suite', () => { + const myUser = new User('my_cfg'); + myUser.setCryptoSuite('test_cryptoSuite'); + myUser._cryptoSuite.should.equal('test_cryptoSuite'); + }); + }); + + describe('#setEnrollment', () => { + it('should throw error if no privateKey parameter', async() => { + const myUser = new User('my_cfg'); + await myUser.setEnrollment(null, 'test_certificate', 'test_mspId', true) + .should.be.rejectedWith(/Invalid parameter. Must have a valid private key./); + }); + + it('should throw error if the privateKey parameter is an empty string', async() => { + const myUser = new User('my_cfg'); + await myUser.setEnrollment('', 'test_certificate', 'test_mspId', true) + .should.be.rejectedWith(/Invalid parameter. Must have a valid private key./); + }); + + it('should throw error if no certificate parameter', async() => { + const myUser = new User('my_cfg'); + await myUser.setEnrollment('test_privateKey', null, 'test_mspId', true) + .should.be.rejectedWith(/Invalid parameter. Must have a valid certificate./); + }); + + it('should throw error if certificate parameter is an empty string', async() => { + const myUser = new User('my_cfg'); + await myUser.setEnrollment('test_privateKey', '', 'test_mspId', true) + .should.be.rejectedWith(/Invalid parameter. Must have a valid certificate./); + }); + + it('should throw error if no mspId parameter', async() => { + const myUser = new User('my_cfg'); + await myUser.setEnrollment('test_privateKey', 'test_certificate', null, true) + .should.be.rejectedWith(/Invalid parameter. Must have a valid mspId./); + }); + + it('should throw error if mspId parameter is an empty string', async() => { + const myUser = new User('my_cfg'); + await myUser.setEnrollment('test_privateKey', 'test_certificate', '', true) + .should.be.rejectedWith(/Invalid parameter. Must have a valid mspId./); + }); + + it('should set the cryptoSuite if its not already set', async() => { + const sandbox = sinon.createSandbox(); + const FakeSdkUtils = { + newCryptoSuite: () => {}, + }; + + const FakeIdentity = sandbox.stub(); + const FakeSigningIdentity = sandbox.stub(); + + const returnStub = sandbox.stub( { setCryptoKeyStore: () =>{}, importKey: () => {}}); + const newCryptoSuiteStub = sandbox.stub(FakeSdkUtils, 'newCryptoSuite').returns(returnStub); + + UserRewire.__set__('sdkUtils', FakeSdkUtils); + UserRewire.__set__('Identity', FakeIdentity); + UserRewire.__set__('SigningIdentity', FakeSigningIdentity); + + const obj = new UserRewire('my_cfg'); + await obj.setEnrollment('test_privateKey', 'test_certificate', 'test_mspId', true); + + sinon.assert.calledOnce(newCryptoSuiteStub); + obj._cryptoSuite.should.equal(returnStub); + }); + + it('should set users crypto key store if skipPersistance is false', async() => { + const sandbox = sinon.createSandbox(); + const FakeSdkUtils = { + newCryptoSuite: () => {}, + newCryptoKeyStore: () => {}, + }; + + const FakeIdentity = sandbox.stub(); + const FakeSigningIdentity = sandbox.stub(); + + const cryptoSuite = { + setCryptoKeyStore: () => {}, + importKey: () => {}, + }; + + const FakeSetCryptoKeyStore = sandbox.stub(cryptoSuite, 'setCryptoKeyStore'); + + sandbox.stub(FakeSdkUtils, 'newCryptoSuite').returns(cryptoSuite); + sandbox.stub(FakeSdkUtils, 'newCryptoKeyStore').returns('test_cryptoKeyStore'); + + + UserRewire.__set__('sdkUtils', FakeSdkUtils); + UserRewire.__set__('Identity', FakeIdentity); + UserRewire.__set__('SigningIdentity', FakeSigningIdentity); + + const obj = new UserRewire('my_cfg'); + await obj.setEnrollment('test_privateKey', 'test_certificate', 'test_mspId', false); + + sinon.assert.calledOnce(FakeSetCryptoKeyStore); + sinon.assert.calledWith(FakeSetCryptoKeyStore, 'test_cryptoKeyStore'); + }); + + it('should create and set a pubKey variable if the cryptoKeyStore is set and skipPersistence is false', async() => { + const sandbox = sinon.createSandbox(); + const FakeSdkUtils = { + newCryptoSuite: () => {}, + newCryptoKeyStore: () => {}, + }; + + const FakeIdentity = sandbox.stub(); + const FakeSigningIdentity = sandbox.stub(); + + const cryptoSuite = { + setCryptoKeyStore: () => {}, + importKey: () => {}, + _cryptoKeyStore: 'cryptoKeyStore', + }; + + const FakeImportKey = sandbox.stub(cryptoSuite, 'importKey'); + + sandbox.stub(FakeSdkUtils, 'newCryptoSuite').returns(cryptoSuite); + sandbox.stub(FakeSdkUtils, 'newCryptoKeyStore').returns('test_cryptoKeyStore'); + + + UserRewire.__set__('sdkUtils', FakeSdkUtils); + UserRewire.__set__('Identity', FakeIdentity); + UserRewire.__set__('SigningIdentity', FakeSigningIdentity); + + const obj = new UserRewire('my_cfg'); + await obj.setEnrollment('test_privateKey', 'test_certificate', 'test_mspId', false); + + sinon.assert.calledOnce(FakeImportKey); + sinon.assert.calledWith(FakeImportKey, 'test_certificate'); + }); + + it('should create and set a pubKey variable if the either the cryptoKeyStore is not set or skipPersitence is true', async() => { + const sandbox = sinon.createSandbox(); + const FakeSdkUtils = { + newCryptoSuite: () => {}, + newCryptoKeyStore: () => {}, + }; + + const FakeIdentity = sandbox.stub(); + const FakeSigningIdentity = sandbox.stub(); + + const cryptoSuite = { + setCryptoKeyStore: () => {}, + importKey: () => {}, + }; + + const FakeImportKey = sandbox.stub(cryptoSuite, 'importKey'); + + sandbox.stub(FakeSdkUtils, 'newCryptoSuite').returns(cryptoSuite); + sandbox.stub(FakeSdkUtils, 'newCryptoKeyStore').returns('test_cryptoKeyStore'); + + + UserRewire.__set__('sdkUtils', FakeSdkUtils); + UserRewire.__set__('Identity', FakeIdentity); + UserRewire.__set__('SigningIdentity', FakeSigningIdentity); + + const obj = new UserRewire('my_cfg'); + await obj.setEnrollment('test_privateKey', 'test_certificate', 'test_mspId', true); + + sinon.assert.calledOnce(FakeImportKey); + sinon.assert.calledWith(FakeImportKey, 'test_certificate', { ephemeral: true }); + }); + + it('should set the users identity', async() => { + const sandbox = sinon.createSandbox(); + const FakeSdkUtils = { + newCryptoSuite: () => {}, + newCryptoKeyStore: () => {}, + }; + + const FakeIdentity = sandbox.stub(); + const FakeSigningIdentity = sandbox.stub(); + + const FakeCryptoSuite = { + setCryptoKeyStore: () => {}, + importKey: () => {}, + }; + + sandbox.stub(FakeSdkUtils, 'newCryptoSuite').returns(FakeCryptoSuite); + sandbox.stub(FakeSdkUtils, 'newCryptoKeyStore').returns('test_cryptoKeyStore'); + sandbox.stub(FakeCryptoSuite, 'importKey').returns('test_key'); + + + UserRewire.__set__('sdkUtils', FakeSdkUtils); + UserRewire.__set__('Identity', FakeIdentity); + UserRewire.__set__('SigningIdentity', FakeSigningIdentity); + + const obj = new UserRewire('my_cfg'); + await obj.setEnrollment('test_privateKey', 'test_certificate', 'test_mspId', false); + + sinon.assert.calledOnce(FakeIdentity); + sinon.assert.calledWith(FakeIdentity, 'test_certificate', 'test_key', 'test_mspId', FakeCryptoSuite); + }); + + it('should set the users signingIdentity', async() => { + const sandbox = sinon.createSandbox(); + const FakeSdkUtils = { + newCryptoSuite: () => {}, + newCryptoKeyStore: () => {}, + }; + + const FakeIdentity = sandbox.stub(); + const FakeSigningIdentity = sandbox.stub(); + + const FakeCryptoSuite = { + setCryptoKeyStore: () => {}, + importKey: () => {}, + }; + + sandbox.stub(FakeSdkUtils, 'newCryptoSuite').returns(FakeCryptoSuite); + sandbox.stub(FakeSdkUtils, 'newCryptoKeyStore').returns('test_cryptoKeyStore'); + sandbox.stub(FakeCryptoSuite, 'importKey').returns('test_key'); + + + UserRewire.__set__('sdkUtils', FakeSdkUtils); + UserRewire.__set__('Identity', FakeIdentity); + UserRewire.__set__('SigningIdentity', FakeSigningIdentity); + + const obj = new UserRewire('my_cfg'); + await obj.setEnrollment('test_privateKey', 'test_certificate', 'test_mspId', false); + + sinon.assert.calledOnce(FakeSigningIdentity); + sinon.assert.calledWith(FakeIdentity, 'test_certificate', 'test_key', 'test_mspId', FakeCryptoSuite); + }); + }); + + describe('#isEnrolled', () => { + it('should return true if user has both an identity and a signing identity', () => { + const myUser = new User('my_cfg'); + myUser._identity = 'test_identity'; + myUser._signingIdentity = 'test_signingIdentity'; + myUser.isEnrolled().should.equal(true); + }); + + it('should return false if user has no identity', () => { + const myUser = new User('my_cfg'); + myUser._signingIdentity = 'test_signingIdentity'; + myUser.isEnrolled().should.equal(false); + }); + + it('should return false if user has no signing identity',() => { + const myUser = new User('my_cfg'); + myUser._identity = 'test_identity'; + myUser.isEnrolled().should.equal(false); + }); + + it('should return false if user has no identity or signing identity', () => { + const myUser = new User('cfg'); + myUser.isEnrolled().should.equal(false); + }); + }); + + describe('#fromString', () => { + const sandbox = sinon.createSandbox(); + + afterEach(() => { + sandbox.restore(); + }); + + it('should log and set the users state, name, roles, afilliation and enrollmentSecret', () => { + const FakeLogger = { + debug: () => {} + }; + + const FakeSdkUtils = { + newCryptoSuite: () => {}, + newCryptoKeyStore: () => {}, + }; + + const FakeCryptoSuite = { + setCryptoKeyStore: () => {}, + importKey: () => {}, + getKey: () => {} + }; + + const debugStub = sandbox.stub(FakeLogger, 'debug'); + const FakeIdentity = sandbox.stub(); + const promise = new Promise(() => {}); + + sandbox.stub(FakeSdkUtils, 'newCryptoSuite').returns(FakeCryptoSuite); + sandbox.stub(FakeSdkUtils, 'newCryptoKeyStore').returns('test_cryptoKeyStore'); + sandbox.stub(FakeCryptoSuite, 'importKey').returns(promise); + + UserRewire.__set__('logger', FakeLogger); + UserRewire.__set__('sdkUtils', FakeSdkUtils); + UserRewire.__set__('Identity', FakeIdentity); + + const obj = new UserRewire('cfg'); + obj.fromString('{ "name":"cfg", "roles":"test_role", "affiliation":"test_affiliation", "enrollmentSecret":"test_enrollmentSecret", "mspid":"test_mspId", "enrollment":{"identity":{"certificate":"test_certificate"}}}', false); + + sinon.assert.calledOnce(debugStub); + sinon.assert.calledWith(debugStub, 'fromString --start'); + obj._name.should.equal('cfg'); + obj._roles.should.equal('test_role'); + obj._affiliation.should.equal('test_affiliation'); + obj._enrollmentSecret.should.equal('test_enrollmentSecret'); + }); + + it('should throw error if state name is not the same as user name', () => { + (() => { + const FakeLogger = { + debug: () => {} + }; + + sandbox.stub(FakeLogger, 'debug'); + + UserRewire.__set__('logger', FakeLogger); + + const obj = new UserRewire('cfg'); + obj.fromString('{ "name":"wrong_name", "roles":"test_role", "affiliation":"test_affiliation", "enrollmentSecret":"test_enrollmentSecret", "mspid":"test_mspid", "enrollment":{"identity":{"certificate":"test_certificate"}}}', false); + }).should.throw(/name mismatch: 'wrong_name' does not equal 'cfg'/); + }); + + it('should throw error if the state has no mspid', () => { + (() => { + const FakeLogger = { + debug: () => {} + }; + + sandbox.stub(FakeLogger, 'debug'); + + UserRewire.__set__('logger', FakeLogger); + + const obj = new UserRewire('cfg'); + obj.fromString('{ "name":"cfg", "roles":"test_role", "affiliation":"test_affiliation", "enrollmentSecret":"test_enrollmentSecret", "mspid":"", "enrollment":{"identity":{"certificate":"test_certificate"}}}', false); + }).should.throw(/Failed to find "mspid" in the deserialized state object for the user. Likely due to an outdated state store./); + }); + + it('should set the users mspid to the state mspid', () => { + const FakeLogger = { + debug: () => {} + }; + + const FakeSdkUtils = { + newCryptoSuite: () => {}, + newCryptoKeyStore: () => {}, + }; + + const FakeCryptoSuite = { + setCryptoKeyStore: () => {}, + importKey: () => {}, + getKey: () => {} + }; + + const FakeIdentity = sandbox.stub(); + const promise = new Promise(() => {}); + + sandbox.stub(FakeSdkUtils, 'newCryptoSuite').returns(FakeCryptoSuite); + sandbox.stub(FakeSdkUtils, 'newCryptoKeyStore').returns('test_cryptoKeyStore'); + sandbox.stub(FakeCryptoSuite, 'importKey').returns(promise); + + UserRewire.__set__('logger', FakeLogger); + UserRewire.__set__('sdkUtils', FakeSdkUtils); + UserRewire.__set__('Identity', FakeIdentity); + + const obj = new UserRewire('cfg'); + obj.fromString('{ "name":"cfg", "roles":"test_role", "affiliation":"test_affiliation", "enrollmentSecret":"test_enrollmentSecret", "mspid":"test_mspId", "enrollment":{"identity":{"certificate":"test_certificate"}}}', true); + + obj._mspId.should.equal('test_mspId'); + }); + + it('should set import_promise if no_save is true', async () => { + const FakeLogger = { + debug: () => {} + }; + + const FakeSdkUtils = { + newCryptoSuite: () => {}, + newCryptoKeyStore: () => {}, + }; + + const FakeCryptoSuite = { + setCryptoKeyStore: () => {}, + importKey: () => {}, + getKey: () => {} + }; + + const FakeApi = { + CryptoAlgorithms: { + X509Certificate: 'X509Certificate', + } + }; + + const FakeIdentity = sandbox.stub(); + const FakeImportKey = sandbox.stub(FakeCryptoSuite, 'importKey').returns('key'); + const promise = new Promise((resolve) => { resolve( {isPrivate(){ return true;}});}); + + sandbox.stub(FakeSdkUtils, 'newCryptoSuite').returns(FakeCryptoSuite); + sandbox.stub(FakeSdkUtils, 'newCryptoKeyStore').returns('test_cryptoKeyStore'); + sandbox.stub(FakeCryptoSuite, 'getKey').returns(promise); + + UserRewire.__set__('logger', FakeLogger); + UserRewire.__set__('sdkUtils', FakeSdkUtils); + UserRewire.__set__('Identity', FakeIdentity); + UserRewire.__set__('api', FakeApi); + + const obj = new UserRewire('cfg'); + await obj.fromString('{ "name":"cfg", "roles":"test_role", "affiliation":"test_affiliation", "enrollmentSecret":"test_enrollmentSecret", "mspid":"test_mspId", "enrollment":{"identity":{"certificate":"test_certificate"}}}', true); + + sinon.assert.calledOnce(FakeImportKey); + sinon.assert.calledWith(FakeImportKey, 'test_certificate', { algorithm: 'X509Certificate', ephemeral: true}); + }); + + it('should throw an error if key is not set if no_save is true', async () => { + const FakeLogger = { + debug: () => {} + }; + + const FakeSdkUtils = { + newCryptoSuite: () => {}, + newCryptoKeyStore: () => {}, + }; + + const FakeCryptoSuite = { + setCryptoKeyStore: () => {}, + importKey: () => {}, + getKey: () => {} + }; + + const FakeApi = { + CryptoAlgorithms: { + X509Certificate: 'X509Certificate', + } + }; + + const FakeIdentity = sandbox.stub(); + sandbox.stub(FakeCryptoSuite, 'importKey').returns(null); + const promise = new Promise((resolve) => { resolve( {isPrivate(){ return true;}});}); + + sandbox.stub(FakeSdkUtils, 'newCryptoSuite').returns(FakeCryptoSuite); + sandbox.stub(FakeSdkUtils, 'newCryptoKeyStore').returns('test_cryptoKeyStore'); + sandbox.stub(FakeCryptoSuite, 'getKey').returns(promise); + + UserRewire.__set__('logger', FakeLogger); + UserRewire.__set__('sdkUtils', FakeSdkUtils); + UserRewire.__set__('Identity', FakeIdentity); + UserRewire.__set__('api', FakeApi); + + const obj = new UserRewire('cfg'); + await obj.fromString('{ "name":"cfg", "roles":"test_role", "affiliation":"test_affiliation", "enrollmentSecret":"test_enrollmentSecret", "mspid":"test_mspId", "enrollment":{"identity":{"certificate":"test_certificate"}}}', true).should.be.rejectedWith(/Import of saved user has failed/); + }); + + it('should set import_promise if no_save is false', async () => { + const FakeLogger = { + debug: () => {} + }; + + const FakeSdkUtils = { + newCryptoSuite: () => {}, + newCryptoKeyStore: () => {}, + }; + + const FakeCryptoSuite = { + setCryptoKeyStore: () => {}, + importKey: () => {}, + getKey: () => {} + }; + + const FakeApi = { + CryptoAlgorithms: { + X509Certificate: 'X509Certificate', + } + }; + + const FakeIdentity = sandbox.stub(); + const promise = new Promise((resolve) => { resolve( {isPrivate(){ return true;}});}); + const FakeImportKey = sandbox.stub(FakeCryptoSuite, 'importKey').returns(promise); + + sandbox.stub(FakeSdkUtils, 'newCryptoSuite').returns(FakeCryptoSuite); + sandbox.stub(FakeSdkUtils, 'newCryptoKeyStore').returns('test_cryptoKeyStore'); + sandbox.stub(FakeCryptoSuite, 'getKey').returns(promise); + + UserRewire.__set__('logger', FakeLogger); + UserRewire.__set__('sdkUtils', FakeSdkUtils); + UserRewire.__set__('Identity', FakeIdentity); + UserRewire.__set__('api', FakeApi); + + const obj = new UserRewire('cfg'); + await obj.fromString('{ "name":"cfg", "roles":"test_role", "affiliation":"test_affiliation", "enrollmentSecret":"test_enrollmentSecret", "mspid":"test_mspId", "enrollment":{"identity":{"certificate":"test_certificate"}}}', false); + + sinon.assert.calledOnce(FakeImportKey); + sinon.assert.calledWith(FakeImportKey, 'test_certificate', { algorithm: 'X509Certificate' }); + }); + + it('should return the _cryptoSuite key', async () => { + const FakeLogger = { + debug: () => {} + }; + + const FakeSdkUtils = { + newCryptoSuite: () => {}, + newCryptoKeyStore: () => {}, + }; + + const FakeCryptoSuite = { + setCryptoKeyStore: () => {}, + importKey: () => {}, + getKey: () => {} + }; + + const FakeApi = { + CryptoAlgorithms: { + X509Certificate: 'X509Certificate', + } + }; + + const FakeKey = { + isPrivate: () => {return true;} + }; + + const FakeIdentity = sandbox.stub(); + sandbox.stub(FakeCryptoSuite, 'importKey').resolves(FakeKey); + sandbox.stub(FakeSdkUtils, 'newCryptoSuite').returns(FakeCryptoSuite); + sandbox.stub(FakeSdkUtils, 'newCryptoKeyStore').returns('test_cryptoKeyStore'); + const FakeGetKey = sandbox.stub(FakeCryptoSuite, 'getKey').resolves(FakeKey); + + UserRewire.__set__('logger', FakeLogger); + UserRewire.__set__('sdkUtils', FakeSdkUtils); + UserRewire.__set__('Identity', FakeIdentity); + UserRewire.__set__('api', FakeApi); + + const obj = new UserRewire('cfg'); + await obj.fromString('{ "name":"cfg", "roles":"test_role", "affiliation":"test_affiliation", "enrollmentSecret":"test_enrollmentSecret", "mspid":"test_mspId", "enrollment":{"identity":{"certificate":"test_certificate"}, "signingIdentity":"test_signingIdentity"}}', false); + + sinon.assert.calledOnce(FakeIdentity); + sinon.assert.calledOnce(FakeGetKey); + sinon.assert.calledWith( FakeIdentity, 'test_certificate', FakeKey, 'test_mspId', FakeCryptoSuite); + sinon.assert.calledWith( FakeGetKey, 'test_signingIdentity'); + }); + + it('should assign the self variable with a signing identity if the privateKey is private', async () => { + const FakeLogger = { + debug: () => {} + }; + + const FakeSdkUtils = { + newCryptoSuite: () => {}, + newCryptoKeyStore: () => {}, + }; + + const FakeCryptoSuite = { + setCryptoKeyStore: () => {}, + importKey: () => {}, + getKey: () => {} + }; + + const FakeKey = { + isPrivate: () => {return true;} + }; + + const FakeIdentity = sandbox.stub(); + const FakeSigningIdentity = sandbox.stub(); + sandbox.stub(FakeCryptoSuite, 'importKey').resolves(FakeKey); + const FakeSigner = sandbox.stub(); + + sandbox.stub(FakeSdkUtils, 'newCryptoSuite').returns(FakeCryptoSuite); + sandbox.stub(FakeSdkUtils, 'newCryptoKeyStore').returns('test_cryptoKeyStore'); + sandbox.stub(FakeCryptoSuite, 'getKey').resolves(FakeKey); + + UserRewire.__set__('logger', FakeLogger); + UserRewire.__set__('sdkUtils', FakeSdkUtils); + UserRewire.__set__('Identity', FakeIdentity); + UserRewire.__set__('SigningIdentity', FakeSigningIdentity); + UserRewire.__set__('Signer', FakeSigner); + + const obj = new UserRewire('cfg'); + await obj.fromString('{ "name":"cfg", "roles":"test_role", "affiliation":"test_affiliation", "enrollmentSecret":"test_enrollmentSecret", "mspid":"test_mspId", "enrollment":{"identity":{"certificate":"test_certificate"}, "signingIdentity":"test_signingIdentity"}}', false); + + sinon.assert.calledWith(FakeSigningIdentity, 'test_certificate', FakeKey, 'test_mspId', FakeCryptoSuite, sinon.match.instanceOf(FakeSigner) ); + sinon.assert.calledWith(FakeSigner, FakeCryptoSuite, FakeKey); + sinon.assert.calledOnce(FakeSigningIdentity); + sinon.assert.calledOnce(FakeSigner); + }); + + it('should throw error if private key is missing', async () => { + const FakeLogger = { + debug: () => {} + }; + + const FakeSdkUtils = { + newCryptoSuite: () => {}, + newCryptoKeyStore: () => {}, + }; + + const FakeCryptoSuite = { + setCryptoKeyStore: () => {}, + importKey: () => {}, + getKey: () => {} + }; + + const FakeIdentity = sandbox.stub(); + const promise = new Promise(function(resolve, reject){ resolve( {isPrivate(){ return false;}});}); + sandbox.stub(FakeCryptoSuite, 'importKey').returns(promise); + + sandbox.stub(FakeSdkUtils, 'newCryptoSuite').returns(FakeCryptoSuite); + sandbox.stub(FakeSdkUtils, 'newCryptoKeyStore').returns('test_cryptoKeyStore'); + sandbox.stub(FakeCryptoSuite, 'getKey').returns(promise); + + UserRewire.__set__('logger', FakeLogger); + UserRewire.__set__('sdkUtils', FakeSdkUtils); + UserRewire.__set__('Identity', FakeIdentity); + + const obj = new UserRewire('cfg'); + await obj.fromString('{ "name":"cfg", "roles":"test_role", "affiliation":"test_affiliation", "enrollmentSecret":"test_enrollmentSecret", "mspid":"test_mspId", "enrollment":{"identity":{"certificate":"test_certificate"}, "signingIdentity":"test_signingIdentity"}}', false).should.be.rejectedWith(/Private key missing from key store. Can not establish the signing identity for user cfg/); + }); + }); + + describe('#toString', () => { + it('should create a state and return it when the user has no signingIdentity or identity', () => { + const user = new User({enrollmentID: 'cfg', name: 'cfg', roles: 'test_role', affiliation: 'test_affiliation'}); + + user.toString().should.equal('{"name":"cfg","mspid":"","roles":"test_role","affiliation":"test_affiliation","enrollmentSecret":"","enrollment":{}}'); + }); + + it('should set serializedEnrollment.signingIdentity if the user has a signingIdentity', () => { + const user = new User({enrollmentID: 'cfg', name: 'cfg', roles: 'test_role', affiliation: 'test_affiliation'}); + user._signingIdentity = {_signer: {_key: {getSKI(){return 'test_signingIdentity';}}}}; + + user.toString().should.equal('{"name":"cfg","mspid":"","roles":"test_role","affiliation":"test_affiliation","enrollmentSecret":"","enrollment":{"signingIdentity":"test_signingIdentity"}}'); + }); + + it('should set serializedEnrollment.identity.certificate if the user has an identity.certificate', () => { + const user = new User({enrollmentID: 'cfg', name: 'cfg', roles: 'test_role', affiliation: 'test_affiliation'}); + user._identity = {_certificate: 'test_certificate'}; + + user.toString().should.equal('{"name":"cfg","mspid":"","roles":"test_role","affiliation":"test_affiliation","enrollmentSecret":"","enrollment":{"identity":{"certificate":"test_certificate"}}}'); + }); + }); + + describe('#User.isInstance', () => { + it('should return true if every user parameter is defined', () => { + const user = new User({enrollmentID: 'cfg', name: 'cfg', roles: 'test_role', affiliation: 'test_affiliation'}); + user._enrollmentSecret = 'test_enrollmentSecret'; + user._identity = 'test_identity'; + user._signingIdentity = 'test_signingIdentity'; + user._mspId = 'test_mspId'; + user.cryptoSuite = 'test_cryptoSuite'; + + User.isInstance(user).should.equal(true); + }); + + it('should return false if user._name is undefined', () => { + const user = new User({enrollmentID: 'cfg', name: 'cfg', roles: 'test_role', affiliation: 'test_affiliation'}); + user._enrollmentSecret = 'test_enrollmentSecret'; + user._identity = 'test_identity'; + user._signingIdentity = 'test_signingIdentity'; + user._mspId = 'test_mspId'; + user.cryptoSuite = 'test_cryptoSuite'; + user._name = undefined; + + User.isInstance(user).should.equal(false); + }); + + it('should return false if user._roles is undefined', () => { + const user = new User({enrollmentID: 'cfg', name: 'cfg', roles: 'test_role', affiliation: 'test_affiliation'}); + user._enrollmentSecret = 'test_enrollmentSecret'; + user._identity = 'test_identity'; + user._signingIdentity = 'test_signingIdentity'; + user._mspId = 'test_mspId'; + user.cryptoSuite = 'test_cryptoSuite'; + user._roles = undefined; + + User.isInstance(user).should.equal(false); + }); + + it('should return false if user._affiliation is undefined', () => { + const user = new User({enrollmentID: 'cfg', name: 'cfg', roles: 'test_role', affiliation: 'test_affiliation'}); + user._enrollmentSecret = 'test_enrollmentSecret'; + user._identity = 'test_identity'; + user._signingIdentity = 'test_signingIdentity'; + user._mspId = 'test_mspId'; + user.cryptoSuite = 'test_cryptoSuite'; + user._affiliation = undefined; + + User.isInstance(user).should.equal(false); + }); + + it('should return false if user._enrollmentSecret is undefined', () => { + const user = new User({enrollmentID: 'cfg', name: 'cfg', roles: 'test_role', affiliation: 'test_affiliation'}); + user._enrollmentSecret = undefined; + user._identity = 'test_identity'; + user._signingIdentity = 'test_signingIdentity'; + user._mspId = 'test_mspId'; + user.cryptoSuite = 'test_cryptoSuite'; + + User.isInstance(user).should.equal(false); + }); + + it('should return false if user._identity is undefined', () => { + const user = new User({enrollmentID: 'cfg', name: 'cfg', roles: 'test_role', affiliation: 'test_affiliation'}); + user._enrollmentSecret = 'test_enrollmentSecret'; + user._identity = undefined; + user._signingIdentity = 'test_signingIdentity'; + user._mspId = 'test_mspId'; + user.cryptoSuite = 'test_cryptoSuite'; + + User.isInstance(user).should.equal(false); + }); + + it('should return false if user._signingIdentity is undefined', () => { + const user = new User({enrollmentID: 'cfg', name: 'cfg', roles: 'test_role', affiliation: 'test_affiliation'}); + user._enrollmentSecret = 'test_enrollmentSecret'; + user._identity = 'test_identity'; + user._signingIdentity = undefined; + user._mspId = 'test_mspId'; + user.cryptoSuite = 'test_cryptoSuite'; + + User.isInstance(user).should.equal(false); + }); + + it('should return false if user._mspId is undefined', () => { + const user = new User({enrollmentID: 'cfg', name: 'cfg', roles: 'test_role', affiliation: 'test_affiliation'}); + user._enrollmentSecret = 'test_enrollmentSecret'; + user._identity = 'test_identity'; + user._signingIdentity = 'test_signingIdentity'; + user._mspId = undefined; + user.cryptoSuite = 'test_cryptoSuite'; + + User.isInstance(user).should.equal(false); + }); + + it('should return false if user._cryptoSuite is undefined', () => { + const user = new User({enrollmentID: 'cfg', name: 'cfg', roles: 'test_role', affiliation: 'test_affiliation'}); + user._enrollmentSecret = 'test_enrollmentSecret'; + user._identity = 'test_identity'; + user._signingIdentity = 'test_signingIdentity'; + user._mspId = 'test_mspId'; + user._cryptoSuite = undefined; + + User.isInstance(user).should.equal(false); + }); + }); +}); diff --git a/fabric-client/test/packager/BasePackager.js b/fabric-client/test/packager/BasePackager.js new file mode 100644 index 0000000000..ad61e5642b --- /dev/null +++ b/fabric-client/test/packager/BasePackager.js @@ -0,0 +1,307 @@ +/* + * 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 BasePackager = rewire('../../lib/packager/BasePackager'); + +const chai = require('chai'); +const chaiAsPromised = require('chai-as-promised'); +chai.use(chaiAsPromised); +const sinon = require('sinon'); +const should = chai.should(); + +describe('BasePackager', () => { + + let revert; + let sandbox; + let FakeLogger; + + let Child; + let ValidChild; + + beforeEach(() => { + revert = []; + sandbox = sinon.createSandbox(); + Child = class extends BasePackager {}; + ValidChild = class extends BasePackager { + package(chaincodePath, metadataPath) { + super.package(chaincodePath, metadataPath); + } + }; + FakeLogger = { + error: () => {}, + debug: () => {} + }; + sinon.stub(FakeLogger); + revert.push(BasePackager.__set__('logger', FakeLogger)); + }); + + afterEach(() => { + if (revert.length) { + revert.forEach(Function.prototype.call, Function.prototype.call); + } + sandbox.restore(); + }); + + describe('#constructor', () => { + it('should throw if instance created', () => { + (() => { + new BasePackager(); + }).should.throw(TypeError, 'Can not construct abstract class.'); + }); + + it ('should throw an error if package isnt overriden', () => { + Child.prototype.constructor = function() {}; + (() => { + new Child(); + }).should.throw(TypeError, 'Please implement method package from child class'); + }); + }); + + describe('#package', () => { + it('should throw a TypeError', () => { + const packager = new ValidChild(); + + (() => { + packager.package(); + }).should.throw(TypeError, 'Please implement method package from child class'); + }); + + it('should throw a TypeError', () => { + const packager = new ValidChild(); + (() => { + packager.package('chaincode-path'); + }).should.throw(TypeError, 'Please implement method package from child class'); + }); + }); + + describe('#findSource', () => { + it('should throw an error', () => { + const packager = new ValidChild(); + (() => { + packager.findSource(); + }).should.throw(Error, 'abstract function called'); + }); + + it('should throw an error', () => { + const packager = new ValidChild(); + (() => { + packager.findSource('filepath'); + }).should.throw(Error, 'abstract function called'); + }); + }); + + describe('#findMetadataDescriptors', () => { + let onStub; + let klawStub; + let isMetadataStub; + let isFileStub; + let packager; + beforeEach(() => { + isMetadataStub = sandbox.stub(); + isFileStub = sandbox.stub(); + onStub = sandbox.stub(); + onStub.returns({on: onStub}); + klawStub = sandbox.stub().returns({on: onStub}); + revert.push(BasePackager.__set__('klaw', klawStub)); + packager = new ValidChild(); + packager.isMetadata = isMetadataStub; + }); + + it('should throw an error and log', async() => { + onStub.withArgs('error').yields(new Error(), {item: 'item'}); + try { + await packager.findMetadataDescriptors('path'); + should.fail(); + } catch (err) { + err.should.be.instanceof(Error); + sinon.assert.calledWith(FakeLogger.error, 'error while packaging item %j :: %s'); + } + }); + + it('should resolve after recieving data and end being called when entry is a file and contains metadata', async() => { + isFileStub.returns(true); + isMetadataStub.returns(true); + onStub.withArgs('data').yields({stats: {isFile: isFileStub}, path: 'path'}).returns({on: onStub}); + onStub.withArgs('end').yields(); + const descriptors = await packager.findMetadataDescriptors('path'); + sinon.assert.called(isFileStub); + sinon.assert.calledWith(isMetadataStub, 'path'); + sinon.assert.calledWith(FakeLogger.debug, ' findMetadataDescriptors :: %j', {name: 'META-INF', fqp: 'path'}); + descriptors.should.deep.equal([{name: 'META-INF', fqp: 'path'}]); + }); + + it('should resolve after recieving data that isnt a file', async() => { + isFileStub.returns(); + isMetadataStub.returns(true); + onStub.withArgs('data').yields({stats: {isFile: isFileStub}, path: 'path'}).returns({on: onStub}); + onStub.withArgs('end').yields(); + const descriptors = await packager.findMetadataDescriptors('path'); + sinon.assert.called(isFileStub); + descriptors.should.deep.equal([]); + }); + }); + + describe('#isMetadata', () => { + it('should return true if the file is a json file', () => { + const packager = new ValidChild(); + packager.isMetadata('file.json').should.be.true; + }); + + it('should return false if the file is not a json file', () => { + const packager = new ValidChild(); + packager.isMetadata('file.yaml').should.be.false; + }); + }); + + describe('#isSource', () => { + + it('should return true if the file format is in keep', () => { + const packager = new ValidChild(); + packager.keep = ['.json', '.yaml']; + packager.isSource('file.json').should.be.true; + packager.isSource('file.yaml').should.be.true; + }); + + it('should return false if the file format is in keep', () => { + const packager = new ValidChild(); + packager.keep = ['.json', '.yaml']; + packager.isMetadata('file.js').should.be.false; + packager.isMetadata('file.py').should.be.false; + }); + }); + + describe('#packEntry', () => { + let packager; + let readFileSyncStub; + let entryStub; + beforeEach(() => { + entryStub = sandbox.stub(); + readFileSyncStub = sandbox.stub(); + revert.push(BasePackager.__set__('fs.readFileSync', readFileSyncStub)); + + packager = new ValidChild(); + }); + + it('should throw an error if fs reads nothing', async() => { + readFileSyncStub.returns(null); + try { + await packager.packEntry({}, {fqp: 'file-name'}); + should.fail(); + } catch (err) { + err.should.be.instanceof(Error); + err.message.should.equal('failed to read file-name'); + } + }); + + it('should reject with error if pack.entry callback has an error', async() => { + readFileSyncStub.returns({size: 10}); + entryStub.yields(new Error('Entry error')); + try { + await packager.packEntry({entry: entryStub}, {fqp: 'file-name'}); + should.fail(); + } catch (err) { + err.should.be.instanceof(Error); + err.message.should.equal('Entry error'); + } + }); + + it('should resolve with true if pack.entry callback does not have an error', async() => { + readFileSyncStub.returns({size: 10}); + entryStub.yields(); + const result = await packager.packEntry({entry: entryStub}, {fqp: 'file-name', name: 'name'}); + const header = { + name: 'name', + size: 10, + mode: 0o100644, + atime: new Date(0), + mtime: new Date(0), + ctime: new Date(0), + }; + sinon.assert.calledWithMatch(entryStub, header, {size: 10}, Function); + result.should.be.true; + }); + }); + + describe('#generateTarGz', () => { + let tarStub; + let zlibStub; + + let onStub; + let pipeStub; + let createGzipStub; + let packStub; + let packEntryStub; + let finalizeStub; + + let packager; + beforeEach(() => { + onStub = sandbox.stub(); + onStub.returns({on: onStub}); + pipeStub = sandbox.stub(); + pipeStub.returns({on: onStub, pipe: pipeStub}); + createGzipStub = sandbox.stub(); + finalizeStub = sandbox.stub(); + packStub = sandbox.stub().returns({pipe: pipeStub, finalize: finalizeStub}); + tarStub = {pack: packStub}; + revert.push(BasePackager.__set__('tar', tarStub)); + zlibStub = {createGzip: createGzipStub}; + revert.push(BasePackager.__set__('zlib', zlibStub)); + packEntryStub = sandbox.stub(); + packager = new ValidChild(); + packager.packEntry = packEntryStub; + }); + + it('should reject with error if on error is called', async() => { + onStub.withArgs('error').yields(new Error()); + createGzipStub.returns('create-gzip'); + try { + await packager.generateTarGz([], 'dest'); + should.fail(); + } catch (e) { + e.should.be.instanceof(Error); + } + sinon.assert.calledWith(pipeStub, 'dest'); + sinon.assert.calledWith(pipeStub, 'create-gzip'); + }); + + it('should resolve with true', async() => { + onStub.withArgs('finish').yields(); + const result = await packager.generateTarGz(); + result.should.be.true; + }); + + it('should throw an error if a task promise is rejected', async() => { + packEntryStub.onCall(0).resolves(); + packEntryStub.onCall(1).rejects(); + try { + await packager.generateTarGz(['desc1', 'desc2'], 'dest'); + should.fail(); + } catch (e) { + e.should.be.instanceof(Error); + } + }); + + it('should resolve all promises and call finalize', (done) => { + finalizeStub.callsFake(done); + packEntryStub.resolves(); + packager.generateTarGz(['desc1'], 'dest') + .then(() => { + sinon.assert.called(finalizeStub); + }); + }); + }); +}); diff --git a/fabric-client/test/utils/ChannelHelper.js b/fabric-client/test/utils/ChannelHelper.js new file mode 100644 index 0000000000..c545f7f775 --- /dev/null +++ b/fabric-client/test/utils/ChannelHelper.js @@ -0,0 +1,137 @@ +/* + * 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 ChannelHelper = rewire('../../lib/utils/ChannelHelper'); + +const chai = require('chai'); +const chaiAsPromised = require('chai-as-promised'); +chai.use(chaiAsPromised); +const sinon = require('sinon'); + +describe('ChannelHelper', () => { + let revert; + let sandbox; + + beforeEach(() => { + revert = []; + sandbox = sinon.createSandbox(); + }); + + afterEach(() => { + if (revert.length) { + revert.forEach(Function.prototype.call, Function.prototype.call); + } + sandbox.restore(); + }); + + describe('#buildTransactionProposal', () => { + let getHeaderStub; + let buildTransactionProposal; + let ChaincodeEndorserActionStub; + let setProposalPayloadStub; + let setEndorsementsStub; + let ChaincodeActionPayloadStub; + let setActionStub; + let ChaincodeProposalPayloadDecodeStub; + let ChaincodeProposalPayloadStub; + let setInputStub; + let setChaincodeProposalPayloadStub; + let TransactionActionStub; + let setHeaderStub; + let headerDecodeStub; + let setPayloadStub; + let getSignatureHeaderStub; + let toBufferStub; + let TransactionStub; + let PayloadStub; + let setDataStub; + + before(() => { + buildTransactionProposal = ChannelHelper.__get__('buildTransactionProposal'); + }); + + beforeEach(() => { + getHeaderStub = sandbox.stub(); + getSignatureHeaderStub = sandbox.stub(); + headerDecodeStub = sandbox.stub().returns({getSignatureHeader: getSignatureHeaderStub}); + revert.push(ChannelHelper.__set__('_commonProto.Header.decode', headerDecodeStub)); + setProposalPayloadStub = sandbox.stub(); + setEndorsementsStub = sandbox.stub(); + ChaincodeEndorserActionStub = sandbox.stub() + .returns({setEndorsements: setEndorsementsStub, setProposalResponsePayload: setProposalPayloadStub}); + revert.push(ChannelHelper.__set__(' _transProto.ChaincodeEndorsedAction', ChaincodeEndorserActionStub)); + setActionStub = sandbox.stub(); + setChaincodeProposalPayloadStub = sandbox.stub(); + toBufferStub = sandbox.stub(); + ChaincodeActionPayloadStub = sandbox.stub() + .returns({setAction: setActionStub, setChaincodeProposalPayload: setChaincodeProposalPayloadStub, toBuffer: toBufferStub}); + revert.push(ChannelHelper.__set__('_transProto.ChaincodeActionPayload', ChaincodeActionPayloadStub)); + setInputStub = sandbox.stub(); + ChaincodeProposalPayloadStub = sandbox.stub().returns({setInput: setInputStub}); + revert.push(ChannelHelper.__set__('_proposalProto.ChaincodeProposalPayload', ChaincodeProposalPayloadStub)); + ChaincodeProposalPayloadDecodeStub = sandbox.stub(); + revert.push(ChannelHelper.__set__('_proposalProto.ChaincodeProposalPayload.decode', ChaincodeProposalPayloadDecodeStub)); + setHeaderStub = sandbox.stub(); + setPayloadStub = sandbox.stub(); + TransactionActionStub = sandbox.stub().returns({setHeader: setHeaderStub, setPayload: setPayloadStub}); + revert.push(ChannelHelper.__set__('_transProto.TransactionAction', TransactionActionStub)); + TransactionStub = sandbox.stub().returns({toBuffer: toBufferStub, setActions: setActionStub}); + revert.push(ChannelHelper.__set__('_transProto.Transaction', TransactionStub)); + setDataStub = sandbox.stub(); + PayloadStub = sandbox.stub().returns({setHeader: setHeaderStub, setData: setDataStub}); + revert.push(ChannelHelper.__set__('_commonProto.Payload', PayloadStub)); + }); + + it('should return the payload', () => { + const chaincodeProposal = { + getHeader: getHeaderStub.returns('header'), + payload: 'chaincode-payload' + }; + ChaincodeProposalPayloadStub.returns({toBuffer: toBufferStub, setInput: setInputStub}); + const endorsements = []; + const proposalReponse = { + payload: 'payload' + }; + toBufferStub.onCall(0).returns('proposal-payload'); + toBufferStub.onCall(1).returns('chaincode-action-payload'); + toBufferStub.onCall(2).returns('transaction'); + ChaincodeProposalPayloadDecodeStub.returns({input: 'input'}); + getSignatureHeaderStub.returns('signature-header'); + const payload = buildTransactionProposal(chaincodeProposal, endorsements, proposalReponse); + sinon.assert.calledWith(headerDecodeStub, 'header'); + sinon.assert.calledWith(setProposalPayloadStub, 'payload'); + sinon.assert.calledWith(setEndorsementsStub, endorsements); + sinon.assert.called(ChaincodeActionPayloadStub); + sinon.assert.calledWith(setActionStub, new ChaincodeEndorserActionStub()); + sinon.assert.calledWith(ChaincodeProposalPayloadDecodeStub, 'chaincode-payload'); + sinon.assert.called(ChaincodeActionPayloadStub); + sinon.assert.calledWith(setInputStub, 'input'); + sinon.assert.calledThrice(toBufferStub); + sinon.assert.calledWith(setChaincodeProposalPayloadStub, 'proposal-payload'); + sinon.assert.calledWith(TransactionActionStub); + sinon.assert.called(getSignatureHeaderStub); + sinon.assert.calledWith(setHeaderStub, 'signature-header'); + sinon.assert.calledWith(setPayloadStub, 'chaincode-action-payload'); + sinon.assert.called(TransactionStub); + sinon.assert.calledWithMatch(setActionStub, Array); + sinon.assert.called(PayloadStub); + sinon.assert.calledWith(setHeaderStub, new headerDecodeStub()); + sinon.assert.calledWith(setDataStub, 'transaction'); + payload.should.deep.equal(new PayloadStub()); + }); + }); +});