From 8d9202939520243dab71025d36602aabca113c49 Mon Sep 17 00:00:00 2001 From: Yi DENG Date: Fri, 1 Mar 2019 10:22:38 +0800 Subject: [PATCH 1/4] Improve test config, #49 Signed-off-by: Yi DENG --- .eslintrc | 11 ++++++++++- package.json | 2 ++ 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/.eslintrc b/.eslintrc index 8a8e1ca..9873a66 100644 --- a/.eslintrc +++ b/.eslintrc @@ -8,6 +8,10 @@ "localStorage": true }, "extends": "eslint-config-airbnb", + "plugins": ["eslint-plugin-jest"], + "env": { + "jest/globals": true + }, "rules": { "import/extensions": 0, "import/no-extraneous-dependencies": 0, @@ -24,6 +28,11 @@ "class-methods-use-this": 0, "no-param-reassign": 0, "no-loop-func": 0, - "global-require": 0 + "global-require": 0, + "jest/no-disabled-tests": "warn", + "jest/no-focused-tests": "error", + "jest/no-identical-title": "error", + "jest/prefer-to-have-length": "warn", + "jest/valid-expect": "error" } } diff --git a/package.json b/package.json index bfec443..ae5c084 100644 --- a/package.json +++ b/package.json @@ -93,6 +93,8 @@ "eslint-plugin-import": "^2", "eslint-plugin-jsx-a11y": "^5", "eslint-plugin-react": "^7", + "@types/jest": "^23.3.13", + "eslint-plugin-jest": "^22.3.0", "jest": "^23.6.0" } } From 82e5c4d804c3bf75008f878fe9c03235e1646867 Mon Sep 17 00:00:00 2001 From: Yi DENG Date: Mon, 4 Mar 2019 17:57:23 +0800 Subject: [PATCH 2/4] Test case: fabricClient, #49 Signed-off-by: Yi DENG --- fabric/v1.1/basic-network/teardown.sh | 2 +- src/util/fabric.js | 21 ++++-- test/util/fabric.test.js | 95 +++++++++++++++++++++++++++ 3 files changed, 111 insertions(+), 7 deletions(-) create mode 100644 test/util/fabric.test.js diff --git a/fabric/v1.1/basic-network/teardown.sh b/fabric/v1.1/basic-network/teardown.sh index 7036793..f115207 100755 --- a/fabric/v1.1/basic-network/teardown.sh +++ b/fabric/v1.1/basic-network/teardown.sh @@ -14,6 +14,6 @@ docker-compose -f docker-compose.yml kill && docker-compose -f docker-compose.ym rm -f ~/.hfc-key-store/* # remove chaincode docker images -docker rmi $(docker images dev-* -q) +# docker rmi $(docker images dev-* -q) # Your system is now clean diff --git a/src/util/fabric.js b/src/util/fabric.js index d3002d9..03b1b2a 100644 --- a/src/util/fabric.js +++ b/src/util/fabric.js @@ -18,10 +18,11 @@ class FabricClient { this.fabric_client = fabricClient; } - _gitConfig() { + // 抽出空挡,插入配置文件,以便集成测试 + _getConfig(configDb) { const self = this; return new Promise((resolve, reject) => { - db.find({}, (err, resultList) => { + configDb.find({}, (err, resultList) => { if (err) { logger.info('the operation of find documents failed!'); reject('error'); @@ -117,6 +118,7 @@ class FabricClient { } this.channels[channelName] = channel; } else { + logger.info(`channel(${channelName}) exists, get it from memory.`); channel = this.channels[channelName]; } return channel; @@ -773,12 +775,11 @@ class FabricClient { let __fabricClient; -// FabricClient单例模式。后续考虑优化为多套身份,多个client -export default function getFabricClientSingleton() { +export function getFabricClientSingletonHelper(dbConfig) { if (!__fabricClient) { - logger.info('strat create fabric client'); + logger.info('instantiating fabric client.'); __fabricClient = new FabricClient(); - return __fabricClient._gitConfig() + return __fabricClient._getConfig(dbConfig) .then(__fabricClient._config) .then(__fabricClient._enrollUser) .then(() => Promise.resolve(__fabricClient)); @@ -786,6 +787,14 @@ export default function getFabricClientSingleton() { return Promise.resolve(__fabricClient); } +// TODO: 考虑是否去除export default,全部使用export。 +// 由此保证import无需再区分 import something from 'lib' 与 import {something} from 'lib' + +// FabricClient单例模式。后续考虑优化为多套身份,多个client +export default function getFabricClientSingleton() { + return getFabricClientSingletonHelper(db); +} + export function deleteFabricClientSingleton() { __fabricClient = null; } diff --git a/test/util/fabric.test.js b/test/util/fabric.test.js new file mode 100644 index 0000000..9f73a73 --- /dev/null +++ b/test/util/fabric.test.js @@ -0,0 +1,95 @@ +// Copyright 2019 The hyperledger-fabric-desktop Authors. All rights reserved. +import getFabricClientSingleton from '../../src/util/fabric'; + +const { execSync } = require('child_process'); +const logger = require('electron-log'); + +// Steps +// 1. start fabric network +// 2. call client functions +// 3. stop fabric network, clean up + +function initFabricNetwork() { + logger.info('Shuting down old Fabric Network.'); + let buf = execSync('cd fabric/v1.1/basic-network && ./teardown.sh'); + logger.debug(buf.toString()); + + logger.info('Initiating Fabric Network.'); + buf = execSync('cd fabric/v1.1/fabcar && ./startFabric.sh'); + logger.debug(buf.toString()); +} + +function clearFabricNetwork() { + // FIXME: 关闭网络需处理异步问题。 直接关闭会导致异步测试未结束,而网络被关闭。 + // 当前解决方案,在initFabricNetwork()中关闭网络 + + // logger.info('clearing Fabric Network.'); + // const buf = execSync('cd fabric/v1.1/basic-network && ./teardown.sh'); + // logger.debug(buf.toString()); +} + +beforeAll(initFabricNetwork); + +afterAll(clearFabricNetwork); + + +describe('Fabric Client Basic', () => { + it('instantiate client.', () => + getFabricClientSingleton() + .then((client) => { + expect(client) + .not + .toBeNull(); + })); + + it('query chaincode', () => { + const clientPromise = getFabricClientSingleton(); + return clientPromise.then((client) => { + logger.info('OK. get client.'); + logger.info('client.channel: ', client.channels); + client.queryCc('fabcar', 'queryAllCars', null, 'mychannel') + .then((result) => { + logger.info('query result: ', result); + expect(result) + .not + .toBeNull(); + }); + }); + }); +}); + +describe('Fabric Client Advanced', () => { + describe('invoke chaincode', () => { + it('invoke for one peer', () => { + }); + + it('invoke for multiple peers', () => { + // TODO: to be implemented + }); + }); + + it('install chaincode', () => { + + }); + + it('instantiate chaincode', () => { + + }); + + it('upgrade chaincode', () => { + + }); + + it('query block info', () => { + + }); + + it('create channel', () => { + + }); + + it('join peer to channel', () => { + + }); +}); + From ea7beceaf6bdaff4c175e1b17684d00b131b67c1 Mon Sep 17 00:00:00 2001 From: Yi DENG Date: Tue, 5 Mar 2019 11:13:56 +0800 Subject: [PATCH 3/4] add introduction video. #187 Signed-off-by: Yi DENG --- README.md | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 2f8be44..d2b4e10 100644 --- a/README.md +++ b/README.md @@ -15,12 +15,14 @@ and aims to help people use and manage fabric networks easily. ## Releases -- [package](https://github.com/blockchain-desktop/hyperledger-fabric-desktop/releases) +- [released app](https://github.com/blockchain-desktop/hyperledger-fabric-desktop/releases) - [source code](https://github.com/blockchain-desktop/hyperledger-fabric-desktop) ## Documentation, Getting Started and Develop Guideline -It is highly recommended to read [index.md](doc/doc-En/index-En.md) first. +It is highly recommended to read [index.md](doc/doc-En/index-En.md) first. + +You can also watch the [introduction video](https://wiki.hyperledger.org/download/attachments/2392116/20190228_Fabric_Desktop.mp4?version=1&modificationDate=1551429922000&api=v2). ## Contributing @@ -76,7 +78,10 @@ available at http://creativecommons.org/licenses/by/4.0/. * [使用者:功能介绍](doc/doc-Ch/tutorial-Ch.md) * [开发者:参与开发](doc/doc-Ch/coding-guidelines-Ch.md) * [开发者:贡献代码](doc/doc-Ch/CONTRIBUTING-Ch.md) -* [项目架构](./doc/architect.md) +* [项目架构](./doc/architect.md) + +介绍视频 +* [超级账本meetup录像](https://pan.baidu.com/s/1VU_bt4Oo_OJtk5DhCZ3L4A) ## 如何参与? From dc9d999131c75624ea659cd47f97c9cb1c9005d2 Mon Sep 17 00:00:00 2001 From: Yi DENG Date: Tue, 5 Mar 2019 19:03:05 +0800 Subject: [PATCH 4/4] Improve fabricClient test: bring fabric network up. #49 Signed-off-by: Yi DENG --- src/util/fabric.js | 4 +- test/resources/key/users/Org1MSP | 1 + ...e62130f8008a0bf996e4e4b84cd097a747fec-priv | 5 ++ ...1e62130f8008a0bf996e4e4b84cd097a747fec-pub | 4 ++ test/resources/persistence/.gitkeep | 3 ++ test/resources/persistence/block.db | 0 test/resources/persistence/chaincode.db | 0 test/resources/persistence/config.db | 1 + test/resources/persistence/invoke.db | 0 test/util/fabric.test.js | 48 +++++++++++++++---- 10 files changed, 56 insertions(+), 10 deletions(-) create mode 100644 test/resources/key/users/Org1MSP create mode 100644 test/resources/key/users/cd96d5260ad4757551ed4a5a991e62130f8008a0bf996e4e4b84cd097a747fec-priv create mode 100644 test/resources/key/users/cd96d5260ad4757551ed4a5a991e62130f8008a0bf996e4e4b84cd097a747fec-pub create mode 100644 test/resources/persistence/.gitkeep create mode 100644 test/resources/persistence/block.db create mode 100644 test/resources/persistence/chaincode.db create mode 100644 test/resources/persistence/config.db create mode 100644 test/resources/persistence/invoke.db diff --git a/src/util/fabric.js b/src/util/fabric.js index 03b1b2a..e74fe2d 100644 --- a/src/util/fabric.js +++ b/src/util/fabric.js @@ -199,9 +199,9 @@ class FabricClient { const self = this; return this._enrollUser(this).then((user) => { if (user && user.isEnrolled()) { - logger.info('Successfully loaded user1 from persistence'); + logger.info(`Successfully loaded user(${user.getName()}) from persistence`); } else { - logger.error('Failed to get user1.... run registerUser.js'); + logger.error('Failed to get user run registerUser.js'); return Promise.reject(new Error('Failed to get user1.... run registerUser.js')); } diff --git a/test/resources/key/users/Org1MSP b/test/resources/key/users/Org1MSP new file mode 100644 index 0000000..b6081c7 --- /dev/null +++ b/test/resources/key/users/Org1MSP @@ -0,0 +1 @@ +{"name":"Org1MSP","mspid":"Org1MSP","roles":null,"affiliation":"","enrollmentSecret":"","enrollment":{"signingIdentity":"cd96d5260ad4757551ed4a5a991e62130f8008a0bf996e4e4b84cd097a747fec","identity":{"certificate":"-----BEGIN CERTIFICATE-----\nMIICGDCCAb+gAwIBAgIQFSxnLAGsu04zrFkAEwzn6zAKBggqhkjOPQQDAjBzMQsw\nCQYDVQQGEwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTEWMBQGA1UEBxMNU2FuIEZy\nYW5jaXNjbzEZMBcGA1UEChMQb3JnMS5leGFtcGxlLmNvbTEcMBoGA1UEAxMTY2Eu\nb3JnMS5leGFtcGxlLmNvbTAeFw0xNzA4MzEwOTE0MzJaFw0yNzA4MjkwOTE0MzJa\nMFsxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpDYWxpZm9ybmlhMRYwFAYDVQQHEw1T\nYW4gRnJhbmNpc2NvMR8wHQYDVQQDDBZBZG1pbkBvcmcxLmV4YW1wbGUuY29tMFkw\nEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEV1dfmKxsFKWo7o6DNBIaIVebCCPAM9C/\nsLBt4pJRre9pWE987DjXZoZ3glc4+DoPMtTmBRqbPVwYcUvpbYY8p6NNMEswDgYD\nVR0PAQH/BAQDAgeAMAwGA1UdEwEB/wQCMAAwKwYDVR0jBCQwIoAgQjmqDc122u64\nugzacBhR0UUE0xqtGy3d26xqVzZeSXwwCgYIKoZIzj0EAwIDRwAwRAIgXMy26AEU\n/GUMPfCMs/nQjQME1ZxBHAYZtKEuRR361JsCIEg9BOZdIoioRivJC+ZUzvJUnkXu\no2HkWiuxLsibGxtE\n-----END CERTIFICATE-----\n"}}} \ No newline at end of file diff --git a/test/resources/key/users/cd96d5260ad4757551ed4a5a991e62130f8008a0bf996e4e4b84cd097a747fec-priv b/test/resources/key/users/cd96d5260ad4757551ed4a5a991e62130f8008a0bf996e4e4b84cd097a747fec-priv new file mode 100644 index 0000000..fef217d --- /dev/null +++ b/test/resources/key/users/cd96d5260ad4757551ed4a5a991e62130f8008a0bf996e4e4b84cd097a747fec-priv @@ -0,0 +1,5 @@ +-----BEGIN PRIVATE KEY----- +MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgRgQr347ij6cjwX7m +KjzbbD8Tlwdfu6FaubjWJWLGyqahRANCAARXV1+YrGwUpajujoM0EhohV5sII8Az +0L+wsG3iklGt72lYT3zsONdmhneCVzj4Og8y1OYFGps9XBhxS+lthjyn +-----END PRIVATE KEY----- diff --git a/test/resources/key/users/cd96d5260ad4757551ed4a5a991e62130f8008a0bf996e4e4b84cd097a747fec-pub b/test/resources/key/users/cd96d5260ad4757551ed4a5a991e62130f8008a0bf996e4e4b84cd097a747fec-pub new file mode 100644 index 0000000..d1ebfea --- /dev/null +++ b/test/resources/key/users/cd96d5260ad4757551ed4a5a991e62130f8008a0bf996e4e4b84cd097a747fec-pub @@ -0,0 +1,4 @@ +-----BEGIN PUBLIC KEY----- +MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEV1dfmKxsFKWo7o6DNBIaIVebCCPA +M9C/sLBt4pJRre9pWE987DjXZoZ3glc4+DoPMtTmBRqbPVwYcUvpbYY8pw== +-----END PUBLIC KEY----- diff --git a/test/resources/persistence/.gitkeep b/test/resources/persistence/.gitkeep new file mode 100644 index 0000000..a10d4fe --- /dev/null +++ b/test/resources/persistence/.gitkeep @@ -0,0 +1,3 @@ +# Ignore everything in this directory +* +# Except this file !.gitkeep \ No newline at end of file diff --git a/test/resources/persistence/block.db b/test/resources/persistence/block.db new file mode 100644 index 0000000..e69de29 diff --git a/test/resources/persistence/chaincode.db b/test/resources/persistence/chaincode.db new file mode 100644 index 0000000..e69de29 diff --git a/test/resources/persistence/config.db b/test/resources/persistence/config.db new file mode 100644 index 0000000..bdbfb7a --- /dev/null +++ b/test/resources/persistence/config.db @@ -0,0 +1 @@ +{"id":0,"isSign":1,"_id":"0RYjfGm5ZQNGBcxV","peerGrpcUrl":"grpc://localhost:7051","peerEventUrl":"grpc://localhost:7053","ordererUrl":"grpc://localhost:7050","mspid":"Org1MSP","tlsPeerPath":"","tlsOrdererPath":"","path":"test/resources/key/users/"} diff --git a/test/resources/persistence/invoke.db b/test/resources/persistence/invoke.db new file mode 100644 index 0000000..e69de29 diff --git a/test/util/fabric.test.js b/test/util/fabric.test.js index 9f73a73..5119ab2 100644 --- a/test/util/fabric.test.js +++ b/test/util/fabric.test.js @@ -1,8 +1,10 @@ // Copyright 2019 The hyperledger-fabric-desktop Authors. All rights reserved. -import getFabricClientSingleton from '../../src/util/fabric'; +import { getFabricClientSingletonHelper } from '../../src/util/fabric'; const { execSync } = require('child_process'); const logger = require('electron-log'); +const Datastore = require('nedb'); +const path = require('path'); // Steps // 1. start fabric network @@ -28,14 +30,23 @@ function clearFabricNetwork() { // logger.debug(buf.toString()); } -beforeAll(initFabricNetwork); +beforeAll(() => { + jest.setTimeout(10000); + initFabricNetwork(); +}); afterAll(clearFabricNetwork); +// 注意, config.db文件中的"path"字段,对应fabric-node-sdk的用户私钥仓库路径,需根据测试环境配置, +// 目前只处理fabric v1.1-basic-network的例子,处理fabric v1.3需同时考虑sdk版本升级,以及启动脚本的调整等 +const configDbForTest = new Datastore({ + filename: path.join(__dirname, '../resources/persistence/config.db'), + autoload: true, +}); describe('Fabric Client Basic', () => { it('instantiate client.', () => - getFabricClientSingleton() + getFabricClientSingletonHelper(configDbForTest) .then((client) => { expect(client) .not @@ -43,16 +54,19 @@ describe('Fabric Client Basic', () => { })); it('query chaincode', () => { - const clientPromise = getFabricClientSingleton(); + const clientPromise = getFabricClientSingletonHelper(configDbForTest); return clientPromise.then((client) => { - logger.info('OK. get client.'); - logger.info('client.channel: ', client.channels); + logger.info('OK. Got client. client.channel: ', client.channels); client.queryCc('fabcar', 'queryAllCars', null, 'mychannel') .then((result) => { logger.info('query result: ', result); expect(result) .not .toBeNull(); + }) + .catch((err) => { + logger.error(err); + throw new Error(); }); }); }); @@ -60,8 +74,26 @@ describe('Fabric Client Basic', () => { describe('Fabric Client Advanced', () => { describe('invoke chaincode', () => { - it('invoke for one peer', () => { - }); + it('invoke for one peer', () => getFabricClientSingletonHelper(configDbForTest) + .then(client => client.invokeCc('fabcar', + 'changeCarOwner', + ['CAR0', 'newPerson'], + 'mychannel', + [], + [], + []) + .then((result) => { + logger.info('invoke result: ', result); + expect(result) + .not + .toBeNull(); + return Promise.resolve(); + })) + .catch((err) => { + logger.info('catch invoke reject'); + logger.info(err); + throw err; + })); it('invoke for multiple peers', () => { // TODO: to be implemented