go.uber.org/yarpc@v1.72.1/encoding/protobuf/v2/stream_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 v2_test 22 23 import ( 24 "context" 25 "errors" 26 "io" 27 "testing" 28 29 "github.com/stretchr/testify/assert" 30 "github.com/stretchr/testify/require" 31 "go.uber.org/yarpc" 32 "go.uber.org/yarpc/api/transport" 33 "go.uber.org/yarpc/encoding/protobuf/internal/testpb/v2" 34 "go.uber.org/yarpc/encoding/protobuf/v2" 35 "go.uber.org/yarpc/internal/clientconfig" 36 "go.uber.org/yarpc/peer" 37 "go.uber.org/yarpc/peer/hostport" 38 "go.uber.org/yarpc/yarpctest" 39 "google.golang.org/protobuf/proto" 40 ) 41 42 var protocolOptionsTable = []struct { 43 msg string 44 opts []v2.ClientOption 45 }{ 46 { 47 msg: "protobuf", 48 }, 49 { 50 msg: "json", 51 opts: []v2.ClientOption{ 52 v2.UseJSON, 53 }, 54 }, 55 } 56 57 func TestUnary(t *testing.T) { 58 for _, tt := range protocolOptionsTable { 59 t.Run(tt.msg, func(t *testing.T) { 60 server := &testServer{} 61 procedures := testpb.BuildTestYARPCProcedures(server) 62 router := yarpc.NewMapRouter("test") 63 router.Register(procedures) 64 65 trans := yarpctest.NewFakeTransport() 66 pc := peer.NewSingle(hostport.Identify("1"), trans) 67 ob := trans.NewOutbound(pc, yarpctest.OutboundRouter(router)) 68 cc := clientconfig.MultiOutbound("test", "test", transport.Outbounds{ 69 Unary: ob, 70 }) 71 client := testpb.NewTestYARPCClient(cc, tt.opts...) 72 73 ctx, cancel := context.WithCancel(context.Background()) 74 defer cancel() 75 76 sent := &testpb.TestMessage{Value: "echo"} 77 received, err := client.Unary(ctx, sent) 78 require.NoError(t, err) 79 assert.True(t, proto.Equal(sent, received)) 80 }) 81 } 82 } 83 84 var _ testpb.TestYARPCServer = (*testServer)(nil) 85 86 // testServer provides unary and streaming echo method implementations. 87 type testServer struct{} 88 89 func (s *testServer) Unary(ctx context.Context, msg *testpb.TestMessage) (*testpb.TestMessage, error) { 90 return msg, nil 91 } 92 93 func (s *testServer) Duplex(str testpb.TestServiceDuplexYARPCServer) error { 94 for { 95 msg, err := str.Recv() 96 if err != nil { 97 return err 98 } 99 if msg.Value == "please explode" { 100 return errors.New("explosion occurred") 101 } 102 err = str.Send(msg) 103 if err != nil { 104 return err 105 } 106 } 107 } 108 109 func TestDuplexStream(t *testing.T) { 110 for _, tt := range protocolOptionsTable { 111 t.Run(tt.msg, func(t *testing.T) { 112 server := &testServer{} 113 procedures := testpb.BuildTestYARPCProcedures(server) 114 router := yarpc.NewMapRouter("test") 115 router.Register(procedures) 116 117 trans := yarpctest.NewFakeTransport() 118 pc := peer.NewSingle(hostport.Identify("1"), trans) 119 ob := trans.NewOutbound(pc, yarpctest.OutboundRouter(router)) 120 cc := clientconfig.MultiOutbound("test", "test", transport.Outbounds{ 121 Stream: ob, 122 }) 123 client := testpb.NewTestYARPCClient(cc, tt.opts...) 124 125 ctx, cancel := context.WithCancel(context.Background()) 126 defer cancel() 127 128 str, err := client.Duplex(ctx) 129 require.NoError(t, err) 130 131 // Send a message. 132 sent := &testpb.TestMessage{Value: "echo"} 133 { 134 err := str.Send(sent) 135 require.NoError(t, err) 136 } 137 138 // Receive the echoed message. 139 { 140 msg, err := str.Recv() 141 require.NoError(t, err) 142 assert.True(t, proto.Equal(sent, msg)) 143 } 144 145 // Close the client side of the stream. 146 str.CloseSend() 147 148 // Verify that the server closes as well. 149 { 150 _, err := str.Recv() 151 require.Equal(t, err, io.EOF) 152 } 153 }) 154 } 155 } 156 157 func TestStreamServerError(t *testing.T) { 158 table := []struct { 159 msg string 160 clientCloses bool 161 }{ 162 { 163 msg: "client closes after error", 164 clientCloses: true, 165 }, 166 { 167 msg: "client returns immediately after error", 168 clientCloses: false, 169 }, 170 } 171 172 for _, ct := range table { 173 t.Run(ct.msg, func(t *testing.T) { 174 for _, tt := range protocolOptionsTable { 175 t.Run(tt.msg, func(t *testing.T) { 176 server := &testServer{} 177 procedures := testpb.BuildTestYARPCProcedures(server) 178 router := yarpc.NewMapRouter("test") 179 router.Register(procedures) 180 181 trans := yarpctest.NewFakeTransport() 182 pc := peer.NewSingle(hostport.Identify("1"), trans) 183 ob := trans.NewOutbound(pc, yarpctest.OutboundRouter(router)) 184 cc := clientconfig.MultiOutbound("test", "test", transport.Outbounds{ 185 Stream: ob, 186 }) 187 client := testpb.NewTestYARPCClient(cc, tt.opts...) 188 189 ctx, cancel := context.WithCancel(context.Background()) 190 defer cancel() 191 192 str, err := client.Duplex(ctx) 193 require.NoError(t, err) 194 195 // Send a message. 196 sent := &testpb.TestMessage{Value: "please explode"} 197 { 198 err := str.Send(sent) 199 require.NoError(t, err) 200 } 201 202 // Receive the handler error on next receive. 203 { 204 msg, err := str.Recv() 205 require.Error(t, err) 206 assert.Nil(t, msg) 207 } 208 209 // Close the client side of the stream. 210 if ct.clientCloses { 211 str.CloseSend() 212 } 213 214 // Verify that the server closes as well. 215 { 216 msg, err := str.Recv() 217 require.Equal(t, err, io.EOF) 218 assert.Nil(t, msg) 219 } 220 }) 221 } 222 }) 223 } 224 }