Skip to content

Commit ea9f840

Browse files
committed
Ported BDD implementations to golang using Godog.
Godog is the official Cucumber BDD framework for Golang, which is the standard that our current behave (python) tests are implemented. In this manner, we reuse our existing feature files and can implement BDD tests in both python and golang. The Jira issue is in https://jira.hyperledger.org/browse/FAB-448 Change-Id: If95051670a0f4357f56b12216cd42f31b1402148 Signed-off-by: jeffgarratt <[email protected]>
1 parent 6d78968 commit ea9f840

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

60 files changed

+10932
-143
lines changed

bddtests/.behaverc

+2
Original file line numberDiff line numberDiff line change
@@ -6,4 +6,6 @@ tags=~@issue_767
66
~@issue_1565
77
~@issue_RBAC_TCERT_With_Attributes
88
~@sdk
9+
~@preV1
10+
911
~@FAB-314

bddtests/chaincode.go

+57
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
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 bddtests
18+
19+
import (
20+
"fmt"
21+
22+
"github.com/golang/protobuf/proto"
23+
"github.com/hyperledger/fabric/core/util"
24+
pb "github.com/hyperledger/fabric/protos"
25+
)
26+
27+
func createChaincodeSpec(ccType string, path string, args [][]byte) *pb.ChaincodeSpec {
28+
// make chaincode spec for chaincode to be deployed
29+
ccSpec := &pb.ChaincodeSpec{Type: pb.ChaincodeSpec_Type(pb.ChaincodeSpec_Type_value[ccType]),
30+
ChaincodeID: &pb.ChaincodeID{Path: path},
31+
CtorMsg: &pb.ChaincodeInput{Args: args}}
32+
return ccSpec
33+
34+
}
35+
36+
func createPropsalID() string {
37+
return util.GenerateUUID()
38+
}
39+
40+
// createChaincodeDeploymentSpec Returns a deployment proposal of chaincode type
41+
func createProposalForChaincode(ccChaincodeDeploymentSpec *pb.ChaincodeDeploymentSpec) (proposal *pb.Proposal, err error) {
42+
var ccDeploymentSpecBytes []byte
43+
if ccDeploymentSpecBytes, err = proto.Marshal(ccChaincodeDeploymentSpec); err != nil {
44+
return nil, fmt.Errorf("Error creating proposal from ChaincodeDeploymentSpec: %s", err)
45+
}
46+
lcChaincodeSpec := &pb.ChaincodeSpec{Type: pb.ChaincodeSpec_GOLANG,
47+
ChaincodeID: &pb.ChaincodeID{Name: "lccc"},
48+
CtorMsg: &pb.ChaincodeInput{Args: [][]byte{[]byte("deploy"), []byte("default"), ccDeploymentSpecBytes}}}
49+
lcChaincodeInvocationSpec := &pb.ChaincodeInvocationSpec{ChaincodeSpec: lcChaincodeSpec}
50+
var ccLifecycleChaincodeInvocationSpecBytes []byte
51+
if ccLifecycleChaincodeInvocationSpecBytes, err = proto.Marshal(lcChaincodeInvocationSpec); err != nil {
52+
return nil, fmt.Errorf("Error creating proposal from ChaincodeDeploymentSpec: %s", err)
53+
}
54+
// make proposal
55+
proposal = &pb.Proposal{Type: pb.Proposal_CHAINCODE, Id: createPropsalID(), Payload: ccLifecycleChaincodeInvocationSpecBytes}
56+
return proposal, nil
57+
}

bddtests/chaincode_rbac.feature

+2-1
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,8 @@
55
#
66
# @doNotDecompose will NOT decompose the named compose_yaml after scenario ends. Useful for setting up environment and reviewing after scenario.
77
#
8-
8+
@preV1
9+
@feature_chaincode_rbac
910
Feature: Role Based Access Control (RBAC)
1011
As a HyperLedger developer
1112
I want various mechanisms available for implementing RBAC within Chaincode

bddtests/compose-defaults.yml

+3-1
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,12 @@ vp:
55
- CORE_VM_ENDPOINT=http://172.17.0.1:2375
66
# TODO: This is currently required due to BUG in variant logic based upon log level.
77
- CORE_LOGGING_LEVEL=DEBUG
8+
- CORE_PEER_NETWORKID=${CORE_PEER_NETWORKID}
89
# Script will wait until membersrvc is up (if it exists) before starting
910
# $$GOPATH (double dollar) required to prevent docker-compose doing its own
1011
# substitution before the value gets to the container
11-
command: sh -c "exec $$GOPATH/src/github.com/hyperledger/fabric/bddtests/scripts/start-peer.sh"
12+
#command: sh -c "exec $$GOPATH/src/github.com/hyperledger/fabric/bddtests/scripts/start-peer.sh"
13+
command: peer node start
1214

1315
# Use these options if coverage desired for peers
1416
#image: hyperledger/fabric-peer-coverage

bddtests/compose.go

+165
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,165 @@
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 bddtests
18+
19+
import (
20+
"fmt"
21+
"os/exec"
22+
"strings"
23+
24+
"github.com/fsouza/go-dockerclient"
25+
)
26+
27+
const dockerComposeCommand = "docker-compose"
28+
29+
// Composition represents a docker-compose execution and management
30+
type Composition struct {
31+
endpoint string
32+
dockerClient *docker.Client
33+
apiContainers []docker.APIContainers
34+
35+
composeFilesYaml string
36+
projectName string
37+
dockerHelper DockerHelper
38+
}
39+
40+
// NewComposition create a new Composition specifying the project name (for isolation) and the compose files.
41+
func NewComposition(projectName string, composeFilesYaml string) (composition *Composition, err error) {
42+
errRetFunc := func() error {
43+
return fmt.Errorf("Error creating new composition '%s' using compose yaml '%s': %s", projectName, composeFilesYaml, err)
44+
}
45+
46+
endpoint := "unix:///var/run/docker.sock"
47+
composition = &Composition{composeFilesYaml: composeFilesYaml, projectName: projectName}
48+
if composition.dockerClient, err = docker.NewClient(endpoint); err != nil {
49+
return nil, errRetFunc()
50+
}
51+
if _, err = composition.issueCommand([]string{"up", "--force-recreate", "-d"}); err != nil {
52+
return nil, errRetFunc()
53+
}
54+
if composition.dockerHelper, err = NewDockerCmdlineHelper(); err != nil {
55+
return nil, errRetFunc()
56+
}
57+
// Now parse the current system
58+
return composition, nil
59+
}
60+
61+
func parseComposeFilesArg(composeFileArgs string) []string {
62+
var args []string
63+
for _, f := range strings.Fields(composeFileArgs) {
64+
args = append(args, []string{"-f", f}...)
65+
}
66+
return args
67+
}
68+
69+
func (c *Composition) getFileArgs() []string {
70+
return parseComposeFilesArg(c.composeFilesYaml)
71+
}
72+
73+
// GetContainerIDs returns the container IDs for the composition (NOTE: does NOT include those defined outside composition, eg. chaincode containers)
74+
func (c *Composition) GetContainerIDs() (containerIDs []string, err error) {
75+
var cmdOutput string
76+
if cmdOutput, err = c.issueCommand([]string{"ps", "-q"}); err != nil {
77+
return nil, fmt.Errorf("Error getting container IDs for project '%s': %s", c.projectName, err)
78+
}
79+
containerIDs = splitDockerCommandResults(cmdOutput)
80+
return containerIDs, err
81+
}
82+
83+
func (c *Composition) getEnv() []string {
84+
return []string{"COMPOSE_PROJECT_NAME=" + c.projectName, "CORE_PEER_NETWORKID=" + c.projectName}
85+
}
86+
87+
func (c *Composition) refreshContainerList() (err error) {
88+
var allAPIContainers []docker.APIContainers
89+
var thisProjectsContainers []docker.APIContainers
90+
if thisProjectsContainers, err = c.dockerClient.ListContainers(docker.ListContainersOptions{All: true, Filters: map[string][]string{"name": {c.projectName}}}); err != nil {
91+
return fmt.Errorf("Error refreshing container list for project '%s': %s", c.projectName, err)
92+
}
93+
//if allApiContainers, err = c.dockerClient.ListContainers(docker.ListContainersOptions{All: true}); err != nil {
94+
// return fmt.Errorf("Error refreshing container list for project '%s': %s", c.projectName, err)
95+
//}
96+
for _, apiContainer := range allAPIContainers {
97+
if composeService, ok := apiContainer.Labels["com.docker.compose.service"]; ok == true {
98+
fmt.Println(fmt.Sprintf("Container name: %s, composeService: %s, IPAddress: %s", apiContainer.Names[0], composeService, apiContainer.Networks.Networks["bridge"].IPAddress))
99+
}
100+
}
101+
c.apiContainers = thisProjectsContainers
102+
return err
103+
}
104+
105+
func (c *Composition) issueCommand(args []string) (_ string, err error) {
106+
errRetFunc := func() error {
107+
return fmt.Errorf("Error issuing command to docker-compose with args '%s': %s", args, err)
108+
}
109+
var cmdArgs []string
110+
cmdArgs = append(cmdArgs, c.getFileArgs()...)
111+
cmdArgs = append(cmdArgs, args...)
112+
var cmdOut []byte
113+
cmd := exec.Command(dockerComposeCommand, cmdArgs...)
114+
cmd.Env = append(cmd.Env, c.getEnv()...)
115+
if cmdOut, err = cmd.CombinedOutput(); err != nil {
116+
return string(cmdOut), errRetFunc()
117+
}
118+
119+
// Reparse Container list
120+
if err = c.refreshContainerList(); err != nil {
121+
return "", errRetFunc()
122+
}
123+
return string(cmdOut), err
124+
}
125+
126+
// Decompose decompose the composition. Will also remove any containers with the same projectName prefix (eg. chaincode containers)
127+
func (c *Composition) Decompose() (output string, err error) {
128+
//var containers []string
129+
output, err = c.issueCommand([]string{"stop"})
130+
output, err = c.issueCommand([]string{"rm", "-f"})
131+
// Now remove associated chaincode containers if any
132+
c.dockerHelper.RemoveContainersWithNamePrefix(c.projectName)
133+
return output, err
134+
}
135+
136+
// parseComposition parses the current docker-compose project from ps command
137+
func (c *Composition) parseComposition() (err error) {
138+
//c.issueCommand()
139+
return nil
140+
}
141+
142+
// GetAPIContainerForComposeService return the docker.APIContainers with the supplied composeService name.
143+
func (c *Composition) GetAPIContainerForComposeService(composeService string) (apiContainer *docker.APIContainers, err error) {
144+
for _, apiContainer := range c.apiContainers {
145+
if currComposeService, ok := apiContainer.Labels["com.docker.compose.service"]; ok == true {
146+
if currComposeService == composeService {
147+
return &apiContainer, nil
148+
}
149+
}
150+
}
151+
return nil, fmt.Errorf("Could not find container with compose service '%s'", composeService)
152+
}
153+
154+
// GetIPAddressForComposeService returns the IPAddress of the container with the supplied composeService name.
155+
func (c *Composition) GetIPAddressForComposeService(composeService string) (ipAddress string, err error) {
156+
errRetFunc := func() error {
157+
return fmt.Errorf("Error getting IPAddress for compose service '%s': %s", composeService, err)
158+
}
159+
var apiContainer *docker.APIContainers
160+
if apiContainer, err = c.GetAPIContainerForComposeService(composeService); err != nil {
161+
return "", errRetFunc()
162+
}
163+
// Now get the IPAddress
164+
return apiContainer.Networks.Networks["bridge"].IPAddress, nil
165+
}

bddtests/conn.go

+39
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
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 bddtests
18+
19+
import (
20+
"fmt"
21+
22+
"github.com/hyperledger/fabric/core/comm"
23+
"google.golang.org/grpc"
24+
)
25+
26+
// NewGrpcClient return a new GRPC client connection for the specified peer address
27+
func NewGrpcClient(peerAddress string) (*grpc.ClientConn, error) {
28+
var tmpConn *grpc.ClientConn
29+
var err error
30+
if comm.TLSEnabled() {
31+
tmpConn, err = comm.NewClientConnectionWithAddress(peerAddress, true, true, comm.InitTLSForPeer())
32+
}
33+
tmpConn, err = comm.NewClientConnectionWithAddress(peerAddress, true, false, nil)
34+
if err != nil {
35+
fmt.Printf("error connection to server at host:port = %s\n", peerAddress)
36+
}
37+
return tmpConn, err
38+
39+
}

bddtests/context.go

+90
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
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 bddtests
18+
19+
import (
20+
"fmt"
21+
"regexp"
22+
"strings"
23+
24+
"github.com/DATA-DOG/godog"
25+
"github.com/DATA-DOG/godog/gherkin"
26+
)
27+
28+
// BDDContext represents the current context for the executing scenario. Commensurate concept of 'context' from behave testing.
29+
type BDDContext struct {
30+
grpcClientPort int
31+
composition *Composition
32+
godogSuite *godog.Suite
33+
scenarioOrScenarioOutline interface{}
34+
users map[string]*UserRegistration
35+
}
36+
37+
func (b *BDDContext) getScenarioDefinition() *gherkin.ScenarioDefinition {
38+
if b.scenarioOrScenarioOutline == nil {
39+
return nil
40+
}
41+
switch t := b.scenarioOrScenarioOutline.(type) {
42+
case *gherkin.Scenario:
43+
return &(t.ScenarioDefinition)
44+
case *gherkin.ScenarioOutline:
45+
return &(t.ScenarioDefinition)
46+
}
47+
return nil
48+
}
49+
50+
func (b *BDDContext) hasTag(tagName string) bool {
51+
if b.scenarioOrScenarioOutline == nil {
52+
return false
53+
}
54+
hasTagInner := func(tags []*gherkin.Tag) bool {
55+
for _, t := range tags {
56+
if t.Name == tagName {
57+
return true
58+
}
59+
}
60+
return false
61+
}
62+
63+
switch t := b.scenarioOrScenarioOutline.(type) {
64+
case *gherkin.Scenario:
65+
return hasTagInner(t.Tags)
66+
case *gherkin.ScenarioOutline:
67+
return hasTagInner(t.Tags)
68+
}
69+
return false
70+
}
71+
72+
// GetArgsForUser will return an arg slice of string allowing for replacement of parameterized values based upon tags for userRegistration
73+
func (b *BDDContext) GetArgsForUser(cells []*gherkin.TableCell, userRegistration *UserRegistration) (args []string, err error) {
74+
regExp := regexp.MustCompile("\\{(.*?)\\}+")
75+
// Loop through cells and replace with user tag values if found
76+
for _, cell := range cells {
77+
var arg = cell.Value
78+
for _, tagNameToFind := range regExp.FindAllStringSubmatch(cell.Value, -1) {
79+
println("toFind = ", tagNameToFind[0], " to replace = ", tagNameToFind[1])
80+
var tagValue interface{}
81+
tagValue, err = userRegistration.GetTagValue(tagNameToFind[1])
82+
if err != nil {
83+
return nil, fmt.Errorf("Error getting args for user '%s': %s", userRegistration.enrollID, err)
84+
}
85+
arg = strings.Replace(arg, tagNameToFind[0], fmt.Sprintf("%v", tagValue), 1)
86+
}
87+
args = append(args, arg)
88+
}
89+
return args, nil
90+
}

bddtests/docker-compose-4-consensus-batch.yml

+2-2
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,8 @@ vp0:
1414
- CORE_PEER_PROFILE_ENABLED=true
1515
links:
1616
- membersrvc0
17-
ports:
18-
- 7050:6060
17+
# ports:
18+
# - 7050:6060
1919

2020

2121
vp1:

0 commit comments

Comments
 (0)