Beispiel #1
0
func TestCallOnewayFailure(t *testing.T) {
	mockCtrl := gomock.NewController(t)
	defer mockCtrl.Finish()

	ctx := context.Background()

	caller := "caller"
	service := "service"
	procedure := "procedure"
	body := []byte{1, 2, 3}

	outbound := transporttest.NewMockOnewayOutbound(mockCtrl)
	client := New(channel.MultiOutbound(caller, service,
		transport.Outbounds{
			Oneway: outbound,
		}))

	outbound.EXPECT().CallOneway(gomock.Any(),
		transporttest.NewRequestMatcher(t,
			&transport.Request{
				Service:   service,
				Caller:    caller,
				Procedure: procedure,
				Encoding:  Encoding,
				Body:      bytes.NewReader(body),
			}),
	).Return(nil, errors.New("some error"))

	_, err := client.CallOneway(
		ctx,
		yarpc.NewReqMeta().Procedure(procedure),
		body)

	assert.Error(t, err)
}
Beispiel #2
0
func TestRecording(t *testing.T) {
	tMock := testingTMock{t, 0}

	dir, err := ioutil.TempDir("", "yarpcgorecorder")
	if err != nil {
		t.Fatal(err)
	}
	defer os.RemoveAll(dir) // clean up

	recorder := NewRecorder(&tMock, RecordMode(Append), RecordsPath(dir))

	withConnectedClient(t, recorder, func(client raw.Client) {
		ctx, cancel := context.WithTimeout(context.Background(), time.Second)
		defer cancel()

		rbody, _, err := client.Call(ctx, yarpc.NewReqMeta().Procedure("hello"), []byte("Hello"))
		require.NoError(t, err)
		assert.Equal(t, []byte("Hello, World"), rbody)
	})

	recordPath := path.Join(dir, refRecordFilename)
	_, err = os.Stat(recordPath)
	require.NoError(t, err)

	recordContent, err := ioutil.ReadFile(recordPath)
	require.NoError(t, err)
	assert.Equal(t, refRecordContent, string(recordContent))
}
Beispiel #3
0
func (h *multiHopHandler) Handle(ctx context.Context, reqMeta yarpc.ReqMeta, body interface{}) (interface{}, yarpc.ResMeta, error) {
	if h.phoneClient == nil {
		panic("call SetClient() and SetTransport() first")
	}

	assertBaggageMatches(ctx, h.t, h.wantBaggage)

	span := opentracing.SpanFromContext(ctx)
	for key, value := range h.addBaggage {
		span.SetBaggageItem(key, value)
	}
	ctx = opentracing.ContextWithSpan(ctx, span)

	var resp js.RawMessage
	phoneResMeta, err := h.phoneClient.Call(
		ctx,
		yarpc.NewReqMeta().Procedure("phone").Headers(reqMeta.Headers()),
		&server.PhoneRequest{
			Service:   "ctxclient",
			Procedure: h.phoneCallTo,
			Transport: h.phoneCallTransport,
			Body:      &js.RawMessage{'{', '}'},
		}, &resp)

	resMeta := yarpc.NewResMeta().Headers(phoneResMeta.Headers())
	return map[string]interface{}{}, resMeta, err
}
Beispiel #4
0
// JSON starts an http run using JSON encoding
func JSON(t crossdock.T, dispatcher yarpc.Dispatcher) {
	fatals := crossdock.Fatals(t)

	client := json.New(dispatcher.Channel("oneway-test"))
	token := getRandomID()

	ack, err := client.CallOneway(
		context.Background(),
		yarpc.NewReqMeta().Procedure("echo/json"),
		&jsonToken{Token: token},
	)

	// ensure channel hasn't been filled yet
	select {
	case <-serverCalledBack:
		fatals.FailNow("oneway json test failed", "client waited for server to fill channel")
	default:
	}

	fatals.NoError(err, "call to oneway/json failed: %v", err)
	fatals.NotNil(ack, "ack is nil")

	serverToken := <-serverCalledBack
	fatals.Equal(token, string(serverToken), "Client/Server token mismatch")
}
Beispiel #5
0
func TestEndToEnd(t *testing.T) {
	tMock := testingTMock{t, 0}

	dir, err := ioutil.TempDir("", "yarpcgorecorder")
	if err != nil {
		t.Fatal(err)
	}
	defer os.RemoveAll(dir) // clean up

	// First we double check that our cache is empty.
	recorder := NewRecorder(&tMock, RecordMode(Replay), RecordsPath(dir))

	withDisconnectedClient(t, recorder, func(client raw.Client) {
		ctx, cancel := context.WithTimeout(context.Background(), time.Second)
		defer cancel()

		require.Panics(t, func() {
			client.Call(ctx, yarpc.NewReqMeta().Procedure("hello"), []byte("Hello"))
		})
		assert.Equal(t, tMock.fatalCount, 1)
	})

	// Now let's record our call.
	recorder = NewRecorder(&tMock, RecordMode(Overwrite), RecordsPath(dir))

	withConnectedClient(t, recorder, func(client raw.Client) {
		ctx, cancel := context.WithTimeout(context.Background(), time.Second)
		defer cancel()

		rbody, _, err := client.Call(ctx, yarpc.NewReqMeta().Procedure("hello"), []byte("Hello"))
		require.NoError(t, err)
		assert.Equal(t, rbody, []byte("Hello, World"))
	})

	// Now replay the call.
	recorder = NewRecorder(&tMock, RecordMode(Replay), RecordsPath(dir))

	withDisconnectedClient(t, recorder, func(client raw.Client) {
		ctx, cancel := context.WithTimeout(context.Background(), time.Second)
		defer cancel()

		rbody, _, err := client.Call(ctx, yarpc.NewReqMeta().Procedure("hello"), []byte("Hello"))
		require.NoError(t, err)
		assert.Equal(t, rbody, []byte("Hello, World"))
	})
}
Beispiel #6
0
func runYARPCClient(b *testing.B, c raw.Client) {
	for i := 0; i < b.N; i++ {
		ctx, cancel := context.WithTimeout(context.Background(), 100*time.Millisecond)
		defer cancel()
		_, _, err := c.Call(ctx, yarpc.NewReqMeta().Procedure("echo"), _reqBody)
		require.NoError(b, err, "request %d failed", i+1)
	}
}
Beispiel #7
0
func (h handler) echoEcho(ctx context.Context) error {
	var resBody echoResBody
	_, err := h.client.Call(
		ctx,
		yarpc.NewReqMeta().Procedure("echoecho"),
		&echoReqBody{},
		&resBody,
	)
	return err
}
Beispiel #8
0
func set(ctx context.Context, c json.Client, k string, v string) error {
	var response setResponse
	ctx, cancel := context.WithTimeout(ctx, 100*time.Millisecond)
	defer cancel()
	_, err := c.Call(
		ctx,
		yarpc.NewReqMeta().Procedure("set"),
		&setRequest{Key: k, Value: v},
		&response,
	)
	return err
}
Beispiel #9
0
func rawCall(dispatcher yarpc.Dispatcher, headers yarpc.Headers, procedure string,
	token []byte) ([]byte, yarpc.CallResMeta, error) {
	client := raw.New(dispatcher.Channel(serverName))

	ctx, cancel := context.WithTimeout(context.Background(), time.Second)
	defer cancel()

	reqMeta := yarpc.NewReqMeta().Procedure(procedure).Headers(headers)
	resBody, resMeta, err := client.Call(ctx, reqMeta, token)

	return resBody, resMeta, err
}
Beispiel #10
0
func get(ctx context.Context, c json.Client, k string) (string, error) {
	var response getResponse
	ctx, cancel := context.WithTimeout(ctx, 100*time.Millisecond)
	defer cancel()
	_, err := c.Call(
		ctx,
		yarpc.NewReqMeta().Procedure("get"),
		&getRequest{Key: k},
		&response,
	)
	return response.Value, err
}
Beispiel #11
0
func jsonCall(dispatcher yarpc.Dispatcher, headers yarpc.Headers, token string) (string, yarpc.CallResMeta, error) {
	client := json.New(dispatcher.Channel(serverName))

	ctx, cancel := context.WithTimeout(context.Background(), time.Second)
	defer cancel()

	reqMeta := yarpc.NewReqMeta().Procedure("echo").Headers(headers)
	reqBody := &jsonEcho{Token: token}

	var resBody jsonEcho
	resMeta, err := client.Call(ctx, reqMeta, reqBody, &resBody)
	return resBody.Token, resMeta, err
}
Beispiel #12
0
func (c rawCaller) Call(h yarpc.Headers) (yarpc.Headers, error) {
	ctx, cancel := context.WithTimeout(context.Background(), time.Second)
	defer cancel()

	_, res, err := c.c.Call(
		ctx,
		yarpc.NewReqMeta().Headers(h).Procedure("echo/raw"),
		[]byte("hello"))

	if err != nil {
		return yarpc.Headers{}, err
	}
	return res.Headers(), nil
}
Beispiel #13
0
func (c thriftCaller) Call(h yarpc.Headers) (yarpc.Headers, error) {
	ctx, cancel := context.WithTimeout(context.Background(), time.Second)
	defer cancel()

	_, res, err := c.c.Echo(
		ctx,
		yarpc.NewReqMeta().Headers(h),
		&echo.Ping{Beep: "hello"})

	if err != nil {
		return yarpc.Headers{}, err
	}
	return res.Headers(), nil
}
Beispiel #14
0
func (h handler) handleEchoEcho(ctx context.Context, reqMeta yarpc.ReqMeta, reqBody *echoReqBody) (*echoResBody, yarpc.ResMeta, error) {
	h.assertBaggage(ctx)
	var resBody echoResBody
	_, err := h.client.Call(
		ctx,
		yarpc.NewReqMeta().Procedure("echo"),
		reqBody,
		&resBody,
	)
	if err != nil {
		return nil, nil, err
	}
	return &resBody, nil, nil
}
Beispiel #15
0
func (c jsonCaller) Call(h yarpc.Headers) (yarpc.Headers, error) {
	ctx, cancel := context.WithTimeout(context.Background(), time.Second)
	defer cancel()

	var resBody interface{}
	res, err := c.c.Call(
		ctx,
		yarpc.NewReqMeta().Headers(h).Procedure("echo"),
		map[string]interface{}{}, &resBody)

	if err != nil {
		return yarpc.Headers{}, err
	}
	return res.Headers(), nil
}
Beispiel #16
0
func call(client helloclient.Interface, message string) (*echo.EchoResponse, yarpc.Headers) {
	ctx, cancel := context.WithTimeout(context.Background(), time.Second)
	defer cancel()

	resBody, resMeta, err := client.Echo(
		ctx,
		yarpc.NewReqMeta().Headers(yarpc.NewHeaders().With("from", "self")),
		&echo.EchoRequest{Message: message, Count: 1},
	)
	if err != nil {
		log.Fatal(err)
	}

	return resBody, resMeta.Headers()
}
Beispiel #17
0
func thriftCall(dispatcher yarpc.Dispatcher, headers yarpc.Headers, token string) (string, yarpc.CallResMeta, error) {
	client := echoclient.New(dispatcher.Channel(serverName))

	ctx, cancel := context.WithTimeout(context.Background(), time.Second)
	defer cancel()

	reqMeta := yarpc.NewReqMeta().Headers(headers)
	ping := &echo.Ping{Beep: token}

	resBody, resMeta, err := client.Echo(ctx, reqMeta, ping)
	if err != nil {
		return "", nil, err
	}
	return resBody.Boop, resMeta, err
}
Beispiel #18
0
// Phone implements the phone procedure
func Phone(ctx context.Context, reqMeta yarpc.ReqMeta, body *PhoneRequest) (*PhoneResponse, yarpc.ResMeta, error) {
	var outbound transport.UnaryOutbound

	switch {
	case body.Transport.HTTP != nil:
		t := body.Transport.HTTP
		url := fmt.Sprintf("http://%s:%d", t.Host, t.Port)
		outbound = ht.NewOutbound(url)
	case body.Transport.TChannel != nil:
		t := body.Transport.TChannel
		hostport := fmt.Sprintf("%s:%d", t.Host, t.Port)
		ch, err := tchannel.NewChannel("yarpc-test-client", nil)
		if err != nil {
			return nil, nil, fmt.Errorf("failed to build TChannel: %v", err)
		}
		outbound = tch.NewOutbound(ch, tch.HostPort(hostport))
	default:
		return nil, nil, fmt.Errorf("unconfigured transport")
	}

	if err := outbound.Start(transport.NoDeps); err != nil {
		return nil, nil, err
	}
	defer outbound.Stop()

	// TODO use reqMeta.Service for caller
	client := json.New(channel.MultiOutbound("yarpc-test", body.Service, transport.Outbounds{
		Unary: outbound,
	}))
	resBody := PhoneResponse{
		Service:   "yarpc-test", // TODO use reqMeta.Service
		Procedure: reqMeta.Procedure(),
	}

	ctx, cancel := context.WithTimeout(ctx, 500*time.Millisecond)
	defer cancel()
	_, err := client.Call(
		ctx,
		yarpc.NewReqMeta().Procedure(body.Procedure),
		body.Body,
		&resBody.Body)
	if err != nil {
		return nil, nil, err
	}

	return &resBody, nil, nil
}
Beispiel #19
0
// Raw implements the 'raw' behavior.
func Raw(t crossdock.T) {
	t = createEchoT("raw", t)
	fatals := crossdock.Fatals(t)

	dispatcher := disp.Create(t)
	fatals.NoError(dispatcher.Start(), "could not start Dispatcher")
	defer dispatcher.Stop()

	client := raw.New(dispatcher.Channel("yarpc-test"))
	ctx, cancel := context.WithTimeout(context.Background(), time.Second)
	defer cancel()

	token := random.Bytes(5)
	resBody, _, err := client.Call(ctx, yarpc.NewReqMeta().Procedure("echo/raw"), token)

	crossdock.Fatals(t).NoError(err, "call to echo/raw failed: %v", err)
	crossdock.Assert(t).True(bytes.Equal(token, resBody), "server said: %v", resBody)
}
Beispiel #20
0
func TestEmptyReplay(t *testing.T) {
	tMock := testingTMock{t, 0}

	dir, err := ioutil.TempDir("", "yarpcgorecorder")
	if err != nil {
		t.Fatal(err)
	}
	defer os.RemoveAll(dir) // clean up

	recorder := NewRecorder(&tMock, RecordMode(Replay), RecordsPath(dir))

	withDisconnectedClient(t, recorder, func(client raw.Client) {
		ctx, cancel := context.WithTimeout(context.Background(), time.Second)
		defer cancel()

		require.Panics(t, func() {
			client.Call(ctx, yarpc.NewReqMeta().Procedure("hello"), []byte("Hello"))
		})
		assert.Equal(t, tMock.fatalCount, 1)
	})
}
Beispiel #21
0
// Raw starts an http run using raw encoding
func Raw(t crossdock.T, dispatcher yarpc.Dispatcher) {
	fatals := crossdock.Fatals(t)

	client := raw.New(dispatcher.Channel("oneway-test"))
	ctx := context.Background()

	token := []byte(getRandomID())
	ack, err := client.CallOneway(ctx, yarpc.NewReqMeta().Procedure("echo/raw"), token)

	// ensure channel hasn't been filled yet
	select {
	case <-serverCalledBack:
		fatals.FailNow("oneway raw test failed", "client waited for server to fill channel")
	default:
	}

	fatals.NoError(err, "call to oneway/raw failed: %v", err)
	fatals.NotNil(ack, "ack is nil")

	serverToken := <-serverCalledBack
	fatals.Equal(token, serverToken, "Client/Server token mismatch.")
}
Beispiel #22
0
// runRaw tests if a yarpc client returns a remote timeout error behind the
// TimeoutError interface when a remote http handler returns a handler timeout.
func runRaw(t crossdock.T, disp yarpc.Dispatcher) {
	assert := crossdock.Assert(t)
	fatals := crossdock.Fatals(t)

	ctx, cancel := context.WithTimeout(context.Background(), 100*time.Millisecond)
	defer cancel()

	ch := raw.New(disp.Channel("yarpc-test"))
	_, _, err := ch.Call(ctx, yarpc.NewReqMeta().Procedure("handlertimeout/raw"), nil)
	fatals.Error(err, "expected an error")

	if transport.IsBadRequestError(err) {
		t.Skipf("handlertimeout/raw method not implemented: %v", err)
		return
	}

	assert.True(transport.IsTimeoutError(err), "returns a TimeoutError: %T", err)

	form := strings.HasPrefix(err.Error(),
		`Timeout: call to procedure "handlertimeout/raw" of service "service" from caller "caller" timed out after`)
	assert.True(form, "must be a remote handler timeout: %q", err.Error())
}
Beispiel #23
0
// JSON implements the 'json' behavior.
func JSON(t crossdock.T) {
	t = createEchoT("json", t)
	fatals := crossdock.Fatals(t)

	dispatcher := disp.Create(t)
	fatals.NoError(dispatcher.Start(), "could not start Dispatcher")
	defer dispatcher.Stop()

	client := json.New(dispatcher.Channel("yarpc-test"))
	ctx, cancel := context.WithTimeout(context.Background(), time.Second)
	defer cancel()

	var response jsonEcho
	token := random.String(5)
	_, err := client.Call(
		ctx,
		yarpc.NewReqMeta().Procedure("echo"),
		&jsonEcho{Token: token},
		&response,
	)
	crossdock.Fatals(t).NoError(err, "call to echo failed: %v", err)
	crossdock.Assert(t).Equal(token, response.Token, "server said: %v", response.Token)
}
Beispiel #24
0
// Run tests if a yarpc client returns correctly a client timeout error behind
// the TimeoutError interface when the context deadline is reached while the
// server is taking too long to respond.
func Run(t crossdock.T) {
	assert := crossdock.Assert(t)
	fatals := crossdock.Fatals(t)

	ctx, cancel := context.WithTimeout(context.Background(), 100*time.Millisecond)
	defer cancel()

	dispatcher := disp.Create(t)
	fatals.NoError(dispatcher.Start(), "could not start Dispatcher")
	defer dispatcher.Stop()

	ch := raw.New(dispatcher.Channel("yarpc-test"))
	_, _, err := ch.Call(ctx, yarpc.NewReqMeta().Procedure("sleep/raw"), nil)
	fatals.Error(err, "expected a failure for timeout")

	if transport.IsBadRequestError(err) {
		t.Skipf("sleep/raw method not implemented: %v", err)
		return
	}

	assert.True(transport.IsTimeoutError(err), "returns a TimeoutError: %T", err)

	trans := t.Param(params.Transport)
	switch trans {
	case "http":
		form := strings.HasPrefix(err.Error(),
			`client timeout for procedure "sleep/raw" of service "yarpc-test" after`)
		assert.True(form, "should be a client timeout: %q", err.Error())
	case "tchannel":
		form := strings.HasPrefix(err.Error(), `timeout`)
		assert.True(form,
			"should be a remote timeout (we cant represent client timeout with tchannel): %q",
			err.Error())
	default:
		fatals.Fail("", "unknown transport %q", trans)
	}
}
Beispiel #25
0
func TestHandlerPanic(t *testing.T) {
	inbound := NewInbound("localhost:0")
	serverDispatcher := yarpc.NewDispatcher(yarpc.Config{
		Name:     "yarpc-test",
		Inbounds: []transport.Inbound{inbound},
	})
	serverDispatcher.Register([]transport.Registrant{
		{
			Procedure:   "panic",
			HandlerSpec: transport.NewUnaryHandlerSpec(panickedHandler{}),
		},
	})

	require.NoError(t, serverDispatcher.Start())
	defer serverDispatcher.Stop()

	clientDispatcher := yarpc.NewDispatcher(yarpc.Config{
		Name: "yarpc-test-client",
		Outbounds: yarpc.Outbounds{
			"yarpc-test": {
				Unary: NewOutbound(fmt.Sprintf("http://%s", inbound.Addr().String())),
			},
		},
	})
	require.NoError(t, clientDispatcher.Start())
	defer clientDispatcher.Stop()

	client := raw.New(clientDispatcher.Channel("yarpc-test"))
	ctx, cancel := context.WithTimeout(context.Background(), time.Second)
	defer cancel()
	_, _, err := client.Call(ctx, yarpc.NewReqMeta().Procedure("panic"), []byte{})

	assert.True(t, transport.IsUnexpectedError(err), "Must be an UnexpectedError")
	assert.Equal(t,
		`UnexpectedError: error for procedure "panic" of service "yarpc-test": panic: oops I panicked!`,
		err.Error())
}
Beispiel #26
0
func TestCallOneway(t *testing.T) {
	mockCtrl := gomock.NewController(t)
	defer mockCtrl.Finish()

	ctx := context.Background()

	caller := "caller"
	service := "service"

	tests := []struct {
		procedure      string
		headers        yarpc.Headers
		body           interface{}
		encodedRequest string

		// whether the outbound receives the request
		noCall bool

		wantErr string // error message
	}{
		{
			procedure:      "foo",
			body:           []string{"foo", "bar"},
			encodedRequest: `["foo","bar"]` + "\n",
		},
		{
			procedure: "baz",
			body:      func() {}, // funcs cannot be json.Marshal'ed
			noCall:    true,
			wantErr:   `failed to encode "json" request body for procedure "baz" of service "service"`,
		},
		{
			procedure:      "requestHeaders",
			headers:        yarpc.NewHeaders().With("user-id", "42"),
			body:           map[string]interface{}{},
			encodedRequest: "{}\n",
		},
	}

	for _, tt := range tests {
		outbound := transporttest.NewMockOnewayOutbound(mockCtrl)
		client := New(channel.MultiOutbound(caller, service,
			transport.Outbounds{
				Oneway: outbound,
			}))

		if !tt.noCall {
			reqMatcher := transporttest.NewRequestMatcher(t,
				&transport.Request{
					Caller:    caller,
					Service:   service,
					Procedure: tt.procedure,
					Encoding:  Encoding,
					Headers:   transport.Headers(tt.headers),
					Body:      bytes.NewReader([]byte(tt.encodedRequest)),
				})

			if tt.wantErr != "" {
				outbound.
					EXPECT().
					CallOneway(gomock.Any(), reqMatcher).
					Return(nil, errors.New(tt.wantErr))
			} else {
				outbound.
					EXPECT().
					CallOneway(gomock.Any(), reqMatcher).
					Return(&successAck{}, nil)
			}
		}

		ack, err := client.CallOneway(
			ctx,
			yarpc.NewReqMeta().Procedure(tt.procedure).Headers(tt.headers),
			tt.body)

		if tt.wantErr != "" {
			assert.Error(t, err)
			assert.Contains(t, err.Error(), tt.wantErr)
		} else {
			assert.NoError(t, err, "")
			assert.Equal(t, ack.String(), "success")
		}
	}
}
Beispiel #27
0
func TestCall(t *testing.T) {
	mockCtrl := gomock.NewController(t)
	defer mockCtrl.Finish()

	ctx := context.Background()

	caller := "caller"
	service := "service"

	tests := []struct {
		procedure       string
		headers         yarpc.Headers
		body            interface{}
		encodedRequest  string
		encodedResponse string

		// whether the outbound receives the request
		noCall bool

		// Either want, or wantType and wantErr must be set.
		want        interface{} // expected response body
		wantHeaders yarpc.Headers
		wantType    reflect.Type // type of response body
		wantErr     string       // error message
	}{
		{
			procedure:       "foo",
			body:            []string{"foo", "bar"},
			encodedRequest:  `["foo","bar"]`,
			encodedResponse: `{"success": true}`,
			want:            map[string]interface{}{"success": true},
		},
		{
			procedure:       "bar",
			body:            []int{1, 2, 3},
			encodedRequest:  `[1,2,3]`,
			encodedResponse: `invalid JSON`,
			wantType:        _typeOfMapInterface,
			wantErr:         `failed to decode "json" response body for procedure "bar" of service "service"`,
		},
		{
			procedure: "baz",
			body:      func() {}, // funcs cannot be json.Marshal'ed
			noCall:    true,
			wantType:  _typeOfMapInterface,
			wantErr:   `failed to encode "json" request body for procedure "baz" of service "service"`,
		},
		{
			procedure:       "requestHeaders",
			headers:         yarpc.NewHeaders().With("user-id", "42"),
			body:            map[string]interface{}{},
			encodedRequest:  "{}",
			encodedResponse: "{}",
			want:            map[string]interface{}{},
			wantHeaders:     yarpc.NewHeaders().With("success", "true"),
		},
	}

	for _, tt := range tests {
		outbound := transporttest.NewMockUnaryOutbound(mockCtrl)
		client := New(channel.MultiOutbound(caller, service,
			transport.Outbounds{
				Unary: outbound,
			}))

		if !tt.noCall {
			outbound.EXPECT().Call(gomock.Any(),
				transporttest.NewRequestMatcher(t,
					&transport.Request{
						Caller:    caller,
						Service:   service,
						Procedure: tt.procedure,
						Encoding:  Encoding,
						Headers:   transport.Headers(tt.headers),
						Body:      bytes.NewReader([]byte(tt.encodedRequest)),
					}),
			).Return(
				&transport.Response{
					Body: ioutil.NopCloser(
						bytes.NewReader([]byte(tt.encodedResponse))),
					Headers: transport.Headers(tt.wantHeaders),
				}, nil)
		}

		var wantType reflect.Type
		if tt.want != nil {
			wantType = reflect.TypeOf(tt.want)
		} else {
			require.NotNil(t, tt.wantType, "wantType is required if want is nil")
			wantType = tt.wantType
		}
		resBody := reflect.Zero(wantType).Interface()

		res, err := client.Call(
			ctx,
			yarpc.NewReqMeta().Procedure(tt.procedure).Headers(tt.headers),
			tt.body,
			&resBody,
		)

		if tt.wantErr != "" {
			if assert.Error(t, err) {
				assert.Contains(t, err.Error(), tt.wantErr)
			}
		} else {
			if assert.NoError(t, err) {
				assert.Equal(t, tt.wantHeaders, res.Headers())
				assert.Equal(t, tt.want, resBody)
			}
		}
	}
}
Beispiel #28
0
// RunGauntlet takes an rpc object and runs the gauntlet
func RunGauntlet(t crossdock.T, c Config) {
	checks := crossdock.Checks(t)
	if c.Services == 0 {
		c.Services = AllServices
	}

	bytesToken := random.Bytes(10)
	tests := []TT{
		{
			Function: "TestBinary",
			Give:     []interface{}{bytesToken},
			Want:     bytesToken,
		},
		{
			Function: "TestByte",
			Give:     []interface{}{ptr.Int8(42)},
			Want:     int8(42),
		},
		{
			Function: "TestDouble",
			Give:     []interface{}{ptr.Float64(12.34)},
			Want:     float64(12.34),
		},
		{
			Function: "TestEnum",
			Details:  "MyNumberz",
			Give:     []interface{}{numberzp(gauntlet.MyNumberz)},
			Want:     gauntlet.MyNumberz,
		},
		{
			Function: "TestEnum",
			Details:  "NumberzThree",
			Give:     []interface{}{numberzp(gauntlet.NumberzThree)},
			Want:     gauntlet.NumberzThree,
		},
		{
			Function: "TestEnum",
			Details:  "unrecognized Numberz",
			Give:     []interface{}{numberzp(gauntlet.Numberz(42))},
			Want:     gauntlet.Numberz(42),
		},
		{
			Function: "TestException",
			Details:  "Xception",
			Give:     []interface{}{ptr.String("Xception")},
			WantError: &gauntlet.Xception{
				ErrorCode: ptr.Int32(1001),
				Message:   ptr.String("Xception"),
			},
		},
		{
			Function:      "TestException",
			Details:       "TException",
			Give:          []interface{}{ptr.String("TException")},
			WantErrorLike: "great sadness",
		},
		{
			Function: "TestException",
			Details:  "no error",
			Give:     []interface{}{ptr.String("yolo")},
		},
		{
			Function: "TestI32",
			Give:     []interface{}{ptr.Int32(123)},
			Want:     int32(123),
		},
		{
			Function: "TestI64",
			Give:     []interface{}{ptr.Int64(18934714)},
			Want:     int64(18934714),
		},
		{
			Function: "TestInsanity",
			Give: []interface{}{
				&gauntlet.Insanity{
					UserMap: map[gauntlet.Numberz]gauntlet.UserId{
						gauntlet.NumberzThree: gauntlet.UserId(100),
						gauntlet.Numberz(100): gauntlet.UserId(200),
					},
					Xtructs: []*gauntlet.Xtruct{
						{StringThing: ptr.String("0")},
						{ByteThing: ptr.Int8(1)},
						{I32Thing: ptr.Int32(2)},
						{I64Thing: ptr.Int64(3)},
					},
				},
			},
			Want: map[gauntlet.UserId]map[gauntlet.Numberz]*gauntlet.Insanity{
				1: {
					gauntlet.NumberzTwo: &gauntlet.Insanity{
						UserMap: map[gauntlet.Numberz]gauntlet.UserId{
							gauntlet.NumberzThree: gauntlet.UserId(100),
							gauntlet.Numberz(100): gauntlet.UserId(200),
						},
						Xtructs: []*gauntlet.Xtruct{
							{StringThing: ptr.String("0")},
							{ByteThing: ptr.Int8(1)},
							{I32Thing: ptr.Int32(2)},
							{I64Thing: ptr.Int64(3)},
						},
					},
					gauntlet.NumberzThree: &gauntlet.Insanity{
						UserMap: map[gauntlet.Numberz]gauntlet.UserId{
							gauntlet.NumberzThree: gauntlet.UserId(100),
							gauntlet.Numberz(100): gauntlet.UserId(200),
						},
						Xtructs: []*gauntlet.Xtruct{
							{StringThing: ptr.String("0")},
							{ByteThing: ptr.Int8(1)},
							{I32Thing: ptr.Int32(2)},
							{I64Thing: ptr.Int64(3)},
						},
					},
				},
				2: {
					gauntlet.NumberzSix: &gauntlet.Insanity{},
				},
			},
		},
		{
			Function: "TestList",
			Give:     []interface{}{[]int32{1, 2, 3}},
			Want:     []int32{1, 2, 3},
		},
		{
			Function: "TestMap",
			Give:     []interface{}{map[int32]int32{1: 2, 3: 4, 5: 6}},
			Want:     map[int32]int32{1: 2, 3: 4, 5: 6},
		},
		{
			Function: "TestMapMap",
			Give:     []interface{}{ptr.Int32(42)},
			Want: map[int32]map[int32]int32{
				-4: {
					-4: -4,
					-3: -3,
					-2: -2,
					-1: -1,
				},
				4: {
					1: 1,
					2: 2,
					3: 3,
					4: 4,
				},
			},
		},
		{
			Function: "TestMulti",
			Give: []interface{}{
				ptr.Int8(100),
				ptr.Int32(200),
				ptr.Int64(300),
				map[int16]string{1: "1", 2: "2", 3: "3"},
				numberzp(gauntlet.NumberzEight),
				useridp(42),
			},
			Want: &gauntlet.Xtruct{
				StringThing: ptr.String("Hello2"),
				ByteThing:   ptr.Int8(100),
				I32Thing:    ptr.Int32(200),
				I64Thing:    ptr.Int64(300),
			},
		},
		{
			Function: "TestMultiException",
			Details:  "Xception",
			Give:     []interface{}{ptr.String("Xception"), ptr.String("foo")},
			WantError: &gauntlet.Xception{
				ErrorCode: ptr.Int32(1001),
				Message:   ptr.String("This is an Xception"),
			},
		},
		{
			Function: "TestMultiException",
			Details:  "Xception2",
			Give:     []interface{}{ptr.String("Xception2"), ptr.String("foo")},
			WantError: &gauntlet.Xception2{
				ErrorCode:   ptr.Int32(2002),
				StructThing: &gauntlet.Xtruct{StringThing: ptr.String("foo")},
			},
		},
		{
			Function: "TestMultiException",
			Details:  "no error",
			Give:     []interface{}{ptr.String("hello"), ptr.String("foo")},
			Want:     &gauntlet.Xtruct{StringThing: ptr.String("foo")},
		},
		{
			Function: "TestNest",
			Give: []interface{}{
				&gauntlet.Xtruct2{
					ByteThing: ptr.Int8(-1),
					I32Thing:  ptr.Int32(-1234),
					StructThing: &gauntlet.Xtruct{
						StringThing: ptr.String("0"),
						ByteThing:   ptr.Int8(1),
						I32Thing:    ptr.Int32(2),
						I64Thing:    ptr.Int64(3),
					},
				},
			},
			Want: &gauntlet.Xtruct2{
				ByteThing: ptr.Int8(-1),
				I32Thing:  ptr.Int32(-1234),
				StructThing: &gauntlet.Xtruct{
					StringThing: ptr.String("0"),
					ByteThing:   ptr.Int8(1),
					I32Thing:    ptr.Int32(2),
					I64Thing:    ptr.Int64(3),
				},
			},
		},
		{
			Function: "TestSet",
			Give: []interface{}{
				map[int32]struct{}{
					1:  {},
					2:  {},
					-1: {},
					-2: {},
				},
			},
			Want: map[int32]struct{}{
				1:  {},
				2:  {},
				-1: {},
				-2: {},
			},
		},
		{
			Function: "TestString",
			Give:     []interface{}{ptr.String("hello")},
			Want:     "hello",
		},
		{
			Function: "TestStringMap",
			Give: []interface{}{
				map[string]string{
					"foo":   "bar",
					"hello": "world",
				},
			},
			Want: map[string]string{
				"foo":   "bar",
				"hello": "world",
			},
		},
		{
			Function: "TestStruct",
			Give: []interface{}{
				&gauntlet.Xtruct{
					StringThing: ptr.String("0"),
					ByteThing:   ptr.Int8(1),
					I32Thing:    ptr.Int32(2),
					I64Thing:    ptr.Int64(3),
				},
			},
			Want: &gauntlet.Xtruct{
				StringThing: ptr.String("0"),
				ByteThing:   ptr.Int8(1),
				I32Thing:    ptr.Int32(2),
				I64Thing:    ptr.Int64(3),
			},
		},
		{
			Function: "TestTypedef",
			Give:     []interface{}{useridp(42)},
			Want:     gauntlet.UserId(42),
		},
		{
			Function: "TestVoid",
			Give:     []interface{}{},
		},
		{
			Function:  "TestOneway",
			Oneway:    true,
			Give:      []interface{}{ptr.Int32(123)},
			WantError: nil,
		},
		{
			Service:  "SecondService",
			Function: "BlahBlah",
			Give:     []interface{}{},
		},
		{
			Service:  "SecondService",
			Function: "SecondtestString",
			Give:     []interface{}{ptr.String("hello")},
			Want:     "hello",
		},
	}

	for _, tt := range tests {
		if tt.Service == "" {
			tt.Service = "ThriftTest"
		}

		switch tt.Service {
		case "ThriftTest":
			if c.Services&ThriftTest == 0 {
				continue
			}
		case "SecondService":
			if c.Services&SecondService == 0 {
				continue
			}
		}

		t.Tag("service", tt.Service)
		t.Tag("function", tt.Function)

		//only run oneway tests if specified
		if !c.EnableOneway && tt.Oneway {
			continue
		}

		desc := BuildDesc(tt)

		client := buildClient(t, desc, tt.Service, c)
		f := client.MethodByName(tt.Function)
		if !checks.True(f.IsValid(), "%v: invalid function", desc) {
			continue
		}

		ctx, cancel := context.WithTimeout(context.Background(), time.Second)
		defer cancel()
		args := []reflect.Value{reflect.ValueOf(ctx), reflect.ValueOf(yarpc.NewReqMeta())}
		if give, ok := BuildArgs(t, desc, f.Type(), tt.Give, 2); ok {
			args = append(args, give...)
		} else {
			continue
		}

		got, err := extractCallResponse(t, desc, f.Call(args))
		if isUnrecognizedProcedure(err) {
			t.Skipf("%v: procedure not defined", desc)
			continue
		}
		Assert(t, tt, desc, got, err)
	}
}
Beispiel #29
0
func TestCall(t *testing.T) {
	mockCtrl := gomock.NewController(t)
	defer mockCtrl.Finish()

	ctx := context.Background()

	caller := "caller"
	service := "service"

	tests := []struct {
		procedure    string
		headers      yarpc.Headers
		body         []byte
		responseBody [][]byte

		want        []byte
		wantErr     string
		wantHeaders yarpc.Headers
	}{
		{
			procedure:    "foo",
			body:         []byte{1, 2, 3},
			responseBody: [][]byte{{4}, {5}, {6}},
			want:         []byte{4, 5, 6},
		},
		{
			procedure:    "bar",
			body:         []byte{1, 2, 3},
			responseBody: [][]byte{{4}, {5}, nil, {6}},
			wantErr:      "error set by user",
		},
		{
			procedure:    "headers",
			headers:      yarpc.NewHeaders().With("x", "y"),
			body:         []byte{},
			responseBody: [][]byte{},
			want:         []byte{},
			wantHeaders:  yarpc.NewHeaders().With("a", "b"),
		},
	}

	for _, tt := range tests {
		outbound := transporttest.NewMockUnaryOutbound(mockCtrl)
		client := New(channel.MultiOutbound(caller, service,
			transport.Outbounds{
				Unary: outbound,
			}))

		writer, responseBody := testreader.ChunkReader()
		for _, chunk := range tt.responseBody {
			writer <- chunk
		}
		close(writer)

		outbound.EXPECT().Call(gomock.Any(),
			transporttest.NewRequestMatcher(t,
				&transport.Request{
					Caller:    caller,
					Service:   service,
					Procedure: tt.procedure,
					Headers:   transport.Headers(tt.headers),
					Encoding:  Encoding,
					Body:      bytes.NewReader(tt.body),
				}),
		).Return(
			&transport.Response{
				Body:    ioutil.NopCloser(responseBody),
				Headers: transport.Headers(tt.wantHeaders),
			}, nil)

		resBody, res, err := client.Call(
			ctx,
			yarpc.NewReqMeta().Procedure(tt.procedure).Headers(tt.headers),
			tt.body)

		if tt.wantErr != "" {
			if assert.Error(t, err) {
				assert.Equal(t, err.Error(), tt.wantErr)
			}
		} else {
			if assert.NoError(t, err) {
				assert.Equal(t, tt.want, resBody)
				assert.Equal(t, tt.wantHeaders, res.Headers())
			}
		}
	}
}
Beispiel #30
0
func TestCallOneway(t *testing.T) {
	mockCtrl := gomock.NewController(t)
	defer mockCtrl.Finish()

	ctx := context.Background()

	caller := "caller"
	service := "service"

	tests := []struct {
		procedure string
		headers   yarpc.Headers
		body      []byte

		wantErr     string
		wantHeaders yarpc.Headers
	}{
		{
			procedure: "foo",
			body:      []byte{1, 2, 3},
		},
		{
			procedure: "headers",
			headers:   yarpc.NewHeaders().With("x", "y"),
			body:      []byte{},
		},
	}

	for _, tt := range tests {
		outbound := transporttest.NewMockOnewayOutbound(mockCtrl)
		client := New(channel.MultiOutbound(caller, service,
			transport.Outbounds{
				Oneway: outbound,
			}))

		outbound.EXPECT().CallOneway(gomock.Any(),
			transporttest.NewRequestMatcher(t,
				&transport.Request{
					Caller:    caller,
					Service:   service,
					Procedure: tt.procedure,
					Headers:   transport.Headers(tt.headers),
					Encoding:  Encoding,
					Body:      bytes.NewReader(tt.body),
				}),
		).Return(&successAck{}, nil)

		ack, err := client.CallOneway(
			ctx,
			yarpc.NewReqMeta().Procedure(tt.procedure).Headers(tt.headers),
			tt.body)

		if tt.wantErr != "" {
			if assert.Error(t, err) {
				assert.Equal(t, err.Error(), tt.wantErr)
			}
		} else {
			assert.Equal(t, "success", ack.String())
		}
	}
}