github.com/hyperledger-labs/bdls@v2.1.1+incompatible/discovery/cmd/endorsers_test.go (about)

     1  /*
     2  Copyright IBM Corp. All Rights Reserved.
     3  
     4  SPDX-License-Identifier: Apache-2.0
     5  */
     6  
     7  package discovery_test
     8  
     9  import (
    10  	"bytes"
    11  	"testing"
    12  
    13  	discprotos "github.com/hyperledger/fabric-protos-go/discovery"
    14  	"github.com/hyperledger/fabric-protos-go/msp"
    15  	"github.com/hyperledger/fabric/cmd/common"
    16  	. "github.com/hyperledger/fabric/discovery/client"
    17  	discovery "github.com/hyperledger/fabric/discovery/cmd"
    18  	"github.com/hyperledger/fabric/discovery/cmd/mocks"
    19  	"github.com/hyperledger/fabric/protoutil"
    20  	"github.com/pkg/errors"
    21  	"github.com/stretchr/testify/assert"
    22  	"github.com/stretchr/testify/mock"
    23  )
    24  
    25  func TestEndorserCmd(t *testing.T) {
    26  	server := "peer0"
    27  	channel := "mychannel"
    28  	stub := &mocks.Stub{}
    29  	parser := &mocks.ResponseParser{}
    30  
    31  	t.Run("no server supplied", func(t *testing.T) {
    32  		cmd := discovery.NewEndorsersCmd(stub, parser)
    33  		cmd.SetChannel(&channel)
    34  
    35  		err := cmd.Execute(common.Config{})
    36  		assert.Equal(t, err.Error(), "no server specified")
    37  	})
    38  
    39  	t.Run("no channel supplied", func(t *testing.T) {
    40  		cmd := discovery.NewEndorsersCmd(stub, parser)
    41  		cmd.SetServer(&server)
    42  
    43  		err := cmd.Execute(common.Config{})
    44  		assert.Equal(t, err.Error(), "no channel specified")
    45  	})
    46  
    47  	t.Run("Endorsement query with no chaincodes", func(t *testing.T) {
    48  		cmd := discovery.NewEndorsersCmd(stub, parser)
    49  		cmd.SetServer(&server)
    50  		cmd.SetChannel(&channel)
    51  
    52  		err := cmd.Execute(common.Config{})
    53  		assert.Contains(t, err.Error(), "invocation chain should not be empty")
    54  	})
    55  
    56  	t.Run("Server return error", func(t *testing.T) {
    57  		chaincodes := []string{"mycc"}
    58  		cmd := discovery.NewEndorsersCmd(stub, parser)
    59  		cmd.SetChannel(&channel)
    60  		cmd.SetServer(&server)
    61  		cmd.SetChaincodes(&chaincodes)
    62  		stub.On("Send", server, mock.Anything, mock.Anything).Return(nil, errors.New("deadline exceeded")).Once()
    63  
    64  		err := cmd.Execute(common.Config{})
    65  		assert.Contains(t, err.Error(), "deadline exceeded")
    66  	})
    67  
    68  	t.Run("Endorsement query with no collections succeeds", func(t *testing.T) {
    69  		chaincodes := []string{"mycc"}
    70  		cmd := discovery.NewEndorsersCmd(stub, parser)
    71  		cmd.SetChannel(&channel)
    72  		cmd.SetServer(&server)
    73  		cmd.SetChaincodes(&chaincodes)
    74  		parser.On("ParseResponse", channel, mock.Anything).Return(nil).Once()
    75  		stub.On("Send", server, mock.Anything, mock.Anything).Return(nil, nil).Once()
    76  
    77  		err := cmd.Execute(common.Config{})
    78  		assert.NoError(t, err)
    79  	})
    80  
    81  	t.Run("Endorsement query with collections succeeds", func(t *testing.T) {
    82  		chaincodes := []string{"mycc", "yourcc"}
    83  		collections := map[string]string{
    84  			"mycc": "col1,col2",
    85  		}
    86  
    87  		stub := &mocks.Stub{}
    88  		cmd := discovery.NewEndorsersCmd(stub, parser)
    89  		cmd.SetChannel(&channel)
    90  		cmd.SetServer(&server)
    91  		cmd.SetChaincodes(&chaincodes)
    92  		cmd.SetCollections(&collections)
    93  		parser.On("ParseResponse", channel, mock.Anything).Return(nil).Once()
    94  		stub.On("Send", server, mock.Anything, mock.Anything).Return(nil, nil).Once().Run(func(arg mock.Arguments) {
    95  			// Ensure the stub got the request that corresponds to what the CLI passed in
    96  			req := arg.Get(2).(*Request)
    97  			// Ensure chaincode names in the invocation chain match
    98  			assert.Equal(t, "mycc", req.Queries[0].GetCcQuery().Interests[0].Chaincodes[0].Name)
    99  			// Ensure collection names in the invocation chain match
   100  			assert.Equal(t, []string{"col1", "col2"}, req.Queries[0].GetCcQuery().Interests[0].Chaincodes[0].CollectionNames)
   101  		})
   102  
   103  		err := cmd.Execute(common.Config{})
   104  		assert.NoError(t, err)
   105  		stub.AssertNumberOfCalls(t, "Send", 1)
   106  	})
   107  
   108  	t.Run("Endorsement query with collections that aren't mapped to any chaincode(s)", func(t *testing.T) {
   109  		chaincodes := []string{"mycc", "yourcc"}
   110  		collections := map[string]string{
   111  			"mycc":  "col1,col2",
   112  			"ourcc": "col3",
   113  		}
   114  
   115  		stub := &mocks.Stub{}
   116  		cmd := discovery.NewEndorsersCmd(stub, parser)
   117  		cmd.SetChannel(&channel)
   118  		cmd.SetServer(&server)
   119  		cmd.SetChaincodes(&chaincodes)
   120  		cmd.SetCollections(&collections)
   121  		stub.On("Send", server, mock.Anything, mock.Anything).Return(nil, nil).Once()
   122  
   123  		err := cmd.Execute(common.Config{})
   124  		assert.Contains(t, err.Error(), "a collection specified chaincode ourcc but it wasn't specified with a chaincode flag")
   125  	})
   126  
   127  	t.Run("Endorsement query with collections that aren't mapped to any chaincode(s)", func(t *testing.T) {
   128  		chaincodes := []string{"mycc", "yourcc"}
   129  		collections := map[string]string{
   130  			"mycc":  "col1,col2",
   131  			"ourcc": "col3",
   132  		}
   133  
   134  		stub := &mocks.Stub{}
   135  		cmd := discovery.NewEndorsersCmd(stub, parser)
   136  		cmd.SetChannel(&channel)
   137  		cmd.SetServer(&server)
   138  		cmd.SetChaincodes(&chaincodes)
   139  		cmd.SetCollections(&collections)
   140  		stub.On("Send", server, mock.Anything, mock.Anything).Return(nil, nil).Once()
   141  
   142  		err := cmd.Execute(common.Config{})
   143  		assert.Contains(t, err.Error(), "a collection specified chaincode ourcc but it wasn't specified with a chaincode flag")
   144  	})
   145  
   146  	t.Run("Endorsement query with noPrivateReads that aren't mapped to any chaincode(s)", func(t *testing.T) {
   147  		chaincodes := []string{"mycc"}
   148  		noPrivateReads := []string{"yourcc"}
   149  
   150  		stub := &mocks.Stub{}
   151  		cmd := discovery.NewEndorsersCmd(stub, parser)
   152  		cmd.SetChannel(&channel)
   153  		cmd.SetServer(&server)
   154  		cmd.SetChaincodes(&chaincodes)
   155  		cmd.SetNoPrivateReads(&noPrivateReads)
   156  		stub.On("Send", server, mock.Anything, mock.Anything).Return(nil, nil).Once()
   157  
   158  		err := cmd.Execute(common.Config{})
   159  		assert.Contains(t, err.Error(), "chaincode yourcc is specified as not containing private data reads but should be explicitly defined via a chaincode flag")
   160  	})
   161  }
   162  
   163  func TestParseEndorsementResponse(t *testing.T) {
   164  	buff := &bytes.Buffer{}
   165  	parser := &discovery.EndorserResponseParser{Writer: buff}
   166  	res := &mocks.ServiceResponse{}
   167  
   168  	t.Run("Server returns empty response", func(t *testing.T) {
   169  		defer buff.Reset()
   170  		res.On("Raw").Return(&discprotos.Response{}).Once()
   171  		err := parser.ParseResponse("mychannel", res)
   172  		assert.Contains(t, err.Error(), "empty results")
   173  	})
   174  
   175  	t.Run("Server returns an error", func(t *testing.T) {
   176  		defer buff.Reset()
   177  		res.On("Raw").Return(&discprotos.Response{
   178  			Results: []*discprotos.QueryResult{
   179  				{
   180  					Result: &discprotos.QueryResult_Error{
   181  						Error: &discprotos.Error{
   182  							Content: "internal error",
   183  						},
   184  					},
   185  				},
   186  			},
   187  		}).Once()
   188  		err := parser.ParseResponse("mychannel", res)
   189  		assert.Contains(t, err.Error(), "internal error")
   190  	})
   191  
   192  	t.Run("Server returns a response with the wrong type", func(t *testing.T) {
   193  		defer buff.Reset()
   194  		res.On("Raw").Return(&discprotos.Response{
   195  			Results: []*discprotos.QueryResult{
   196  				{
   197  					Result: &discprotos.QueryResult_Members{
   198  						Members: &discprotos.PeerMembershipResult{PeersByOrg: map[string]*discprotos.Peers{
   199  							"Org1MSP": {},
   200  						}},
   201  					},
   202  				},
   203  			},
   204  		}).Once()
   205  		err := parser.ParseResponse("mychannel", res)
   206  		assert.Contains(t, err.Error(), "server returned response of unexpected type: *discovery.QueryResult")
   207  	})
   208  
   209  	t.Run("Server returns a proper response", func(t *testing.T) {
   210  		defer buff.Reset()
   211  		res.On("Raw").Return(&discprotos.Response{
   212  			Results: []*discprotos.QueryResult{
   213  				{
   214  					Result: endorsersResponse,
   215  				},
   216  			},
   217  		}).Once()
   218  		err := parser.ParseResponse("mychannel", res)
   219  		assert.NoError(t, err)
   220  		assert.Equal(t, expectedEndorsersOutput, buff.String())
   221  	})
   222  }
   223  
   224  var endorsersResponse = &discprotos.QueryResult_CcQueryRes{
   225  	CcQueryRes: &discprotos.ChaincodeQueryResult{
   226  		Content: []*discprotos.EndorsementDescriptor{
   227  			{
   228  				Chaincode: "mycc",
   229  				EndorsersByGroups: map[string]*discprotos.Peers{
   230  					"Org1MSP": {
   231  						Peers: []*discprotos.Peer{
   232  							{
   233  								Identity: protoutil.MarshalOrPanic(&msp.SerializedIdentity{
   234  									Mspid:   "Org1MSP",
   235  									IdBytes: []byte("identity"),
   236  								}),
   237  								StateInfo:      stateInfoMessage(100).Envelope,
   238  								MembershipInfo: aliveMessage(0).Envelope,
   239  							},
   240  						},
   241  					},
   242  				},
   243  				Layouts: []*discprotos.Layout{
   244  					{
   245  						QuantitiesByGroup: map[string]uint32{
   246  							"Org1MSP": 2,
   247  						},
   248  					},
   249  				},
   250  			},
   251  		},
   252  	},
   253  }
   254  
   255  const expectedEndorsersOutput = `[
   256  	{
   257  		"Chaincode": "mycc",
   258  		"EndorsersByGroups": {
   259  			"Org1MSP": [
   260  				{
   261  					"MSPID": "Org1MSP",
   262  					"LedgerHeight": 100,
   263  					"Endpoint": "p0",
   264  					"Identity": "identity"
   265  				}
   266  			]
   267  		},
   268  		"Layouts": [
   269  			{
   270  				"quantities_by_group": {
   271  					"Org1MSP": 2
   272  				}
   273  			}
   274  		]
   275  	}
   276  ]
   277  `