Skip to content
This repository was archived by the owner on Apr 22, 2025. It is now read-only.

Commit 21473c4

Browse files
committed
Added "happy path end-to-end test"
This is the main happy path test that connects all the individual v1.0 APIs together to ensure the return values and parameters are properly constructed and designed to allow applications to use the SDK to implement complete flows. The change also includes corresponding README.md updates for instructions to set up the target environment and drive the tests. fixes in response to comments: - ESLint warnings Change-Id: Id523f479434de3588ba9ba345c500c5d0519e7f9 Signed-off-by: Jim Zhang <jzhang@us.ibm.com>
1 parent 9731107 commit 21473c4

File tree

8 files changed

+388
-9
lines changed

8 files changed

+388
-9
lines changed

README.md

Lines changed: 84 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,10 +17,90 @@ Clone the project and launch the following commands in the project root folder t
1717
* `gulp doc` to generate API docs
1818
* `npm test` to run the headless tests that do not require any additional set up
1919

20-
The following tests require setting up a local blockchain network as the target.
21-
* Test user management with a member services. Start a member service instance using the _hyperledger/fabric-membersrvc_ docker image. Then run `node test/unit/ca-tests.js`
22-
* Test transaction proposals. Start a peer instance using the _hyperledger/fabric-peer_ docker image. Then run `node test/unit/endorser-tests.js`
23-
* Test sending endorsed transactions for consensus. Start a peer instance using the _hyperledger/fabric-orderer_ docker image. Then run `node test/unit/orderer-tests.js`
20+
The following tests require setting up a local blockchain network as the target. Because v1.0 is still in active development, you still need the vagrant environment to build the necessary Docker images needed to run the network. Follow the steps below to set it up.
21+
* `cd fabric/devenv`
22+
* Open the file `Vagrantfile` and insert the following statement below the existing `config.vm.network` statements:
23+
* ` config.vm.network :forwarded_port, guest: 5151, host: 5151 # orderer service`
24+
* run `vagrant up` to launch the vagrant VM
25+
* Once inside vagrant, `cd $GOPATH/src/github.com/hyperledger/fabric`
26+
* run `make images` to build the docker images
27+
* create a docker-compose.yaml file in home directory (/home/vagrant) and copy the following content into the file
28+
```yaml
29+
vp:
30+
image: hyperledger/fabric-peer
31+
environment:
32+
- CORE_PEER_ADDRESSAUTODETECT=true
33+
- CORE_VM_ENDPOINT=unix:///host/var/run/docker.sock
34+
- CORE_LOGGING_LEVEL=DEBUG
35+
- CORE_PEER_NETWORKID=${CORE_PEER_NETWORKID}
36+
- CORE_NEXT=true
37+
- CORE_PEER_ENDORSER_ENABLED=true
38+
- CORE_SECURITY_ENABLED=true
39+
- CORE_PEER_PKI_ECA_PADDR=membersrvc:7054
40+
- CORE_PEER_PKI_TCA_PADDR=membersrvc:7054
41+
- CORE_PEER_PKI_TLSCA_PADDR=membersrvc:7054
42+
- CORE_PEER_PKI_TLS_ROOTCERT_FILE=./bddtests/tlsca.cert
43+
command: peer node start
44+
volumes:
45+
- /var/run/:/host/var/run/
46+
47+
membersrvc:
48+
image: hyperledger/fabric-membersrvc
49+
command: membersrvc
50+
ports:
51+
- 7054:7054
52+
53+
orderer:
54+
image: hyperledger/fabric-orderer
55+
environment:
56+
- ORDERER_GENERAL_LEDGERTYPE=ram
57+
- ORDERER_GENERAL_BATCHTIMEOUT=10s
58+
- ORDERER_GENERAL_BATCHSIZE=10
59+
- ORDERER_GENERAL_MAXWINDOWSIZE=1000
60+
- ORDERER_GENERAL_LISTENADDRESS=0.0.0.0
61+
- ORDERER_GENERAL_LISTENPORT=5005
62+
- ORDERER_RAMLEDGER_HISTORY_SIZE=100
63+
- ORDERER_GENERAL_ORDERERTYPE=solo
64+
working_dir: /opt/gopath/src/github.com/hyperledger/fabric/orderer
65+
command: orderer
66+
ports:
67+
- 5151:5005
68+
69+
vp0:
70+
extends:
71+
service: vp
72+
environment:
73+
- CORE_PEER_ID=vp0
74+
- CORE_SECURITY_ENROLLID=test_vp0
75+
- CORE_SECURITY_ENROLLSECRET=MwYpmSRjupbT
76+
- CORE_PEER_PROFILE_ENABLED=true
77+
links:
78+
- membersrvc
79+
- orderer0
80+
ports:
81+
- 7051:7051
82+
- 7053:7053
83+
```
84+
* run `docker-compose up` to launch the network
85+
* Back in your native host (MacOS, or Windows, or Ubuntu, etc), run the following tests:
86+
* Test user management with a member services, run `node test/unit/ca-tests.js`
87+
* Test happy path from end to end, run `node test/unit/end-2-end.js`
88+
* Test transaction proposals, run `node test/unit/endorser-tests.js`
89+
* Test sending endorsed transactions for consensus, run `node test/unit/orderer-tests.js`
90+
91+
### Contributor Check-list
92+
The following check-list is for code contributors to make sure their changesets are compliant to the coding standards and avoid time wasted in rejected changesets:
93+
94+
Check the coding styles, run the following command and make sure no ESLint violations are present:
95+
* `gulp`
96+
97+
Run the full unit test bucket:
98+
* `node test/unit/headless-tests.js`
99+
* `node test/unit/ca-tests.js`
100+
* `node test/unit/end-2-end.js`
101+
* `node test/unit/endorser-tests.js`
102+
* `node test/unit/orderer-tests.js`
103+
* `node test/unit/orderer-member-tests.js`
24104

25105
### HFC objects and reference documentation
26106
For a high-level design specificiation for Fabric SDKs of all languages, visit [this google doc](https://docs.google.com/document/d/1R5RtIBMW9fZpli37E5Li5_Q9ve3BnQ4q3gWmGZj6Sv4/edit?usp=sharing) (Work-In-Progress).

lib/Member.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -445,8 +445,8 @@ var Member = class {
445445
let peer = new Peer(request.endorserUrl);
446446
return peer.sendProposal(proposal)
447447
.then(
448-
function(status) {
449-
resolve(status);
448+
function(response) {
449+
resolve(response);
450450
}
451451
);
452452
}

lib/Peer.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ var Peer = class extends Remote {
6565
} else {
6666
if (response) {
6767
logger.info('Received proposal response: code - %s', JSON.stringify(response.response.status));
68-
resolve(response.response.status);
68+
resolve(response);
6969
} else {
7070
logger.error('GRPC client failed to get a proper response from the peer.');
7171
reject(new Error('GRPC client failed to get a proper response from the peer.'));

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@
4646
},
4747
"devDependencies": {
4848
"bunyan": "^1.8.1",
49+
"fs-extra": "^0.30.0",
4950
"gulp": "^3.9.1",
5051
"gulp-eslint": "^3.0.1",
5152
"gulp-jsdoc3": "^0.3.0",

test/fixtures/example_cc.go

Lines changed: 187 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,187 @@
1+
/*
2+
Copyright IBM Corp. 2016 All Rights Reserved.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
package main
18+
19+
// A copy of fabric/examples/chaincode/go/chaincode_example02/chaincode_example02.go
20+
21+
import (
22+
"errors"
23+
"fmt"
24+
"strconv"
25+
26+
"github.com/hyperledger/fabric/core/chaincode/shim"
27+
)
28+
29+
// SimpleChaincode example simple Chaincode implementation
30+
type SimpleChaincode struct {
31+
}
32+
33+
func (t *SimpleChaincode) Init(stub shim.ChaincodeStubInterface) ([]byte, error) {
34+
_, args := stub.GetFunctionAndParameters()
35+
var A, B string // Entities
36+
var Aval, Bval int // Asset holdings
37+
var err error
38+
39+
if len(args) != 4 {
40+
return nil, errors.New("Incorrect number of arguments. Expecting 4")
41+
}
42+
43+
// Initialize the chaincode
44+
A = args[0]
45+
Aval, err = strconv.Atoi(args[1])
46+
if err != nil {
47+
return nil, errors.New("Expecting integer value for asset holding")
48+
}
49+
B = args[2]
50+
Bval, err = strconv.Atoi(args[3])
51+
if err != nil {
52+
return nil, errors.New("Expecting integer value for asset holding")
53+
}
54+
fmt.Printf("Aval = %d, Bval = %d\n", Aval, Bval)
55+
56+
// Write the state to the ledger
57+
err = stub.PutState(A, []byte(strconv.Itoa(Aval)))
58+
if err != nil {
59+
return nil, err
60+
}
61+
62+
err = stub.PutState(B, []byte(strconv.Itoa(Bval)))
63+
if err != nil {
64+
return nil, err
65+
}
66+
67+
return nil, nil
68+
}
69+
70+
// Transaction makes payment of X units from A to B
71+
func (t *SimpleChaincode) Invoke(stub shim.ChaincodeStubInterface) ([]byte, error) {
72+
function, args := stub.GetFunctionAndParameters()
73+
if function == "delete" {
74+
// Deletes an entity from its state
75+
return t.delete(stub, args)
76+
}
77+
78+
var A, B string // Entities
79+
var Aval, Bval int // Asset holdings
80+
var X int // Transaction value
81+
var err error
82+
83+
if len(args) != 3 {
84+
return nil, errors.New("Incorrect number of arguments. Expecting 3")
85+
}
86+
87+
A = args[0]
88+
B = args[1]
89+
90+
// Get the state from the ledger
91+
// TODO: will be nice to have a GetAllState call to ledger
92+
Avalbytes, err := stub.GetState(A)
93+
if err != nil {
94+
return nil, errors.New("Failed to get state")
95+
}
96+
if Avalbytes == nil {
97+
return nil, errors.New("Entity not found")
98+
}
99+
Aval, _ = strconv.Atoi(string(Avalbytes))
100+
101+
Bvalbytes, err := stub.GetState(B)
102+
if err != nil {
103+
return nil, errors.New("Failed to get state")
104+
}
105+
if Bvalbytes == nil {
106+
return nil, errors.New("Entity not found")
107+
}
108+
Bval, _ = strconv.Atoi(string(Bvalbytes))
109+
110+
// Perform the execution
111+
X, err = strconv.Atoi(args[2])
112+
if err != nil {
113+
return nil, errors.New("Invalid transaction amount, expecting a integer value")
114+
}
115+
Aval = Aval - X
116+
Bval = Bval + X
117+
fmt.Printf("Aval = %d, Bval = %d\n", Aval, Bval)
118+
119+
// Write the state back to the ledger
120+
err = stub.PutState(A, []byte(strconv.Itoa(Aval)))
121+
if err != nil {
122+
return nil, err
123+
}
124+
125+
err = stub.PutState(B, []byte(strconv.Itoa(Bval)))
126+
if err != nil {
127+
return nil, err
128+
}
129+
130+
return nil, nil
131+
}
132+
133+
// Deletes an entity from state
134+
func (t *SimpleChaincode) delete(stub shim.ChaincodeStubInterface, args []string) ([]byte, error) {
135+
if len(args) != 1 {
136+
return nil, errors.New("Incorrect number of arguments. Expecting 1")
137+
}
138+
139+
A := args[0]
140+
141+
// Delete the key from the state in ledger
142+
err := stub.DelState(A)
143+
if err != nil {
144+
return nil, errors.New("Failed to delete state")
145+
}
146+
147+
return nil, nil
148+
}
149+
150+
// Query callback representing the query of a chaincode
151+
func (t *SimpleChaincode) Query(stub shim.ChaincodeStubInterface) ([]byte, error) {
152+
function, args := stub.GetFunctionAndParameters()
153+
if function != "query" {
154+
return nil, errors.New("Invalid query function name. Expecting \"query\"")
155+
}
156+
var A string // Entities
157+
var err error
158+
159+
if len(args) != 1 {
160+
return nil, errors.New("Incorrect number of arguments. Expecting name of the person to query")
161+
}
162+
163+
A = args[0]
164+
165+
// Get the state from the ledger
166+
Avalbytes, err := stub.GetState(A)
167+
if err != nil {
168+
jsonResp := "{\"Error\":\"Failed to get state for " + A + "\"}"
169+
return nil, errors.New(jsonResp)
170+
}
171+
172+
if Avalbytes == nil {
173+
jsonResp := "{\"Error\":\"Nil amount for " + A + "\"}"
174+
return nil, errors.New(jsonResp)
175+
}
176+
177+
jsonResp := "{\"Name\":\"" + A + "\",\"Amount\":\"" + string(Avalbytes) + "\"}"
178+
fmt.Printf("Query Response:%s\n", jsonResp)
179+
return Avalbytes, nil
180+
}
181+
182+
func main() {
183+
err := shim.Start(new(SimpleChaincode))
184+
if err != nil {
185+
fmt.Printf("Error starting Simple chaincode: %s", err)
186+
}
187+
}

test/unit/end-to-end.js

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
// This is an end-to-end test that focuses on exercising all parts of the fabric APIs
2+
// in a happy-path scenario
3+
var tape = require('tape');
4+
var _test = require('tape-promise');
5+
var test = _test(tape);
6+
7+
var path = require('path');
8+
9+
var hfc = require('../..');
10+
var util = require('util');
11+
var grpc = require('grpc');
12+
var testUtil = require('./util.js');
13+
14+
var _fabricProto = grpc.load(path.join(__dirname,'../../lib/protos/fabric_next.proto')).protos;
15+
16+
var chain = hfc.newChain('testChain');
17+
var webUser;
18+
19+
testUtil.setupChaincodeDeploy();
20+
21+
chain.setKeyValueStore(hfc.newKeyValueStore({
22+
path: '/tmp/kvs-hfc-e2e'
23+
}));
24+
25+
chain.setMemberServicesUrl('grpc://localhost:7054');
26+
chain.setOrderer('grpc://localhost:5151');
27+
28+
test('End-to-end flow of chaincode deploy, transaction invocation, and query', function(t) {
29+
chain.enroll('admin', 'Xurw3yU9zI0l')
30+
.then(
31+
function(admin) {
32+
t.pass('Successfully enrolled user \'admin\'');
33+
webUser = admin;
34+
35+
// send proposal to endorser
36+
var request = {
37+
endorserUrl: 'grpc://localhost:7051',
38+
chaincodePath: testUtil.CHAINCODE_PATH,
39+
fcn: 'init',
40+
args: ['a', '100', 'b', '200']
41+
};
42+
43+
return admin.sendDeploymentProposal(request);
44+
},
45+
function(err) {
46+
t.fail('Failed to enroll user \'admin\'. ' + err);
47+
t.end();
48+
}
49+
).then(
50+
function(response) {
51+
if (response && response.response && response.response.status === 200) {
52+
t.pass(util.format('Successfully sent Proposal and received ProposalResponse: Status - %s, message - "%s", metadata - "%s", endorsement signature: %s', response.response.status, response.response.message, response.response.payload, response.endorsement.signature));
53+
54+
var tx = new _fabricProto.Transaction2();
55+
tx.setEndorsedActions([{
56+
actionBytes: response.actionBytes,
57+
endorsements: response.response.endorsement
58+
}]);
59+
60+
return webUser.sendTransaction(tx.toBuffer());
61+
62+
} else {
63+
t.fail('Failed to send Proposal or receive valid response. Response null or status is not 200. exiting...');
64+
t.end();
65+
}
66+
},
67+
function(err) {
68+
t.fail('Failed to send deployment proposal due to error: ' + err.stack ? err.stack : err);
69+
t.end();
70+
}
71+
).then(
72+
function(data) {
73+
t.pass(util.format('Response from orderer: %j', data));
74+
75+
t.end();
76+
}
77+
).catch(
78+
function(err) {
79+
t.fail('Failed to send deployment proposal. ' + err.stack ? err.stack : err);
80+
t.end();
81+
}
82+
);
83+
});

0 commit comments

Comments
 (0)