github.com/hechain20/hechain@v0.0.0-20220316014945-b544036ba106/internal/pkg/gateway/event/iterator_test.go (about)

     1  /*
     2  Copyright hechain. All Rights Reserved.
     3  SPDX-License-Identifier: Apache-2.0
     4  */
     5  
     6  package event_test
     7  
     8  import (
     9  	"fmt"
    10  	"testing"
    11  	"time"
    12  
    13  	"github.com/golang/protobuf/proto"
    14  	"github.com/golang/protobuf/ptypes/timestamp"
    15  	"github.com/hechain20/hechain/common/ledger"
    16  	"github.com/hechain20/hechain/internal/pkg/gateway/event"
    17  	"github.com/hechain20/hechain/internal/pkg/gateway/event/mocks"
    18  	"github.com/hechain20/hechain/protoutil"
    19  	"github.com/hyperledger/fabric-protos-go/common"
    20  	"github.com/hyperledger/fabric-protos-go/gateway"
    21  	"github.com/hyperledger/fabric-protos-go/peer"
    22  	"github.com/pkg/errors"
    23  	"github.com/stretchr/testify/require"
    24  )
    25  
    26  //go:generate counterfeiter -o mocks/resultsiterator.go --fake-name ResultsIterator . mockResultsIterator
    27  type mockResultsIterator interface {
    28  	ledger.ResultsIterator
    29  }
    30  
    31  func TestIterators(t *testing.T) {
    32  	now := time.Now()
    33  	transactionId := "TRANSACTION_ID"
    34  
    35  	chaincodeEvent := &peer.ChaincodeEvent{
    36  		ChaincodeId: "CHAINCODE_ID",
    37  		TxId:        transactionId,
    38  		EventName:   "EVENT_NAME",
    39  		Payload:     []byte("PAYLOAD"),
    40  	}
    41  
    42  	txEnvelope := &common.Envelope{
    43  		Payload: protoutil.MarshalOrPanic(&common.Payload{
    44  			Header: &common.Header{
    45  				ChannelHeader: protoutil.MarshalOrPanic(&common.ChannelHeader{
    46  					Type: int32(common.HeaderType_ENDORSER_TRANSACTION),
    47  					Timestamp: &timestamp.Timestamp{
    48  						Seconds: now.Unix(),
    49  						Nanos:   int32(now.Nanosecond()),
    50  					},
    51  					TxId: transactionId,
    52  				}),
    53  			},
    54  			Data: protoutil.MarshalOrPanic(&peer.Transaction{
    55  				Actions: []*peer.TransactionAction{
    56  					{
    57  						Payload: protoutil.MarshalOrPanic(&peer.ChaincodeActionPayload{
    58  							Action: &peer.ChaincodeEndorsedAction{
    59  								ProposalResponsePayload: protoutil.MarshalOrPanic(&peer.ProposalResponsePayload{
    60  									Extension: protoutil.MarshalOrPanic(&peer.ChaincodeAction{
    61  										Events: protoutil.MarshalOrPanic(chaincodeEvent),
    62  									}),
    63  								}),
    64  							},
    65  						}),
    66  					},
    67  				},
    68  			}),
    69  		}),
    70  	}
    71  
    72  	blockProto := &common.Block{
    73  		Header: &common.BlockHeader{
    74  			Number: 1337,
    75  		},
    76  		Metadata: &common.BlockMetadata{
    77  			Metadata: [][]byte{
    78  				nil,
    79  				nil,
    80  				{
    81  					byte(peer.TxValidationCode_MVCC_READ_CONFLICT),
    82  					byte(peer.TxValidationCode_VALID),
    83  					byte(peer.TxValidationCode_VALID),
    84  				},
    85  				nil,
    86  				nil,
    87  			},
    88  		},
    89  		Data: &common.BlockData{
    90  			Data: [][]byte{
    91  				protoutil.MarshalOrPanic(txEnvelope),
    92  				protoutil.MarshalOrPanic(txEnvelope),
    93  				protoutil.MarshalOrPanic(&common.Envelope{
    94  					Payload: protoutil.MarshalOrPanic(&common.Payload{
    95  						Header: &common.Header{
    96  							ChannelHeader: protoutil.MarshalOrPanic(&common.ChannelHeader{
    97  								Type: int32(common.HeaderType_CONFIG_UPDATE),
    98  							}),
    99  						},
   100  					}),
   101  				}),
   102  			},
   103  		},
   104  	}
   105  
   106  	const invalidTxIndex = 0
   107  	const validTxIndex = 1
   108  
   109  	assertExpectedBlock := func(t *testing.T, block *event.Block) {
   110  		require.NotNil(t, block, "block")
   111  		require.EqualValues(t, blockProto.GetHeader().GetNumber(), block.Number(), "block.Number()")
   112  
   113  		transactions, err := block.Transactions()
   114  		require.NoError(t, err, "Transactions()")
   115  		require.Len(t, transactions, 2, "transactions")
   116  
   117  		for txIndex, transaction := range transactions {
   118  			require.Equal(t, block, transaction.Block(), "transaction[%d].Block()", txIndex)
   119  			require.Equal(t, transactionId, transaction.ID(), "transaction[%d].ID()", txIndex)
   120  			require.EqualValues(t, now.Unix(), transaction.Timestamp().Seconds, "transaction[%d].Timestamp.Seconds", txIndex)
   121  			require.EqualValues(t, now.Nanosecond(), transaction.Timestamp().Nanos, "transaction[%d].Tomestamp.Nanos", txIndex)
   122  
   123  			events, err := transaction.ChaincodeEvents()
   124  			require.NoError(t, err, "ChaincodeEvents()")
   125  			require.Len(t, events, 1, "chaincodeEvents")
   126  
   127  			for eventIndex, event := range events {
   128  				require.Equal(t, transaction, event.Transaction(), "transaction[%d].ChaincodeEvents()[%d].Transaction()", txIndex, eventIndex)
   129  				require.Equal(t, chaincodeEvent.GetChaincodeId(), event.ChaincodeID(), "transaction[%d].ChaincodeEvents()[%d].ChaincodeID()", txIndex, eventIndex)
   130  				require.Equal(t, chaincodeEvent.GetEventName(), event.EventName(), "transaction[%d].ChaincodeEvents()[%d].EventName()", txIndex, eventIndex)
   131  				require.EqualValues(t, chaincodeEvent.GetPayload(), event.Payload(), "transaction[%d].ChaincodeEvents()[%d].Payload()", txIndex, eventIndex)
   132  				require.True(t, proto.Equal(chaincodeEvent, event.ProtoMessage()), "transaction[%d].ChaincodeEvents()[%d].ProtoMessage(): %v", txIndex, eventIndex, event.ProtoMessage())
   133  			}
   134  		}
   135  	}
   136  
   137  	t.Run("BlockIterator", func(t *testing.T) {
   138  		t.Run("Next", func(t *testing.T) {
   139  			t.Run("returns error from wrapped iterator", func(t *testing.T) {
   140  				resultIter := &mocks.ResultsIterator{}
   141  				resultIter.NextReturns(nil, errors.New("MY_ERROR"))
   142  
   143  				blockIter := event.NewBlockIterator(resultIter)
   144  				_, err := blockIter.Next()
   145  
   146  				require.ErrorContains(t, err, "MY_ERROR")
   147  			})
   148  
   149  			t.Run("returns error if wrapped iterator returns unexpected type", func(t *testing.T) {
   150  				resultIter := &mocks.ResultsIterator{}
   151  				result := &common.Envelope{}
   152  				resultIter.NextReturns(result, nil)
   153  
   154  				blockIter := event.NewBlockIterator(resultIter)
   155  				_, err := blockIter.Next()
   156  
   157  				require.ErrorContains(t, err, fmt.Sprintf("%T", result))
   158  			})
   159  
   160  			t.Run("returns a block with no transactions", func(t *testing.T) {
   161  				resultIter := &mocks.ResultsIterator{}
   162  				result := &common.Block{
   163  					Header: &common.BlockHeader{
   164  						Number: 418,
   165  					},
   166  				}
   167  				resultIter.NextReturns(result, nil)
   168  
   169  				blockIter := event.NewBlockIterator(resultIter)
   170  				block, err := blockIter.Next()
   171  
   172  				require.NoError(t, err, "Next()")
   173  				require.NotNil(t, block, "block")
   174  				require.EqualValues(t, result.GetHeader().GetNumber(), block.Number(), "Number()")
   175  
   176  				transactions, err := block.Transactions()
   177  				require.NoError(t, err, "Transactions()")
   178  				require.Len(t, transactions, 0, "transactions")
   179  			})
   180  
   181  			t.Run("returns a block with invalid transaction", func(t *testing.T) {
   182  				resultIter := &mocks.ResultsIterator{}
   183  				resultIter.NextReturns(blockProto, nil)
   184  
   185  				blockIter := event.NewBlockIterator(resultIter)
   186  				block, err := blockIter.Next()
   187  
   188  				require.NoError(t, err, "Next()")
   189  				assertExpectedBlock(t, block)
   190  
   191  				transactions, _ := block.Transactions()
   192  				transaction := transactions[invalidTxIndex]
   193  				require.Equal(t, peer.TxValidationCode_MVCC_READ_CONFLICT, transaction.Status())
   194  				require.False(t, transaction.Valid(), "Valid()")
   195  			})
   196  
   197  			t.Run("returns a block with valid transaction", func(t *testing.T) {
   198  				resultIter := &mocks.ResultsIterator{}
   199  				resultIter.NextReturns(blockProto, nil)
   200  
   201  				blockIter := event.NewBlockIterator(resultIter)
   202  				block, err := blockIter.Next()
   203  
   204  				require.NoError(t, err, "Next()")
   205  				assertExpectedBlock(t, block)
   206  
   207  				transactions, _ := block.Transactions()
   208  				transaction := transactions[validTxIndex]
   209  				require.Equal(t, peer.TxValidationCode_VALID, transaction.Status())
   210  				require.True(t, transaction.Valid(), "Valid()")
   211  			})
   212  		})
   213  
   214  		t.Run("Close", func(t *testing.T) {
   215  			t.Run("closes wrapped iterator", func(t *testing.T) {
   216  				resultIter := &mocks.ResultsIterator{}
   217  
   218  				blockIter := event.NewBlockIterator(resultIter)
   219  				blockIter.Close()
   220  
   221  				require.Equal(t, 1, resultIter.CloseCallCount())
   222  			})
   223  		})
   224  	})
   225  
   226  	t.Run("ChaincodeEventsIterator", func(t *testing.T) {
   227  		t.Run("Next", func(t *testing.T) {
   228  			t.Run("returns error from wrapped iterator", func(t *testing.T) {
   229  				resultIter := &mocks.ResultsIterator{}
   230  				resultIter.NextReturns(nil, errors.New("MY_ERROR"))
   231  
   232  				eventsIter := event.NewChaincodeEventsIterator(resultIter)
   233  				_, err := eventsIter.Next()
   234  
   235  				require.ErrorContains(t, err, "MY_ERROR")
   236  			})
   237  		})
   238  
   239  		t.Run("only returns events for valid transactions", func(t *testing.T) {
   240  			resultIter := &mocks.ResultsIterator{}
   241  			resultIter.NextReturns(blockProto, nil)
   242  
   243  			eventsIter := event.NewChaincodeEventsIterator(resultIter)
   244  			actual, err := eventsIter.Next()
   245  
   246  			require.NoError(t, err, "Next()")
   247  			require.NotNil(t, actual, "events")
   248  
   249  			expected := &gateway.ChaincodeEventsResponse{
   250  				BlockNumber: blockProto.GetHeader().GetNumber(),
   251  				Events: []*peer.ChaincodeEvent{
   252  					chaincodeEvent,
   253  				},
   254  			}
   255  			require.True(t, proto.Equal(expected, actual), "ChaincodeEventsResponse: %v", actual)
   256  		})
   257  
   258  		t.Run("skips blocks with no valid chaincode events", func(t *testing.T) {
   259  			emptyBlock := &common.Block{
   260  				Header: &common.BlockHeader{
   261  					Number: 418,
   262  				},
   263  			}
   264  			resultIter := &mocks.ResultsIterator{}
   265  			resultIter.NextReturnsOnCall(0, emptyBlock, nil)
   266  			resultIter.NextReturnsOnCall(1, blockProto, nil)
   267  
   268  			eventsIter := event.NewChaincodeEventsIterator(resultIter)
   269  			actual, err := eventsIter.Next()
   270  
   271  			require.NoError(t, err, "Next()")
   272  			require.NotNil(t, actual, "events")
   273  
   274  			expected := &gateway.ChaincodeEventsResponse{
   275  				BlockNumber: blockProto.GetHeader().GetNumber(),
   276  				Events: []*peer.ChaincodeEvent{
   277  					chaincodeEvent,
   278  				},
   279  			}
   280  			require.True(t, proto.Equal(expected, actual), "ChaincodeEventsResponse: %v", actual)
   281  		})
   282  
   283  		t.Run("Close", func(t *testing.T) {
   284  			t.Run("closes wrapped iterator", func(t *testing.T) {
   285  				resultIter := &mocks.ResultsIterator{}
   286  
   287  				eventsIter := event.NewChaincodeEventsIterator(resultIter)
   288  				eventsIter.Close()
   289  
   290  				require.Equal(t, 1, resultIter.CloseCallCount())
   291  			})
   292  		})
   293  	})
   294  }