github.com/defanghe/fabric@v2.1.1+incompatible/internal/peer/lifecycle/chaincode/approveformyorg_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("ApproverForMyOrg", func() { 27 Describe("Approve", 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.ApproveForMyOrgInput 37 approver *chaincode.ApproverForMyOrg 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.ApproveForMyOrgInput{ 54 ChannelID: "testchannel", 55 Name: "testcc", 56 Version: "1.0", 57 PackageID: "testpackageid", 58 Sequence: 1, 59 } 60 61 mockSigner = &mock.Signer{} 62 63 certificate = tls.Certificate{} 64 mockBroadcastClient = &mock.BroadcastClient{} 65 66 approver = &chaincode.ApproverForMyOrg{ 67 Certificate: certificate, 68 BroadcastClient: mockBroadcastClient, 69 DeliverClients: []pb.DeliverClient{mockDeliverClient}, 70 EndorserClients: mockEndorserClients, 71 Input: input, 72 Signer: mockSigner, 73 } 74 }) 75 76 It("approves a chaincode definition for an organization", func() { 77 err := approver.Approve() 78 Expect(err).NotTo(HaveOccurred()) 79 }) 80 81 Context("when the channel name is not provided", func() { 82 BeforeEach(func() { 83 approver.Input.ChannelID = "" 84 }) 85 86 It("returns an error", func() { 87 err := approver.Approve() 88 Expect(err).To(MatchError("The required parameter 'channelID' is empty. Rerun the command with -C flag")) 89 }) 90 }) 91 92 Context("when the chaincode name is not provided", func() { 93 BeforeEach(func() { 94 approver.Input.Name = "" 95 }) 96 97 It("returns an error", func() { 98 err := approver.Approve() 99 Expect(err).To(MatchError("The required parameter 'name' is empty. Rerun the command with -n flag")) 100 }) 101 }) 102 103 Context("when the chaincode version is not provided", func() { 104 BeforeEach(func() { 105 approver.Input.Version = "" 106 }) 107 108 It("returns an error", func() { 109 err := approver.Approve() 110 Expect(err).To(MatchError("The required parameter 'version' is empty. Rerun the command with -v flag")) 111 }) 112 }) 113 114 Context("when the sequence is not provided", func() { 115 BeforeEach(func() { 116 approver.Input.Sequence = 0 117 }) 118 119 It("returns an error", func() { 120 err := approver.Approve() 121 Expect(err).To(MatchError("The required parameter 'sequence' is empty. Rerun the command with --sequence flag")) 122 }) 123 }) 124 125 Context("when the signer cannot be serialized", func() { 126 BeforeEach(func() { 127 mockSigner.SerializeReturns(nil, errors.New("cafe")) 128 }) 129 130 It("returns an error", func() { 131 err := approver.Approve() 132 Expect(err).To(MatchError("failed to create proposal: failed to serialize identity: cafe")) 133 }) 134 }) 135 136 Context("when the signer fails to sign the proposal", func() { 137 BeforeEach(func() { 138 mockSigner.SignReturns(nil, errors.New("tea")) 139 }) 140 141 It("returns an error", func() { 142 err := approver.Approve() 143 Expect(err).To(MatchError("failed to create signed proposal: tea")) 144 }) 145 }) 146 147 Context("when the endorser fails to endorse the proposal", func() { 148 BeforeEach(func() { 149 mockEndorserClient.ProcessProposalReturns(nil, errors.New("latte")) 150 }) 151 152 It("returns an error", func() { 153 err := approver.Approve() 154 Expect(err).To(MatchError("failed to endorse proposal: latte")) 155 }) 156 }) 157 158 Context("when no endorser clients are set", func() { 159 BeforeEach(func() { 160 approver.EndorserClients = nil 161 }) 162 163 It("doesn't receive any responses and returns an error", func() { 164 err := approver.Approve() 165 Expect(err).To(MatchError("no proposal responses received")) 166 }) 167 }) 168 169 Context("when the endorser returns a nil proposal response", func() { 170 BeforeEach(func() { 171 mockProposalResponse = nil 172 mockEndorserClient.ProcessProposalReturns(mockProposalResponse, nil) 173 }) 174 175 It("returns an error", func() { 176 err := approver.Approve() 177 Expect(err).To(MatchError("received nil proposal response")) 178 }) 179 }) 180 181 Context("when the endorser returns a proposal response with a nil response", func() { 182 BeforeEach(func() { 183 mockProposalResponse.Response = nil 184 mockEndorserClient.ProcessProposalReturns(mockProposalResponse, nil) 185 }) 186 187 It("returns an error", func() { 188 err := approver.Approve() 189 Expect(err).To(MatchError("received proposal response with nil response")) 190 }) 191 }) 192 193 Context("when the endorser returns a non-success status", func() { 194 BeforeEach(func() { 195 mockProposalResponse.Response = &pb.Response{ 196 Status: 500, 197 Message: "capuccino", 198 } 199 mockEndorserClient.ProcessProposalReturns(mockProposalResponse, nil) 200 }) 201 202 It("returns an error", func() { 203 err := approver.Approve() 204 Expect(err).To(MatchError("proposal failed with status: 500 - capuccino")) 205 }) 206 }) 207 208 Context("when the signer fails to sign the transaction", func() { 209 BeforeEach(func() { 210 mockSigner.SignReturnsOnCall(1, nil, errors.New("peaberry")) 211 }) 212 213 It("returns an error", func() { 214 err := approver.Approve() 215 Expect(err).To(MatchError("failed to create signed transaction: peaberry")) 216 Expect(mockSigner.SignCallCount()).To(Equal(2)) 217 }) 218 }) 219 220 Context("when the broadcast client fails to send the envelope", func() { 221 BeforeEach(func() { 222 mockBroadcastClient.SendReturns(errors.New("arabica")) 223 }) 224 225 It("returns an error", func() { 226 err := approver.Approve() 227 Expect(err).To(MatchError("failed to send transaction: arabica")) 228 }) 229 }) 230 231 Context("when the wait for event flag is enabled and the transaction is committed", func() { 232 BeforeEach(func() { 233 input.WaitForEvent = true 234 input.WaitForEventTimeout = 3 * time.Second 235 input.TxID = "testtx" 236 input.PeerAddresses = []string{"approvepeer0"} 237 mockDeliverClient.DeliverFilteredStub = func(ctx context.Context, opts ...grpc.CallOption) (pb.Deliver_DeliverFilteredClient, error) { 238 mockDF := &mock.Deliver{} 239 resp := &pb.DeliverResponse{ 240 Type: &pb.DeliverResponse_FilteredBlock{ 241 FilteredBlock: createFilteredBlock(input.ChannelID, "testtx"), 242 }, 243 } 244 mockDF.RecvReturns(resp, nil) 245 return mockDF, nil 246 } 247 }) 248 249 It("waits for the event containing the txid", func() { 250 err := approver.Approve() 251 Expect(err).NotTo(HaveOccurred()) 252 }) 253 }) 254 255 Context("when the wait for event flag is enabled and the client can't connect", func() { 256 BeforeEach(func() { 257 input.WaitForEvent = true 258 input.WaitForEventTimeout = 3 * time.Second 259 input.TxID = "testtx" 260 input.PeerAddresses = []string{"approvepeer0"} 261 mockDeliverClient.DeliverFilteredReturns(nil, errors.New("robusta")) 262 }) 263 264 It("returns an error", func() { 265 err := approver.Approve() 266 Expect(err).To(MatchError("failed to connect to deliver on all peers: error connecting to deliver filtered at approvepeer0: robusta")) 267 }) 268 }) 269 270 Context("when the wait for event flag is enabled and the transaction isn't returned before the timeout", func() { 271 BeforeEach(func() { 272 input.WaitForEvent = true 273 input.WaitForEventTimeout = 10 * time.Millisecond 274 input.TxID = "testtx" 275 input.PeerAddresses = []string{"approvepeer0"} 276 delayChan := make(chan struct{}) 277 mockDeliverClient.DeliverFilteredStub = func(ctx context.Context, opts ...grpc.CallOption) (pb.Deliver_DeliverFilteredClient, error) { 278 mockDF := &mock.Deliver{} 279 mockDF.RecvStub = func() (*pb.DeliverResponse, error) { 280 <-delayChan 281 resp := &pb.DeliverResponse{ 282 Type: &pb.DeliverResponse_FilteredBlock{ 283 FilteredBlock: createFilteredBlock(input.ChannelID, "testtx"), 284 }, 285 } 286 return resp, nil 287 } 288 return mockDF, nil 289 } 290 }) 291 292 It("returns an error", func() { 293 err := approver.Approve() 294 Expect(err).To(MatchError("timed out waiting for txid on all peers")) 295 }) 296 }) 297 }) 298 299 Describe("ApproveForMyOrgCmd", func() { 300 var ( 301 approveForMyOrgCmd *cobra.Command 302 ) 303 304 BeforeEach(func() { 305 cryptoProvider, err := sw.NewDefaultSecurityLevelWithKeystore(sw.NewDummyKeyStore()) 306 Expect(err).To(BeNil()) 307 approveForMyOrgCmd = chaincode.ApproveForMyOrgCmd(nil, cryptoProvider) 308 approveForMyOrgCmd.SetArgs([]string{ 309 "--channelID=testchannel", 310 "--name=testcc", 311 "--version=testversion", 312 "--package-id=testpackageid", 313 "--sequence=1", 314 "--peerAddresses=querypeer1", 315 "--tlsRootCertFiles=tls1", 316 "--signature-policy=AND ('Org1MSP.member','Org2MSP.member')", 317 }) 318 }) 319 320 AfterEach(func() { 321 chaincode.ResetFlags() 322 }) 323 324 It("sets up the approver for my org and attempts to approve the chaincode definition", func() { 325 err := approveForMyOrgCmd.Execute() 326 Expect(err).To(MatchError(ContainSubstring("failed to retrieve endorser client"))) 327 }) 328 329 Context("when the channel config policy is specified", func() { 330 BeforeEach(func() { 331 cryptoProvider, err := sw.NewDefaultSecurityLevelWithKeystore(sw.NewDummyKeyStore()) 332 Expect(err).To(BeNil()) 333 approveForMyOrgCmd = chaincode.ApproveForMyOrgCmd(nil, cryptoProvider) 334 approveForMyOrgCmd.SetArgs([]string{ 335 "--channel-config-policy=/Channel/Application/Readers", 336 "--channelID=testchannel", 337 "--name=testcc", 338 "--version=testversion", 339 "--package-id=testpackageid", 340 "--sequence=1", 341 "--peerAddresses=querypeer1", 342 "--tlsRootCertFiles=tls1", 343 }) 344 }) 345 346 It("sets up the approver for my org and attempts to approve the chaincode definition", func() { 347 err := approveForMyOrgCmd.Execute() 348 Expect(err).To(MatchError(ContainSubstring("failed to retrieve endorser client"))) 349 }) 350 }) 351 352 Context("when a signature policy and channel config policy are both specified", func() { 353 BeforeEach(func() { 354 approveForMyOrgCmd.SetArgs([]string{ 355 "--signature-policy=a_policy", 356 "--channel-config-policy=anotha_policy", 357 "--channelID=testchannel", 358 "--name=testcc", 359 "--version=testversion", 360 "--package-id=testpackageid", 361 "--sequence=1", 362 "--peerAddresses=querypeer1", 363 "--tlsRootCertFiles=tls1", 364 }) 365 }) 366 367 It("returns an error", func() { 368 err := approveForMyOrgCmd.Execute() 369 Expect(err).To(MatchError("cannot specify both \"--signature-policy\" and \"--channel-config-policy\"")) 370 }) 371 }) 372 373 Context("when the signature policy is invalid", func() { 374 BeforeEach(func() { 375 approveForMyOrgCmd.SetArgs([]string{ 376 "--signature-policy=notapolicy", 377 "--channelID=testchannel", 378 "--name=testcc", 379 "--version=testversion", 380 "--package-id=testpackageid", 381 "--sequence=1", 382 "--peerAddresses=querypeer1", 383 "--tlsRootCertFiles=tls1", 384 }) 385 }) 386 387 It("returns an error", func() { 388 err := approveForMyOrgCmd.Execute() 389 Expect(err).To(MatchError("invalid signature policy: notapolicy")) 390 }) 391 }) 392 393 Context("when the collections config is invalid", func() { 394 BeforeEach(func() { 395 approveForMyOrgCmd.SetArgs([]string{ 396 "--collections-config=idontexist.json", 397 "--channelID=testchannel", 398 "--name=testcc", 399 "--version=testversion", 400 "--package-id=testpackageid", 401 "--sequence=1", 402 "--peerAddresses=querypeer1", 403 "--tlsRootCertFiles=tls1", 404 }) 405 }) 406 407 It("returns an error", func() { 408 err := approveForMyOrgCmd.Execute() 409 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")) 410 }) 411 }) 412 }) 413 }) 414 415 func createFilteredBlock(channelID string, txIDs ...string) *pb.FilteredBlock { 416 var filteredTransactions []*pb.FilteredTransaction 417 for _, txID := range txIDs { 418 ft := &pb.FilteredTransaction{ 419 Txid: txID, 420 TxValidationCode: pb.TxValidationCode_VALID, 421 } 422 filteredTransactions = append(filteredTransactions, ft) 423 } 424 fb := &pb.FilteredBlock{ 425 Number: 0, 426 ChannelId: channelID, 427 FilteredTransactions: filteredTransactions, 428 } 429 return fb 430 }