Skip to content

Commit

Permalink
[FABN-975] cucumber test framework
Browse files Browse the repository at this point in the history
- Framework for cucumber testing driven by CCP
- Addition of scenarios for create/join channels based on CCP
- Addition of scenarios for install/instantiate chaincode based on CCP
- Addition of scenarios for network API submit/execute transaction
- Modification of gulp test.js to include cucumber test suite

Change-Id: I1abe78254db09abe28339b8f5ed58e9a55f20511
Signed-off-by: nkl199@yahoo.co.uk <nkl199@yahoo.co.uk>
  • Loading branch information
nklincoln committed Oct 31, 2018
1 parent 0ef68a5 commit 9a37ef1
Show file tree
Hide file tree
Showing 110 changed files with 5,117 additions and 74 deletions.
140 changes: 82 additions & 58 deletions build/tasks/test.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ const addsrc = require('gulp-add-src');
const gulp = require('gulp');
const mocha = require('gulp-mocha');
const tape = require('gulp-tape');

const runSequence = require('run-sequence');
const tapColorize = require('tap-colorize');

const fs = require('fs-extra');
Expand All @@ -22,6 +22,9 @@ const util = require('util');
const shell = require('gulp-shell');
const testConstants = require('../../test/unit/constants.js');

// Debug level of Docker containers used in scenario tests
process.env.DOCKER_DEBUG='INFO';

// by default for running the tests print debug to a file
const debugPath = path.join(testConstants.tempdir, 'test-log/debug.log');
process.env.HFC_LOGGING = util.format('{"debug":"%s"}', escapeWindowsPath(debugPath));
Expand Down Expand Up @@ -88,14 +91,17 @@ gulp.task('clean-up', () => {

gulp.task('docker-clean', shell.task([
// stop and remove chaincode docker instances
'docker kill $(docker ps | grep "dev-peer0.org[12].example.com-[en]" | awk \'{print $1}\')',
'docker rm $(docker ps -a | grep "dev-peer0.org[12].example.com-[en]" | awk \'{print $1}\')',
'docker kill $(docker ps | grep "dev-" | awk \'{print $1}\')',
'docker rm $(docker ps -a | grep "dev-" | awk \'{print $1}\')',

// remove chaincode images so that they get rebuilt during test
'docker rmi $(docker images | grep "^dev-peer0.org[12].example.com-[en]" | awk \'{print $3}\')',
'docker rmi $(docker images | grep "^dev-" | awk \'{print $3}\')',

// clean up all the containers created by docker-compose
'docker-compose -f test/fixtures/docker-compose.yaml down'
// -tape
'docker-compose -f test/fixtures/docker-compose.yaml down',
// -cucumber
'docker-compose -f test/scenario/docker-compose/docker-compose-tls.yaml down'
], {
verbose: true, // so we can see the docker command output
ignoreErrors: true // kill and rm may fail because the containers may have been cleaned up
Expand All @@ -113,21 +119,37 @@ gulp.task('compile', shell.task([
ignoreErrors: false // once compile failed, throw error
}));

// Use nyc instead of gulp-istanbul to generate coverage report
// Cannot use gulp-istabul because it throws "unexpected identifier" for async/await functions
// Execute specific tests with code coverage enabled
// - Use nyc instead of gulp-istanbul to generate coverage report
// - Cannot use gulp-istabul because it throws "unexpected identifier" for async/await functions

// Main test to run all tests
gulp.task('test', shell.task(
'./node_modules/nyc/bin/nyc.js gulp run-test'
));

// Test to run all unit tests
gulp.task('test-headless', shell.task(
'./node_modules/nyc/bin/nyc.js gulp run-test-headless'
));

// Only run Mocha unit tests
gulp.task('test-mocha', shell.task(
'./node_modules/nyc/bin/nyc.js gulp run-test-mocha'
));

gulp.task('run-test-mocha', ['mocha-fabric-client', 'mocha-fabric-network'],
// Only run scenario tests
gulp.task('test-cucumber', shell.task(
'./node_modules/nyc/bin/nyc.js npm run test:cucumber'
));

// Definition of Mocha (unit) test suites
gulp.task('run-test-mocha', (done) => {
const tasks = ['mocha-fabric-ca-client', 'mocha-fabric-client', 'mocha-fabric-network'];
runSequence(...tasks, done);
});

gulp.task('mocha-fabric-ca-client',
() => {
return gulp.src(['./fabric-ca-client/test/**/*.js'], { read: false })
.pipe(mocha({ reporter: 'list', exit: true }));
Expand All @@ -148,22 +170,56 @@ gulp.task('mocha-fabric-network',
}
);

gulp.task('run-test', ['run-full', 'mocha-fabric-client', 'mocha-fabric-network'],
() => {
return gulp.src(['./fabric-ca-client/test/**/*.js'], { read: false })
.pipe(mocha({ reporter: 'list', exit: true }));
}
);
// Test to run all unit tests
gulp.task('test-tape', shell.task(
'./node_modules/nyc/bin/nyc.js gulp run-tape-unit'
));

// Definition of Cucumber (scenario) test suite
gulp.task('run-test-cucumber', shell.task(
'npm run test:cucumber'
));

// Main test method to run all test suites
// - lint, unit first, then FV, then scenario
gulp.task('run-test', (done) => {
const tasks = ['clean-up', 'docker-clean', 'pre-test', 'ca', 'compile', 'lint', 'run-test-mocha', 'run-tape-unit', 'docker-clean', 'run-test-cucumber'];
runSequence(...tasks, done);
});

gulp.task('run-test-headless', ['run-headless', 'mocha-fabric-client', 'mocha-fabric-network'],
// Run all non-integration tests
gulp.task('run-test-headless', (done) => {
const tasks = ['clean-up', 'pre-test', 'ca', 'lint', 'run-test-mocha', 'run-tape-unit'];
runSequence(...tasks, done);
});

gulp.task('run-tape-unit',
() => {
return gulp.src(['./fabric-ca-client/test/**/*.js'], { read: false })
.pipe(mocha({ reporter: 'list', exit: true }));
}
);
// this is needed to avoid a problem in tape-promise with adding
// too many listeners to the "unhandledRejection" event
process.setMaxListeners(0);

gulp.task('run-full', ['clean-up', 'lint', 'pre-test', 'compile', 'docker-ready', 'ca'],
return gulp.src(shouldRunPKCS11Tests([
'test/unit/**/*.js',
'!test/unit/constants.js',
'!test/unit/util.js',
'!test/unit/logger.js',
]))
.pipe(addsrc.append(
'test/unit/logger.js' // put this to the last so the debugging levels are not mixed up
))
.pipe(tape({
reporter: tapColorize()
}));
});

// Run tape e2e test suite
gulp.task('run-tape-e2e', ['docker-ready'],
() => {
// this is needed to avoid a problem in tape-promise with adding
// too many listeners to the "unhandledRejection" event
process.setMaxListeners(0);

// use individual tests to control the sequence they get executed
// first run the ca-tests that tests all the member registration
// and enrollment scenarios (good and bad calls). Then the rest
Expand All @@ -172,19 +228,13 @@ gulp.task('run-full', ['clean-up', 'lint', 'pre-test', 'compile', 'docker-ready'
// network
return gulp.src(shouldRunPKCS11Tests([
'test/unit/config.js', // needs to be first
'test/unit/**/*.js',
'!test/unit/constants.js',
'!test/unit/util.js',
'!test/unit/logger.js',
'test/integration/fabric-ca-affiliation-service-tests.js',
'test/integration/fabric-ca-identity-service-tests.js',
'test/integration/fabric-ca-certificate-service-tests.js',
'test/integration/fabric-ca-services-tests.js',
// channel: mychannel, chaincode: e2enodecc:v0
'test/integration/nodechaincode/e2e.js',
'test/integration/network-e2e/e2e.js',
'test/integration/network-e2e/e2e-hsm.js',
// channel: mychannel, chaincode: end2endnodesdk:v0/v1
'test/integration/e2e.js',
'test/integration/signTransactionOffline.js',
'test/integration/query.js',
Expand All @@ -196,10 +246,8 @@ gulp.task('run-full', ['clean-up', 'lint', 'pre-test', 'compile', 'docker-ready'
'test/integration/install.js',
'test/integration/events.js',
'test/integration/channel-event-hub.js',
// channel: mychannel, chaincode: end2endnodesdk:v3
'test/integration/upgrade.js',
'test/integration/get-config.js',
// channel: mychanneltx, chaincode: end2endnodesdk:v0
'test/integration/create-configtx-channel.js',
'test/integration/e2e/join-channel.js',
'test/integration/instantiate.js',
Expand All @@ -211,46 +259,22 @@ gulp.task('run-full', ['clean-up', 'lint', 'pre-test', 'compile', 'docker-ready'
'test/integration/javachaincode/e2e.js',
'test/integration/discovery.js',
'test/integration/grpc.js',
// channel: mychannelts chaincode: examplets:v1

// Typescript
'test/typescript/test.js',

// Perf
'test/integration/perf/orderer.js',
'test/integration/perf/peer.js'
]))
.pipe(addsrc.append(
'test/unit/logger.js' // put this to the last so the debugging levels are not mixed up
))
.pipe(tape({
reporter: tapColorize()
}));
});

gulp.task('run-headless', ['clean-up', 'lint', 'pre-test', 'ca'],
() => {
// this is needed to avoid a problem in tape-promise with adding
// too many listeners
// to the "unhandledRejection" event
process.setMaxListeners(0);

return gulp.src(shouldRunPKCS11Tests([
'test/unit/**/*.js',
'!test/unit/constants.js',
'!test/unit/util.js',
'!test/unit/logger.js'
]))
.pipe(addsrc.append(
'test/unit/logger.js' // put this to the last so the debugging levels are not mixed up
))
.pipe(tape({
reporter: tapColorize()
}));
});

// currently only the x64 CI jobs are configured with SoftHSM
// disable the pkcs11.js test for s390 or other jobs
// also skip it by default and allow it to be turned on manuall
// with an environment variable so everyone don't have to
// install SoftHsm just to run unit tests
// Filter out tests that should not be run on specific operating systems since only the x64 CI jobs are configured with SoftHSM
// - disable the pkcs11.js test for s390 or other jobs
// - may be enabled manually with an environment variable
function shouldRunPKCS11Tests(tests) {
if (typeof process.env.PKCS11_TESTS === 'string' && process.env.PKCS11_TESTS.toLowerCase() === 'false' && os.arch().match(/(x64|x86)/) !== null) {
tests.push('!test/unit/pkcs11.js');
Expand Down
6 changes: 5 additions & 1 deletion fabric-ca-client/package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
{
"name": "fabric-ca-client",
"description": "SDK for writing node.js applications to interact with Hyperledger Fabric. This package encapsulates the APIs to interact with the Fabric CA to manage user certificates lifecycle such as register, enroll, renew and revoke.",
"keywords" : ["hyperledger", "blockchain"],
"keywords": [
"hyperledger",
"blockchain"
],
"version": "1.4.0-snapshot",
"tag": "unstable",
"main": "index.js",
Expand All @@ -22,6 +25,7 @@
"bn.js": "^4.11.3",
"elliptic": "^6.2.3",
"fs-extra": "^6.0.1",
"grpc": "1.14.2",
"js-sha3": "^0.7.0",
"jsrsasign": "^7.2.2",
"jssha": "^2.1.0",
Expand Down
1 change: 0 additions & 1 deletion fabric-client/lib/Client.js
Original file line number Diff line number Diff line change
Expand Up @@ -1048,7 +1048,6 @@ const Client = class extends BaseClient {
throw new Error('Missing "chaincodePath" parameter in the proposal request');
}

console.log('request.targets', request.targets);
let peers = this.getTargetPeers(request.targets);
if (!peers && request.channelNames) {
peers = this.getPeersForOrgOnChannel(request.channelNames);
Expand Down
5 changes: 4 additions & 1 deletion fabric-client/package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
{
"name": "fabric-client",
"description": "SDK for writing node.js applications to interact with Hyperledger Fabric. This package encapsulates the APIs to interact with Peers and Orderers of the Fabric network to install and instantiate chaincodes, send transaction invocations and perform chaincode queries.",
"keywords" : ["hyperledger", "blockchain"],
"keywords": [
"hyperledger",
"blockchain"
],
"version": "1.4.0-snapshot",
"tag": "unstable",
"main": "index.js",
Expand Down
3 changes: 3 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
"test:ca-client": "npm run coverage -- fabric-ca-client/test",
"test:client": "npm run coverage -- fabric-client/test",
"test:network": "npm run coverage -- fabric-network/test",
"test:cucumber": "cucumber-js ./test/scenario/features/*.feature",
"test:all": "nyc npm run unit-test:all",
"unit-test:all": "npm run unit-test -- fabric-ca-client/test && npm run unit-test -- fabric-client/test && npm run unit-test -- fabric-network/test",
"coverage": "nyc npm run unit-test",
Expand All @@ -32,6 +33,7 @@
"bunyan": "^1.8.10",
"chai": "^4.1.2",
"chai-as-promised": "^7.1.1",
"cucumber": "5.0.1",
"del": "^3.0.0",
"elliptic": "^6.3.2",
"eslint": "^5.1.0",
Expand Down Expand Up @@ -64,6 +66,7 @@
"require-dir": "^1.0.0",
"rewire": "^4.0.1",
"rimraf": "2.6.2",
"run-sequence": "^2.2.1",
"sinon": "6.1.3",
"tap-colorize": "^1.2.0",
"tape": "^4.5.1",
Expand Down
2 changes: 1 addition & 1 deletion scripts/Jenkins_Scripts/CI_Script.sh
Original file line number Diff line number Diff line change
Expand Up @@ -182,7 +182,7 @@ sdk_E2e_Tests() {
~/npm/bin/gulp ca || err_Check "ERROR!!! gulp ca failed"
rm -rf node_modules/fabric-ca-client && npm install || err_Check "ERROR!!! npm install failed"

echo "------> Run node headless & e2e tests"
echo "------> Run Node SDK Unit, FV, and scenario tests"
echo "============"
~/npm/bin/gulp test
if [ $? == 0 ]; then
Expand Down
8 changes: 3 additions & 5 deletions test/fixtures/src/node_cc/example_cc/chaincode.js
Original file line number Diff line number Diff line change
Expand Up @@ -94,16 +94,14 @@ const Chaincode = class {
async move(stub, args) {
logger.info('########### example_cc0 move ###########');

let A, B;
let Aval, Bval;
let X;

if (args.length != 3) {
return shim.error('Incorrect number of arguments. Expecting 4, function followed by 2 names and 1 value');
}

A = args[0];
B = args[1];
const A = args[0];
const B = args[1];

try {
const Avalbytes = await stub.getState(A);
Expand All @@ -127,7 +125,7 @@ const Chaincode = class {
return shim.error('Failed to get state B');
}
// Perform the execution
X = parseInt(args[2]);
const X = parseInt(args[2]);
if (isNaN(X)) {
return shim.error('Invalid transaction amount, expecting a integer value');
}
Expand Down
8 changes: 3 additions & 5 deletions test/fixtures/src/node_cc/example_cc1/chaincode.js
Original file line number Diff line number Diff line change
Expand Up @@ -65,16 +65,14 @@ const Chaincode = class {
}

async move(stub, args) {
let A, B;
let Aval, Bval;
let X;

if (args.length != 3) {
return shim.error('Incorrect number of arguments. Expecting 4, function followed by 2 names and 1 value');
}

A = args[0];
B = args[1];
const A = args[0];
const B = args[1];

try {
const Avalbytes = await stub.getState(A);
Expand All @@ -98,7 +96,7 @@ const Chaincode = class {
return shim.error('Failed to get state B');
}
// Perform the execution
X = parseInt(args[2]);
const X = parseInt(args[2]);
if (isNaN(X)) {
return shim.error('Invalid transaction amount, expecting a integer value');
}
Expand Down
2 changes: 1 addition & 1 deletion test/integration/javachaincode/instantiate-chaincode.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ test('\n\n***** Java-Chaincode End-to-end flow: instantiate chaincode *****\n\n'
const chaincode_id = 'example_java';
const version = 'v0';
try {
const result = await e2eUtils.instantiateChaincodeWithId('org1', chaincode_id, testUtil.JAVA_CHAINCODE_PATH, version, 'java', false, false, t);
await e2eUtils.instantiateChaincodeWithId('org1', chaincode_id, testUtil.JAVA_CHAINCODE_PATH, version, 'java', false, false, t);
t.pass('Successfully instantiated java chaincode on the channel');
await testUtil.sleep(5000);
} catch(err) {
Expand Down
1 change: 0 additions & 1 deletion test/integration/javachaincode/query.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ const tape = require('tape');
const _test = require('tape-promise').default;
const test = _test(tape);
const e2eUtils = require('../e2e/e2eUtils.js');
const testUtils = require('../../unit/util');

test('\n\n***** Java-Chaincode End-to-end flow: query chaincode *****\n\n', async (t) => {
const chaincode_id = 'example_java';
Expand Down
Loading

0 comments on commit 9a37ef1

Please sign in to comment.