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