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 }