go.uber.org/yarpc@v1.72.1/transport/grpc/external_integration_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 grpc_test 22 23 import ( 24 "context" 25 "errors" 26 "net" 27 "testing" 28 "time" 29 30 "github.com/stretchr/testify/assert" 31 "github.com/stretchr/testify/require" 32 "go.uber.org/yarpc" 33 "go.uber.org/yarpc/api/middleware" 34 "go.uber.org/yarpc/api/peer" 35 "go.uber.org/yarpc/api/transport" 36 "go.uber.org/yarpc/internal/prototest/example" 37 "go.uber.org/yarpc/internal/prototest/examplepb" 38 "go.uber.org/yarpc/peer/hostport" 39 "go.uber.org/yarpc/peer/roundrobin" 40 "go.uber.org/yarpc/transport/grpc" 41 "go.uber.org/yarpc/x/yarpctest" 42 "go.uber.org/yarpc/x/yarpctest/api" 43 "go.uber.org/yarpc/x/yarpctest/types" 44 ) 45 46 func TestStreamingWithNoCtxDeadline(t *testing.T) { 47 // This test ensures that we can use gRPC streaming without a context deadline 48 // set. For long-lived streams, it should be unnecesary for users to set a 49 // deadline; instead they should use context.WithCancel to cancel the stream. 50 51 const serviceName = "service-name" 52 53 // init YARPC transport / inbound / outbound 54 grpcTransport := grpc.NewTransport() 55 peerList := roundrobin.New(grpcTransport) 56 57 listener, err := net.Listen("tcp", "127.0.0.1:0") 58 require.NoError(t, err, "could not start listener") 59 inbound := grpcTransport.NewInbound(listener) 60 61 dispatcher := yarpc.NewDispatcher(yarpc.Config{ 62 Name: serviceName, 63 Inbounds: yarpc.Inbounds{inbound}, 64 Outbounds: yarpc.Outbounds{ 65 serviceName: { 66 ServiceName: serviceName, 67 Stream: grpcTransport.NewOutbound(peerList), 68 }, 69 }, 70 }) 71 dispatcher.Register( 72 examplepb.BuildFooYARPCProcedures( 73 example.NewFooYARPCServer(transport.NewHeaders()))) 74 75 require.NoError(t, dispatcher.Start(), "could not start dispatcher") 76 defer func() { assert.NoError(t, dispatcher.Stop(), "could not stop dispatcher") }() 77 78 // add streaming peer so we can call ourself 79 err = peerList.Update(peer.ListUpdates{Additions: []peer.Identifier{ 80 hostport.PeerIdentifier(listener.Addr().String()), 81 }}) 82 require.NoError(t, err, "could not add peer to peer list") 83 84 waitForPeerAvailable(t, peerList, time.Second) 85 86 // init streaming client 87 client := examplepb.NewFooYARPCClient(dispatcher.ClientConfig(serviceName)) 88 ctx, cancel := context.WithCancel(context.Background()) 89 defer cancel() 90 91 streamClient, err := client.EchoBoth(ctx) 92 require.NoError(t, err, "could not create client stream") 93 94 // veryify we can send a request 95 err = streamClient.Send(&examplepb.EchoBothRequest{ 96 Message: "test message!", 97 NumResponses: 0, 98 }) 99 require.NoError(t, err, "could not send message") 100 assert.NoError(t, streamClient.CloseSend(), "could not close stream") 101 } 102 103 // waitForPeerAvailable ensures that the peer becomes available before 104 // proceeding, and that we do not wait forever. 105 func waitForPeerAvailable(t *testing.T, peerList *roundrobin.List, wait time.Duration) { 106 peerAvailable := make(chan struct{}) 107 go func() { 108 for { 109 if peerList.Peers()[0].Status().ConnectionStatus == peer.Available { 110 close(peerAvailable) 111 return 112 } 113 time.Sleep(10 * time.Millisecond) 114 } 115 }() 116 117 select { 118 case <-time.After(wait): 119 t.Fatal("failed waiting to connect to peer") 120 case <-peerAvailable: 121 return 122 } 123 } 124 125 func TestFoo(t *testing.T) { 126 const ( 127 serviceName = "test-service" 128 procedureName = "test-procedure" 129 130 appErrName = "ProtoAppErrName" 131 appErrDetails = " this is an app error detail string!" 132 133 portName = "port" 134 ) 135 136 handler := &types.UnaryHandler{ 137 Handler: api.UnaryHandlerFunc(func(ctx context.Context, req *transport.Request, resw transport.ResponseWriter) error { 138 // simulate Protobuf encoding setting `transport.ApplicationErrorMeta` 139 metaSetter, ok := resw.(transport.ApplicationErrorMetaSetter) 140 if !ok { 141 return errors.New("missing transport.ApplicationErrorMetaSetter") 142 } 143 metaSetter.SetApplicationErrorMeta(&transport.ApplicationErrorMeta{ 144 Name: appErrName, 145 Details: appErrDetails, 146 }) 147 return nil 148 })} 149 150 outboundMwAssertion := middleware.UnaryOutboundFunc( 151 func(ctx context.Context, req *transport.Request, next transport.UnaryOutbound) (*transport.Response, error) { 152 res, err := next.Call(ctx, req) 153 154 // verify gRPC propagating `transport.ApplicationErrorMeta` 155 require.NotNil(t, res.ApplicationErrorMeta, "missing transport.ApplicationErrorMeta") 156 assert.Equal(t, appErrName, res.ApplicationErrorMeta.Name, "incorrect app error name") 157 assert.Equal(t, appErrDetails, res.ApplicationErrorMeta.Details, "incorrect app error message") 158 assert.Nil(t, res.ApplicationErrorMeta.Code, "unexpected code") 159 160 return res, err 161 }) 162 163 portProvider := yarpctest.NewPortProvider(t) 164 service := yarpctest.GRPCService( 165 yarpctest.Name(serviceName), 166 portProvider.NamedPort(portName), 167 yarpctest.Proc(yarpctest.Name(procedureName), handler), 168 ) 169 require.NoError(t, service.Start(t)) 170 defer func() { assert.NoError(t, service.Stop(t)) }() 171 172 request := yarpctest.GRPCRequest( 173 yarpctest.Service(serviceName), 174 portProvider.NamedPort(portName), 175 yarpctest.Procedure(procedureName), 176 yarpctest.GiveTimeout(time.Second), 177 api.RequestOptionFunc(func(opts *api.RequestOpts) { 178 opts.UnaryMiddleware = []middleware.UnaryOutbound{outboundMwAssertion} 179 }), 180 ) 181 request.Run(t) 182 }