github.com/yimialmonte/fabric@v2.1.1+incompatible/core/chaincode/handler_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 "io" 11 "time" 12 13 "github.com/golang/protobuf/proto" 14 pb "github.com/hyperledger/fabric-protos-go/peer" 15 "github.com/hyperledger/fabric/common/metrics/metricsfakes" 16 "github.com/hyperledger/fabric/common/util" 17 ar "github.com/hyperledger/fabric/core/aclmgmt/resources" 18 "github.com/hyperledger/fabric/core/chaincode" 19 "github.com/hyperledger/fabric/core/chaincode/fake" 20 "github.com/hyperledger/fabric/core/chaincode/mock" 21 "github.com/hyperledger/fabric/core/common/ccprovider" 22 "github.com/hyperledger/fabric/core/common/sysccprovider" 23 "github.com/hyperledger/fabric/core/scc" 24 . "github.com/onsi/ginkgo" 25 . "github.com/onsi/ginkgo/extensions/table" 26 . "github.com/onsi/gomega" 27 "github.com/pkg/errors" 28 ) 29 30 var _ = Describe("Handler", func() { 31 var ( 32 fakeTransactionRegistry *mock.TransactionRegistry 33 fakeContextRegistry *fake.ContextRegistry 34 fakeChatStream *mock.ChaincodeStream 35 builtinSCCs scc.BuiltinSCCs 36 fakeTxSimulator *mock.TxSimulator 37 fakeHistoryQueryExecutor *mock.HistoryQueryExecutor 38 fakeQueryResponseBuilder *fake.QueryResponseBuilder 39 fakeACLProvider *mock.ACLProvider 40 fakeInvoker *mock.Invoker 41 fakeLedgerGetter *mock.LedgerGetter 42 fakeHandlerRegistry *fake.Registry 43 fakeApplicationConfigRetriever *fake.ApplicationConfigRetriever 44 fakeCollectionStore *mock.CollectionStore 45 fakeShimRequestsReceived *metricsfakes.Counter 46 fakeShimRequestsCompleted *metricsfakes.Counter 47 fakeShimRequestDuration *metricsfakes.Histogram 48 fakeExecuteTimeouts *metricsfakes.Counter 49 fakeCapabilites *mock.ApplicationCapabilities 50 51 responseNotifier chan *pb.ChaincodeMessage 52 txContext *chaincode.TransactionContext 53 54 handler *chaincode.Handler 55 ) 56 57 BeforeEach(func() { 58 fakeTransactionRegistry = &mock.TransactionRegistry{} 59 fakeTransactionRegistry.AddReturns(true) 60 61 fakeChatStream = &mock.ChaincodeStream{} 62 63 fakeTxSimulator = &mock.TxSimulator{} 64 fakeHistoryQueryExecutor = &mock.HistoryQueryExecutor{} 65 fakeCollectionStore = &mock.CollectionStore{} 66 67 responseNotifier = make(chan *pb.ChaincodeMessage, 1) 68 txContext = &chaincode.TransactionContext{ 69 ChannelID: "channel-id", 70 NamespaceID: "cc-instance-name", 71 TXSimulator: fakeTxSimulator, 72 HistoryQueryExecutor: fakeHistoryQueryExecutor, 73 ResponseNotifier: responseNotifier, 74 CollectionStore: fakeCollectionStore, 75 } 76 txContext.InitializeCollectionACLCache() 77 78 fakeACLProvider = &mock.ACLProvider{} 79 fakeInvoker = &mock.Invoker{} 80 fakeLedgerGetter = &mock.LedgerGetter{} 81 fakeQueryResponseBuilder = &fake.QueryResponseBuilder{} 82 fakeHandlerRegistry = &fake.Registry{} 83 84 fakeContextRegistry = &fake.ContextRegistry{} 85 fakeContextRegistry.GetReturns(txContext) 86 fakeContextRegistry.CreateReturns(txContext, nil) 87 88 fakeApplicationConfig := &mock.ApplicationConfig{} 89 fakeCapabilites = &mock.ApplicationCapabilities{} 90 fakeCapabilites.KeyLevelEndorsementReturns(true) 91 fakeApplicationConfig.CapabilitiesReturns(fakeCapabilites) 92 fakeApplicationConfigRetriever = &fake.ApplicationConfigRetriever{} 93 fakeApplicationConfigRetriever.GetApplicationConfigReturns(fakeApplicationConfig, true) 94 95 fakeShimRequestsReceived = &metricsfakes.Counter{} 96 fakeShimRequestsReceived.WithReturns(fakeShimRequestsReceived) 97 fakeShimRequestsCompleted = &metricsfakes.Counter{} 98 fakeShimRequestsCompleted.WithReturns(fakeShimRequestsCompleted) 99 fakeShimRequestDuration = &metricsfakes.Histogram{} 100 fakeShimRequestDuration.WithReturns(fakeShimRequestDuration) 101 fakeExecuteTimeouts = &metricsfakes.Counter{} 102 fakeExecuteTimeouts.WithReturns(fakeExecuteTimeouts) 103 104 builtinSCCs = map[string]struct{}{} 105 106 chaincodeMetrics := &chaincode.HandlerMetrics{ 107 ShimRequestsReceived: fakeShimRequestsReceived, 108 ShimRequestsCompleted: fakeShimRequestsCompleted, 109 ShimRequestDuration: fakeShimRequestDuration, 110 ExecuteTimeouts: fakeExecuteTimeouts, 111 } 112 113 handler = &chaincode.Handler{ 114 ACLProvider: fakeACLProvider, 115 ActiveTransactions: fakeTransactionRegistry, 116 Invoker: fakeInvoker, 117 LedgerGetter: fakeLedgerGetter, 118 QueryResponseBuilder: fakeQueryResponseBuilder, 119 Registry: fakeHandlerRegistry, 120 BuiltinSCCs: builtinSCCs, 121 TXContexts: fakeContextRegistry, 122 UUIDGenerator: chaincode.UUIDGeneratorFunc(func() string { 123 return "generated-query-id" 124 }), 125 AppConfig: fakeApplicationConfigRetriever, 126 Metrics: chaincodeMetrics, 127 } 128 chaincode.SetHandlerChatStream(handler, fakeChatStream) 129 chaincode.SetHandlerChaincodeID(handler, "test-handler-name:1.0") 130 }) 131 132 Describe("HandleTransaction", func() { 133 var ( 134 incomingMessage *pb.ChaincodeMessage 135 fakeMessageHandler *fake.MessageHandler 136 expectedResponse *pb.ChaincodeMessage 137 ) 138 139 BeforeEach(func() { 140 incomingMessage = &pb.ChaincodeMessage{ 141 Type: pb.ChaincodeMessage_GET_STATE, 142 Txid: "tx-id", 143 ChannelId: "channel-id", 144 } 145 146 expectedResponse = &pb.ChaincodeMessage{ 147 Type: pb.ChaincodeMessage_UNDEFINED, 148 Payload: []byte("handler-response-payload"), 149 Txid: "response-tx-id", 150 ChannelId: "response-channel-id", 151 } 152 fakeMessageHandler = &fake.MessageHandler{} 153 fakeMessageHandler.HandleReturns(expectedResponse, nil) 154 }) 155 156 It("registers the transaction ID from the message", func() { 157 handler.HandleTransaction(incomingMessage, fakeMessageHandler.Handle) 158 159 Expect(fakeTransactionRegistry.AddCallCount()).To(Equal(1)) 160 channelID, transactionID := fakeTransactionRegistry.AddArgsForCall(0) 161 Expect(channelID).To(Equal("channel-id")) 162 Expect(transactionID).To(Equal("tx-id")) 163 }) 164 165 It("ensures there is a valid transaction simulator available in the context", func() { 166 handler.HandleTransaction(incomingMessage, fakeMessageHandler.Handle) 167 168 Expect(fakeContextRegistry.GetCallCount()).To(Equal(1)) 169 channelID, txID := fakeContextRegistry.GetArgsForCall(0) 170 Expect(channelID).To(Equal("channel-id")) 171 Expect(txID).To(Equal("tx-id")) 172 }) 173 174 It("calls the delegate with the correct arguments", func() { 175 handler.HandleTransaction(incomingMessage, fakeMessageHandler.Handle) 176 177 Expect(fakeMessageHandler.HandleCallCount()).To(Equal(1)) 178 msg, ctx := fakeMessageHandler.HandleArgsForCall(0) 179 Expect(msg).To(Equal(incomingMessage)) 180 Expect(ctx).To(Equal(txContext)) 181 }) 182 183 It("sends the response message returned by the delegate", func() { 184 handler.HandleTransaction(incomingMessage, fakeMessageHandler.Handle) 185 186 Eventually(fakeChatStream.SendCallCount).Should(Equal(1)) 187 msg := fakeChatStream.SendArgsForCall(0) 188 Expect(msg).To(Equal(expectedResponse)) 189 }) 190 191 It("deregisters the transaction ID before sending the response", func() { 192 fakeTransactionRegistry.RemoveStub = func(channelID, txID string) { 193 Consistently(fakeChatStream.SendCallCount).Should(Equal(0)) 194 } 195 196 handler.HandleTransaction(incomingMessage, fakeMessageHandler.Handle) 197 198 Expect(fakeTransactionRegistry.RemoveCallCount()).To(Equal(1)) 199 channelID, transactionID := fakeTransactionRegistry.RemoveArgsForCall(0) 200 Expect(channelID).To(Equal("channel-id")) 201 Expect(transactionID).To(Equal("tx-id")) 202 }) 203 204 It("records shim requests received before requests completed", func() { 205 fakeShimRequestsReceived.AddStub = func(delta float64) { 206 defer GinkgoRecover() 207 Expect(fakeShimRequestsCompleted.AddCallCount()).To(Equal(0)) 208 } 209 210 handler.HandleTransaction(incomingMessage, fakeMessageHandler.Handle) 211 Eventually(fakeChatStream.SendCallCount).Should(Equal(1)) 212 msg := fakeChatStream.SendArgsForCall(0) 213 Expect(msg).To(Equal(expectedResponse)) 214 215 Expect(fakeShimRequestsReceived.WithCallCount()).To(Equal(1)) 216 labelValues := fakeShimRequestsReceived.WithArgsForCall(0) 217 Expect(labelValues).To(Equal([]string{ 218 "type", "GET_STATE", 219 "channel", "channel-id", 220 "chaincode", "test-handler-name:1.0", 221 })) 222 Expect(fakeShimRequestsReceived.AddCallCount()).To(Equal(1)) 223 Expect(fakeShimRequestsReceived.AddArgsForCall(0)).To(BeNumerically("~", 1.0)) 224 }) 225 226 It("records transactions completed after transactions received", func() { 227 fakeShimRequestsCompleted.AddStub = func(delta float64) { 228 defer GinkgoRecover() 229 Expect(fakeShimRequestsReceived.AddCallCount()).To(Equal(1)) 230 } 231 232 handler.HandleTransaction(incomingMessage, fakeMessageHandler.Handle) 233 Eventually(fakeChatStream.SendCallCount).Should(Equal(1)) 234 235 Expect(fakeShimRequestsCompleted.WithCallCount()).To(Equal(1)) 236 labelValues := fakeShimRequestsCompleted.WithArgsForCall(0) 237 Expect(labelValues).To(Equal([]string{ 238 "type", "GET_STATE", 239 "channel", "channel-id", 240 "chaincode", "test-handler-name:1.0", 241 "success", "true", 242 })) 243 Expect(fakeShimRequestsCompleted.AddCallCount()).To(Equal(1)) 244 Expect(fakeShimRequestsCompleted.AddArgsForCall(0)).To(BeNumerically("~", 1.0)) 245 }) 246 247 It("records transactions duration", func() { 248 handler.HandleTransaction(incomingMessage, fakeMessageHandler.Handle) 249 Eventually(fakeChatStream.SendCallCount).Should(Equal(1)) 250 251 Expect(fakeShimRequestDuration.WithCallCount()).To(Equal(1)) 252 labelValues := fakeShimRequestDuration.WithArgsForCall(0) 253 Expect(labelValues).To(Equal([]string{ 254 "type", "GET_STATE", 255 "channel", "channel-id", 256 "chaincode", "test-handler-name:1.0", 257 "success", "true", 258 })) 259 Expect(fakeShimRequestDuration.ObserveArgsForCall(0)).NotTo(BeZero()) 260 Expect(fakeShimRequestDuration.ObserveArgsForCall(0)).To(BeNumerically("<", 1.0)) 261 }) 262 263 Context("when the transaction returns an error", func() { 264 BeforeEach(func() { 265 fakeMessageHandler.HandleReturns(nil, errors.New("I am a total failure")) 266 }) 267 268 It("records metrics with success=false", func() { 269 handler.HandleTransaction(incomingMessage, fakeMessageHandler.Handle) 270 Eventually(fakeChatStream.SendCallCount).Should(Equal(1)) 271 272 Expect(fakeShimRequestsCompleted.WithCallCount()).To(Equal(1)) 273 labelValues := fakeShimRequestsCompleted.WithArgsForCall(0) 274 Expect(labelValues).To(Equal([]string{ 275 "type", "GET_STATE", 276 "channel", "channel-id", 277 "chaincode", "test-handler-name:1.0", 278 "success", "false", 279 })) 280 Expect(fakeShimRequestsCompleted.AddCallCount()).To(Equal(1)) 281 Expect(fakeShimRequestsCompleted.AddArgsForCall(0)).To(BeNumerically("~", 1.0)) 282 283 Expect(fakeShimRequestDuration.WithCallCount()).To(Equal(1)) 284 labelValues = fakeShimRequestDuration.WithArgsForCall(0) 285 Expect(labelValues).To(Equal([]string{ 286 "type", "GET_STATE", 287 "channel", "channel-id", 288 "chaincode", "test-handler-name:1.0", 289 "success", "false", 290 })) 291 Expect(fakeShimRequestDuration.ObserveArgsForCall(0)).NotTo(BeZero()) 292 Expect(fakeShimRequestDuration.ObserveArgsForCall(0)).To(BeNumerically("<", 1.0)) 293 }) 294 }) 295 296 Context("when the transaction ID has already been registered", func() { 297 BeforeEach(func() { 298 fakeTransactionRegistry.AddReturns(false) 299 }) 300 301 It("returns without sending a response", func() { 302 handler.HandleTransaction(incomingMessage, fakeMessageHandler.Handle) 303 Consistently(fakeChatStream.SendCallCount).Should(Equal(0)) 304 }) 305 306 It("does not call the delegate", func() { 307 handler.HandleTransaction(incomingMessage, fakeMessageHandler.Handle) 308 Expect(fakeMessageHandler.HandleCallCount()).To(Equal(0)) 309 }) 310 311 It("does not attempt to deregister the transaction ID", func() { 312 handler.HandleTransaction(incomingMessage, fakeMessageHandler.Handle) 313 Expect(fakeTransactionRegistry.RemoveCallCount()).To(Equal(0)) 314 }) 315 }) 316 317 Context("when the transaction context does not exist", func() { 318 BeforeEach(func() { 319 fakeContextRegistry.GetReturns(nil) 320 }) 321 322 It("sends an error message", func() { 323 handler.HandleTransaction(incomingMessage, fakeMessageHandler.Handle) 324 325 Eventually(fakeChatStream.SendCallCount).Should(Equal(1)) 326 msg := fakeChatStream.SendArgsForCall(0) 327 Expect(msg).To(Equal(&pb.ChaincodeMessage{ 328 Type: pb.ChaincodeMessage_ERROR, 329 Payload: []byte("GET_STATE failed: transaction ID: tx-id: no ledger context"), 330 Txid: "tx-id", 331 ChannelId: "channel-id", 332 })) 333 }) 334 335 It("deregisters the message transaction ID", func() { 336 handler.HandleTransaction(incomingMessage, fakeMessageHandler.Handle) 337 338 Expect(fakeTransactionRegistry.RemoveCallCount()).To(Equal(1)) 339 channelID, transactionID := fakeTransactionRegistry.RemoveArgsForCall(0) 340 Expect(channelID).To(Equal("channel-id")) 341 Expect(transactionID).To(Equal("tx-id")) 342 }) 343 }) 344 345 Context("when the transaction context is missing a transaction simulator", func() { 346 BeforeEach(func() { 347 txContext.TXSimulator = nil 348 }) 349 350 It("sends an error response", func() { 351 handler.HandleTransaction(incomingMessage, fakeMessageHandler.Handle) 352 353 Eventually(fakeChatStream.SendCallCount).Should(Equal(1)) 354 msg := fakeChatStream.SendArgsForCall(0) 355 Expect(msg).To(Equal(&pb.ChaincodeMessage{ 356 Type: pb.ChaincodeMessage_ERROR, 357 Payload: []byte("GET_STATE failed: transaction ID: tx-id: no ledger context"), 358 Txid: "tx-id", 359 ChannelId: "channel-id", 360 })) 361 }) 362 363 It("deregisters the message transaction ID", func() { 364 handler.HandleTransaction(incomingMessage, fakeMessageHandler.Handle) 365 366 Expect(fakeTransactionRegistry.RemoveCallCount()).To(Equal(1)) 367 channelID, transactionID := fakeTransactionRegistry.RemoveArgsForCall(0) 368 Expect(channelID).To(Equal("channel-id")) 369 Expect(transactionID).To(Equal("tx-id")) 370 }) 371 }) 372 373 Context("when the incoming message is INVOKE_CHAINCODE", func() { 374 var chaincodeSpec *pb.ChaincodeSpec 375 376 BeforeEach(func() { 377 chaincodeSpec = &pb.ChaincodeSpec{ 378 Type: pb.ChaincodeSpec_GOLANG, 379 ChaincodeId: &pb.ChaincodeID{ 380 Name: "target-chaincode-name", 381 Version: "target-chaincode-version", 382 }, 383 Input: &pb.ChaincodeInput{ 384 Args: util.ToChaincodeArgs("command", "arg"), 385 }, 386 } 387 payloadBytes, err := proto.Marshal(chaincodeSpec) 388 Expect(err).NotTo(HaveOccurred()) 389 390 incomingMessage.Type = pb.ChaincodeMessage_INVOKE_CHAINCODE 391 incomingMessage.Payload = payloadBytes 392 }) 393 394 It("validates the transaction context", func() { 395 txContext.TXSimulator = nil 396 handler.HandleTransaction(incomingMessage, fakeMessageHandler.Handle) 397 398 Eventually(fakeChatStream.SendCallCount).Should(Equal(1)) 399 msg := fakeChatStream.SendArgsForCall(0) 400 Expect(msg).To(Equal(&pb.ChaincodeMessage{ 401 Type: pb.ChaincodeMessage_ERROR, 402 Payload: []byte("INVOKE_CHAINCODE failed: transaction ID: tx-id: could not get valid transaction"), 403 Txid: "tx-id", 404 ChannelId: "channel-id", 405 })) 406 }) 407 408 Context("and the channel ID is not set", func() { 409 BeforeEach(func() { 410 incomingMessage.ChannelId = "" 411 }) 412 413 It("validates the transaction context", func() { 414 txContext.TXSimulator = nil 415 handler.HandleTransaction(incomingMessage, fakeMessageHandler.Handle) 416 417 Eventually(fakeChatStream.SendCallCount).Should(Equal(1)) 418 msg := fakeChatStream.SendArgsForCall(0) 419 Expect(msg).To(Equal(&pb.ChaincodeMessage{ 420 Type: pb.ChaincodeMessage_ERROR, 421 Payload: []byte("INVOKE_CHAINCODE failed: transaction ID: tx-id: could not get valid transaction"), 422 Txid: "tx-id", 423 })) 424 }) 425 426 Context("when the target is system chaincode", func() { 427 BeforeEach(func() { 428 builtinSCCs["target-chaincode-name"] = struct{}{} 429 }) 430 431 It("gets the transaction context without validation", func() { 432 txContext.TXSimulator = nil 433 handler.HandleTransaction(incomingMessage, fakeMessageHandler.Handle) 434 435 Expect(fakeContextRegistry.GetCallCount()).To(Equal(1)) 436 Eventually(fakeChatStream.SendCallCount).Should(Equal(1)) 437 msg := fakeChatStream.SendArgsForCall(0) 438 Expect(msg).To(Equal(expectedResponse)) 439 }) 440 441 Context("and the transaction context is missing", func() { 442 It("returns an error", func() { 443 fakeContextRegistry.GetReturns(nil) 444 handler.HandleTransaction(incomingMessage, fakeMessageHandler.Handle) 445 446 Eventually(fakeChatStream.SendCallCount).Should(Equal(1)) 447 msg := fakeChatStream.SendArgsForCall(0) 448 Expect(msg).To(Equal(&pb.ChaincodeMessage{ 449 Type: pb.ChaincodeMessage_ERROR, 450 Payload: []byte("INVOKE_CHAINCODE failed: transaction ID: tx-id: failed to get transaction context"), 451 Txid: "tx-id", 452 })) 453 }) 454 }) 455 }) 456 457 Context("when the payload fails to unmarshal into a chaincode spec", func() { 458 BeforeEach(func() { 459 incomingMessage.Payload = []byte("this-is-a-bogus-payload") 460 }) 461 462 It("sends an error response", func() { 463 handler.HandleTransaction(incomingMessage, fakeMessageHandler.Handle) 464 465 Eventually(fakeChatStream.SendCallCount).Should(Equal(1)) 466 msg := fakeChatStream.SendArgsForCall(0) 467 Expect(msg.Type).To(Equal(pb.ChaincodeMessage_ERROR)) 468 Expect(msg.Txid).To(Equal("tx-id")) 469 Expect(string(msg.Payload)).To(HavePrefix("INVOKE_CHAINCODE failed: transaction ID: tx-id: unmarshal failed: proto: ")) 470 }) 471 }) 472 }) 473 }) 474 475 Context("when the delegate returns an error", func() { 476 BeforeEach(func() { 477 fakeMessageHandler.HandleReturns(nil, errors.New("watermelon-swirl")) 478 }) 479 480 It("sends an error response", func() { 481 handler.HandleTransaction(incomingMessage, fakeMessageHandler.Handle) 482 483 Eventually(fakeChatStream.SendCallCount).Should(Equal(1)) 484 msg := fakeChatStream.SendArgsForCall(0) 485 Expect(msg).To(Equal(&pb.ChaincodeMessage{ 486 Type: pb.ChaincodeMessage_ERROR, 487 Payload: []byte("GET_STATE failed: transaction ID: tx-id: watermelon-swirl"), 488 Txid: "tx-id", 489 ChannelId: "channel-id", 490 })) 491 }) 492 }) 493 }) 494 495 Describe("HandlePutState", func() { 496 var incomingMessage *pb.ChaincodeMessage 497 var request *pb.PutState 498 499 BeforeEach(func() { 500 request = &pb.PutState{ 501 Key: "put-state-key", 502 Value: []byte("put-state-value"), 503 } 504 payload, err := proto.Marshal(request) 505 Expect(err).NotTo(HaveOccurred()) 506 507 incomingMessage = &pb.ChaincodeMessage{ 508 Type: pb.ChaincodeMessage_PUT_STATE, 509 Payload: payload, 510 Txid: "tx-id", 511 ChannelId: "channel-id", 512 } 513 }) 514 515 It("returns a response message", func() { 516 resp, err := handler.HandlePutState(incomingMessage, txContext) 517 Expect(err).NotTo(HaveOccurred()) 518 Expect(resp).To(Equal(&pb.ChaincodeMessage{ 519 Type: pb.ChaincodeMessage_RESPONSE, 520 Txid: "tx-id", 521 ChannelId: "channel-id", 522 })) 523 }) 524 525 Context("when unmarshaling the request fails", func() { 526 BeforeEach(func() { 527 incomingMessage.Payload = []byte("this-is-a-bogus-payload") 528 }) 529 530 It("returns an error", func() { 531 _, err := handler.HandlePutState(incomingMessage, txContext) 532 Expect(err).To(MatchError("unmarshal failed: proto: can't skip unknown wire type 4")) 533 }) 534 }) 535 536 Context("when the collection is not provided", func() { 537 It("calls SetState on the transaction simulator", func() { 538 _, err := handler.HandlePutState(incomingMessage, txContext) 539 Expect(err).NotTo(HaveOccurred()) 540 541 Expect(fakeTxSimulator.SetStateCallCount()).To(Equal(1)) 542 ccname, key, value := fakeTxSimulator.SetStateArgsForCall(0) 543 Expect(ccname).To(Equal("cc-instance-name")) 544 Expect(key).To(Equal("put-state-key")) 545 Expect(value).To(Equal([]byte("put-state-value"))) 546 }) 547 548 Context("when SeteState fails", func() { 549 BeforeEach(func() { 550 fakeTxSimulator.SetStateReturns(errors.New("king-kong")) 551 }) 552 553 It("returns an error", func() { 554 _, err := handler.HandlePutState(incomingMessage, txContext) 555 Expect(err).To(MatchError("king-kong")) 556 }) 557 }) 558 }) 559 560 Context("when the collection is provided", func() { 561 BeforeEach(func() { 562 request.Collection = "collection-name" 563 payload, err := proto.Marshal(request) 564 Expect(err).NotTo(HaveOccurred()) 565 incomingMessage.Payload = payload 566 fakeCollectionStore.RetrieveReadWritePermissionReturns(false, true, nil) // to 567 }) 568 569 It("calls SetPrivateData on the transaction simulator", func() { 570 _, err := handler.HandlePutState(incomingMessage, txContext) 571 Expect(err).NotTo(HaveOccurred()) 572 573 Expect(fakeTxSimulator.SetPrivateDataCallCount()).To(Equal(1)) 574 ccname, collection, key, value := fakeTxSimulator.SetPrivateDataArgsForCall(0) 575 Expect(ccname).To(Equal("cc-instance-name")) 576 Expect(collection).To(Equal("collection-name")) 577 Expect(key).To(Equal("put-state-key")) 578 Expect(value).To(Equal([]byte("put-state-value"))) 579 }) 580 581 Context("when SetPrivateData fails due to ledger error", func() { 582 BeforeEach(func() { 583 fakeTxSimulator.SetPrivateDataReturns(errors.New("godzilla")) 584 fakeCollectionStore.RetrieveReadWritePermissionReturns(false, true, nil) // to 585 }) 586 587 It("returns an error", func() { 588 _, err := handler.HandlePutState(incomingMessage, txContext) 589 Expect(err).To(MatchError("godzilla")) 590 }) 591 }) 592 593 Context("when SetPrivateData fails due to Init transaction", func() { 594 BeforeEach(func() { 595 txContext.IsInitTransaction = true 596 }) 597 598 It("returns the error from errorIfInitTransaction", func() { 599 _, err := handler.HandlePutState(incomingMessage, txContext) 600 Expect(err).To(MatchError("private data APIs are not allowed in chaincode Init()")) 601 }) 602 }) 603 604 Context("when SetPrivateData fails due to no write access permission", func() { 605 BeforeEach(func() { 606 fakeCollectionStore.RetrieveReadWritePermissionReturns(false, false, nil) 607 }) 608 609 It("returns the error from errorIfCreatorHasNoWriteAccess", func() { 610 _, err := handler.HandlePutState(incomingMessage, txContext) 611 Expect(err).To(MatchError("tx creator does not have write access" + 612 " permission on privatedata in chaincodeName:cc-instance-name" + 613 " collectionName: collection-name")) 614 }) 615 }) 616 }) 617 }) 618 619 Describe("HandlePutStateMetadata", func() { 620 var incomingMessage *pb.ChaincodeMessage 621 var request *pb.PutStateMetadata 622 623 BeforeEach(func() { 624 request = &pb.PutStateMetadata{ 625 Key: "put-state-key", 626 Metadata: &pb.StateMetadata{ 627 Metakey: "put-state-metakey", 628 Value: []byte("put-state-metadata-value"), 629 }, 630 } 631 payload, err := proto.Marshal(request) 632 Expect(err).NotTo(HaveOccurred()) 633 634 incomingMessage = &pb.ChaincodeMessage{ 635 Type: pb.ChaincodeMessage_PUT_STATE_METADATA, 636 Payload: payload, 637 Txid: "tx-id", 638 ChannelId: "channel-id", 639 } 640 }) 641 642 It("returns a response message", func() { 643 resp, err := handler.HandlePutStateMetadata(incomingMessage, txContext) 644 Expect(err).NotTo(HaveOccurred()) 645 Expect(resp).To(Equal(&pb.ChaincodeMessage{ 646 Type: pb.ChaincodeMessage_RESPONSE, 647 Txid: "tx-id", 648 ChannelId: "channel-id", 649 })) 650 }) 651 652 It("acquires application config for the channel", func() { 653 _, err := handler.HandlePutStateMetadata(incomingMessage, txContext) 654 Expect(err).NotTo(HaveOccurred()) 655 656 Expect(fakeApplicationConfigRetriever.GetApplicationConfigCallCount()).To(Equal(1)) 657 cid := fakeApplicationConfigRetriever.GetApplicationConfigArgsForCall(0) 658 Expect(cid).To(Equal("channel-id")) 659 }) 660 661 Context("when getting the app config metadata fails", func() { 662 BeforeEach(func() { 663 fakeApplicationConfigRetriever.GetApplicationConfigReturns(nil, false) 664 }) 665 666 It("returns an error", func() { 667 _, err := handler.HandlePutStateMetadata(incomingMessage, txContext) 668 Expect(err).To(MatchError("application config does not exist for channel-id")) 669 }) 670 }) 671 672 Context("when key level endorsement is not supported", func() { 673 BeforeEach(func() { 674 fakeCapabilites.KeyLevelEndorsementReturns(false) 675 }) 676 677 It("returns an error", func() { 678 _, err := handler.HandlePutStateMetadata(incomingMessage, txContext) 679 Expect(err).To(MatchError("key level endorsement is not enabled, channel application capability of V1_3 or later is required")) 680 }) 681 }) 682 683 Context("when unmarshaling the request fails", func() { 684 BeforeEach(func() { 685 incomingMessage.Payload = []byte("this-is-a-bogus-payload") 686 }) 687 688 It("returns an error", func() { 689 _, err := handler.HandlePutStateMetadata(incomingMessage, txContext) 690 Expect(err).To(MatchError("unmarshal failed: proto: can't skip unknown wire type 4")) 691 }) 692 }) 693 694 Context("when the collection is not provided", func() { 695 It("calls SetStateMetadata on the transaction simulator", func() { 696 _, err := handler.HandlePutStateMetadata(incomingMessage, txContext) 697 Expect(err).NotTo(HaveOccurred()) 698 699 Expect(fakeTxSimulator.SetStateMetadataCallCount()).To(Equal(1)) 700 ccname, key, value := fakeTxSimulator.SetStateMetadataArgsForCall(0) 701 Expect(ccname).To(Equal("cc-instance-name")) 702 Expect(key).To(Equal("put-state-key")) 703 Expect(value).To(Equal(map[string][]byte{ 704 "put-state-metakey": []byte("put-state-metadata-value"), 705 })) 706 }) 707 708 Context("when SetStateMetadata fails", func() { 709 BeforeEach(func() { 710 fakeTxSimulator.SetStateMetadataReturns(errors.New("king-kong")) 711 }) 712 713 It("returns an error", func() { 714 _, err := handler.HandlePutStateMetadata(incomingMessage, txContext) 715 Expect(err).To(MatchError("king-kong")) 716 }) 717 }) 718 }) 719 720 Context("when the collection is provided", func() { 721 BeforeEach(func() { 722 request.Collection = "collection-name" 723 payload, err := proto.Marshal(request) 724 Expect(err).NotTo(HaveOccurred()) 725 incomingMessage.Payload = payload 726 fakeCollectionStore.RetrieveReadWritePermissionReturns(false, true, nil) // to 727 }) 728 729 It("calls SetPrivateDataMetadata on the transaction simulator", func() { 730 _, err := handler.HandlePutStateMetadata(incomingMessage, txContext) 731 Expect(err).NotTo(HaveOccurred()) 732 733 Expect(fakeTxSimulator.SetPrivateDataMetadataCallCount()).To(Equal(1)) 734 ccname, collection, key, value := fakeTxSimulator.SetPrivateDataMetadataArgsForCall(0) 735 Expect(ccname).To(Equal("cc-instance-name")) 736 Expect(collection).To(Equal("collection-name")) 737 Expect(key).To(Equal("put-state-key")) 738 Expect(value).To(Equal(map[string][]byte{ 739 "put-state-metakey": []byte("put-state-metadata-value"), 740 })) 741 }) 742 743 Context("when SetPrivateDataMetadata fails due to ledger error", func() { 744 BeforeEach(func() { 745 fakeTxSimulator.SetPrivateDataMetadataReturns(errors.New("godzilla")) 746 }) 747 748 It("returns an error", func() { 749 _, err := handler.HandlePutStateMetadata(incomingMessage, txContext) 750 Expect(err).To(MatchError("godzilla")) 751 }) 752 }) 753 754 Context("when SetPrivateDataMetadata fails due to Init transaction", func() { 755 BeforeEach(func() { 756 txContext.IsInitTransaction = true 757 }) 758 759 It("returns the error from errorIfInitTransaction", func() { 760 _, err := handler.HandlePutStateMetadata(incomingMessage, txContext) 761 Expect(err).To(MatchError("private data APIs are not allowed in chaincode Init()")) 762 }) 763 }) 764 765 Context("when SetPrivateDataMetadata fails due to no write access permission", func() { 766 BeforeEach(func() { 767 fakeCollectionStore.RetrieveReadWritePermissionReturns(false, false, nil) 768 }) 769 770 It("returns the error from errorIfCreatorHasNoWriteAccess", func() { 771 _, err := handler.HandlePutStateMetadata(incomingMessage, txContext) 772 Expect(err).To(MatchError("tx creator does not have write access" + 773 " permission on privatedata in chaincodeName:cc-instance-name" + 774 " collectionName: collection-name")) 775 }) 776 }) 777 }) 778 }) 779 780 Describe("HandleDelState", func() { 781 var incomingMessage *pb.ChaincodeMessage 782 var request *pb.DelState 783 784 BeforeEach(func() { 785 request = &pb.DelState{ 786 Key: "del-state-key", 787 } 788 payload, err := proto.Marshal(request) 789 Expect(err).NotTo(HaveOccurred()) 790 791 incomingMessage = &pb.ChaincodeMessage{ 792 Type: pb.ChaincodeMessage_DEL_STATE, 793 Payload: payload, 794 Txid: "tx-id", 795 ChannelId: "channel-id", 796 } 797 }) 798 799 It("returns a response message", func() { 800 resp, err := handler.HandleDelState(incomingMessage, txContext) 801 Expect(err).NotTo(HaveOccurred()) 802 Expect(resp).To(Equal(&pb.ChaincodeMessage{ 803 Type: pb.ChaincodeMessage_RESPONSE, 804 Txid: "tx-id", 805 ChannelId: "channel-id", 806 })) 807 }) 808 809 Context("when unmarshalling the request fails", func() { 810 BeforeEach(func() { 811 incomingMessage.Payload = []byte("this-is-a-bogus-payload") 812 }) 813 814 It("returns an error", func() { 815 _, err := handler.HandleDelState(incomingMessage, txContext) 816 Expect(err).To(MatchError("unmarshal failed: proto: can't skip unknown wire type 4")) 817 }) 818 }) 819 820 Context("when collection is not set", func() { 821 It("calls DeleteState on the transaction simulator", func() { 822 _, err := handler.HandleDelState(incomingMessage, txContext) 823 Expect(err).NotTo(HaveOccurred()) 824 825 Expect(fakeTxSimulator.DeleteStateCallCount()).To(Equal(1)) 826 ccname, key := fakeTxSimulator.DeleteStateArgsForCall(0) 827 Expect(ccname).To(Equal("cc-instance-name")) 828 Expect(key).To(Equal("del-state-key")) 829 }) 830 831 Context("when DeleteState returns an error", func() { 832 BeforeEach(func() { 833 fakeTxSimulator.DeleteStateReturns(errors.New("orange")) 834 }) 835 836 It("return an error", func() { 837 _, err := handler.HandleDelState(incomingMessage, txContext) 838 Expect(err).To(MatchError("orange")) 839 }) 840 }) 841 }) 842 843 Context("when collection is set", func() { 844 BeforeEach(func() { 845 request.Collection = "collection-name" 846 payload, err := proto.Marshal(request) 847 Expect(err).NotTo(HaveOccurred()) 848 incomingMessage.Payload = payload 849 fakeCollectionStore.RetrieveReadWritePermissionReturns(false, true, nil) // to 850 }) 851 852 It("calls DeletePrivateData on the transaction simulator", func() { 853 _, err := handler.HandleDelState(incomingMessage, txContext) 854 Expect(err).NotTo(HaveOccurred()) 855 856 Expect(fakeTxSimulator.DeletePrivateDataCallCount()).To(Equal(1)) 857 ccname, collection, key := fakeTxSimulator.DeletePrivateDataArgsForCall(0) 858 Expect(ccname).To(Equal("cc-instance-name")) 859 Expect(collection).To(Equal("collection-name")) 860 Expect(key).To(Equal("del-state-key")) 861 }) 862 863 Context("when DeletePrivateData fails due to ledger error", func() { 864 BeforeEach(func() { 865 fakeTxSimulator.DeletePrivateDataReturns(errors.New("mango")) 866 }) 867 868 It("returns an error", func() { 869 _, err := handler.HandleDelState(incomingMessage, txContext) 870 Expect(err).To(MatchError("mango")) 871 }) 872 }) 873 874 Context("when DeletePrivateData fails due to Init transaction", func() { 875 BeforeEach(func() { 876 txContext.IsInitTransaction = true 877 }) 878 879 It("returns the error from errorIfInitTransaction", func() { 880 _, err := handler.HandleDelState(incomingMessage, txContext) 881 Expect(err).To(MatchError("private data APIs are not allowed in chaincode Init()")) 882 }) 883 }) 884 885 Context("when DeletePrivateData fails due to no write access permission", func() { 886 BeforeEach(func() { 887 fakeCollectionStore.RetrieveReadWritePermissionReturns(false, false, nil) 888 }) 889 890 It("returns the error from errorIfCreatorHasNoWriteAccess", func() { 891 _, err := handler.HandleDelState(incomingMessage, txContext) 892 Expect(err).To(MatchError("tx creator does not have write access" + 893 " permission on privatedata in chaincodeName:cc-instance-name" + 894 " collectionName: collection-name")) 895 }) 896 }) 897 }) 898 }) 899 900 Describe("HandleGetState", func() { 901 var ( 902 incomingMessage *pb.ChaincodeMessage 903 request *pb.GetState 904 expectedResponse *pb.ChaincodeMessage 905 ) 906 907 BeforeEach(func() { 908 request = &pb.GetState{ 909 Key: "get-state-key", 910 } 911 payload, err := proto.Marshal(request) 912 Expect(err).NotTo(HaveOccurred()) 913 914 incomingMessage = &pb.ChaincodeMessage{ 915 Type: pb.ChaincodeMessage_GET_STATE, 916 Payload: payload, 917 Txid: "tx-id", 918 ChannelId: "channel-id", 919 } 920 921 expectedResponse = &pb.ChaincodeMessage{ 922 Type: pb.ChaincodeMessage_RESPONSE, 923 Txid: "tx-id", 924 ChannelId: "channel-id", 925 } 926 }) 927 928 Context("when unmarshalling the request fails", func() { 929 BeforeEach(func() { 930 incomingMessage.Payload = []byte("this-is-a-bogus-payload") 931 }) 932 933 It("returns an error", func() { 934 _, err := handler.HandleGetState(incomingMessage, txContext) 935 Expect(err).To(MatchError("unmarshal failed: proto: can't skip unknown wire type 4")) 936 }) 937 }) 938 939 Context("when collection is set", func() { 940 BeforeEach(func() { 941 request.Collection = "collection-name" 942 payload, err := proto.Marshal(request) 943 Expect(err).NotTo(HaveOccurred()) 944 incomingMessage.Payload = payload 945 946 fakeCollectionStore.RetrieveReadWritePermissionReturns(true, false, nil) 947 fakeTxSimulator.GetPrivateDataReturns([]byte("get-private-data-response"), nil) 948 expectedResponse.Payload = []byte("get-private-data-response") 949 }) 950 951 It("calls GetPrivateData on the transaction simulator", func() { 952 _, err := handler.HandleGetState(incomingMessage, txContext) 953 Expect(err).NotTo(HaveOccurred()) 954 955 Expect(fakeTxSimulator.GetPrivateDataCallCount()).To(Equal(1)) 956 ccname, collection, key := fakeTxSimulator.GetPrivateDataArgsForCall(0) 957 Expect(ccname).To(Equal("cc-instance-name")) 958 Expect(collection).To(Equal("collection-name")) 959 Expect(key).To(Equal("get-state-key")) 960 }) 961 962 Context("and GetPrivateData fails due to ledger error", func() { 963 BeforeEach(func() { 964 fakeTxSimulator.GetPrivateDataReturns(nil, errors.New("french fries")) 965 }) 966 967 It("returns the error from GetPrivateData", func() { 968 _, err := handler.HandleGetState(incomingMessage, txContext) 969 Expect(err).To(MatchError("french fries")) 970 }) 971 }) 972 973 Context("and GetPrivateData fails due to no read access permission", func() { 974 BeforeEach(func() { 975 fakeCollectionStore.RetrieveReadWritePermissionReturns(false, false, nil) 976 }) 977 978 It("returns the error from errorIfCreatorHasNoReadAccess", func() { 979 _, err := handler.HandleGetState(incomingMessage, txContext) 980 Expect(err).To(MatchError("tx creator does not have read access" + 981 " permission on privatedata in chaincodeName:cc-instance-name" + 982 " collectionName: collection-name")) 983 }) 984 }) 985 986 Context("and GetPrivateData fails due to error in checking the read access permission", func() { 987 BeforeEach(func() { 988 fakeCollectionStore.RetrieveReadWritePermissionReturns(false, false, errors.New("no collection config")) 989 }) 990 991 It("returns the error from errorIfCreatorHasNoReadAccess", func() { 992 _, err := handler.HandleGetState(incomingMessage, txContext) 993 Expect(err).To(MatchError("no collection config")) 994 }) 995 }) 996 997 Context("and GetPrivateData fails due to Init transaction", func() { 998 BeforeEach(func() { 999 txContext.IsInitTransaction = true 1000 }) 1001 1002 It("returns the error from errorIfInitTransaction", func() { 1003 _, err := handler.HandleGetState(incomingMessage, txContext) 1004 Expect(err).To(MatchError("private data APIs are not allowed in chaincode Init()")) 1005 }) 1006 }) 1007 1008 Context("and GetPrivateData returns the response message", func() { 1009 BeforeEach(func() { 1010 //txContext.CollectionACLCache.put("collection-name", true, false) 1011 //fakeCollectionStore.HasReadAccessReturns(false, nil) // to 1012 // ensure that the access cache is used 1013 }) 1014 1015 It("returns the the response message from GetPrivateData", func() { 1016 fakeCollectionStore.RetrieveReadWritePermissionReturns(true, false, nil) // to 1017 resp, err := handler.HandleGetState(incomingMessage, txContext) 1018 Expect(err).NotTo(HaveOccurred()) 1019 Expect(resp).To(Equal(&pb.ChaincodeMessage{ 1020 Type: pb.ChaincodeMessage_RESPONSE, 1021 Payload: []byte("get-private-data-response"), 1022 Txid: "tx-id", 1023 ChannelId: "channel-id", 1024 })) 1025 // as the cache hit should happen in CollectionACLCache, the following 1026 // RetrieveReadWritePermissionReturns should not be called 1027 fakeCollectionStore.RetrieveReadWritePermissionReturns(false, false, nil) // to 1028 resp, err = handler.HandleGetState(incomingMessage, txContext) 1029 Expect(err).NotTo(HaveOccurred()) 1030 Expect(resp).To(Equal(&pb.ChaincodeMessage{ 1031 Type: pb.ChaincodeMessage_RESPONSE, 1032 Payload: []byte("get-private-data-response"), 1033 Txid: "tx-id", 1034 ChannelId: "channel-id", 1035 })) 1036 }) 1037 }) 1038 1039 It("returns the response message from GetPrivateData", func() { 1040 resp, err := handler.HandleGetState(incomingMessage, txContext) 1041 Expect(err).NotTo(HaveOccurred()) 1042 Expect(resp).To(Equal(&pb.ChaincodeMessage{ 1043 Type: pb.ChaincodeMessage_RESPONSE, 1044 Payload: []byte("get-private-data-response"), 1045 Txid: "tx-id", 1046 ChannelId: "channel-id", 1047 })) 1048 }) 1049 }) 1050 1051 Context("when collection is not set", func() { 1052 BeforeEach(func() { 1053 fakeTxSimulator.GetStateReturns([]byte("get-state-response"), nil) 1054 expectedResponse.Payload = []byte("get-state-response") 1055 }) 1056 1057 It("calls GetState on the transaction simulator", func() { 1058 _, err := handler.HandleGetState(incomingMessage, txContext) 1059 Expect(err).NotTo(HaveOccurred()) 1060 1061 Expect(fakeTxSimulator.GetStateCallCount()).To(Equal(1)) 1062 ccname, key := fakeTxSimulator.GetStateArgsForCall(0) 1063 Expect(ccname).To(Equal("cc-instance-name")) 1064 Expect(key).To(Equal("get-state-key")) 1065 }) 1066 1067 Context("and GetState fails", func() { 1068 BeforeEach(func() { 1069 fakeTxSimulator.GetStateReturns(nil, errors.New("tomato")) 1070 }) 1071 1072 It("returns the error from GetState", func() { 1073 _, err := handler.HandleGetState(incomingMessage, txContext) 1074 Expect(err).To(MatchError("tomato")) 1075 }) 1076 }) 1077 1078 It("returns the response from GetState", func() { 1079 resp, err := handler.HandleGetState(incomingMessage, txContext) 1080 Expect(err).NotTo(HaveOccurred()) 1081 Expect(resp).To(Equal(&pb.ChaincodeMessage{ 1082 Type: pb.ChaincodeMessage_RESPONSE, 1083 Payload: []byte("get-state-response"), 1084 Txid: "tx-id", 1085 ChannelId: "channel-id", 1086 })) 1087 }) 1088 }) 1089 }) 1090 1091 Describe("HandleGetPrivateDataHash", func() { 1092 var ( 1093 incomingMessage *pb.ChaincodeMessage 1094 request *pb.GetState 1095 expectedResponse *pb.ChaincodeMessage 1096 ) 1097 1098 BeforeEach(func() { 1099 request = &pb.GetState{ 1100 Collection: "collection-name", 1101 Key: "get-pvtdata-hash-key", 1102 } 1103 payload, err := proto.Marshal(request) 1104 Expect(err).NotTo(HaveOccurred()) 1105 1106 incomingMessage = &pb.ChaincodeMessage{ 1107 Type: pb.ChaincodeMessage_GET_PRIVATE_DATA_HASH, 1108 Payload: payload, 1109 Txid: "tx-id", 1110 ChannelId: "channel-id", 1111 } 1112 1113 expectedResponse = &pb.ChaincodeMessage{ 1114 Type: pb.ChaincodeMessage_RESPONSE, 1115 Payload: []byte("get-private-data-hash-response"), 1116 Txid: "tx-id", 1117 ChannelId: "channel-id", 1118 } 1119 fakeTxSimulator.GetPrivateDataHashReturns([]byte("get-private-data-hash-response"), nil) 1120 }) 1121 1122 It("calls GetPrivateDataHash on the transaction simulator and receives expected response", func() { 1123 response, err := handler.HandleGetPrivateDataHash(incomingMessage, txContext) 1124 Expect(err).NotTo(HaveOccurred()) 1125 1126 Expect(fakeTxSimulator.GetPrivateDataHashCallCount()).To(Equal(1)) 1127 ccname, collection, key := fakeTxSimulator.GetPrivateDataHashArgsForCall(0) 1128 Expect(ccname).To(Equal("cc-instance-name")) 1129 Expect(collection).To(Equal("collection-name")) 1130 Expect(key).To(Equal("get-pvtdata-hash-key")) 1131 Expect(response).To(Equal(expectedResponse)) 1132 }) 1133 1134 Context("when unmarshalling the request fails", func() { 1135 BeforeEach(func() { 1136 incomingMessage.Payload = []byte("this-is-a-bogus-payload") 1137 }) 1138 1139 It("returns an error", func() { 1140 _, err := handler.HandleGetPrivateDataHash(incomingMessage, txContext) 1141 Expect(err).To(MatchError("unmarshal failed: proto: can't skip unknown wire type 4")) 1142 }) 1143 }) 1144 1145 Context("and GetPrivateDataHash fails due to ledger error", func() { 1146 BeforeEach(func() { 1147 fakeTxSimulator.GetPrivateDataHashReturns(nil, errors.New("french fries")) 1148 }) 1149 1150 It("returns the error from GetPrivateData", func() { 1151 _, err := handler.HandleGetPrivateDataHash(incomingMessage, txContext) 1152 Expect(err).To(MatchError("french fries")) 1153 }) 1154 }) 1155 1156 Context("and GetPrivateData fails due to Init transaction", func() { 1157 BeforeEach(func() { 1158 txContext.IsInitTransaction = true 1159 }) 1160 1161 It("returns the error from errorIfInitTransaction", func() { 1162 _, err := handler.HandleGetPrivateDataHash(incomingMessage, txContext) 1163 Expect(err).To(MatchError("private data APIs are not allowed in chaincode Init()")) 1164 }) 1165 }) 1166 }) 1167 1168 Describe("HandleGetStateMetadata", func() { 1169 var ( 1170 incomingMessage *pb.ChaincodeMessage 1171 request *pb.GetStateMetadata 1172 expectedResponse *pb.ChaincodeMessage 1173 ) 1174 1175 BeforeEach(func() { 1176 request = &pb.GetStateMetadata{ 1177 Key: "get-state-key", 1178 } 1179 payload, err := proto.Marshal(request) 1180 Expect(err).NotTo(HaveOccurred()) 1181 1182 incomingMessage = &pb.ChaincodeMessage{ 1183 Type: pb.ChaincodeMessage_GET_STATE_METADATA, 1184 Payload: payload, 1185 Txid: "tx-id", 1186 ChannelId: "channel-id", 1187 } 1188 1189 expectedResponse = &pb.ChaincodeMessage{ 1190 Type: pb.ChaincodeMessage_RESPONSE, 1191 Txid: "tx-id", 1192 ChannelId: "channel-id", 1193 } 1194 }) 1195 1196 It("acquires application config for the channel", func() { 1197 _, err := handler.HandleGetStateMetadata(incomingMessage, txContext) 1198 Expect(err).NotTo(HaveOccurred()) 1199 1200 Expect(fakeApplicationConfigRetriever.GetApplicationConfigCallCount()).To(Equal(1)) 1201 cid := fakeApplicationConfigRetriever.GetApplicationConfigArgsForCall(0) 1202 Expect(cid).To(Equal("channel-id")) 1203 }) 1204 1205 Context("when getting the app config metadata fails", func() { 1206 BeforeEach(func() { 1207 fakeApplicationConfigRetriever.GetApplicationConfigReturns(nil, false) 1208 }) 1209 1210 It("returns an error", func() { 1211 _, err := handler.HandleGetStateMetadata(incomingMessage, txContext) 1212 Expect(err).To(MatchError("application config does not exist for channel-id")) 1213 }) 1214 }) 1215 1216 Context("when key level endorsement is not supported", func() { 1217 BeforeEach(func() { 1218 fakeCapabilites.KeyLevelEndorsementReturns(false) 1219 }) 1220 1221 It("returns an error", func() { 1222 _, err := handler.HandleGetStateMetadata(incomingMessage, txContext) 1223 Expect(err).To(MatchError("key level endorsement is not enabled, channel application capability of V1_3 or later is required")) 1224 }) 1225 }) 1226 1227 Context("when unmarshalling the request fails", func() { 1228 BeforeEach(func() { 1229 incomingMessage.Payload = []byte("this-is-a-bogus-payload") 1230 }) 1231 1232 It("returns an error", func() { 1233 _, err := handler.HandleGetStateMetadata(incomingMessage, txContext) 1234 Expect(err).To(MatchError("unmarshal failed: proto: can't skip unknown wire type 4")) 1235 }) 1236 }) 1237 1238 Context("when collection is set", func() { 1239 BeforeEach(func() { 1240 request.Collection = "collection-name" 1241 payload, err := proto.Marshal(request) 1242 Expect(err).NotTo(HaveOccurred()) 1243 incomingMessage.Payload = payload 1244 1245 metadata := map[string][]byte{ 1246 "get-state-metakey": []byte("get-private-metadata-response"), 1247 } 1248 fakeCollectionStore.RetrieveReadWritePermissionReturns(true, false, nil) 1249 fakeTxSimulator.GetPrivateDataMetadataReturns(metadata, nil) 1250 responsePayload, err := proto.Marshal(&pb.StateMetadataResult{ 1251 Entries: []*pb.StateMetadata{{ 1252 Metakey: "get-state-metakey", 1253 Value: []byte("get-private-metadata-response"), 1254 }}, 1255 }) 1256 Expect(err).NotTo(HaveOccurred()) 1257 expectedResponse.Payload = responsePayload 1258 }) 1259 1260 It("calls GetPrivateDataMetadata on the transaction simulator", func() { 1261 _, err := handler.HandleGetStateMetadata(incomingMessage, txContext) 1262 Expect(err).NotTo(HaveOccurred()) 1263 1264 Expect(fakeTxSimulator.GetPrivateDataMetadataCallCount()).To(Equal(1)) 1265 ccname, collection, key := fakeTxSimulator.GetPrivateDataMetadataArgsForCall(0) 1266 Expect(ccname).To(Equal("cc-instance-name")) 1267 Expect(collection).To(Equal("collection-name")) 1268 Expect(key).To(Equal("get-state-key")) 1269 }) 1270 1271 It("returns the response message from GetPrivateDataMetadata", func() { 1272 resp, err := handler.HandleGetStateMetadata(incomingMessage, txContext) 1273 Expect(err).NotTo(HaveOccurred()) 1274 Expect(resp).To(Equal(expectedResponse)) 1275 }) 1276 1277 Context("and GetPrivateDataMetadata fails due to ledger error", func() { 1278 BeforeEach(func() { 1279 fakeTxSimulator.GetPrivateDataMetadataReturns(nil, errors.New("french fries")) 1280 }) 1281 1282 It("returns the error from GetPrivateDataMetadata", func() { 1283 _, err := handler.HandleGetStateMetadata(incomingMessage, txContext) 1284 Expect(err).To(MatchError("french fries")) 1285 }) 1286 }) 1287 1288 Context("and GetPrivateDataMetadata fails due to no read access permission", func() { 1289 BeforeEach(func() { 1290 fakeCollectionStore.RetrieveReadWritePermissionReturns(false, false, nil) 1291 }) 1292 1293 It("returns the error from GetPrivateDataMetadata", func() { 1294 _, err := handler.HandleGetStateMetadata(incomingMessage, txContext) 1295 Expect(err).To(MatchError("tx creator does not have read access" + 1296 " permission on privatedata in chaincodeName:cc-instance-name" + 1297 " collectionName: collection-name")) 1298 }) 1299 }) 1300 1301 Context("and GetPrivateDataMetadata fails due to error in checking the read access permission", func() { 1302 BeforeEach(func() { 1303 fakeCollectionStore.RetrieveReadWritePermissionReturns(false, false, errors.New("no collection config")) 1304 }) 1305 1306 It("returns the error from GetPrivateDataMetadata", func() { 1307 _, err := handler.HandleGetStateMetadata(incomingMessage, txContext) 1308 Expect(err).To(MatchError("no collection config")) 1309 }) 1310 }) 1311 1312 Context("and GetPrivateDataMetadata fails due to Init transaction", func() { 1313 BeforeEach(func() { 1314 txContext.IsInitTransaction = true 1315 }) 1316 1317 It("returns the error from errorIfInitTransaction", func() { 1318 _, err := handler.HandleGetStateMetadata(incomingMessage, txContext) 1319 Expect(err).To(MatchError("private data APIs are not allowed in chaincode Init()")) 1320 }) 1321 }) 1322 }) 1323 1324 Context("when collection is not set", func() { 1325 BeforeEach(func() { 1326 metadata := map[string][]byte{ 1327 "get-state-metakey": []byte("get-state-metadata-response"), 1328 } 1329 fakeTxSimulator.GetStateMetadataReturns(metadata, nil) 1330 responsePayload, err := proto.Marshal(&pb.StateMetadataResult{ 1331 Entries: []*pb.StateMetadata{{ 1332 Metakey: "get-state-metakey", 1333 Value: []byte("get-state-metadata-response"), 1334 }}, 1335 }) 1336 Expect(err).NotTo(HaveOccurred()) 1337 expectedResponse.Payload = responsePayload 1338 }) 1339 1340 It("calls GetStateMetadata on the transaction simulator", func() { 1341 _, err := handler.HandleGetStateMetadata(incomingMessage, txContext) 1342 Expect(err).NotTo(HaveOccurred()) 1343 1344 Expect(fakeTxSimulator.GetStateMetadataCallCount()).To(Equal(1)) 1345 ccname, key := fakeTxSimulator.GetStateMetadataArgsForCall(0) 1346 Expect(ccname).To(Equal("cc-instance-name")) 1347 Expect(key).To(Equal("get-state-key")) 1348 }) 1349 1350 It("returns the response from GetStateMetadata", func() { 1351 resp, err := handler.HandleGetStateMetadata(incomingMessage, txContext) 1352 Expect(err).NotTo(HaveOccurred()) 1353 Expect(resp).To(Equal(expectedResponse)) 1354 }) 1355 1356 Context("and GetStateMetadata fails", func() { 1357 BeforeEach(func() { 1358 fakeTxSimulator.GetStateMetadataReturns(nil, errors.New("tomato")) 1359 }) 1360 1361 It("returns the error from GetStateMetadata", func() { 1362 _, err := handler.HandleGetStateMetadata(incomingMessage, txContext) 1363 Expect(err).To(MatchError("tomato")) 1364 }) 1365 }) 1366 }) 1367 }) 1368 1369 Describe("HandleGetStateByRange", func() { 1370 var ( 1371 incomingMessage *pb.ChaincodeMessage 1372 request *pb.GetStateByRange 1373 expectedResponse *pb.ChaincodeMessage 1374 fakeIterator *mock.QueryResultsIterator 1375 expectedQueryResponse *pb.QueryResponse 1376 expectedPayload []byte 1377 ) 1378 1379 BeforeEach(func() { 1380 request = &pb.GetStateByRange{ 1381 StartKey: "get-state-start-key", 1382 EndKey: "get-state-end-key", 1383 } 1384 payload, err := proto.Marshal(request) 1385 Expect(err).NotTo(HaveOccurred()) 1386 1387 incomingMessage = &pb.ChaincodeMessage{ 1388 Type: pb.ChaincodeMessage_GET_STATE, 1389 Payload: payload, 1390 Txid: "tx-id", 1391 ChannelId: "channel-id", 1392 } 1393 1394 fakeIterator = &mock.QueryResultsIterator{} 1395 fakeTxSimulator.GetStateRangeScanIteratorReturns(fakeIterator, nil) 1396 1397 expectedQueryResponse = &pb.QueryResponse{ 1398 Results: nil, 1399 HasMore: true, 1400 Id: "query-response-id", 1401 } 1402 fakeQueryResponseBuilder.BuildQueryResponseReturns(expectedQueryResponse, nil) 1403 1404 expectedPayload, err = proto.Marshal(expectedQueryResponse) 1405 Expect(err).NotTo(HaveOccurred()) 1406 1407 expectedResponse = &pb.ChaincodeMessage{ 1408 Type: pb.ChaincodeMessage_RESPONSE, 1409 Txid: "tx-id", 1410 Payload: expectedPayload, 1411 ChannelId: "channel-id", 1412 } 1413 }) 1414 1415 It("initializes a query context", func() { 1416 _, err := handler.HandleGetStateByRange(incomingMessage, txContext) 1417 Expect(err).NotTo(HaveOccurred()) 1418 1419 pqr := txContext.GetPendingQueryResult("generated-query-id") 1420 Expect(pqr).To(Equal(&chaincode.PendingQueryResult{})) 1421 iter := txContext.GetQueryIterator("generated-query-id") 1422 Expect(iter).To(Equal(fakeIterator)) 1423 retCount := txContext.GetTotalReturnCount("generated-query-id") 1424 Expect(*retCount).To(Equal(int32(0))) 1425 }) 1426 1427 It("returns the response message", func() { 1428 resp, err := handler.HandleGetStateByRange(incomingMessage, txContext) 1429 Expect(err).NotTo(HaveOccurred()) 1430 Expect(resp).To(Equal(expectedResponse)) 1431 }) 1432 1433 Context("when collection is not set", func() { 1434 It("calls GetStateRangeScanIterator on the transaction simulator", func() { 1435 _, err := handler.HandleGetStateByRange(incomingMessage, txContext) 1436 Expect(err).NotTo(HaveOccurred()) 1437 1438 Expect(fakeTxSimulator.GetStateRangeScanIteratorCallCount()).To(Equal(1)) 1439 ccname, startKey, endKey := fakeTxSimulator.GetStateRangeScanIteratorArgsForCall(0) 1440 Expect(ccname).To(Equal("cc-instance-name")) 1441 Expect(startKey).To(Equal("get-state-start-key")) 1442 Expect(endKey).To(Equal("get-state-end-key")) 1443 }) 1444 1445 Context("and GetStateRangeScanIterator fails", func() { 1446 BeforeEach(func() { 1447 fakeTxSimulator.GetStateRangeScanIteratorReturns(nil, errors.New("tomato")) 1448 }) 1449 1450 It("returns the error from GetStateRangeScanIterator", func() { 1451 _, err := handler.HandleGetStateByRange(incomingMessage, txContext) 1452 Expect(err).To(MatchError("tomato")) 1453 }) 1454 }) 1455 1456 It("returns the response from GetStateRangeScanIterator", func() { 1457 resp, err := handler.HandleGetStateByRange(incomingMessage, txContext) 1458 Expect(err).NotTo(HaveOccurred()) 1459 Expect(resp).To(Equal(expectedResponse)) 1460 }) 1461 }) 1462 1463 Context("when collection is set", func() { 1464 BeforeEach(func() { 1465 request.Collection = "collection-name" 1466 payload, err := proto.Marshal(request) 1467 Expect(err).NotTo(HaveOccurred()) 1468 incomingMessage.Payload = payload 1469 1470 fakeCollectionStore.RetrieveReadWritePermissionReturns(true, false, nil) 1471 fakeTxSimulator.GetPrivateDataRangeScanIteratorReturns(fakeIterator, nil) 1472 }) 1473 1474 It("calls GetPrivateDataRangeScanIterator on the transaction simulator", func() { 1475 _, err := handler.HandleGetStateByRange(incomingMessage, txContext) 1476 Expect(err).NotTo(HaveOccurred()) 1477 1478 Expect(fakeTxSimulator.GetPrivateDataRangeScanIteratorCallCount()).To(Equal(1)) 1479 ccname, collection, startKey, endKey := fakeTxSimulator.GetPrivateDataRangeScanIteratorArgsForCall(0) 1480 Expect(ccname).To(Equal("cc-instance-name")) 1481 Expect(collection).To(Equal("collection-name")) 1482 Expect(startKey).To(Equal("get-state-start-key")) 1483 Expect(endKey).To(Equal("get-state-end-key")) 1484 }) 1485 1486 Context("and GetPrivateDataRangeScanIterator fails due to ledger error", func() { 1487 BeforeEach(func() { 1488 fakeTxSimulator.GetPrivateDataRangeScanIteratorReturns(nil, errors.New("french fries")) 1489 }) 1490 1491 It("returns the error from GetPrivateDataRangeScanIterator", func() { 1492 _, err := handler.HandleGetStateByRange(incomingMessage, txContext) 1493 Expect(err).To(MatchError("french fries")) 1494 }) 1495 }) 1496 1497 Context("and GetPrivateDataRangeScanIterator fails due to no read access permission", func() { 1498 BeforeEach(func() { 1499 fakeCollectionStore.RetrieveReadWritePermissionReturns(false, false, nil) 1500 }) 1501 1502 It("returns the error from GetPrivateDataRangeScanIterator", func() { 1503 _, err := handler.HandleGetStateByRange(incomingMessage, txContext) 1504 Expect(err).To(MatchError("tx creator does not have read access" + 1505 " permission on privatedata in chaincodeName:cc-instance-name" + 1506 " collectionName: collection-name")) 1507 }) 1508 }) 1509 1510 Context("and GetPrivateDataRangeScanIterator fails due to error in checking the read access permission", func() { 1511 BeforeEach(func() { 1512 fakeCollectionStore.RetrieveReadWritePermissionReturns(false, false, errors.New("no collection config")) 1513 }) 1514 1515 It("returns the error from GetPrivateDataRangeScanIterator", func() { 1516 _, err := handler.HandleGetStateByRange(incomingMessage, txContext) 1517 Expect(err).To(MatchError("no collection config")) 1518 }) 1519 }) 1520 1521 Context("and GetPrivateDataRangeScanIterator fails due to Init transaction", func() { 1522 BeforeEach(func() { 1523 txContext.IsInitTransaction = true 1524 }) 1525 1526 It("returns the error from errorIfInitTransaction", func() { 1527 _, err := handler.HandleGetStateByRange(incomingMessage, txContext) 1528 Expect(err).To(MatchError("private data APIs are not allowed in chaincode Init()")) 1529 }) 1530 }) 1531 }) 1532 1533 Context("when unmarshalling the request fails", func() { 1534 BeforeEach(func() { 1535 incomingMessage.Payload = []byte("this-is-a-bogus-payload") 1536 }) 1537 1538 It("returns an error", func() { 1539 _, err := handler.HandleGetStateByRange(incomingMessage, txContext) 1540 Expect(err).To(MatchError("unmarshal failed: proto: can't skip unknown wire type 4")) 1541 }) 1542 }) 1543 1544 Context("when building the query response fails", func() { 1545 BeforeEach(func() { 1546 fakeQueryResponseBuilder.BuildQueryResponseReturns(nil, errors.New("garbanzo")) 1547 }) 1548 1549 It("returns the error", func() { 1550 _, err := handler.HandleGetStateByRange(incomingMessage, txContext) 1551 Expect(err).To(MatchError("garbanzo")) 1552 }) 1553 1554 It("cleans up the query context", func() { 1555 handler.HandleGetStateByRange(incomingMessage, txContext) 1556 1557 pqr := txContext.GetPendingQueryResult("generated-query-id") 1558 Expect(pqr).To(BeNil()) 1559 iter := txContext.GetQueryIterator("generated-query-id") 1560 Expect(iter).To(BeNil()) 1561 }) 1562 }) 1563 1564 Context("when marshling the response fails", func() { 1565 BeforeEach(func() { 1566 fakeQueryResponseBuilder.BuildQueryResponseReturns(nil, nil) 1567 }) 1568 1569 It("returns the error", func() { 1570 _, err := handler.HandleGetStateByRange(incomingMessage, txContext) 1571 Expect(err).To(MatchError("marshal failed: proto: Marshal called with nil")) 1572 }) 1573 1574 It("cleans up the query context", func() { 1575 handler.HandleGetStateByRange(incomingMessage, txContext) 1576 1577 pqr := txContext.GetPendingQueryResult("generated-query-id") 1578 Expect(pqr).To(BeNil()) 1579 iter := txContext.GetQueryIterator("generated-query-id") 1580 Expect(iter).To(BeNil()) 1581 retCount := txContext.GetTotalReturnCount("generated-query-id") 1582 Expect(retCount).To(BeNil()) 1583 }) 1584 }) 1585 }) 1586 1587 Describe("HandleQueryStateNext", func() { 1588 var ( 1589 fakeIterator *mock.QueryResultsIterator 1590 expectedQueryResponse *pb.QueryResponse 1591 request *pb.QueryStateNext 1592 incomingMessage *pb.ChaincodeMessage 1593 ) 1594 1595 BeforeEach(func() { 1596 request = &pb.QueryStateNext{ 1597 Id: "query-state-next-id", 1598 } 1599 payload, err := proto.Marshal(request) 1600 Expect(err).NotTo(HaveOccurred()) 1601 1602 fakeIterator = &mock.QueryResultsIterator{} 1603 txContext.InitializeQueryContext("query-state-next-id", fakeIterator) 1604 1605 incomingMessage = &pb.ChaincodeMessage{ 1606 Type: pb.ChaincodeMessage_GET_STATE, 1607 Payload: payload, 1608 Txid: "tx-id", 1609 ChannelId: "channel-id", 1610 } 1611 expectedQueryResponse = &pb.QueryResponse{ 1612 Results: nil, 1613 HasMore: true, 1614 Id: "query-response-id", 1615 } 1616 fakeQueryResponseBuilder.BuildQueryResponseReturns(expectedQueryResponse, nil) 1617 }) 1618 1619 It("builds a query response", func() { 1620 _, err := handler.HandleQueryStateNext(incomingMessage, txContext) 1621 Expect(err).NotTo(HaveOccurred()) 1622 1623 Expect(fakeQueryResponseBuilder.BuildQueryResponseCallCount()).To(Equal(1)) 1624 tctx, iter, id, _, _ := fakeQueryResponseBuilder.BuildQueryResponseArgsForCall(0) 1625 Expect(tctx).To(Equal(txContext)) 1626 Expect(iter).To(Equal(fakeIterator)) 1627 Expect(id).To(Equal("query-state-next-id")) 1628 }) 1629 1630 It("returns a chaincode message with the query response", func() { 1631 resp, err := handler.HandleQueryStateNext(incomingMessage, txContext) 1632 Expect(err).NotTo(HaveOccurred()) 1633 1634 expectedPayload, err := proto.Marshal(expectedQueryResponse) 1635 Expect(err).NotTo(HaveOccurred()) 1636 1637 Expect(resp).To(Equal(&pb.ChaincodeMessage{ 1638 Type: pb.ChaincodeMessage_RESPONSE, 1639 Payload: expectedPayload, 1640 ChannelId: "channel-id", 1641 Txid: "tx-id", 1642 })) 1643 }) 1644 1645 Context("when unmarshalling the request fails", func() { 1646 BeforeEach(func() { 1647 incomingMessage.Payload = []byte("this-is-a-bogus-payload") 1648 }) 1649 1650 It("returns an error", func() { 1651 _, err := handler.HandleQueryStateNext(incomingMessage, txContext) 1652 Expect(err).To(MatchError("unmarshal failed: proto: can't skip unknown wire type 4")) 1653 }) 1654 }) 1655 1656 Context("when the query iterator is missing", func() { 1657 BeforeEach(func() { 1658 txContext.CleanupQueryContext("query-state-next-id") 1659 }) 1660 1661 It("returns an error", func() { 1662 _, err := handler.HandleQueryStateNext(incomingMessage, txContext) 1663 Expect(err).To(MatchError("query iterator not found")) 1664 }) 1665 }) 1666 1667 Context("when building the query response fails", func() { 1668 BeforeEach(func() { 1669 fakeQueryResponseBuilder.BuildQueryResponseReturns(nil, errors.New("potato")) 1670 }) 1671 1672 It("returns an error", func() { 1673 _, err := handler.HandleQueryStateNext(incomingMessage, txContext) 1674 Expect(err).To(MatchError("potato")) 1675 }) 1676 1677 It("cleans up the query context", func() { 1678 handler.HandleQueryStateNext(incomingMessage, txContext) 1679 1680 pqr := txContext.GetPendingQueryResult("generated-query-id") 1681 Expect(pqr).To(BeNil()) 1682 iter := txContext.GetQueryIterator("generated-query-id") 1683 Expect(iter).To(BeNil()) 1684 retCount := txContext.GetTotalReturnCount("generated-query-id") 1685 Expect(retCount).To(BeNil()) 1686 }) 1687 }) 1688 1689 Context("when marshaling the query response fails", func() { 1690 BeforeEach(func() { 1691 fakeQueryResponseBuilder.BuildQueryResponseReturns(nil, nil) 1692 }) 1693 1694 It("returns an error", func() { 1695 _, err := handler.HandleQueryStateNext(incomingMessage, txContext) 1696 Expect(err).To(MatchError("marshal failed: proto: Marshal called with nil")) 1697 }) 1698 1699 It("cleans up the query context", func() { 1700 handler.HandleQueryStateNext(incomingMessage, txContext) 1701 1702 pqr := txContext.GetPendingQueryResult("generated-query-id") 1703 Expect(pqr).To(BeNil()) 1704 iter := txContext.GetQueryIterator("generated-query-id") 1705 Expect(iter).To(BeNil()) 1706 retCount := txContext.GetTotalReturnCount("generated-query-id") 1707 Expect(retCount).To(BeNil()) 1708 }) 1709 }) 1710 }) 1711 1712 Describe("HandleQueryStateClose", func() { 1713 var ( 1714 fakeIterator *mock.QueryResultsIterator 1715 expectedQueryResponse *pb.QueryResponse 1716 request *pb.QueryStateClose 1717 incomingMessage *pb.ChaincodeMessage 1718 ) 1719 1720 BeforeEach(func() { 1721 request = &pb.QueryStateClose{ 1722 Id: "query-state-close-id", 1723 } 1724 payload, err := proto.Marshal(request) 1725 Expect(err).NotTo(HaveOccurred()) 1726 1727 fakeIterator = &mock.QueryResultsIterator{} 1728 txContext.InitializeQueryContext("query-state-close-id", fakeIterator) 1729 1730 incomingMessage = &pb.ChaincodeMessage{ 1731 Type: pb.ChaincodeMessage_GET_STATE, 1732 Payload: payload, 1733 Txid: "tx-id", 1734 ChannelId: "channel-id", 1735 } 1736 expectedQueryResponse = &pb.QueryResponse{ 1737 Id: "query-state-close-id", 1738 } 1739 }) 1740 1741 It("returns a chaincode message with the query response", func() { 1742 resp, err := handler.HandleQueryStateClose(incomingMessage, txContext) 1743 Expect(err).NotTo(HaveOccurred()) 1744 1745 expectedPayload, err := proto.Marshal(expectedQueryResponse) 1746 Expect(err).NotTo(HaveOccurred()) 1747 1748 Expect(resp).To(Equal(&pb.ChaincodeMessage{ 1749 Type: pb.ChaincodeMessage_RESPONSE, 1750 Payload: expectedPayload, 1751 ChannelId: "channel-id", 1752 Txid: "tx-id", 1753 })) 1754 }) 1755 1756 Context("when unmarshalling the request fails", func() { 1757 BeforeEach(func() { 1758 incomingMessage.Payload = []byte("this-is-a-bogus-payload") 1759 }) 1760 1761 It("returns an error", func() { 1762 _, err := handler.HandleQueryStateClose(incomingMessage, txContext) 1763 Expect(err).To(MatchError("unmarshal failed: proto: can't skip unknown wire type 4")) 1764 }) 1765 }) 1766 1767 Context("when the query iterator is missing", func() { 1768 BeforeEach(func() { 1769 txContext.CleanupQueryContext("query-state-close-id") 1770 }) 1771 1772 It("keeps calm and carries on", func() { 1773 _, err := handler.HandleQueryStateClose(incomingMessage, txContext) 1774 Expect(err).NotTo(HaveOccurred()) 1775 }) 1776 }) 1777 }) 1778 1779 Describe("HandleGetQueryResult", func() { 1780 var ( 1781 request *pb.GetQueryResult 1782 incomingMessage *pb.ChaincodeMessage 1783 expectedQueryResponse *pb.QueryResponse 1784 fakeIterator *mock.QueryResultsIterator 1785 ) 1786 1787 BeforeEach(func() { 1788 request = &pb.GetQueryResult{ 1789 Query: "query-result", 1790 } 1791 payload, err := proto.Marshal(request) 1792 Expect(err).NotTo(HaveOccurred()) 1793 1794 incomingMessage = &pb.ChaincodeMessage{ 1795 Type: pb.ChaincodeMessage_GET_STATE, 1796 Payload: payload, 1797 Txid: "tx-id", 1798 ChannelId: "channel-id", 1799 } 1800 1801 expectedQueryResponse = &pb.QueryResponse{ 1802 Id: "query-response-id", 1803 } 1804 fakeQueryResponseBuilder.BuildQueryResponseReturns(expectedQueryResponse, nil) 1805 1806 fakeIterator = &mock.QueryResultsIterator{} 1807 fakeTxSimulator.ExecuteQueryReturns(fakeIterator, nil) 1808 }) 1809 1810 Context("when collection is not set", func() { 1811 It("calls ExecuteQuery on the transaction simulator", func() { 1812 _, err := handler.HandleGetQueryResult(incomingMessage, txContext) 1813 Expect(err).NotTo(HaveOccurred()) 1814 1815 Expect(fakeTxSimulator.ExecuteQueryCallCount()).To(Equal(1)) 1816 ccname, query := fakeTxSimulator.ExecuteQueryArgsForCall(0) 1817 Expect(ccname).To(Equal("cc-instance-name")) 1818 Expect(query).To(Equal("query-result")) 1819 }) 1820 1821 Context("and ExecuteQuery fails", func() { 1822 BeforeEach(func() { 1823 fakeTxSimulator.ExecuteQueryReturns(nil, errors.New("mushrooms")) 1824 }) 1825 1826 It("returns the error", func() { 1827 _, err := handler.HandleGetQueryResult(incomingMessage, txContext) 1828 Expect(err).To(MatchError("mushrooms")) 1829 }) 1830 }) 1831 }) 1832 1833 Context("when collection is set", func() { 1834 BeforeEach(func() { 1835 request.Collection = "collection-name" 1836 payload, err := proto.Marshal(request) 1837 Expect(err).NotTo(HaveOccurred()) 1838 incomingMessage.Payload = payload 1839 1840 fakeCollectionStore.RetrieveReadWritePermissionReturns(true, false, nil) 1841 fakeTxSimulator.ExecuteQueryOnPrivateDataReturns(fakeIterator, nil) 1842 }) 1843 1844 It("calls ExecuteQueryOnPrivateDataon the transaction simulator", func() { 1845 _, err := handler.HandleGetQueryResult(incomingMessage, txContext) 1846 Expect(err).NotTo(HaveOccurred()) 1847 1848 Expect(fakeTxSimulator.ExecuteQueryOnPrivateDataCallCount()).To(Equal(1)) 1849 ccname, collection, query := fakeTxSimulator.ExecuteQueryOnPrivateDataArgsForCall(0) 1850 Expect(ccname).To(Equal("cc-instance-name")) 1851 Expect(collection).To(Equal("collection-name")) 1852 Expect(query).To(Equal("query-result")) 1853 }) 1854 1855 It("initializes a query context", func() { 1856 _, err := handler.HandleGetQueryResult(incomingMessage, txContext) 1857 Expect(err).NotTo(HaveOccurred()) 1858 1859 pqr := txContext.GetPendingQueryResult("generated-query-id") 1860 Expect(pqr).To(Equal(&chaincode.PendingQueryResult{})) 1861 iter := txContext.GetQueryIterator("generated-query-id") 1862 Expect(iter).To(Equal(fakeIterator)) 1863 retCount := txContext.GetTotalReturnCount("generated-query-id") 1864 Expect(*retCount).To(Equal(int32(0))) 1865 }) 1866 1867 Context("and ExecuteQueryOnPrivateData fails due to ledger error", func() { 1868 BeforeEach(func() { 1869 fakeTxSimulator.ExecuteQueryOnPrivateDataReturns(nil, errors.New("pizza")) 1870 }) 1871 1872 It("returns the error", func() { 1873 _, err := handler.HandleGetQueryResult(incomingMessage, txContext) 1874 Expect(err).To(MatchError("pizza")) 1875 }) 1876 }) 1877 1878 Context("and ExecuteQueryOnPrivateData fails due to no read access permission", func() { 1879 BeforeEach(func() { 1880 fakeCollectionStore.RetrieveReadWritePermissionReturns(false, false, nil) 1881 }) 1882 1883 It("returns the error", func() { 1884 _, err := handler.HandleGetQueryResult(incomingMessage, txContext) 1885 Expect(err).To(MatchError("tx creator does not have read access" + 1886 " permission on privatedata in chaincodeName:cc-instance-name" + 1887 " collectionName: collection-name")) 1888 }) 1889 }) 1890 1891 Context("and ExecuteQueryOnPrivateData fails due to error in checking the read access permission", func() { 1892 BeforeEach(func() { 1893 fakeCollectionStore.RetrieveReadWritePermissionReturns(false, false, errors.New("no collection config")) 1894 }) 1895 1896 It("returns the error", func() { 1897 _, err := handler.HandleGetQueryResult(incomingMessage, txContext) 1898 Expect(err).To(MatchError("no collection config")) 1899 }) 1900 }) 1901 1902 Context("and ExecuteQueryOnPrivateData fails due to Init transaction", func() { 1903 BeforeEach(func() { 1904 txContext.IsInitTransaction = true 1905 }) 1906 1907 It("returns the error from errorIfInitTransaction", func() { 1908 _, err := handler.HandleGetQueryResult(incomingMessage, txContext) 1909 Expect(err).To(MatchError("private data APIs are not allowed in chaincode Init()")) 1910 }) 1911 }) 1912 }) 1913 1914 It("builds the query response", func() { 1915 _, err := handler.HandleGetQueryResult(incomingMessage, txContext) 1916 Expect(err).NotTo(HaveOccurred()) 1917 1918 Expect(fakeQueryResponseBuilder.BuildQueryResponseCallCount()).To(Equal(1)) 1919 tctx, iter, iterID, _, _ := fakeQueryResponseBuilder.BuildQueryResponseArgsForCall(0) 1920 Expect(tctx).To(Equal(txContext)) 1921 Expect(iter).To(Equal(fakeIterator)) 1922 Expect(iterID).To(Equal("generated-query-id")) 1923 }) 1924 1925 Context("when building the query response fails", func() { 1926 BeforeEach(func() { 1927 fakeQueryResponseBuilder.BuildQueryResponseReturns(nil, errors.New("peppers")) 1928 }) 1929 1930 It("returns the error", func() { 1931 _, err := handler.HandleGetQueryResult(incomingMessage, txContext) 1932 Expect(err).To(MatchError("peppers")) 1933 }) 1934 1935 It("cleans up the query context", func() { 1936 handler.HandleGetQueryResult(incomingMessage, txContext) 1937 1938 pqr := txContext.GetPendingQueryResult("generated-query-id") 1939 Expect(pqr).To(BeNil()) 1940 iter := txContext.GetQueryIterator("generated-query-id") 1941 Expect(iter).To(BeNil()) 1942 retCount := txContext.GetTotalReturnCount("generated-query-id") 1943 Expect(retCount).To(BeNil()) 1944 }) 1945 }) 1946 1947 Context("when unmarshalling the request fails", func() { 1948 BeforeEach(func() { 1949 incomingMessage.Payload = []byte("this-is-a-bogus-payload") 1950 }) 1951 1952 It("returns an error", func() { 1953 _, err := handler.HandleGetQueryResult(incomingMessage, txContext) 1954 Expect(err).To(MatchError("unmarshal failed: proto: can't skip unknown wire type 4")) 1955 }) 1956 }) 1957 1958 Context("when marshaling the response fails", func() { 1959 BeforeEach(func() { 1960 fakeQueryResponseBuilder.BuildQueryResponseReturns(nil, nil) 1961 }) 1962 1963 It("returns an error", func() { 1964 _, err := handler.HandleGetQueryResult(incomingMessage, txContext) 1965 Expect(err).To(MatchError("marshal failed: proto: Marshal called with nil")) 1966 }) 1967 1968 It("cleans up the query context", func() { 1969 handler.HandleGetQueryResult(incomingMessage, txContext) 1970 1971 pqr := txContext.GetPendingQueryResult("generated-query-id") 1972 Expect(pqr).To(BeNil()) 1973 iter := txContext.GetQueryIterator("generated-query-id") 1974 Expect(iter).To(BeNil()) 1975 retCount := txContext.GetTotalReturnCount("generated-query-id") 1976 Expect(retCount).To(BeNil()) 1977 }) 1978 }) 1979 }) 1980 1981 Describe("HandleGetHistoryForKey", func() { 1982 var ( 1983 request *pb.GetHistoryForKey 1984 incomingMessage *pb.ChaincodeMessage 1985 expectedQueryResponse *pb.QueryResponse 1986 fakeIterator *mock.QueryResultsIterator 1987 ) 1988 1989 BeforeEach(func() { 1990 request = &pb.GetHistoryForKey{ 1991 Key: "history-key", 1992 } 1993 payload, err := proto.Marshal(request) 1994 Expect(err).NotTo(HaveOccurred()) 1995 1996 incomingMessage = &pb.ChaincodeMessage{ 1997 Type: pb.ChaincodeMessage_GET_HISTORY_FOR_KEY, 1998 Payload: payload, 1999 Txid: "tx-id", 2000 ChannelId: "channel-id", 2001 } 2002 2003 expectedQueryResponse = &pb.QueryResponse{ 2004 Id: "query-response-id", 2005 } 2006 fakeQueryResponseBuilder.BuildQueryResponseReturns(expectedQueryResponse, nil) 2007 2008 fakeIterator = &mock.QueryResultsIterator{} 2009 fakeHistoryQueryExecutor.GetHistoryForKeyReturns(fakeIterator, nil) 2010 }) 2011 2012 It("calls GetHistoryForKey on the history query executor", func() { 2013 _, err := handler.HandleGetHistoryForKey(incomingMessage, txContext) 2014 Expect(err).NotTo(HaveOccurred()) 2015 2016 Expect(fakeHistoryQueryExecutor.GetHistoryForKeyCallCount()).To(Equal(1)) 2017 ccname, key := fakeHistoryQueryExecutor.GetHistoryForKeyArgsForCall(0) 2018 Expect(ccname).To(Equal("cc-instance-name")) 2019 Expect(key).To(Equal("history-key")) 2020 }) 2021 2022 It("initializes a query context", func() { 2023 _, err := handler.HandleGetHistoryForKey(incomingMessage, txContext) 2024 Expect(err).NotTo(HaveOccurred()) 2025 2026 pqr := txContext.GetPendingQueryResult("generated-query-id") 2027 Expect(pqr).To(Equal(&chaincode.PendingQueryResult{})) 2028 iter := txContext.GetQueryIterator("generated-query-id") 2029 Expect(iter).To(Equal(fakeIterator)) 2030 retCount := txContext.GetTotalReturnCount("generated-query-id") 2031 Expect(*retCount).To(Equal(int32(0))) 2032 }) 2033 2034 It("builds a query response", func() { 2035 _, err := handler.HandleGetHistoryForKey(incomingMessage, txContext) 2036 Expect(err).NotTo(HaveOccurred()) 2037 2038 Expect(fakeQueryResponseBuilder.BuildQueryResponseCallCount()).To(Equal(1)) 2039 tctx, iter, iterID, _, _ := fakeQueryResponseBuilder.BuildQueryResponseArgsForCall(0) 2040 Expect(tctx).To(Equal(txContext)) 2041 Expect(iter).To(Equal(fakeIterator)) 2042 Expect(iterID).To(Equal("generated-query-id")) 2043 }) 2044 2045 Context("when unmarshalling the request fails", func() { 2046 BeforeEach(func() { 2047 incomingMessage.Payload = []byte("this-is-a-bogus-payload") 2048 }) 2049 2050 It("returns an error", func() { 2051 _, err := handler.HandleGetHistoryForKey(incomingMessage, txContext) 2052 Expect(err).To(MatchError("unmarshal failed: proto: can't skip unknown wire type 4")) 2053 }) 2054 }) 2055 2056 Context("when the history query executor fails", func() { 2057 BeforeEach(func() { 2058 fakeHistoryQueryExecutor.GetHistoryForKeyReturns(nil, errors.New("pepperoni")) 2059 }) 2060 2061 It("returns an error", func() { 2062 _, err := handler.HandleGetHistoryForKey(incomingMessage, txContext) 2063 Expect(err).To(MatchError("pepperoni")) 2064 }) 2065 }) 2066 2067 Context("when building the query response fails", func() { 2068 BeforeEach(func() { 2069 fakeQueryResponseBuilder.BuildQueryResponseReturns(nil, errors.New("mushrooms")) 2070 }) 2071 2072 It("returns an error", func() { 2073 _, err := handler.HandleGetHistoryForKey(incomingMessage, txContext) 2074 Expect(err).To(MatchError("mushrooms")) 2075 }) 2076 2077 It("cleans up the query context", func() { 2078 handler.HandleGetHistoryForKey(incomingMessage, txContext) 2079 2080 pqr := txContext.GetPendingQueryResult("generated-query-id") 2081 Expect(pqr).To(BeNil()) 2082 iter := txContext.GetQueryIterator("generated-query-id") 2083 Expect(iter).To(BeNil()) 2084 retCount := txContext.GetTotalReturnCount("generated-query-id") 2085 Expect(retCount).To(BeNil()) 2086 }) 2087 }) 2088 2089 Context("when marshaling the query response fails", func() { 2090 BeforeEach(func() { 2091 fakeQueryResponseBuilder.BuildQueryResponseReturns(nil, nil) 2092 }) 2093 2094 It("returns an error", func() { 2095 _, err := handler.HandleGetHistoryForKey(incomingMessage, txContext) 2096 Expect(err).To(MatchError("marshal failed: proto: Marshal called with nil")) 2097 }) 2098 2099 It("cleans up the query context", func() { 2100 handler.HandleGetHistoryForKey(incomingMessage, txContext) 2101 2102 pqr := txContext.GetPendingQueryResult("generated-query-id") 2103 Expect(pqr).To(BeNil()) 2104 iter := txContext.GetQueryIterator("generated-query-id") 2105 Expect(iter).To(BeNil()) 2106 retCount := txContext.GetTotalReturnCount("generated-query-id") 2107 Expect(retCount).To(BeNil()) 2108 }) 2109 }) 2110 2111 Context("when HistoryQueryExecutor is nil", func() { 2112 BeforeEach(func() { 2113 txContext.HistoryQueryExecutor = nil 2114 }) 2115 2116 It("returns an error", func() { 2117 _, err := handler.HandleGetHistoryForKey(incomingMessage, txContext) 2118 Expect(err).To(MatchError("history database is not enabled")) 2119 }) 2120 }) 2121 }) 2122 2123 Describe("HandleInvokeChaincode", func() { 2124 var ( 2125 expectedSignedProp *pb.SignedProposal 2126 expectedProposal *pb.Proposal 2127 fakePeerLedger *mock.PeerLedger 2128 newTxSimulator *mock.TxSimulator 2129 newHistoryQueryExecutor *mock.HistoryQueryExecutor 2130 request *pb.ChaincodeSpec 2131 incomingMessage *pb.ChaincodeMessage 2132 responseMessage *pb.ChaincodeMessage 2133 ) 2134 2135 BeforeEach(func() { 2136 expectedProposal = &pb.Proposal{ 2137 Header: []byte("proposal-header"), 2138 Payload: []byte("proposal-payload"), 2139 } 2140 expectedSignedProp = &pb.SignedProposal{ 2141 ProposalBytes: []byte("signed-proposal-bytes"), 2142 Signature: []byte("signature"), 2143 } 2144 txContext.Proposal = expectedProposal 2145 txContext.SignedProp = expectedSignedProp 2146 2147 newTxSimulator = &mock.TxSimulator{} 2148 newHistoryQueryExecutor = &mock.HistoryQueryExecutor{} 2149 fakePeerLedger = &mock.PeerLedger{} 2150 fakePeerLedger.NewTxSimulatorReturns(newTxSimulator, nil) 2151 fakeLedgerGetter.GetLedgerReturns(fakePeerLedger) 2152 fakePeerLedger.NewHistoryQueryExecutorReturns(newHistoryQueryExecutor, nil) 2153 2154 request = &pb.ChaincodeSpec{ 2155 ChaincodeId: &pb.ChaincodeID{ 2156 Name: "target-chaincode-name:target-version", 2157 }, 2158 } 2159 payload, err := proto.Marshal(request) 2160 Expect(err).NotTo(HaveOccurred()) 2161 2162 incomingMessage = &pb.ChaincodeMessage{ 2163 Type: pb.ChaincodeMessage_INVOKE_CHAINCODE, 2164 Payload: payload, 2165 Txid: "tx-id", 2166 ChannelId: "channel-id", 2167 } 2168 2169 responseMessage = &pb.ChaincodeMessage{} 2170 fakeInvoker.InvokeReturns(responseMessage, nil) 2171 }) 2172 2173 It("evaluates the access control policy", func() { 2174 _, err := handler.HandleInvokeChaincode(incomingMessage, txContext) 2175 Expect(err).NotTo(HaveOccurred()) 2176 2177 Expect(fakeACLProvider.CheckACLCallCount()).To(Equal(1)) 2178 resource, chainID, proposal := fakeACLProvider.CheckACLArgsForCall(0) 2179 Expect(resource).To(Equal(ar.Peer_ChaincodeToChaincode)) 2180 Expect(chainID).To(Equal("channel-id")) 2181 Expect(proposal).To(Equal(expectedSignedProp)) 2182 }) 2183 2184 Context("when the target channel is different from the context", func() { 2185 BeforeEach(func() { 2186 request = &pb.ChaincodeSpec{ 2187 ChaincodeId: &pb.ChaincodeID{ 2188 Name: "target-chaincode-name:target-version/target-channel-id", 2189 }, 2190 } 2191 payload, err := proto.Marshal(request) 2192 Expect(err).NotTo(HaveOccurred()) 2193 incomingMessage.Payload = payload 2194 }) 2195 2196 It("uses the channel form the target for access checks", func() { 2197 _, err := handler.HandleInvokeChaincode(incomingMessage, txContext) 2198 Expect(err).NotTo(HaveOccurred()) 2199 2200 Expect(fakeACLProvider.CheckACLCallCount()).To(Equal(1)) 2201 resource, chainID, proposal := fakeACLProvider.CheckACLArgsForCall(0) 2202 Expect(resource).To(Equal(ar.Peer_ChaincodeToChaincode)) 2203 Expect(chainID).To(Equal("target-channel-id")) 2204 Expect(proposal).To(Equal(expectedSignedProp)) 2205 }) 2206 2207 It("gets the ledger for the target channel", func() { 2208 _, err := handler.HandleInvokeChaincode(incomingMessage, txContext) 2209 Expect(err).NotTo(HaveOccurred()) 2210 2211 Expect(fakeLedgerGetter.GetLedgerCallCount()).To(Equal(1)) 2212 chainID := fakeLedgerGetter.GetLedgerArgsForCall(0) 2213 Expect(chainID).To(Equal("target-channel-id")) 2214 }) 2215 2216 It("creates a new tx simulator for target execution", func() { 2217 _, err := handler.HandleInvokeChaincode(incomingMessage, txContext) 2218 Expect(err).NotTo(HaveOccurred()) 2219 2220 Expect(fakePeerLedger.NewTxSimulatorCallCount()).To(Equal(1)) 2221 txid := fakePeerLedger.NewTxSimulatorArgsForCall(0) 2222 Expect(txid).To(Equal("tx-id")) 2223 }) 2224 2225 It("provides the new simulator in the context used for execution", func() { 2226 _, err := handler.HandleInvokeChaincode(incomingMessage, txContext) 2227 Expect(err).NotTo(HaveOccurred()) 2228 2229 Expect(fakeInvoker.InvokeCallCount()).To(Equal(1)) 2230 txParams, _, _ := fakeInvoker.InvokeArgsForCall(0) 2231 Expect(txParams.TXSimulator).To(BeIdenticalTo(newTxSimulator)) // same instance, not just equal 2232 }) 2233 2234 It("creates a new history query executor for target execution", func() { 2235 _, err := handler.HandleInvokeChaincode(incomingMessage, txContext) 2236 Expect(err).NotTo(HaveOccurred()) 2237 2238 Expect(fakePeerLedger.NewHistoryQueryExecutorCallCount()).To(Equal(1)) 2239 }) 2240 2241 It("provides the new history query executor in the context used for execution", func() { 2242 _, err := handler.HandleInvokeChaincode(incomingMessage, txContext) 2243 Expect(err).NotTo(HaveOccurred()) 2244 2245 Expect(fakeInvoker.InvokeCallCount()).To(Equal(1)) 2246 txParams, _, _ := fakeInvoker.InvokeArgsForCall(0) 2247 Expect(txParams.HistoryQueryExecutor).To(BeIdenticalTo(newHistoryQueryExecutor)) // same instance, not just equal 2248 }) 2249 2250 It("marks the new transaction simulator as done after execute", func() { 2251 fakeInvoker.InvokeStub = func(*ccprovider.TransactionParams, string, *pb.ChaincodeInput) (*pb.ChaincodeMessage, error) { 2252 Expect(newTxSimulator.DoneCallCount()).To(Equal(0)) 2253 return responseMessage, nil 2254 } 2255 _, err := handler.HandleInvokeChaincode(incomingMessage, txContext) 2256 Expect(err).NotTo(HaveOccurred()) 2257 2258 Expect(fakeInvoker.InvokeCallCount()).To(Equal(1)) 2259 Expect(newTxSimulator.DoneCallCount()).To(Equal(1)) 2260 }) 2261 2262 Context("when getting the ledger for the target channel fails", func() { 2263 BeforeEach(func() { 2264 fakeLedgerGetter.GetLedgerReturns(nil) 2265 }) 2266 2267 It("returns an error", func() { 2268 _, err := handler.HandleInvokeChaincode(incomingMessage, txContext) 2269 Expect(err).To(MatchError("failed to find ledger for channel: target-channel-id")) 2270 }) 2271 }) 2272 2273 Context("when creating the new tx simulator fails", func() { 2274 BeforeEach(func() { 2275 fakePeerLedger.NewTxSimulatorReturns(nil, errors.New("bonkers")) 2276 }) 2277 2278 It("returns an error", func() { 2279 _, err := handler.HandleInvokeChaincode(incomingMessage, txContext) 2280 Expect(err).To(MatchError("bonkers")) 2281 }) 2282 }) 2283 2284 Context("when creating the new history query executor fails", func() { 2285 BeforeEach(func() { 2286 fakePeerLedger.NewHistoryQueryExecutorReturns(nil, errors.New("razzies")) 2287 }) 2288 2289 It("returns an error", func() { 2290 _, err := handler.HandleInvokeChaincode(incomingMessage, txContext) 2291 Expect(err).To(MatchError("razzies")) 2292 }) 2293 }) 2294 }) 2295 2296 Context("when the target is a system chaincode", func() { 2297 BeforeEach(func() { 2298 builtinSCCs["target-chaincode-name"] = struct{}{} 2299 }) 2300 2301 It("does not perform acl checks", func() { 2302 _, err := handler.HandleInvokeChaincode(incomingMessage, txContext) 2303 Expect(err).NotTo(HaveOccurred()) 2304 2305 Expect(fakeACLProvider.CheckACLCallCount()).To(Equal(0)) 2306 }) 2307 }) 2308 2309 Context("when the target is not a system chaincode", func() { 2310 Context("when the signed proposal is nil", func() { 2311 BeforeEach(func() { 2312 txContext.SignedProp = nil 2313 }) 2314 2315 It("returns an error", func() { 2316 _, err := handler.HandleInvokeChaincode(incomingMessage, txContext) 2317 Expect(err).To(MatchError("signed proposal must not be nil from caller [channel-id.target-chaincode-name#target-version]")) 2318 }) 2319 }) 2320 }) 2321 2322 Context("when the access control check fails", func() { 2323 BeforeEach(func() { 2324 fakeACLProvider.CheckACLReturns(errors.New("no-soup-for-you")) 2325 }) 2326 2327 It("returns the error", func() { 2328 _, err := handler.HandleInvokeChaincode(incomingMessage, txContext) 2329 Expect(err).To(MatchError("no-soup-for-you")) 2330 }) 2331 }) 2332 2333 Context("when execute fails", func() { 2334 BeforeEach(func() { 2335 fakeInvoker.InvokeReturns(nil, errors.New("lemons")) 2336 }) 2337 2338 It("returns an error", func() { 2339 _, err := handler.HandleInvokeChaincode(incomingMessage, txContext) 2340 Expect(err).To(MatchError("execute failed: lemons")) 2341 }) 2342 }) 2343 2344 Context("when unmarshaling the request fails", func() { 2345 BeforeEach(func() { 2346 incomingMessage.Payload = []byte("this-is-a-bogus-payload") 2347 }) 2348 2349 It("returns an error", func() { 2350 _, err := handler.HandleInvokeChaincode(incomingMessage, txContext) 2351 Expect(err).To(MatchError("unmarshal failed: proto: can't skip unknown wire type 4")) 2352 }) 2353 }) 2354 2355 Context("when marshaling the response fails", func() { 2356 BeforeEach(func() { 2357 fakeInvoker.InvokeReturns(nil, nil) 2358 }) 2359 2360 It("returns an error", func() { 2361 _, err := handler.HandleInvokeChaincode(incomingMessage, txContext) 2362 Expect(err).To(MatchError("marshal failed: proto: Marshal called with nil")) 2363 }) 2364 }) 2365 }) 2366 2367 Describe("Execute", func() { 2368 var ( 2369 incomingMessage *pb.ChaincodeMessage 2370 expectedProposal *pb.Proposal 2371 expectedSignedProp *pb.SignedProposal 2372 txParams *ccprovider.TransactionParams 2373 ) 2374 2375 BeforeEach(func() { 2376 request := &pb.ChaincodeInput{ 2377 Args: util.ToChaincodeArgs("arg1", "arg2"), 2378 } 2379 payload, err := proto.Marshal(request) 2380 Expect(err).NotTo(HaveOccurred()) 2381 2382 expectedProposal = &pb.Proposal{ 2383 Header: []byte("proposal-header"), 2384 Payload: []byte("proposal-payload"), 2385 } 2386 2387 expectedSignedProp = &pb.SignedProposal{ 2388 ProposalBytes: []byte("signed-proposal-bytes"), 2389 Signature: []byte("signature"), 2390 } 2391 2392 incomingMessage = &pb.ChaincodeMessage{ 2393 Type: pb.ChaincodeMessage_TRANSACTION, 2394 Txid: "tx-id", 2395 Payload: payload, 2396 ChannelId: "channel-id", 2397 } 2398 2399 txParams = &ccprovider.TransactionParams{ 2400 TxID: "tx-id", 2401 ChannelID: "channel-id", 2402 SignedProp: expectedSignedProp, 2403 Proposal: expectedProposal, 2404 } 2405 }) 2406 2407 It("creates transaction context", func() { 2408 close(responseNotifier) 2409 handler.Execute(txParams, "chaincode-name", incomingMessage, time.Second) 2410 2411 Expect(fakeContextRegistry.CreateCallCount()).To(Equal(1)) 2412 Expect(fakeContextRegistry.CreateArgsForCall(0)).To(Equal(txParams)) 2413 }) 2414 2415 It("sends an execute message to the chaincode with the correct proposal", func() { 2416 expectedMessage := *incomingMessage 2417 expectedMessage.Proposal = expectedSignedProp 2418 2419 close(responseNotifier) 2420 handler.Execute(txParams, "chaincode-name", incomingMessage, time.Second) 2421 2422 Eventually(fakeChatStream.SendCallCount).Should(Equal(1)) 2423 Consistently(fakeChatStream.SendCallCount).Should(Equal(1)) 2424 msg := fakeChatStream.SendArgsForCall(0) 2425 Expect(msg).To(Equal(&expectedMessage)) 2426 Expect(msg.Proposal).To(Equal(expectedSignedProp)) 2427 }) 2428 2429 It("waits for the chaincode to respond", func() { 2430 doneCh := make(chan struct{}) 2431 go func() { 2432 handler.Execute(txParams, "chaincode-name", incomingMessage, time.Second) 2433 close(doneCh) 2434 }() 2435 2436 Eventually(fakeChatStream.SendCallCount).Should(Equal(1)) 2437 Consistently(fakeChatStream.SendCallCount).Should(Equal(1)) 2438 2439 Consistently(doneCh).ShouldNot(Receive()) 2440 Eventually(responseNotifier).Should(BeSent(&pb.ChaincodeMessage{})) 2441 Eventually(doneCh).Should(BeClosed()) 2442 }) 2443 2444 It("returns the chaincode response", func() { 2445 Eventually(responseNotifier).Should(BeSent(&pb.ChaincodeMessage{Txid: "a-transaction-id"})) 2446 2447 resp, err := handler.Execute(txParams, "chaincode-name", incomingMessage, time.Second) 2448 Expect(err).NotTo(HaveOccurred()) 2449 Expect(resp).To(Equal(&pb.ChaincodeMessage{Txid: "a-transaction-id"})) 2450 }) 2451 2452 It("deletes the transaction context", func() { 2453 close(responseNotifier) 2454 handler.Execute(txParams, "chaincode-name", incomingMessage, time.Second) 2455 2456 Expect(fakeContextRegistry.DeleteCallCount()).Should(Equal(1)) 2457 channelID, txid := fakeContextRegistry.DeleteArgsForCall(0) 2458 Expect(channelID).To(Equal("channel-id")) 2459 Expect(txid).To(Equal("tx-id")) 2460 }) 2461 2462 Context("when the serial send fails", func() { 2463 BeforeEach(func() { 2464 fakeChatStream.SendReturns(errors.New("where-is-waldo?")) 2465 }) 2466 2467 It("returns an error before timing out", func() { 2468 respCh := make(chan *pb.ChaincodeMessage, 1) 2469 go func() { 2470 defer GinkgoRecover() 2471 resp, err := handler.Execute(txParams, "chaincode-name", incomingMessage, time.Second) 2472 Expect(err).NotTo(HaveOccurred()) 2473 Eventually(respCh).Should(BeSent(resp)) 2474 }() 2475 2476 Eventually(respCh).Should(Receive(Equal(&pb.ChaincodeMessage{ 2477 Type: pb.ChaincodeMessage_ERROR, 2478 Payload: []byte("[tx-id] error sending TRANSACTION: where-is-waldo?"), 2479 Txid: "tx-id", 2480 ChannelId: "channel-id", 2481 }))) 2482 }) 2483 }) 2484 2485 Context("when the proposal is missing", func() { 2486 BeforeEach(func() { 2487 txParams.Proposal = nil 2488 }) 2489 2490 It("sends a nil proposal", func() { 2491 close(responseNotifier) 2492 _, err := handler.Execute(txParams, "chaincode-name", incomingMessage, time.Second) 2493 Expect(err).NotTo(HaveOccurred()) 2494 2495 Eventually(fakeChatStream.SendCallCount).Should(Equal(1)) 2496 msg := fakeChatStream.SendArgsForCall(0) 2497 Expect(msg).NotTo(BeNil()) 2498 Expect(msg.Proposal).To(BeNil()) 2499 }) 2500 }) 2501 2502 Context("when the signed proposal is missing", func() { 2503 BeforeEach(func() { 2504 txParams.SignedProp = nil 2505 }) 2506 2507 It("returns an error", func() { 2508 close(responseNotifier) 2509 _, err := handler.Execute(txParams, "chaincode-name", incomingMessage, time.Second) 2510 2511 Expect(err).To(MatchError("failed getting proposal context. Signed proposal is nil")) 2512 }) 2513 2514 It("deletes the transaction context", func() { 2515 close(responseNotifier) 2516 handler.Execute(txParams, "chaincode-name", incomingMessage, time.Second) 2517 2518 Expect(fakeContextRegistry.DeleteCallCount()).Should(Equal(1)) 2519 channelID, txid := fakeContextRegistry.DeleteArgsForCall(0) 2520 Expect(channelID).To(Equal("channel-id")) 2521 Expect(txid).To(Equal("tx-id")) 2522 }) 2523 }) 2524 2525 Context("when creating the transaction context fails", func() { 2526 BeforeEach(func() { 2527 fakeContextRegistry.CreateReturns(nil, errors.New("burger")) 2528 }) 2529 2530 It("returns an error", func() { 2531 _, err := handler.Execute(txParams, "chaincode-name", incomingMessage, time.Second) 2532 Expect(err).To(MatchError("burger")) 2533 }) 2534 2535 It("does not try to delete the tranasction context", func() { 2536 handler.Execute(txParams, "chaincode-name", incomingMessage, time.Second) 2537 Expect(fakeContextRegistry.CreateCallCount()).To(Equal(1)) 2538 Expect(fakeContextRegistry.DeleteCallCount()).To(Equal(0)) 2539 }) 2540 }) 2541 2542 Context("when the chaincode stream terminates", func() { 2543 It("returns an error", func() { 2544 streamDoneChan := make(chan struct{}) 2545 chaincode.SetStreamDoneChan(handler, streamDoneChan) 2546 2547 errCh := make(chan error, 1) 2548 go func() { 2549 _, err := handler.Execute(txParams, "chaincode-name", incomingMessage, time.Hour) 2550 errCh <- err 2551 }() 2552 Consistently(errCh).ShouldNot(Receive()) 2553 2554 close(streamDoneChan) 2555 Eventually(errCh).Should(Receive(MatchError("chaincode stream terminated"))) 2556 }) 2557 }) 2558 2559 Context("when execute times out", func() { 2560 It("returns an error", func() { 2561 errCh := make(chan error, 1) 2562 go func() { 2563 _, err := handler.Execute(txParams, "chaincode-name", incomingMessage, time.Millisecond) 2564 errCh <- err 2565 }() 2566 Eventually(errCh).Should(Receive(MatchError("timeout expired while executing transaction"))) 2567 }) 2568 2569 It("records execute timeouts", func() { 2570 errCh := make(chan error, 1) 2571 go func() { 2572 _, err := handler.Execute(txParams, "chaincode-name", incomingMessage, time.Millisecond) 2573 errCh <- err 2574 }() 2575 Eventually(errCh).Should(Receive(MatchError("timeout expired while executing transaction"))) 2576 Expect(fakeExecuteTimeouts.WithCallCount()).To(Equal(1)) 2577 labelValues := fakeExecuteTimeouts.WithArgsForCall(0) 2578 Expect(labelValues).To(Equal([]string{ 2579 "chaincode", "test-handler-name:1.0", 2580 })) 2581 Expect(fakeExecuteTimeouts.AddCallCount()).To(Equal(1)) 2582 Expect(fakeExecuteTimeouts.AddArgsForCall(0)).To(BeNumerically("~", 1.0)) 2583 }) 2584 2585 It("deletes the transaction context", func() { 2586 handler.Execute(txParams, "chaincode-name", incomingMessage, time.Millisecond) 2587 2588 Expect(fakeContextRegistry.DeleteCallCount()).Should(Equal(1)) 2589 channelID, txid := fakeContextRegistry.DeleteArgsForCall(0) 2590 Expect(channelID).To(Equal("channel-id")) 2591 Expect(txid).To(Equal("tx-id")) 2592 }) 2593 }) 2594 }) 2595 2596 Describe("HandleRegister", func() { 2597 var incomingMessage *pb.ChaincodeMessage 2598 2599 BeforeEach(func() { 2600 request := &pb.ChaincodeID{ 2601 Name: "chaincode-id-name", 2602 Version: "chaincode-id-version", 2603 } 2604 payload, err := proto.Marshal(request) 2605 Expect(err).NotTo(HaveOccurred()) 2606 2607 incomingMessage = &pb.ChaincodeMessage{ 2608 Type: pb.ChaincodeMessage_REGISTER, 2609 Txid: "tx-id", 2610 ChannelId: "channel-id", 2611 Payload: payload, 2612 } 2613 }) 2614 2615 It("registers the handler with the registry", func() { 2616 handler.HandleRegister(incomingMessage) 2617 2618 Expect(fakeHandlerRegistry.RegisterCallCount()).To(Equal(1)) 2619 h := fakeHandlerRegistry.RegisterArgsForCall(0) 2620 Expect(h).To(Equal(handler)) 2621 }) 2622 2623 It("transitions the handler into ready state", func() { 2624 handler.HandleRegister(incomingMessage) 2625 Eventually(handler.State).Should(Equal(chaincode.Ready)) 2626 }) 2627 2628 It("notifies the registry that the handler is ready", func() { 2629 handler.HandleRegister(incomingMessage) 2630 Expect(fakeHandlerRegistry.FailedCallCount()).To(Equal(0)) 2631 Expect(fakeHandlerRegistry.ReadyCallCount()).To(Equal(1)) 2632 name := fakeHandlerRegistry.ReadyArgsForCall(0) 2633 Expect(name).To(Equal("chaincode-id-name")) 2634 }) 2635 2636 It("sends registered and ready messsages", func() { 2637 handler.HandleRegister(incomingMessage) 2638 2639 Eventually(fakeChatStream.SendCallCount).Should(Equal(2)) 2640 Consistently(fakeChatStream.SendCallCount).Should(Equal(2)) 2641 registeredMessage := fakeChatStream.SendArgsForCall(0) 2642 readyMessage := fakeChatStream.SendArgsForCall(1) 2643 2644 Expect(registeredMessage).To(Equal(&pb.ChaincodeMessage{ 2645 Type: pb.ChaincodeMessage_REGISTERED, 2646 })) 2647 2648 Expect(readyMessage).To(Equal(&pb.ChaincodeMessage{ 2649 Type: pb.ChaincodeMessage_READY, 2650 })) 2651 }) 2652 2653 Context("when sending the ready message fails", func() { 2654 BeforeEach(func() { 2655 fakeChatStream.SendReturnsOnCall(1, errors.New("carrot")) 2656 }) 2657 2658 It("state remains in established", func() { 2659 Expect(handler.State()).To(Equal(chaincode.Created)) 2660 handler.HandleRegister(incomingMessage) 2661 Expect(handler.State()).To(Equal(chaincode.Established)) 2662 }) 2663 2664 It("notifies the registry of the failure", func() { 2665 handler.HandleRegister(incomingMessage) 2666 Expect(fakeHandlerRegistry.ReadyCallCount()).To(Equal(0)) 2667 Expect(fakeHandlerRegistry.FailedCallCount()).To(Equal(1)) 2668 name, err := fakeHandlerRegistry.FailedArgsForCall(0) 2669 Expect(name).To(Equal("chaincode-id-name")) 2670 Expect(err).To(MatchError("[] error sending READY: carrot")) 2671 }) 2672 }) 2673 2674 Context("when registering the handler with registry fails", func() { 2675 BeforeEach(func() { 2676 fakeHandlerRegistry.RegisterReturns(errors.New("cake")) 2677 }) 2678 2679 It("remains in created state", func() { 2680 Expect(handler.State()).To(Equal(chaincode.Created)) 2681 handler.HandleRegister(incomingMessage) 2682 Expect(handler.State()).To(Equal(chaincode.Created)) 2683 }) 2684 }) 2685 2686 Context("when sending a registered message failed", func() { 2687 BeforeEach(func() { 2688 fakeChatStream.SendReturns(errors.New("potato")) 2689 }) 2690 2691 It("ready is not sent", func() { 2692 handler.HandleRegister(incomingMessage) 2693 2694 Eventually(fakeChatStream.SendCallCount).Should(Equal(1)) 2695 Consistently(fakeChatStream.SendCallCount).Should(Equal(1)) 2696 msg := fakeChatStream.SendArgsForCall(0) 2697 Expect(msg.Type).To(Equal(pb.ChaincodeMessage_REGISTERED)) 2698 }) 2699 2700 It("remains in created state", func() { 2701 Expect(handler.State()).To(Equal(chaincode.Created)) 2702 handler.HandleRegister(incomingMessage) 2703 Expect(handler.State()).To(Equal(chaincode.Created)) 2704 }) 2705 }) 2706 2707 Context("when unmarshaling the request fails", func() { 2708 BeforeEach(func() { 2709 incomingMessage.Payload = []byte("this-is-a-bogus-payload") 2710 }) 2711 2712 It("sends no messages", func() { 2713 handler.HandleRegister(incomingMessage) 2714 Consistently(fakeChatStream.SendCallCount).Should(Equal(0)) 2715 }) 2716 }) 2717 }) 2718 2719 Describe("ProcessStream", func() { 2720 BeforeEach(func() { 2721 incomingMessage := &pb.ChaincodeMessage{ 2722 Type: pb.ChaincodeMessage_KEEPALIVE, 2723 Txid: "tx-id", 2724 ChannelId: "channel-id", 2725 } 2726 fakeChatStream.RecvReturns(incomingMessage, nil) 2727 }) 2728 2729 It("receives messages until an error is received", func() { 2730 fakeChatStream.RecvReturnsOnCall(99, nil, errors.New("done-for-now")) 2731 handler.ProcessStream(fakeChatStream) 2732 2733 Eventually(fakeChatStream.RecvCallCount).Should(Equal(100)) 2734 }) 2735 2736 It("manages the stream done channel", func() { 2737 releaseChan := make(chan struct{}) 2738 fakeChatStream.RecvStub = func() (*pb.ChaincodeMessage, error) { 2739 <-releaseChan 2740 return nil, errors.New("cc-went-away") 2741 } 2742 go handler.ProcessStream(fakeChatStream) 2743 Eventually(fakeChatStream.RecvCallCount).Should(Equal(1)) 2744 2745 streamDoneChan := chaincode.StreamDone(handler) 2746 Consistently(streamDoneChan).ShouldNot(Receive()) 2747 2748 close(releaseChan) 2749 Eventually(streamDoneChan).Should(BeClosed()) 2750 }) 2751 2752 Context("when receive fails with an io.EOF", func() { 2753 BeforeEach(func() { 2754 fakeChatStream.RecvReturns(nil, io.EOF) 2755 }) 2756 2757 It("returns the error", func() { 2758 err := handler.ProcessStream(fakeChatStream) 2759 Expect(err).To(Equal(io.EOF)) 2760 }) 2761 }) 2762 2763 Context("when receive fails", func() { 2764 BeforeEach(func() { 2765 fakeChatStream.RecvReturns(nil, errors.New("chocolate")) 2766 }) 2767 2768 It("returns an error", func() { 2769 err := handler.ProcessStream(fakeChatStream) 2770 Expect(err).To(MatchError("receive from chaincode support stream failed: chocolate")) 2771 }) 2772 }) 2773 2774 Context("when a nil message is received", func() { 2775 BeforeEach(func() { 2776 fakeChatStream.RecvReturns(nil, nil) 2777 }) 2778 2779 It("returns an error", func() { 2780 err := handler.ProcessStream(fakeChatStream) 2781 Expect(err).To(MatchError("received nil message, ending chaincode support stream")) 2782 }) 2783 }) 2784 2785 Describe("keepalive messages", func() { 2786 var recvChan chan *pb.ChaincodeMessage 2787 2788 BeforeEach(func() { 2789 recvChan = make(chan *pb.ChaincodeMessage, 1) 2790 fakeChatStream.RecvStub = func() (*pb.ChaincodeMessage, error) { 2791 msg := <-recvChan 2792 return msg, nil 2793 } 2794 2795 handler.Keepalive = 50 * time.Millisecond 2796 }) 2797 2798 It("sends a keep alive messages until the stream ends", func() { 2799 errChan := make(chan error, 1) 2800 go func() { errChan <- handler.ProcessStream(fakeChatStream) }() 2801 2802 Eventually(fakeChatStream.SendCallCount).Should(Equal(5)) 2803 recvChan <- nil 2804 Eventually(errChan).Should(Receive()) 2805 2806 for i := 0; i < 5; i++ { 2807 m := fakeChatStream.SendArgsForCall(i) 2808 Expect(m.Type).To(Equal(pb.ChaincodeMessage_KEEPALIVE)) 2809 } 2810 }) 2811 2812 Context("when keepalive is disabled", func() { 2813 BeforeEach(func() { 2814 handler.Keepalive = 0 2815 }) 2816 2817 It("does not send keep alive messages", func() { 2818 errChan := make(chan error, 1) 2819 go func() { errChan <- handler.ProcessStream(fakeChatStream) }() 2820 2821 Consistently(fakeChatStream.SendCallCount).Should(Equal(0)) 2822 recvChan <- nil 2823 Eventually(errChan).Should(Receive()) 2824 }) 2825 }) 2826 }) 2827 2828 Context("when handling a received message fails", func() { 2829 var recvChan chan *pb.ChaincodeMessage 2830 2831 BeforeEach(func() { 2832 recvChan = make(chan *pb.ChaincodeMessage, 1) 2833 fakeChatStream.RecvStub = func() (*pb.ChaincodeMessage, error) { 2834 msg := <-recvChan 2835 return msg, nil 2836 } 2837 2838 recvChan <- &pb.ChaincodeMessage{ 2839 Txid: "tx-id", 2840 Type: pb.ChaincodeMessage_Type(9999), 2841 } 2842 }) 2843 2844 It("returns an error", func() { 2845 err := handler.ProcessStream(fakeChatStream) 2846 Expect(err).To(MatchError("error handling message, ending stream: [tx-id] Fabric side handler cannot handle message (9999) while in created state")) 2847 }) 2848 }) 2849 2850 Context("when an async error is sent", func() { 2851 var ( 2852 incomingMessage *pb.ChaincodeMessage 2853 recvChan chan *pb.ChaincodeMessage 2854 ) 2855 2856 BeforeEach(func() { 2857 request := &pb.ChaincodeInput{} 2858 payload, err := proto.Marshal(request) 2859 Expect(err).NotTo(HaveOccurred()) 2860 2861 incomingMessage = &pb.ChaincodeMessage{ 2862 Type: pb.ChaincodeMessage_TRANSACTION, 2863 Txid: "tx-id", 2864 Payload: payload, 2865 ChannelId: "channel-id", 2866 } 2867 2868 recvChan = make(chan *pb.ChaincodeMessage, 1) 2869 shadowRecvChan := recvChan 2870 fakeChatStream.RecvStub = func() (*pb.ChaincodeMessage, error) { 2871 msg := <-shadowRecvChan 2872 return msg, nil 2873 } 2874 fakeChatStream.SendReturns(errors.New("candy")) 2875 }) 2876 2877 AfterEach(func() { 2878 close(recvChan) 2879 }) 2880 2881 It("returns an error", func() { 2882 errChan := make(chan error, 1) 2883 go func() { errChan <- handler.ProcessStream(fakeChatStream) }() 2884 Eventually(fakeChatStream.RecvCallCount).ShouldNot(Equal(0)) // wait for loop to start 2885 handler.Execute(&ccprovider.TransactionParams{}, "chaincode-name", incomingMessage, time.Millisecond) // force async error 2886 2887 Eventually(errChan).Should(Receive(MatchError("received error while sending message, ending chaincode support stream: [tx-id] error sending TRANSACTION: candy"))) 2888 }) 2889 2890 It("stops receiving messages", func() { 2891 errChan := make(chan error, 1) 2892 go func() { errChan <- handler.ProcessStream(fakeChatStream) }() 2893 Eventually(fakeChatStream.RecvCallCount).ShouldNot(Equal(0)) // wait for loop to start 2894 handler.Execute(&ccprovider.TransactionParams{}, "chaincode-name", incomingMessage, time.Millisecond) // force async error 2895 2896 Eventually(fakeChatStream.RecvCallCount).Should(Equal(1)) 2897 Consistently(fakeChatStream.RecvCallCount).Should(Equal(1)) 2898 }) 2899 }) 2900 }) 2901 2902 Describe("Notify", func() { 2903 var fakeIterator *mock.QueryResultsIterator 2904 var incomingMessage *pb.ChaincodeMessage 2905 2906 BeforeEach(func() { 2907 fakeIterator = &mock.QueryResultsIterator{} 2908 incomingMessage = &pb.ChaincodeMessage{ 2909 Txid: "tx-id", 2910 ChannelId: "channel-id", 2911 } 2912 }) 2913 2914 It("gets the transaction context", func() { 2915 handler.Notify(incomingMessage) 2916 2917 Expect(fakeContextRegistry.GetCallCount()).To(Equal(1)) 2918 channelID, txID := fakeContextRegistry.GetArgsForCall(0) 2919 Expect(channelID).To(Equal("channel-id")) 2920 Expect(txID).To(Equal("tx-id")) 2921 }) 2922 2923 It("sends the message on response notifier", func() { 2924 handler.Notify(incomingMessage) 2925 2926 Eventually(responseNotifier).Should(Receive(Equal(incomingMessage))) 2927 }) 2928 2929 It("should close query iterators on the transaction context", func() { 2930 txContext.InitializeQueryContext("query-id", fakeIterator) 2931 Expect(fakeIterator.CloseCallCount()).To(Equal(0)) 2932 2933 handler.Notify(incomingMessage) 2934 Eventually(fakeIterator.CloseCallCount).Should(Equal(1)) 2935 }) 2936 2937 Context("when the transaction context cannot be found", func() { 2938 BeforeEach(func() { 2939 fakeContextRegistry.GetReturns(nil) 2940 }) 2941 2942 It("keeps calm and carries on", func() { 2943 handler.Notify(incomingMessage) 2944 Expect(fakeContextRegistry.GetCallCount()).To(Equal(1)) 2945 }) 2946 }) 2947 }) 2948 2949 Describe("ParseName", func() { 2950 It("parses the chaincode name", func() { 2951 ci := chaincode.ParseName("name") 2952 Expect(ci).To(Equal(&sysccprovider.ChaincodeInstance{ChaincodeName: "name"})) 2953 ci = chaincode.ParseName("name:version") 2954 Expect(ci).To(Equal(&sysccprovider.ChaincodeInstance{ChaincodeName: "name", ChaincodeVersion: "version"})) 2955 ci = chaincode.ParseName("name/channel-id") 2956 Expect(ci).To(Equal(&sysccprovider.ChaincodeInstance{ChaincodeName: "name", ChannelID: "channel-id"})) 2957 ci = chaincode.ParseName("name:version/channel-id") 2958 Expect(ci).To(Equal(&sysccprovider.ChaincodeInstance{ChaincodeName: "name", ChaincodeVersion: "version", ChannelID: "channel-id"})) 2959 }) 2960 }) 2961 2962 DescribeTable("Handler State", 2963 func(state chaincode.State, strval string) { 2964 Expect(state.String()).To(Equal(strval)) 2965 }, 2966 Entry("created", chaincode.Created, "created"), 2967 Entry("ready", chaincode.Ready, "ready"), 2968 Entry("established", chaincode.Established, "established"), 2969 Entry("unknown", chaincode.State(999), "UNKNOWN"), 2970 ) 2971 })