github.com/MetalBlockchain/metalgo@v1.11.9/vms/rpcchainvm/grpcutils/client_test.go (about)

     1  // Copyright (C) 2019-2024, Ava Labs, Inc. All rights reserved.
     2  // See the file LICENSE for licensing terms.
     3  
     4  package grpcutils
     5  
     6  import (
     7  	"context"
     8  	"fmt"
     9  	"testing"
    10  	"time"
    11  
    12  	"github.com/stretchr/testify/require"
    13  	"google.golang.org/grpc"
    14  	"google.golang.org/grpc/codes"
    15  	"google.golang.org/grpc/credentials/insecure"
    16  	"google.golang.org/grpc/status"
    17  
    18  	"github.com/MetalBlockchain/metalgo/database/memdb"
    19  	"github.com/MetalBlockchain/metalgo/database/rpcdb"
    20  
    21  	pb "github.com/MetalBlockchain/metalgo/proto/pb/rpcdb"
    22  	grpc_prometheus "github.com/grpc-ecosystem/go-grpc-prometheus"
    23  )
    24  
    25  func TestDialOptsSmoke(t *testing.T) {
    26  	require := require.New(t)
    27  
    28  	opts := newDialOpts()
    29  	require.Len(opts, 3)
    30  
    31  	opts = newDialOpts(
    32  		WithChainUnaryInterceptor(grpc_prometheus.UnaryClientInterceptor),
    33  		WithChainStreamInterceptor(grpc_prometheus.StreamClientInterceptor),
    34  	)
    35  	require.Len(opts, 5)
    36  }
    37  
    38  // Test_WaitForReady shows the expected results from the DialOption during
    39  // client creation.  If true the client will block and wait forever for the
    40  // server to become Ready even if the listener is closed.
    41  // ref. https://github.com/grpc/grpc/blob/master/doc/wait-for-ready.md
    42  func TestWaitForReady(t *testing.T) {
    43  	require := require.New(t)
    44  
    45  	listener, err := NewListener()
    46  	require.NoError(err)
    47  	defer listener.Close()
    48  
    49  	server := NewServer()
    50  	defer server.Stop()
    51  	pb.RegisterDatabaseServer(server, rpcdb.NewServer(memdb.New()))
    52  
    53  	go func() {
    54  		time.Sleep(100 * time.Millisecond)
    55  		Serve(listener, server)
    56  	}()
    57  
    58  	// The default is WaitForReady = true.
    59  	conn, err := Dial(listener.Addr().String())
    60  	require.NoError(err)
    61  
    62  	db := rpcdb.NewClient(pb.NewDatabaseClient(conn))
    63  	require.NoError(db.Put([]byte("foo"), []byte("bar")))
    64  
    65  	noWaitListener, err := NewListener()
    66  	require.NoError(err)
    67  	// close listener causes RPC to fail fast.
    68  	// The client would timeout otherwise.
    69  	_ = noWaitListener.Close()
    70  
    71  	// By directly calling `grpc.Dial` rather than `Dial`, the default does not
    72  	// include setting grpc.WaitForReady(true).
    73  	noWaitConn, err := grpc.Dial(
    74  		noWaitListener.Addr().String(),
    75  		grpc.WithTransportCredentials(insecure.NewCredentials()),
    76  	)
    77  	require.NoError(err)
    78  
    79  	db = rpcdb.NewClient(pb.NewDatabaseClient(noWaitConn))
    80  
    81  	err = db.Put([]byte("foo"), []byte("bar"))
    82  	status, ok := status.FromError(err)
    83  	require.True(ok)
    84  	require.Equal(codes.Unavailable, status.Code())
    85  }
    86  
    87  func TestWaitForReadyCallOption(t *testing.T) {
    88  	require := require.New(t)
    89  
    90  	listener, err := NewListener()
    91  	require.NoError(err)
    92  	conn, err := Dial(listener.Addr().String())
    93  	require.NoError(err)
    94  	// close listener causes RPC to fail fast.
    95  	_ = listener.Close()
    96  
    97  	db := pb.NewDatabaseClient(conn)
    98  	_, err = db.Put(context.Background(), &pb.PutRequest{Key: []byte("foo"), Value: []byte("bar")}, grpc.WaitForReady(false))
    99  	s, ok := status.FromError(err)
   100  	fmt.Printf("status: %v\n", s)
   101  	require.True(ok)
   102  	require.Equal(codes.Unavailable, s.Code())
   103  }