From d19022bcd493a33374bab474cbba2457b9151158 Mon Sep 17 00:00:00 2001 From: "Mark S. Lewis" Date: Sat, 12 Oct 2024 11:16:51 +0100 Subject: [PATCH] Update fabric-protos (#755) New version of fabric-protos uses updated protoc and protobuf versions, including protobuf-java v4. This change updates from protobuf-java v3 to v4. The change to use generics in the newer generated gRPC client stubs means that the previous approach of mocking them using uber-go/mock is not possible. This change uses mockery to mock the gRPC client connection instead. Mockery does support mocking of most generics, and mocking at the connection allows unit testing to be done using the public API without test hooks to allow replacement of gRPC stubs. Signed-off-by: Mark S. Lewis --- .golangci.yml | 1 + .mockery.yaml | 13 + Makefile | 18 +- go.mod | 13 +- go.sum | 24 +- java/pom.xml | 18 +- node/package.json | 2 +- pkg/client/blockevents_test.go | 552 ++++------- pkg/client/blockeventsfiltered_test.go | 366 ++++--- pkg/client/blockeventswithprivatedata_test.go | 374 ++++--- pkg/client/chaincodeevents_test.go | 587 ++++------- pkg/client/evaluate_test.go | 267 ++--- pkg/client/gateway_test.go | 31 +- pkg/client/identity_test.go | 45 +- pkg/client/network_test.go | 7 +- pkg/client/offlinesign_test.go | 936 +++++++++--------- pkg/client/protobuf_test.go | 418 ++++++++ pkg/client/sign_test.go | 85 +- pkg/client/submit_test.go | 779 ++++++--------- pkg/internal/test/credentials.go | 17 +- pkg/internal/test/transaction.go | 81 -- scenario/go/scenario_test.go | 2 +- 22 files changed, 2091 insertions(+), 2545 deletions(-) create mode 100644 .mockery.yaml create mode 100644 pkg/client/protobuf_test.go delete mode 100644 pkg/internal/test/transaction.go diff --git a/.golangci.yml b/.golangci.yml index 1e52fc3d9..b6f06ac84 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -19,6 +19,7 @@ linters: - govet - ineffassign - misspell + - testifylint - typecheck - unused diff --git a/.mockery.yaml b/.mockery.yaml new file mode 100644 index 000000000..1c9454373 --- /dev/null +++ b/.mockery.yaml @@ -0,0 +1,13 @@ +with-expecter: true +exported: true +filename: "{{.InterfaceName | lower}}_mock_test.go" +mockname: "Mock{{.InterfaceName | firstUpper}}" +unroll-variadic: false +packages: + google.golang.org/grpc: + config: + dir: pkg/client + outpkg: client + interfaces: + ClientConnInterface: + ClientStream: diff --git a/Makefile b/Makefile index 24c00a595..96fe72f9a 100644 --- a/Makefile +++ b/Makefile @@ -13,6 +13,10 @@ scenario_dir := $(base_dir)/scenario go_bin_dir := $(shell go env GOPATH)/bin +mockery_version := 2.46.2 +kernel_name := $(shell uname -s) +machine_hardware := $(shell uname -m) + export SOFTHSM2_CONF ?= $(base_dir)/softhsm2.conf TMPDIR ?= /tmp @@ -130,10 +134,18 @@ scan-java-osv-scanner: go install github.com/google/osv-scanner/cmd/osv-scanner@latest osv-scanner scan --lockfile='$(java_dir)/pom.xml' +.PHONY: install-mockery +install-mockery: + curl --fail --location \ + 'https://github.com/vektra/mockery/releases/download/v$(mockery_version)/mockery_$(mockery_version)_$(kernel_name)_$(machine_hardware).tar.gz' \ + | tar -C '$(go_bin_dir)' -xzf - mockery + +$(go_bin_dir)/mockery: + $(MAKE) install-mockery + .PHONY: generate -generate: - go install go.uber.org/mock/mockgen@latest - go generate '$(go_dir)/...' +generate: $(go_bin_dir)/mockery clean-generated + cd '$(base_dir)' && mockery .PHONY: vendor-chaincode vendor-chaincode: diff --git a/go.mod b/go.mod index e1dc34340..ae4125ec4 100644 --- a/go.mod +++ b/go.mod @@ -4,13 +4,13 @@ go 1.22.0 require ( github.com/cucumber/godog v0.14.1 - github.com/hyperledger/fabric-protos-go-apiv2 v0.3.3 + github.com/google/go-cmp v0.6.0 + github.com/hyperledger/fabric-protos-go-apiv2 v0.3.4 github.com/miekg/pkcs11 v1.1.1 github.com/spf13/pflag v1.0.5 github.com/stretchr/testify v1.9.0 - go.uber.org/mock v0.4.0 - golang.org/x/crypto v0.27.0 - google.golang.org/grpc v1.67.0 + golang.org/x/crypto v0.28.0 + google.golang.org/grpc v1.67.1 google.golang.org/protobuf v1.34.2 ) @@ -23,9 +23,10 @@ require ( github.com/hashicorp/go-memdb v1.3.4 // indirect github.com/hashicorp/golang-lru v1.0.2 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect + github.com/stretchr/objx v0.5.2 // indirect golang.org/x/net v0.28.0 // indirect - golang.org/x/sys v0.25.0 // indirect - golang.org/x/text v0.18.0 // indirect + golang.org/x/sys v0.26.0 // indirect + golang.org/x/text v0.19.0 // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20240814211410-ddb44dafa142 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/go.sum b/go.sum index f89ff07ec..7182b770b 100644 --- a/go.sum +++ b/go.sum @@ -27,8 +27,8 @@ github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ github.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= github.com/hashicorp/golang-lru v1.0.2 h1:dV3g9Z/unq5DpblPpw+Oqcv4dU/1omnb4Ok8iPY6p1c= github.com/hashicorp/golang-lru v1.0.2/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= -github.com/hyperledger/fabric-protos-go-apiv2 v0.3.3 h1:Xpd6fzG/KjAOHJsq7EQXY2l+qi/y8muxBaY7R6QWABk= -github.com/hyperledger/fabric-protos-go-apiv2 v0.3.3/go.mod h1:2pq0ui6ZWA0cC8J+eCErgnMDCS1kPOEYVY+06ZAK0qE= +github.com/hyperledger/fabric-protos-go-apiv2 v0.3.4 h1:YJrd+gMaeY0/vsN0aS0QkEKTivGoUnSRIXxGJ7KI+Pc= +github.com/hyperledger/fabric-protos-go-apiv2 v0.3.4/go.mod h1:bau/6AJhvEcu9GKKYHlDXAxXKzYNfhP6xu2GXuxEcFk= github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= github.com/kr/pretty v0.2.1 h1:Fmg33tUaq4/8ym9TJN1x7sLJnHVwhP33CNkpYV/7rwI= github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= @@ -46,26 +46,26 @@ github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= +github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY= +github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= -go.uber.org/mock v0.4.0 h1:VcM4ZOtdbR4f6VXfiOpwpVJDL6lCReaZ6mw31wqh7KU= -go.uber.org/mock v0.4.0/go.mod h1:a6FSlNadKUHUa9IP5Vyt1zh4fC7uAwxMutEAscFbkZc= -golang.org/x/crypto v0.27.0 h1:GXm2NjJrPaiv/h1tb2UH8QfgC/hOf/+z0p6PT8o1w7A= -golang.org/x/crypto v0.27.0/go.mod h1:1Xngt8kV6Dvbssa53Ziq6Eqn0HqbZi5Z6R0ZpwQzt70= +golang.org/x/crypto v0.28.0 h1:GBDwsMXVQi34v5CCYUm2jkJvu4cbtru2U4TN2PSyQnw= +golang.org/x/crypto v0.28.0/go.mod h1:rmgy+3RHxRZMyY0jjAJShp2zgEdOqj2AO7U0pYmeQ7U= golang.org/x/net v0.28.0 h1:a9JDOJc5GMUJ0+UDqmLT86WiEy7iWyIhz8gz8E4e5hE= golang.org/x/net v0.28.0/go.mod h1:yqtgsTWOOnlGLG9GFRrK3++bGOUEkNBoHZc8MEDWPNg= -golang.org/x/sys v0.25.0 h1:r+8e+loiHxRqhXVl6ML1nO3l1+oFoWbnlu2Ehimmi34= -golang.org/x/sys v0.25.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/text v0.18.0 h1:XvMDiNzPAl0jr17s6W9lcaIhGUfUORdGCNsuLmPG224= -golang.org/x/text v0.18.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= +golang.org/x/sys v0.26.0 h1:KHjCJyddX0LoSTb3J+vWpupP9p0oznkqVk/IfjymZbo= +golang.org/x/sys v0.26.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/text v0.19.0 h1:kTxAhCbGbxhK0IwgSKiMO5awPoDQ0RpfiVYBfK860YM= +golang.org/x/text v0.19.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= google.golang.org/genproto/googleapis/rpc v0.0.0-20240814211410-ddb44dafa142 h1:e7S5W7MGGLaSu8j3YjdezkZ+m1/Nm0uRVRMEMGk26Xs= google.golang.org/genproto/googleapis/rpc v0.0.0-20240814211410-ddb44dafa142/go.mod h1:UqMtugtsSgubUsoxbuAoiCXvqvErP7Gf0so0mK9tHxU= -google.golang.org/grpc v1.67.0 h1:IdH9y6PF5MPSdAntIcpjQ+tXO41pcQsfZV2RxtQgVcw= -google.golang.org/grpc v1.67.0/go.mod h1:1gLDyUQU7CTLJI90u3nXZ9ekeghjeM7pTDZlqFNg2AA= +google.golang.org/grpc v1.67.1 h1:zWnc1Vrcno+lHZCOofnIMvycFcc0QRGIzm9dhnDX68E= +google.golang.org/grpc v1.67.1/go.mod h1:1gLDyUQU7CTLJI90u3nXZ9ekeghjeM7pTDZlqFNg2AA= google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg= google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= diff --git a/java/pom.xml b/java/pom.xml index 4eb670dcb..c8bdf588d 100644 --- a/java/pom.xml +++ b/java/pom.xml @@ -40,7 +40,7 @@ 8 1.78.1 ${skipTests} - 7.5.0 + 7.6.0 @@ -48,29 +48,29 @@ io.cucumber cucumber-bom - 7.19.0 + 7.20.0 pom import org.junit junit-bom - 5.11.0 + 5.11.2 pom import io.grpc grpc-bom - 1.68.0 + 1.67.1 pom import - + com.google.protobuf protobuf-bom - 3.25.5 + 4.28.2 pom import @@ -97,7 +97,7 @@ org.mockito mockito-core - 5.13.0 + 5.14.1 test @@ -146,7 +146,7 @@ org.hyperledger.fabric fabric-protos - 0.3.3 + 0.3.4 @@ -530,7 +530,7 @@ org.apache.maven.plugins maven-gpg-plugin - 3.2.5 + 3.2.6 sign-artifacts diff --git a/node/package.json b/node/package.json index ca5f18cae..539890521 100644 --- a/node/package.json +++ b/node/package.json @@ -57,6 +57,6 @@ "ts-jest": "^29.2.4", "typedoc": "^0.26.6", "typescript": "~5.5.4", - "typescript-eslint": "~8.5.0" + "typescript-eslint": "^8.7.0" } } diff --git a/pkg/client/blockevents_test.go b/pkg/client/blockevents_test.go index dd28af3af..0c524701e 100644 --- a/pkg/client/blockevents_test.go +++ b/pkg/client/blockevents_test.go @@ -6,49 +6,29 @@ package client import ( "context" "errors" + "io" "testing" - "github.com/hyperledger/fabric-gateway/pkg/internal/test" "github.com/hyperledger/fabric-protos-go-apiv2/common" - "github.com/hyperledger/fabric-protos-go-apiv2/msp" "github.com/hyperledger/fabric-protos-go-apiv2/orderer" "github.com/hyperledger/fabric-protos-go-apiv2/peer" "github.com/stretchr/testify/require" - "go.uber.org/mock/gomock" "google.golang.org/grpc" "google.golang.org/grpc/codes" "google.golang.org/grpc/status" ) -func AssertValidBlockEventRequestHeader(t *testing.T, payload *common.Payload, expectedChannel string) { - channelHeader := &common.ChannelHeader{} - test.AssertUnmarshal(t, payload.GetHeader().GetChannelHeader(), channelHeader) - - require.Equal(t, expectedChannel, channelHeader.GetChannelId(), "channel name") - - signatureHeader := &common.SignatureHeader{} - test.AssertUnmarshal(t, payload.GetHeader().GetSignatureHeader(), signatureHeader) - - expectedCreator := &msp.SerializedIdentity{ - Mspid: TestCredentials.Identity().MspID(), - IdBytes: TestCredentials.Identity().Credentials(), - } - actualCreator := &msp.SerializedIdentity{} - test.AssertUnmarshal(t, signatureHeader.GetCreator(), actualCreator) - test.AssertProtoEqual(t, expectedCreator, actualCreator) -} - func TestBlockEvents(t *testing.T) { t.Run("Returns connect error", func(t *testing.T) { expected := NewStatusError(t, codes.Aborted, "BLOCK_EVENTS_ERROR") - mockClient := NewMockDeliverClient(gomock.NewController(t)) - mockClient.EXPECT().Deliver(gomock.Any(), gomock.Any()). - Return(nil, expected) + + mockConnection := NewMockClientConnInterface(t) + ExpectDeliver(mockConnection, WithNewStreamError(expected)) ctx, cancel := context.WithCancel(context.Background()) defer cancel() - network := AssertNewTestNetwork(t, "NETWORK", WithDeliverClient(mockClient)) + network := AssertNewTestNetwork(t, "NETWORK", WithClientConnection(mockConnection)) _, err := network.BlockEvents(ctx) require.Equal(t, status.Code(expected), status.Code(err), "status code") @@ -56,298 +36,146 @@ func TestBlockEvents(t *testing.T) { require.ErrorContains(t, err, expected.Error(), "message") }) - t.Run("Sends valid request with default start position", func(t *testing.T) { - controller := gomock.NewController(t) - mockClient := NewMockDeliverClient(controller) - mockEvents := NewMockDeliver_DeliverClient(controller) - - mockClient.EXPECT().Deliver(gomock.Any(), gomock.Any()). - Return(mockEvents, nil) - - payload := &common.Payload{} - mockEvents.EXPECT().Send(gomock.Any()). - Do(func(in *common.Envelope) { - test.AssertUnmarshal(t, in.GetPayload(), payload) - }). - Return(nil). - Times(1) - mockEvents.EXPECT().Recv(). - Return(nil, errors.New("fake")). - AnyTimes() - - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() - - network := AssertNewTestNetwork(t, "NETWORK", WithDeliverClient(mockClient)) - _, err := network.BlockEvents(ctx) - require.NoError(t, err) - - AssertValidBlockEventRequestHeader(t, payload, network.Name()) - actual := &orderer.SeekInfo{} - test.AssertUnmarshal(t, payload.GetData(), actual) - - expected := &orderer.SeekInfo{ - Start: &orderer.SeekPosition{ - Type: &orderer.SeekPosition_NextCommit{ - NextCommit: &orderer.SeekNextCommit{}, + for testName, testCase := range map[string]struct { + options []BlockEventsOption + expected *orderer.SeekInfo + }{ + "Sends valid request with default start position": { + options: nil, + expected: &orderer.SeekInfo{ + Start: &orderer.SeekPosition{ + Type: &orderer.SeekPosition_NextCommit{ + NextCommit: &orderer.SeekNextCommit{}, + }, }, + Stop: seekLargestBlockNumber(), }, - Stop: seekLargestBlockNumber(), - } - - test.AssertProtoEqual(t, expected, actual) - }) - - t.Run("Sends valid request with specified start block number", func(t *testing.T) { - controller := gomock.NewController(t) - mockClient := NewMockDeliverClient(controller) - mockEvents := NewMockDeliver_DeliverClient(controller) - - mockClient.EXPECT().Deliver(gomock.Any(), gomock.Any()). - Return(mockEvents, nil) - - payload := &common.Payload{} - mockEvents.EXPECT().Send(gomock.Any()). - Do(func(in *common.Envelope) { - test.AssertUnmarshal(t, in.GetPayload(), payload) - }). - Return(nil). - Times(1) - mockEvents.EXPECT().Recv(). - Return(nil, errors.New("fake")). - AnyTimes() - - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() - - network := AssertNewTestNetwork(t, "NETWORK", WithDeliverClient(mockClient)) - _, err := network.BlockEvents(ctx, WithStartBlock(418)) - require.NoError(t, err) - - AssertValidBlockEventRequestHeader(t, payload, network.Name()) - actual := &orderer.SeekInfo{} - test.AssertUnmarshal(t, payload.GetData(), actual) - - expected := &orderer.SeekInfo{ - Start: &orderer.SeekPosition{ - Type: &orderer.SeekPosition_Specified{ - Specified: &orderer.SeekSpecified{ - Number: 418, + }, + "Sends valid request with specified start block number": { + options: []BlockEventsOption{ + WithStartBlock(418), + }, + expected: &orderer.SeekInfo{ + Start: &orderer.SeekPosition{ + Type: &orderer.SeekPosition_Specified{ + Specified: &orderer.SeekSpecified{ + Number: 418, + }, }, }, + Stop: seekLargestBlockNumber(), }, - Stop: seekLargestBlockNumber(), - } - - test.AssertProtoEqual(t, expected, actual) - }) - - t.Run("Uses specified start block instead of unset checkpoint", func(t *testing.T) { - controller := gomock.NewController(t) - mockClient := NewMockDeliverClient(controller) - mockEvents := NewMockDeliver_DeliverClient(controller) - - mockClient.EXPECT().Deliver(gomock.Any(), gomock.Any()). - Return(mockEvents, nil) - - payload := &common.Payload{} - mockEvents.EXPECT().Send(gomock.Any()). - Do(func(in *common.Envelope) { - test.AssertUnmarshal(t, in.GetPayload(), payload) - }). - Return(nil). - Times(1) - mockEvents.EXPECT().Recv(). - Return(nil, errors.New("fake")). - AnyTimes() - - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() - - network := AssertNewTestNetwork(t, "NETWORK", WithDeliverClient(mockClient)) - - checkpointer := new(InMemoryCheckpointer) - - _, err := network.BlockEvents(ctx, WithStartBlock(418), WithCheckpoint(checkpointer)) - require.NoError(t, err) - - AssertValidBlockEventRequestHeader(t, payload, network.Name()) - actual := &orderer.SeekInfo{} - test.AssertUnmarshal(t, payload.GetData(), actual) - - expected := &orderer.SeekInfo{ - Start: &orderer.SeekPosition{ - Type: &orderer.SeekPosition_Specified{ - Specified: &orderer.SeekSpecified{ - Number: 418, + }, + "Uses specified start block instead of unset checkpoint": { + options: []BlockEventsOption{ + WithStartBlock(418), + WithCheckpoint(new(InMemoryCheckpointer)), + }, + expected: &orderer.SeekInfo{ + Start: &orderer.SeekPosition{ + Type: &orderer.SeekPosition_Specified{ + Specified: &orderer.SeekSpecified{ + Number: 418, + }, }, }, + Stop: seekLargestBlockNumber(), }, - Stop: seekLargestBlockNumber(), - } - - test.AssertProtoEqual(t, expected, actual) - }) - - t.Run("Uses checkpoint block instead of specified start block", func(t *testing.T) { - controller := gomock.NewController(t) - mockClient := NewMockDeliverClient(controller) - mockEvents := NewMockDeliver_DeliverClient(controller) - - mockClient.EXPECT().Deliver(gomock.Any(), gomock.Any()). - Return(mockEvents, nil) - - payload := &common.Payload{} - mockEvents.EXPECT().Send(gomock.Any()). - Do(func(in *common.Envelope) { - test.AssertUnmarshal(t, in.GetPayload(), payload) - }). - Return(nil). - Times(1) - mockEvents.EXPECT().Recv(). - Return(nil, errors.New("fake")). - AnyTimes() - - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() - - network := AssertNewTestNetwork(t, "NETWORK", WithDeliverClient(mockClient)) - - checkpointer := new(InMemoryCheckpointer) - checkpointer.CheckpointBlock(uint64(500)) - - _, err := network.BlockEvents(ctx, WithStartBlock(418), WithCheckpoint(checkpointer)) - require.NoError(t, err) - - AssertValidBlockEventRequestHeader(t, payload, network.Name()) - actual := &orderer.SeekInfo{} - test.AssertUnmarshal(t, payload.GetData(), actual) - - expected := &orderer.SeekInfo{ - Start: &orderer.SeekPosition{ - Type: &orderer.SeekPosition_Specified{ - Specified: &orderer.SeekSpecified{ - Number: 501, + }, + "Uses checkpoint block instead of specified start block": { + options: func() []BlockEventsOption { + checkpointer := new(InMemoryCheckpointer) + checkpointer.CheckpointBlock(500) + return []BlockEventsOption{ + WithStartBlock(418), + WithCheckpoint(checkpointer), + } + }(), + expected: &orderer.SeekInfo{ + Start: &orderer.SeekPosition{ + Type: &orderer.SeekPosition_Specified{ + Specified: &orderer.SeekSpecified{ + Number: 501, + }, }, }, + Stop: seekLargestBlockNumber(), }, - Stop: seekLargestBlockNumber(), - } - - test.AssertProtoEqual(t, expected, actual) - }) - - t.Run("Uses checkpoint block zero with set transaction ID instead of specified start block", func(t *testing.T) { - controller := gomock.NewController(t) - mockClient := NewMockDeliverClient(controller) - mockEvents := NewMockDeliver_DeliverClient(controller) - - mockClient.EXPECT().Deliver(gomock.Any(), gomock.Any()). - Return(mockEvents, nil) - - payload := &common.Payload{} - mockEvents.EXPECT().Send(gomock.Any()). - Do(func(in *common.Envelope) { - test.AssertUnmarshal(t, in.GetPayload(), payload) - }). - Return(nil). - Times(1) - mockEvents.EXPECT().Recv(). - Return(nil, errors.New("fake")). - AnyTimes() - - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() - - network := AssertNewTestNetwork(t, "NETWORK", WithDeliverClient(mockClient)) - - checkpointer := new(InMemoryCheckpointer) - blockNumber := uint64(0) - checkpointer.CheckpointTransaction(blockNumber, "transctionId") - - _, err := network.BlockEvents(ctx, WithStartBlock(418), WithCheckpoint(checkpointer)) - require.NoError(t, err) - - AssertValidBlockEventRequestHeader(t, payload, network.Name()) - actual := &orderer.SeekInfo{} - test.AssertUnmarshal(t, payload.GetData(), actual) - - expected := &orderer.SeekInfo{ - Start: &orderer.SeekPosition{ - Type: &orderer.SeekPosition_Specified{ - Specified: &orderer.SeekSpecified{ - Number: blockNumber, + }, + "Uses checkpoint block zero with set transaction ID instead of specified start block": { + options: func() []BlockEventsOption { + checkpointer := new(InMemoryCheckpointer) + checkpointer.CheckpointTransaction(0, "transctionId") + return []BlockEventsOption{ + WithStartBlock(418), + WithCheckpoint(checkpointer), + } + }(), + expected: &orderer.SeekInfo{ + Start: &orderer.SeekPosition{ + Type: &orderer.SeekPosition_Specified{ + Specified: &orderer.SeekSpecified{ + Number: 0, + }, }, }, + Stop: seekLargestBlockNumber(), }, - Stop: seekLargestBlockNumber(), - } - - test.AssertProtoEqual(t, expected, actual) - }) - - t.Run("Uses default start position with unset checkpoint and no start block", func(t *testing.T) { - controller := gomock.NewController(t) - mockClient := NewMockDeliverClient(controller) - mockEvents := NewMockDeliver_DeliverClient(controller) - - mockClient.EXPECT().Deliver(gomock.Any(), gomock.Any()). - Return(mockEvents, nil) - - payload := &common.Payload{} - mockEvents.EXPECT().Send(gomock.Any()). - Do(func(in *common.Envelope) { - test.AssertUnmarshal(t, in.GetPayload(), payload) - }). - Return(nil). - Times(1) - mockEvents.EXPECT().Recv(). - Return(nil, errors.New("fake")). - AnyTimes() - - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() - - network := AssertNewTestNetwork(t, "NETWORK", WithDeliverClient(mockClient)) - - checkpointer := new(InMemoryCheckpointer) - - _, err := network.BlockEvents(ctx, WithCheckpoint(checkpointer)) - require.NoError(t, err) - - AssertValidBlockEventRequestHeader(t, payload, network.Name()) - actual := &orderer.SeekInfo{} - test.AssertUnmarshal(t, payload.GetData(), actual) - - expected := &orderer.SeekInfo{ - Start: &orderer.SeekPosition{ - Type: &orderer.SeekPosition_NextCommit{ - NextCommit: &orderer.SeekNextCommit{}, + }, + "Uses default start position with unset checkpoint and no start block": { + options: []BlockEventsOption{ + WithCheckpoint(new(InMemoryCheckpointer)), + }, + expected: &orderer.SeekInfo{ + Start: &orderer.SeekPosition{ + Type: &orderer.SeekPosition_NextCommit{ + NextCommit: &orderer.SeekNextCommit{}, + }, }, + Stop: seekLargestBlockNumber(), }, - Stop: seekLargestBlockNumber(), - } - - test.AssertProtoEqual(t, expected, actual) - }) + }, + } { + t.Run(testName, func(t *testing.T) { + mockConnection := NewMockClientConnInterface(t) + mockStream := NewMockClientStream(t) + ExpectDeliver(mockConnection, WithNewStreamResult(mockStream)) + + messages := make(chan *common.Envelope, 1) + ExpectSendMsg(mockStream, CaptureSendMsg(messages)) + mockStream.EXPECT().CloseSend().Maybe().Return(nil) + ExpectRecvMsg(mockStream).Maybe().Return(io.EOF) + + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + network := AssertNewTestNetwork(t, "NETWORK", WithClientConnection(mockConnection)) + _, err := network.BlockEvents(ctx, testCase.options...) + require.NoError(t, err) + + payload := &common.Payload{} + AssertUnmarshal(t, (<-messages).GetPayload(), payload) + AssertValidBlockEventRequestHeader(t, payload, network.Name()) + actual := &orderer.SeekInfo{} + AssertUnmarshal(t, payload.GetData(), actual) + + AssertProtoEqual(t, testCase.expected, actual) + }) + } t.Run("Closes event channel on receive error", func(t *testing.T) { - controller := gomock.NewController(t) - mockClient := NewMockDeliverClient(controller) - mockEvents := NewMockDeliver_DeliverClient(controller) + mockConnection := NewMockClientConnInterface(t) + mockStream := NewMockClientStream(t) + ExpectDeliver(mockConnection, WithNewStreamResult(mockStream)) - mockClient.EXPECT().Deliver(gomock.Any(), gomock.Any()). - Return(mockEvents, nil) - - mockEvents.EXPECT().Send(gomock.Any()). - Return(nil) - mockEvents.EXPECT().Recv(). - Return(nil, errors.New("fake")) + ExpectSendMsg(mockStream) + mockStream.EXPECT().CloseSend().Maybe().Return(nil) + ExpectRecvMsg(mockStream).Return(errors.New("fake")) ctx, cancel := context.WithCancel(context.Background()) defer cancel() - network := AssertNewTestNetwork(t, "NETWORK", WithDeliverClient(mockClient)) + network := AssertNewTestNetwork(t, "NETWORK", WithClientConnection(mockConnection)) receive, err := network.BlockEvents(ctx, WithStartBlock(418)) require.NoError(t, err) @@ -357,17 +185,7 @@ func TestBlockEvents(t *testing.T) { }) t.Run("Receives events", func(t *testing.T) { - controller := gomock.NewController(t) - mockClient := NewMockDeliverClient(controller) - mockEvents := NewMockDeliver_DeliverClient(controller) - - mockClient.EXPECT().Deliver(gomock.Any(), gomock.Any()). - Return(mockEvents, nil) - - mockEvents.EXPECT().Send(gomock.Any()). - Return(nil) - - blocks := []*common.Block{ + expected := []*common.Block{ { Header: &common.BlockHeader{ Number: 1, @@ -389,45 +207,44 @@ func TestBlockEvents(t *testing.T) { }, }, } - responseIndex := 0 - mockEvents.EXPECT().Recv(). - DoAndReturn(func() (*peer.DeliverResponse, error) { - if responseIndex >= len(blocks) { - return nil, errors.New("fake") - } - response := &peer.DeliverResponse{ - Type: &peer.DeliverResponse_Block{ - Block: blocks[responseIndex], - }, - } - responseIndex++ - return response, nil - }). - AnyTimes() + + mockConnection := NewMockClientConnInterface(t) + mockStream := NewMockClientStream(t) + ExpectDeliver(mockConnection, WithNewStreamResult(mockStream)) + + ExpectSendMsg(mockStream) + mockStream.EXPECT().CloseSend().Maybe().Return(nil) + + var responses []*peer.DeliverResponse + for _, block := range expected { + responses = append(responses, &peer.DeliverResponse{ + Type: &peer.DeliverResponse_Block{ + Block: block, + }, + }) + } + ExpectRecvMsg(mockStream, WithRecvMsgs(responses...)) ctx, cancel := context.WithCancel(context.Background()) defer cancel() - network := AssertNewTestNetwork(t, "NETWORK", WithDeliverClient(mockClient)) + network := AssertNewTestNetwork(t, "NETWORK", WithClientConnection(mockConnection)) receive, err := network.BlockEvents(ctx) require.NoError(t, err) - for _, event := range blocks { + for _, event := range expected { actual := <-receive - test.AssertProtoEqual(t, event, actual) + AssertProtoEqual(t, event, actual) } }) t.Run("Closes event channel on non-block message", func(t *testing.T) { - controller := gomock.NewController(t) - mockClient := NewMockDeliverClient(controller) - mockEvents := NewMockDeliver_DeliverClient(controller) + mockConnection := NewMockClientConnInterface(t) + mockStream := NewMockClientStream(t) + ExpectDeliver(mockConnection, WithNewStreamResult(mockStream)) - mockClient.EXPECT().Deliver(gomock.Any(), gomock.Any()). - Return(mockEvents, nil) - - mockEvents.EXPECT().Send(gomock.Any()). - Return(nil) + ExpectSendMsg(mockStream) + mockStream.EXPECT().CloseSend().Maybe().Return(nil) block := &common.Block{ Header: &common.BlockHeader{ @@ -456,22 +273,12 @@ func TestBlockEvents(t *testing.T) { }, }, } - responseIndex := 0 - mockEvents.EXPECT().Recv(). - DoAndReturn(func() (*peer.DeliverResponse, error) { - if responseIndex >= len(responses) { - return nil, errors.New("fake") - } - response := responses[responseIndex] - responseIndex++ - return response, nil - }). - AnyTimes() + ExpectRecvMsg(mockStream, WithRecvMsgs(responses...)) ctx, cancel := context.WithCancel(context.Background()) defer cancel() - network := AssertNewTestNetwork(t, "NETWORK", WithDeliverClient(mockClient)) + network := AssertNewTestNetwork(t, "NETWORK", WithClientConnection(mockConnection)) receive, err := network.BlockEvents(ctx) require.NoError(t, err) @@ -482,75 +289,58 @@ func TestBlockEvents(t *testing.T) { } for _, event := range expected { actual := <-receive - test.AssertProtoEqual(t, event, actual) + AssertProtoEqual(t, event, actual) } }) t.Run("Uses specified gRPC call options", func(t *testing.T) { - var actual []grpc.CallOption expected := grpc.WaitForReady(true) - controller := gomock.NewController(t) - mockClient := NewMockDeliverClient(controller) - mockEvents := NewMockDeliver_DeliverClient(controller) + mockConnection := NewMockClientConnInterface(t) + mockStream := NewMockClientStream(t) + options := make(chan []grpc.CallOption, 1) + ExpectDeliver(mockConnection, CaptureNewStreamOptions(options), WithNewStreamResult(mockStream)) - mockClient.EXPECT().Deliver(gomock.Any(), gomock.Any()). - Do(func(_ context.Context, opts ...grpc.CallOption) { - actual = opts - }). - Return(mockEvents, nil). - Times(1) - - mockEvents.EXPECT().Send(gomock.Any()). - Return(nil). - AnyTimes() - mockEvents.EXPECT().Recv(). - Return(nil, errors.New("fake")). - AnyTimes() + ExpectSendMsg(mockStream) + mockStream.EXPECT().CloseSend().Maybe().Return(nil) + ExpectRecvMsg(mockStream).Maybe().Return(io.EOF) ctx, cancel := context.WithCancel(context.Background()) defer cancel() - network := AssertNewTestNetwork(t, "NETWORK", WithDeliverClient(mockClient)) + network := AssertNewTestNetwork(t, "NETWORK", WithClientConnection(mockConnection)) request, err := network.NewBlockEventsRequest() require.NoError(t, err, "NewBlockEventsRequest") _, err = request.Events(ctx, expected) require.NoError(t, err, "Events") - require.Contains(t, actual, expected, "CallOptions") + require.Contains(t, (<-options), expected, "CallOptions") }) t.Run("Sends request with TLS client certificate hash", func(t *testing.T) { expected := []byte("TLS_CLIENT_CERTIFICATE_HASH") - controller := gomock.NewController(t) - mockClient := NewMockDeliverClient(controller) - mockEvents := NewMockDeliver_DeliverClient(controller) - - mockClient.EXPECT().Deliver(gomock.Any(), gomock.Any()). - Return(mockEvents, nil) + mockConnection := NewMockClientConnInterface(t) + mockStream := NewMockClientStream(t) + ExpectDeliver(mockConnection, WithNewStreamResult(mockStream)) - payload := &common.Payload{} - mockEvents.EXPECT().Send(gomock.Any()). - Do(func(in *common.Envelope) { - test.AssertUnmarshal(t, in.GetPayload(), payload) - }). - Return(nil). - Times(1) - mockEvents.EXPECT().Recv(). - Return(nil, errors.New("fake")). - AnyTimes() + requests := make(chan *common.Envelope, 1) + ExpectSendMsg(mockStream, CaptureSendMsg(requests)) + mockStream.EXPECT().CloseSend().Maybe().Return(nil) + ExpectRecvMsg(mockStream).Maybe().Return(io.EOF) ctx, cancel := context.WithCancel(context.Background()) defer cancel() - network := AssertNewTestNetwork(t, "NETWORK", WithDeliverClient(mockClient), WithTLSClientCertificateHash(expected)) + network := AssertNewTestNetwork(t, "NETWORK", WithClientConnection(mockConnection), WithTLSClientCertificateHash(expected)) _, err := network.BlockEvents(ctx) require.NoError(t, err) + payload := &common.Payload{} + AssertUnmarshal(t, (<-requests).GetPayload(), payload) channelHeader := &common.ChannelHeader{} - test.AssertUnmarshal(t, payload.GetHeader().GetChannelHeader(), channelHeader) + AssertUnmarshal(t, payload.GetHeader().GetChannelHeader(), channelHeader) require.Equal(t, expected, channelHeader.GetTlsCertHash()) }) diff --git a/pkg/client/blockeventsfiltered_test.go b/pkg/client/blockeventsfiltered_test.go index fe3014c10..46aa3e2b4 100644 --- a/pkg/client/blockeventsfiltered_test.go +++ b/pkg/client/blockeventsfiltered_test.go @@ -6,14 +6,13 @@ package client import ( "context" "errors" + "io" "testing" - "github.com/hyperledger/fabric-gateway/pkg/internal/test" "github.com/hyperledger/fabric-protos-go-apiv2/common" "github.com/hyperledger/fabric-protos-go-apiv2/orderer" "github.com/hyperledger/fabric-protos-go-apiv2/peer" "github.com/stretchr/testify/require" - "go.uber.org/mock/gomock" "google.golang.org/grpc" "google.golang.org/grpc/codes" "google.golang.org/grpc/status" @@ -22,14 +21,14 @@ import ( func TestFilteredBlockEvents(t *testing.T) { t.Run("Returns connect error", func(t *testing.T) { expected := NewStatusError(t, codes.Aborted, "BLOCK_EVENTS_ERROR") - mockClient := NewMockDeliverClient(gomock.NewController(t)) - mockClient.EXPECT().DeliverFiltered(gomock.Any(), gomock.Any()). - Return(nil, expected) + + mockConnection := NewMockClientConnInterface(t) + ExpectDeliverFiltered(mockConnection, WithNewStreamError(expected)) ctx, cancel := context.WithCancel(context.Background()) defer cancel() - network := AssertNewTestNetwork(t, "NETWORK", WithDeliverClient(mockClient)) + network := AssertNewTestNetwork(t, "NETWORK", WithClientConnection(mockConnection)) _, err := network.FilteredBlockEvents(ctx) require.Equal(t, status.Code(expected), status.Code(err), "status code") @@ -37,109 +36,146 @@ func TestFilteredBlockEvents(t *testing.T) { require.ErrorContains(t, err, expected.Error(), "message") }) - t.Run("Sends valid request with default start position", func(t *testing.T) { - controller := gomock.NewController(t) - mockClient := NewMockDeliverClient(controller) - mockEvents := NewMockDeliver_DeliverFilteredClient(controller) - - mockClient.EXPECT().DeliverFiltered(gomock.Any(), gomock.Any()). - Return(mockEvents, nil) - - payload := &common.Payload{} - mockEvents.EXPECT().Send(gomock.Any()). - Do(func(in *common.Envelope) { - test.AssertUnmarshal(t, in.GetPayload(), payload) - }). - Return(nil). - Times(1) - mockEvents.EXPECT().Recv(). - Return(nil, errors.New("fake")). - AnyTimes() - - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() - - network := AssertNewTestNetwork(t, "NETWORK", WithDeliverClient(mockClient)) - _, err := network.FilteredBlockEvents(ctx) - require.NoError(t, err) - - AssertValidBlockEventRequestHeader(t, payload, network.Name()) - actual := &orderer.SeekInfo{} - test.AssertUnmarshal(t, payload.GetData(), actual) - - expected := &orderer.SeekInfo{ - Start: &orderer.SeekPosition{ - Type: &orderer.SeekPosition_NextCommit{ - NextCommit: &orderer.SeekNextCommit{}, + for testName, testCase := range map[string]struct { + options []BlockEventsOption + expected *orderer.SeekInfo + }{ + "Sends valid request with default start position": { + options: nil, + expected: &orderer.SeekInfo{ + Start: &orderer.SeekPosition{ + Type: &orderer.SeekPosition_NextCommit{ + NextCommit: &orderer.SeekNextCommit{}, + }, }, + Stop: seekLargestBlockNumber(), }, - Stop: seekLargestBlockNumber(), - } - - test.AssertProtoEqual(t, expected, actual) - }) - - t.Run("Sends valid request with specified start block number", func(t *testing.T) { - controller := gomock.NewController(t) - mockClient := NewMockDeliverClient(controller) - mockEvents := NewMockDeliver_DeliverFilteredClient(controller) - - mockClient.EXPECT().DeliverFiltered(gomock.Any(), gomock.Any()). - Return(mockEvents, nil) - - payload := &common.Payload{} - mockEvents.EXPECT().Send(gomock.Any()). - Do(func(in *common.Envelope) { - test.AssertUnmarshal(t, in.GetPayload(), payload) - }). - Return(nil). - Times(1) - mockEvents.EXPECT().Recv(). - Return(nil, errors.New("fake")). - AnyTimes() - - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() - - network := AssertNewTestNetwork(t, "NETWORK", WithDeliverClient(mockClient)) - _, err := network.FilteredBlockEvents(ctx, WithStartBlock(418)) - require.NoError(t, err) - - AssertValidBlockEventRequestHeader(t, payload, network.Name()) - actual := &orderer.SeekInfo{} - test.AssertUnmarshal(t, payload.GetData(), actual) - - expected := &orderer.SeekInfo{ - Start: &orderer.SeekPosition{ - Type: &orderer.SeekPosition_Specified{ - Specified: &orderer.SeekSpecified{ - Number: 418, + }, + "Sends valid request with specified start block number": { + options: []BlockEventsOption{ + WithStartBlock(418), + }, + expected: &orderer.SeekInfo{ + Start: &orderer.SeekPosition{ + Type: &orderer.SeekPosition_Specified{ + Specified: &orderer.SeekSpecified{ + Number: 418, + }, }, }, + Stop: seekLargestBlockNumber(), }, - Stop: seekLargestBlockNumber(), - } - - test.AssertProtoEqual(t, expected, actual) - }) + }, + "Uses specified start block instead of unset checkpoint": { + options: []BlockEventsOption{ + WithStartBlock(418), + WithCheckpoint(new(InMemoryCheckpointer)), + }, + expected: &orderer.SeekInfo{ + Start: &orderer.SeekPosition{ + Type: &orderer.SeekPosition_Specified{ + Specified: &orderer.SeekSpecified{ + Number: 418, + }, + }, + }, + Stop: seekLargestBlockNumber(), + }, + }, + "Uses checkpoint block instead of specified start block": { + options: func() []BlockEventsOption { + checkpointer := new(InMemoryCheckpointer) + checkpointer.CheckpointBlock(500) + return []BlockEventsOption{ + WithStartBlock(418), + WithCheckpoint(checkpointer), + } + }(), + expected: &orderer.SeekInfo{ + Start: &orderer.SeekPosition{ + Type: &orderer.SeekPosition_Specified{ + Specified: &orderer.SeekSpecified{ + Number: 501, + }, + }, + }, + Stop: seekLargestBlockNumber(), + }, + }, + "Uses checkpoint block zero with set transaction ID instead of specified start block": { + options: func() []BlockEventsOption { + checkpointer := new(InMemoryCheckpointer) + checkpointer.CheckpointTransaction(0, "transctionId") + return []BlockEventsOption{ + WithStartBlock(418), + WithCheckpoint(checkpointer), + } + }(), + expected: &orderer.SeekInfo{ + Start: &orderer.SeekPosition{ + Type: &orderer.SeekPosition_Specified{ + Specified: &orderer.SeekSpecified{ + Number: 0, + }, + }, + }, + Stop: seekLargestBlockNumber(), + }, + }, + "Uses default start position with unset checkpoint and no start block": { + options: []BlockEventsOption{ + WithCheckpoint(new(InMemoryCheckpointer)), + }, + expected: &orderer.SeekInfo{ + Start: &orderer.SeekPosition{ + Type: &orderer.SeekPosition_NextCommit{ + NextCommit: &orderer.SeekNextCommit{}, + }, + }, + Stop: seekLargestBlockNumber(), + }, + }, + } { + t.Run(testName, func(t *testing.T) { + mockConnection := NewMockClientConnInterface(t) + mockStream := NewMockClientStream(t) + ExpectDeliverFiltered(mockConnection, WithNewStreamResult(mockStream)) + + messages := make(chan *common.Envelope, 1) + ExpectSendMsg(mockStream, CaptureSendMsg(messages)) + mockStream.EXPECT().CloseSend().Maybe().Return(nil) + ExpectRecvMsg(mockStream).Maybe().Return(io.EOF) + + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + network := AssertNewTestNetwork(t, "NETWORK", WithClientConnection(mockConnection)) + _, err := network.FilteredBlockEvents(ctx, testCase.options...) + require.NoError(t, err) + + payload := &common.Payload{} + AssertUnmarshal(t, (<-messages).GetPayload(), payload) + AssertValidBlockEventRequestHeader(t, payload, network.Name()) + actual := &orderer.SeekInfo{} + AssertUnmarshal(t, payload.GetData(), actual) + + AssertProtoEqual(t, testCase.expected, actual) + }) + } t.Run("Closes event channel on receive error", func(t *testing.T) { - controller := gomock.NewController(t) - mockClient := NewMockDeliverClient(controller) - mockEvents := NewMockDeliver_DeliverFilteredClient(controller) - - mockClient.EXPECT().DeliverFiltered(gomock.Any(), gomock.Any()). - Return(mockEvents, nil) + mockConnection := NewMockClientConnInterface(t) + mockStream := NewMockClientStream(t) + ExpectDeliverFiltered(mockConnection, WithNewStreamResult(mockStream)) - mockEvents.EXPECT().Send(gomock.Any()). - Return(nil) - mockEvents.EXPECT().Recv(). - Return(nil, errors.New("fake")) + ExpectSendMsg(mockStream) + mockStream.EXPECT().CloseSend().Maybe().Return(nil) + ExpectRecvMsg(mockStream).Return(errors.New("fake")) ctx, cancel := context.WithCancel(context.Background()) defer cancel() - network := AssertNewTestNetwork(t, "NETWORK", WithDeliverClient(mockClient)) + network := AssertNewTestNetwork(t, "NETWORK", WithClientConnection(mockConnection)) receive, err := network.FilteredBlockEvents(ctx, WithStartBlock(418)) require.NoError(t, err) @@ -149,17 +185,7 @@ func TestFilteredBlockEvents(t *testing.T) { }) t.Run("Receives events", func(t *testing.T) { - controller := gomock.NewController(t) - mockClient := NewMockDeliverClient(controller) - mockEvents := NewMockDeliver_DeliverFilteredClient(controller) - - mockClient.EXPECT().DeliverFiltered(gomock.Any(), gomock.Any()). - Return(mockEvents, nil) - - mockEvents.EXPECT().Send(gomock.Any()). - Return(nil) - - blocks := []*peer.FilteredBlock{ + expected := []*peer.FilteredBlock{ { ChannelId: "NETWORK", Number: 1, @@ -179,45 +205,44 @@ func TestFilteredBlockEvents(t *testing.T) { }, }, } - responseIndex := 0 - mockEvents.EXPECT().Recv(). - DoAndReturn(func() (*peer.DeliverResponse, error) { - if responseIndex >= len(blocks) { - return nil, errors.New("fake") - } - response := &peer.DeliverResponse{ - Type: &peer.DeliverResponse_FilteredBlock{ - FilteredBlock: blocks[responseIndex], - }, - } - responseIndex++ - return response, nil - }). - AnyTimes() + + mockConnection := NewMockClientConnInterface(t) + mockStream := NewMockClientStream(t) + ExpectDeliverFiltered(mockConnection, WithNewStreamResult(mockStream)) + + ExpectSendMsg(mockStream) + mockStream.EXPECT().CloseSend().Maybe().Return(nil) + + var responses []*peer.DeliverResponse + for _, block := range expected { + responses = append(responses, &peer.DeliverResponse{ + Type: &peer.DeliverResponse_FilteredBlock{ + FilteredBlock: block, + }, + }) + } + ExpectRecvMsg(mockStream, WithRecvMsgs(responses...)) ctx, cancel := context.WithCancel(context.Background()) defer cancel() - network := AssertNewTestNetwork(t, "NETWORK", WithDeliverClient(mockClient)) + network := AssertNewTestNetwork(t, "NETWORK", WithClientConnection(mockConnection)) receive, err := network.FilteredBlockEvents(ctx) require.NoError(t, err) - for _, event := range blocks { + for _, event := range expected { actual := <-receive - test.AssertProtoEqual(t, event, actual) + AssertProtoEqual(t, event, actual) } }) t.Run("Closes event channel on non-block message", func(t *testing.T) { - controller := gomock.NewController(t) - mockClient := NewMockDeliverClient(controller) - mockEvents := NewMockDeliver_DeliverFilteredClient(controller) + mockConnection := NewMockClientConnInterface(t) + mockStream := NewMockClientStream(t) + ExpectDeliverFiltered(mockConnection, WithNewStreamResult(mockStream)) - mockClient.EXPECT().DeliverFiltered(gomock.Any(), gomock.Any()). - Return(mockEvents, nil) - - mockEvents.EXPECT().Send(gomock.Any()). - Return(nil) + ExpectSendMsg(mockStream) + mockStream.EXPECT().CloseSend().Maybe().Return(nil) block := &peer.FilteredBlock{ ChannelId: "NETWORK", @@ -245,22 +270,12 @@ func TestFilteredBlockEvents(t *testing.T) { }, }, } - responseIndex := 0 - mockEvents.EXPECT().Recv(). - DoAndReturn(func() (*peer.DeliverResponse, error) { - if responseIndex >= len(responses) { - return nil, errors.New("fake") - } - response := responses[responseIndex] - responseIndex++ - return response, nil - }). - AnyTimes() + ExpectRecvMsg(mockStream, WithRecvMsgs(responses...)) ctx, cancel := context.WithCancel(context.Background()) defer cancel() - network := AssertNewTestNetwork(t, "NETWORK", WithDeliverClient(mockClient)) + network := AssertNewTestNetwork(t, "NETWORK", WithClientConnection(mockConnection)) receive, err := network.FilteredBlockEvents(ctx) require.NoError(t, err) @@ -271,75 +286,58 @@ func TestFilteredBlockEvents(t *testing.T) { } for _, event := range expected { actual := <-receive - test.AssertProtoEqual(t, event, actual) + AssertProtoEqual(t, event, actual) } }) t.Run("Uses specified gRPC call options", func(t *testing.T) { - var actual []grpc.CallOption expected := grpc.WaitForReady(true) - controller := gomock.NewController(t) - mockClient := NewMockDeliverClient(controller) - mockEvents := NewMockDeliver_DeliverClient(controller) + mockConnection := NewMockClientConnInterface(t) + mockStream := NewMockClientStream(t) + options := make(chan []grpc.CallOption, 1) + ExpectDeliverFiltered(mockConnection, CaptureNewStreamOptions(options), WithNewStreamResult(mockStream)) - mockClient.EXPECT().DeliverFiltered(gomock.Any(), gomock.Any()). - Do(func(_ context.Context, opts ...grpc.CallOption) { - actual = opts - }). - Return(mockEvents, nil). - Times(1) - - mockEvents.EXPECT().Send(gomock.Any()). - Return(nil). - AnyTimes() - mockEvents.EXPECT().Recv(). - Return(nil, errors.New("fake")). - AnyTimes() + ExpectSendMsg(mockStream) + mockStream.EXPECT().CloseSend().Maybe().Return(nil) + ExpectRecvMsg(mockStream).Maybe().Return(io.EOF) ctx, cancel := context.WithCancel(context.Background()) defer cancel() - network := AssertNewTestNetwork(t, "NETWORK", WithDeliverClient(mockClient)) + network := AssertNewTestNetwork(t, "NETWORK", WithClientConnection(mockConnection)) request, err := network.NewFilteredBlockEventsRequest() require.NoError(t, err, "NewFilteredBlockEventsRequest") _, err = request.Events(ctx, expected) require.NoError(t, err, "Events") - require.Contains(t, actual, expected, "CallOptions") + require.Contains(t, (<-options), expected, "CallOptions") }) t.Run("Sends request with TLS client certificate hash", func(t *testing.T) { expected := []byte("TLS_CLIENT_CERTIFICATE_HASH") - controller := gomock.NewController(t) - mockClient := NewMockDeliverClient(controller) - mockEvents := NewMockDeliver_DeliverClient(controller) - - mockClient.EXPECT().DeliverFiltered(gomock.Any(), gomock.Any()). - Return(mockEvents, nil) + mockConnection := NewMockClientConnInterface(t) + mockStream := NewMockClientStream(t) + ExpectDeliverFiltered(mockConnection, WithNewStreamResult(mockStream)) - payload := &common.Payload{} - mockEvents.EXPECT().Send(gomock.Any()). - Do(func(in *common.Envelope) { - test.AssertUnmarshal(t, in.GetPayload(), payload) - }). - Return(nil). - Times(1) - mockEvents.EXPECT().Recv(). - Return(nil, errors.New("fake")). - AnyTimes() + requests := make(chan *common.Envelope, 1) + ExpectSendMsg(mockStream, CaptureSendMsg(requests)) + mockStream.EXPECT().CloseSend().Maybe().Return(nil) + ExpectRecvMsg(mockStream).Maybe().Return(io.EOF) ctx, cancel := context.WithCancel(context.Background()) defer cancel() - network := AssertNewTestNetwork(t, "NETWORK", WithDeliverClient(mockClient), WithTLSClientCertificateHash(expected)) + network := AssertNewTestNetwork(t, "NETWORK", WithClientConnection(mockConnection), WithTLSClientCertificateHash(expected)) _, err := network.FilteredBlockEvents(ctx) require.NoError(t, err) + payload := &common.Payload{} + AssertUnmarshal(t, (<-requests).GetPayload(), payload) channelHeader := &common.ChannelHeader{} - test.AssertUnmarshal(t, payload.GetHeader().GetChannelHeader(), channelHeader) + AssertUnmarshal(t, payload.GetHeader().GetChannelHeader(), channelHeader) require.Equal(t, expected, channelHeader.GetTlsCertHash()) }) diff --git a/pkg/client/blockeventswithprivatedata_test.go b/pkg/client/blockeventswithprivatedata_test.go index 34e9ce04e..c3970fca3 100644 --- a/pkg/client/blockeventswithprivatedata_test.go +++ b/pkg/client/blockeventswithprivatedata_test.go @@ -6,15 +6,14 @@ package client import ( "context" "errors" + "io" "testing" - "github.com/hyperledger/fabric-gateway/pkg/internal/test" "github.com/hyperledger/fabric-protos-go-apiv2/common" "github.com/hyperledger/fabric-protos-go-apiv2/ledger/rwset" "github.com/hyperledger/fabric-protos-go-apiv2/orderer" "github.com/hyperledger/fabric-protos-go-apiv2/peer" "github.com/stretchr/testify/require" - "go.uber.org/mock/gomock" "google.golang.org/grpc" "google.golang.org/grpc/codes" "google.golang.org/grpc/status" @@ -23,14 +22,14 @@ import ( func TestBlockAndPrivateDataEvents(t *testing.T) { t.Run("Returns connect error", func(t *testing.T) { expected := NewStatusError(t, codes.Aborted, "BLOCK_EVENTS_ERROR") - mockClient := NewMockDeliverClient(gomock.NewController(t)) - mockClient.EXPECT().DeliverWithPrivateData(gomock.Any(), gomock.Any()). - Return(nil, expected) + + mockConnection := NewMockClientConnInterface(t) + ExpectDeliverWithPrivateData(mockConnection, WithNewStreamError(expected)) ctx, cancel := context.WithCancel(context.Background()) defer cancel() - network := AssertNewTestNetwork(t, "NETWORK", WithDeliverClient(mockClient)) + network := AssertNewTestNetwork(t, "NETWORK", WithClientConnection(mockConnection)) _, err := network.BlockAndPrivateDataEvents(ctx) require.Equal(t, status.Code(expected), status.Code(err), "status code") @@ -38,109 +37,146 @@ func TestBlockAndPrivateDataEvents(t *testing.T) { require.ErrorContains(t, err, expected.Error(), "message") }) - t.Run("Sends valid request with default start position", func(t *testing.T) { - controller := gomock.NewController(t) - mockClient := NewMockDeliverClient(controller) - mockEvents := NewMockDeliver_DeliverWithPrivateDataClient(controller) - - mockClient.EXPECT().DeliverWithPrivateData(gomock.Any(), gomock.Any()). - Return(mockEvents, nil) - - payload := &common.Payload{} - mockEvents.EXPECT().Send(gomock.Any()). - Do(func(in *common.Envelope) { - test.AssertUnmarshal(t, in.GetPayload(), payload) - }). - Return(nil). - Times(1) - mockEvents.EXPECT().Recv(). - Return(nil, errors.New("fake")). - AnyTimes() - - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() - - network := AssertNewTestNetwork(t, "NETWORK", WithDeliverClient(mockClient)) - _, err := network.BlockAndPrivateDataEvents(ctx) - require.NoError(t, err) - - AssertValidBlockEventRequestHeader(t, payload, network.Name()) - actual := &orderer.SeekInfo{} - test.AssertUnmarshal(t, payload.GetData(), actual) - - expected := &orderer.SeekInfo{ - Start: &orderer.SeekPosition{ - Type: &orderer.SeekPosition_NextCommit{ - NextCommit: &orderer.SeekNextCommit{}, + for testName, testCase := range map[string]struct { + options []BlockEventsOption + expected *orderer.SeekInfo + }{ + "Sends valid request with default start position": { + options: nil, + expected: &orderer.SeekInfo{ + Start: &orderer.SeekPosition{ + Type: &orderer.SeekPosition_NextCommit{ + NextCommit: &orderer.SeekNextCommit{}, + }, }, + Stop: seekLargestBlockNumber(), }, - Stop: seekLargestBlockNumber(), - } - - test.AssertProtoEqual(t, expected, actual) - }) - - t.Run("Sends valid request with specified start block number", func(t *testing.T) { - controller := gomock.NewController(t) - mockClient := NewMockDeliverClient(controller) - mockEvents := NewMockDeliver_DeliverWithPrivateDataClient(controller) - - mockClient.EXPECT().DeliverWithPrivateData(gomock.Any(), gomock.Any()). - Return(mockEvents, nil) - - payload := &common.Payload{} - mockEvents.EXPECT().Send(gomock.Any()). - Do(func(in *common.Envelope) { - test.AssertUnmarshal(t, in.GetPayload(), payload) - }). - Return(nil). - Times(1) - mockEvents.EXPECT().Recv(). - Return(nil, errors.New("fake")). - AnyTimes() - - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() - - network := AssertNewTestNetwork(t, "NETWORK", WithDeliverClient(mockClient)) - _, err := network.BlockAndPrivateDataEvents(ctx, WithStartBlock(418)) - require.NoError(t, err) - - AssertValidBlockEventRequestHeader(t, payload, network.Name()) - actual := &orderer.SeekInfo{} - test.AssertUnmarshal(t, payload.GetData(), actual) - - expected := &orderer.SeekInfo{ - Start: &orderer.SeekPosition{ - Type: &orderer.SeekPosition_Specified{ - Specified: &orderer.SeekSpecified{ - Number: 418, + }, + "Sends valid request with specified start block number": { + options: []BlockEventsOption{ + WithStartBlock(418), + }, + expected: &orderer.SeekInfo{ + Start: &orderer.SeekPosition{ + Type: &orderer.SeekPosition_Specified{ + Specified: &orderer.SeekSpecified{ + Number: 418, + }, }, }, + Stop: seekLargestBlockNumber(), }, - Stop: seekLargestBlockNumber(), - } - - test.AssertProtoEqual(t, expected, actual) - }) + }, + "Uses specified start block instead of unset checkpoint": { + options: []BlockEventsOption{ + WithStartBlock(418), + WithCheckpoint(new(InMemoryCheckpointer)), + }, + expected: &orderer.SeekInfo{ + Start: &orderer.SeekPosition{ + Type: &orderer.SeekPosition_Specified{ + Specified: &orderer.SeekSpecified{ + Number: 418, + }, + }, + }, + Stop: seekLargestBlockNumber(), + }, + }, + "Uses checkpoint block instead of specified start block": { + options: func() []BlockEventsOption { + checkpointer := new(InMemoryCheckpointer) + checkpointer.CheckpointBlock(500) + return []BlockEventsOption{ + WithStartBlock(418), + WithCheckpoint(checkpointer), + } + }(), + expected: &orderer.SeekInfo{ + Start: &orderer.SeekPosition{ + Type: &orderer.SeekPosition_Specified{ + Specified: &orderer.SeekSpecified{ + Number: 501, + }, + }, + }, + Stop: seekLargestBlockNumber(), + }, + }, + "Uses checkpoint block zero with set transaction ID instead of specified start block": { + options: func() []BlockEventsOption { + checkpointer := new(InMemoryCheckpointer) + checkpointer.CheckpointTransaction(0, "transctionId") + return []BlockEventsOption{ + WithStartBlock(418), + WithCheckpoint(checkpointer), + } + }(), + expected: &orderer.SeekInfo{ + Start: &orderer.SeekPosition{ + Type: &orderer.SeekPosition_Specified{ + Specified: &orderer.SeekSpecified{ + Number: 0, + }, + }, + }, + Stop: seekLargestBlockNumber(), + }, + }, + "Uses default start position with unset checkpoint and no start block": { + options: []BlockEventsOption{ + WithCheckpoint(new(InMemoryCheckpointer)), + }, + expected: &orderer.SeekInfo{ + Start: &orderer.SeekPosition{ + Type: &orderer.SeekPosition_NextCommit{ + NextCommit: &orderer.SeekNextCommit{}, + }, + }, + Stop: seekLargestBlockNumber(), + }, + }, + } { + t.Run(testName, func(t *testing.T) { + mockConnection := NewMockClientConnInterface(t) + mockStream := NewMockClientStream(t) + ExpectDeliverWithPrivateData(mockConnection, WithNewStreamResult(mockStream)) + + messages := make(chan *common.Envelope, 1) + ExpectSendMsg(mockStream, CaptureSendMsg(messages)) + mockStream.EXPECT().CloseSend().Maybe().Return(nil) + ExpectRecvMsg(mockStream).Maybe().Return(io.EOF) + + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + network := AssertNewTestNetwork(t, "NETWORK", WithClientConnection(mockConnection)) + _, err := network.BlockAndPrivateDataEvents(ctx, testCase.options...) + require.NoError(t, err) + + payload := &common.Payload{} + AssertUnmarshal(t, (<-messages).GetPayload(), payload) + AssertValidBlockEventRequestHeader(t, payload, network.Name()) + actual := &orderer.SeekInfo{} + AssertUnmarshal(t, payload.GetData(), actual) + + AssertProtoEqual(t, testCase.expected, actual) + }) + } t.Run("Closes event channel on receive error", func(t *testing.T) { - controller := gomock.NewController(t) - mockClient := NewMockDeliverClient(controller) - mockEvents := NewMockDeliver_DeliverWithPrivateDataClient(controller) - - mockClient.EXPECT().DeliverWithPrivateData(gomock.Any(), gomock.Any()). - Return(mockEvents, nil) + mockConnection := NewMockClientConnInterface(t) + mockStream := NewMockClientStream(t) + ExpectDeliverWithPrivateData(mockConnection, WithNewStreamResult(mockStream)) - mockEvents.EXPECT().Send(gomock.Any()). - Return(nil) - mockEvents.EXPECT().Recv(). - Return(nil, errors.New("fake")) + ExpectSendMsg(mockStream) + mockStream.EXPECT().CloseSend().Maybe().Return(nil) + ExpectRecvMsg(mockStream).Return(errors.New("fake")) ctx, cancel := context.WithCancel(context.Background()) defer cancel() - network := AssertNewTestNetwork(t, "NETWORK", WithDeliverClient(mockClient)) + network := AssertNewTestNetwork(t, "NETWORK", WithClientConnection(mockConnection)) receive, err := network.BlockAndPrivateDataEvents(ctx, WithStartBlock(418)) require.NoError(t, err) @@ -150,17 +186,7 @@ func TestBlockAndPrivateDataEvents(t *testing.T) { }) t.Run("Receives events", func(t *testing.T) { - controller := gomock.NewController(t) - mockClient := NewMockDeliverClient(controller) - mockEvents := NewMockDeliver_DeliverWithPrivateDataClient(controller) - - mockClient.EXPECT().DeliverWithPrivateData(gomock.Any(), gomock.Any()). - Return(mockEvents, nil) - - mockEvents.EXPECT().Send(gomock.Any()). - Return(nil) - - blocksAndPrivateData := []*peer.BlockAndPrivateData{ + expected := []*peer.BlockAndPrivateData{ { Block: &common.Block{ Header: &common.BlockHeader{ @@ -198,47 +224,46 @@ func TestBlockAndPrivateDataEvents(t *testing.T) { }, }, } - responseIndex := 0 - mockEvents.EXPECT().Recv(). - DoAndReturn(func() (*peer.DeliverResponse, error) { - if responseIndex >= len(blocksAndPrivateData) { - return nil, errors.New("fake") - } - response := &peer.DeliverResponse{ - Type: &peer.DeliverResponse_BlockAndPrivateData{ - BlockAndPrivateData: blocksAndPrivateData[responseIndex], - }, - } - responseIndex++ - return response, nil - }). - AnyTimes() + + mockConnection := NewMockClientConnInterface(t) + mockStream := NewMockClientStream(t) + ExpectDeliverWithPrivateData(mockConnection, WithNewStreamResult(mockStream)) + + ExpectSendMsg(mockStream) + mockStream.EXPECT().CloseSend().Maybe().Return(nil) + + var responses []*peer.DeliverResponse + for _, block := range expected { + responses = append(responses, &peer.DeliverResponse{ + Type: &peer.DeliverResponse_BlockAndPrivateData{ + BlockAndPrivateData: block, + }, + }) + } + ExpectRecvMsg(mockStream, WithRecvMsgs(responses...)) ctx, cancel := context.WithCancel(context.Background()) defer cancel() - network := AssertNewTestNetwork(t, "NETWORK", WithDeliverClient(mockClient)) + network := AssertNewTestNetwork(t, "NETWORK", WithClientConnection(mockConnection)) receive, err := network.BlockAndPrivateDataEvents(ctx) require.NoError(t, err) - for _, event := range blocksAndPrivateData { + for _, event := range expected { actual := <-receive - test.AssertProtoEqual(t, event, actual) + AssertProtoEqual(t, event, actual) } }) t.Run("Closes event channel on non-block message", func(t *testing.T) { - controller := gomock.NewController(t) - mockClient := NewMockDeliverClient(controller) - mockEvents := NewMockDeliver_DeliverWithPrivateDataClient(controller) + mockConnection := NewMockClientConnInterface(t) + mockStream := NewMockClientStream(t) + ExpectDeliverWithPrivateData(mockConnection, WithNewStreamResult(mockStream)) - mockClient.EXPECT().DeliverWithPrivateData(gomock.Any(), gomock.Any()). - Return(mockEvents, nil) + ExpectSendMsg(mockStream) + mockStream.EXPECT().CloseSend().Maybe().Return(nil) - mockEvents.EXPECT().Send(gomock.Any()). - Return(nil) - - blockAndPrivateData := &peer.BlockAndPrivateData{ + block := &peer.BlockAndPrivateData{ Block: &common.Block{ Header: &common.BlockHeader{ Number: 1, @@ -259,7 +284,7 @@ func TestBlockAndPrivateDataEvents(t *testing.T) { responses := []*peer.DeliverResponse{ { Type: &peer.DeliverResponse_BlockAndPrivateData{ - BlockAndPrivateData: blockAndPrivateData, + BlockAndPrivateData: block, }, }, { @@ -269,105 +294,78 @@ func TestBlockAndPrivateDataEvents(t *testing.T) { }, { Type: &peer.DeliverResponse_BlockAndPrivateData{ - BlockAndPrivateData: blockAndPrivateData, + BlockAndPrivateData: block, }, }, } - responseIndex := 0 - mockEvents.EXPECT().Recv(). - DoAndReturn(func() (*peer.DeliverResponse, error) { - if responseIndex >= len(responses) { - return nil, errors.New("fake") - } - response := responses[responseIndex] - responseIndex++ - return response, nil - }). - AnyTimes() + ExpectRecvMsg(mockStream, WithRecvMsgs(responses...)) ctx, cancel := context.WithCancel(context.Background()) defer cancel() - network := AssertNewTestNetwork(t, "NETWORK", WithDeliverClient(mockClient)) + network := AssertNewTestNetwork(t, "NETWORK", WithClientConnection(mockConnection)) receive, err := network.BlockAndPrivateDataEvents(ctx) require.NoError(t, err) expected := []*peer.BlockAndPrivateData{ - blockAndPrivateData, + block, nil, nil, } for _, event := range expected { actual := <-receive - test.AssertProtoEqual(t, event, actual) + AssertProtoEqual(t, event, actual) } }) t.Run("Uses specified gRPC call options", func(t *testing.T) { - var actual []grpc.CallOption expected := grpc.WaitForReady(true) - controller := gomock.NewController(t) - mockClient := NewMockDeliverClient(controller) - mockEvents := NewMockDeliver_DeliverClient(controller) + mockConnection := NewMockClientConnInterface(t) + mockStream := NewMockClientStream(t) + options := make(chan []grpc.CallOption, 1) + ExpectDeliverWithPrivateData(mockConnection, CaptureNewStreamOptions(options), WithNewStreamResult(mockStream)) - mockClient.EXPECT().DeliverWithPrivateData(gomock.Any(), gomock.Any()). - Do(func(_ context.Context, opts ...grpc.CallOption) { - actual = opts - }). - Return(mockEvents, nil). - Times(1) - - mockEvents.EXPECT().Send(gomock.Any()). - Return(nil). - AnyTimes() - mockEvents.EXPECT().Recv(). - Return(nil, errors.New("fake")). - AnyTimes() + ExpectSendMsg(mockStream) + mockStream.EXPECT().CloseSend().Maybe().Return(nil) + ExpectRecvMsg(mockStream).Maybe().Return(io.EOF) ctx, cancel := context.WithCancel(context.Background()) defer cancel() - network := AssertNewTestNetwork(t, "NETWORK", WithDeliverClient(mockClient)) + network := AssertNewTestNetwork(t, "NETWORK", WithClientConnection(mockConnection)) request, err := network.NewBlockAndPrivateDataEventsRequest() require.NoError(t, err, "NewBlockAndPrivateDataEventsRequest") _, err = request.Events(ctx, expected) require.NoError(t, err, "Events") - require.Contains(t, actual, expected, "CallOptions") + require.Contains(t, (<-options), expected, "CallOptions") }) t.Run("Sends request with TLS client certificate hash", func(t *testing.T) { expected := []byte("TLS_CLIENT_CERTIFICATE_HASH") - controller := gomock.NewController(t) - mockClient := NewMockDeliverClient(controller) - mockEvents := NewMockDeliver_DeliverClient(controller) - - mockClient.EXPECT().DeliverWithPrivateData(gomock.Any(), gomock.Any()). - Return(mockEvents, nil) + mockConnection := NewMockClientConnInterface(t) + mockStream := NewMockClientStream(t) + ExpectDeliverWithPrivateData(mockConnection, WithNewStreamResult(mockStream)) - payload := &common.Payload{} - mockEvents.EXPECT().Send(gomock.Any()). - Do(func(in *common.Envelope) { - test.AssertUnmarshal(t, in.GetPayload(), payload) - }). - Return(nil). - Times(1) - mockEvents.EXPECT().Recv(). - Return(nil, errors.New("fake")). - AnyTimes() + requests := make(chan *common.Envelope, 1) + ExpectSendMsg(mockStream, CaptureSendMsg(requests)) + mockStream.EXPECT().CloseSend().Maybe().Return(nil) + ExpectRecvMsg(mockStream).Maybe().Return(io.EOF) ctx, cancel := context.WithCancel(context.Background()) defer cancel() - network := AssertNewTestNetwork(t, "NETWORK", WithDeliverClient(mockClient), WithTLSClientCertificateHash(expected)) + network := AssertNewTestNetwork(t, "NETWORK", WithClientConnection(mockConnection), WithTLSClientCertificateHash(expected)) _, err := network.BlockAndPrivateDataEvents(ctx) require.NoError(t, err) + payload := &common.Payload{} + AssertUnmarshal(t, (<-requests).GetPayload(), payload) channelHeader := &common.ChannelHeader{} - test.AssertUnmarshal(t, payload.GetHeader().GetChannelHeader(), channelHeader) + AssertUnmarshal(t, payload.GetHeader().GetChannelHeader(), channelHeader) require.Equal(t, expected, channelHeader.GetTlsCertHash()) }) diff --git a/pkg/client/chaincodeevents_test.go b/pkg/client/chaincodeevents_test.go index 8c54d2d3a..f98b5291c 100644 --- a/pkg/client/chaincodeevents_test.go +++ b/pkg/client/chaincodeevents_test.go @@ -6,17 +6,17 @@ package client import ( "context" "errors" + "io" "testing" - "github.com/hyperledger/fabric-gateway/pkg/internal/test" "github.com/hyperledger/fabric-protos-go-apiv2/gateway" "github.com/hyperledger/fabric-protos-go-apiv2/orderer" "github.com/hyperledger/fabric-protos-go-apiv2/peer" "github.com/stretchr/testify/require" - "go.uber.org/mock/gomock" "google.golang.org/grpc" "google.golang.org/grpc/codes" "google.golang.org/grpc/status" + "google.golang.org/protobuf/proto" ) func TestChaincodeEvents(t *testing.T) { @@ -42,14 +42,14 @@ func TestChaincodeEvents(t *testing.T) { t.Run("Returns connect error", func(t *testing.T) { expected := NewStatusError(t, codes.Aborted, "CHAINCODE_EVENTS_ERROR") - mockClient := NewMockGatewayClient(gomock.NewController(t)) - mockClient.EXPECT().ChaincodeEvents(gomock.Any(), gomock.Any()). - Return(nil, expected) + + mockConnection := NewMockClientConnInterface(t) + ExpectChaincodeEvents(mockConnection, WithNewStreamError(expected)) ctx, cancel := context.WithCancel(context.Background()) defer cancel() - network := AssertNewTestNetwork(t, "NETWORK", WithGatewayClient(mockClient)) + network := AssertNewTestNetwork(t, "NETWORK", WithClientConnection(mockConnection)) _, err := network.ChaincodeEvents(ctx, "CHAINCODE") require.Equal(t, status.Code(expected), status.Code(err), "status code") @@ -57,396 +57,189 @@ func TestChaincodeEvents(t *testing.T) { require.ErrorContains(t, err, expected.Error(), "message") }) - t.Run("Sends valid request with default start position", func(t *testing.T) { - controller := gomock.NewController(t) - mockClient := NewMockGatewayClient(controller) - mockEvents := NewMockGateway_ChaincodeEventsClient(controller) - - var actual *gateway.ChaincodeEventsRequest - mockClient.EXPECT().ChaincodeEvents(gomock.Any(), gomock.Any()). - Do(func(_ context.Context, in *gateway.SignedChaincodeEventsRequest, _ ...grpc.CallOption) { - request := &gateway.ChaincodeEventsRequest{} - test.AssertUnmarshal(t, in.GetRequest(), request) - actual = request - }). - Return(mockEvents, nil). - Times(1) - - mockEvents.EXPECT().Recv(). - Return(nil, errors.New("fake")). - AnyTimes() - - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() - - network := AssertNewTestNetwork(t, "NETWORK", WithGatewayClient(mockClient)) - _, err := network.ChaincodeEvents(ctx, "CHAINCODE") - require.NoError(t, err) - - creator, err := network.signingID.Creator() - require.NoError(t, err) - - expected := &gateway.ChaincodeEventsRequest{ - ChannelId: "NETWORK", - ChaincodeId: "CHAINCODE", - Identity: creator, - StartPosition: &orderer.SeekPosition{ - Type: &orderer.SeekPosition_NextCommit{ - NextCommit: &orderer.SeekNextCommit{}, + for testName, testCase := range map[string]struct { + options []ChaincodeEventsOption + expected *gateway.ChaincodeEventsRequest + }{ + "Sends valid request with default start position": { + options: nil, + expected: &gateway.ChaincodeEventsRequest{ + StartPosition: &orderer.SeekPosition{ + Type: &orderer.SeekPosition_NextCommit{ + NextCommit: &orderer.SeekNextCommit{}, + }, }, }, - } - test.AssertProtoEqual(t, expected, actual) - }) - - t.Run("Sends valid request with specified start block number", func(t *testing.T) { - controller := gomock.NewController(t) - mockClient := NewMockGatewayClient(controller) - mockEvents := NewMockGateway_ChaincodeEventsClient(controller) - - var actual *gateway.ChaincodeEventsRequest - mockClient.EXPECT().ChaincodeEvents(gomock.Any(), gomock.Any()). - Do(func(_ context.Context, in *gateway.SignedChaincodeEventsRequest, _ ...grpc.CallOption) { - request := &gateway.ChaincodeEventsRequest{} - test.AssertUnmarshal(t, in.GetRequest(), request) - actual = request - }). - Return(mockEvents, nil). - Times(1) - - mockEvents.EXPECT().Recv(). - Return(nil, errors.New("fake")). - AnyTimes() - - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() - - network := AssertNewTestNetwork(t, "NETWORK", WithGatewayClient(mockClient)) - _, err := network.ChaincodeEvents(ctx, "CHAINCODE", WithStartBlock(418)) - require.NoError(t, err) - - creator, err := network.signingID.Creator() - require.NoError(t, err) - - expected := &gateway.ChaincodeEventsRequest{ - ChannelId: "NETWORK", - ChaincodeId: "CHAINCODE", - Identity: creator, - StartPosition: &orderer.SeekPosition{ - Type: &orderer.SeekPosition_Specified{ - Specified: &orderer.SeekSpecified{ - Number: 418, + }, + "Sends valid request with specified start block number": { + options: []ChaincodeEventsOption{ + WithStartBlock(418), + }, + expected: &gateway.ChaincodeEventsRequest{ + StartPosition: &orderer.SeekPosition{ + Type: &orderer.SeekPosition_Specified{ + Specified: &orderer.SeekSpecified{ + Number: 418, + }, }, }, }, - } - test.AssertProtoEqual(t, expected, actual) - }) - - t.Run("Sends valid request with specified start block number and fresh checkpointer", func(t *testing.T) { - controller := gomock.NewController(t) - mockClient := NewMockGatewayClient(controller) - mockEvents := NewMockGateway_ChaincodeEventsClient(controller) - - var actual *gateway.ChaincodeEventsRequest - mockClient.EXPECT().ChaincodeEvents(gomock.Any(), gomock.Any()). - Do(func(_ context.Context, in *gateway.SignedChaincodeEventsRequest, _ ...grpc.CallOption) { - request := &gateway.ChaincodeEventsRequest{} - test.AssertUnmarshal(t, in.GetRequest(), request) - actual = request - }). - Return(mockEvents, nil). - Times(1) - - mockEvents.EXPECT().Recv(). - Return(nil, errors.New("fake")). - AnyTimes() - - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() - - network := AssertNewTestNetwork(t, "NETWORK", WithGatewayClient(mockClient)) - - checkpointer := new(InMemoryCheckpointer) - - _, err := network.ChaincodeEvents(ctx, "CHAINCODE", WithStartBlock(418), WithCheckpoint(checkpointer)) - require.NoError(t, err) - - creator, err := network.signingID.Creator() - require.NoError(t, err) - - expected := &gateway.ChaincodeEventsRequest{ - ChannelId: "NETWORK", - ChaincodeId: "CHAINCODE", - Identity: creator, - StartPosition: &orderer.SeekPosition{ - Type: &orderer.SeekPosition_Specified{ - Specified: &orderer.SeekSpecified{ - Number: 418, + }, + "Sends valid request with specified start block number and fresh checkpointer": { + options: []ChaincodeEventsOption{ + WithStartBlock(418), + WithCheckpoint(new(InMemoryCheckpointer)), + }, + expected: &gateway.ChaincodeEventsRequest{ + StartPosition: &orderer.SeekPosition{ + Type: &orderer.SeekPosition_Specified{ + Specified: &orderer.SeekSpecified{ + Number: 418, + }, }, }, }, - } - test.AssertProtoEqual(t, expected, actual) - }) - t.Run("Sends valid request with specified start block and checkpoint block", func(t *testing.T) { - controller := gomock.NewController(t) - mockClient := NewMockGatewayClient(controller) - mockEvents := NewMockGateway_ChaincodeEventsClient(controller) - - var actual *gateway.ChaincodeEventsRequest - mockClient.EXPECT().ChaincodeEvents(gomock.Any(), gomock.Any()). - Do(func(_ context.Context, in *gateway.SignedChaincodeEventsRequest, _ ...grpc.CallOption) { - request := &gateway.ChaincodeEventsRequest{} - test.AssertUnmarshal(t, in.GetRequest(), request) - actual = request - }). - Return(mockEvents, nil). - Times(1) - - mockEvents.EXPECT().Recv(). - Return(nil, errors.New("fake")). - AnyTimes() - - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() - - network := AssertNewTestNetwork(t, "NETWORK", WithGatewayClient(mockClient)) - - checkpointer := new(InMemoryCheckpointer) - checkpointer.CheckpointBlock(uint64(500)) - _, err := network.ChaincodeEvents(ctx, "CHAINCODE", WithStartBlock(418), WithCheckpoint(checkpointer)) - require.NoError(t, err) - - creator, err := network.signingID.Creator() - require.NoError(t, err) - - expected := &gateway.ChaincodeEventsRequest{ - ChannelId: "NETWORK", - ChaincodeId: "CHAINCODE", - Identity: creator, - StartPosition: &orderer.SeekPosition{ - Type: &orderer.SeekPosition_Specified{ - Specified: &orderer.SeekSpecified{ - Number: 501, + }, + "Sends valid request with specified start block and checkpoint block": { + options: func() []ChaincodeEventsOption { + checkpointer := new(InMemoryCheckpointer) + checkpointer.CheckpointBlock(500) + return []ChaincodeEventsOption{ + WithStartBlock(418), + WithCheckpoint(checkpointer), + } + }(), + expected: &gateway.ChaincodeEventsRequest{ + StartPosition: &orderer.SeekPosition{ + Type: &orderer.SeekPosition_Specified{ + Specified: &orderer.SeekSpecified{ + Number: 501, + }, }, }, }, - } - test.AssertProtoEqual(t, expected, actual) - }) - - t.Run("Sends valid request with specified start block and checkpoint transaction ID", func(t *testing.T) { - controller := gomock.NewController(t) - mockClient := NewMockGatewayClient(controller) - mockEvents := NewMockGateway_ChaincodeEventsClient(controller) - - var actual *gateway.ChaincodeEventsRequest - mockClient.EXPECT().ChaincodeEvents(gomock.Any(), gomock.Any()). - Do(func(_ context.Context, in *gateway.SignedChaincodeEventsRequest, _ ...grpc.CallOption) { - request := &gateway.ChaincodeEventsRequest{} - test.AssertUnmarshal(t, in.GetRequest(), request) - actual = request - }). - Return(mockEvents, nil). - Times(1) - - mockEvents.EXPECT().Recv(). - Return(nil, errors.New("fake")). - AnyTimes() - - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() - - network := AssertNewTestNetwork(t, "NETWORK", WithGatewayClient(mockClient)) - - checkpointer := new(InMemoryCheckpointer) - checkpointer.CheckpointTransaction(uint64(500), "txn1") - _, err := network.ChaincodeEvents(ctx, "CHAINCODE", WithStartBlock(418), WithCheckpoint(checkpointer)) - require.NoError(t, err) - - creator, err := network.signingID.Creator() - require.NoError(t, err) - - expected := &gateway.ChaincodeEventsRequest{ - ChannelId: "NETWORK", - ChaincodeId: "CHAINCODE", - Identity: creator, - StartPosition: &orderer.SeekPosition{ - Type: &orderer.SeekPosition_Specified{ - Specified: &orderer.SeekSpecified{ - Number: 500, + }, + "Sends valid request with specified start block and checkpoint transaction ID": { + options: func() []ChaincodeEventsOption { + checkpointer := new(InMemoryCheckpointer) + checkpointer.CheckpointTransaction(500, "txn1") + return []ChaincodeEventsOption{ + WithStartBlock(418), + WithCheckpoint(checkpointer), + } + }(), + expected: &gateway.ChaincodeEventsRequest{ + StartPosition: &orderer.SeekPosition{ + Type: &orderer.SeekPosition_Specified{ + Specified: &orderer.SeekSpecified{ + Number: 500, + }, }, }, + AfterTransactionId: "txn1", }, - AfterTransactionId: "txn1", - } - test.AssertProtoEqual(t, expected, actual) - }) - - t.Run("Sends valid request with no start block and fresh checkpointer", func(t *testing.T) { - controller := gomock.NewController(t) - mockClient := NewMockGatewayClient(controller) - mockEvents := NewMockGateway_ChaincodeEventsClient(controller) - - var actual *gateway.ChaincodeEventsRequest - mockClient.EXPECT().ChaincodeEvents(gomock.Any(), gomock.Any()). - Do(func(_ context.Context, in *gateway.SignedChaincodeEventsRequest, _ ...grpc.CallOption) { - request := &gateway.ChaincodeEventsRequest{} - test.AssertUnmarshal(t, in.GetRequest(), request) - actual = request - }). - Return(mockEvents, nil). - Times(1) - - mockEvents.EXPECT().Recv(). - Return(nil, errors.New("fake")). - AnyTimes() - - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() - - network := AssertNewTestNetwork(t, "NETWORK", WithGatewayClient(mockClient)) - - checkpointer := new(InMemoryCheckpointer) - _, err := network.ChaincodeEvents(ctx, "CHAINCODE", WithCheckpoint(checkpointer)) - require.NoError(t, err) - - creator, err := network.signingID.Creator() - require.NoError(t, err) - expected := &gateway.ChaincodeEventsRequest{ - ChannelId: "NETWORK", - ChaincodeId: "CHAINCODE", - Identity: creator, - StartPosition: &orderer.SeekPosition{ - Type: &orderer.SeekPosition_NextCommit{ - NextCommit: &orderer.SeekNextCommit{}, + }, + "Sends valid request with no start block and fresh checkpointer": { + options: []ChaincodeEventsOption{ + WithCheckpoint(new(InMemoryCheckpointer)), + }, + expected: &gateway.ChaincodeEventsRequest{ + StartPosition: &orderer.SeekPosition{ + Type: &orderer.SeekPosition_NextCommit{ + NextCommit: &orderer.SeekNextCommit{}, + }, }, }, - } - test.AssertProtoEqual(t, expected, actual) - }) - - t.Run("Sends valid request with no start block and checkpoint transaction ID", func(t *testing.T) { - controller := gomock.NewController(t) - mockClient := NewMockGatewayClient(controller) - mockEvents := NewMockGateway_ChaincodeEventsClient(controller) - - var actual *gateway.ChaincodeEventsRequest - mockClient.EXPECT().ChaincodeEvents(gomock.Any(), gomock.Any()). - Do(func(_ context.Context, in *gateway.SignedChaincodeEventsRequest, _ ...grpc.CallOption) { - request := &gateway.ChaincodeEventsRequest{} - test.AssertUnmarshal(t, in.GetRequest(), request) - actual = request - }). - Return(mockEvents, nil). - Times(1) - - mockEvents.EXPECT().Recv(). - Return(nil, errors.New("fake")). - AnyTimes() - - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() - - network := AssertNewTestNetwork(t, "NETWORK", WithGatewayClient(mockClient)) - - checkpointer := new(InMemoryCheckpointer) - checkpointer.CheckpointTransaction(uint64(500), "txn1") - - _, err := network.ChaincodeEvents(ctx, "CHAINCODE", WithCheckpoint(checkpointer)) - require.NoError(t, err) - - creator, err := network.signingID.Creator() - require.NoError(t, err) - expected := &gateway.ChaincodeEventsRequest{ - ChannelId: "NETWORK", - ChaincodeId: "CHAINCODE", - Identity: creator, - StartPosition: &orderer.SeekPosition{ - Type: &orderer.SeekPosition_Specified{ - Specified: &orderer.SeekSpecified{ - Number: 500, + }, + "Sends valid request with no start block and checkpoint transaction ID": { + options: func() []ChaincodeEventsOption { + checkpointer := new(InMemoryCheckpointer) + checkpointer.CheckpointTransaction(500, "txn1") + return []ChaincodeEventsOption{ + WithCheckpoint(checkpointer), + } + }(), + expected: &gateway.ChaincodeEventsRequest{ + StartPosition: &orderer.SeekPosition{ + Type: &orderer.SeekPosition_Specified{ + Specified: &orderer.SeekSpecified{ + Number: 500, + }, }, }, + AfterTransactionId: "txn1", }, - AfterTransactionId: "txn1", - } - test.AssertProtoEqual(t, expected, actual) - }) - - t.Run("Sends valid request with with start block and checkpoint chaincode event", func(t *testing.T) { - controller := gomock.NewController(t) - mockClient := NewMockGatewayClient(controller) - mockEvents := NewMockGateway_ChaincodeEventsClient(controller) - - var actual *gateway.ChaincodeEventsRequest - mockClient.EXPECT().ChaincodeEvents(gomock.Any(), gomock.Any()). - Do(func(_ context.Context, in *gateway.SignedChaincodeEventsRequest, _ ...grpc.CallOption) { - request := &gateway.ChaincodeEventsRequest{} - test.AssertUnmarshal(t, in.GetRequest(), request) - actual = request - }). - Return(mockEvents, nil). - Times(1) - - mockEvents.EXPECT().Recv(). - Return(nil, errors.New("fake")). - AnyTimes() - - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() - - network := AssertNewTestNetwork(t, "NETWORK", WithGatewayClient(mockClient)) - - checkpointer := new(InMemoryCheckpointer) - event := &ChaincodeEvent{ - BlockNumber: 1, - ChaincodeName: "CHAINCODE", - EventName: "EVENT_1", - Payload: []byte("PAYLOAD_1"), - TransactionID: "TRANSACTION_1", - } - - checkpointer.CheckpointChaincodeEvent(event) - - _, err := network.ChaincodeEvents(ctx, "CHAINCODE", WithStartBlock(418), WithCheckpoint(checkpointer)) - require.NoError(t, err) - - creator, err := network.signingID.Creator() - require.NoError(t, err) - expected := &gateway.ChaincodeEventsRequest{ - ChannelId: "NETWORK", - ChaincodeId: "CHAINCODE", - Identity: creator, - StartPosition: &orderer.SeekPosition{ - Type: &orderer.SeekPosition_Specified{ - Specified: &orderer.SeekSpecified{ - Number: event.BlockNumber, + }, + "Sends valid request with with start block and checkpoint chaincode event": { + options: func() []ChaincodeEventsOption { + checkpointer := new(InMemoryCheckpointer) + event := &ChaincodeEvent{ + BlockNumber: 1, + TransactionID: "TRANSACTION_1", + } + checkpointer.CheckpointChaincodeEvent(event) + return []ChaincodeEventsOption{ + WithCheckpoint(checkpointer), + } + }(), + expected: &gateway.ChaincodeEventsRequest{ + StartPosition: &orderer.SeekPosition{ + Type: &orderer.SeekPosition_Specified{ + Specified: &orderer.SeekSpecified{ + Number: 1, + }, }, }, + AfterTransactionId: "TRANSACTION_1", }, - AfterTransactionId: event.TransactionID, - } - test.AssertProtoEqual(t, expected, actual) - }) + }, + } { + t.Run(testName, func(t *testing.T) { + mockConnection := NewMockClientConnInterface(t) + mockStream := NewMockClientStream(t) + ExpectChaincodeEvents(mockConnection, WithNewStreamResult(mockStream)) + + messages := make(chan *gateway.SignedChaincodeEventsRequest, 1) + ExpectSendMsg(mockStream, CaptureSendMsg(messages)) + mockStream.EXPECT().CloseSend().Return(nil) + ExpectRecvMsg(mockStream).Maybe().Return(io.EOF) + + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + network := AssertNewTestNetwork(t, "NETWORK", WithClientConnection(mockConnection)) + _, err := network.ChaincodeEvents(ctx, "CHAINCODE", testCase.options...) + require.NoError(t, err) + + creator, err := network.signingID.Creator() + require.NoError(t, err) + + expected := &gateway.ChaincodeEventsRequest{ + ChannelId: "NETWORK", + ChaincodeId: "CHAINCODE", + Identity: creator, + } + proto.Merge(expected, testCase.expected) + actual := &gateway.ChaincodeEventsRequest{} + AssertUnmarshal(t, (<-messages).Request, actual) + AssertProtoEqual(t, expected, actual) + }) + } t.Run("Closes event channel on receive error", func(t *testing.T) { - controller := gomock.NewController(t) - mockClient := NewMockGatewayClient(controller) - mockEvents := NewMockGateway_ChaincodeEventsClient(controller) + mockConnection := NewMockClientConnInterface(t) + mockStream := NewMockClientStream(t) + ExpectChaincodeEvents(mockConnection, WithNewStreamResult(mockStream)) - mockClient.EXPECT().ChaincodeEvents(gomock.Any(), gomock.Any()). - Return(mockEvents, nil) - - mockEvents.EXPECT().Recv(). - Return(nil, errors.New("fake")). - AnyTimes() + ExpectSendMsg(mockStream) + mockStream.EXPECT().CloseSend().Return(nil) + ExpectRecvMsg(mockStream).Maybe().Return(errors.New("fake")) ctx, cancel := context.WithCancel(context.Background()) defer cancel() - network := AssertNewTestNetwork(t, "NETWORK", WithGatewayClient(mockClient)) + network := AssertNewTestNetwork(t, "NETWORK", WithClientConnection(mockConnection)) + receive, err := network.ChaincodeEvents(ctx, "CHAINCODE") require.NoError(t, err) @@ -456,13 +249,6 @@ func TestChaincodeEvents(t *testing.T) { }) t.Run("Receives events", func(t *testing.T) { - controller := gomock.NewController(t) - mockClient := NewMockGatewayClient(controller) - mockEvents := NewMockGateway_ChaincodeEventsClient(controller) - - mockClient.EXPECT().ChaincodeEvents(gomock.Any(), gomock.Any()). - Return(mockEvents, nil) - expected := []*ChaincodeEvent{ { BlockNumber: 1, @@ -487,26 +273,24 @@ func TestChaincodeEvents(t *testing.T) { }, } - responses := []*gateway.ChaincodeEventsResponse{ + mockConnection := NewMockClientConnInterface(t) + mockStream := NewMockClientStream(t) + options := make(chan []grpc.CallOption, 1) + ExpectChaincodeEvents(mockConnection, CaptureNewStreamOptions(options), WithNewStreamResult(mockStream)) + + messages := make(chan *gateway.SignedChaincodeEventsRequest, 1) + ExpectSendMsg(mockStream, CaptureSendMsg(messages)) + mockStream.EXPECT().CloseSend().Return(nil) + ExpectRecvMsg(mockStream, WithRecvMsgs( newChaincodeEventsResponse(expected[0:2]), newChaincodeEventsResponse(expected[2:]), - } - responseIndex := 0 - mockEvents.EXPECT().Recv(). - DoAndReturn(func() (*gateway.ChaincodeEventsResponse, error) { - if responseIndex >= len(responses) { - return nil, errors.New("fake") - } - response := responses[responseIndex] - responseIndex++ - return response, nil - }). - AnyTimes() + )) ctx, cancel := context.WithCancel(context.Background()) defer cancel() - network := AssertNewTestNetwork(t, "NETWORK", WithGatewayClient(mockClient)) + network := AssertNewTestNetwork(t, "NETWORK", WithClientConnection(mockConnection)) + receive, err := network.ChaincodeEvents(ctx, "CHAINCODE") require.NoError(t, err) @@ -517,34 +301,29 @@ func TestChaincodeEvents(t *testing.T) { }) t.Run("Uses specified gRPC call options", func(t *testing.T) { - var actual []grpc.CallOption expected := grpc.WaitForReady(true) - controller := gomock.NewController(t) - mockClient := NewMockGatewayClient(controller) - mockEvents := NewMockGateway_ChaincodeEventsClient(controller) - - mockClient.EXPECT().ChaincodeEvents(gomock.Any(), gomock.Any(), gomock.Any()). - Do(func(_ context.Context, _ *gateway.SignedChaincodeEventsRequest, opts ...grpc.CallOption) { - actual = opts - }). - Return(mockEvents, nil). - Times(1) + mockConnection := NewMockClientConnInterface(t) + mockStream := NewMockClientStream(t) + options := make(chan []grpc.CallOption, 1) + ExpectChaincodeEvents(mockConnection, CaptureNewStreamOptions(options), WithNewStreamResult(mockStream)) - mockEvents.EXPECT().Recv(). - Return(nil, errors.New("fake")). - AnyTimes() + messages := make(chan *gateway.SignedChaincodeEventsRequest, 1) + ExpectSendMsg(mockStream, CaptureSendMsg(messages)) + mockStream.EXPECT().CloseSend().Return(nil) + ExpectRecvMsg(mockStream).Maybe().Return(io.EOF) ctx, cancel := context.WithCancel(context.Background()) defer cancel() - network := AssertNewTestNetwork(t, "NETWORK", WithGatewayClient(mockClient)) + network := AssertNewTestNetwork(t, "NETWORK", WithClientConnection(mockConnection)) + request, err := network.NewChaincodeEventsRequest("CHAINCODE") require.NoError(t, err, "NewChaincodeEventsRequest") _, err = request.Events(ctx, expected) require.NoError(t, err, "Events") - require.Contains(t, actual, expected, "CallOptions") + require.Contains(t, <-options, expected, "CallOptions") }) } diff --git a/pkg/client/evaluate_test.go b/pkg/client/evaluate_test.go index c034ae9a3..2894c1fa7 100644 --- a/pkg/client/evaluate_test.go +++ b/pkg/client/evaluate_test.go @@ -6,43 +6,22 @@ package client import ( "context" "testing" - "time" - "github.com/hyperledger/fabric-gateway/pkg/internal/test" "github.com/hyperledger/fabric-protos-go-apiv2/gateway" - "github.com/hyperledger/fabric-protos-go-apiv2/peer" "github.com/stretchr/testify/require" - "go.uber.org/mock/gomock" "google.golang.org/grpc" "google.golang.org/grpc/codes" "google.golang.org/grpc/status" - "google.golang.org/protobuf/runtime/protoiface" ) -func NewStatusError(t *testing.T, code codes.Code, message string, details ...protoiface.MessageV1) error { - s, err := status.New(code, message).WithDetails(details...) - require.NoError(t, err) - - return s.Err() -} - func TestEvaluateTransaction(t *testing.T) { - newEvaluateResponse := func(value []byte) *gateway.EvaluateResponse { - return &gateway.EvaluateResponse{ - Result: &peer.Response{ - Payload: []byte(value), - }, - } - } - t.Run("Returns evaluate error", func(t *testing.T) { expected := NewStatusError(t, codes.Aborted, "EVALUATE_ERROR") - mockClient := NewMockGatewayClient(gomock.NewController(t)) - mockClient.EXPECT().Evaluate(gomock.Any(), gomock.Any()). - Return(nil, expected) - contract := AssertNewTestContract(t, "chaincode", WithGatewayClient(mockClient)) + mockConnection := NewMockClientConnInterface(t) + ExpectEvaluate(mockConnection, WithInvokeError(expected)) + contract := AssertNewTestContract(t, "chaincode", WithClientConnection(mockConnection)) _, err := contract.EvaluateTransaction("transaction") require.ErrorIs(t, err, expected, "error type: %T", err) @@ -66,11 +45,10 @@ func TestEvaluateTransaction(t *testing.T) { } { t.Run(name, func(t *testing.T) { expected := []byte("TRANSACTION_RESULT") - mockClient := NewMockGatewayClient(gomock.NewController(t)) - mockClient.EXPECT().Evaluate(gomock.Any(), gomock.Any()). - Return(newEvaluateResponse(expected), nil) - contract := AssertNewTestContract(t, "chaincode", WithGatewayClient(mockClient)) + mockConnection := NewMockClientConnInterface(t) + ExpectEvaluate(mockConnection, WithEvaluateResponse(expected)) + contract := AssertNewTestContract(t, "chaincode", WithClientConnection(mockConnection)) actual, err := testCase.run(t, contract) require.NoError(t, err) @@ -78,231 +56,196 @@ func TestEvaluateTransaction(t *testing.T) { require.EqualValues(t, expected, actual) }) } + t.Run("Includes channel name in proposal", func(t *testing.T) { - var actual string - mockClient := NewMockGatewayClient(gomock.NewController(t)) - mockClient.EXPECT().Evaluate(gomock.Any(), gomock.Any()). - Do(func(_ context.Context, in *gateway.EvaluateRequest, _ ...grpc.CallOption) { - actual = test.AssertUnmarshalChannelheader(t, in.ProposedTransaction).ChannelId - }). - Return(newEvaluateResponse(nil), nil). - Times(1) + expected := "CHANNEL_NAME" - contract := AssertNewTestContract(t, "chaincode", WithGatewayClient(mockClient)) + mockConnection := NewMockClientConnInterface(t) + network := AssertNewTestNetwork(t, expected, WithClientConnection(mockConnection)) + + requests := make(chan *gateway.EvaluateRequest, 1) + ExpectEvaluate(mockConnection, CaptureInvokeRequest(requests), WithEvaluateResponse(nil)) + contract := network.GetContract("chaincode") _, err := contract.EvaluateTransaction("transaction") require.NoError(t, err) - expected := contract.channelName + actual := AssertUnmarshalChannelheader(t, (<-requests).ProposedTransaction).ChannelId require.Equal(t, expected, actual) }) t.Run("Includes chaincode name in proposal", func(t *testing.T) { - var actual string - mockClient := NewMockGatewayClient(gomock.NewController(t)) - mockClient.EXPECT().Evaluate(gomock.Any(), gomock.Any()). - Do(func(_ context.Context, in *gateway.EvaluateRequest, _ ...grpc.CallOption) { - actual = test.AssertUnmarshalInvocationSpec(t, in.ProposedTransaction).ChaincodeSpec.ChaincodeId.Name - }). - Return(newEvaluateResponse(nil), nil). - Times(1) + expected := "CHAINCODE_NAME" - contract := AssertNewTestContract(t, "chaincode", WithGatewayClient(mockClient)) + mockConnection := NewMockClientConnInterface(t) + requests := make(chan *gateway.EvaluateRequest, 1) + ExpectEvaluate(mockConnection, CaptureInvokeRequest(requests), WithEvaluateResponse(nil)) + contract := AssertNewTestContract(t, expected, WithClientConnection(mockConnection)) _, err := contract.EvaluateTransaction("transaction") require.NoError(t, err) - expected := contract.chaincodeName + actual := AssertUnmarshalInvocationSpec(t, (<-requests).ProposedTransaction).ChaincodeSpec.ChaincodeId.Name require.Equal(t, expected, actual) }) t.Run("Includes transaction name in proposal for default smart contract", func(t *testing.T) { - var args [][]byte - mockClient := NewMockGatewayClient(gomock.NewController(t)) - mockClient.EXPECT().Evaluate(gomock.Any(), gomock.Any()). - Do(func(_ context.Context, in *gateway.EvaluateRequest, _ ...grpc.CallOption) { - args = test.AssertUnmarshalInvocationSpec(t, in.ProposedTransaction).ChaincodeSpec.Input.Args - }). - Return(newEvaluateResponse(nil), nil). - Times(1) + expected := "TRANSACTION_NAME" - contract := AssertNewTestContract(t, "chaincode", WithGatewayClient(mockClient)) + mockConnection := NewMockClientConnInterface(t) + requests := make(chan *gateway.EvaluateRequest, 1) + ExpectEvaluate(mockConnection, CaptureInvokeRequest(requests), WithEvaluateResponse(nil)) + contract := AssertNewTestContract(t, "chaincode", WithClientConnection(mockConnection)) - expected := "TRANSACTION_NAME" _, err := contract.EvaluateTransaction(expected) require.NoError(t, err) + args := AssertUnmarshalInvocationSpec(t, (<-requests).ProposedTransaction).ChaincodeSpec.Input.Args actual := string(args[0]) require.Equal(t, expected, actual, "got Args: %s", args) }) t.Run("Includes transaction name in proposal for named smart contract", func(t *testing.T) { - var args [][]byte - mockClient := NewMockGatewayClient(gomock.NewController(t)) - mockClient.EXPECT().Evaluate(gomock.Any(), gomock.Any()). - Do(func(_ context.Context, in *gateway.EvaluateRequest, _ ...grpc.CallOption) { - args = test.AssertUnmarshalInvocationSpec(t, in.ProposedTransaction).ChaincodeSpec.Input.Args - }). - Return(newEvaluateResponse(nil), nil). - Times(1) - - contract := AssertNewTestContractWithName(t, "chaincode", "CONTRACT_NAME", WithGatewayClient(mockClient)) + mockConnection := NewMockClientConnInterface(t) + requests := make(chan *gateway.EvaluateRequest, 1) + ExpectEvaluate(mockConnection, CaptureInvokeRequest(requests), WithEvaluateResponse(nil)) + contract := AssertNewTestContractWithName(t, "chaincode", "CONTRACT_NAME", WithClientConnection(mockConnection)) _, err := contract.EvaluateTransaction("TRANSACTION_NAME") require.NoError(t, err) + args := AssertUnmarshalInvocationSpec(t, (<-requests).ProposedTransaction).ChaincodeSpec.Input.Args actual := string(args[0]) expected := "CONTRACT_NAME:TRANSACTION_NAME" require.Equal(t, expected, actual, "got Args: %s", args) }) t.Run("Includes arguments in proposal", func(t *testing.T) { - var args [][]byte - mockClient := NewMockGatewayClient(gomock.NewController(t)) - mockClient.EXPECT().Evaluate(gomock.Any(), gomock.Any()). - Do(func(_ context.Context, in *gateway.EvaluateRequest, _ ...grpc.CallOption) { - args = test.AssertUnmarshalInvocationSpec(t, in.ProposedTransaction).ChaincodeSpec.Input.Args - }). - Return(newEvaluateResponse(nil), nil). - Times(1) + expected := []string{"one", "two", "three"} - contract := AssertNewTestContract(t, "chaincode", WithGatewayClient(mockClient)) + mockConnection := NewMockClientConnInterface(t) + requests := make(chan *gateway.EvaluateRequest, 1) + ExpectEvaluate(mockConnection, CaptureInvokeRequest(requests), WithEvaluateResponse(nil)) + contract := AssertNewTestContract(t, "chaincode", WithClientConnection(mockConnection)) - expected := []string{"one", "two", "three"} _, err := contract.EvaluateTransaction("transaction", expected...) require.NoError(t, err) + args := AssertUnmarshalInvocationSpec(t, (<-requests).ProposedTransaction).ChaincodeSpec.Input.Args actual := bytesAsStrings(args[1:]) require.EqualValues(t, expected, actual, "got Args: %s", args) }) t.Run("Includes channel name in proposed transaction", func(t *testing.T) { - var actual string - mockClient := NewMockGatewayClient(gomock.NewController(t)) - mockClient.EXPECT().Evaluate(gomock.Any(), gomock.Any()). - Do(func(_ context.Context, in *gateway.EvaluateRequest, _ ...grpc.CallOption) { - actual = in.ChannelId - }). - Return(newEvaluateResponse(nil), nil). - Times(1) + expected := "CHANNEL_NAME" - contract := AssertNewTestContract(t, "chaincode", WithGatewayClient(mockClient)) + mockConnection := NewMockClientConnInterface(t) + network := AssertNewTestNetwork(t, expected, WithClientConnection(mockConnection)) + contract := network.GetContract("chaincode") + + requests := make(chan *gateway.EvaluateRequest, 1) + ExpectEvaluate(mockConnection, CaptureInvokeRequest(requests), WithEvaluateResponse(nil)) _, err := contract.EvaluateTransaction("transaction") require.NoError(t, err) - expected := contract.channelName - require.Equal(t, expected, actual) + actual := (<-requests).ChannelId + require.Equal(t, contract.channelName, actual) }) t.Run("Includes transaction ID in proposed transaction", func(t *testing.T) { - var actual string - mockClient := NewMockGatewayClient(gomock.NewController(t)) - mockClient.EXPECT().Evaluate(gomock.Any(), gomock.Any()). - Do(func(_ context.Context, in *gateway.EvaluateRequest, _ ...grpc.CallOption) { - actual = test.AssertUnmarshalChannelheader(t, in.ProposedTransaction).TxId - }). - Return(newEvaluateResponse(nil), nil). - Times(1) - - contract := AssertNewTestContract(t, "chaincode", WithGatewayClient(mockClient)) + mockConnection := NewMockClientConnInterface(t) + requests := make(chan *gateway.EvaluateRequest, 1) + ExpectEvaluate(mockConnection, CaptureInvokeRequest(requests), WithEvaluateResponse(nil)) + contract := AssertNewTestContract(t, "chaincode", WithClientConnection(mockConnection)) proposal, err := contract.NewProposal("transaction") require.NoError(t, err, "NewProposal") _, err = proposal.Evaluate() require.NoError(t, err, "Evaluate") + actual := AssertUnmarshalChannelheader(t, (<-requests).ProposedTransaction).TxId require.Equal(t, proposal.TransactionID(), actual) }) t.Run("Includes transaction ID in evaluate request", func(t *testing.T) { - var actual string - mockClient := NewMockGatewayClient(gomock.NewController(t)) - mockClient.EXPECT().Evaluate(gomock.Any(), gomock.Any()). - Do(func(_ context.Context, in *gateway.EvaluateRequest, _ ...grpc.CallOption) { - actual = in.TransactionId - }). - Return(newEvaluateResponse(nil), nil). - Times(1) - - contract := AssertNewTestContract(t, "chaincode", WithGatewayClient(mockClient)) + mockConnection := NewMockClientConnInterface(t) + requests := make(chan *gateway.EvaluateRequest, 1) + ExpectEvaluate(mockConnection, CaptureInvokeRequest(requests), WithEvaluateResponse(nil)) + contract := AssertNewTestContract(t, "chaincode", WithClientConnection(mockConnection)) proposal, err := contract.NewProposal("transaction") require.NoError(t, err, "NewProposal") _, err = proposal.Evaluate() require.NoError(t, err, "Evaluate") + actual := (<-requests).TransactionId require.Equal(t, proposal.TransactionID(), actual) }) t.Run("Uses sign", func(t *testing.T) { - var actual []byte expected := []byte("MY_SIGNATURE") + + mockConnection := NewMockClientConnInterface(t) + requests := make(chan *gateway.EvaluateRequest, 1) + ExpectEvaluate(mockConnection, CaptureInvokeRequest(requests), WithEvaluateResponse(nil)) + sign := func(digest []byte) ([]byte, error) { return expected, nil } - mockClient := NewMockGatewayClient(gomock.NewController(t)) - mockClient.EXPECT().Evaluate(gomock.Any(), gomock.Any()). - Do(func(_ context.Context, in *gateway.EvaluateRequest, _ ...grpc.CallOption) { - actual = in.ProposedTransaction.Signature - }). - Return(newEvaluateResponse(nil), nil). - Times(1) - - contract := AssertNewTestContract(t, "chaincode", WithGatewayClient(mockClient), WithSign(sign)) + contract := AssertNewTestContract(t, "chaincode", WithClientConnection(mockConnection), WithSign(sign)) _, err := contract.EvaluateTransaction("transaction") require.NoError(t, err) + actual := (<-requests).ProposedTransaction.Signature require.EqualValues(t, expected, actual) }) t.Run("Uses hash", func(t *testing.T) { - var actual []byte expected := []byte("MY_DIGEST") + + mockConnection := NewMockClientConnInterface(t) + ExpectEvaluate(mockConnection, WithEvaluateResponse(nil)) + + digests := make(chan []byte, 1) sign := func(digest []byte) ([]byte, error) { - actual = digest - return expected, nil + digests <- digest + return digest, nil } hash := func(message []byte) []byte { return expected } - mockClient := NewMockGatewayClient(gomock.NewController(t)) - mockClient.EXPECT().Evaluate(gomock.Any(), gomock.Any()). - Return(newEvaluateResponse(nil), nil) - - contract := AssertNewTestContract(t, "chaincode", WithGatewayClient(mockClient), WithSign(sign), WithHash(hash)) + contract := AssertNewTestContract(t, "chaincode", WithClientConnection(mockConnection), WithSign(sign), WithHash(hash)) _, err := contract.EvaluateTransaction("transaction") require.NoError(t, err) + actual := <-digests require.EqualValues(t, expected, actual) }) t.Run("Sends private data with evaluate", func(t *testing.T) { - var actualOrgs []string expectedOrgs := []string{"MY_ORG"} - var actualPrice []byte expectedPrice := []byte("3000") - mockClient := NewMockGatewayClient(gomock.NewController(t)) - mockClient.EXPECT().Evaluate(gomock.Any(), gomock.Any()). - Do(func(_ context.Context, in *gateway.EvaluateRequest, _ ...grpc.CallOption) { - actualOrgs = in.TargetOrganizations - transient := test.AssertUnmarshalProposalPayload(t, in.ProposedTransaction).TransientMap - actualPrice = transient["price"] - }). - Return(newEvaluateResponse(nil), nil) - contract := AssertNewTestContract(t, "chaincode", WithGatewayClient(mockClient)) + mockConnection := NewMockClientConnInterface(t) + requests := make(chan *gateway.EvaluateRequest, 1) + ExpectEvaluate(mockConnection, CaptureInvokeRequest(requests), WithEvaluateResponse(nil)) + contract := AssertNewTestContract(t, "chaincode", WithClientConnection(mockConnection)) privateData := map[string][]byte{ "price": []byte("3000"), } - _, err := contract.Evaluate("transaction", WithTransient(privateData), WithEndorsingOrganizations("MY_ORG")) require.NoError(t, err) + request := <-requests + actualOrgs := request.TargetOrganizations require.EqualValues(t, expectedOrgs, actualOrgs) + + transient := AssertUnmarshalProposalPayload(t, request.ProposedTransaction).TransientMap + actualPrice := transient["price"] require.EqualValues(t, expectedPrice, actualPrice) }) @@ -326,37 +269,20 @@ func TestEvaluateTransaction(t *testing.T) { ctx, cancel := context.WithCancel(context.Background()) cancel() - mockClient := NewMockGatewayClient(gomock.NewController(t)) - mockClient.EXPECT().Evaluate(gomock.Any(), gomock.Any()). - DoAndReturn(func(ctx context.Context, _ *gateway.EvaluateRequest, _ ...grpc.CallOption) (*gateway.EvaluateResponse, error) { - err := ctx.Err() - if err != nil { - return nil, err - } - return newEvaluateResponse(nil), nil - }) - - contract := AssertNewTestContract(t, "chaincode", WithGatewayClient(mockClient)) + mockConnection := NewMockClientConnInterface(t) + ExpectEvaluate(mockConnection, WithInvokeContextErr()) + contract := AssertNewTestContract(t, "chaincode", WithClientConnection(mockConnection)) _, err := testCase.run(t, ctx, contract) - require.ErrorIs(t, err, context.Canceled) }) } t.Run("Uses default context", func(t *testing.T) { - mockClient := NewMockGatewayClient(gomock.NewController(t)) - mockClient.EXPECT().Evaluate(gomock.Any(), gomock.Any()). - DoAndReturn(func(ctx context.Context, _ *gateway.EvaluateRequest, _ ...grpc.CallOption) (*gateway.EvaluateResponse, error) { - select { - case <-time.After(1 * time.Second): - return newEvaluateResponse(nil), nil - case <-ctx.Done(): // Zero timeout context should cancel immediately, selecting this case - return nil, ctx.Err() - } - }) - - contract := AssertNewTestContract(t, "chaincode", WithGatewayClient(mockClient), WithEvaluateTimeout(0)) + mockConnection := NewMockClientConnInterface(t) + ExpectEvaluate(mockConnection, WithInvokeContextErr(), WithEvaluateResponse(nil)) + + contract := AssertNewTestContract(t, "chaincode", WithClientConnection(mockConnection), WithEvaluateTimeout(0)) _, err := contract.Evaluate("transaction") @@ -382,22 +308,17 @@ func TestEvaluateTransaction(t *testing.T) { }, } { t.Run(testName, func(t *testing.T) { - var actual []grpc.CallOption expected := grpc.WaitForReady(true) - mockClient := NewMockGatewayClient(gomock.NewController(t)) - mockClient.EXPECT().Evaluate(gomock.Any(), gomock.Any(), gomock.Any()). - Do(func(_ context.Context, _ *gateway.EvaluateRequest, opts ...grpc.CallOption) { - actual = opts - }). - Return(newEvaluateResponse(nil), nil). - Times(1) - - contract := AssertNewTestContract(t, "chaincode", WithGatewayClient(mockClient)) + options := make(chan []grpc.CallOption, 1) + mockConnection := NewMockClientConnInterface(t) + ExpectEvaluate(mockConnection, CaptureInvokeOptions(options), WithEvaluateResponse(nil)) + contract := AssertNewTestContract(t, "chaincode", WithClientConnection(mockConnection)) _, err := testCase.run(t, contract, []grpc.CallOption{expected}) require.NoError(t, err, "Evaluate") + actual := <-options require.Contains(t, actual, expected, "CallOptions") }) } diff --git a/pkg/client/gateway_test.go b/pkg/client/gateway_test.go index 540ad28e8..04e727b2a 100644 --- a/pkg/client/gateway_test.go +++ b/pkg/client/gateway_test.go @@ -8,32 +8,10 @@ import ( "testing" "github.com/hyperledger/fabric-gateway/pkg/identity" - "github.com/hyperledger/fabric-protos-go-apiv2/gateway" - "github.com/hyperledger/fabric-protos-go-apiv2/peer" "github.com/stretchr/testify/require" - "go.uber.org/mock/gomock" "google.golang.org/grpc" ) -//go:generate mockgen -destination ./gateway_mock_test.go -package ${GOPACKAGE} github.com/hyperledger/fabric-protos-go-apiv2/gateway GatewayClient,Gateway_ChaincodeEventsClient -//go:generate mockgen -destination ./deliver_mock_test.go -package ${GOPACKAGE} github.com/hyperledger/fabric-protos-go-apiv2/peer DeliverClient,Deliver_DeliverClient,Deliver_DeliverFilteredClient,Deliver_DeliverWithPrivateDataClient - -// WithGatewayClient uses the supplied client for the Gateway. Allows a stub implementation to be used for testing. -func WithGatewayClient(client gateway.GatewayClient) ConnectOption { - return func(gateway *Gateway) error { - gateway.client.grpcGatewayClient = client - return nil - } -} - -// WithDeliverClient uses the supplied client for the Deliver service. Allows a stub implementation to be used for testing. -func WithDeliverClient(client peer.DeliverClient) ConnectOption { - return func(gateway *Gateway) error { - gateway.client.grpcDeliverClient = client - return nil - } -} - // WithIdentity uses the supplied identity for the Gateway. func WithIdentity(id identity.Identity) ConnectOption { return func(gateway *Gateway) error { @@ -45,8 +23,7 @@ func WithIdentity(id identity.Identity) ConnectOption { func AssertNewTestGateway(t *testing.T, options ...ConnectOption) *Gateway { defaultOptions := []ConnectOption{ WithSign(TestCredentials.sign), - WithGatewayClient(NewMockGatewayClient(gomock.NewController(t))), - WithDeliverClient(NewMockDeliverClient(gomock.NewController(t))), + WithClientConnection(NewMockClientConnInterface(t)), } options = append(defaultOptions, options...) gateway, err := Connect(TestCredentials.Identity(), options...) @@ -94,8 +71,7 @@ func TestGateway(t *testing.T) { t.Run("GetNetwork returns correctly named Network", func(t *testing.T) { networkName := "network" - mockClient := NewMockGatewayClient(gomock.NewController(t)) - gateway := AssertNewTestGateway(t, WithGatewayClient(mockClient)) + gateway := AssertNewTestGateway(t) network := gateway.GetNetwork(networkName) @@ -104,8 +80,7 @@ func TestGateway(t *testing.T) { }) t.Run("Identity returns connecting identity", func(t *testing.T) { - mockClient := NewMockGatewayClient(gomock.NewController(t)) - gateway := AssertNewTestGateway(t, WithIdentity(id), WithGatewayClient(mockClient)) + gateway := AssertNewTestGateway(t, WithIdentity(id)) result := gateway.Identity() diff --git a/pkg/client/identity_test.go b/pkg/client/identity_test.go index 586dd15a3..f56f34191 100644 --- a/pkg/client/identity_test.go +++ b/pkg/client/identity_test.go @@ -4,7 +4,6 @@ package client import ( - "context" "testing" "github.com/hyperledger/fabric-gateway/pkg/identity" @@ -13,8 +12,6 @@ import ( "github.com/hyperledger/fabric-protos-go-apiv2/msp" "github.com/hyperledger/fabric-protos-go-apiv2/peer" "github.com/stretchr/testify/require" - "go.uber.org/mock/gomock" - "google.golang.org/grpc" "google.golang.org/protobuf/proto" ) @@ -36,51 +33,33 @@ func TestIdentity(t *testing.T) { require.NoError(t, err) t.Run("Evaluate uses client identity for proposals", func(t *testing.T) { - var actual []byte - mockClient := NewMockGatewayClient(gomock.NewController(t)) - evaluateResponse := &gateway.EvaluateResponse{ - Result: &peer.Response{ - Payload: nil, - }, - } - mockClient.EXPECT().Evaluate(gomock.Any(), gomock.Any()). - Do(func(_ context.Context, in *gateway.EvaluateRequest, _ ...grpc.CallOption) { - actual = test.AssertUnmarshalSignatureHeader(t, in.ProposedTransaction).Creator - }). - Return(evaluateResponse, nil). - Times(1) + mockConnection := NewMockClientConnInterface(t) + requests := make(chan *gateway.EvaluateRequest, 1) + ExpectEvaluate(mockConnection, CaptureInvokeRequest(requests), WithEvaluateResponse(nil)) - contract := AssertNewTestContract(t, "contract", WithGatewayClient(mockClient), WithIdentity(id)) + contract := AssertNewTestContract(t, "contract", WithClientConnection(mockConnection), WithIdentity(id)) _, err := contract.EvaluateTransaction("transaction") require.NoError(t, err) + actual := AssertUnmarshalSignatureHeader(t, (<-requests).ProposedTransaction).Creator require.EqualValues(t, creator, actual) }) t.Run("Submit uses client identity for proposals", func(t *testing.T) { - var actual []byte - mockClient := NewMockGatewayClient(gomock.NewController(t)) + mockConnection := NewMockClientConnInterface(t) + requests := make(chan *gateway.EndorseRequest, 1) endorseResponse := AssertNewEndorseResponse(t, "result", "channel") - statusResponse := &gateway.CommitStatusResponse{ - Result: peer.TxValidationCode_VALID, - } - mockClient.EXPECT().Endorse(gomock.Any(), gomock.Any()). - Do(func(_ context.Context, in *gateway.EndorseRequest, _ ...grpc.CallOption) { - actual = test.AssertUnmarshalSignatureHeader(t, in.ProposedTransaction).Creator - }). - Return(endorseResponse, nil). - Times(1) - mockClient.EXPECT().Submit(gomock.Any(), gomock.Any()). - Return(nil, nil) - mockClient.EXPECT().CommitStatus(gomock.Any(), gomock.Any()). - Return(statusResponse, nil) + ExpectEndorse(mockConnection, CaptureInvokeRequest(requests), WithEndorseResponse(endorseResponse)) + ExpectSubmit(mockConnection) + ExpectCommitStatus(mockConnection, WithCommitStatusResponse(peer.TxValidationCode_VALID, 1)) - contract := AssertNewTestContract(t, "contract", WithGatewayClient(mockClient), WithIdentity(id)) + contract := AssertNewTestContract(t, "contract", WithClientConnection(mockConnection), WithIdentity(id)) _, err := contract.SubmitTransaction("transaction") require.NoError(t, err) + actual := AssertUnmarshalSignatureHeader(t, (<-requests).ProposedTransaction).Creator require.EqualValues(t, creator, actual) }) } diff --git a/pkg/client/network_test.go b/pkg/client/network_test.go index 676463443..9c30d6efa 100644 --- a/pkg/client/network_test.go +++ b/pkg/client/network_test.go @@ -7,7 +7,6 @@ import ( "testing" "github.com/stretchr/testify/require" - "go.uber.org/mock/gomock" ) func AssertNewTestNetwork(t *testing.T, networkName string, options ...ConnectOption) *Network { @@ -18,8 +17,7 @@ func AssertNewTestNetwork(t *testing.T, networkName string, options ...ConnectOp func TestNetwork(t *testing.T) { t.Run("GetContract returns correctly named Contract", func(t *testing.T) { chaincodeName := "chaincode" - mockClient := NewMockGatewayClient(gomock.NewController(t)) - network := AssertNewTestNetwork(t, "network", WithGatewayClient(mockClient)) + network := AssertNewTestNetwork(t, "network") contract := network.GetContract(chaincodeName) @@ -31,8 +29,7 @@ func TestNetwork(t *testing.T) { t.Run("GetContractWithName returns correctly named Contract", func(t *testing.T) { chaincodeName := "chaincode" contractName := "contract" - mockClient := NewMockGatewayClient(gomock.NewController(t)) - network := AssertNewTestNetwork(t, "network", WithGatewayClient(mockClient)) + network := AssertNewTestNetwork(t, "network") contract := network.GetContractWithName(chaincodeName, contractName) diff --git a/pkg/client/offlinesign_test.go b/pkg/client/offlinesign_test.go index 323649f0f..535d051fe 100644 --- a/pkg/client/offlinesign_test.go +++ b/pkg/client/offlinesign_test.go @@ -5,573 +5,551 @@ package client import ( "context" - "errors" + "io" "testing" "github.com/hyperledger/fabric-protos-go-apiv2/common" "github.com/hyperledger/fabric-protos-go-apiv2/gateway" "github.com/hyperledger/fabric-protos-go-apiv2/peer" "github.com/stretchr/testify/require" - "go.uber.org/mock/gomock" - "google.golang.org/grpc" ) func TestOfflineSign(t *testing.T) { - var signature []byte - - newGatewayWithNoSign := func(t *testing.T, options ...ConnectOption) *Gateway { - defaultOptions := []ConnectOption{ - WithDeliverClient(NewMockDeliverClient(gomock.NewController(t))), - } - options = append(defaultOptions, options...) - gateway, err := Connect(TestCredentials.Identity(), options...) - require.NoError(t, err) - - return gateway - } - - newMockDeliverEvents := func(controller *gomock.Controller) *MockDeliver_DeliverClient { - mockEvents := NewMockDeliver_DeliverClient(controller) - - mockEvents.EXPECT().Send(gomock.Any()). - Do(func(in *common.Envelope) { - signature = in.GetSignature() - }). - Return(nil). - AnyTimes() - mockEvents.EXPECT().Recv(). - Return(nil, errors.New("fake")). - AnyTimes() - - return mockEvents - } - - type Invocation struct { - Invoke func() error - } - - type Signable struct { - Invocations map[string]Invocation - OfflineSign func([]byte) *Signable - State interface{} - Recreate func() *Signable - } - - var newSignableFromProposal func(t *testing.T, gateway *Gateway, proposal *Proposal) *Signable - newSignableFromProposal = func(t *testing.T, gateway *Gateway, proposal *Proposal) *Signable { - return &Signable{ - Invocations: map[string]Invocation{ - "Evaluate": { - Invoke: func() error { - _, err := proposal.Evaluate() - return err - }, + for testName, testCase := range map[string]struct { + New func(*signingTest) + Sign func(*signingTest, []byte) + Invocations map[string](func(*signingTest) ([]byte, error)) + Recreate func(*signingTest) + State func(*signingTest) any + }{ + "Proposal": { + New: func(s *signingTest) { + s.NewProposal() + }, + Sign: func(s *signingTest, signature []byte) { + s.SignProposal(signature) + }, + Invocations: map[string](func(*signingTest) ([]byte, error)){ + "Evaluate": func(s *signingTest) ([]byte, error) { + return s.Evaluate() }, - "Endorse": { - Invoke: func() error { - _, err := proposal.Endorse() - return err - }, + "Endorse": func(s *signingTest) ([]byte, error) { + return s.Endorse() }, }, - OfflineSign: func(signature []byte) *Signable { - bytes, err := proposal.Bytes() - require.NoError(t, err, "Bytes") - - result, err := gateway.NewSignedProposal(bytes, signature) - require.NoError(t, err, "NewSignedProposal") - - return newSignableFromProposal(t, gateway, result) + Recreate: func(s *signingTest) { + s.RecreateProposal() }, - State: struct { - Digest []byte - TransactionID string - EndorsingOrgs []string - }{ - Digest: proposal.Digest(), - TransactionID: proposal.TransactionID(), - EndorsingOrgs: proposal.proposedTransaction.GetEndorsingOrganizations(), + State: func(s *signingTest) any { + return struct { + Digest []byte + TransactionID string + EndorsingOrgs []string + }{ + Digest: s.proposal.Digest(), + TransactionID: s.proposal.TransactionID(), + EndorsingOrgs: s.proposal.proposedTransaction.GetEndorsingOrganizations(), + } }, - Recreate: func() *Signable { - signedBytes, err := proposal.Bytes() - require.NoError(t, err, "NewSignedProposal") - - newProposal, err := gateway.NewProposal(signedBytes) - require.NoError(t, err, "NewProposal") - - return newSignableFromProposal(t, gateway, newProposal) + }, + "Transaction": { + New: func(s *signingTest) { + s.NewTransaction() }, - } - } - - var newSignableFromTransaction func(t *testing.T, gateway *Gateway, transaction *Transaction) *Signable - newSignableFromTransaction = func(t *testing.T, gateway *Gateway, transaction *Transaction) *Signable { - return &Signable{ - Invocations: map[string]Invocation{ - "Submit": { - Invoke: func() error { - _, err := transaction.Submit() - return err - }, + Sign: func(s *signingTest, signature []byte) { + s.SignTransaction(signature) + }, + Invocations: map[string](func(*signingTest) ([]byte, error)){ + "Submit": func(s *signingTest) ([]byte, error) { + return s.Submit() }, }, - OfflineSign: func(signature []byte) *Signable { - bytes, err := transaction.Bytes() - require.NoError(t, err, "Bytes") - - result, err := gateway.NewSignedTransaction(bytes, signature) - require.NoError(t, err, "NewSignedTransaction") - - return newSignableFromTransaction(t, gateway, result) + Recreate: func(s *signingTest) { + s.RecreateTransaction() }, - State: struct { - Digest []byte - TransactionID string - }{ - Digest: transaction.Digest(), - TransactionID: transaction.TransactionID(), + State: func(s *signingTest) any { + return struct { + Digest []byte + TransactionID string + Result []byte + }{ + Digest: s.transaction.Digest(), + TransactionID: s.transaction.TransactionID(), + Result: s.transaction.Result(), + } }, - Recreate: func() *Signable { - signedBytes, err := transaction.Bytes() - require.NoError(t, err, "NewSignedTransactionBytes") - - newTransaction, err := gateway.NewTransaction(signedBytes) - require.NoError(t, err, "NewTransaction") - - return newSignableFromTransaction(t, gateway, newTransaction) + }, + "Commit": { + New: func(s *signingTest) { + s.NewCommit() }, - } - } - - var newSignableFromCommit func(t *testing.T, gateway *Gateway, commit *Commit) *Signable - newSignableFromCommit = func(t *testing.T, gateway *Gateway, commit *Commit) *Signable { - return &Signable{ - Invocations: map[string]Invocation{ - "Status": { - Invoke: func() error { - _, err := commit.Status() - return err - }, + Sign: func(s *signingTest, signature []byte) { + s.SignCommit(signature) + }, + Invocations: map[string](func(*signingTest) ([]byte, error)){ + "Status": func(s *signingTest) ([]byte, error) { + return s.CommitStatus() }, }, - OfflineSign: func(signature []byte) *Signable { - bytes, err := commit.Bytes() - require.NoError(t, err, "Bytes") - - result, err := gateway.NewSignedCommit(bytes, signature) - require.NoError(t, err, "NewSignedCommit") - - return newSignableFromCommit(t, gateway, result) + Recreate: func(s *signingTest) { + s.RecreateCommit() }, - State: struct { - Digest []byte - TransactionID string - }{ - Digest: commit.Digest(), - TransactionID: commit.TransactionID(), + State: func(s *signingTest) any { + return struct { + Digest []byte + TransactionID string + }{ + Digest: s.transaction.Digest(), + TransactionID: s.transaction.TransactionID(), + } }, - Recreate: func() *Signable { - signedBytes, err := commit.Bytes() - require.NoError(t, err, "NewSignedCommitBytes") - - newCommit, err := gateway.NewCommit(signedBytes) - require.NoError(t, err, "NewCommit") - - return newSignableFromCommit(t, gateway, newCommit) + }, + "ChaincodeEvents": { + New: func(s *signingTest) { + s.NewChaincodeEvents() }, - } - } - - var newSignableFromChaincodeEventsRequest func(t *testing.T, gateway *Gateway, request *ChaincodeEventsRequest) *Signable - newSignableFromChaincodeEventsRequest = func(t *testing.T, gateway *Gateway, request *ChaincodeEventsRequest) *Signable { - return &Signable{ - Invocations: map[string]Invocation{ - "Events": { - Invoke: func() error { - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() - - _, err := request.Events(ctx) - return err - }, + Sign: func(s *signingTest, signature []byte) { + s.SignChaincodeEvents(signature) + }, + Invocations: map[string](func(*signingTest) ([]byte, error)){ + "Events": func(s *signingTest) ([]byte, error) { + return s.ChaincodeEvents() }, }, - OfflineSign: func(signature []byte) *Signable { - bytes, err := request.Bytes() - require.NoError(t, err, "Bytes") - - result, err := gateway.NewSignedChaincodeEventsRequest(bytes, signature) - require.NoError(t, err, "NewSignedChaincodeEventsRequest") - - return newSignableFromChaincodeEventsRequest(t, gateway, result) + Recreate: func(s *signingTest) { + s.RecreateChaincodeEvents() }, - State: struct { - Digest []byte - }{ - Digest: request.Digest(), + State: func(s *signingTest) any { + return struct { + Digest []byte + }{ + Digest: s.chaincodeEvents.Digest(), + } }, - Recreate: func() *Signable { - signedBytes, err := request.Bytes() - require.NoError(t, err, "NewSignedChaincodeEventsRequestBytes") - - newChaincodeRequest, err := gateway.NewChaincodeEventsRequest(signedBytes) - require.NoError(t, err, "newChaincodeRequest") - - return newSignableFromChaincodeEventsRequest(t, gateway, newChaincodeRequest) + }, + "BlockEvents": { + New: func(s *signingTest) { + s.NewBlockEvents() }, - } - } - - var newSignableFromBlockEventsRequest func(t *testing.T, gateway *Gateway, request *BlockEventsRequest) *Signable - newSignableFromBlockEventsRequest = func(t *testing.T, gateway *Gateway, request *BlockEventsRequest) *Signable { - return &Signable{ - Invocations: map[string]Invocation{ - "Events": { - Invoke: func() error { - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() - - _, err := request.Events(ctx) - return err - }, + Sign: func(s *signingTest, signature []byte) { + s.SignBlockEvents(signature) + }, + Invocations: map[string](func(*signingTest) ([]byte, error)){ + "Events": func(s *signingTest) ([]byte, error) { + return s.BlockEvents() }, }, - OfflineSign: func(signature []byte) *Signable { - bytes, err := request.Bytes() - require.NoError(t, err, "Bytes") - - result, err := gateway.NewSignedBlockEventsRequest(bytes, signature) - require.NoError(t, err, "NewSignedBlockEventsRequest") - - return newSignableFromBlockEventsRequest(t, gateway, result) + Recreate: func(s *signingTest) { + s.RecreateBlockEvents() }, - State: struct { - Digest []byte - }{ - Digest: request.Digest(), + State: func(s *signingTest) any { + return struct { + Digest []byte + }{ + Digest: s.blockEvents.Digest(), + } }, - Recreate: func() *Signable { - signedBytes, err1 := request.Bytes() - require.NoError(t, err1, "NewSignedBlockEventsRequestBytes") - - newBlockRequest, err2 := gateway.NewBlockEventsRequest(signedBytes) - require.NoError(t, err2, "newBlockRequest") - - return newSignableFromBlockEventsRequest(t, gateway, newBlockRequest) + }, + "FilteredBlockEvents": { + New: func(s *signingTest) { + s.NewFilteredBlockEvents() }, - } - } - - var newSignableFromFilteredBlockEventsRequest func(t *testing.T, gateway *Gateway, request *FilteredBlockEventsRequest) *Signable - newSignableFromFilteredBlockEventsRequest = func(t *testing.T, gateway *Gateway, request *FilteredBlockEventsRequest) *Signable { - return &Signable{ - Invocations: map[string]Invocation{ - "Events": { - Invoke: func() error { - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() - - _, err := request.Events(ctx) - return err - }, + Sign: func(s *signingTest, signature []byte) { + s.SignFilteredBlockEvents(signature) + }, + Invocations: map[string](func(*signingTest) ([]byte, error)){ + "Events": func(s *signingTest) ([]byte, error) { + return s.FilteredBlockEvents() }, }, - OfflineSign: func(signature []byte) *Signable { - bytes, err := request.Bytes() - require.NoError(t, err, "Bytes") - - result, err := gateway.NewSignedFilteredBlockEventsRequest(bytes, signature) - require.NoError(t, err, "NewSignedFilteredBlockEventsRequest") - - return newSignableFromFilteredBlockEventsRequest(t, gateway, result) + Recreate: func(s *signingTest) { + s.RecreateFilteredBlockEvents() }, - State: struct { - Digest []byte - }{ - Digest: request.Digest(), + State: func(s *signingTest) any { + return struct { + Digest []byte + }{ + Digest: s.filteredBlockEvents.Digest(), + } }, - Recreate: func() *Signable { - signedBytes, err := request.Bytes() - require.NoError(t, err, "NewSignedFilteredBlockEventsRequestBytes") - - newFilteredBlockRequest, err := gateway.NewFilteredBlockEventsRequest(signedBytes) - require.NoError(t, err, "newRequest") - - return newSignableFromFilteredBlockEventsRequest(t, gateway, newFilteredBlockRequest) + }, + "BlockAndPrivateDataEvents": { + New: func(s *signingTest) { + s.NewBlockAndPrivateDataEvents() }, - } - } - - var newSignableFromBlockAndPrivateDataEventsRequest func(t *testing.T, gateway *Gateway, request *BlockAndPrivateDataEventsRequest) *Signable - newSignableFromBlockAndPrivateDataEventsRequest = func(t *testing.T, gateway *Gateway, request *BlockAndPrivateDataEventsRequest) *Signable { - return &Signable{ - Invocations: map[string]Invocation{ - "Events": { - Invoke: func() error { - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() - - _, err := request.Events(ctx) - return err - }, + Sign: func(s *signingTest, signature []byte) { + s.SignBlockAndPrivateDataEvents(signature) + }, + Invocations: map[string](func(*signingTest) ([]byte, error)){ + "Events": func(s *signingTest) ([]byte, error) { + return s.BlockAndPrivateDataEvents() }, }, - OfflineSign: func(signature []byte) *Signable { - bytes, err := request.Bytes() - require.NoError(t, err, "Bytes") - - result, err := gateway.NewSignedBlockAndPrivateDataEventsRequest(bytes, signature) - require.NoError(t, err, "NewSignedBlockEventsRequest") - - return newSignableFromBlockAndPrivateDataEventsRequest(t, gateway, result) + Recreate: func(s *signingTest) { + s.RecreateBlockAndPrivateDataEvents() }, - State: struct { - Digest []byte - }{ - Digest: request.Digest(), + State: func(s *signingTest) any { + return struct { + Digest []byte + }{ + Digest: s.blockAndPrivateDataEvents.Digest(), + } }, - Recreate: func() *Signable { - signedBytes, err1 := request.Bytes() - require.NoError(t, err1, "NewSignedBlockAndPrivateDataEventsRequestBytes") + }, + } { + t.Run(testName, func(t *testing.T) { + for invocationName, invoke := range testCase.Invocations { + t.Run(invocationName, func(t *testing.T) { + t.Run("Returns error with no signer and no explicit signing", func(t *testing.T) { + s := NewSigningTest(t) + testCase.New(s) + _, err := invoke(s) + require.Error(t, err) + }) - newBlockAndPrivateDataRequest, err2 := gateway.NewBlockAndPrivateDataEventsRequest(signedBytes) - require.NoError(t, err2, "newBlockAndPrivateDataRequest") + t.Run("Uses off-line signature", func(t *testing.T) { + expected := []byte("SIGNATURE") + s := NewSigningTest(t) - return newSignableFromBlockAndPrivateDataEventsRequest(t, gateway, newBlockAndPrivateDataRequest) - }, - } + testCase.New(s) + testCase.Sign(s, expected) + actual, err := invoke(s) + require.NoError(t, err) + + require.Equal(t, expected, actual) + }) + + t.Run("retains signature", func(t *testing.T) { + expected := []byte("SIGNATURE") + s := NewSigningTest(t) + + testCase.New(s) + testCase.Sign(s, expected) + + testCase.Recreate(s) + actual, err := invoke(s) + require.NoError(t, err) + + require.Equal(t, expected, actual) + }) + }) + } + + t.Run("Retains state after signing", func(t *testing.T) { + s := NewSigningTest(t) + + testCase.New(s) + expected := testCase.State(s) + + testCase.Sign(s, []byte("SIGNATURE")) + actual := testCase.State(s) + + require.Equal(t, expected, actual) + }) + }) } +} - for testName, testCase := range map[string]struct { - Create func(*testing.T) *Signable - }{ - "Proposal": { - Create: func(t *testing.T) *Signable { - mockClient := NewMockGatewayClient(gomock.NewController(t)) - mockClient.EXPECT().Evaluate(gomock.Any(), gomock.Any()). - Do(func(_ context.Context, in *gateway.EvaluateRequest, _ ...grpc.CallOption) { - signature = in.GetProposedTransaction().GetSignature() - }). - Return(&gateway.EvaluateResponse{ - Result: &peer.Response{ - Payload: nil, - }, - }, nil). - AnyTimes() - mockClient.EXPECT().Endorse(gomock.Any(), gomock.Any()). - Do(func(_ context.Context, in *gateway.EndorseRequest, _ ...grpc.CallOption) { - signature = in.GetProposedTransaction().GetSignature() - }). - Return(AssertNewEndorseResponse(t, "result", "network"), nil). - AnyTimes() - - gateway := newGatewayWithNoSign(t, WithGatewayClient(mockClient)) - contract := gateway.GetNetwork("NETWORK").GetContract("CHAINCODE") - - proposal, err := contract.NewProposal("transaction") - require.NoError(t, err) - - return newSignableFromProposal(t, gateway, proposal) - }, - }, - "Transaction": { - Create: func(t *testing.T) *Signable { - mockClient := NewMockGatewayClient(gomock.NewController(t)) - mockClient.EXPECT().Endorse(gomock.Any(), gomock.Any()). - Return(AssertNewEndorseResponse(t, "result", "network"), nil) - mockClient.EXPECT().Submit(gomock.Any(), gomock.Any()). - Do(func(_ context.Context, in *gateway.SubmitRequest, _ ...grpc.CallOption) { - signature = in.GetPreparedTransaction().GetSignature() - }). - Return(nil, nil). - AnyTimes() +type signingTest struct { + t *testing.T + mockConnection *MockClientConnInterface + gateway *Gateway + proposal *Proposal + transaction *Transaction + commit *Commit + chaincodeEvents *ChaincodeEventsRequest + blockEvents *BlockEventsRequest + filteredBlockEvents *FilteredBlockEventsRequest + blockAndPrivateDataEvents *BlockAndPrivateDataEventsRequest +} - gateway := newGatewayWithNoSign(t, WithGatewayClient(mockClient)) - contract := gateway.GetNetwork("NETWORK").GetContract("CHAINCODE") +func NewSigningTest(t *testing.T) *signingTest { + mockConnection := NewMockClientConnInterface(t) + gateway, err := Connect(TestCredentials.Identity(), WithClientConnection(mockConnection)) + require.NoError(t, err, "Connect") - unsignedProposal, err := contract.NewProposal("transaction") - require.NoError(t, err) + return &signingTest{ + t: t, + mockConnection: mockConnection, + gateway: gateway, + } +} - proposalBytes, err := unsignedProposal.Bytes() - require.NoError(t, err) +func (s *signingTest) NewProposal() { + result, err := s.gateway.GetNetwork("channel").GetContract("chaincode").NewProposal("transaction") + require.NoError(s.t, err, "NewProposal") + s.proposal = result +} - signedProposal, err := gateway.NewSignedProposal(proposalBytes, []byte("SIGNATURE")) - require.NoError(t, err) +func (s *signingTest) SignProposal(signature []byte) { + bytes := s.getBytes(s.proposal) + result, err := s.gateway.NewSignedProposal(bytes, signature) + require.NoError(s.t, err, "NewSignedProposal") + s.proposal = result +} - transaction, err := signedProposal.Endorse() - require.NoError(t, err) +func (s *signingTest) getBytes(serializable interface { + Bytes() ([]byte, error) +}) []byte { + bytes, err := serializable.Bytes() + require.NoError(s.t, err, "Bytes") + return bytes +} - return newSignableFromTransaction(t, gateway, transaction) - }, - }, - "Commit": { - Create: func(t *testing.T) *Signable { - mockClient := NewMockGatewayClient(gomock.NewController(t)) - mockClient.EXPECT().Endorse(gomock.Any(), gomock.Any()). - Return(AssertNewEndorseResponse(t, "result", "network"), nil) - mockClient.EXPECT().Submit(gomock.Any(), gomock.Any()). - Return(nil, nil). - AnyTimes() - mockClient.EXPECT().CommitStatus(gomock.Any(), gomock.Any()). - Do(func(_ context.Context, in *gateway.SignedCommitStatusRequest, _ ...grpc.CallOption) { - signature = in.GetSignature() - }). - Return(&gateway.CommitStatusResponse{ - Result: peer.TxValidationCode_VALID, - }, nil). - AnyTimes() +func (s *signingTest) RecreateProposal() { + bytes := s.getBytes(s.proposal) + result, err := s.gateway.NewProposal(bytes) + require.NoError(s.t, err, "NewProposal") + s.proposal = result +} - gateway := newGatewayWithNoSign(t, WithGatewayClient(mockClient)) - contract := gateway.GetNetwork("NETWORK").GetContract("CHAINCODE") +func (s *signingTest) Evaluate() ([]byte, error) { + requests := make(chan *gateway.EvaluateRequest, 1) + ExpectEvaluate(s.mockConnection, CaptureInvokeRequest(requests), WithEvaluateResponse(nil)).Maybe() - unsignedProposal, err := contract.NewProposal("transaction") - require.NoError(t, err) + _, err := s.proposal.Evaluate() + if err != nil { + return nil, err + } - proposalBytes, err := unsignedProposal.Bytes() - require.NoError(t, err) + return (<-requests).GetProposedTransaction().GetSignature(), nil +} - signedProposal, err := gateway.NewSignedProposal(proposalBytes, []byte("SIGNATURE")) - require.NoError(t, err) +func (s *signingTest) endorse() (*Transaction, []byte, error) { + requests := make(chan *gateway.EndorseRequest, 1) + response := AssertNewEndorseResponse(s.t, "TRANSACTION_RESULT", "network") + ExpectEndorse(s.mockConnection, CaptureInvokeRequest(requests), WithEndorseResponse(response)).Maybe() - unsignedTransaction, err := signedProposal.Endorse() - require.NoError(t, err) + transaction, err := s.proposal.Endorse() + if err != nil { + return nil, nil, err + } - transactionBytes, err := unsignedTransaction.Bytes() - require.NoError(t, err) + return transaction, (<-requests).ProposedTransaction.GetSignature(), nil +} - signedTransaction, err := gateway.NewSignedTransaction(transactionBytes, []byte("SIGNATURE")) - require.NoError(t, err) +func (s *signingTest) Endorse() ([]byte, error) { + _, signature, err := s.endorse() + return signature, err +} - commit, err := signedTransaction.Submit() - require.NoError(t, err) +func (s *signingTest) NewTransaction() { + s.NewProposal() + s.SignProposal([]byte("SIGNATURE")) + transaction, _, err := s.endorse() + require.NoError(s.t, err, "Endorse") - return newSignableFromCommit(t, gateway, commit) - }, - }, - "Chaincode events": { - Create: func(t *testing.T) *Signable { - controller := gomock.NewController(t) - mockClient := NewMockGatewayClient(controller) - mockEvents := NewMockGateway_ChaincodeEventsClient(controller) + s.transaction = transaction +} - mockClient.EXPECT().ChaincodeEvents(gomock.Any(), gomock.Any()). - Do(func(_ context.Context, in *gateway.SignedChaincodeEventsRequest, _ ...grpc.CallOption) { - signature = in.GetSignature() - }). - Return(mockEvents, nil). - AnyTimes() +func (s *signingTest) SignTransaction(signature []byte) { + bytes := s.getBytes(s.transaction) + result, err := s.gateway.NewSignedTransaction(bytes, signature) + require.NoError(s.t, err, "NewSignedTransaction") - mockEvents.EXPECT().Recv(). - Return(nil, errors.New("fake")). - AnyTimes() + s.transaction = result +} - gateway := newGatewayWithNoSign(t, WithGatewayClient(mockClient)) - network := gateway.GetNetwork("NETWORK") +func (s *signingTest) RecreateTransaction() { + bytes := s.getBytes(s.transaction) + result, err := s.gateway.NewTransaction(bytes) + require.NoError(s.t, err, "NewTransaction") - request, err := network.NewChaincodeEventsRequest("CHAINCODE") - require.NoError(t, err) + s.transaction = result +} - return newSignableFromChaincodeEventsRequest(t, gateway, request) - }, - }, - "Block events": { - Create: func(t *testing.T) *Signable { - controller := gomock.NewController(t) - mockClient := NewMockDeliverClient(controller) - mockEvents := newMockDeliverEvents(controller) +func (s *signingTest) submit() (*Commit, []byte, error) { + requests := make(chan *gateway.SubmitRequest, 1) + ExpectSubmit(s.mockConnection, CaptureInvokeRequest(requests)).Maybe() - mockClient.EXPECT().Deliver(gomock.Any(), gomock.Any()). - Return(mockEvents, nil). - AnyTimes() + commit, err := s.transaction.Submit() + if err != nil { + return nil, nil, err + } - gateway := newGatewayWithNoSign(t, WithGatewayClient(NewMockGatewayClient(controller)), WithDeliverClient(mockClient)) - network := gateway.GetNetwork("NETWORK") + return commit, (<-requests).GetPreparedTransaction().GetSignature(), nil +} - request, err := network.NewBlockEventsRequest() - require.NoError(t, err) +func (s *signingTest) Submit() ([]byte, error) { + _, signature, err := s.submit() + return signature, err +} - return newSignableFromBlockEventsRequest(t, gateway, request) - }, - }, - "Filtered block events": { - Create: func(t *testing.T) *Signable { - controller := gomock.NewController(t) - mockClient := NewMockDeliverClient(controller) - mockEvents := newMockDeliverEvents(controller) +func (s *signingTest) NewCommit() { + s.NewTransaction() + s.SignTransaction([]byte("SIGNATURE")) + commit, _, err := s.submit() + require.NoError(s.t, err, "Submit") - mockClient.EXPECT().DeliverFiltered(gomock.Any(), gomock.Any()). - Return(mockEvents, nil). - AnyTimes() + s.commit = commit +} - gateway := newGatewayWithNoSign(t, WithGatewayClient(NewMockGatewayClient(controller)), WithDeliverClient(mockClient)) - network := gateway.GetNetwork("NETWORK") +func (s *signingTest) SignCommit(signature []byte) { + bytes := s.getBytes(s.commit) + result, err := s.gateway.NewSignedCommit(bytes, signature) + require.NoError(s.t, err, "NewSignedCommit") - request, err := network.NewFilteredBlockEventsRequest() - require.NoError(t, err) + s.commit = result +} - return newSignableFromFilteredBlockEventsRequest(t, gateway, request) - }, - }, - "Block and private data events": { - Create: func(t *testing.T) *Signable { - controller := gomock.NewController(t) - mockClient := NewMockDeliverClient(controller) - mockEvents := newMockDeliverEvents(controller) +func (s *signingTest) RecreateCommit() { + bytes := s.getBytes(s.commit) + result, err := s.gateway.NewCommit(bytes) + require.NoError(s.t, err, "NewCommit") + s.commit = result +} - mockClient.EXPECT().DeliverWithPrivateData(gomock.Any(), gomock.Any()). - Return(mockEvents, nil). - AnyTimes() +func (s *signingTest) CommitStatus() ([]byte, error) { + requests := make(chan *gateway.SignedCommitStatusRequest, 1) + ExpectCommitStatus(s.mockConnection, CaptureInvokeRequest(requests), WithCommitStatusResponse(peer.TxValidationCode_VALID, 1)).Maybe() - gateway := newGatewayWithNoSign(t, WithGatewayClient(NewMockGatewayClient(controller)), WithDeliverClient(mockClient)) - network := gateway.GetNetwork("NETWORK") + _, err := s.commit.Status() + if err != nil { + return nil, err + } - request, err := network.NewBlockAndPrivateDataEventsRequest() - require.NoError(t, err) + return (<-requests).GetSignature(), nil +} - return newSignableFromBlockAndPrivateDataEventsRequest(t, gateway, request) - }, - }, - } { - t.Run(testName, func(t *testing.T) { - unsigned := testCase.Create(t) +func (s *signingTest) NewChaincodeEvents() { + result, err := s.gateway.GetNetwork("channel").NewChaincodeEventsRequest("chaincode") + require.NoError(s.t, err, "NewChaincodeEventsRequest") + s.chaincodeEvents = result +} - for invocationName, invocation := range unsigned.Invocations { - t.Run(invocationName, func(t *testing.T) { - t.Run("Returns error with no signer and no explicit signing", func(t *testing.T) { - err := invocation.Invoke() - require.Error(t, err) - }) +func (s *signingTest) SignChaincodeEvents(signature []byte) { + bytes := s.getBytes(s.chaincodeEvents) + result, err := s.gateway.NewSignedChaincodeEventsRequest(bytes, signature) + require.NoError(s.t, err, "NewSignedChaincodeEventsRequest") + s.chaincodeEvents = result +} - t.Run("Uses off-line signature", func(t *testing.T) { - signature = nil - expected := []byte("SIGNATURE") +func (s *signingTest) RecreateChaincodeEvents() { + bytes := s.getBytes(s.chaincodeEvents) + result, err := s.gateway.NewChaincodeEventsRequest(bytes) + require.NoError(s.t, err, "NewChaincodeEventsRequest") + s.chaincodeEvents = result +} - signed := unsigned.OfflineSign(expected) - err := signed.Invocations[invocationName].Invoke() - require.NoError(t, err) +func (s *signingTest) ChaincodeEvents() ([]byte, error) { + mockStream := NewMockClientStream(s.t) + ExpectChaincodeEvents(s.mockConnection, WithNewStreamResult(mockStream)).Maybe() - require.EqualValues(t, expected, signature) - }) + messages := make(chan *gateway.SignedChaincodeEventsRequest, 1) + ExpectSendMsg(mockStream, CaptureSendMsg(messages)).Maybe() + mockStream.EXPECT().CloseSend().Return(nil).Maybe() + ExpectRecvMsg(mockStream).Return(io.EOF).Maybe() - t.Run("retains signature", func(t *testing.T) { - signature = nil - expected := []byte("SIGNATURE") + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() - signed := unsigned.OfflineSign(expected) - recreated := signed.Recreate() - err := recreated.Invocations[invocationName].Invoke() - require.NoError(t, err) + _, err := s.chaincodeEvents.Events(ctx) + if err != nil { + return nil, err + } - require.EqualValues(t, expected, signature) + return (<-messages).GetSignature(), nil +} - }) - }) - } +func (s *signingTest) NewBlockEvents() { + result, err := s.gateway.GetNetwork("channel").NewBlockEventsRequest() + require.NoError(s.t, err, "NewBlockEventsRequest") + s.blockEvents = result +} - t.Run("Retains state after signing", func(t *testing.T) { - signed := unsigned.OfflineSign([]byte("SIGNATURE")) - require.EqualValues(t, unsigned.State, signed.State) - }) - }) +func (s *signingTest) SignBlockEvents(signature []byte) { + bytes := s.getBytes(s.blockEvents) + result, err := s.gateway.NewSignedBlockEventsRequest(bytes, signature) + require.NoError(s.t, err, "NewSignedBlockEventsRequest") + s.blockEvents = result +} + +func (s *signingTest) RecreateBlockEvents() { + bytes := s.getBytes(s.blockEvents) + result, err := s.gateway.NewBlockEventsRequest(bytes) + require.NoError(s.t, err, "NewBlockEventsRequest") + s.blockEvents = result +} + +func (s *signingTest) BlockEvents() ([]byte, error) { + return s.deliverEvents(func(ctx context.Context) error { + _, err := s.blockEvents.Events(ctx) + return err + }) +} + +func (s *signingTest) deliverEvents(invoke func(context.Context) error) ([]byte, error) { + mockStream := NewMockClientStream(s.t) + ExpectDeliver(s.mockConnection, WithNewStreamResult(mockStream)).Maybe() + ExpectDeliverFiltered(s.mockConnection, WithNewStreamResult(mockStream)).Maybe() + ExpectDeliverWithPrivateData(s.mockConnection, WithNewStreamResult(mockStream)).Maybe() + + messages := make(chan *common.Envelope, 1) + ExpectSendMsg(mockStream, CaptureSendMsg(messages)).Maybe() + mockStream.EXPECT().CloseSend().Return(nil).Maybe() + ExpectRecvMsg(mockStream).Return(io.EOF).Maybe() + + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + err := invoke(ctx) + if err != nil { + return nil, err } + + return (<-messages).GetSignature(), nil +} + +func (s *signingTest) NewFilteredBlockEvents() { + result, err := s.gateway.GetNetwork("channel").NewFilteredBlockEventsRequest() + require.NoError(s.t, err, "NewFilteredBlockEventsRequest") + s.filteredBlockEvents = result +} + +func (s *signingTest) SignFilteredBlockEvents(signature []byte) { + bytes := s.getBytes(s.filteredBlockEvents) + result, err := s.gateway.NewSignedFilteredBlockEventsRequest(bytes, signature) + require.NoError(s.t, err, "NewSignedFilteredBlockEventsRequest") + s.filteredBlockEvents = result +} + +func (s *signingTest) RecreateFilteredBlockEvents() { + bytes := s.getBytes(s.filteredBlockEvents) + result, err := s.gateway.NewFilteredBlockEventsRequest(bytes) + require.NoError(s.t, err, "NewFilteredBlockEventsRequest") + s.filteredBlockEvents = result +} + +func (s *signingTest) FilteredBlockEvents() ([]byte, error) { + return s.deliverEvents(func(ctx context.Context) error { + _, err := s.filteredBlockEvents.Events(ctx) + return err + }) +} + +func (s *signingTest) NewBlockAndPrivateDataEvents() { + result, err := s.gateway.GetNetwork("channel").NewBlockAndPrivateDataEventsRequest() + require.NoError(s.t, err, "NewBlockAndPrivateDataEventsRequest") + s.blockAndPrivateDataEvents = result +} + +func (s *signingTest) SignBlockAndPrivateDataEvents(signature []byte) { + bytes := s.getBytes(s.blockAndPrivateDataEvents) + result, err := s.gateway.NewSignedBlockAndPrivateDataEventsRequest(bytes, signature) + require.NoError(s.t, err, "NewSignedBlockAndPrivateDataEventsRequest") + s.blockAndPrivateDataEvents = result +} + +func (s *signingTest) RecreateBlockAndPrivateDataEvents() { + bytes := s.getBytes(s.blockAndPrivateDataEvents) + result, err := s.gateway.NewBlockAndPrivateDataEventsRequest(bytes) + require.NoError(s.t, err, "NewBlockAndPrivateDataEventsRequest") + s.blockAndPrivateDataEvents = result +} + +func (s *signingTest) BlockAndPrivateDataEvents() ([]byte, error) { + return s.deliverEvents(func(ctx context.Context) error { + _, err := s.blockAndPrivateDataEvents.Events(ctx) + return err + }) } diff --git a/pkg/client/protobuf_test.go b/pkg/client/protobuf_test.go new file mode 100644 index 000000000..8a720d73e --- /dev/null +++ b/pkg/client/protobuf_test.go @@ -0,0 +1,418 @@ +// Copyright IBM Corp. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +package client + +import ( + "context" + "fmt" + "io" + "strings" + "testing" + + "github.com/google/go-cmp/cmp" + "github.com/hyperledger/fabric-protos-go-apiv2/common" + "github.com/hyperledger/fabric-protos-go-apiv2/gateway" + "github.com/hyperledger/fabric-protos-go-apiv2/msp" + "github.com/hyperledger/fabric-protos-go-apiv2/peer" + "github.com/stretchr/testify/mock" + "github.com/stretchr/testify/require" + "google.golang.org/grpc" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" + "google.golang.org/protobuf/encoding/prototext" + "google.golang.org/protobuf/proto" + "google.golang.org/protobuf/reflect/protoreflect" + "google.golang.org/protobuf/runtime/protoiface" + "google.golang.org/protobuf/testing/protocmp" +) + +// AssertProtoEqual ensures an expected protobuf message matches an actual message +func AssertProtoEqual(t *testing.T, expected protoreflect.ProtoMessage, actual protoreflect.ProtoMessage) { + if diff := cmp.Diff(expected, actual, protocmp.Transform()); diff != "" { + require.FailNow(t, fmt.Sprintf( + "Not equal:\nexpected: %s\nactual : %s\n\nDiff:\n- Expected\n+ Actual\n\n%s", + formatProto(expected), + formatProto(actual), + diff, + )) + } +} + +func formatProto(message proto.Message) string { + marshal := prototext.MarshalOptions{ + Multiline: true, + Indent: "\t", + AllowPartial: true, + } + formatted := strings.TrimSpace(marshal.Format(message)) + return fmt.Sprintf("%s{\n%s\n}", protoMessageType(message), indent(formatted)) +} + +func protoMessageType(message proto.Message) string { + return string(message.ProtoReflect().Descriptor().Name()) +} + +func indent(text string) string { + return "\t" + strings.ReplaceAll(text, "\n", "\n\t") +} + +// AssertUnmarshal ensures that a protobuf is umarshaled without error +func AssertUnmarshal(t *testing.T, b []byte, m protoreflect.ProtoMessage) { + err := proto.Unmarshal(b, m) + require.NoError(t, err) +} + +// AssertUnmarshalProposalPayload ensures that a ChaincodeProposalPayload protobuf is umarshalled without error +func AssertUnmarshalProposalPayload(t *testing.T, proposedTransaction *peer.SignedProposal) *peer.ChaincodeProposalPayload { + proposal := &peer.Proposal{} + AssertUnmarshal(t, proposedTransaction.ProposalBytes, proposal) + + payload := &peer.ChaincodeProposalPayload{} + AssertUnmarshal(t, proposal.Payload, payload) + + return payload +} + +// AssertUnmarshalInvocationSpec ensures that a ChaincodeInvocationSpec protobuf is umarshalled without error +func AssertUnmarshalInvocationSpec(t *testing.T, proposedTransaction *peer.SignedProposal) *peer.ChaincodeInvocationSpec { + proposal := &peer.Proposal{} + AssertUnmarshal(t, proposedTransaction.ProposalBytes, proposal) + + payload := &peer.ChaincodeProposalPayload{} + AssertUnmarshal(t, proposal.Payload, payload) + + input := &peer.ChaincodeInvocationSpec{} + AssertUnmarshal(t, payload.Input, input) + + return input +} + +// AssertUnmarshalChannelheader ensures that a ChannelHeader protobuf is umarshalled without error +func AssertUnmarshalChannelheader(t *testing.T, proposedTransaction *peer.SignedProposal) *common.ChannelHeader { + header := AssertUnmarshalHeader(t, proposedTransaction) + + channelHeader := &common.ChannelHeader{} + AssertUnmarshal(t, header.ChannelHeader, channelHeader) + + return channelHeader +} + +// AssertUnmarshalHeader ensures that a Header protobuf is umarshalled without error +func AssertUnmarshalHeader(t *testing.T, proposedTransaction *peer.SignedProposal) *common.Header { + proposal := &peer.Proposal{} + AssertUnmarshal(t, proposedTransaction.ProposalBytes, proposal) + + header := &common.Header{} + AssertUnmarshal(t, proposal.Header, header) + + return header +} + +// AssertUnmarshalSignatureHeader ensures that a SignatureHeader protobuf is umarshalled without error +func AssertUnmarshalSignatureHeader(t *testing.T, proposedTransaction *peer.SignedProposal) *common.SignatureHeader { + header := AssertUnmarshalHeader(t, proposedTransaction) + + signatureHeader := &common.SignatureHeader{} + AssertUnmarshal(t, header.SignatureHeader, signatureHeader) + + return signatureHeader +} + +type invokeFunction func(ctx context.Context, method string, args any, reply any, opts ...grpc.CallOption) error + +func ExpectEvaluate(mockConnection *MockClientConnInterface, options ...invokeFunction) *MockClientConnInterface_Invoke_Call { + invokeCall := mockConnection.EXPECT(). + Invoke(mock.Anything, "/gateway.Gateway/Evaluate", mock.Anything, mock.Anything, mock.Anything) + fakeInvoke(invokeCall, options...) + return invokeCall +} + +func fakeInvoke(mock *MockClientConnInterface_Invoke_Call, options ...invokeFunction) { + mock.RunAndReturn(func(ctx context.Context, method string, args any, reply any, opts ...grpc.CallOption) error { + for _, option := range options { + if err := option(ctx, method, args, reply, opts...); err != nil { + return err + } + } + + return nil + }) +} + +func WithEvaluateResponse(value []byte) invokeFunction { + return func(ctx context.Context, method string, args any, reply any, opts ...grpc.CallOption) error { + proto.Merge(reply.(proto.Message), &gateway.EvaluateResponse{ + Result: &peer.Response{ + Payload: value, + }, + }) + return nil + } +} + +// WithInvokeContextErr causes the invoke to return any error associated with the invocation context; otherwise it has +// no effect. +func WithInvokeContextErr() invokeFunction { + return func(ctx context.Context, method string, args any, reply any, opts ...grpc.CallOption) error { + return ctx.Err() + } +} + +func WithInvokeError(err error) invokeFunction { + return func(ctx context.Context, method string, args any, reply any, opts ...grpc.CallOption) error { + return err + } +} + +func CaptureInvokeRequest[T proto.Message](requests chan<- T) invokeFunction { + return func(ctx context.Context, method string, args any, reply any, opts ...grpc.CallOption) error { + TrySend(requests, args.(T)) + return nil + } +} + +func CaptureInvokeOptions(callOptions chan<- []grpc.CallOption) invokeFunction { + return func(ctx context.Context, method string, args any, reply any, opts ...grpc.CallOption) error { + TrySend(callOptions, opts) + return nil + } +} + +func CaptureInvokeContext(contexts chan<- context.Context) invokeFunction { + return func(ctx context.Context, method string, args any, reply any, opts ...grpc.CallOption) error { + TrySend(contexts, ctx) + return nil + } +} + +func TrySend[T any](channel chan<- T, value T) bool { + select { + case channel <- value: + return true + default: + return false + } +} + +func NewStatusError(t *testing.T, code codes.Code, message string, details ...protoiface.MessageV1) error { + s, err := status.New(code, message).WithDetails(details...) + require.NoError(t, err) + + return s.Err() +} + +func ExpectEndorse(mockConnection *MockClientConnInterface, options ...invokeFunction) *MockClientConnInterface_Invoke_Call { + invokeCall := mockConnection.EXPECT(). + Invoke(mock.Anything, "/gateway.Gateway/Endorse", mock.Anything, mock.Anything, mock.Anything) + fakeInvoke(invokeCall, options...) + return invokeCall +} + +func ExpectSubmit(mockConnection *MockClientConnInterface, options ...invokeFunction) *MockClientConnInterface_Invoke_Call { + invokeCall := mockConnection.EXPECT(). + Invoke(mock.Anything, "/gateway.Gateway/Submit", mock.Anything, mock.Anything, mock.Anything) + fakeInvoke(invokeCall, options...) + return invokeCall +} + +func ExpectCommitStatus(mockConnection *MockClientConnInterface, options ...invokeFunction) *MockClientConnInterface_Invoke_Call { + invokeCall := mockConnection.EXPECT(). + Invoke(mock.Anything, "/gateway.Gateway/CommitStatus", mock.Anything, mock.Anything, mock.Anything) + fakeInvoke(invokeCall, options...) + return invokeCall +} + +func WithEndorseResponse(response *gateway.EndorseResponse) invokeFunction { + return func(ctx context.Context, method string, args any, reply any, opts ...grpc.CallOption) error { + proto.Merge(reply.(proto.Message), response) + return nil + } +} + +func WithCommitStatusResponse(status peer.TxValidationCode, blockNumber uint64) invokeFunction { + return func(ctx context.Context, method string, args any, reply any, opts ...grpc.CallOption) error { + proto.Merge(reply.(proto.Message), &gateway.CommitStatusResponse{ + Result: status, + BlockNumber: blockNumber, + }) + return nil + } +} + +type newStreamFunction func(ctx context.Context, desc *grpc.StreamDesc, method string, opts ...grpc.CallOption) (grpc.ClientStream, error) + +func ExpectChaincodeEvents(mockConnection *MockClientConnInterface, options ...newStreamFunction) *MockClientConnInterface_NewStream_Call { + newStreamCall := mockConnection.EXPECT(). + NewStream(mock.Anything, mock.Anything, "/gateway.Gateway/ChaincodeEvents", mock.Anything) + fakeNewStream(newStreamCall, options...) + return newStreamCall +} + +func ExpectDeliver(mockConnection *MockClientConnInterface, options ...newStreamFunction) *MockClientConnInterface_NewStream_Call { + newStreamCall := mockConnection.EXPECT(). + NewStream(mock.Anything, mock.Anything, "/protos.Deliver/Deliver", mock.Anything) + fakeNewStream(newStreamCall, options...) + return newStreamCall +} + +func ExpectDeliverFiltered(mockConnection *MockClientConnInterface, options ...newStreamFunction) *MockClientConnInterface_NewStream_Call { + newStreamCall := mockConnection.EXPECT(). + NewStream(mock.Anything, mock.Anything, "/protos.Deliver/DeliverFiltered", mock.Anything) + fakeNewStream(newStreamCall, options...) + return newStreamCall +} + +func ExpectDeliverWithPrivateData(mockConnection *MockClientConnInterface, options ...newStreamFunction) *MockClientConnInterface_NewStream_Call { + newStreamCall := mockConnection.EXPECT(). + NewStream(mock.Anything, mock.Anything, "/protos.Deliver/DeliverWithPrivateData", mock.Anything) + fakeNewStream(newStreamCall, options...) + return newStreamCall +} + +func fakeNewStream(mock *MockClientConnInterface_NewStream_Call, options ...newStreamFunction) { + mock.RunAndReturn(func(ctx context.Context, desc *grpc.StreamDesc, method string, opts ...grpc.CallOption) (grpc.ClientStream, error) { + for _, option := range options { + if stream, err := option(ctx, desc, method, opts...); stream != nil || err != nil { + return stream, err + } + } + + return nil, nil + }) +} + +func WithNewStreamResult(stream grpc.ClientStream) newStreamFunction { + return func(ctx context.Context, desc *grpc.StreamDesc, method string, opts ...grpc.CallOption) (grpc.ClientStream, error) { + return stream, nil + } +} + +func WithNewStreamError(err error) newStreamFunction { + return func(ctx context.Context, desc *grpc.StreamDesc, method string, opts ...grpc.CallOption) (grpc.ClientStream, error) { + return nil, err + } +} + +func CaptureNewStreamOptions(callOptions chan<- []grpc.CallOption) newStreamFunction { + return func(ctx context.Context, desc *grpc.StreamDesc, method string, opts ...grpc.CallOption) (grpc.ClientStream, error) { + TrySend(callOptions, opts) + return nil, nil + } +} + +type sendMsgFunction func(message any) error + +func ExpectSendMsg(mockStream *MockClientStream, options ...sendMsgFunction) *MockClientStream_SendMsg_Call { + result := mockStream.EXPECT().SendMsg(mock.Anything) + result.RunAndReturn(func(message any) error { + for _, option := range options { + if err := option(message); err != nil { + return err + } + } + + return nil + }) + return result +} + +func CaptureSendMsg[T proto.Message](messages chan<- T) sendMsgFunction { + return func(message any) error { + TrySend(messages, message.(T)) + return nil + } +} + +type recvMsgFunction func(message any) error + +func ExpectRecvMsg(mockStream *MockClientStream, options ...recvMsgFunction) *MockClientStream_RecvMsg_Call { + result := mockStream.EXPECT().RecvMsg(mock.Anything) + + if len(options) > 0 { + result.RunAndReturn(func(message any) error { + for _, option := range options { + if err := option(message); err != nil { + return err + } + } + + return nil + }) + } + + return result +} + +func WithRecvMsgs[T proto.Message](responses ...T) recvMsgFunction { + responseChannel := make(chan proto.Message, len(responses)) + for _, response := range responses { + responseChannel <- response + } + close(responseChannel) + + return func(message any) error { + response, ok := <-responseChannel + if !ok { + return io.EOF + } + + proto.Merge(message.(proto.Message), response) + return nil + } +} + +func AssertMarshal(t *testing.T, message protoreflect.ProtoMessage, msgAndArgs ...any) []byte { + bytes, err := proto.Marshal(message) + require.NoError(t, err, msgAndArgs...) + return bytes +} + +func AssertNewEndorseResponse(t *testing.T, result string, channelName string) *gateway.EndorseResponse { + return &gateway.EndorseResponse{ + PreparedTransaction: &common.Envelope{ + Payload: AssertMarshal(t, &common.Payload{ + Header: &common.Header{ + ChannelHeader: AssertMarshal(t, &common.ChannelHeader{ + ChannelId: channelName, + }), + }, + Data: AssertMarshal(t, &peer.Transaction{ + Actions: []*peer.TransactionAction{ + { + Payload: AssertMarshal(t, &peer.ChaincodeActionPayload{ + Action: &peer.ChaincodeEndorsedAction{ + ProposalResponsePayload: AssertMarshal(t, &peer.ProposalResponsePayload{ + Extension: AssertMarshal(t, &peer.ChaincodeAction{ + Response: &peer.Response{ + Payload: []byte(result), + }, + }), + }), + }, + }), + }, + }, + }), + }), + }, + } +} + +func AssertValidBlockEventRequestHeader(t *testing.T, payload *common.Payload, expectedChannel string) { + channelHeader := &common.ChannelHeader{} + AssertUnmarshal(t, payload.GetHeader().GetChannelHeader(), channelHeader) + + require.Equal(t, expectedChannel, channelHeader.GetChannelId(), "channel name") + + signatureHeader := &common.SignatureHeader{} + AssertUnmarshal(t, payload.GetHeader().GetSignatureHeader(), signatureHeader) + + expectedCreator := &msp.SerializedIdentity{ + Mspid: TestCredentials.Identity().MspID(), + IdBytes: TestCredentials.Identity().Credentials(), + } + actualCreator := &msp.SerializedIdentity{} + AssertUnmarshal(t, signatureHeader.GetCreator(), actualCreator) + AssertProtoEqual(t, expectedCreator, actualCreator) +} diff --git a/pkg/client/sign_test.go b/pkg/client/sign_test.go index d9cd96f37..1c567254b 100644 --- a/pkg/client/sign_test.go +++ b/pkg/client/sign_test.go @@ -4,110 +4,81 @@ package client import ( - "context" "testing" "github.com/hyperledger/fabric-protos-go-apiv2/gateway" "github.com/hyperledger/fabric-protos-go-apiv2/peer" "github.com/stretchr/testify/require" - "go.uber.org/mock/gomock" - "google.golang.org/grpc" ) func TestSign(t *testing.T) { - evaluateResponse := &gateway.EvaluateResponse{ - Result: &peer.Response{ - Payload: nil, - }, - } - - statusResponse := &gateway.CommitStatusResponse{ - Result: peer.TxValidationCode_VALID, - } + endorseResponse := AssertNewEndorseResponse(t, "TRANSACTION_RESULT", "network") t.Run("Evaluate signs proposal using client signing implementation", func(t *testing.T) { expected := []byte("SIGNATURE") + + mockConnection := NewMockClientConnInterface(t) + requests := make(chan *gateway.EvaluateRequest, 1) + ExpectEvaluate(mockConnection, CaptureInvokeRequest(requests), WithEvaluateResponse(expected)) + sign := func(digest []byte) ([]byte, error) { return expected, nil } - var actual []byte - mockClient := NewMockGatewayClient(gomock.NewController(t)) - mockClient.EXPECT().Evaluate(gomock.Any(), gomock.Any()). - Do(func(_ context.Context, in *gateway.EvaluateRequest, _ ...grpc.CallOption) { - actual = in.ProposedTransaction.Signature - }). - Return(evaluateResponse, nil). - Times(1) - - contract := AssertNewTestContract(t, "contract", WithGatewayClient(mockClient), WithSign(sign)) + contract := AssertNewTestContract(t, "contract", WithClientConnection(mockConnection), WithSign(sign)) _, err := contract.EvaluateTransaction("transaction") require.NoError(t, err) + actual := (<-requests).ProposedTransaction.Signature require.EqualValues(t, expected, actual) }) t.Run("Submit signs proposal using client signing implementation", func(t *testing.T) { expected := []byte("SIGNATURE") + + mockConnection := NewMockClientConnInterface(t) + requests := make(chan *gateway.EndorseRequest, 1) + ExpectEndorse(mockConnection, CaptureInvokeRequest(requests), WithEndorseResponse(endorseResponse)) + ExpectSubmit(mockConnection) + ExpectCommitStatus(mockConnection, WithCommitStatusResponse(peer.TxValidationCode_VALID, 1)) + sign := func(digest []byte) ([]byte, error) { return expected, nil } - var actual []byte - mockClient := NewMockGatewayClient(gomock.NewController(t)) - mockClient.EXPECT().Endorse(gomock.Any(), gomock.Any()). - Do(func(_ context.Context, in *gateway.EndorseRequest, _ ...grpc.CallOption) { - actual = in.ProposedTransaction.Signature - }). - Return(AssertNewEndorseResponse(t, "result", "network"), nil). - Times(1) - mockClient.EXPECT().Submit(gomock.Any(), gomock.Any()). - Return(nil, nil) - mockClient.EXPECT().CommitStatus(gomock.Any(), gomock.Any()). - Return(statusResponse, nil) - - contract := AssertNewTestContract(t, "contract", WithGatewayClient(mockClient), WithSign(sign)) + contract := AssertNewTestContract(t, "contract", WithClientConnection(mockConnection), WithSign(sign)) _, err := contract.SubmitTransaction("transaction") require.NoError(t, err) + actual := (<-requests).ProposedTransaction.Signature require.EqualValues(t, expected, actual) }) t.Run("Submit signs transaction using client signing implementation", func(t *testing.T) { expected := []byte("SIGNATURE") + + mockConnection := NewMockClientConnInterface(t) + ExpectEndorse(mockConnection, WithEndorseResponse(endorseResponse)) + requests := make(chan *gateway.SubmitRequest, 1) + ExpectSubmit(mockConnection, CaptureInvokeRequest(requests)) + ExpectCommitStatus(mockConnection, WithCommitStatusResponse(peer.TxValidationCode_VALID, 1)) + sign := func(digest []byte) ([]byte, error) { return expected, nil } - var actual []byte - mockClient := NewMockGatewayClient(gomock.NewController(t)) - mockClient.EXPECT().Endorse(gomock.Any(), gomock.Any()). - Return(AssertNewEndorseResponse(t, "result", "network"), nil) - mockClient.EXPECT().Submit(gomock.Any(), gomock.Any()). - Do(func(_ context.Context, in *gateway.SubmitRequest, _ ...grpc.CallOption) { - actual = in.PreparedTransaction.Signature - }). - Return(nil, nil). - Times(1) - mockClient.EXPECT().CommitStatus(gomock.Any(), gomock.Any()). - Return(statusResponse, nil) - - contract := AssertNewTestContract(t, "contract", WithGatewayClient(mockClient), WithSign(sign)) + contract := AssertNewTestContract(t, "contract", WithClientConnection(mockConnection), WithSign(sign)) _, err := contract.SubmitTransaction("transaction") require.NoError(t, err) + actual := (<-requests).PreparedTransaction.Signature require.EqualValues(t, expected, actual) }) t.Run("Default error implementation is used if no signing implementation supplied", func(t *testing.T) { - mockClient := NewMockGatewayClient(gomock.NewController(t)) - mockClient.EXPECT().Evaluate(gomock.Any(), gomock.Any()). - Return(evaluateResponse, nil). - AnyTimes() - - mockDeliver := NewMockDeliverClient(gomock.NewController(t)) + mockConnection := NewMockClientConnInterface(t) - gateway, err := Connect(TestCredentials.Identity(), WithGatewayClient(mockClient), WithDeliverClient(mockDeliver)) + gateway, err := Connect(TestCredentials.Identity(), WithClientConnection(mockConnection)) require.NoError(t, err) contract := gateway.GetNetwork("network").GetContract("chaincode") diff --git a/pkg/client/submit_test.go b/pkg/client/submit_test.go index c63a3bdbb..6fea403d9 100644 --- a/pkg/client/submit_test.go +++ b/pkg/client/submit_test.go @@ -6,73 +6,47 @@ package client import ( "context" "testing" - "time" - "github.com/hyperledger/fabric-gateway/pkg/internal/test" - "github.com/hyperledger/fabric-protos-go-apiv2/common" "github.com/hyperledger/fabric-protos-go-apiv2/gateway" "github.com/hyperledger/fabric-protos-go-apiv2/peer" "github.com/stretchr/testify/require" - "go.uber.org/mock/gomock" "google.golang.org/grpc" "google.golang.org/grpc/codes" "google.golang.org/grpc/status" - "google.golang.org/protobuf/proto" - "google.golang.org/protobuf/reflect/protoreflect" ) -func AssertMarshal(t *testing.T, message protoreflect.ProtoMessage, msgAndArgs ...interface{}) []byte { - bytes, err := proto.Marshal(message) - require.NoError(t, err, msgAndArgs...) - return bytes +func ReceiveAll[T any](channel <-chan T) []T { + var results []T + + for { + if value, ok := TryReceive(channel); !ok { + return results + } else { + results = append(results, value) + } + } } -func AssertNewEndorseResponse(t *testing.T, result string, channelName string) *gateway.EndorseResponse { - return &gateway.EndorseResponse{ - PreparedTransaction: &common.Envelope{ - Payload: AssertMarshal(t, &common.Payload{ - Header: &common.Header{ - ChannelHeader: AssertMarshal(t, &common.ChannelHeader{ - ChannelId: channelName, - }), - }, - Data: AssertMarshal(t, &peer.Transaction{ - Actions: []*peer.TransactionAction{ - { - Payload: AssertMarshal(t, &peer.ChaincodeActionPayload{ - Action: &peer.ChaincodeEndorsedAction{ - ProposalResponsePayload: AssertMarshal(t, &peer.ProposalResponsePayload{ - Extension: AssertMarshal(t, &peer.ChaincodeAction{ - Response: &peer.Response{ - Payload: []byte(result), - }, - }), - }), - }, - }), - }, - }, - }), - }), - }, +func TryReceive[T any](channel <-chan T) (T, bool) { + var result T + select { + case result = <-channel: + return result, true + default: + return result, false } } func TestSubmitTransaction(t *testing.T) { - newCommitStatusResponse := func(status peer.TxValidationCode, blockNumber uint64) *gateway.CommitStatusResponse { - return &gateway.CommitStatusResponse{ - Result: status, - BlockNumber: blockNumber, - } - } + defaultEndorseResponse := AssertNewEndorseResponse(t, "TRANSACTION_RESULT", "network") t.Run("Returns endorse error", func(t *testing.T) { expected := NewStatusError(t, codes.Aborted, "ENDORSE_ERROR") - mockClient := NewMockGatewayClient(gomock.NewController(t)) - mockClient.EXPECT().Endorse(gomock.Any(), gomock.Any()). - Return(nil, expected) - contract := AssertNewTestContract(t, "chaincode", WithGatewayClient(mockClient)) + mockConnection := NewMockClientConnInterface(t) + ExpectEndorse(mockConnection, WithInvokeError(expected)) + + contract := AssertNewTestContract(t, "chaincode", WithClientConnection(mockConnection)) proposal, err := contract.NewProposal("transaction") require.NoError(t, err, "NewProposal") @@ -87,13 +61,12 @@ func TestSubmitTransaction(t *testing.T) { t.Run("Returns submit error", func(t *testing.T) { expected := NewStatusError(t, codes.Aborted, "SUBMIT_ERROR") - mockClient := NewMockGatewayClient(gomock.NewController(t)) - mockClient.EXPECT().Endorse(gomock.Any(), gomock.Any()). - Return(AssertNewEndorseResponse(t, "TRANSACTION_RESULT", "network"), nil) - mockClient.EXPECT().Submit(gomock.Any(), gomock.Any()). - Return(nil, expected) - contract := AssertNewTestContract(t, "chaincode", WithGatewayClient(mockClient)) + mockConnection := NewMockClientConnInterface(t) + ExpectEndorse(mockConnection, WithEndorseResponse(defaultEndorseResponse)) + ExpectSubmit(mockConnection, WithInvokeError(expected)) + + contract := AssertNewTestContract(t, "chaincode", WithClientConnection(mockConnection)) proposal, err := contract.NewProposal("transaction") require.NoError(t, err, "NewProposal") transaction, err := proposal.Endorse() @@ -110,15 +83,13 @@ func TestSubmitTransaction(t *testing.T) { t.Run("Returns commit status error", func(t *testing.T) { expected := NewStatusError(t, codes.Aborted, "COMMIT_ERROR") - mockClient := NewMockGatewayClient(gomock.NewController(t)) - mockClient.EXPECT().Endorse(gomock.Any(), gomock.Any()). - Return(AssertNewEndorseResponse(t, "TRANSACTION_RESULT", "network"), nil) - mockClient.EXPECT().Submit(gomock.Any(), gomock.Any()). - Return(nil, nil) - mockClient.EXPECT().CommitStatus(gomock.Any(), gomock.Any()). - Return(nil, expected) - - contract := AssertNewTestContract(t, "chaincode", WithGatewayClient(mockClient)) + + mockConnection := NewMockClientConnInterface(t) + ExpectEndorse(mockConnection, WithEndorseResponse(defaultEndorseResponse)) + ExpectSubmit(mockConnection) + ExpectCommitStatus(mockConnection, WithInvokeError(expected)) + + contract := AssertNewTestContract(t, "chaincode", WithClientConnection(mockConnection)) proposal, err := contract.NewProposal("transaction") require.NoError(t, err, "NewProposal") transaction, err := proposal.Endorse() @@ -136,7 +107,7 @@ func TestSubmitTransaction(t *testing.T) { }) for name, testCase := range map[string]struct { - run func(t *testing.T, contract *Contract) ([]byte, error) + run func(*testing.T, *Contract) ([]byte, error) }{ "SubmitTransaction returns result for committed transaction": { run: func(t *testing.T, contract *Contract) ([]byte, error) { @@ -151,15 +122,13 @@ func TestSubmitTransaction(t *testing.T) { } { t.Run(name, func(t *testing.T) { expected := []byte("TRANSACTION_RESULT") - mockClient := NewMockGatewayClient(gomock.NewController(t)) - mockClient.EXPECT().Endorse(gomock.Any(), gomock.Any()). - Return(AssertNewEndorseResponse(t, "TRANSACTION_RESULT", "network"), nil) - mockClient.EXPECT().Submit(gomock.Any(), gomock.Any()). - Return(nil, nil) - mockClient.EXPECT().CommitStatus(gomock.Any(), gomock.Any()). - Return(newCommitStatusResponse(peer.TxValidationCode_VALID, 1), nil) - contract := AssertNewTestContract(t, "chaincode", WithGatewayClient(mockClient)) + mockConnection := NewMockClientConnInterface(t) + ExpectEndorse(mockConnection, WithEndorseResponse(defaultEndorseResponse)) + ExpectSubmit(mockConnection) + ExpectCommitStatus(mockConnection, WithCommitStatusResponse(peer.TxValidationCode_VALID, 1)) + + contract := AssertNewTestContract(t, "chaincode", WithClientConnection(mockConnection)) actual, err := testCase.run(t, contract) require.NoError(t, err) @@ -169,7 +138,7 @@ func TestSubmitTransaction(t *testing.T) { } for testName, testCase := range map[string]struct { - run func(t *testing.T, contract *Contract) ([]byte, error) + run func(*testing.T, *Contract) ([]byte, error) }{ "SubmitTransaction returns commit error for invalid commit status": { run: func(t *testing.T, contract *Contract) ([]byte, error) { @@ -183,15 +152,12 @@ func TestSubmitTransaction(t *testing.T) { }, } { t.Run(testName, func(t *testing.T) { - mockClient := NewMockGatewayClient(gomock.NewController(t)) - mockClient.EXPECT().Endorse(gomock.Any(), gomock.Any()). - Return(AssertNewEndorseResponse(t, "TRANSACTION_RESULT", "network"), nil) - mockClient.EXPECT().Submit(gomock.Any(), gomock.Any()). - Return(nil, nil) - mockClient.EXPECT().CommitStatus(gomock.Any(), gomock.Any()). - Return(newCommitStatusResponse(peer.TxValidationCode_MVCC_READ_CONFLICT, 1), nil) - - contract := AssertNewTestContract(t, "chaincode", WithGatewayClient(mockClient)) + mockConnection := NewMockClientConnInterface(t) + ExpectEndorse(mockConnection, WithEndorseResponse(defaultEndorseResponse)) + ExpectSubmit(mockConnection) + ExpectCommitStatus(mockConnection, WithCommitStatusResponse(peer.TxValidationCode_MVCC_READ_CONFLICT, 1)) + + contract := AssertNewTestContract(t, "chaincode", WithClientConnection(mockConnection)) _, err := testCase.run(t, contract) var actual *CommitError @@ -202,388 +168,309 @@ func TestSubmitTransaction(t *testing.T) { } t.Run("Includes channel name in proposal", func(t *testing.T) { - var actual string - mockClient := NewMockGatewayClient(gomock.NewController(t)) - mockClient.EXPECT().Endorse(gomock.Any(), gomock.Any()). - Do(func(_ context.Context, in *gateway.EndorseRequest, _ ...grpc.CallOption) { - actual = test.AssertUnmarshalChannelheader(t, in.ProposedTransaction).ChannelId - }). - Return(AssertNewEndorseResponse(t, "TRANSACTION_RESULT", "network"), nil). - Times(1) - mockClient.EXPECT().Submit(gomock.Any(), gomock.Any()). - Return(nil, nil) - mockClient.EXPECT().CommitStatus(gomock.Any(), gomock.Any()). - Return(newCommitStatusResponse(peer.TxValidationCode_VALID, 1), nil) - - contract := AssertNewTestContract(t, "chaincode", WithGatewayClient(mockClient)) + mockConnection := NewMockClientConnInterface(t) + requests := make(chan *gateway.EndorseRequest, 1) + ExpectEndorse(mockConnection, CaptureInvokeRequest(requests), WithEndorseResponse(defaultEndorseResponse)) + ExpectSubmit(mockConnection) + ExpectCommitStatus(mockConnection, WithCommitStatusResponse(peer.TxValidationCode_VALID, 1)) + + contract := AssertNewTestContract(t, "chaincode", WithClientConnection(mockConnection)) _, err := contract.SubmitTransaction("transaction") require.NoError(t, err) + actual := AssertUnmarshalChannelheader(t, (<-requests).ProposedTransaction).ChannelId expected := contract.channelName require.Equal(t, expected, actual) }) t.Run("Includes chaincode name in proposal", func(t *testing.T) { - var actual string - mockClient := NewMockGatewayClient(gomock.NewController(t)) - mockClient.EXPECT().Endorse(gomock.Any(), gomock.Any()). - Do(func(_ context.Context, in *gateway.EndorseRequest, _ ...grpc.CallOption) { - actual = test.AssertUnmarshalInvocationSpec(t, in.ProposedTransaction).ChaincodeSpec.ChaincodeId.Name - }). - Return(AssertNewEndorseResponse(t, "TRANSACTION_RESULT", "network"), nil). - Times(1) - mockClient.EXPECT().Submit(gomock.Any(), gomock.Any()). - Return(nil, nil) - mockClient.EXPECT().CommitStatus(gomock.Any(), gomock.Any()). - Return(newCommitStatusResponse(peer.TxValidationCode_VALID, 1), nil) - - contract := AssertNewTestContract(t, "chaincode", WithGatewayClient(mockClient)) + mockConnection := NewMockClientConnInterface(t) + requests := make(chan *gateway.EndorseRequest, 1) + ExpectEndorse(mockConnection, CaptureInvokeRequest(requests), WithEndorseResponse(defaultEndorseResponse)) + ExpectSubmit(mockConnection) + ExpectCommitStatus(mockConnection, WithCommitStatusResponse(peer.TxValidationCode_VALID, 1)) + + contract := AssertNewTestContract(t, "chaincode", WithClientConnection(mockConnection)) _, err := contract.SubmitTransaction("transaction") require.NoError(t, err) + actual := AssertUnmarshalInvocationSpec(t, (<-requests).ProposedTransaction).ChaincodeSpec.ChaincodeId.Name expected := contract.chaincodeName require.Equal(t, expected, actual) }) t.Run("Includes transaction name in proposal for default contract", func(t *testing.T) { - var args [][]byte - mockClient := NewMockGatewayClient(gomock.NewController(t)) - mockClient.EXPECT().Endorse(gomock.Any(), gomock.Any()). - Do(func(_ context.Context, in *gateway.EndorseRequest, _ ...grpc.CallOption) { - args = test.AssertUnmarshalInvocationSpec(t, in.ProposedTransaction).ChaincodeSpec.Input.Args - }). - Return(AssertNewEndorseResponse(t, "TRANSACTION_RESULT", "network"), nil). - Times(1) - mockClient.EXPECT().Submit(gomock.Any(), gomock.Any()). - Return(nil, nil) - mockClient.EXPECT().CommitStatus(gomock.Any(), gomock.Any()). - Return(newCommitStatusResponse(peer.TxValidationCode_VALID, 1), nil) - - contract := AssertNewTestContract(t, "chaincode", WithGatewayClient(mockClient)) - expected := "TRANSACTION_NAME" + + mockConnection := NewMockClientConnInterface(t) + + requests := make(chan *gateway.EndorseRequest, 1) + ExpectEndorse(mockConnection, CaptureInvokeRequest(requests), WithEndorseResponse(defaultEndorseResponse)) + ExpectSubmit(mockConnection) + ExpectCommitStatus(mockConnection, WithCommitStatusResponse(peer.TxValidationCode_VALID, 1)) + + contract := AssertNewTestContract(t, "chaincode", WithClientConnection(mockConnection)) + _, err := contract.SubmitTransaction(expected) require.NoError(t, err) + args := AssertUnmarshalInvocationSpec(t, (<-requests).ProposedTransaction).ChaincodeSpec.Input.Args actual := string(args[0]) require.Equal(t, expected, actual) }) t.Run("Includes transaction name in proposal for named contract", func(t *testing.T) { - var args [][]byte - mockClient := NewMockGatewayClient(gomock.NewController(t)) - mockClient.EXPECT().Endorse(gomock.Any(), gomock.Any()). - Do(func(_ context.Context, in *gateway.EndorseRequest, _ ...grpc.CallOption) { - args = test.AssertUnmarshalInvocationSpec(t, in.ProposedTransaction).ChaincodeSpec.Input.Args - }). - Return(AssertNewEndorseResponse(t, "TRANSACTION_RESULT", "network"), nil). - Times(1) - mockClient.EXPECT().Submit(gomock.Any(), gomock.Any()). - Return(nil, nil) - mockClient.EXPECT().CommitStatus(gomock.Any(), gomock.Any()). - Return(newCommitStatusResponse(peer.TxValidationCode_VALID, 1), nil) - - contract := AssertNewTestContractWithName(t, "chaincode", "CONTRACT_NAME", WithGatewayClient(mockClient)) + mockConnection := NewMockClientConnInterface(t) + requests := make(chan *gateway.EndorseRequest, 1) + ExpectEndorse(mockConnection, CaptureInvokeRequest(requests), WithEndorseResponse(defaultEndorseResponse)) + ExpectSubmit(mockConnection) + ExpectCommitStatus(mockConnection, WithCommitStatusResponse(peer.TxValidationCode_VALID, 1)) + + contract := AssertNewTestContractWithName(t, "chaincode", "CONTRACT_NAME", WithClientConnection(mockConnection)) _, err := contract.SubmitTransaction("TRANSACTION_NAME") require.NoError(t, err) + args := AssertUnmarshalInvocationSpec(t, (<-requests).ProposedTransaction).ChaincodeSpec.Input.Args actual := string(args[0]) expected := "CONTRACT_NAME:TRANSACTION_NAME" require.Equal(t, expected, actual) }) t.Run("Includes arguments in proposal", func(t *testing.T) { - var args [][]byte - mockClient := NewMockGatewayClient(gomock.NewController(t)) - mockClient.EXPECT().Endorse(gomock.Any(), gomock.Any()). - Do(func(_ context.Context, in *gateway.EndorseRequest, _ ...grpc.CallOption) { - args = test.AssertUnmarshalInvocationSpec(t, in.ProposedTransaction).ChaincodeSpec.Input.Args - }). - Return(AssertNewEndorseResponse(t, "TRANSACTION_RESULT", "network"), nil). - Times(1) - mockClient.EXPECT().Submit(gomock.Any(), gomock.Any()). - Return(nil, nil) - mockClient.EXPECT().CommitStatus(gomock.Any(), gomock.Any()). - Return(newCommitStatusResponse(peer.TxValidationCode_VALID, 1), nil) - - contract := AssertNewTestContract(t, "chaincode", WithGatewayClient(mockClient)) + mockConnection := NewMockClientConnInterface(t) + requests := make(chan *gateway.EndorseRequest, 1) + ExpectEndorse(mockConnection, CaptureInvokeRequest(requests), WithEndorseResponse(defaultEndorseResponse)) + ExpectSubmit(mockConnection) + ExpectCommitStatus(mockConnection, WithCommitStatusResponse(peer.TxValidationCode_VALID, 1)) + + contract := AssertNewTestContract(t, "chaincode", WithClientConnection(mockConnection)) expected := []string{"one", "two", "three"} _, err := contract.SubmitTransaction("transaction", expected...) require.NoError(t, err) + args := AssertUnmarshalInvocationSpec(t, (<-requests).ProposedTransaction).ChaincodeSpec.Input.Args actual := bytesAsStrings(args[1:]) require.EqualValues(t, expected, actual) }) - t.Run("Includes channel name in proposed transaction", func(t *testing.T) { - var actual string - mockClient := NewMockGatewayClient(gomock.NewController(t)) - mockClient.EXPECT().Endorse(gomock.Any(), gomock.Any()). - Do(func(_ context.Context, in *gateway.EndorseRequest, _ ...grpc.CallOption) { - actual = in.ChannelId - }). - Return(AssertNewEndorseResponse(t, "TRANSACTION_RESULT", "network"), nil). - Times(1) - mockClient.EXPECT().Submit(gomock.Any(), gomock.Any()). - Return(nil, nil) - mockClient.EXPECT().CommitStatus(gomock.Any(), gomock.Any()). - Return(newCommitStatusResponse(peer.TxValidationCode_VALID, 1), nil) - - contract := AssertNewTestContract(t, "chaincode", WithGatewayClient(mockClient)) + t.Run("Includes channel name in endorse request", func(t *testing.T) { + expected := "CHANNEL_NAME" + + mockConnection := NewMockClientConnInterface(t) + network := AssertNewTestNetwork(t, expected, WithClientConnection(mockConnection)) + + requests := make(chan *gateway.EndorseRequest, 1) + ExpectEndorse(mockConnection, CaptureInvokeRequest(requests), WithEndorseResponse(defaultEndorseResponse)) + ExpectSubmit(mockConnection) + ExpectCommitStatus(mockConnection, WithCommitStatusResponse(peer.TxValidationCode_VALID, 1)) + + contract := network.GetContract("chaincode") _, err := contract.SubmitTransaction("transaction") require.NoError(t, err) - expected := contract.channelName + actual := (<-requests).ChannelId require.Equal(t, expected, actual) }) - t.Run("Includes transaction ID in proposed transaction", func(t *testing.T) { - var actual string - - mockClient := NewMockGatewayClient(gomock.NewController(t)) - mockClient.EXPECT().Endorse(gomock.Any(), gomock.Any()). - Do(func(_ context.Context, in *gateway.EndorseRequest, _ ...grpc.CallOption) { - actual = test.AssertUnmarshalChannelheader(t, in.ProposedTransaction).TxId - }). - Return(AssertNewEndorseResponse(t, "TRANSACTION_RESULT", "network"), nil). - Times(1) + t.Run("Includes transaction ID in proposal", func(t *testing.T) { + mockConnection := NewMockClientConnInterface(t) + requests := make(chan *gateway.EndorseRequest, 1) + ExpectEndorse(mockConnection, CaptureInvokeRequest(requests), WithEndorseResponse(defaultEndorseResponse)) - contract := AssertNewTestContract(t, "chaincode", WithGatewayClient(mockClient)) + contract := AssertNewTestContract(t, "chaincode", WithClientConnection(mockConnection)) proposal, err := contract.NewProposal("transaction") require.NoError(t, err, "NewProposal") _, err = proposal.Endorse() require.NoError(t, err, "Endorse") + actual := AssertUnmarshalChannelheader(t, (<-requests).ProposedTransaction).TxId require.Equal(t, proposal.TransactionID(), actual) }) t.Run("Includes transaction ID in endorse request", func(t *testing.T) { - var actual string + mockConnection := NewMockClientConnInterface(t) + requests := make(chan *gateway.EndorseRequest, 1) + ExpectEndorse(mockConnection, CaptureInvokeRequest(requests), WithEndorseResponse(defaultEndorseResponse)) - mockClient := NewMockGatewayClient(gomock.NewController(t)) - mockClient.EXPECT().Endorse(gomock.Any(), gomock.Any()). - Do(func(_ context.Context, in *gateway.EndorseRequest, _ ...grpc.CallOption) { - actual = in.TransactionId - }). - Return(AssertNewEndorseResponse(t, "TRANSACTION_RESULT", "network"), nil). - Times(1) - - contract := AssertNewTestContract(t, "chaincode", WithGatewayClient(mockClient)) + contract := AssertNewTestContract(t, "chaincode", WithClientConnection(mockConnection)) proposal, err := contract.NewProposal("transaction") require.NoError(t, err, "NewProposal") _, err = proposal.Endorse() require.NoError(t, err, "Endorse") + actual := (<-requests).TransactionId require.Equal(t, proposal.TransactionID(), actual) }) t.Run("Includes channel name in commit status request", func(t *testing.T) { - var actual string - mockClient := NewMockGatewayClient(gomock.NewController(t)) - mockClient.EXPECT().Endorse(gomock.Any(), gomock.Any()). - Return(AssertNewEndorseResponse(t, "TRANSACTION_RESULT", "network"), nil) - mockClient.EXPECT().Submit(gomock.Any(), gomock.Any()). - Return(nil, nil) - mockClient.EXPECT().CommitStatus(gomock.Any(), gomock.Any()). - Do(func(_ context.Context, in *gateway.SignedCommitStatusRequest, _ ...grpc.CallOption) { - request := &gateway.CommitStatusRequest{} - test.AssertUnmarshal(t, in.Request, request) - actual = request.ChannelId - }). - Return(newCommitStatusResponse(peer.TxValidationCode_VALID, 1), nil). - Times(1) - - contract := AssertNewTestContract(t, "chaincode", WithGatewayClient(mockClient)) + expected := "CHANNEL_NAME" + + mockConnection := NewMockClientConnInterface(t) + network := AssertNewTestNetwork(t, expected, WithClientConnection(mockConnection)) + endorseResponse := AssertNewEndorseResponse(t, "TRANSACTION_RESULT", expected) + ExpectEndorse(mockConnection, WithEndorseResponse(endorseResponse)) + ExpectSubmit(mockConnection) + requests := make(chan *gateway.SignedCommitStatusRequest, 1) + ExpectCommitStatus(mockConnection, CaptureInvokeRequest(requests), WithCommitStatusResponse(peer.TxValidationCode_VALID, 1)) + + contract := network.GetContract("chaincode") _, err := contract.SubmitTransaction("transaction") require.NoError(t, err) - expected := contract.channelName + request := &gateway.CommitStatusRequest{} + AssertUnmarshal(t, (<-requests).Request, request) + actual := request.ChannelId require.Equal(t, expected, actual) }) t.Run("Includes transaction ID in commit status request", func(t *testing.T) { - var actual string - var expected string - mockClient := NewMockGatewayClient(gomock.NewController(t)) - mockClient.EXPECT().Endorse(gomock.Any(), gomock.Any()). - Do(func(_ context.Context, in *gateway.EndorseRequest, _ ...grpc.CallOption) { - expected = test.AssertUnmarshalChannelheader(t, in.ProposedTransaction).TxId - }). - Return(AssertNewEndorseResponse(t, "TRANSACTION_RESULT", "network"), nil) - mockClient.EXPECT().Submit(gomock.Any(), gomock.Any()). - Return(nil, nil) - mockClient.EXPECT().CommitStatus(gomock.Any(), gomock.Any()). - Do(func(_ context.Context, in *gateway.SignedCommitStatusRequest, _ ...grpc.CallOption) { - request := &gateway.CommitStatusRequest{} - test.AssertUnmarshal(t, in.Request, request) - actual = request.TransactionId - }). - Return(newCommitStatusResponse(peer.TxValidationCode_VALID, 1), nil). - Times(1) - - contract := AssertNewTestContract(t, "chaincode", WithGatewayClient(mockClient)) + mockConnection := NewMockClientConnInterface(t) + endorseRequests := make(chan *gateway.EndorseRequest, 1) + ExpectEndorse(mockConnection, CaptureInvokeRequest(endorseRequests), WithEndorseResponse(defaultEndorseResponse)) + ExpectSubmit(mockConnection) + commitStatusRequests := make(chan *gateway.SignedCommitStatusRequest, 1) + ExpectCommitStatus(mockConnection, CaptureInvokeRequest(commitStatusRequests), WithCommitStatusResponse(peer.TxValidationCode_VALID, 1)) + + contract := AssertNewTestContract(t, "chaincode", WithClientConnection(mockConnection)) _, err := contract.SubmitTransaction("transaction") require.NoError(t, err) + expected := AssertUnmarshalChannelheader(t, (<-endorseRequests).ProposedTransaction).TxId + request := &gateway.CommitStatusRequest{} + AssertUnmarshal(t, (<-commitStatusRequests).Request, request) + actual := request.TransactionId require.Equal(t, expected, actual) }) t.Run("Uses signer for endorse", func(t *testing.T) { - var actual []byte expected := []byte("MY_SIGNATURE") + + mockConnection := NewMockClientConnInterface(t) + requests := make(chan *gateway.EndorseRequest, 1) + ExpectEndorse(mockConnection, CaptureInvokeRequest(requests), WithEndorseResponse(defaultEndorseResponse)) + ExpectSubmit(mockConnection) + ExpectCommitStatus(mockConnection, WithCommitStatusResponse(peer.TxValidationCode_VALID, 1)) + sign := func(digest []byte) ([]byte, error) { return expected, nil } - mockClient := NewMockGatewayClient(gomock.NewController(t)) - mockClient.EXPECT().Endorse(gomock.Any(), gomock.Any()). - Do(func(_ context.Context, in *gateway.EndorseRequest, _ ...grpc.CallOption) { - actual = in.ProposedTransaction.Signature - }). - Return(AssertNewEndorseResponse(t, "TRANSACTION_RESULT", "network"), nil). - Times(1) - mockClient.EXPECT().Submit(gomock.Any(), gomock.Any()). - Return(nil, nil) - mockClient.EXPECT().CommitStatus(gomock.Any(), gomock.Any()). - Return(newCommitStatusResponse(peer.TxValidationCode_VALID, 1), nil) - - contract := AssertNewTestContract(t, "chaincode", WithGatewayClient(mockClient), WithSign(sign)) + contract := AssertNewTestContract(t, "chaincode", WithClientConnection(mockConnection), WithSign(sign)) _, err := contract.SubmitTransaction("transaction") require.NoError(t, err) + actual := (<-requests).ProposedTransaction.Signature require.EqualValues(t, expected, actual) }) t.Run("Uses signer for submit", func(t *testing.T) { - var actual []byte expected := []byte("MY_SIGNATURE") + + mockConnection := NewMockClientConnInterface(t) + ExpectEndorse(mockConnection, WithEndorseResponse(defaultEndorseResponse)) + requests := make(chan *gateway.SubmitRequest, 1) + ExpectSubmit(mockConnection, CaptureInvokeRequest(requests)) + ExpectCommitStatus(mockConnection, WithCommitStatusResponse(peer.TxValidationCode_VALID, 1)) + sign := func(digest []byte) ([]byte, error) { return expected, nil } - mockClient := NewMockGatewayClient(gomock.NewController(t)) - mockClient.EXPECT().Endorse(gomock.Any(), gomock.Any()). - Return(AssertNewEndorseResponse(t, "TRANSACTION_RESULT", "network"), nil) - mockClient.EXPECT().Submit(gomock.Any(), gomock.Any()). - Do(func(_ context.Context, in *gateway.SubmitRequest, _ ...grpc.CallOption) { - actual = in.PreparedTransaction.Signature - }). - Return(nil, nil). - Times(1) - mockClient.EXPECT().CommitStatus(gomock.Any(), gomock.Any()). - Return(newCommitStatusResponse(peer.TxValidationCode_VALID, 1), nil) - - contract := AssertNewTestContract(t, "chaincode", WithGatewayClient(mockClient), WithSign(sign)) + contract := AssertNewTestContract(t, "chaincode", WithClientConnection(mockConnection), WithSign(sign)) _, err := contract.SubmitTransaction("transaction") require.NoError(t, err) + actual := (<-requests).PreparedTransaction.Signature require.EqualValues(t, expected, actual) }) t.Run("Sends private data with submit", func(t *testing.T) { - var actualOrgs []string - expectedOrgs := []string{"MY_ORG"} - var actualPrice []byte + expectedOrg := "MY_ORG" expectedPrice := []byte("3000") - mockClient := NewMockGatewayClient(gomock.NewController(t)) - mockClient.EXPECT().Endorse(gomock.Any(), gomock.Any()). - Do(func(_ context.Context, in *gateway.EndorseRequest, _ ...grpc.CallOption) { - actualOrgs = in.EndorsingOrganizations - transient := test.AssertUnmarshalProposalPayload(t, in.ProposedTransaction).TransientMap - actualPrice = transient["price"] - }). - Return(AssertNewEndorseResponse(t, "TRANSACTION_RESULT", "network"), nil). - Times(1) - mockClient.EXPECT().Submit(gomock.Any(), gomock.Any()). - Return(nil, nil) - mockClient.EXPECT().CommitStatus(gomock.Any(), gomock.Any()). - Return(newCommitStatusResponse(peer.TxValidationCode_VALID, 1), nil) - - contract := AssertNewTestContract(t, "chaincode", WithGatewayClient(mockClient)) + + mockConnection := NewMockClientConnInterface(t) + requests := make(chan *gateway.EndorseRequest, 1) + ExpectEndorse(mockConnection, CaptureInvokeRequest(requests), WithEndorseResponse(defaultEndorseResponse)) + ExpectSubmit(mockConnection) + ExpectCommitStatus(mockConnection, WithCommitStatusResponse(peer.TxValidationCode_VALID, 1)) + + contract := AssertNewTestContract(t, "chaincode", WithClientConnection(mockConnection)) privateData := map[string][]byte{ - "price": []byte("3000"), + "price": expectedPrice, } - - _, err := contract.Submit("transaction", WithTransient(privateData), WithEndorsingOrganizations("MY_ORG")) + _, err := contract.Submit("transaction", WithTransient(privateData), WithEndorsingOrganizations(expectedOrg)) require.NoError(t, err) - require.EqualValues(t, expectedOrgs, actualOrgs) - require.EqualValues(t, expectedPrice, actualPrice) + request := <-requests + require.ElementsMatch(t, []string{expectedOrg}, request.EndorsingOrganizations) + + transient := AssertUnmarshalProposalPayload(t, request.ProposedTransaction).TransientMap + require.EqualValues(t, expectedPrice, transient["price"]) }) t.Run("Uses signer for commit status", func(t *testing.T) { - var actual []byte expected := []byte("MY_SIGNATURE") + + mockConnection := NewMockClientConnInterface(t) + ExpectEndorse(mockConnection, WithEndorseResponse(defaultEndorseResponse)) + ExpectSubmit(mockConnection) + requests := make(chan *gateway.SignedCommitStatusRequest, 1) + ExpectCommitStatus(mockConnection, CaptureInvokeRequest(requests), WithCommitStatusResponse(peer.TxValidationCode_VALID, 1)) + sign := func(digest []byte) ([]byte, error) { return expected, nil } - mockClient := NewMockGatewayClient(gomock.NewController(t)) - mockClient.EXPECT().Endorse(gomock.Any(), gomock.Any()). - Return(AssertNewEndorseResponse(t, "TRANSACTION_RESULT", "network"), nil) - mockClient.EXPECT().Submit(gomock.Any(), gomock.Any()). - Return(nil, nil) - mockClient.EXPECT().CommitStatus(gomock.Any(), gomock.Any()). - Do(func(_ context.Context, in *gateway.SignedCommitStatusRequest, _ ...grpc.CallOption) { - actual = in.Signature - }). - Return(newCommitStatusResponse(peer.TxValidationCode_VALID, 1), nil). - Times(1) - - contract := AssertNewTestContract(t, "chaincode", WithGatewayClient(mockClient), WithSign(sign)) + contract := AssertNewTestContract(t, "chaincode", WithClientConnection(mockConnection), WithSign(sign)) _, err := contract.SubmitTransaction("transaction") require.NoError(t, err) + actual := (<-requests).Signature require.EqualValues(t, expected, actual) }) t.Run("Uses hash", func(t *testing.T) { - var actual [][]byte + digests := make(chan []byte, 3) digest := []byte("MY_DIGEST") sign := func(digest []byte) ([]byte, error) { - actual = append(actual, digest) + digests <- digest return digest, nil } hash := func(message []byte) []byte { return digest } - mockClient := NewMockGatewayClient(gomock.NewController(t)) - mockClient.EXPECT().Endorse(gomock.Any(), gomock.Any()). - Return(AssertNewEndorseResponse(t, "TRANSACTION_RESULT", "network"), nil) - mockClient.EXPECT().Submit(gomock.Any(), gomock.Any()). - Return(nil, nil) - mockClient.EXPECT().CommitStatus(gomock.Any(), gomock.Any()). - Return(newCommitStatusResponse(peer.TxValidationCode_VALID, 1), nil) + mockConnection := NewMockClientConnInterface(t) + ExpectEndorse(mockConnection, WithEndorseResponse(defaultEndorseResponse)) + ExpectSubmit(mockConnection) + ExpectCommitStatus(mockConnection, WithCommitStatusResponse(peer.TxValidationCode_VALID, 1)) - contract := AssertNewTestContract(t, "chaincode", WithGatewayClient(mockClient), WithSign(sign), WithHash(hash)) + contract := AssertNewTestContract(t, "chaincode", WithClientConnection(mockConnection), WithSign(sign), WithHash(hash)) _, err := contract.SubmitTransaction("transaction") require.NoError(t, err) expected := [][]byte{digest, digest, digest} + actual := ReceiveAll(digests) require.EqualValues(t, expected, actual) }) t.Run("Commit returns transaction status", func(t *testing.T) { - mockClient := NewMockGatewayClient(gomock.NewController(t)) - mockClient.EXPECT().Endorse(gomock.Any(), gomock.Any()). - Return(AssertNewEndorseResponse(t, "TRANSACTION_RESULT", "network"), nil) - mockClient.EXPECT().Submit(gomock.Any(), gomock.Any()). - Return(nil, nil) - mockClient.EXPECT().CommitStatus(gomock.Any(), gomock.Any()). - Return(newCommitStatusResponse(peer.TxValidationCode_MVCC_READ_CONFLICT, 1), nil) + mockConnection := NewMockClientConnInterface(t) + ExpectEndorse(mockConnection, WithEndorseResponse(defaultEndorseResponse)) + ExpectSubmit(mockConnection) + ExpectCommitStatus(mockConnection, WithCommitStatusResponse(peer.TxValidationCode_MVCC_READ_CONFLICT, 1)) - contract := AssertNewTestContract(t, "chaincode", WithGatewayClient(mockClient)) + contract := AssertNewTestContract(t, "chaincode", WithClientConnection(mockConnection)) _, commit, err := contract.SubmitAsync("transaction") require.NoError(t, err) @@ -595,15 +482,12 @@ func TestSubmitTransaction(t *testing.T) { }) t.Run("Commit returns successful for successful transaction", func(t *testing.T) { - mockClient := NewMockGatewayClient(gomock.NewController(t)) - mockClient.EXPECT().Endorse(gomock.Any(), gomock.Any()). - Return(AssertNewEndorseResponse(t, "TRANSACTION_RESULT", "network"), nil) - mockClient.EXPECT().Submit(gomock.Any(), gomock.Any()). - Return(nil, nil) - mockClient.EXPECT().CommitStatus(gomock.Any(), gomock.Any()). - Return(newCommitStatusResponse(peer.TxValidationCode_VALID, 1), nil) + mockConnection := NewMockClientConnInterface(t) + ExpectEndorse(mockConnection, WithEndorseResponse(defaultEndorseResponse)) + ExpectSubmit(mockConnection) + ExpectCommitStatus(mockConnection, WithCommitStatusResponse(peer.TxValidationCode_VALID, 1)) - contract := AssertNewTestContract(t, "chaincode", WithGatewayClient(mockClient)) + contract := AssertNewTestContract(t, "chaincode", WithClientConnection(mockConnection)) _, commit, err := contract.SubmitAsync("transaction") require.NoError(t, err, "submit") @@ -615,15 +499,12 @@ func TestSubmitTransaction(t *testing.T) { }) t.Run("Commit returns unsuccessful for failed transaction", func(t *testing.T) { - mockClient := NewMockGatewayClient(gomock.NewController(t)) - mockClient.EXPECT().Endorse(gomock.Any(), gomock.Any()). - Return(AssertNewEndorseResponse(t, "TRANSACTION_RESULT", "network"), nil) - mockClient.EXPECT().Submit(gomock.Any(), gomock.Any()). - Return(nil, nil) - mockClient.EXPECT().CommitStatus(gomock.Any(), gomock.Any()). - Return(newCommitStatusResponse(peer.TxValidationCode_MVCC_READ_CONFLICT, 1), nil) + mockConnection := NewMockClientConnInterface(t) + ExpectEndorse(mockConnection, WithEndorseResponse(defaultEndorseResponse)) + ExpectSubmit(mockConnection) + ExpectCommitStatus(mockConnection, WithCommitStatusResponse(peer.TxValidationCode_MVCC_READ_CONFLICT, 1)) - contract := AssertNewTestContract(t, "chaincode", WithGatewayClient(mockClient)) + contract := AssertNewTestContract(t, "chaincode", WithClientConnection(mockConnection)) _, commit, err := contract.SubmitAsync("transaction") require.NoError(t, err, "submit") @@ -636,15 +517,13 @@ func TestSubmitTransaction(t *testing.T) { t.Run("Commit returns block number", func(t *testing.T) { expectedBlockNumber := uint64(101) - mockClient := NewMockGatewayClient(gomock.NewController(t)) - mockClient.EXPECT().Endorse(gomock.Any(), gomock.Any()). - Return(AssertNewEndorseResponse(t, "TRANSACTION_RESULT", "network"), nil) - mockClient.EXPECT().Submit(gomock.Any(), gomock.Any()). - Return(nil, nil) - mockClient.EXPECT().CommitStatus(gomock.Any(), gomock.Any()). - Return(newCommitStatusResponse(peer.TxValidationCode_MVCC_READ_CONFLICT, expectedBlockNumber), nil) - contract := AssertNewTestContract(t, "chaincode", WithGatewayClient(mockClient)) + mockConnection := NewMockClientConnInterface(t) + ExpectEndorse(mockConnection, WithEndorseResponse(defaultEndorseResponse)) + ExpectSubmit(mockConnection) + ExpectCommitStatus(mockConnection, WithCommitStatusResponse(peer.TxValidationCode_MVCC_READ_CONFLICT, expectedBlockNumber)) + + contract := AssertNewTestContract(t, "chaincode", WithClientConnection(mockConnection)) _, commit, err := contract.SubmitAsync("transaction") require.NoError(t, err, "submit") @@ -656,24 +535,10 @@ func TestSubmitTransaction(t *testing.T) { }) t.Run("Uses default context for endorse", func(t *testing.T) { - mockClient := NewMockGatewayClient(gomock.NewController(t)) - mockClient.EXPECT().Endorse(gomock.Any(), gomock.Any()). - DoAndReturn(func(ctx context.Context, _ *gateway.EndorseRequest, _ ...grpc.CallOption) (*gateway.EndorseResponse, error) { - select { - case <-time.After(1 * time.Second): - return AssertNewEndorseResponse(t, "TRANSACTION_RESULT", "network"), nil - case <-ctx.Done(): // Zero timeout context should cancel immediately, selecting this case - return nil, ctx.Err() - } - }) - mockClient.EXPECT().Submit(gomock.Any(), gomock.Any()). - Return(nil, nil). - AnyTimes() - mockClient.EXPECT().CommitStatus(gomock.Any(), gomock.Any()). - Return(newCommitStatusResponse(peer.TxValidationCode_VALID, 1), nil). - AnyTimes() - - contract := AssertNewTestContract(t, "chaincode", WithGatewayClient(mockClient), WithEndorseTimeout(0)) + mockConnection := NewMockClientConnInterface(t) + ExpectEndorse(mockConnection, WithInvokeContextErr(), WithEndorseResponse(defaultEndorseResponse)) + + contract := AssertNewTestContract(t, "chaincode", WithClientConnection(mockConnection), WithEndorseTimeout(0)) _, err := contract.Submit("transaction") @@ -681,23 +546,11 @@ func TestSubmitTransaction(t *testing.T) { }) t.Run("Uses default context for submit", func(t *testing.T) { - mockClient := NewMockGatewayClient(gomock.NewController(t)) - mockClient.EXPECT().Endorse(gomock.Any(), gomock.Any()). - Return(AssertNewEndorseResponse(t, "TRANSACTION_RESULT", "network"), nil) - mockClient.EXPECT().Submit(gomock.Any(), gomock.Any()). - DoAndReturn(func(ctx context.Context, _ *gateway.SubmitRequest, _ ...grpc.CallOption) (*gateway.SubmitResponse, error) { - select { - case <-time.After(1 * time.Second): - return nil, nil - case <-ctx.Done(): // Zero timeout context should cancel immediately, selecting this case - return nil, ctx.Err() - } - }) - mockClient.EXPECT().CommitStatus(gomock.Any(), gomock.Any()). - Return(newCommitStatusResponse(peer.TxValidationCode_VALID, 1), nil). - AnyTimes() - - contract := AssertNewTestContract(t, "chaincode", WithGatewayClient(mockClient), WithSubmitTimeout(0)) + mockConnection := NewMockClientConnInterface(t) + ExpectEndorse(mockConnection, WithEndorseResponse(defaultEndorseResponse)) + ExpectSubmit(mockConnection, WithInvokeContextErr()) + + contract := AssertNewTestContract(t, "chaincode", WithClientConnection(mockConnection), WithSubmitTimeout(0)) _, err := contract.Submit("transaction") @@ -724,61 +577,41 @@ func TestSubmitTransaction(t *testing.T) { }, } { t.Run(name, func(t *testing.T) { - var endorseCtxErr error - var submitCtxErr error - var commitStatusCtxErr error + endorseContexts := make(chan context.Context, 1) + submitContexts := make(chan context.Context, 1) + commitStatusContexts := make(chan context.Context, 1) ctx, cancel := context.WithCancel(context.Background()) cancel() - mockClient := NewMockGatewayClient(gomock.NewController(t)) - mockClient.EXPECT().Endorse(gomock.Any(), gomock.Any()). - Do(func(ctx context.Context, _ *gateway.EndorseRequest, _ ...grpc.CallOption) { - endorseCtxErr = ctx.Err() - }). - Return(AssertNewEndorseResponse(t, "TRANSACTION_RESULT", "network"), nil). - Times(1) - mockClient.EXPECT().Submit(gomock.Any(), gomock.Any()). - Do(func(ctx context.Context, _ *gateway.SubmitRequest, _ ...grpc.CallOption) { - submitCtxErr = ctx.Err() - }). - Return(nil, nil). - Times(1) - mockClient.EXPECT().CommitStatus(gomock.Any(), gomock.Any()). - Do(func(ctx context.Context, _ *gateway.SignedCommitStatusRequest, _ ...grpc.CallOption) { - commitStatusCtxErr = ctx.Err() - }). - Return(newCommitStatusResponse(peer.TxValidationCode_VALID, 1), nil). - Times(1) - - contract := AssertNewTestContract(t, "chaincode", WithGatewayClient(mockClient)) + mockConnection := NewMockClientConnInterface(t) + ExpectEndorse(mockConnection, + CaptureInvokeContext(endorseContexts), + WithEndorseResponse(defaultEndorseResponse), + ) + ExpectSubmit(mockConnection, CaptureInvokeContext(submitContexts)) + ExpectCommitStatus(mockConnection, + CaptureInvokeContext(commitStatusContexts), + WithCommitStatusResponse(peer.TxValidationCode_VALID, 1), + ) + + contract := AssertNewTestContract(t, "chaincode", WithClientConnection(mockConnection)) testCase.run(t, ctx, contract) - require.ErrorIs(t, endorseCtxErr, context.Canceled, "endorse context") - require.ErrorIs(t, submitCtxErr, context.Canceled, "submit context") - require.ErrorIs(t, commitStatusCtxErr, context.Canceled, "commit status context") + require.ErrorIs(t, (<-endorseContexts).Err(), context.Canceled, "endorse context") + require.ErrorIs(t, (<-submitContexts).Err(), context.Canceled, "submit context") + require.ErrorIs(t, (<-commitStatusContexts).Err(), context.Canceled, "commit status context") }) } t.Run("Uses default context for commit status", func(t *testing.T) { - mockClient := NewMockGatewayClient(gomock.NewController(t)) - mockClient.EXPECT().Endorse(gomock.Any(), gomock.Any()). - Return(AssertNewEndorseResponse(t, "TRANSACTION_RESULT", "network"), nil) - mockClient.EXPECT().Submit(gomock.Any(), gomock.Any()). - Return(nil, nil) - mockClient.EXPECT().CommitStatus(gomock.Any(), gomock.Any()). - Return(newCommitStatusResponse(peer.TxValidationCode_VALID, 1), nil). - DoAndReturn(func(ctx context.Context, _ *gateway.SignedCommitStatusRequest, _ ...grpc.CallOption) (*gateway.CommitStatusResponse, error) { - select { - case <-time.After(1 * time.Second): - return nil, nil - case <-ctx.Done(): // Zero timeout context should cancel immediately, selecting this case - return nil, ctx.Err() - } - }) - - contract := AssertNewTestContract(t, "chaincode", WithGatewayClient(mockClient), WithCommitStatusTimeout(0)) + mockConnection := NewMockClientConnInterface(t) + ExpectEndorse(mockConnection, WithEndorseResponse(defaultEndorseResponse)) + ExpectSubmit(mockConnection) + ExpectCommitStatus(mockConnection, WithInvokeContextErr()) + + contract := AssertNewTestContract(t, "chaincode", WithClientConnection(mockConnection), WithCommitStatusTimeout(0)) _, err := contract.Submit("transaction") @@ -786,18 +619,16 @@ func TestSubmitTransaction(t *testing.T) { }) t.Run("Endorse uses specified gRPC call options", func(t *testing.T) { - var actual []grpc.CallOption + callOptions := make(chan []grpc.CallOption, 1) expected := grpc.WaitForReady(true) - mockClient := NewMockGatewayClient(gomock.NewController(t)) - mockClient.EXPECT().Endorse(gomock.Any(), gomock.Any(), gomock.Any()). - Do(func(_ context.Context, _ *gateway.EndorseRequest, opts ...grpc.CallOption) { - actual = opts - }). - Return(AssertNewEndorseResponse(t, "TRANSACTION_RESULT", "network"), nil). - Times(1) + mockConnection := NewMockClientConnInterface(t) + ExpectEndorse(mockConnection, + CaptureInvokeOptions(callOptions), + WithEndorseResponse(defaultEndorseResponse), + ) - contract := AssertNewTestContract(t, "chaincode", WithGatewayClient(mockClient)) + contract := AssertNewTestContract(t, "chaincode", WithClientConnection(mockConnection)) proposal, err := contract.NewProposal("transaction") require.NoError(t, err, "NewProposal") @@ -805,22 +636,20 @@ func TestSubmitTransaction(t *testing.T) { _, err = proposal.Endorse(expected) require.NoError(t, err, "Endorse") - require.Contains(t, actual, expected, "CallOptions") + require.Contains(t, <-callOptions, expected, "CallOptions") }) t.Run("Endorse uses specified gRPC call options with specified context", func(t *testing.T) { - var actual []grpc.CallOption + callOptions := make(chan []grpc.CallOption, 1) expected := grpc.WaitForReady(true) - mockClient := NewMockGatewayClient(gomock.NewController(t)) - mockClient.EXPECT().Endorse(gomock.Any(), gomock.Any(), gomock.Any()). - Do(func(_ context.Context, _ *gateway.EndorseRequest, opts ...grpc.CallOption) { - actual = opts - }). - Return(AssertNewEndorseResponse(t, "TRANSACTION_RESULT", "network"), nil). - Times(1) + mockConnection := NewMockClientConnInterface(t) + ExpectEndorse(mockConnection, + CaptureInvokeOptions(callOptions), + WithEndorseResponse(defaultEndorseResponse), + ) - contract := AssertNewTestContract(t, "chaincode", WithGatewayClient(mockClient)) + contract := AssertNewTestContract(t, "chaincode", WithClientConnection(mockConnection)) proposal, err := contract.NewProposal("transaction") require.NoError(t, err, "NewProposal") @@ -831,24 +660,18 @@ func TestSubmitTransaction(t *testing.T) { _, err = proposal.EndorseWithContext(ctx, expected) require.NoError(t, err, "Endorse") - require.Contains(t, actual, expected, "CallOptions") + require.Contains(t, <-callOptions, expected, "CallOptions") }) t.Run("Submit uses specified gRPC call options", func(t *testing.T) { - var actual []grpc.CallOption + callOptions := make(chan []grpc.CallOption, 1) expected := grpc.WaitForReady(true) - mockClient := NewMockGatewayClient(gomock.NewController(t)) - mockClient.EXPECT().Endorse(gomock.Any(), gomock.Any(), gomock.Any()). - Return(AssertNewEndorseResponse(t, "TRANSACTION_RESULT", "network"), nil) - mockClient.EXPECT().Submit(gomock.Any(), gomock.Any(), gomock.Any()). - Do(func(_ context.Context, _ *gateway.SubmitRequest, opts ...grpc.CallOption) { - actual = opts - }). - Return(nil, nil). - Times(1) + mockConnection := NewMockClientConnInterface(t) + ExpectEndorse(mockConnection, WithEndorseResponse(defaultEndorseResponse)) + ExpectSubmit(mockConnection, CaptureInvokeOptions(callOptions)) - contract := AssertNewTestContract(t, "chaincode", WithGatewayClient(mockClient)) + contract := AssertNewTestContract(t, "chaincode", WithClientConnection(mockConnection)) proposal, err := contract.NewProposal("transaction") require.NoError(t, err, "NewProposal") @@ -859,24 +682,18 @@ func TestSubmitTransaction(t *testing.T) { _, err = transaction.Submit(expected) require.NoError(t, err, "Submit") - require.Contains(t, actual, expected, "CallOptions") + require.Contains(t, <-callOptions, expected, "CallOptions") }) t.Run("Submit uses specified gRPC call options with specified context", func(t *testing.T) { - var actual []grpc.CallOption + callOptions := make(chan []grpc.CallOption, 1) expected := grpc.WaitForReady(true) - mockClient := NewMockGatewayClient(gomock.NewController(t)) - mockClient.EXPECT().Endorse(gomock.Any(), gomock.Any(), gomock.Any()). - Return(AssertNewEndorseResponse(t, "TRANSACTION_RESULT", "network"), nil) - mockClient.EXPECT().Submit(gomock.Any(), gomock.Any(), gomock.Any()). - Do(func(_ context.Context, _ *gateway.SubmitRequest, opts ...grpc.CallOption) { - actual = opts - }). - Return(nil, nil). - Times(1) + mockConnection := NewMockClientConnInterface(t) + ExpectEndorse(mockConnection, WithEndorseResponse(defaultEndorseResponse)) + ExpectSubmit(mockConnection, CaptureInvokeOptions(callOptions)) - contract := AssertNewTestContract(t, "chaincode", WithGatewayClient(mockClient)) + contract := AssertNewTestContract(t, "chaincode", WithClientConnection(mockConnection)) proposal, err := contract.NewProposal("transaction") require.NoError(t, err, "NewProposal") @@ -890,26 +707,22 @@ func TestSubmitTransaction(t *testing.T) { _, err = transaction.SubmitWithContext(ctx, expected) require.NoError(t, err, "Submit") - require.Contains(t, actual, expected, "CallOptions") + require.Contains(t, <-callOptions, expected, "CallOptions") }) t.Run("CommisStatus uses specified gRPC call options", func(t *testing.T) { - var actual []grpc.CallOption + callOptions := make(chan []grpc.CallOption, 1) expected := grpc.WaitForReady(true) - mockClient := NewMockGatewayClient(gomock.NewController(t)) - mockClient.EXPECT().Endorse(gomock.Any(), gomock.Any(), gomock.Any()). - Return(AssertNewEndorseResponse(t, "TRANSACTION_RESULT", "network"), nil) - mockClient.EXPECT().Submit(gomock.Any(), gomock.Any(), gomock.Any()). - Return(nil, nil) - mockClient.EXPECT().CommitStatus(gomock.Any(), gomock.Any(), gomock.Any()). - Return(newCommitStatusResponse(peer.TxValidationCode_VALID, 1), nil). - Do(func(ctx context.Context, _ *gateway.SignedCommitStatusRequest, opts ...grpc.CallOption) { - actual = opts - }). - Times(1) + mockConnection := NewMockClientConnInterface(t) + ExpectEndorse(mockConnection, WithEndorseResponse(defaultEndorseResponse)) + ExpectSubmit(mockConnection) + ExpectCommitStatus(mockConnection, + CaptureInvokeOptions(callOptions), + WithCommitStatusResponse(peer.TxValidationCode_VALID, 1), + ) - contract := AssertNewTestContract(t, "chaincode", WithGatewayClient(mockClient)) + contract := AssertNewTestContract(t, "chaincode", WithClientConnection(mockConnection)) proposal, err := contract.NewProposal("transaction") require.NoError(t, err, "NewProposal") @@ -923,26 +736,22 @@ func TestSubmitTransaction(t *testing.T) { _, err = commit.Status(expected) require.NoError(t, err, "Status") - require.Contains(t, actual, expected, "CallOptions") + require.Contains(t, <-callOptions, expected, "CallOptions") }) t.Run("CommisStatus uses specified gRPC call options with specified context", func(t *testing.T) { - var actual []grpc.CallOption + callOptions := make(chan []grpc.CallOption, 1) expected := grpc.WaitForReady(true) - mockClient := NewMockGatewayClient(gomock.NewController(t)) - mockClient.EXPECT().Endorse(gomock.Any(), gomock.Any(), gomock.Any()). - Return(AssertNewEndorseResponse(t, "TRANSACTION_RESULT", "network"), nil) - mockClient.EXPECT().Submit(gomock.Any(), gomock.Any(), gomock.Any()). - Return(nil, nil) - mockClient.EXPECT().CommitStatus(gomock.Any(), gomock.Any(), gomock.Any()). - Return(newCommitStatusResponse(peer.TxValidationCode_VALID, 1), nil). - Do(func(ctx context.Context, _ *gateway.SignedCommitStatusRequest, opts ...grpc.CallOption) { - actual = opts - }). - Times(1) + mockConnection := NewMockClientConnInterface(t) + ExpectEndorse(mockConnection, WithEndorseResponse(defaultEndorseResponse)) + ExpectSubmit(mockConnection) + ExpectCommitStatus(mockConnection, + CaptureInvokeOptions(callOptions), + WithCommitStatusResponse(peer.TxValidationCode_VALID, 1), + ) - contract := AssertNewTestContract(t, "chaincode", WithGatewayClient(mockClient)) + contract := AssertNewTestContract(t, "chaincode", WithClientConnection(mockConnection)) proposal, err := contract.NewProposal("transaction") require.NoError(t, err, "NewProposal") @@ -959,6 +768,6 @@ func TestSubmitTransaction(t *testing.T) { _, err = commit.StatusWithContext(ctx, expected) require.NoError(t, err, "Status") - require.Contains(t, actual, expected, "CallOptions") + require.Contains(t, <-callOptions, expected, "CallOptions") }) } diff --git a/pkg/internal/test/credentials.go b/pkg/internal/test/credentials.go index 5813af844..252ec6f48 100644 --- a/pkg/internal/test/credentials.go +++ b/pkg/internal/test/credentials.go @@ -9,7 +9,6 @@ import ( "crypto/ed25519" "crypto/elliptic" "crypto/rand" - "crypto/rsa" "crypto/x509" "crypto/x509/pkix" "fmt" @@ -27,19 +26,6 @@ func NewEd25519KeyPair() (ed25519.PublicKey, ed25519.PrivateKey, error) { return ed25519.GenerateKey(rand.Reader) } -func publicKey(priv crypto.PrivateKey) crypto.PublicKey { - switch k := priv.(type) { - case *rsa.PrivateKey: - return &k.PublicKey - case *ecdsa.PrivateKey: - return &k.PublicKey - case ed25519.PrivateKey: - return k.Public().(ed25519.PublicKey) - default: - return nil - } -} - // NewCertificate generates a new certificate from a private key for testing func NewCertificate(privateKey crypto.PrivateKey) (*x509.Certificate, error) { serialNumberLimit := new(big.Int).Lsh(big.NewInt(1), 128) @@ -66,7 +52,8 @@ func NewCertificate(privateKey crypto.PrivateKey) (*x509.Certificate, error) { DNSNames: []string{"test.example.org"}, } - certificateBytes, err := x509.CreateCertificate(rand.Reader, &template, &template, publicKey(privateKey), privateKey) + publicKey := privateKey.(crypto.Signer).Public() + certificateBytes, err := x509.CreateCertificate(rand.Reader, &template, &template, publicKey, privateKey) if err != nil { return nil, fmt.Errorf("failed to generate certificate: %w", err) } diff --git a/pkg/internal/test/transaction.go b/pkg/internal/test/transaction.go deleted file mode 100644 index 965f97784..000000000 --- a/pkg/internal/test/transaction.go +++ /dev/null @@ -1,81 +0,0 @@ -// Copyright IBM Corp. All Rights Reserved. -// SPDX-License-Identifier: Apache-2.0 - -package test - -import ( - "testing" - - "github.com/hyperledger/fabric-protos-go-apiv2/common" - "github.com/hyperledger/fabric-protos-go-apiv2/peer" - "github.com/stretchr/testify/require" - "google.golang.org/protobuf/proto" - "google.golang.org/protobuf/reflect/protoreflect" -) - -// AssertProtoEqual ensures an expected protobuf message matches an actual message -func AssertProtoEqual(t *testing.T, expected protoreflect.ProtoMessage, actual protoreflect.ProtoMessage) { - require.True(t, proto.Equal(expected, actual), "Expected %v, got %v", expected, actual) -} - -// AssertUnmarshal ensures that a protobuf is umarshaled without error -func AssertUnmarshal(t *testing.T, b []byte, m protoreflect.ProtoMessage) { - err := proto.Unmarshal(b, m) - require.NoError(t, err) -} - -// AssertUnmarshalProposalPayload ensures that a ChaincodeProposalPayload protobuf is umarshalled without error -func AssertUnmarshalProposalPayload(t *testing.T, proposedTransaction *peer.SignedProposal) *peer.ChaincodeProposalPayload { - proposal := &peer.Proposal{} - AssertUnmarshal(t, proposedTransaction.ProposalBytes, proposal) - - payload := &peer.ChaincodeProposalPayload{} - AssertUnmarshal(t, proposal.Payload, payload) - - return payload -} - -// AssertUnmarshalInvocationSpec ensures that a ChaincodeInvocationSpec protobuf is umarshalled without error -func AssertUnmarshalInvocationSpec(t *testing.T, proposedTransaction *peer.SignedProposal) *peer.ChaincodeInvocationSpec { - proposal := &peer.Proposal{} - AssertUnmarshal(t, proposedTransaction.ProposalBytes, proposal) - - payload := &peer.ChaincodeProposalPayload{} - AssertUnmarshal(t, proposal.Payload, payload) - - input := &peer.ChaincodeInvocationSpec{} - AssertUnmarshal(t, payload.Input, input) - - return input -} - -// AssertUnmarshalChannelheader ensures that a ChannelHeader protobuf is umarshalled without error -func AssertUnmarshalChannelheader(t *testing.T, proposedTransaction *peer.SignedProposal) *common.ChannelHeader { - header := AssertUnmarshalHeader(t, proposedTransaction) - - channelHeader := &common.ChannelHeader{} - AssertUnmarshal(t, header.ChannelHeader, channelHeader) - - return channelHeader -} - -// AssertUnmarshalHeader ensures that a Header protobuf is umarshalled without error -func AssertUnmarshalHeader(t *testing.T, proposedTransaction *peer.SignedProposal) *common.Header { - proposal := &peer.Proposal{} - AssertUnmarshal(t, proposedTransaction.ProposalBytes, proposal) - - header := &common.Header{} - AssertUnmarshal(t, proposal.Header, header) - - return header -} - -// AssertUnmarshalSignatureHeader ensures that a SignatureHeader protobuf is umarshalled without error -func AssertUnmarshalSignatureHeader(t *testing.T, proposedTransaction *peer.SignedProposal) *common.SignatureHeader { - header := AssertUnmarshalHeader(t, proposedTransaction) - - signatureHeader := &common.SignatureHeader{} - AssertUnmarshal(t, header.SignatureHeader, signatureHeader) - - return signatureHeader -} diff --git a/scenario/go/scenario_test.go b/scenario/go/scenario_test.go index dfc5bacbc..7f98b3234 100644 --- a/scenario/go/scenario_test.go +++ b/scenario/go/scenario_test.go @@ -320,7 +320,7 @@ func theResponseShouldBeJSONMatching(arg *godog.DocString) error { } func jsonEqual(a, b []byte) (bool, error) { - var j, j2 interface{} + var j, j2 any if err := json.Unmarshal(a, &j); err != nil { return false, err }