github.com/osdi23p228/fabric@v0.0.0-20221218062954-77808885f5db/internal/peer/lifecycle/chaincode/querycommitted_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 "encoding/json" 11 "fmt" 12 13 "github.com/golang/protobuf/proto" 14 pb "github.com/hyperledger/fabric-protos-go/peer" 15 lb "github.com/hyperledger/fabric-protos-go/peer/lifecycle" 16 "github.com/osdi23p228/fabric/bccsp/sw" 17 "github.com/osdi23p228/fabric/internal/peer/lifecycle/chaincode" 18 "github.com/osdi23p228/fabric/internal/peer/lifecycle/chaincode/mock" 19 "github.com/pkg/errors" 20 "github.com/spf13/cobra" 21 22 . "github.com/onsi/ginkgo" 23 . "github.com/onsi/gomega" 24 "github.com/onsi/gomega/gbytes" 25 ) 26 27 var _ = Describe("QueryCommitted", func() { 28 Describe("CommittedQuerier", func() { 29 var ( 30 mockProposalResponse *pb.ProposalResponse 31 mockEndorserClient *mock.EndorserClient 32 mockSigner *mock.Signer 33 input *chaincode.CommittedQueryInput 34 committedQuerier *chaincode.CommittedQuerier 35 ) 36 37 BeforeEach(func() { 38 mockResult := &lb.QueryChaincodeDefinitionsResult{ 39 ChaincodeDefinitions: []*lb.QueryChaincodeDefinitionsResult_ChaincodeDefinition{ 40 { 41 Name: "woohoo", 42 Sequence: 93, 43 Version: "a-version", 44 EndorsementPlugin: "e-plugin", 45 ValidationPlugin: "v-plugin", 46 }, 47 { 48 Name: "yahoo", 49 Sequence: 20, 50 Version: "another-version", 51 EndorsementPlugin: "e-plugin", 52 ValidationPlugin: "v-plugin", 53 }, 54 }, 55 } 56 57 mockResultBytes, err := proto.Marshal(mockResult) 58 Expect(err).NotTo(HaveOccurred()) 59 Expect(mockResultBytes).NotTo(BeNil()) 60 mockProposalResponse = &pb.ProposalResponse{ 61 Response: &pb.Response{ 62 Status: 200, 63 Payload: mockResultBytes, 64 }, 65 } 66 67 mockEndorserClient = &mock.EndorserClient{} 68 mockEndorserClient.ProcessProposalReturns(mockProposalResponse, nil) 69 70 mockSigner = &mock.Signer{} 71 buffer := gbytes.NewBuffer() 72 73 input = &chaincode.CommittedQueryInput{ 74 ChannelID: "test-channel", 75 } 76 77 committedQuerier = &chaincode.CommittedQuerier{ 78 Input: input, 79 EndorserClient: mockEndorserClient, 80 Signer: mockSigner, 81 Writer: buffer, 82 } 83 }) 84 85 It("queries committed chaincodes and writes the output as human readable plain-text", func() { 86 err := committedQuerier.Query() 87 Expect(err).NotTo(HaveOccurred()) 88 Eventually(committedQuerier.Writer).Should(gbytes.Say("Committed chaincode definitions on channel 'test-channel':\n")) 89 Eventually(committedQuerier.Writer).Should(gbytes.Say("Name: woohoo, Version: a-version, Sequence: 93, Endorsement Plugin: e-plugin, Validation Plugin: v-plugin\n")) 90 Eventually(committedQuerier.Writer).Should(gbytes.Say("Name: yahoo, Version: another-version, Sequence: 20, Endorsement Plugin: e-plugin, Validation Plugin: v-plugin\n")) 91 }) 92 93 Context("when JSON-formatted output is requested", func() { 94 BeforeEach(func() { 95 committedQuerier.Input.OutputFormat = "json" 96 }) 97 98 It("queries committed chaincodes and writes the output as JSON", func() { 99 err := committedQuerier.Query() 100 Expect(err).NotTo(HaveOccurred()) 101 expectedOutput := &lb.QueryChaincodeDefinitionsResult{ 102 ChaincodeDefinitions: []*lb.QueryChaincodeDefinitionsResult_ChaincodeDefinition{ 103 { 104 Name: "woohoo", 105 Sequence: 93, 106 Version: "a-version", 107 EndorsementPlugin: "e-plugin", 108 ValidationPlugin: "v-plugin", 109 }, 110 { 111 Name: "yahoo", 112 Sequence: 20, 113 Version: "another-version", 114 EndorsementPlugin: "e-plugin", 115 ValidationPlugin: "v-plugin", 116 }, 117 }, 118 } 119 json, err := json.MarshalIndent(expectedOutput, "", "\t") 120 Expect(err).NotTo(HaveOccurred()) 121 Eventually(committedQuerier.Writer).Should(gbytes.Say(fmt.Sprintf(`\Q%s\E`, string(json)))) 122 }) 123 }) 124 125 Context("when a single chaincode definition is requested", func() { 126 BeforeEach(func() { 127 input.Name = "test-cc" 128 129 mockResult := &lb.QueryChaincodeDefinitionResult{ 130 Sequence: 93, 131 Version: "a-version", 132 EndorsementPlugin: "e-plugin", 133 ValidationPlugin: "v-plugin", 134 Approvals: map[string]bool{ 135 "whatkindoforgisthis": true, 136 "nowaydoiapprove": false, 137 }, 138 } 139 140 mockResultBytes, err := proto.Marshal(mockResult) 141 Expect(err).NotTo(HaveOccurred()) 142 Expect(mockResultBytes).NotTo(BeNil()) 143 mockProposalResponse = &pb.ProposalResponse{ 144 Response: &pb.Response{ 145 Status: 200, 146 Payload: mockResultBytes, 147 }, 148 } 149 150 mockEndorserClient.ProcessProposalReturns(mockProposalResponse, nil) 151 }) 152 153 It("queries the committed chaincode and writes the output as human readable plain-text", func() { 154 err := committedQuerier.Query() 155 Expect(err).NotTo(HaveOccurred()) 156 Eventually(committedQuerier.Writer).Should(gbytes.Say("Committed chaincode definition for chaincode 'test-cc' on channel 'test-channel'")) 157 Eventually(committedQuerier.Writer).Should(gbytes.Say(`\QVersion: a-version, Sequence: 93, Endorsement Plugin: e-plugin, Validation Plugin: v-plugin, Approvals: [nowaydoiapprove: false, whatkindoforgisthis: true]\E`)) 158 }) 159 160 Context("when the payload contains bytes that aren't a QueryChaincodeDefinitionResult", func() { 161 BeforeEach(func() { 162 mockProposalResponse.Response = &pb.Response{ 163 Payload: []byte("badpayloadbadpayload"), 164 Status: 200, 165 } 166 mockEndorserClient.ProcessProposalReturns(mockProposalResponse, nil) 167 }) 168 169 It("returns an error", func() { 170 err := committedQuerier.Query() 171 Expect(err).To(MatchError(ContainSubstring("failed to unmarshal proposal response's response payload"))) 172 }) 173 }) 174 175 Context("when JSON-formatted output is requested", func() { 176 BeforeEach(func() { 177 committedQuerier.Input.OutputFormat = "json" 178 }) 179 180 It("queries the committed chaincodes and writes the output as JSON", func() { 181 err := committedQuerier.Query() 182 Expect(err).NotTo(HaveOccurred()) 183 expectedOutput := &lb.QueryChaincodeDefinitionResult{ 184 Sequence: 93, 185 Version: "a-version", 186 EndorsementPlugin: "e-plugin", 187 ValidationPlugin: "v-plugin", 188 Approvals: map[string]bool{ 189 "whatkindoforgisthis": true, 190 "nowaydoiapprove": false, 191 }, 192 } 193 json, err := json.MarshalIndent(expectedOutput, "", "\t") 194 Expect(err).NotTo(HaveOccurred()) 195 Eventually(committedQuerier.Writer).Should(gbytes.Say(fmt.Sprintf(`\Q%s\E`, string(json)))) 196 }) 197 }) 198 }) 199 200 Context("when the channel is not provided", func() { 201 BeforeEach(func() { 202 committedQuerier.Input.ChannelID = "" 203 }) 204 205 It("returns an error", func() { 206 err := committedQuerier.Query() 207 Expect(err).To(MatchError("channel name must be specified")) 208 }) 209 }) 210 211 Context("when the signer cannot be serialized", func() { 212 BeforeEach(func() { 213 mockSigner.SerializeReturns(nil, errors.New("cafe")) 214 }) 215 216 It("returns an error", func() { 217 err := committedQuerier.Query() 218 Expect(err).To(MatchError("failed to create proposal: failed to serialize identity: cafe")) 219 }) 220 }) 221 222 Context("when the signer fails to sign the proposal", func() { 223 BeforeEach(func() { 224 mockSigner.SignReturns(nil, errors.New("tea")) 225 }) 226 227 It("returns an error", func() { 228 err := committedQuerier.Query() 229 Expect(err).To(MatchError("failed to create signed proposal: tea")) 230 }) 231 }) 232 233 Context("when the endorser fails to endorse the proposal", func() { 234 BeforeEach(func() { 235 mockEndorserClient.ProcessProposalReturns(nil, errors.New("latte")) 236 }) 237 238 It("returns an error", func() { 239 err := committedQuerier.Query() 240 Expect(err).To(MatchError("failed to endorse proposal: latte")) 241 }) 242 }) 243 244 Context("when the endorser returns a nil proposal response", func() { 245 BeforeEach(func() { 246 mockProposalResponse = nil 247 mockEndorserClient.ProcessProposalReturns(mockProposalResponse, nil) 248 }) 249 250 It("returns an error", func() { 251 err := committedQuerier.Query() 252 Expect(err).To(MatchError("received nil proposal response")) 253 }) 254 }) 255 256 Context("when the endorser returns a proposal response with a nil response", func() { 257 BeforeEach(func() { 258 mockProposalResponse.Response = nil 259 mockEndorserClient.ProcessProposalReturns(mockProposalResponse, nil) 260 }) 261 262 It("returns an error", func() { 263 err := committedQuerier.Query() 264 Expect(err).To(MatchError("received proposal response with nil response")) 265 }) 266 }) 267 268 Context("when the endorser returns a non-success status", func() { 269 BeforeEach(func() { 270 mockProposalResponse.Response = &pb.Response{ 271 Status: 500, 272 Message: "capuccino", 273 } 274 mockEndorserClient.ProcessProposalReturns(mockProposalResponse, nil) 275 }) 276 277 It("returns an error", func() { 278 err := committedQuerier.Query() 279 Expect(err).To(MatchError("query failed with status: 500 - capuccino")) 280 }) 281 }) 282 283 Context("when the payload contains bytes that aren't a QueryChaincodeDefinitionsResult", func() { 284 BeforeEach(func() { 285 mockProposalResponse.Response = &pb.Response{ 286 Payload: []byte("badpayloadbadpayload"), 287 Status: 200, 288 } 289 mockEndorserClient.ProcessProposalReturns(mockProposalResponse, nil) 290 }) 291 292 It("returns an error", func() { 293 err := committedQuerier.Query() 294 Expect(err).To(MatchError(ContainSubstring("failed to unmarshal proposal response's response payload"))) 295 }) 296 }) 297 298 Context("when the payload contains bytes that aren't a QueryChaincodeDefinitionsResult and JSON-output is requested", func() { 299 BeforeEach(func() { 300 mockProposalResponse.Response = &pb.Response{ 301 Payload: []byte("badpayloadbadpayload"), 302 Status: 200, 303 } 304 mockEndorserClient.ProcessProposalReturns(mockProposalResponse, nil) 305 committedQuerier.Input.OutputFormat = "json" 306 }) 307 308 It("returns an error", func() { 309 err := committedQuerier.Query() 310 Expect(err).To(MatchError(ContainSubstring("failed to unmarshal proposal response's response payload as type *lifecycle.QueryChaincodeDefinitionsResult"))) 311 }) 312 }) 313 }) 314 315 Describe("QueryCommittedCmd", func() { 316 var queryCommittedCmd *cobra.Command 317 318 BeforeEach(func() { 319 cryptoProvider, err := sw.NewDefaultSecurityLevelWithKeystore(sw.NewDummyKeyStore()) 320 Expect(err).To(BeNil()) 321 queryCommittedCmd = chaincode.QueryCommittedCmd(nil, cryptoProvider) 322 queryCommittedCmd.SilenceErrors = true 323 queryCommittedCmd.SilenceUsage = true 324 queryCommittedCmd.SetArgs([]string{ 325 "--name=testcc", 326 "--channelID=testchannel", 327 "--peerAddresses=querycommittedpeer1", 328 "--tlsRootCertFiles=tls1", 329 }) 330 }) 331 332 AfterEach(func() { 333 chaincode.ResetFlags() 334 }) 335 336 It("attempts to connect to the endorser", func() { 337 err := queryCommittedCmd.Execute() 338 Expect(err).To(MatchError(ContainSubstring("failed to retrieve endorser client"))) 339 }) 340 341 Context("when more than one peer address is provided", func() { 342 BeforeEach(func() { 343 queryCommittedCmd.SetArgs([]string{ 344 "--name=testcc", 345 "--channelID=testchannel", 346 "--peerAddresses=querycommittedpeer1", 347 "--tlsRootCertFiles=tls1", 348 "--peerAddresses=querycommittedpeer2", 349 "--tlsRootCertFiles=tls2", 350 }) 351 }) 352 353 It("returns an error", func() { 354 err := queryCommittedCmd.Execute() 355 Expect(err).To(MatchError(ContainSubstring("failed to validate peer connection parameters"))) 356 }) 357 }) 358 }) 359 })