github.com/pingcap/tiflow@v0.0.0-20240520035814-5bf52d54e205/engine/pkg/client/internal/call_test.go (about) 1 // Copyright 2022 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 internal 15 16 import ( 17 "context" 18 "sync" 19 "testing" 20 21 "github.com/stretchr/testify/require" 22 "google.golang.org/grpc" 23 "google.golang.org/grpc/codes" 24 "google.golang.org/grpc/status" 25 ) 26 27 type mockRequest struct { 28 val int 29 } 30 31 type mockResponse struct { 32 val int 33 } 34 35 func TestCallNormal(t *testing.T) { 36 t.Parallel() 37 38 mockCallFn := func(ctx context.Context, req *mockRequest, opts ...grpc.CallOption) (*mockResponse, error) { 39 return &mockResponse{ 40 val: req.val, 41 }, nil 42 } 43 44 call := NewCall(mockCallFn, &mockRequest{val: 1}) 45 resp, err := call.Do(context.Background()) 46 require.NoError(t, err) 47 require.Equal(t, &mockResponse{val: 1}, resp) 48 } 49 50 func TestCallFuncCancel(t *testing.T) { 51 t.Parallel() 52 53 mockCallFn := func(ctx context.Context, req *mockRequest, opts ...grpc.CallOption) (*mockResponse, error) { 54 <-ctx.Done() 55 return nil, ctx.Err() 56 } 57 58 call := NewCall(mockCallFn, &mockRequest{val: 1}) 59 ctx, cancel := context.WithCancel(context.Background()) 60 61 var wg sync.WaitGroup 62 wg.Add(1) 63 go func() { 64 defer wg.Done() 65 66 resp, err := call.Do(ctx) 67 require.Nil(t, resp) 68 require.Error(t, err) 69 require.ErrorIs(t, err, context.Canceled) 70 }() 71 72 cancel() 73 wg.Wait() 74 } 75 76 func TestCallRetryCancel(t *testing.T) { 77 t.Parallel() 78 79 mockCallFn := func(ctx context.Context, req *mockRequest, opts ...grpc.CallOption) (*mockResponse, error) { 80 return nil, status.Error(codes.Unavailable, "") 81 } 82 83 call := NewCall(mockCallFn, &mockRequest{val: 1}) 84 ctx, cancel := context.WithCancel(context.Background()) 85 86 var wg sync.WaitGroup 87 wg.Add(1) 88 go func() { 89 defer wg.Done() 90 91 resp, err := call.Do(ctx) 92 require.Nil(t, resp) 93 require.Error(t, err) 94 require.ErrorIs(t, err, context.Canceled) 95 }() 96 97 cancel() 98 wg.Wait() 99 } 100 101 func TestCallRetrySucceed(t *testing.T) { 102 t.Parallel() 103 104 retryCount := 0 105 mockCallFn := func(ctx context.Context, req *mockRequest, opts ...grpc.CallOption) (*mockResponse, error) { 106 if retryCount < 5 { 107 retryCount++ 108 } else { 109 return &mockResponse{val: req.val}, nil 110 } 111 return nil, status.Error(codes.Unavailable, "") 112 } 113 114 call := NewCall(mockCallFn, &mockRequest{val: 1}) 115 resp, err := call.Do(context.Background()) 116 require.NoError(t, err) 117 require.Equal(t, &mockResponse{val: 1}, resp) 118 } 119 120 func TestCallForceNoRetry(t *testing.T) { 121 t.Parallel() 122 123 retryCount := 0 124 mockCallFn := func(ctx context.Context, req *mockRequest, opts ...grpc.CallOption) (*mockResponse, error) { 125 if retryCount < 5 { 126 retryCount++ 127 } else { 128 return &mockResponse{val: req.val}, nil 129 } 130 return nil, status.Error(codes.Unavailable, "should not retry") 131 } 132 133 call := NewCall(mockCallFn, &mockRequest{val: 1}, WithForceNoRetry()) 134 _, err := call.Do(context.Background()) 135 require.Error(t, err) 136 }