github.com/anjalikarhana/fabric@v2.1.1+incompatible/internal/peer/lifecycle/chaincode/commit_test.go (about)

     1  /*
     2  Copyright IBM Corp. All Rights Reserved.
     3  
     4  SPDX-License-Identifier: Apache-2.0
     5  */
     6  
     7  package chaincode_test
     8  
     9  import (
    10  	"context"
    11  	"crypto/tls"
    12  	"time"
    13  
    14  	pb "github.com/hyperledger/fabric-protos-go/peer"
    15  	"github.com/hyperledger/fabric/bccsp/sw"
    16  	"github.com/hyperledger/fabric/internal/peer/lifecycle/chaincode"
    17  	"github.com/hyperledger/fabric/internal/peer/lifecycle/chaincode/mock"
    18  	"github.com/pkg/errors"
    19  	"github.com/spf13/cobra"
    20  	"google.golang.org/grpc"
    21  
    22  	. "github.com/onsi/ginkgo"
    23  	. "github.com/onsi/gomega"
    24  )
    25  
    26  var _ = Describe("Commit", func() {
    27  	Describe("Committer", func() {
    28  		var (
    29  			mockProposalResponse *pb.ProposalResponse
    30  			mockEndorserClient   *mock.EndorserClient
    31  			mockEndorserClients  []chaincode.EndorserClient
    32  			mockDeliverClient    *mock.PeerDeliverClient
    33  			mockSigner           *mock.Signer
    34  			certificate          tls.Certificate
    35  			mockBroadcastClient  *mock.BroadcastClient
    36  			input                *chaincode.CommitInput
    37  			committer            *chaincode.Committer
    38  		)
    39  
    40  		BeforeEach(func() {
    41  			mockEndorserClient = &mock.EndorserClient{}
    42  
    43  			mockProposalResponse = &pb.ProposalResponse{
    44  				Response: &pb.Response{
    45  					Status: 200,
    46  				},
    47  				Endorsement: &pb.Endorsement{},
    48  			}
    49  			mockEndorserClient.ProcessProposalReturns(mockProposalResponse, nil)
    50  			mockEndorserClients = []chaincode.EndorserClient{mockEndorserClient}
    51  
    52  			mockDeliverClient = &mock.PeerDeliverClient{}
    53  			input = &chaincode.CommitInput{
    54  				ChannelID: "testchannel",
    55  				Name:      "testcc",
    56  				Version:   "1.0",
    57  				Sequence:  1,
    58  			}
    59  
    60  			mockSigner = &mock.Signer{}
    61  
    62  			certificate = tls.Certificate{}
    63  			mockBroadcastClient = &mock.BroadcastClient{}
    64  
    65  			committer = &chaincode.Committer{
    66  				Certificate:     certificate,
    67  				BroadcastClient: mockBroadcastClient,
    68  				DeliverClients:  []pb.DeliverClient{mockDeliverClient},
    69  				EndorserClients: mockEndorserClients,
    70  				Input:           input,
    71  				Signer:          mockSigner,
    72  			}
    73  		})
    74  
    75  		It("commits a chaincode definition", func() {
    76  			err := committer.Commit()
    77  			Expect(err).NotTo(HaveOccurred())
    78  		})
    79  
    80  		Context("when the channel name is not provided", func() {
    81  			BeforeEach(func() {
    82  				committer.Input.ChannelID = ""
    83  			})
    84  
    85  			It("returns an error", func() {
    86  				err := committer.Commit()
    87  				Expect(err).To(MatchError("The required parameter 'channelID' is empty. Rerun the command with -C flag"))
    88  			})
    89  		})
    90  
    91  		Context("when the chaincode name is not provided", func() {
    92  			BeforeEach(func() {
    93  				committer.Input.Name = ""
    94  			})
    95  
    96  			It("returns an error", func() {
    97  				err := committer.Commit()
    98  				Expect(err).To(MatchError("The required parameter 'name' is empty. Rerun the command with -n flag"))
    99  			})
   100  		})
   101  
   102  		Context("when the chaincode version is not provided", func() {
   103  			BeforeEach(func() {
   104  				committer.Input.Version = ""
   105  			})
   106  
   107  			It("returns an error", func() {
   108  				err := committer.Commit()
   109  				Expect(err).To(MatchError("The required parameter 'version' is empty. Rerun the command with -v flag"))
   110  			})
   111  		})
   112  
   113  		Context("when the sequence is not provided", func() {
   114  			BeforeEach(func() {
   115  				committer.Input.Sequence = 0
   116  			})
   117  
   118  			It("returns an error", func() {
   119  				err := committer.Commit()
   120  				Expect(err).To(MatchError("The required parameter 'sequence' is empty. Rerun the command with --sequence flag"))
   121  			})
   122  		})
   123  
   124  		Context("when the signer cannot be serialized", func() {
   125  			BeforeEach(func() {
   126  				mockSigner.SerializeReturns(nil, errors.New("cafe"))
   127  			})
   128  
   129  			It("returns an error", func() {
   130  				err := committer.Commit()
   131  				Expect(err).To(MatchError("failed to create proposal: failed to serialize identity: cafe"))
   132  			})
   133  		})
   134  
   135  		Context("when the signer fails to sign the proposal", func() {
   136  			BeforeEach(func() {
   137  				mockSigner.SignReturns(nil, errors.New("tea"))
   138  			})
   139  
   140  			It("returns an error", func() {
   141  				err := committer.Commit()
   142  				Expect(err).To(MatchError("failed to create signed proposal: tea"))
   143  			})
   144  		})
   145  
   146  		Context("when the endorser fails to endorse the proposal", func() {
   147  			BeforeEach(func() {
   148  				mockEndorserClient.ProcessProposalReturns(nil, errors.New("latte"))
   149  			})
   150  
   151  			It("returns an error", func() {
   152  				err := committer.Commit()
   153  				Expect(err).To(MatchError("failed to endorse proposal: latte"))
   154  			})
   155  		})
   156  
   157  		Context("when no endorser clients are set", func() {
   158  			BeforeEach(func() {
   159  				committer.EndorserClients = nil
   160  			})
   161  
   162  			It("doesn't receive any responses and returns an error", func() {
   163  				err := committer.Commit()
   164  				Expect(err).To(MatchError("no proposal responses received"))
   165  			})
   166  		})
   167  
   168  		Context("when the endorser returns a nil proposal response", func() {
   169  			BeforeEach(func() {
   170  				mockProposalResponse = nil
   171  				mockEndorserClient.ProcessProposalReturns(mockProposalResponse, nil)
   172  			})
   173  
   174  			It("returns an error", func() {
   175  				err := committer.Commit()
   176  				Expect(err).To(MatchError("received nil proposal response"))
   177  			})
   178  		})
   179  
   180  		Context("when the endorser returns a proposal response with a nil response", func() {
   181  			BeforeEach(func() {
   182  				mockProposalResponse.Response = nil
   183  				mockEndorserClient.ProcessProposalReturns(mockProposalResponse, nil)
   184  			})
   185  
   186  			It("returns an error", func() {
   187  				err := committer.Commit()
   188  				Expect(err).To(MatchError("received proposal response with nil response"))
   189  			})
   190  		})
   191  
   192  		Context("when the endorser returns a non-success status", func() {
   193  			BeforeEach(func() {
   194  				mockProposalResponse.Response = &pb.Response{
   195  					Status:  500,
   196  					Message: "capuccino",
   197  				}
   198  				mockEndorserClient.ProcessProposalReturns(mockProposalResponse, nil)
   199  			})
   200  
   201  			It("returns an error", func() {
   202  				err := committer.Commit()
   203  				Expect(err).To(MatchError("proposal failed with status: 500 - capuccino"))
   204  			})
   205  		})
   206  
   207  		Context("when the signer fails to sign the transaction", func() {
   208  			BeforeEach(func() {
   209  				mockSigner.SignReturnsOnCall(1, nil, errors.New("peaberry"))
   210  			})
   211  
   212  			It("returns an error", func() {
   213  				err := committer.Commit()
   214  				Expect(err).To(MatchError("failed to create signed transaction: peaberry"))
   215  				Expect(mockSigner.SignCallCount()).To(Equal(2))
   216  			})
   217  		})
   218  
   219  		Context("when the broadcast client fails to send the envelope", func() {
   220  			BeforeEach(func() {
   221  				mockBroadcastClient.SendReturns(errors.New("arabica"))
   222  			})
   223  
   224  			It("returns an error", func() {
   225  				err := committer.Commit()
   226  				Expect(err).To(MatchError("failed to send transaction: arabica"))
   227  			})
   228  		})
   229  
   230  		Context("when the wait for event flag is enabled and the transaction is committed", func() {
   231  			BeforeEach(func() {
   232  				input.WaitForEvent = true
   233  				input.WaitForEventTimeout = 3 * time.Second
   234  				input.TxID = "testtx"
   235  				input.PeerAddresses = []string{"commitpeer0"}
   236  				mockDeliverClient.DeliverFilteredStub = func(ctx context.Context, opts ...grpc.CallOption) (pb.Deliver_DeliverFilteredClient, error) {
   237  					mockDF := &mock.Deliver{}
   238  					resp := &pb.DeliverResponse{
   239  						Type: &pb.DeliverResponse_FilteredBlock{
   240  							FilteredBlock: createFilteredBlock(input.ChannelID, "testtx"),
   241  						},
   242  					}
   243  					mockDF.RecvReturns(resp, nil)
   244  					return mockDF, nil
   245  				}
   246  			})
   247  
   248  			It("waits for the event containing the txid", func() {
   249  				err := committer.Commit()
   250  				Expect(err).NotTo(HaveOccurred())
   251  			})
   252  		})
   253  
   254  		Context("when the wait for event flag is enabled and the client can't connect", func() {
   255  			BeforeEach(func() {
   256  				input.WaitForEvent = true
   257  				input.WaitForEventTimeout = 3 * time.Second
   258  				input.TxID = "testtx"
   259  				input.PeerAddresses = []string{"commitpeer0"}
   260  				mockDeliverClient.DeliverFilteredReturns(nil, errors.New("robusta"))
   261  			})
   262  
   263  			It("returns an error", func() {
   264  				err := committer.Commit()
   265  				Expect(err).To(MatchError("failed to connect to deliver on all peers: error connecting to deliver filtered at commitpeer0: robusta"))
   266  			})
   267  		})
   268  
   269  		Context("when the wait for event flag is enabled and the transaction isn't returned before the timeout", func() {
   270  			BeforeEach(func() {
   271  				input.WaitForEvent = true
   272  				input.WaitForEventTimeout = 10 * time.Millisecond
   273  				input.TxID = "testtx"
   274  				input.PeerAddresses = []string{"commitpeer0"}
   275  				delayChan := make(chan struct{})
   276  				mockDeliverClient.DeliverFilteredStub = func(ctx context.Context, opts ...grpc.CallOption) (pb.Deliver_DeliverFilteredClient, error) {
   277  					mockDF := &mock.Deliver{}
   278  					mockDF.RecvStub = func() (*pb.DeliverResponse, error) {
   279  						<-delayChan
   280  						resp := &pb.DeliverResponse{
   281  							Type: &pb.DeliverResponse_FilteredBlock{
   282  								FilteredBlock: createFilteredBlock(input.ChannelID, "testtx"),
   283  							},
   284  						}
   285  						return resp, nil
   286  					}
   287  					return mockDF, nil
   288  				}
   289  			})
   290  
   291  			It("returns an error", func() {
   292  				err := committer.Commit()
   293  				Expect(err).To(MatchError("timed out waiting for txid on all peers"))
   294  			})
   295  		})
   296  	})
   297  
   298  	Describe("CommitCmd", func() {
   299  		var (
   300  			commitCmd *cobra.Command
   301  		)
   302  
   303  		BeforeEach(func() {
   304  			cryptoProvider, err := sw.NewDefaultSecurityLevelWithKeystore(sw.NewDummyKeyStore())
   305  			Expect(err).To(BeNil())
   306  			commitCmd = chaincode.CommitCmd(nil, cryptoProvider)
   307  			commitCmd.SetArgs([]string{
   308  				"--channelID=testchannel",
   309  				"--name=testcc",
   310  				"--version=testversion",
   311  				"--sequence=1",
   312  				"--peerAddresses=querypeer1",
   313  				"--tlsRootCertFiles=tls1",
   314  				"--signature-policy=AND ('Org1MSP.member','Org2MSP.member')",
   315  			})
   316  		})
   317  
   318  		AfterEach(func() {
   319  			chaincode.ResetFlags()
   320  		})
   321  
   322  		It("sets up the committer and attempts to commit the chaincode definition", func() {
   323  			err := commitCmd.Execute()
   324  			Expect(err).To(MatchError(ContainSubstring("failed to retrieve endorser client")))
   325  		})
   326  
   327  		Context("when the policy is invalid", func() {
   328  			BeforeEach(func() {
   329  				commitCmd.SetArgs([]string{
   330  					"--signature-policy=notapolicy",
   331  					"--channelID=testchannel",
   332  					"--name=testcc",
   333  					"--version=testversion",
   334  					"--sequence=1",
   335  					"--peerAddresses=querypeer1",
   336  					"--tlsRootCertFiles=tls1",
   337  				})
   338  			})
   339  
   340  			It("returns an error", func() {
   341  				err := commitCmd.Execute()
   342  				Expect(err).To(MatchError("invalid signature policy: notapolicy"))
   343  			})
   344  		})
   345  
   346  		Context("when the collections config is invalid", func() {
   347  			BeforeEach(func() {
   348  				commitCmd.SetArgs([]string{
   349  					"--collections-config=idontexist.json",
   350  					"--channelID=testchannel",
   351  					"--name=testcc",
   352  					"--version=testversion",
   353  					"--sequence=1",
   354  					"--peerAddresses=querypeer1",
   355  					"--tlsRootCertFiles=tls1",
   356  				})
   357  			})
   358  
   359  			It("returns an error", func() {
   360  				err := commitCmd.Execute()
   361  				Expect(err).To(MatchError("invalid collection configuration in file idontexist.json: could not read file 'idontexist.json': open idontexist.json: no such file or directory"))
   362  			})
   363  		})
   364  	})
   365  })