github.com/hechain20/hechain@v0.0.0-20220316014945-b544036ba106/orderer/common/cluster/service_test.go (about)

     1  /*
     2  Copyright hechain. 2017 All Rights Reserved.
     3  
     4  SPDX-License-Identifier: Apache-2.0
     5  */
     6  
     7  package cluster_test
     8  
     9  import (
    10  	"context"
    11  	"io"
    12  	"strings"
    13  	"testing"
    14  	"time"
    15  
    16  	"github.com/hechain20/hechain/common/crypto/tlsgen"
    17  	"github.com/hechain20/hechain/common/flogging"
    18  	"github.com/hechain20/hechain/common/metrics/disabled"
    19  	"github.com/hechain20/hechain/internal/pkg/comm"
    20  	"github.com/hechain20/hechain/orderer/common/cluster"
    21  	"github.com/hechain20/hechain/orderer/common/cluster/mocks"
    22  	"github.com/hyperledger/fabric-protos-go/orderer"
    23  	"github.com/pkg/errors"
    24  	"github.com/stretchr/testify/mock"
    25  	"github.com/stretchr/testify/require"
    26  	"go.uber.org/zap"
    27  	"go.uber.org/zap/zapcore"
    28  )
    29  
    30  var (
    31  	submitRequest1 = &orderer.StepRequest{
    32  		Payload: &orderer.StepRequest_SubmitRequest{
    33  			SubmitRequest: &orderer.SubmitRequest{},
    34  		},
    35  	}
    36  	submitRequest2 = &orderer.StepRequest{
    37  		Payload: &orderer.StepRequest_SubmitRequest{
    38  			SubmitRequest: &orderer.SubmitRequest{},
    39  		},
    40  	}
    41  	submitResponse1 = &orderer.StepResponse{
    42  		Payload: &orderer.StepResponse_SubmitRes{
    43  			SubmitRes: &orderer.SubmitResponse{},
    44  		},
    45  	}
    46  	consensusRequest = &orderer.StepRequest{
    47  		Payload: &orderer.StepRequest_ConsensusRequest{
    48  			ConsensusRequest: &orderer.ConsensusRequest{
    49  				Payload: []byte{1, 2, 3},
    50  				Channel: "mychannel",
    51  			},
    52  		},
    53  	}
    54  )
    55  
    56  func TestStep(t *testing.T) {
    57  	dispatcher := &mocks.Dispatcher{}
    58  
    59  	svc := &cluster.Service{
    60  		StreamCountReporter: &cluster.StreamCountReporter{
    61  			Metrics: cluster.NewMetrics(&disabled.Provider{}),
    62  		},
    63  		Logger:     flogging.MustGetLogger("test"),
    64  		StepLogger: flogging.MustGetLogger("test"),
    65  		Dispatcher: dispatcher,
    66  	}
    67  
    68  	t.Run("Success", func(t *testing.T) {
    69  		stream := &mocks.StepStream{}
    70  		stream.On("Context").Return(context.Background())
    71  		stream.On("Recv").Return(consensusRequest, nil).Once()
    72  		stream.On("Recv").Return(consensusRequest, nil).Once()
    73  		dispatcher.On("DispatchConsensus", mock.Anything, consensusRequest.GetConsensusRequest()).Return(nil).Once()
    74  		dispatcher.On("DispatchConsensus", mock.Anything, consensusRequest.GetConsensusRequest()).Return(io.EOF).Once()
    75  		err := svc.Step(stream)
    76  		require.NoError(t, err)
    77  	})
    78  
    79  	t.Run("Failure", func(t *testing.T) {
    80  		stream := &mocks.StepStream{}
    81  		stream.On("Context").Return(context.Background())
    82  		stream.On("Recv").Return(consensusRequest, nil).Once()
    83  		dispatcher.On("DispatchConsensus", mock.Anything, consensusRequest.GetConsensusRequest()).Return(errors.New("oops")).Once()
    84  		err := svc.Step(stream)
    85  		require.EqualError(t, err, "oops")
    86  	})
    87  }
    88  
    89  func TestSubmitSuccess(t *testing.T) {
    90  	dispatcher := &mocks.Dispatcher{}
    91  
    92  	stream := &mocks.StepStream{}
    93  	stream.On("Context").Return(context.Background())
    94  	// Send to the stream 2 messages, and afterwards close the stream
    95  	stream.On("Recv").Return(submitRequest1, nil).Once()
    96  	stream.On("Recv").Return(submitRequest2, nil).Once()
    97  	stream.On("Recv").Return(nil, io.EOF).Once()
    98  	// Send should be called for each corresponding receive
    99  	stream.On("Send", submitResponse1).Return(nil).Twice()
   100  
   101  	responses := make(chan *orderer.StepRequest, 2)
   102  	responses <- submitRequest1
   103  	responses <- submitRequest2
   104  
   105  	dispatcher.On("DispatchSubmit", mock.Anything, mock.Anything).Return(nil).Once()
   106  	dispatcher.On("DispatchSubmit", mock.Anything, mock.Anything).Return(nil).Once()
   107  	// Ensure we pass requests to DispatchSubmit in-order
   108  	dispatcher.On("DispatchSubmit", mock.Anything, mock.Anything).Run(func(args mock.Arguments) {
   109  		expectedRequest := <-responses
   110  		actualRequest := args.Get(1).(*orderer.StepRequest)
   111  		require.True(t, expectedRequest == actualRequest)
   112  	})
   113  
   114  	svc := &cluster.Service{
   115  		StreamCountReporter: &cluster.StreamCountReporter{
   116  			Metrics: cluster.NewMetrics(&disabled.Provider{}),
   117  		},
   118  		Logger:     flogging.MustGetLogger("test"),
   119  		StepLogger: flogging.MustGetLogger("test"),
   120  		Dispatcher: dispatcher,
   121  	}
   122  
   123  	err := svc.Step(stream)
   124  	require.NoError(t, err)
   125  	dispatcher.AssertNumberOfCalls(t, "DispatchSubmit", 2)
   126  }
   127  
   128  type tuple struct {
   129  	msg interface{}
   130  	err error
   131  }
   132  
   133  func (t tuple) asArray() []interface{} {
   134  	return []interface{}{t.msg, t.err}
   135  }
   136  
   137  func TestSubmitFailure(t *testing.T) {
   138  	oops := errors.New("oops")
   139  	testCases := []struct {
   140  		name               string
   141  		receiveReturns     []tuple
   142  		sendReturns        []error
   143  		dispatchReturns    error
   144  		expectedDispatches int
   145  	}{
   146  		{
   147  			name: "Recv() fails",
   148  			receiveReturns: []tuple{
   149  				{msg: nil, err: oops},
   150  			},
   151  		},
   152  		{
   153  			name: "DispatchSubmit() fails",
   154  			receiveReturns: []tuple{
   155  				{msg: submitRequest1},
   156  			},
   157  			expectedDispatches: 1,
   158  			dispatchReturns:    oops,
   159  		},
   160  	}
   161  
   162  	for _, testCase := range testCases {
   163  		testCase := testCase
   164  		t.Run(testCase.name, func(t *testing.T) {
   165  			dispatcher := &mocks.Dispatcher{}
   166  			stream := &mocks.StepStream{}
   167  			stream.On("Context").Return(context.Background())
   168  			for _, recv := range testCase.receiveReturns {
   169  				stream.On("Recv").Return(recv.asArray()...).Once()
   170  			}
   171  			for _, send := range testCase.sendReturns {
   172  				stream.On("Send", mock.Anything).Return(send).Once()
   173  			}
   174  			defer dispatcher.AssertNumberOfCalls(t, "DispatchSubmit", testCase.expectedDispatches)
   175  			dispatcher.On("DispatchSubmit", mock.Anything, mock.Anything).Return(testCase.dispatchReturns)
   176  			svc := &cluster.Service{
   177  				StreamCountReporter: &cluster.StreamCountReporter{
   178  					Metrics: cluster.NewMetrics(&disabled.Provider{}),
   179  				},
   180  				Logger:     flogging.MustGetLogger("test"),
   181  				StepLogger: flogging.MustGetLogger("test"),
   182  				Dispatcher: dispatcher,
   183  			}
   184  			err := svc.Step(stream)
   185  			require.EqualError(t, err, oops.Error())
   186  		})
   187  	}
   188  }
   189  
   190  func TestIngresStreamsMetrics(t *testing.T) {
   191  	dispatcher := &mocks.Dispatcher{}
   192  	dispatcher.On("DispatchConsensus", mock.Anything, mock.Anything).Return(nil)
   193  
   194  	fakeProvider := &mocks.MetricsProvider{}
   195  	testMetrics := &testMetrics{
   196  		fakeProvider: fakeProvider,
   197  	}
   198  	testMetrics.initialize()
   199  
   200  	metrics := cluster.NewMetrics(fakeProvider)
   201  
   202  	svc := &cluster.Service{
   203  		Logger:     flogging.MustGetLogger("test"),
   204  		StepLogger: flogging.MustGetLogger("test"),
   205  		Dispatcher: dispatcher,
   206  		StreamCountReporter: &cluster.StreamCountReporter{
   207  			Metrics: metrics,
   208  		},
   209  	}
   210  
   211  	stream := &mocks.StepStream{}
   212  	stream.On("Context").Return(context.Background())
   213  	// Upon first receive, return nil to proceed to the next receive.
   214  	stream.On("Recv").Return(nil, nil).Once()
   215  	// Upon the second receive, return EOF to trigger the stream to end
   216  	stream.On("Recv").Return(nil, io.EOF).Once()
   217  
   218  	svc.Step(stream)
   219  	// The stream started so stream count incremented from 0 to 1
   220  	require.Equal(t, float64(1), testMetrics.ingressStreamsCount.SetArgsForCall(0))
   221  	// The stream ended so stream count is decremented from 1 to 0
   222  	require.Equal(t, float64(0), testMetrics.ingressStreamsCount.SetArgsForCall(1))
   223  }
   224  
   225  func TestServiceGRPC(t *testing.T) {
   226  	// Check that Service correctly implements the gRPC interface
   227  	srv, err := comm.NewGRPCServer("127.0.0.1:0", comm.ServerConfig{})
   228  	require.NoError(t, err)
   229  	orderer.RegisterClusterServer(srv.Server(), &cluster.Service{
   230  		Logger:     flogging.MustGetLogger("test"),
   231  		StepLogger: flogging.MustGetLogger("test"),
   232  	})
   233  }
   234  
   235  func TestExpirationWarningIngress(t *testing.T) {
   236  	ca, err := tlsgen.NewCA()
   237  	require.NoError(t, err)
   238  
   239  	serverCert, err := ca.NewServerCertKeyPair("127.0.0.1")
   240  	require.NoError(t, err)
   241  
   242  	clientCert, err := ca.NewClientCertKeyPair()
   243  	require.NoError(t, err)
   244  
   245  	dispatcher := &mocks.Dispatcher{}
   246  	dispatcher.On("DispatchConsensus", mock.Anything, mock.Anything).Return(nil)
   247  
   248  	svc := &cluster.Service{
   249  		CertExpWarningThreshold:          time.Until(clientCert.TLSCert.NotAfter),
   250  		MinimumExpirationWarningInterval: time.Second * 2,
   251  		StreamCountReporter: &cluster.StreamCountReporter{
   252  			Metrics: cluster.NewMetrics(&disabled.Provider{}),
   253  		},
   254  		Logger:     flogging.MustGetLogger("test"),
   255  		StepLogger: flogging.MustGetLogger("test"),
   256  		Dispatcher: dispatcher,
   257  	}
   258  
   259  	alerts := make(chan struct{}, 10)
   260  	svc.Logger = svc.Logger.WithOptions(zap.Hooks(func(entry zapcore.Entry) error {
   261  		if strings.Contains(entry.Message, "expires in less than 23h59m") {
   262  			alerts <- struct{}{}
   263  		}
   264  		return nil
   265  	}))
   266  
   267  	srvConf := comm.ServerConfig{
   268  		SecOpts: comm.SecureOptions{
   269  			Certificate:       serverCert.Cert,
   270  			Key:               serverCert.Key,
   271  			UseTLS:            true,
   272  			ClientRootCAs:     [][]byte{ca.CertBytes()},
   273  			RequireClientCert: true,
   274  		},
   275  	}
   276  
   277  	srv, err := comm.NewGRPCServer("127.0.0.1:0", srvConf)
   278  	require.NoError(t, err)
   279  	orderer.RegisterClusterServer(srv.Server(), svc)
   280  	go srv.Start()
   281  	defer srv.Stop()
   282  
   283  	clientConf := comm.ClientConfig{
   284  		DialTimeout: time.Second * 3,
   285  		SecOpts: comm.SecureOptions{
   286  			ServerRootCAs:     [][]byte{ca.CertBytes()},
   287  			UseTLS:            true,
   288  			Key:               clientCert.Key,
   289  			Certificate:       clientCert.Cert,
   290  			RequireClientCert: true,
   291  		},
   292  	}
   293  
   294  	conn, err := clientConf.Dial(srv.Address())
   295  	require.NoError(t, err)
   296  
   297  	cl := orderer.NewClusterClient(conn)
   298  	stream, err := cl.Step(context.Background())
   299  	require.NoError(t, err)
   300  
   301  	err = stream.Send(consensusRequest)
   302  	require.NoError(t, err)
   303  
   304  	// An alert is logged at the first time.
   305  	select {
   306  	case <-alerts:
   307  	case <-time.After(time.Second * 5):
   308  		t.Fatal("Should have received an alert")
   309  	}
   310  
   311  	err = stream.Send(consensusRequest)
   312  	require.NoError(t, err)
   313  
   314  	// No alerts in a consecutive time.
   315  	select {
   316  	case <-alerts:
   317  		t.Fatal("Should have not received an alert")
   318  	case <-time.After(time.Millisecond * 500):
   319  	}
   320  
   321  	// Wait for alert expiration interval to expire.
   322  	time.Sleep(svc.MinimumExpirationWarningInterval + time.Second)
   323  
   324  	err = stream.Send(consensusRequest)
   325  	require.NoError(t, err)
   326  
   327  	// An alert should be logged now after the timeout expired.
   328  	select {
   329  	case <-alerts:
   330  	case <-time.After(time.Second * 5):
   331  		t.Fatal("Should have received an alert")
   332  	}
   333  }