go.uber.org/yarpc@v1.72.1/peer/direct/direct_test.go (about)

     1  // Copyright (c) 2022 Uber Technologies, Inc.
     2  //
     3  // Permission is hereby granted, free of charge, to any person obtaining a copy
     4  // of this software and associated documentation files (the "Software"), to deal
     5  // in the Software without restriction, including without limitation the rights
     6  // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
     7  // copies of the Software, and to permit persons to whom the Software is
     8  // furnished to do so, subject to the following conditions:
     9  //
    10  // The above copyright notice and this permission notice shall be included in
    11  // all copies or substantial portions of the Software.
    12  //
    13  // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    14  // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
    15  // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
    16  // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
    17  // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
    18  // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
    19  // THE SOFTWARE.
    20  
    21  package direct
    22  
    23  import (
    24  	"context"
    25  	"errors"
    26  	"sync"
    27  	"testing"
    28  
    29  	"github.com/stretchr/testify/assert"
    30  	"github.com/stretchr/testify/require"
    31  	"go.uber.org/yarpc/api/transport"
    32  	"go.uber.org/yarpc/peer/hostport"
    33  	"go.uber.org/yarpc/transport/grpc"
    34  	"go.uber.org/yarpc/yarpctest"
    35  	"go.uber.org/zap"
    36  	"go.uber.org/zap/zapcore"
    37  	"go.uber.org/zap/zaptest/observer"
    38  )
    39  
    40  func TestDirect(t *testing.T) {
    41  	t.Run("nil transport", func(t *testing.T) {
    42  		_, err := New(Configuration{}, nil)
    43  		assert.Error(t, err)
    44  	})
    45  
    46  	t.Run("chooser interface", func(t *testing.T) {
    47  		chooser, err := New(Configuration{}, yarpctest.NewFakeTransport())
    48  		require.NoError(t, err)
    49  
    50  		assert.NoError(t, chooser.Start())
    51  		assert.True(t, chooser.IsRunning())
    52  		assert.NoError(t, chooser.Stop())
    53  	})
    54  
    55  	t.Run("missing shard key", func(t *testing.T) {
    56  		chooser, err := New(Configuration{}, yarpctest.NewFakeTransport())
    57  		require.NoError(t, err)
    58  		_, _, err = chooser.Choose(context.Background(), &transport.Request{})
    59  		assert.Error(t, err)
    60  	})
    61  
    62  	t.Run("retain error", func(t *testing.T) {
    63  		const addr = "foohost:barport"
    64  		giveErr := errors.New("transport retain error")
    65  
    66  		trans := yarpctest.NewFakeTransport(
    67  			yarpctest.RetainErrors(giveErr, []string{addr}))
    68  
    69  		chooser, err := New(Configuration{}, trans)
    70  		require.NoError(t, err)
    71  
    72  		_, _, err = chooser.Choose(context.Background(), &transport.Request{ShardKey: addr})
    73  		assert.EqualError(t, err, giveErr.Error())
    74  	})
    75  
    76  	t.Run("release error", func(t *testing.T) {
    77  		const addr = "foohost:barport"
    78  
    79  		core, observedLogs := observer.New(zapcore.ErrorLevel)
    80  		logger := zap.New(core)
    81  		giveErr := errors.New("transport retain error")
    82  
    83  		trans := yarpctest.NewFakeTransport(
    84  			yarpctest.ReleaseErrors(giveErr, []string{addr}))
    85  
    86  		chooser, err := New(Configuration{}, trans, Logger(logger))
    87  		require.NoError(t, err)
    88  
    89  		_, onFinish, err := chooser.Choose(context.Background(), &transport.Request{ShardKey: addr})
    90  		require.NoError(t, err)
    91  
    92  		onFinish(nil)
    93  
    94  		logs := observedLogs.TakeAll()
    95  		require.Len(t, logs, 1, "unexpected number of logs")
    96  
    97  		logCtx := logs[0].Context[0]
    98  		assert.Equal(t, "error", logCtx.Key)
    99  
   100  		err, ok := logCtx.Interface.(error)
   101  		require.True(t, ok)
   102  		assert.EqualError(t, err, giveErr.Error())
   103  	})
   104  
   105  	t.Run("choose", func(t *testing.T) {
   106  		const addr = "foohost:barport"
   107  
   108  		chooser, err := New(Configuration{}, yarpctest.NewFakeTransport())
   109  		require.NoError(t, err)
   110  
   111  		p, onFinish, err := chooser.Choose(context.Background(), &transport.Request{ShardKey: addr})
   112  		require.NoError(t, err)
   113  
   114  		require.NotNil(t, p)
   115  		assert.Equal(t, addr, p.Identifier())
   116  
   117  		require.NotNil(t, onFinish)
   118  		onFinish(nil)
   119  	})
   120  }
   121  
   122  // TestPeerSubscriber tests that two new created peerSubscriber
   123  // are not even.
   124  // struct with no fields does not behave the same way as struct with fields.
   125  // For instance, with no fields and p1 := &peerSubscriber{}, p2 := &peerSubscriber{}
   126  // &p1 == &p2 will be true.
   127  // Internally, YARPC stores this *peerSubscriber as a hash's key. p1 and p2 must be different.
   128  // More details here: https://dave.cheney.net/2014/03/25/the-empty-struct
   129  func TestPeerSubscriber(t *testing.T) {
   130  	t.Run("peerSubscriber as map key", func(t *testing.T) {
   131  		p1 := &peerSubscriber{}
   132  		p2 := &peerSubscriber{}
   133  		subscribers := map[*peerSubscriber]struct{}{}
   134  		subscribers[p1] = struct{}{}
   135  		subscribers[p2] = struct{}{}
   136  		assert.Equal(t, 2, len(subscribers))
   137  	})
   138  
   139  	t.Run("concurrent call with peerSubscriber and grpc transport", func(t *testing.T) {
   140  		// Here we test that concurrent calls of RetainPeer and ReleasePeer
   141  		// methods from grpc.NewTransport does not return any errors.
   142  		const addr = "foohost:barport"
   143  		dialer := grpc.NewTransport().NewDialer()
   144  
   145  		var wg sync.WaitGroup
   146  		numberOfConcurrentCalls := 100
   147  		wg.Add(numberOfConcurrentCalls)
   148  
   149  		for i := 0; i < numberOfConcurrentCalls; i++ {
   150  			go func() {
   151  				defer wg.Done()
   152  				id := hostport.Identify(addr)
   153  				sub := &peerSubscriber{
   154  					peerIdentifier: id,
   155  				}
   156  				transportPeer, err := dialer.RetainPeer(id, sub)
   157  				assert.NoError(t, err)
   158  				err = dialer.ReleasePeer(transportPeer, sub)
   159  				assert.NoError(t, err)
   160  			}()
   161  		}
   162  		wg.Wait()
   163  	})
   164  }