go.uber.org/yarpc@v1.72.1/encoding/raw/outbound_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 raw 22 23 import ( 24 "bytes" 25 "context" 26 "errors" 27 "io/ioutil" 28 "testing" 29 30 "github.com/golang/mock/gomock" 31 "github.com/stretchr/testify/assert" 32 "github.com/uber/tchannel-go/testutils/testreader" 33 "go.uber.org/yarpc" 34 "go.uber.org/yarpc/api/transport" 35 "go.uber.org/yarpc/api/transport/transporttest" 36 "go.uber.org/yarpc/internal/clientconfig" 37 ) 38 39 func TestCall(t *testing.T) { 40 mockCtrl := gomock.NewController(t) 41 defer mockCtrl.Finish() 42 43 ctx := context.Background() 44 45 caller := "caller" 46 service := "service" 47 48 tests := []struct { 49 procedure string 50 headers map[string]string 51 body []byte 52 responseBody [][]byte 53 responseErr error 54 55 want []byte 56 wantErr string 57 wantHeaders map[string]string 58 }{ 59 { 60 procedure: "foo", 61 body: []byte{1, 2, 3}, 62 responseBody: [][]byte{{4}, {5}, {6}}, 63 want: []byte{4, 5, 6}, 64 }, 65 { 66 procedure: "foo", 67 body: []byte{1, 2, 3}, 68 responseBody: [][]byte{{4}, {5}, {6}}, 69 responseErr: errors.New("bar"), 70 want: []byte{4, 5, 6}, 71 wantErr: "bar", 72 }, 73 { 74 procedure: "bar", 75 body: []byte{1, 2, 3}, 76 responseBody: [][]byte{{4}, {5}, nil, {6}}, 77 wantErr: "error set by user", 78 }, 79 { 80 procedure: "headers", 81 headers: map[string]string{"x": "y"}, 82 body: []byte{}, 83 responseBody: [][]byte{}, 84 want: []byte{}, 85 wantHeaders: map[string]string{"a": "b"}, 86 }, 87 } 88 89 for _, tt := range tests { 90 outbound := transporttest.NewMockUnaryOutbound(mockCtrl) 91 client := New(clientconfig.MultiOutbound(caller, service, 92 transport.Outbounds{ 93 Unary: outbound, 94 })) 95 96 writer, responseBody := testreader.ChunkReader() 97 for _, chunk := range tt.responseBody { 98 writer <- chunk 99 } 100 close(writer) 101 102 outbound.EXPECT().Call(gomock.Any(), 103 transporttest.NewRequestMatcher(t, 104 &transport.Request{ 105 Caller: caller, 106 Service: service, 107 Procedure: tt.procedure, 108 Headers: transport.HeadersFromMap(tt.headers), 109 Encoding: Encoding, 110 Body: bytes.NewReader(tt.body), 111 }), 112 ).Return( 113 &transport.Response{ 114 Body: ioutil.NopCloser(responseBody), 115 Headers: transport.HeadersFromMap(tt.wantHeaders), 116 }, tt.responseErr) 117 118 var ( 119 opts []yarpc.CallOption 120 resHeaders map[string]string 121 ) 122 123 for k, v := range tt.headers { 124 opts = append(opts, yarpc.WithHeader(k, v)) 125 } 126 opts = append(opts, yarpc.ResponseHeaders(&resHeaders)) 127 128 resBody, err := client.Call(ctx, tt.procedure, tt.body, opts...) 129 if tt.wantErr != "" { 130 if assert.Error(t, err) { 131 assert.Equal(t, err.Error(), tt.wantErr) 132 } 133 } else { 134 assert.NoError(t, err) 135 } 136 if tt.want != nil { 137 assert.Equal(t, tt.want, resBody) 138 } 139 if tt.wantHeaders != nil { 140 assert.Equal(t, tt.wantHeaders, resHeaders) 141 } 142 } 143 } 144 145 type successAck struct{} 146 147 func (a successAck) String() string { 148 return "success" 149 } 150 151 func TestCallOneway(t *testing.T) { 152 mockCtrl := gomock.NewController(t) 153 defer mockCtrl.Finish() 154 155 ctx := context.Background() 156 157 caller := "caller" 158 service := "service" 159 160 tests := []struct { 161 procedure string 162 headers map[string]string 163 body []byte 164 165 wantErr string 166 }{ 167 { 168 procedure: "foo", 169 body: []byte{1, 2, 3}, 170 }, 171 { 172 procedure: "headers", 173 headers: map[string]string{"x": "y"}, 174 body: []byte{}, 175 }, 176 } 177 178 for _, tt := range tests { 179 outbound := transporttest.NewMockOnewayOutbound(mockCtrl) 180 client := New(clientconfig.MultiOutbound(caller, service, 181 transport.Outbounds{ 182 Oneway: outbound, 183 })) 184 185 outbound.EXPECT().CallOneway(gomock.Any(), 186 transporttest.NewRequestMatcher(t, 187 &transport.Request{ 188 Caller: caller, 189 Service: service, 190 Procedure: tt.procedure, 191 Headers: transport.HeadersFromMap(tt.headers), 192 Encoding: Encoding, 193 Body: bytes.NewReader(tt.body), 194 }), 195 ).Return(&successAck{}, nil) 196 197 var opts []yarpc.CallOption 198 for k, v := range tt.headers { 199 opts = append(opts, yarpc.WithHeader(k, v)) 200 } 201 202 ack, err := client.CallOneway(ctx, tt.procedure, tt.body, opts...) 203 if tt.wantErr != "" { 204 if assert.Error(t, err) { 205 assert.Equal(t, err.Error(), tt.wantErr) 206 } 207 } else { 208 assert.Equal(t, "success", ack.String()) 209 } 210 } 211 } 212 213 func TestCallOnewayFailure(t *testing.T) { 214 mockCtrl := gomock.NewController(t) 215 defer mockCtrl.Finish() 216 217 ctx := context.Background() 218 219 caller := "caller" 220 service := "service" 221 procedure := "procedure" 222 body := []byte{1, 2, 3} 223 224 outbound := transporttest.NewMockOnewayOutbound(mockCtrl) 225 client := New(clientconfig.MultiOutbound(caller, service, 226 transport.Outbounds{ 227 Oneway: outbound, 228 })) 229 230 outbound.EXPECT().CallOneway(gomock.Any(), 231 transporttest.NewRequestMatcher(t, 232 &transport.Request{ 233 Service: service, 234 Caller: caller, 235 Procedure: procedure, 236 Encoding: Encoding, 237 Body: bytes.NewReader(body), 238 }), 239 ).Return(nil, errors.New("some error")) 240 241 _, err := client.CallOneway(ctx, procedure, body) 242 assert.Error(t, err) 243 }