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 }