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  }