github.com/zhouxv/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  })