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 }