github.com/hxx258456/ccgo@v0.0.5-0.20230213014102-48b35f46f66f/grpc/test/resolver_test.go (about)

     1  /*
     2   *
     3   * Copyright 2020 gRPC authors.
     4   *
     5   * Licensed under the Apache License, Version 2.0 (the "License");
     6   * you may not use this file except in compliance with the License.
     7   * You may obtain a copy of the License at
     8   *
     9   *     http://www.apache.org/licenses/LICENSE-2.0
    10   *
    11   * Unless required by applicable law or agreed to in writing, software
    12   * distributed under the License is distributed on an "AS IS" BASIS,
    13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    14   * See the License for the specific language governing permissions and
    15   * limitations under the License.
    16   *
    17   */
    18  
    19  package test
    20  
    21  import (
    22  	"context"
    23  	"fmt"
    24  	"testing"
    25  	"time"
    26  
    27  	"github.com/google/go-cmp/cmp"
    28  	"github.com/google/go-cmp/cmp/cmpopts"
    29  	"github.com/hxx258456/ccgo/grpc/codes"
    30  	iresolver "github.com/hxx258456/ccgo/grpc/internal/resolver"
    31  	"github.com/hxx258456/ccgo/grpc/internal/serviceconfig"
    32  	"github.com/hxx258456/ccgo/grpc/internal/stubserver"
    33  	"github.com/hxx258456/ccgo/grpc/internal/testutils"
    34  	"github.com/hxx258456/ccgo/grpc/metadata"
    35  	"github.com/hxx258456/ccgo/grpc/resolver"
    36  	"github.com/hxx258456/ccgo/grpc/resolver/manual"
    37  	"github.com/hxx258456/ccgo/grpc/status"
    38  	testpb "github.com/hxx258456/ccgo/grpc/test/grpc_testing"
    39  )
    40  
    41  type funcConfigSelector struct {
    42  	f func(iresolver.RPCInfo) (*iresolver.RPCConfig, error)
    43  }
    44  
    45  func (f funcConfigSelector) SelectConfig(i iresolver.RPCInfo) (*iresolver.RPCConfig, error) {
    46  	return f.f(i)
    47  }
    48  
    49  func (s) TestConfigSelector(t *testing.T) {
    50  	gotContextChan := testutils.NewChannelWithSize(1)
    51  
    52  	ss := &stubserver.StubServer{
    53  		EmptyCallF: func(ctx context.Context, in *testpb.Empty) (*testpb.Empty, error) {
    54  			gotContextChan.SendContext(ctx, ctx)
    55  			return &testpb.Empty{}, nil
    56  		},
    57  	}
    58  	ss.R = manual.NewBuilderWithScheme("confSel")
    59  
    60  	if err := ss.Start(nil); err != nil {
    61  		t.Fatalf("Error starting endpoint server: %v", err)
    62  	}
    63  	defer ss.Stop()
    64  
    65  	ctxDeadline := time.Now().Add(10 * time.Second)
    66  	ctx, cancel := context.WithDeadline(context.Background(), ctxDeadline)
    67  	defer cancel()
    68  
    69  	longCtxDeadline := time.Now().Add(30 * time.Second)
    70  	longdeadlineCtx, cancel := context.WithDeadline(context.Background(), longCtxDeadline)
    71  	defer cancel()
    72  	shorterTimeout := 3 * time.Second
    73  
    74  	testMD := metadata.MD{"footest": []string{"bazbar"}}
    75  	mdOut := metadata.MD{"handler": []string{"value"}}
    76  
    77  	var onCommittedCalled bool
    78  
    79  	testCases := []struct {
    80  		name   string
    81  		md     metadata.MD          // MD sent with RPC
    82  		config *iresolver.RPCConfig // config returned by config selector
    83  		csErr  error                // error returned by config selector
    84  
    85  		wantMD       metadata.MD
    86  		wantDeadline time.Time
    87  		wantTimeout  time.Duration
    88  		wantErr      error
    89  	}{{
    90  		name:         "basic",
    91  		md:           testMD,
    92  		config:       &iresolver.RPCConfig{},
    93  		wantMD:       testMD,
    94  		wantDeadline: ctxDeadline,
    95  	}, {
    96  		name: "alter MD",
    97  		md:   testMD,
    98  		config: &iresolver.RPCConfig{
    99  			Context: metadata.NewOutgoingContext(ctx, mdOut),
   100  		},
   101  		wantMD:       mdOut,
   102  		wantDeadline: ctxDeadline,
   103  	}, {
   104  		name:    "erroring SelectConfig",
   105  		csErr:   status.Errorf(codes.Unavailable, "cannot send RPC"),
   106  		wantErr: status.Errorf(codes.Unavailable, "cannot send RPC"),
   107  	}, {
   108  		name: "alter timeout; remove MD",
   109  		md:   testMD,
   110  		config: &iresolver.RPCConfig{
   111  			Context: longdeadlineCtx, // no metadata
   112  		},
   113  		wantMD:       nil,
   114  		wantDeadline: longCtxDeadline,
   115  	}, {
   116  		name:         "nil config",
   117  		md:           metadata.MD{},
   118  		config:       nil,
   119  		wantMD:       nil,
   120  		wantDeadline: ctxDeadline,
   121  	}, {
   122  		name: "alter timeout via method config; remove MD",
   123  		md:   testMD,
   124  		config: &iresolver.RPCConfig{
   125  			MethodConfig: serviceconfig.MethodConfig{
   126  				Timeout: &shorterTimeout,
   127  			},
   128  		},
   129  		wantMD:      nil,
   130  		wantTimeout: shorterTimeout,
   131  	}, {
   132  		name: "onCommitted callback",
   133  		md:   testMD,
   134  		config: &iresolver.RPCConfig{
   135  			OnCommitted: func() {
   136  				onCommittedCalled = true
   137  			},
   138  		},
   139  		wantMD:       testMD,
   140  		wantDeadline: ctxDeadline,
   141  	}}
   142  
   143  	for _, tc := range testCases {
   144  		t.Run(tc.name, func(t *testing.T) {
   145  			var gotInfo *iresolver.RPCInfo
   146  			state := iresolver.SetConfigSelector(resolver.State{
   147  				Addresses:     []resolver.Address{{Addr: ss.Address}},
   148  				ServiceConfig: parseCfg(ss.R, "{}"),
   149  			}, funcConfigSelector{
   150  				f: func(i iresolver.RPCInfo) (*iresolver.RPCConfig, error) {
   151  					gotInfo = &i
   152  					cfg := tc.config
   153  					if cfg != nil && cfg.Context == nil {
   154  						cfg.Context = i.Context
   155  					}
   156  					return cfg, tc.csErr
   157  				},
   158  			})
   159  			ss.R.UpdateState(state) // Blocks until config selector is applied
   160  
   161  			onCommittedCalled = false
   162  			ctx := metadata.NewOutgoingContext(ctx, tc.md)
   163  			startTime := time.Now()
   164  			if _, err := ss.Client.EmptyCall(ctx, &testpb.Empty{}); fmt.Sprint(err) != fmt.Sprint(tc.wantErr) {
   165  				t.Fatalf("client.EmptyCall(_, _) = _, %v; want _, %v", err, tc.wantErr)
   166  			} else if err != nil {
   167  				return // remaining checks are invalid
   168  			}
   169  
   170  			if gotInfo == nil {
   171  				t.Fatalf("no config selector data")
   172  			}
   173  
   174  			if want := "/grpc.testing.TestService/EmptyCall"; gotInfo.Method != want {
   175  				t.Errorf("gotInfo.Method = %q; want %q", gotInfo.Method, want)
   176  			}
   177  
   178  			gotContextI, ok := gotContextChan.ReceiveOrFail()
   179  			if !ok {
   180  				t.Fatalf("no context received")
   181  			}
   182  			gotContext := gotContextI.(context.Context)
   183  
   184  			gotMD, _ := metadata.FromOutgoingContext(gotInfo.Context)
   185  			if diff := cmp.Diff(tc.md, gotMD); diff != "" {
   186  				t.Errorf("gotInfo.Context contains MD %v; want %v\nDiffs: %v", gotMD, tc.md, diff)
   187  			}
   188  
   189  			gotMD, _ = metadata.FromIncomingContext(gotContext)
   190  			// Remove entries from gotMD not in tc.wantMD (e.g. authority header).
   191  			for k := range gotMD {
   192  				if _, ok := tc.wantMD[k]; !ok {
   193  					delete(gotMD, k)
   194  				}
   195  			}
   196  			if diff := cmp.Diff(tc.wantMD, gotMD, cmpopts.EquateEmpty()); diff != "" {
   197  				t.Errorf("received md = %v; want %v\nDiffs: %v", gotMD, tc.wantMD, diff)
   198  			}
   199  
   200  			wantDeadline := tc.wantDeadline
   201  			if wantDeadline == (time.Time{}) {
   202  				wantDeadline = startTime.Add(tc.wantTimeout)
   203  			}
   204  			deadlineGot, _ := gotContext.Deadline()
   205  			if diff := deadlineGot.Sub(wantDeadline); diff > time.Second || diff < -time.Second {
   206  				t.Errorf("received deadline = %v; want ~%v", deadlineGot, wantDeadline)
   207  			}
   208  
   209  			if tc.config != nil && tc.config.OnCommitted != nil && !onCommittedCalled {
   210  				t.Errorf("OnCommitted callback not called")
   211  			}
   212  		})
   213  	}
   214  
   215  }