Skip to content

Commit

Permalink
Refactor block eventing unit tests (#760)
Browse files Browse the repository at this point in the history
Simplify tests by using an abstraction to the eventing set up and
invocation so that a set of common tests can be applied to block,
filtered block, and block and private data unit tests.

Signed-off-by: Mark S. Lewis <[email protected]>
  • Loading branch information
bestbeforetoday authored Oct 18, 2024
1 parent 5edc6fa commit b6d95e2
Show file tree
Hide file tree
Showing 7 changed files with 480 additions and 738 deletions.
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ require (
github.com/stretchr/testify v1.9.0
golang.org/x/crypto v0.28.0
google.golang.org/grpc v1.67.1
google.golang.org/protobuf v1.34.2
google.golang.org/protobuf v1.35.1
)

require (
Expand Down
4 changes: 2 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -66,8 +66,8 @@ google.golang.org/genproto/googleapis/rpc v0.0.0-20240814211410-ddb44dafa142 h1:
google.golang.org/genproto/googleapis/rpc v0.0.0-20240814211410-ddb44dafa142/go.mod h1:UqMtugtsSgubUsoxbuAoiCXvqvErP7Gf0so0mK9tHxU=
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=
google.golang.org/protobuf v1.35.1 h1:m3LfL6/Ca+fqnjnlqQXNpFPABW1UD7mjh8KO2mKFytA=
google.golang.org/protobuf v1.35.1/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
Expand Down
255 changes: 10 additions & 245 deletions pkg/client/blockevents_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,185 +5,14 @@ package client

import (
"context"
"errors"
"io"
"testing"

"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"
"google.golang.org/grpc"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
)

func TestBlockEvents(t *testing.T) {
t.Run("Returns connect error", func(t *testing.T) {
expected := NewStatusError(t, codes.Aborted, "BLOCK_EVENTS_ERROR")

mockConnection := NewMockClientConnInterface(t)
ExpectDeliver(mockConnection, WithNewStreamError(expected))

ctx, cancel := context.WithCancel(context.Background())
defer cancel()

network := AssertNewTestNetwork(t, "NETWORK", WithClientConnection(mockConnection))
_, err := network.BlockEvents(ctx)

require.Equal(t, status.Code(expected), status.Code(err), "status code")
require.ErrorIs(t, err, expected, "error type: %T", err)
require.ErrorContains(t, err, expected.Error(), "message")
})

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(),
},
},
"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(),
},
},
"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)
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) {
mockConnection := NewMockClientConnInterface(t)
mockStream := NewMockClientStream(t)
ExpectDeliver(mockConnection, WithNewStreamResult(mockStream))

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", WithClientConnection(mockConnection))
receive, err := network.BlockEvents(ctx, WithStartBlock(418))
require.NoError(t, err)

actual, ok := <-receive

require.False(t, ok, "Expected event listening to be cancelled, got %v", actual)
})

t.Run("Receives events", func(t *testing.T) {
expected := []*common.Block{
{
Expand All @@ -208,13 +37,6 @@ func TestBlockEvents(t *testing.T) {
},
}

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{
Expand All @@ -223,29 +45,23 @@ func TestBlockEvents(t *testing.T) {
},
})
}
ExpectRecvMsg(mockStream, WithRecvMsgs(responses...))

tester := NewBlockEventsTest(t)
tester.SetResponses(responses...)

ctx, cancel := context.WithCancel(context.Background())
defer cancel()

network := AssertNewTestNetwork(t, "NETWORK", WithClientConnection(mockConnection))
receive, err := network.BlockEvents(ctx)
err := tester.Events(ctx)
require.NoError(t, err)

for _, event := range expected {
actual := <-receive
actual := <-tester.BlockEvents
AssertProtoEqual(t, event, actual)
}
})

t.Run("Closes event channel on non-block message", func(t *testing.T) {
mockConnection := NewMockClientConnInterface(t)
mockStream := NewMockClientStream(t)
ExpectDeliver(mockConnection, WithNewStreamResult(mockStream))

ExpectSendMsg(mockStream)
mockStream.EXPECT().CloseSend().Maybe().Return(nil)

block := &common.Block{
Header: &common.BlockHeader{
Number: 1,
Expand Down Expand Up @@ -273,13 +89,14 @@ func TestBlockEvents(t *testing.T) {
},
},
}
ExpectRecvMsg(mockStream, WithRecvMsgs(responses...))

tester := NewBlockEventsTest(t)
tester.SetResponses(responses...)

ctx, cancel := context.WithCancel(context.Background())
defer cancel()

network := AssertNewTestNetwork(t, "NETWORK", WithClientConnection(mockConnection))
receive, err := network.BlockEvents(ctx)
err := tester.Events(ctx)
require.NoError(t, err)

expected := []*common.Block{
Expand All @@ -288,60 +105,8 @@ func TestBlockEvents(t *testing.T) {
nil,
}
for _, event := range expected {
actual := <-receive
actual := <-tester.BlockEvents
AssertProtoEqual(t, event, actual)
}
})

t.Run("Uses specified gRPC call options", func(t *testing.T) {
expected := grpc.WaitForReady(true)

mockConnection := NewMockClientConnInterface(t)
mockStream := NewMockClientStream(t)
options := make(chan []grpc.CallOption, 1)
ExpectDeliver(mockConnection, CaptureNewStreamOptions(options), WithNewStreamResult(mockStream))

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", WithClientConnection(mockConnection))
request, err := network.NewBlockEventsRequest()
require.NoError(t, err, "NewBlockEventsRequest")

_, err = request.Events(ctx, expected)
require.NoError(t, err, "Events")

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")

mockConnection := NewMockClientConnInterface(t)
mockStream := NewMockClientStream(t)
ExpectDeliver(mockConnection, WithNewStreamResult(mockStream))

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", WithClientConnection(mockConnection), WithTLSClientCertificateHash(expected))
_, err := network.BlockEvents(ctx)
require.NoError(t, err)

payload := &common.Payload{}
AssertUnmarshal(t, (<-requests).GetPayload(), payload)
channelHeader := &common.ChannelHeader{}
AssertUnmarshal(t, payload.GetHeader().GetChannelHeader(), channelHeader)

require.Equal(t, expected, channelHeader.GetTlsCertHash())
})
}
Loading

0 comments on commit b6d95e2

Please sign in to comment.