github.com/pingcap/tiflow@v0.0.0-20240520035814-5bf52d54e205/pkg/p2p/server_wrapper_test.go (about)

     1  // Copyright 2021 PingCAP, Inc.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // See the License for the specific language governing permissions and
    12  // limitations under the License.
    13  
    14  package p2p
    15  
    16  import (
    17  	"context"
    18  	"fmt"
    19  	"net"
    20  	"sync"
    21  	"sync/atomic"
    22  	"testing"
    23  	"time"
    24  
    25  	"github.com/phayes/freeport"
    26  	"github.com/pingcap/failpoint"
    27  	"github.com/pingcap/log"
    28  	"github.com/pingcap/tiflow/proto/p2p"
    29  	"github.com/stretchr/testify/mock"
    30  	"github.com/stretchr/testify/require"
    31  	"go.uber.org/zap"
    32  	"google.golang.org/grpc"
    33  	"google.golang.org/grpc/codes"
    34  	"google.golang.org/grpc/status"
    35  )
    36  
    37  type mockGrpcService struct {
    38  	mock.Mock
    39  	t           *testing.T
    40  	streamCount int64
    41  }
    42  
    43  func (s *mockGrpcService) SendMessage(stream MessageServerStream) error {
    44  	atomic.AddInt64(&s.streamCount, 1)
    45  	defer atomic.AddInt64(&s.streamCount, -1)
    46  
    47  	go func() {
    48  		for {
    49  			msg, err := stream.Recv()
    50  			if err != nil {
    51  				log.Info("error received", zap.Error(err))
    52  				return
    53  			}
    54  			s.Mock.MethodCalled("OnNewMessage", msg)
    55  		}
    56  	}()
    57  
    58  	<-stream.Context().Done()
    59  	return status.Error(codes.Canceled, stream.Context().Err().Error())
    60  }
    61  
    62  func newServerWrapperForTesting(t *testing.T) (server *ServerWrapper, newClient func() (p2p.CDCPeerToPeerClient, func()), cancel func()) {
    63  	port := freeport.GetPort()
    64  	addr := fmt.Sprintf("127.0.0.1:%d", port)
    65  	lis, err := net.Listen("tcp", addr)
    66  	require.NoError(t, err)
    67  
    68  	var opts []grpc.ServerOption
    69  	grpcServer := grpc.NewServer(opts...)
    70  
    71  	cfg := &MessageServerConfig{
    72  		MaxRecvMsgSize: 4 * 1024 * 1024, // 4MB
    73  	}
    74  	server = NewServerWrapper(cfg)
    75  	p2p.RegisterCDCPeerToPeerServer(grpcServer, server)
    76  
    77  	var wg sync.WaitGroup
    78  	wg.Add(1)
    79  	go func() {
    80  		defer wg.Done()
    81  		_ = grpcServer.Serve(lis)
    82  	}()
    83  
    84  	cancel = func() {
    85  		grpcServer.Stop()
    86  		wg.Wait()
    87  	}
    88  
    89  	newClient = func() (p2p.CDCPeerToPeerClient, func()) {
    90  		conn, err := grpc.Dial(
    91  			addr,
    92  			grpc.WithInsecure(),
    93  			grpc.WithContextDialer(func(_ context.Context, s string) (net.Conn, error) {
    94  				return net.Dial("tcp", addr)
    95  			}))
    96  		require.NoError(t, err)
    97  
    98  		cancel2 := func() {
    99  			_ = conn.Close()
   100  		}
   101  		return p2p.NewCDCPeerToPeerClient(conn), cancel2
   102  	}
   103  	return
   104  }
   105  
   106  func TestServerWrapperBasics(t *testing.T) {
   107  	ctx, cancel := context.WithTimeout(context.Background(), time.Second*10)
   108  	defer cancel()
   109  
   110  	serverWrapper, newClient, cancelServer := newServerWrapperForTesting(t)
   111  	defer cancelServer()
   112  
   113  	client, closeClient := newClient()
   114  	defer closeClient()
   115  
   116  	// initiates a stream to an empty server
   117  	clientStream, err := client.SendMessage(ctx)
   118  	require.NoError(t, err)
   119  
   120  	_, err = clientStream.Recv()
   121  	require.Error(t, err)
   122  
   123  	st, ok := status.FromError(err)
   124  	require.True(t, ok)
   125  	require.Equal(t, codes.Unavailable, st.Code())
   126  
   127  	innerServer := &mockGrpcService{t: t}
   128  
   129  	serverWrapper.Reset(innerServer)
   130  
   131  	clientStream, err = client.SendMessage(ctx)
   132  	require.NoError(t, err)
   133  
   134  	innerServer.On("OnNewMessage", &p2p.MessagePacket{})
   135  	err = clientStream.Send(&p2p.MessagePacket{})
   136  	require.NoError(t, err)
   137  
   138  	time.Sleep(100 * time.Millisecond)
   139  	innerServer.AssertExpectations(t)
   140  
   141  	require.Equal(t, int64(1), atomic.LoadInt64(&innerServer.streamCount))
   142  
   143  	serverWrapper.Reset(nil)
   144  	_, err = clientStream.Recv()
   145  	require.Error(t, err)
   146  
   147  	st, ok = status.FromError(err)
   148  	require.True(t, ok)
   149  	require.Equal(t, codes.Canceled, st.Code())
   150  }
   151  
   152  func TestServerWrapperDelayed(t *testing.T) {
   153  	ctx, cancel := context.WithTimeout(context.Background(), time.Second*10)
   154  	defer cancel()
   155  
   156  	serverWrapper, newClient, cancelServer := newServerWrapperForTesting(t)
   157  	defer cancelServer()
   158  
   159  	client, closeClient := newClient()
   160  	defer closeClient()
   161  
   162  	err := failpoint.Enable("github.com/pingcap/tiflow/pkg/p2p/ServerWrapperSendMessageDelay", "pause")
   163  	require.NoError(t, err)
   164  	defer func() {
   165  		_ = failpoint.Disable("github.com/pingcap/tiflow/pkg/p2p/ServerWrapperSendMessageDelay")
   166  	}()
   167  
   168  	innerServer := &mockGrpcService{t: t}
   169  
   170  	serverWrapper.Reset(innerServer)
   171  	innerServer.On("OnNewMessage", &p2p.MessagePacket{})
   172  
   173  	// initiates a stream to an empty server
   174  	clientStream, err := client.SendMessage(ctx)
   175  	require.NoError(t, err)
   176  
   177  	err = clientStream.Send(&p2p.MessagePacket{})
   178  	require.NoError(t, err)
   179  
   180  	require.Eventually(t, func() bool {
   181  		serverWrapper.wrappedStreamsMu.Lock()
   182  		defer serverWrapper.wrappedStreamsMu.Unlock()
   183  		return len(serverWrapper.wrappedStreams) > 0
   184  	}, time.Second*1, time.Millisecond*20)
   185  
   186  	serverWrapper.Reset(nil)
   187  
   188  	_ = failpoint.Disable("github.com/pingcap/tiflow/pkg/p2p/ServerWrapperSendMessageDelay")
   189  
   190  	// It is enough for this test case to finish without panicking.
   191  }