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 `