1303 lines
		
	
	
		
			32 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			1303 lines
		
	
	
		
			32 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
| package builder_test
 | |
| 
 | |
| import (
 | |
| 	"testing"
 | |
| 
 | |
| 	"github.com/stretchr/testify/require"
 | |
| 	"google.golang.org/protobuf/proto"
 | |
| 
 | |
| 	"go.unistack.org/micro-client-http/v3/builder"
 | |
| 	pb "go.unistack.org/micro-client-http/v3/builder/proto"
 | |
| )
 | |
| 
 | |
| func TestNewRequestBuilder(t *testing.T) {
 | |
| 	tests := []struct {
 | |
| 		name      string
 | |
| 		path      string
 | |
| 		method    string
 | |
| 		bodyOpt   string
 | |
| 		msg       proto.Message
 | |
| 		wantError bool
 | |
| 	}{
 | |
| 		{
 | |
| 			name:      "empty path",
 | |
| 			path:      "",
 | |
| 			method:    "GET",
 | |
| 			bodyOpt:   "",
 | |
| 			msg:       &pb.TestRequestBuilder{},
 | |
| 			wantError: true,
 | |
| 		},
 | |
| 		{
 | |
| 			name:      "invalid method",
 | |
| 			path:      "/v1/users",
 | |
| 			method:    "INVALID",
 | |
| 			bodyOpt:   "",
 | |
| 			msg:       &pb.TestRequestBuilder{},
 | |
| 			wantError: true,
 | |
| 		},
 | |
| 		{
 | |
| 			name:      "nil msg",
 | |
| 			path:      "/v1/users",
 | |
| 			method:    "POST",
 | |
| 			bodyOpt:   "*",
 | |
| 			msg:       nil,
 | |
| 			wantError: true,
 | |
| 		},
 | |
| 		{
 | |
| 			name:      "GET without body",
 | |
| 			path:      "/v1/users",
 | |
| 			method:    "GET",
 | |
| 			bodyOpt:   "",
 | |
| 			msg:       &pb.TestRequestBuilder{},
 | |
| 			wantError: false,
 | |
| 		},
 | |
| 		{
 | |
| 			name:      "GET with body",
 | |
| 			path:      "/v1/users",
 | |
| 			method:    "GET",
 | |
| 			bodyOpt:   "*",
 | |
| 			msg:       &pb.TestRequestBuilder{},
 | |
| 			wantError: true,
 | |
| 		},
 | |
| 		{
 | |
| 			name:      "DELETE without body",
 | |
| 			path:      "/v1/users/42",
 | |
| 			method:    "DELETE",
 | |
| 			bodyOpt:   "",
 | |
| 			msg:       &pb.TestRequestBuilder{},
 | |
| 			wantError: false,
 | |
| 		},
 | |
| 		{
 | |
| 			name:      "DELETE with body",
 | |
| 			path:      "/v1/users/42",
 | |
| 			method:    "DELETE",
 | |
| 			bodyOpt:   "*",
 | |
| 			msg:       &pb.TestRequestBuilder{},
 | |
| 			wantError: true,
 | |
| 		},
 | |
| 		{
 | |
| 			name:      "POST with body",
 | |
| 			path:      "/v1/users",
 | |
| 			method:    "POST",
 | |
| 			bodyOpt:   "*",
 | |
| 			msg:       &pb.TestRequestBuilder{},
 | |
| 			wantError: false,
 | |
| 		},
 | |
| 		{
 | |
| 			name:      "POST without body",
 | |
| 			path:      "/v1/users",
 | |
| 			method:    "POST",
 | |
| 			bodyOpt:   "",
 | |
| 			msg:       &pb.TestRequestBuilder{},
 | |
| 			wantError: false,
 | |
| 		},
 | |
| 		{
 | |
| 			name:      "PUT with body",
 | |
| 			path:      "/v1/users/42",
 | |
| 			method:    "PUT",
 | |
| 			bodyOpt:   "*",
 | |
| 			msg:       &pb.TestRequestBuilder{},
 | |
| 			wantError: false,
 | |
| 		},
 | |
| 		{
 | |
| 			name:      "PUT without body",
 | |
| 			path:      "/v1/users/42",
 | |
| 			method:    "PUT",
 | |
| 			bodyOpt:   "",
 | |
| 			msg:       &pb.TestRequestBuilder{},
 | |
| 			wantError: false,
 | |
| 		},
 | |
| 		{
 | |
| 			name:      "PATCH with body",
 | |
| 			path:      "/v1/users/42",
 | |
| 			method:    "PATCH",
 | |
| 			bodyOpt:   "*",
 | |
| 			msg:       &pb.TestRequestBuilder{},
 | |
| 			wantError: false,
 | |
| 		},
 | |
| 		{
 | |
| 			name:      "PATCH without body",
 | |
| 			path:      "/v1/users/42",
 | |
| 			method:    "PATCH",
 | |
| 			bodyOpt:   "",
 | |
| 			msg:       &pb.TestRequestBuilder{},
 | |
| 			wantError: false,
 | |
| 		},
 | |
| 		{
 | |
| 			name:      "HEAD without body",
 | |
| 			path:      "/v1/users/42",
 | |
| 			method:    "HEAD",
 | |
| 			bodyOpt:   "",
 | |
| 			msg:       &pb.TestRequestBuilder{},
 | |
| 			wantError: false,
 | |
| 		},
 | |
| 		{
 | |
| 			name:      "HEAD with body",
 | |
| 			path:      "/v1/users/42",
 | |
| 			method:    "HEAD",
 | |
| 			bodyOpt:   "*",
 | |
| 			msg:       &pb.TestRequestBuilder{},
 | |
| 			wantError: true,
 | |
| 		},
 | |
| 		{
 | |
| 			name:      "OPTIONS without body",
 | |
| 			path:      "/v1/users/42",
 | |
| 			method:    "OPTIONS",
 | |
| 			bodyOpt:   "",
 | |
| 			msg:       &pb.TestRequestBuilder{},
 | |
| 			wantError: false,
 | |
| 		},
 | |
| 		{
 | |
| 			name:      "OPTIONS with body",
 | |
| 			path:      "/v1/users/42",
 | |
| 			method:    "OPTIONS",
 | |
| 			bodyOpt:   "*",
 | |
| 			msg:       &pb.TestRequestBuilder{},
 | |
| 			wantError: true,
 | |
| 		},
 | |
| 		{
 | |
| 			name:      "lowercase method still valid",
 | |
| 			path:      "/v1/users",
 | |
| 			method:    "post",
 | |
| 			bodyOpt:   "*",
 | |
| 			msg:       &pb.TestRequestBuilder{},
 | |
| 			wantError: false,
 | |
| 		},
 | |
| 	}
 | |
| 
 | |
| 	for _, tt := range tests {
 | |
| 		t.Run(tt.name, func(t *testing.T) {
 | |
| 			rb, err := builder.NewRequestBuilder(tt.path, tt.method, tt.bodyOpt, tt.msg)
 | |
| 
 | |
| 			if tt.wantError {
 | |
| 				require.Error(t, err)
 | |
| 				require.Nil(t, rb)
 | |
| 			} else {
 | |
| 				require.NoError(t, err)
 | |
| 				require.NotNil(t, rb)
 | |
| 			}
 | |
| 		})
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func TestRequestBuilder_PathOnly(t *testing.T) {
 | |
| 	tests := []struct {
 | |
| 		name         string
 | |
| 		path         string
 | |
| 		method       string
 | |
| 		msg          func() proto.Message
 | |
| 		expectedPath string
 | |
| 		expectedMsg  func() proto.Message
 | |
| 		wantError    bool
 | |
| 	}{
 | |
| 		{
 | |
| 			name:   "primitive case",
 | |
| 			path:   "/v1/users/{user_id}/orders/{order_id}",
 | |
| 			method: "GET",
 | |
| 			msg: func() proto.Message {
 | |
| 				type Msg = pb.Test_PathOnly_PrimitiveCase
 | |
| 				return &Msg{
 | |
| 					UserId:  "42",
 | |
| 					OrderId: 123,
 | |
| 				}
 | |
| 			},
 | |
| 			expectedPath: "/v1/users/42/orders/123",
 | |
| 			expectedMsg: func() proto.Message {
 | |
| 				type Msg = pb.Test_PathOnly_PrimitiveCase
 | |
| 				return &Msg{}
 | |
| 			},
 | |
| 			wantError: false,
 | |
| 		},
 | |
| 		{
 | |
| 			name:   "nested case",
 | |
| 			path:   "/v1/users/{user.id}/orders/{order.id}/products/{order.product.id}",
 | |
| 			method: "GET",
 | |
| 			msg: func() proto.Message {
 | |
| 				type Msg = pb.Test_PathOnly_NestedCase
 | |
| 				type User = pb.Test_PathOnly_NestedCase_User
 | |
| 				type Order = pb.Test_PathOnly_NestedCase_Order
 | |
| 				type Product = pb.Test_PathOnly_NestedCase_Order_Product
 | |
| 				return &Msg{
 | |
| 					User: &User{Id: "42"},
 | |
| 					Order: &Order{
 | |
| 						Id:      123,
 | |
| 						Product: &Product{Id: 456},
 | |
| 					},
 | |
| 				}
 | |
| 			},
 | |
| 			expectedPath: "/v1/users/42/orders/123/products/456",
 | |
| 			expectedMsg: func() proto.Message {
 | |
| 				type Msg = pb.Test_PathOnly_NestedCase
 | |
| 				return &Msg{}
 | |
| 			},
 | |
| 			wantError: false,
 | |
| 		},
 | |
| 		{
 | |
| 			name:   "multiply case",
 | |
| 			path:   "/v1/users/{user_id}/orders/{order.id}",
 | |
| 			method: "GET",
 | |
| 			msg: func() proto.Message {
 | |
| 				type Msg = pb.Test_PathOnly_MultipleCase
 | |
| 				type Order = pb.Test_PathOnly_MultipleCase_Order
 | |
| 				return &Msg{
 | |
| 					UserId: "42",
 | |
| 					Order: &Order{
 | |
| 						Id: "123",
 | |
| 					},
 | |
| 				}
 | |
| 			},
 | |
| 			expectedPath: "/v1/users/42/orders/123",
 | |
| 			expectedMsg: func() proto.Message {
 | |
| 				type Msg = pb.Test_PathOnly_MultipleCase
 | |
| 				return &Msg{}
 | |
| 			},
 | |
| 			wantError: false,
 | |
| 		},
 | |
| 		{
 | |
| 			name:   "not found case",
 | |
| 			path:   "/v1/users/{userId}/orders/{order_id}",
 | |
| 			method: "GET",
 | |
| 			msg: func() proto.Message {
 | |
| 				type Msg = pb.Test_PathOnly_PrimitiveCase
 | |
| 				return &Msg{
 | |
| 					UserId:  "42",
 | |
| 					OrderId: 123,
 | |
| 				}
 | |
| 			},
 | |
| 			expectedPath: "",
 | |
| 			expectedMsg: func() proto.Message {
 | |
| 				return nil
 | |
| 			},
 | |
| 			wantError: true,
 | |
| 		},
 | |
| 		{
 | |
| 			name:   "zero-value case",
 | |
| 			path:   "/v1/users/{user_id}/orders/{order_id}",
 | |
| 			method: "GET",
 | |
| 			msg: func() proto.Message {
 | |
| 				type Msg = pb.Test_PathOnly_PrimitiveCase
 | |
| 				return &Msg{UserId: "42"}
 | |
| 			},
 | |
| 			expectedPath: "",
 | |
| 			expectedMsg: func() proto.Message {
 | |
| 				return nil
 | |
| 			},
 | |
| 			wantError: true,
 | |
| 		},
 | |
| 		{
 | |
| 			name:   "repeated case",
 | |
| 			path:   "/v1/users/{user_id}/orders/{order_id}",
 | |
| 			method: "GET",
 | |
| 			msg: func() proto.Message {
 | |
| 				type Msg = pb.Test_PathOnly_RepeatedCase
 | |
| 				return &Msg{
 | |
| 					UserId:  []string{"42"},
 | |
| 					OrderId: 123,
 | |
| 				}
 | |
| 			},
 | |
| 			expectedPath: "",
 | |
| 			expectedMsg: func() proto.Message {
 | |
| 				return nil
 | |
| 			},
 | |
| 			wantError: true,
 | |
| 		},
 | |
| 		{
 | |
| 			name:   "non-primitive message case",
 | |
| 			path:   "/v1/users/{user_id}/orders/{order_id}",
 | |
| 			method: "GET",
 | |
| 			msg: func() proto.Message {
 | |
| 				type Msg = pb.Test_PathOnly_NonPrimitiveMessageCase
 | |
| 				type User = pb.Test_PathOnly_NonPrimitiveMessageCase_User
 | |
| 				return &Msg{
 | |
| 					UserId:  &User{Id: "42"},
 | |
| 					OrderId: 123,
 | |
| 				}
 | |
| 			},
 | |
| 			expectedPath: "",
 | |
| 			expectedMsg: func() proto.Message {
 | |
| 				return nil
 | |
| 			},
 | |
| 			wantError: true,
 | |
| 		},
 | |
| 		{
 | |
| 			name:   "non-primitive map case",
 | |
| 			path:   "/v1/users/{user_id}/orders/{order_id}",
 | |
| 			method: "GET",
 | |
| 			msg: func() proto.Message {
 | |
| 				type Msg = pb.Test_PathOnly_NonPrimitiveMapCase
 | |
| 				return &Msg{
 | |
| 					UserId:  map[string]string{"": ""},
 | |
| 					OrderId: 123,
 | |
| 				}
 | |
| 			},
 | |
| 			expectedPath: "",
 | |
| 			expectedMsg: func() proto.Message {
 | |
| 				return nil
 | |
| 			},
 | |
| 			wantError: true,
 | |
| 		},
 | |
| 		{
 | |
| 			name:   "custom verb case",
 | |
| 			path:   "/v1/users/{user_id}:get",
 | |
| 			method: "GET",
 | |
| 			msg: func() proto.Message {
 | |
| 				type Msg = pb.Test_PathOnly_PrimitiveCase
 | |
| 				return &Msg{UserId: "42"}
 | |
| 			},
 | |
| 			expectedPath: "/v1/users/42:get",
 | |
| 			expectedMsg: func() proto.Message {
 | |
| 				type Msg = pb.Test_PathOnly_PrimitiveCase
 | |
| 				return &Msg{}
 | |
| 			},
 | |
| 			wantError: false,
 | |
| 		},
 | |
| 		// pattern cases
 | |
| 		{
 | |
| 			name:   "pattern case -> *",
 | |
| 			path:   "/v1/users/{pattern=*}",
 | |
| 			method: "GET",
 | |
| 			msg: func() proto.Message {
 | |
| 				type Msg = pb.Test_PathOnly_PatternCase
 | |
| 				return &Msg{Pattern: "42"}
 | |
| 			},
 | |
| 			expectedPath: "/v1/users/42",
 | |
| 			expectedMsg: func() proto.Message {
 | |
| 				type Msg = pb.Test_PathOnly_PatternCase
 | |
| 				return &Msg{}
 | |
| 			},
 | |
| 			wantError: false,
 | |
| 		},
 | |
| 		{
 | |
| 			name:   "pattern case -> * (invalid value)",
 | |
| 			path:   "/v1/users/{pattern=*}",
 | |
| 			method: "GET",
 | |
| 			msg: func() proto.Message {
 | |
| 				type Msg = pb.Test_PathOnly_PatternCase
 | |
| 				return &Msg{Pattern: "a/b/c"}
 | |
| 			},
 | |
| 			expectedPath: "",
 | |
| 			expectedMsg: func() proto.Message {
 | |
| 				return nil
 | |
| 			},
 | |
| 			wantError: true,
 | |
| 		},
 | |
| 		{
 | |
| 			name:   "pattern case -> * (empty value)",
 | |
| 			path:   "/v1/users/{pattern=*}",
 | |
| 			method: "GET",
 | |
| 			msg: func() proto.Message {
 | |
| 				type Msg = pb.Test_PathOnly_PatternCase
 | |
| 				return &Msg{}
 | |
| 			},
 | |
| 			expectedPath: "",
 | |
| 			expectedMsg: func() proto.Message {
 | |
| 				return nil
 | |
| 			},
 | |
| 			wantError: true,
 | |
| 		},
 | |
| 		{
 | |
| 			name:   "pattern case -> **",
 | |
| 			path:   "/v1/users/{pattern=**}",
 | |
| 			method: "GET",
 | |
| 			msg: func() proto.Message {
 | |
| 				type Msg = pb.Test_PathOnly_PatternCase
 | |
| 				return &Msg{Pattern: "a/b/c"}
 | |
| 			},
 | |
| 			expectedPath: "/v1/users/a/b/c",
 | |
| 			expectedMsg: func() proto.Message {
 | |
| 				type Msg = pb.Test_PathOnly_PatternCase
 | |
| 				return &Msg{}
 | |
| 			},
 | |
| 			wantError: false,
 | |
| 		},
 | |
| 		{
 | |
| 			name:   "pattern case -> ** (empty value)",
 | |
| 			path:   "/v1/users/{pattern=**}",
 | |
| 			method: "GET",
 | |
| 			msg: func() proto.Message {
 | |
| 				type Msg = pb.Test_PathOnly_PatternCase
 | |
| 				return &Msg{}
 | |
| 			},
 | |
| 			expectedPath: "/v1/users/",
 | |
| 			expectedMsg: func() proto.Message {
 | |
| 				type Msg = pb.Test_PathOnly_PatternCase
 | |
| 				return &Msg{}
 | |
| 			},
 | |
| 			wantError: false,
 | |
| 		},
 | |
| 		{
 | |
| 			name:   "pattern case -> composite pattern",
 | |
| 			path:   "/v1/users/{pattern=*/orders/*}",
 | |
| 			method: "GET",
 | |
| 			msg: func() proto.Message {
 | |
| 				type Msg = pb.Test_PathOnly_PatternCase
 | |
| 				return &Msg{Pattern: "42/orders/123"}
 | |
| 			},
 | |
| 			expectedPath: "/v1/users/42/orders/123",
 | |
| 			expectedMsg: func() proto.Message {
 | |
| 				type Msg = pb.Test_PathOnly_PatternCase
 | |
| 				return &Msg{}
 | |
| 			},
 | |
| 			wantError: false,
 | |
| 		},
 | |
| 		{
 | |
| 			name:   "pattern case -> composite pattern (with extra segment)",
 | |
| 			path:   "/v1/users/{pattern=*/orders/*}",
 | |
| 			method: "GET",
 | |
| 			msg: func() proto.Message {
 | |
| 				type Msg = pb.Test_PathOnly_PatternCase
 | |
| 				return &Msg{Pattern: "42/orders/123/456"}
 | |
| 			},
 | |
| 			expectedPath: "",
 | |
| 			expectedMsg: func() proto.Message {
 | |
| 				return nil
 | |
| 			},
 | |
| 			wantError: true,
 | |
| 		},
 | |
| 		{
 | |
| 			name:   "pattern case -> ** (composite segments)",
 | |
| 			path:   "/v1/users/{pattern=**}/orders/{order_id}/products/{product_id}",
 | |
| 			method: "GET",
 | |
| 			msg: func() proto.Message {
 | |
| 				type Msg = pb.Test_PathOnly_CompositePatternCase
 | |
| 				return &Msg{
 | |
| 					Pattern:   "a/b/c",
 | |
| 					OrderId:   "123",
 | |
| 					ProductId: "456",
 | |
| 				}
 | |
| 			},
 | |
| 			expectedPath: "/v1/users/a/b/c/orders/123/products/456",
 | |
| 			expectedMsg: func() proto.Message {
 | |
| 				type Msg = pb.Test_PathOnly_CompositePatternCase
 | |
| 				return &Msg{}
 | |
| 			},
 | |
| 			wantError: false,
 | |
| 		},
 | |
| 		{
 | |
| 			name:   "pattern case -> ** (with empty value and composite segments)",
 | |
| 			path:   "/v1/users/{pattern=**}/orders/{order_id}/products/{product_id}",
 | |
| 			method: "GET",
 | |
| 			msg: func() proto.Message {
 | |
| 				type Msg = pb.Test_PathOnly_CompositePatternCase
 | |
| 				return &Msg{
 | |
| 					Pattern:   "",
 | |
| 					OrderId:   "123",
 | |
| 					ProductId: "456",
 | |
| 				}
 | |
| 			},
 | |
| 			expectedPath: "/v1/users//orders/123/products/456",
 | |
| 			expectedMsg: func() proto.Message {
 | |
| 				type Msg = pb.Test_PathOnly_CompositePatternCase
 | |
| 				return &Msg{}
 | |
| 			},
 | |
| 			wantError: false,
 | |
| 		},
 | |
| 		{
 | |
| 			name:   "pattern case -> composite pattern (multiple consecutive variables)",
 | |
| 			path:   "/v1/{pattern}/{order_id}/{product_id}",
 | |
| 			method: "GET",
 | |
| 			msg: func() proto.Message {
 | |
| 				type Msg = pb.Test_PathOnly_CompositePatternCase
 | |
| 				return &Msg{
 | |
| 					Pattern:   "123",
 | |
| 					OrderId:   "456",
 | |
| 					ProductId: "789",
 | |
| 				}
 | |
| 			},
 | |
| 			expectedPath: "/v1/123/456/789",
 | |
| 			expectedMsg: func() proto.Message {
 | |
| 				type Msg = pb.Test_PathOnly_CompositePatternCase
 | |
| 				return &Msg{}
 | |
| 			},
 | |
| 			wantError: false,
 | |
| 		},
 | |
| 	}
 | |
| 
 | |
| 	for _, tt := range tests {
 | |
| 		t.Run(tt.name, func(t *testing.T) {
 | |
| 			rb, err := builder.NewRequestBuilder(tt.path, tt.method, "", tt.msg())
 | |
| 			require.NoError(t, err)
 | |
| 
 | |
| 			path, newMsg, err := rb.Build()
 | |
| 			if tt.wantError {
 | |
| 				require.Error(t, err)
 | |
| 				require.Empty(t, path)
 | |
| 				require.Nil(t, newMsg)
 | |
| 			} else {
 | |
| 				require.NoError(t, err)
 | |
| 				require.Equal(t, tt.expectedPath, path)
 | |
| 				require.True(t, proto.Equal(tt.expectedMsg(), newMsg))
 | |
| 			}
 | |
| 		})
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func TestRequestBuilder_QueryOnly(t *testing.T) {
 | |
| 	tests := []struct {
 | |
| 		name         string
 | |
| 		path         string
 | |
| 		method       string
 | |
| 		msg          func() proto.Message
 | |
| 		expectedPath string
 | |
| 		expectedMsg  func() proto.Message
 | |
| 		wantError    bool
 | |
| 	}{
 | |
| 		{
 | |
| 			name:   "primitive case",
 | |
| 			path:   "/v1/users",
 | |
| 			method: "GET",
 | |
| 			msg: func() proto.Message {
 | |
| 				type Msg = pb.Test_QueryOnly_PrimitiveCase
 | |
| 				return &Msg{
 | |
| 					UserId:  "42",
 | |
| 					OrderId: 123,
 | |
| 					Flag:    true,
 | |
| 				}
 | |
| 			},
 | |
| 			expectedPath: "/v1/users?flag=true&order_id=123&user_id=42",
 | |
| 			expectedMsg: func() proto.Message {
 | |
| 				type Msg = pb.Test_QueryOnly_PrimitiveCase
 | |
| 				return &Msg{}
 | |
| 			},
 | |
| 			wantError: false,
 | |
| 		},
 | |
| 		{
 | |
| 			name:   "primitive case (with empty fields)",
 | |
| 			path:   "/v1/users",
 | |
| 			method: "GET",
 | |
| 			msg: func() proto.Message {
 | |
| 				type Msg = pb.Test_QueryOnly_PrimitiveCase
 | |
| 				return &Msg{UserId: "42"}
 | |
| 			},
 | |
| 			expectedPath: "/v1/users?user_id=42",
 | |
| 			expectedMsg: func() proto.Message {
 | |
| 				type Msg = pb.Test_QueryOnly_PrimitiveCase
 | |
| 				return &Msg{}
 | |
| 			},
 | |
| 			wantError: false,
 | |
| 		},
 | |
| 		{
 | |
| 			name:   "repeated case",
 | |
| 			path:   "/v1/users",
 | |
| 			method: "GET",
 | |
| 			msg: func() proto.Message {
 | |
| 				type Msg = pb.Test_QueryOnly_RepeatedCase
 | |
| 				return &Msg{
 | |
| 					Strings:  []string{"foo", "bar"},
 | |
| 					Integers: []int64{1, 2, 3},
 | |
| 				}
 | |
| 			},
 | |
| 			expectedPath: "/v1/users?integers=1&integers=2&integers=3&strings=foo&strings=bar",
 | |
| 			expectedMsg: func() proto.Message {
 | |
| 				type Msg = pb.Test_QueryOnly_RepeatedCase
 | |
| 				return &Msg{}
 | |
| 			},
 | |
| 			wantError: false,
 | |
| 		},
 | |
| 		{
 | |
| 			name:   "nested message case",
 | |
| 			path:   "/v1/users",
 | |
| 			method: "GET",
 | |
| 			msg: func() proto.Message {
 | |
| 				type Msg = pb.Test_QueryOnly_NestedMessageCase
 | |
| 				type Filter = pb.Test_QueryOnly_NestedMessageCase_Filter
 | |
| 				type SubFilter = pb.Test_QueryOnly_NestedMessageCase_Filter_SubFilter
 | |
| 				return &Msg{
 | |
| 					UserId: "42",
 | |
| 					Filter: &Filter{
 | |
| 						Age:  30,
 | |
| 						Name: "Alice",
 | |
| 						SubFilter: &SubFilter{
 | |
| 							SubAge:  20,
 | |
| 							SubName: "John",
 | |
| 						},
 | |
| 					},
 | |
| 				}
 | |
| 			},
 | |
| 			expectedPath: "/v1/users?filter.age=30&filter.name=Alice&filter.sub_filter.sub_age=20&filter.sub_filter.sub_name=John&user_id=42",
 | |
| 			expectedMsg: func() proto.Message {
 | |
| 				type Msg = pb.Test_QueryOnly_NestedMessageCase
 | |
| 				return &Msg{}
 | |
| 			},
 | |
| 			wantError: false,
 | |
| 		},
 | |
| 		{
 | |
| 			name:   "nested map case",
 | |
| 			path:   "/v1/users",
 | |
| 			method: "GET",
 | |
| 			msg: func() proto.Message {
 | |
| 				type Msg = pb.Test_QueryOnly_NestedMapCase
 | |
| 				type SubFilter = pb.Test_QueryOnly_NestedMapCase_SubFilter
 | |
| 				return &Msg{
 | |
| 					UserId:      "42",
 | |
| 					FirstFilter: map[string]string{"age": "30", "name": "Alice"},
 | |
| 					SecondFilter: map[string]*SubFilter{
 | |
| 						"filter1": {SubAge: 20, SubName: "John"},
 | |
| 						"filter2": {SubAge: 40, SubName: "Travolta"},
 | |
| 					},
 | |
| 				}
 | |
| 			},
 | |
| 			expectedPath: "/v1/users?first_filter.age=30&first_filter.name=Alice&second_filter.filter1.sub_age=20&second_filter.filter1.sub_name=John&second_filter.filter2.sub_age=40&second_filter.filter2.sub_name=Travolta&user_id=42",
 | |
| 			expectedMsg: func() proto.Message {
 | |
| 				type Msg = pb.Test_QueryOnly_NestedMapCase
 | |
| 				return &Msg{}
 | |
| 			},
 | |
| 			wantError: false,
 | |
| 		},
 | |
| 		{
 | |
| 			name:   "multiple case",
 | |
| 			path:   "/v1/users",
 | |
| 			method: "GET",
 | |
| 			msg: func() proto.Message {
 | |
| 				type Msg = pb.Test_QueryOnly_MultipleCase
 | |
| 				type Filter = pb.Test_QueryOnly_MultipleCase_Filter
 | |
| 				type SubFilter = pb.Test_QueryOnly_MultipleCase_SubFilter
 | |
| 				return &Msg{
 | |
| 					UserId:  "42",
 | |
| 					Strings: []string{"foo", "bar"},
 | |
| 					FirstFilter: &Filter{
 | |
| 						Age: 30,
 | |
| 						SubFilter: &SubFilter{
 | |
| 							SubAge: 20,
 | |
| 						},
 | |
| 					},
 | |
| 					SecondFilter: map[string]*SubFilter{
 | |
| 						"filter1": {SubAge: 20},
 | |
| 						"filter2": {SubAge: 40},
 | |
| 					},
 | |
| 				}
 | |
| 			},
 | |
| 			expectedPath: "/v1/users?first_filter.age=30&first_filter.sub_filter.sub_age=20&second_filter.filter1.sub_age=20&second_filter.filter2.sub_age=40&strings=foo&strings=bar&user_id=42",
 | |
| 			expectedMsg: func() proto.Message {
 | |
| 				type Msg = pb.Test_QueryOnly_MultipleCase
 | |
| 				return &Msg{}
 | |
| 			},
 | |
| 			wantError: false,
 | |
| 		},
 | |
| 		{
 | |
| 			name:   "repeated message case",
 | |
| 			path:   "/v1/users",
 | |
| 			method: "GET",
 | |
| 			msg: func() proto.Message {
 | |
| 				type Msg = pb.Test_QueryOnly_RepeatedMessageCase
 | |
| 				type Filter = pb.Test_QueryOnly_RepeatedMessageCase_Filter
 | |
| 				return &Msg{Filters: []*Filter{{Age: 20}, {Age: 30}, {Age: 40}}}
 | |
| 			},
 | |
| 			expectedPath: "",
 | |
| 			expectedMsg:  nil,
 | |
| 			wantError:    true,
 | |
| 		},
 | |
| 	}
 | |
| 
 | |
| 	for _, tt := range tests {
 | |
| 		t.Run(tt.name, func(t *testing.T) {
 | |
| 			rb, err := builder.NewRequestBuilder(tt.path, tt.method, "", tt.msg())
 | |
| 			require.NoError(t, err)
 | |
| 
 | |
| 			path, newMsg, err := rb.Build()
 | |
| 			if tt.wantError {
 | |
| 				require.Error(t, err)
 | |
| 				require.Empty(t, path)
 | |
| 				require.Nil(t, newMsg)
 | |
| 			} else {
 | |
| 				require.NoError(t, err)
 | |
| 				require.Equal(t, tt.expectedPath, path)
 | |
| 				require.True(t, proto.Equal(tt.expectedMsg(), newMsg))
 | |
| 			}
 | |
| 		})
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func TestRequestBuilder_BodyOnly(t *testing.T) {
 | |
| 	tests := []struct {
 | |
| 		name         string
 | |
| 		path         string
 | |
| 		method       string
 | |
| 		bodyOpt      string
 | |
| 		msg          func() proto.Message
 | |
| 		expectedPath string
 | |
| 		expectedMsg  func() proto.Message
 | |
| 		wantError    bool
 | |
| 	}{
 | |
| 		{
 | |
| 			name:    "primitive case: full body",
 | |
| 			path:    "/v1/users",
 | |
| 			method:  "POST",
 | |
| 			bodyOpt: "*",
 | |
| 			msg: func() proto.Message {
 | |
| 				type Msg = pb.Test_BodyOnly_PrimitiveCase
 | |
| 				type Product = pb.Test_BodyOnly_PrimitiveCase_Product
 | |
| 				return &Msg{
 | |
| 					UserId:  "42",
 | |
| 					OrderId: 123,
 | |
| 					Flag:    true,
 | |
| 					Strings: []string{"foo", "bar"},
 | |
| 					Product: &Product{
 | |
| 						Id:   "product_id",
 | |
| 						Name: "product_name",
 | |
| 					},
 | |
| 				}
 | |
| 			},
 | |
| 			expectedPath: "/v1/users",
 | |
| 			expectedMsg: func() proto.Message {
 | |
| 				type Msg = pb.Test_BodyOnly_PrimitiveCase
 | |
| 				type Product = pb.Test_BodyOnly_PrimitiveCase_Product
 | |
| 				return &Msg{
 | |
| 					UserId:  "42",
 | |
| 					OrderId: 123,
 | |
| 					Flag:    true,
 | |
| 					Strings: []string{"foo", "bar"},
 | |
| 					Product: &Product{
 | |
| 						Id:   "product_id",
 | |
| 						Name: "product_name",
 | |
| 					},
 | |
| 				}
 | |
| 			},
 | |
| 			wantError: false,
 | |
| 		},
 | |
| 		{
 | |
| 			name:    "primitive case: specified primitive field",
 | |
| 			path:    "/v1/users",
 | |
| 			method:  "POST",
 | |
| 			bodyOpt: "user_id",
 | |
| 			msg: func() proto.Message {
 | |
| 				type Msg = pb.Test_BodyOnly_PrimitiveCase
 | |
| 				type Product = pb.Test_BodyOnly_PrimitiveCase_Product
 | |
| 				return &Msg{
 | |
| 					UserId:  "42",
 | |
| 					OrderId: 123,
 | |
| 					Flag:    true,
 | |
| 					Strings: []string{"foo", "bar"},
 | |
| 					Product: &Product{
 | |
| 						Id:   "product_id",
 | |
| 						Name: "product_name",
 | |
| 					},
 | |
| 				}
 | |
| 			},
 | |
| 			expectedPath: "/v1/users?flag=true&order_id=123&product.id=product_id&product.name=product_name&strings=foo&strings=bar",
 | |
| 			expectedMsg: func() proto.Message {
 | |
| 				type Msg = pb.Test_BodyOnly_PrimitiveCase
 | |
| 				return &Msg{
 | |
| 					UserId: "42",
 | |
| 				}
 | |
| 			},
 | |
| 			wantError: false,
 | |
| 		},
 | |
| 		{
 | |
| 			name:    "primitive case: specified non-primitive field",
 | |
| 			path:    "/v1/users",
 | |
| 			method:  "POST",
 | |
| 			bodyOpt: "product",
 | |
| 			msg: func() proto.Message {
 | |
| 				type Msg = pb.Test_BodyOnly_PrimitiveCase
 | |
| 				type Product = pb.Test_BodyOnly_PrimitiveCase_Product
 | |
| 				return &Msg{
 | |
| 					UserId:  "42",
 | |
| 					OrderId: 123,
 | |
| 					Flag:    true,
 | |
| 					Strings: []string{"foo", "bar"},
 | |
| 					Product: &Product{
 | |
| 						Id:   "product_id",
 | |
| 						Name: "product_name",
 | |
| 					},
 | |
| 				}
 | |
| 			},
 | |
| 			expectedPath: "/v1/users?flag=true&order_id=123&strings=foo&strings=bar&user_id=42",
 | |
| 			expectedMsg: func() proto.Message {
 | |
| 				type Product = pb.Test_BodyOnly_PrimitiveCase_Product
 | |
| 				return &Product{
 | |
| 					Id:   "product_id",
 | |
| 					Name: "product_name",
 | |
| 				}
 | |
| 			},
 | |
| 			wantError: false,
 | |
| 		},
 | |
| 		{
 | |
| 			name:    "primitive case: empty fields",
 | |
| 			path:    "/v1/users",
 | |
| 			method:  "POST",
 | |
| 			bodyOpt: "*",
 | |
| 			msg: func() proto.Message {
 | |
| 				type Msg = pb.Test_BodyOnly_PrimitiveCase
 | |
| 				return &Msg{
 | |
| 					UserId: "42",
 | |
| 					Flag:   true,
 | |
| 				}
 | |
| 			},
 | |
| 			expectedPath: "/v1/users",
 | |
| 			expectedMsg: func() proto.Message {
 | |
| 				type Msg = pb.Test_BodyOnly_PrimitiveCase
 | |
| 				type Product = pb.Test_BodyOnly_PrimitiveCase_Product
 | |
| 				return &Msg{
 | |
| 					UserId:  "42",
 | |
| 					OrderId: 0,
 | |
| 					Flag:    true,
 | |
| 					Strings: []string{},
 | |
| 					Product: &Product{},
 | |
| 				}
 | |
| 			},
 | |
| 			wantError: false,
 | |
| 		},
 | |
| 		{
 | |
| 			name:    "primitive case: empty all fields",
 | |
| 			path:    "/v1/users",
 | |
| 			method:  "POST",
 | |
| 			bodyOpt: "*",
 | |
| 			msg: func() proto.Message {
 | |
| 				type Msg = pb.Test_BodyOnly_PrimitiveCase
 | |
| 				return &Msg{}
 | |
| 			},
 | |
| 			expectedPath: "/v1/users",
 | |
| 			expectedMsg: func() proto.Message {
 | |
| 				type Msg = pb.Test_BodyOnly_PrimitiveCase
 | |
| 				type Product = pb.Test_BodyOnly_PrimitiveCase_Product
 | |
| 				return &Msg{
 | |
| 					UserId:  "",
 | |
| 					OrderId: 0,
 | |
| 					Flag:    false,
 | |
| 					Strings: []string{},
 | |
| 					Product: &Product{},
 | |
| 				}
 | |
| 			},
 | |
| 			wantError: false,
 | |
| 		},
 | |
| 		{
 | |
| 			name:    "nested case: full body",
 | |
| 			path:    "/v1/users",
 | |
| 			method:  "POST",
 | |
| 			bodyOpt: "*",
 | |
| 			msg: func() proto.Message {
 | |
| 				type Msg = pb.Test_BodyOnly_NestedCase
 | |
| 				type Filter = pb.Test_BodyOnly_NestedCase_Filter
 | |
| 				type SubFilter = pb.Test_BodyOnly_NestedCase_Filter_SubFilter
 | |
| 				return &Msg{
 | |
| 					UserId: "42",
 | |
| 					FirstFilter: &Filter{
 | |
| 						Age:  30,
 | |
| 						Name: "Alice",
 | |
| 						SubFilter: &SubFilter{
 | |
| 							SubAge:  40,
 | |
| 							SubName: "John",
 | |
| 						},
 | |
| 					},
 | |
| 					SecondFilter: &Filter{
 | |
| 						Age:  50,
 | |
| 						Name: "Alex",
 | |
| 						SubFilter: &SubFilter{
 | |
| 							SubAge:  60,
 | |
| 							SubName: "Mike",
 | |
| 						},
 | |
| 					},
 | |
| 				}
 | |
| 			},
 | |
| 			expectedPath: "/v1/users",
 | |
| 			expectedMsg: func() proto.Message {
 | |
| 				type Msg = pb.Test_BodyOnly_NestedCase
 | |
| 				type Filter = pb.Test_BodyOnly_NestedCase_Filter
 | |
| 				type SubFilter = pb.Test_BodyOnly_NestedCase_Filter_SubFilter
 | |
| 				return &Msg{
 | |
| 					UserId: "42",
 | |
| 					FirstFilter: &Filter{
 | |
| 						Age:  30,
 | |
| 						Name: "Alice",
 | |
| 						SubFilter: &SubFilter{
 | |
| 							SubAge:  40,
 | |
| 							SubName: "John",
 | |
| 						},
 | |
| 					},
 | |
| 					SecondFilter: &Filter{
 | |
| 						Age:  50,
 | |
| 						Name: "Alex",
 | |
| 						SubFilter: &SubFilter{
 | |
| 							SubAge:  60,
 | |
| 							SubName: "Mike",
 | |
| 						},
 | |
| 					},
 | |
| 				}
 | |
| 			},
 | |
| 			wantError: false,
 | |
| 		},
 | |
| 		{
 | |
| 			name:    "nested case: specified non-primitive field",
 | |
| 			path:    "/v1/users",
 | |
| 			method:  "POST",
 | |
| 			bodyOpt: "second_filter",
 | |
| 			msg: func() proto.Message {
 | |
| 				type Msg = pb.Test_BodyOnly_NestedCase
 | |
| 				type Filter = pb.Test_BodyOnly_NestedCase_Filter
 | |
| 				type SubFilter = pb.Test_BodyOnly_NestedCase_Filter_SubFilter
 | |
| 				return &Msg{
 | |
| 					UserId: "42",
 | |
| 					FirstFilter: &Filter{
 | |
| 						Age:  30,
 | |
| 						Name: "Alice",
 | |
| 						SubFilter: &SubFilter{
 | |
| 							SubAge:  40,
 | |
| 							SubName: "John",
 | |
| 						},
 | |
| 					},
 | |
| 					SecondFilter: &Filter{
 | |
| 						Age:  50,
 | |
| 						Name: "Alex",
 | |
| 						SubFilter: &SubFilter{
 | |
| 							SubAge:  60,
 | |
| 							SubName: "Mike",
 | |
| 						},
 | |
| 					},
 | |
| 				}
 | |
| 			},
 | |
| 			expectedPath: "/v1/users?first_filter.age=30&first_filter.name=Alice&first_filter.sub_filter.sub_age=40&first_filter.sub_filter.sub_name=John&user_id=42",
 | |
| 			expectedMsg: func() proto.Message {
 | |
| 				type Filter = pb.Test_BodyOnly_NestedCase_Filter
 | |
| 				type SubFilter = pb.Test_BodyOnly_NestedCase_Filter_SubFilter
 | |
| 				return &Filter{
 | |
| 					Age:  50,
 | |
| 					Name: "Alex",
 | |
| 					SubFilter: &SubFilter{
 | |
| 						SubAge:  60,
 | |
| 						SubName: "Mike",
 | |
| 					},
 | |
| 				}
 | |
| 			},
 | |
| 			wantError: false,
 | |
| 		},
 | |
| 		{
 | |
| 			name:    "repeated message case",
 | |
| 			path:    "/v1/users",
 | |
| 			method:  "POST",
 | |
| 			bodyOpt: "*",
 | |
| 			msg: func() proto.Message {
 | |
| 				type Msg = pb.Test_BodyOnly_RepeatedMessageCase
 | |
| 				type Product = pb.Test_BodyOnly_RepeatedMessageCase_Product
 | |
| 				return &Msg{
 | |
| 					UserId: "42",
 | |
| 					Products: []*Product{
 | |
| 						{Id: "product_id_1", Name: "product_name_1"},
 | |
| 						{Id: "product_id_2", Name: "product_name_2"},
 | |
| 					},
 | |
| 				}
 | |
| 			},
 | |
| 			expectedPath: "/v1/users",
 | |
| 			expectedMsg: func() proto.Message {
 | |
| 				type Msg = pb.Test_BodyOnly_RepeatedMessageCase
 | |
| 				type Product = pb.Test_BodyOnly_RepeatedMessageCase_Product
 | |
| 				return &Msg{
 | |
| 					UserId: "42",
 | |
| 					Products: []*Product{
 | |
| 						{Id: "product_id_1", Name: "product_name_1"},
 | |
| 						{Id: "product_id_2", Name: "product_name_2"},
 | |
| 					},
 | |
| 				}
 | |
| 			},
 | |
| 			wantError: false,
 | |
| 		},
 | |
| 		{
 | |
| 			name:    "repeated message case (empty)",
 | |
| 			path:    "/v1/users",
 | |
| 			method:  "POST",
 | |
| 			bodyOpt: "*",
 | |
| 			msg: func() proto.Message {
 | |
| 				type Msg = pb.Test_BodyOnly_RepeatedMessageCase
 | |
| 				type Product = pb.Test_BodyOnly_RepeatedMessageCase_Product
 | |
| 				return &Msg{
 | |
| 					UserId:   "42",
 | |
| 					Products: []*Product{},
 | |
| 				}
 | |
| 			},
 | |
| 			expectedPath: "/v1/users",
 | |
| 			expectedMsg: func() proto.Message {
 | |
| 				type Msg = pb.Test_BodyOnly_RepeatedMessageCase
 | |
| 				type Product = pb.Test_BodyOnly_RepeatedMessageCase_Product
 | |
| 				return &Msg{
 | |
| 					UserId:   "42",
 | |
| 					Products: []*Product{},
 | |
| 				}
 | |
| 			},
 | |
| 			wantError: false,
 | |
| 		},
 | |
| 		{
 | |
| 			name:    "primitive and non-primitive map case",
 | |
| 			path:    "/v1/users",
 | |
| 			method:  "POST",
 | |
| 			bodyOpt: "*",
 | |
| 			msg: func() proto.Message {
 | |
| 				type Msg = pb.Test_BodyOnly_MapCase
 | |
| 				type SubFilter = pb.Test_BodyOnly_MapCase_SubFilter
 | |
| 				return &Msg{
 | |
| 					FirstFilter: map[string]string{"age": "50", "name": "Alex"},
 | |
| 					SecondFilter: map[string]*SubFilter{
 | |
| 						"second_filter_1": {SubAge: 30, SubName: "Alice"},
 | |
| 						"second_filter_2": {SubAge: 40, SubName: "John"},
 | |
| 					},
 | |
| 				}
 | |
| 			},
 | |
| 			expectedPath: "/v1/users",
 | |
| 			expectedMsg: func() proto.Message {
 | |
| 				type Msg = pb.Test_BodyOnly_MapCase
 | |
| 				type SubFilter = pb.Test_BodyOnly_MapCase_SubFilter
 | |
| 				return &Msg{
 | |
| 					FirstFilter: map[string]string{"age": "50", "name": "Alex"},
 | |
| 					SecondFilter: map[string]*SubFilter{
 | |
| 						"second_filter_1": {SubAge: 30, SubName: "Alice"},
 | |
| 						"second_filter_2": {SubAge: 40, SubName: "John"},
 | |
| 					},
 | |
| 				}
 | |
| 			},
 | |
| 			wantError: false,
 | |
| 		},
 | |
| 		{
 | |
| 			name:    "primitive and non-primitive map case (empty)",
 | |
| 			path:    "/v1/users",
 | |
| 			method:  "POST",
 | |
| 			bodyOpt: "*",
 | |
| 			msg: func() proto.Message {
 | |
| 				type Msg = pb.Test_BodyOnly_MapCase
 | |
| 				return &Msg{}
 | |
| 			},
 | |
| 			expectedPath: "/v1/users",
 | |
| 			expectedMsg: func() proto.Message {
 | |
| 				type Msg = pb.Test_BodyOnly_MapCase
 | |
| 				type SubFilter = pb.Test_BodyOnly_MapCase_SubFilter
 | |
| 				return &Msg{
 | |
| 					FirstFilter:  map[string]string{},
 | |
| 					SecondFilter: map[string]*SubFilter{},
 | |
| 				}
 | |
| 			},
 | |
| 			wantError: false,
 | |
| 		},
 | |
| 		{
 | |
| 			name:    "multiple case",
 | |
| 			path:    "/v1/users",
 | |
| 			method:  "POST",
 | |
| 			bodyOpt: "*",
 | |
| 			msg: func() proto.Message {
 | |
| 				type Msg = pb.Test_BodyOnly_MultipleCase
 | |
| 				type SubFilter = pb.Test_BodyOnly_MultipleCase_SubFilter
 | |
| 				return &Msg{
 | |
| 					UserId: "42",
 | |
| 					FirstFilter: []*SubFilter{
 | |
| 						{SubAge: 30, SubName: "Alice"},
 | |
| 						{SubAge: 40, SubName: "John"},
 | |
| 					},
 | |
| 					SecondFilter: map[string]*SubFilter{
 | |
| 						"second_filter_1": {SubAge: 50, SubName: "Alex"},
 | |
| 						"second_filter_2": {SubAge: 60, SubName: "Max"},
 | |
| 					},
 | |
| 					ThirdFilter: &SubFilter{SubAge: 70, SubName: "Ricardo"},
 | |
| 				}
 | |
| 			},
 | |
| 			expectedPath: "/v1/users",
 | |
| 			expectedMsg: func() proto.Message {
 | |
| 				type Msg = pb.Test_BodyOnly_MultipleCase
 | |
| 				type SubFilter = pb.Test_BodyOnly_MultipleCase_SubFilter
 | |
| 				return &Msg{
 | |
| 					UserId: "42",
 | |
| 					FirstFilter: []*SubFilter{
 | |
| 						{SubAge: 30, SubName: "Alice"},
 | |
| 						{SubAge: 40, SubName: "John"},
 | |
| 					},
 | |
| 					SecondFilter: map[string]*SubFilter{
 | |
| 						"second_filter_1": {SubAge: 50, SubName: "Alex"},
 | |
| 						"second_filter_2": {SubAge: 60, SubName: "Max"},
 | |
| 					},
 | |
| 					ThirdFilter: &SubFilter{SubAge: 70, SubName: "Ricardo"},
 | |
| 				}
 | |
| 			},
 | |
| 			wantError: false,
 | |
| 		},
 | |
| 		{
 | |
| 			name:    "multiple case (empty)",
 | |
| 			path:    "/v1/users",
 | |
| 			method:  "POST",
 | |
| 			bodyOpt: "*",
 | |
| 			msg: func() proto.Message {
 | |
| 				type Msg = pb.Test_BodyOnly_MultipleCase
 | |
| 				return &Msg{}
 | |
| 			},
 | |
| 			expectedPath: "/v1/users",
 | |
| 			expectedMsg: func() proto.Message {
 | |
| 				type Msg = pb.Test_BodyOnly_MultipleCase
 | |
| 				type SubFilter = pb.Test_BodyOnly_MultipleCase_SubFilter
 | |
| 				return &Msg{
 | |
| 					UserId:       "",
 | |
| 					FirstFilter:  []*SubFilter{},
 | |
| 					SecondFilter: map[string]*SubFilter{},
 | |
| 					ThirdFilter:  &SubFilter{},
 | |
| 				}
 | |
| 			},
 | |
| 			wantError: false,
 | |
| 		},
 | |
| 		{
 | |
| 			name:    "nonexistent body field",
 | |
| 			path:    "/v1/users",
 | |
| 			method:  "POST",
 | |
| 			bodyOpt: "nonexistent_field",
 | |
| 			msg: func() proto.Message {
 | |
| 				type Msg = pb.Test_BodyOnly_PrimitiveCase
 | |
| 				return &Msg{}
 | |
| 			},
 | |
| 			expectedPath: "",
 | |
| 			expectedMsg:  nil,
 | |
| 			wantError:    true,
 | |
| 		},
 | |
| 	}
 | |
| 
 | |
| 	for _, tt := range tests {
 | |
| 		t.Run(tt.name, func(t *testing.T) {
 | |
| 			rb, err := builder.NewRequestBuilder(tt.path, tt.method, tt.bodyOpt, tt.msg())
 | |
| 			require.NoError(t, err)
 | |
| 
 | |
| 			path, newMsg, err := rb.Build()
 | |
| 			if tt.wantError {
 | |
| 				require.Error(t, err)
 | |
| 				require.Empty(t, path)
 | |
| 				require.Nil(t, newMsg)
 | |
| 			} else {
 | |
| 				require.NoError(t, err)
 | |
| 				require.Equal(t, tt.expectedPath, path)
 | |
| 				require.True(t, proto.Equal(tt.expectedMsg(), newMsg))
 | |
| 			}
 | |
| 		})
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func TestRequestBuilder_Mixed(t *testing.T) {
 | |
| 	tests := []struct {
 | |
| 		name         string
 | |
| 		path         string
 | |
| 		method       string
 | |
| 		bodyOpt      string
 | |
| 		msg          func() proto.Message
 | |
| 		expectedPath string
 | |
| 		expectedMsg  func() proto.Message
 | |
| 		wantError    bool
 | |
| 	}{
 | |
| 		{
 | |
| 			name:    "path + query",
 | |
| 			path:    "/v1/users/{user_id}/orders/{order_id}",
 | |
| 			method:  "POST",
 | |
| 			bodyOpt: "",
 | |
| 			msg: func() proto.Message {
 | |
| 				type Msg = pb.Test_Mixed_PrimitiveCase
 | |
| 				type Product = pb.Test_Mixed_PrimitiveCase_Product
 | |
| 				return &Msg{
 | |
| 					UserId:  "42",
 | |
| 					OrderId: 123,
 | |
| 					Product: &Product{
 | |
| 						Id:   "product_id",
 | |
| 						Name: "product_name",
 | |
| 					},
 | |
| 				}
 | |
| 			},
 | |
| 			expectedPath: "/v1/users/42/orders/123?product.id=product_id&product.name=product_name",
 | |
| 			expectedMsg: func() proto.Message {
 | |
| 				type Msg = pb.Test_Mixed_PrimitiveCase
 | |
| 				return &Msg{}
 | |
| 			},
 | |
| 			wantError: false,
 | |
| 		},
 | |
| 		{
 | |
| 			name:    "path + body",
 | |
| 			path:    "/v1/users/{user_id}/orders/{order_id}",
 | |
| 			method:  "POST",
 | |
| 			bodyOpt: "*",
 | |
| 			msg: func() proto.Message {
 | |
| 				type Msg = pb.Test_Mixed_PrimitiveCase
 | |
| 				type Product = pb.Test_Mixed_PrimitiveCase_Product
 | |
| 				return &Msg{
 | |
| 					UserId:  "42",
 | |
| 					OrderId: 123,
 | |
| 					Product: &Product{
 | |
| 						Id:   "product_id",
 | |
| 						Name: "product_name",
 | |
| 					},
 | |
| 				}
 | |
| 			},
 | |
| 			expectedPath: "/v1/users/42/orders/123",
 | |
| 			expectedMsg: func() proto.Message {
 | |
| 				type Msg = pb.Test_Mixed_PrimitiveCase
 | |
| 				type Product = pb.Test_Mixed_PrimitiveCase_Product
 | |
| 				return &Msg{
 | |
| 					Product: &Product{
 | |
| 						Id:   "product_id",
 | |
| 						Name: "product_name",
 | |
| 					},
 | |
| 				}
 | |
| 			},
 | |
| 			wantError: false,
 | |
| 		},
 | |
| 		{
 | |
| 			name:    "path + body + query",
 | |
| 			path:    "/v1/users/{user_id}",
 | |
| 			method:  "POST",
 | |
| 			bodyOpt: "product",
 | |
| 			msg: func() proto.Message {
 | |
| 				type Msg = pb.Test_Mixed_PrimitiveCase
 | |
| 				type Product = pb.Test_Mixed_PrimitiveCase_Product
 | |
| 				return &Msg{
 | |
| 					UserId:  "42",
 | |
| 					OrderId: 123,
 | |
| 					Product: &Product{
 | |
| 						Id:   "product_id",
 | |
| 						Name: "product_name",
 | |
| 					},
 | |
| 				}
 | |
| 			},
 | |
| 			expectedPath: "/v1/users/42?order_id=123",
 | |
| 			expectedMsg: func() proto.Message {
 | |
| 				type Product = pb.Test_Mixed_PrimitiveCase_Product
 | |
| 				return &Product{
 | |
| 					Id:   "product_id",
 | |
| 					Name: "product_name",
 | |
| 				}
 | |
| 			},
 | |
| 			wantError: false,
 | |
| 		},
 | |
| 		{
 | |
| 			name:    "query + body",
 | |
| 			path:    "/v1/users",
 | |
| 			method:  "POST",
 | |
| 			bodyOpt: "product",
 | |
| 			msg: func() proto.Message {
 | |
| 				type Msg = pb.Test_Mixed_PrimitiveCase
 | |
| 				type Product = pb.Test_Mixed_PrimitiveCase_Product
 | |
| 				return &Msg{
 | |
| 					UserId:  "42",
 | |
| 					OrderId: 123,
 | |
| 					Product: &Product{
 | |
| 						Id:   "product_id",
 | |
| 						Name: "product_name",
 | |
| 					},
 | |
| 				}
 | |
| 			},
 | |
| 			expectedPath: "/v1/users?order_id=123&user_id=42",
 | |
| 			expectedMsg: func() proto.Message {
 | |
| 				type Product = pb.Test_Mixed_PrimitiveCase_Product
 | |
| 				return &Product{
 | |
| 					Id:   "product_id",
 | |
| 					Name: "product_name",
 | |
| 				}
 | |
| 			},
 | |
| 			wantError: false,
 | |
| 		},
 | |
| 	}
 | |
| 
 | |
| 	for _, tt := range tests {
 | |
| 		t.Run(tt.name, func(t *testing.T) {
 | |
| 			rb, err := builder.NewRequestBuilder(tt.path, tt.method, tt.bodyOpt, tt.msg())
 | |
| 			require.NoError(t, err)
 | |
| 
 | |
| 			path, newMsg, err := rb.Build()
 | |
| 			if tt.wantError {
 | |
| 				require.Error(t, err)
 | |
| 				require.Empty(t, path)
 | |
| 				require.Nil(t, newMsg)
 | |
| 			} else {
 | |
| 				require.NoError(t, err)
 | |
| 				require.Equal(t, tt.expectedPath, path)
 | |
| 				require.True(t, proto.Equal(tt.expectedMsg(), newMsg))
 | |
| 			}
 | |
| 		})
 | |
| 	}
 | |
| }
 |