github.com/grpc-ecosystem/grpc-gateway/v2@v2.19.1/runtime/query_test.go (about)

     1  package runtime_test
     2  
     3  import (
     4  	"errors"
     5  	"net/url"
     6  	"strconv"
     7  	"testing"
     8  	"time"
     9  
    10  	"github.com/google/go-cmp/cmp"
    11  	"github.com/grpc-ecosystem/grpc-gateway/v2/runtime"
    12  	"github.com/grpc-ecosystem/grpc-gateway/v2/runtime/internal/examplepb"
    13  	"github.com/grpc-ecosystem/grpc-gateway/v2/utilities"
    14  	"google.golang.org/protobuf/proto"
    15  	"google.golang.org/protobuf/testing/protocmp"
    16  	"google.golang.org/protobuf/types/known/durationpb"
    17  	field_mask "google.golang.org/protobuf/types/known/fieldmaskpb"
    18  	"google.golang.org/protobuf/types/known/structpb"
    19  	"google.golang.org/protobuf/types/known/timestamppb"
    20  	"google.golang.org/protobuf/types/known/wrapperspb"
    21  )
    22  
    23  func BenchmarkPopulateQueryParameters(b *testing.B) {
    24  	timeT := time.Date(2016, time.December, 15, 12, 23, 32, 49, time.UTC)
    25  	timeStr := timeT.Format(time.RFC3339Nano)
    26  
    27  	durationT := 13 * time.Hour
    28  	durationStr := durationT.String()
    29  
    30  	fieldmaskStr := "float_value,double_value"
    31  
    32  	msg := &examplepb.Proto3Message{}
    33  	values := url.Values{
    34  		"float_value":            {"1.5"},
    35  		"double_value":           {"2.5"},
    36  		"int64_value":            {"-1"},
    37  		"int32_value":            {"-2"},
    38  		"uint64_value":           {"3"},
    39  		"uint32_value":           {"4"},
    40  		"bool_value":             {"true"},
    41  		"string_value":           {"str"},
    42  		"bytes_value":            {"Ynl0ZXM="},
    43  		"repeated_value":         {"a", "b", "c"},
    44  		"enum_value":             {"1"},
    45  		"repeated_enum":          {"1", "2", "0"},
    46  		"timestamp_value":        {timeStr},
    47  		"duration_value":         {durationStr},
    48  		"fieldmask_value":        {fieldmaskStr},
    49  		"optional_string_value":  {"optional-str"},
    50  		"wrapper_float_value":    {"1.5"},
    51  		"wrapper_double_value":   {"2.5"},
    52  		"wrapper_int64_value":    {"-1"},
    53  		"wrapper_int32_value":    {"-2"},
    54  		"wrapper_u_int64_value":  {"3"},
    55  		"wrapper_u_int32_value":  {"4"},
    56  		"wrapper_bool_value":     {"true"},
    57  		"wrapper_string_value":   {"str"},
    58  		"wrapper_bytes_value":    {"Ynl0ZXM="},
    59  		"map_value[key]":         {"value"},
    60  		"map_value[second]":      {"bar"},
    61  		"map_value[third]":       {"zzz"},
    62  		"map_value[fourth]":      {""},
    63  		`map_value[~!@#$%^&*()]`: {"value"},
    64  		"map_value2[key]":        {"-2"},
    65  		"map_value3[-2]":         {"value"},
    66  		"map_value4[key]":        {"-1"},
    67  		"map_value5[-1]":         {"value"},
    68  		"map_value6[key]":        {"3"},
    69  		"map_value7[3]":          {"value"},
    70  		"map_value8[key]":        {"4"},
    71  		"map_value9[4]":          {"value"},
    72  		"map_value10[key]":       {"1.5"},
    73  		"map_value11[1.5]":       {"value"},
    74  		"map_value12[key]":       {"2.5"},
    75  		"map_value13[2.5]":       {"value"},
    76  		"map_value14[key]":       {"true"},
    77  		"map_value15[true]":      {"value"},
    78  	}
    79  	filter := utilities.NewDoubleArray([][]string{
    80  		{"bool_value"}, {"repeated_value"},
    81  	})
    82  
    83  	for i := 0; i < b.N; i++ {
    84  		_ = runtime.PopulateQueryParameters(msg, values, filter)
    85  	}
    86  }
    87  
    88  func TestPopulateParameters(t *testing.T) {
    89  	timeT := time.Date(2016, time.December, 15, 12, 23, 32, 49, time.UTC)
    90  	timeStr := timeT.Format(time.RFC3339Nano)
    91  	timePb := timestamppb.New(timeT)
    92  
    93  	durationT := 13 * time.Hour
    94  	durationStr := durationT.String()
    95  	durationPb := durationpb.New(durationT)
    96  
    97  	fieldmaskStr := "float_value,double_value"
    98  	fieldmaskPb := &field_mask.FieldMask{Paths: []string{"float_value", "double_value"}}
    99  
   100  	structValueJsonStrings := []string{`{"a":{"b":1}}`, `""`, "{}", "[]", "true", "0"}
   101  	structValueValues := make([]*structpb.Value, len(structValueJsonStrings))
   102  	for i := range structValueValues {
   103  		structValueValues[i] = &structpb.Value{}
   104  		err := structValueValues[i].UnmarshalJSON([]byte(structValueJsonStrings[i]))
   105  		if err != nil {
   106  			t.Errorf("build struct.Value value failed: %s", err.Error())
   107  		}
   108  	}
   109  	structJsonStrings := []string{`{"a":{"b":1}}`, "{}", `{"c":[1,2],"d":[{"e":1,"f":{}}]}`}
   110  	structValues := make([]*structpb.Struct, len(structJsonStrings))
   111  	for i := range structValues {
   112  		structValues[i] = &structpb.Struct{}
   113  		err := structValues[i].UnmarshalJSON([]byte(structJsonStrings[i]))
   114  		if err != nil {
   115  			t.Errorf("build struct.Struct value failed: %s", err.Error())
   116  		}
   117  	}
   118  
   119  	for i, spec := range []struct {
   120  		values  url.Values
   121  		filter  *utilities.DoubleArray
   122  		want    proto.Message
   123  		wanterr error
   124  	}{
   125  		{
   126  			values: url.Values{
   127  				"float_value":            {"1.5"},
   128  				"double_value":           {"2.5"},
   129  				"int64_value":            {"-1"},
   130  				"int32_value":            {"-2"},
   131  				"uint64_value":           {"3"},
   132  				"uint32_value":           {"4"},
   133  				"bool_value":             {"true"},
   134  				"string_value":           {"str"},
   135  				"bytes_value":            {"YWJjMTIzIT8kKiYoKSctPUB-"},
   136  				"repeated_value":         {"a", "b", "c"},
   137  				"repeated_message":       {"1", "2", "3"},
   138  				"enum_value":             {"1"},
   139  				"repeated_enum":          {"1", "2", "0"},
   140  				"timestamp_value":        {timeStr},
   141  				"duration_value":         {durationStr},
   142  				"fieldmask_value":        {fieldmaskStr},
   143  				"wrapper_float_value":    {"1.5"},
   144  				"wrapper_double_value":   {"2.5"},
   145  				"wrapper_int64_value":    {"-1"},
   146  				"wrapper_int32_value":    {"-2"},
   147  				"wrapper_u_int64_value":  {"3"},
   148  				"wrapper_u_int32_value":  {"4"},
   149  				"wrapper_bool_value":     {"true"},
   150  				"wrapper_string_value":   {"str"},
   151  				"wrapper_bytes_value":    {"YWJjMTIzIT8kKiYoKSctPUB-"},
   152  				"map_value[key]":         {"value"},
   153  				"map_value[second]":      {"bar"},
   154  				"map_value[third]":       {"zzz"},
   155  				"map_value[fourth]":      {""},
   156  				`map_value[~!@#$%^&*()]`: {"value"},
   157  				"map_value2[key]":        {"-2"},
   158  				"map_value3[-2]":         {"value"},
   159  				"map_value4[key]":        {"-1"},
   160  				"map_value5[-1]":         {"value"},
   161  				"map_value6[key]":        {"3"},
   162  				"map_value7[3]":          {"value"},
   163  				"map_value8[key]":        {"4"},
   164  				"map_value9[4]":          {"value"},
   165  				"map_value10[key]":       {"1.5"},
   166  				"map_value11[1.5]":       {"value"},
   167  				"map_value12[key]":       {"2.5"},
   168  				"map_value13[2.5]":       {"value"},
   169  				"map_value14[key]":       {"true"},
   170  				"map_value15[true]":      {"value"},
   171  				"map_value16[key]":       {"2"},
   172  				"struct_value_value":     {structValueJsonStrings[0]},
   173  				"struct_value":           {structJsonStrings[0]},
   174  			},
   175  			filter: utilities.NewDoubleArray(nil),
   176  			want: &examplepb.Proto3Message{
   177  				FloatValue:         1.5,
   178  				DoubleValue:        2.5,
   179  				Int64Value:         -1,
   180  				Int32Value:         -2,
   181  				Uint64Value:        3,
   182  				Uint32Value:        4,
   183  				BoolValue:          true,
   184  				StringValue:        "str",
   185  				BytesValue:         []byte("abc123!?$*&()'-=@~"),
   186  				RepeatedValue:      []string{"a", "b", "c"},
   187  				RepeatedMessage:    []*wrapperspb.UInt64Value{{Value: 1}, {Value: 2}, {Value: 3}},
   188  				EnumValue:          examplepb.EnumValue_Y,
   189  				RepeatedEnum:       []examplepb.EnumValue{examplepb.EnumValue_Y, examplepb.EnumValue_Z, examplepb.EnumValue_X},
   190  				TimestampValue:     timePb,
   191  				DurationValue:      durationPb,
   192  				FieldmaskValue:     fieldmaskPb,
   193  				WrapperFloatValue:  wrapperspb.Float(1.5),
   194  				WrapperDoubleValue: wrapperspb.Double(2.5),
   195  				WrapperInt64Value:  wrapperspb.Int64(-1),
   196  				WrapperInt32Value:  wrapperspb.Int32(-2),
   197  				WrapperUInt64Value: wrapperspb.UInt64(3),
   198  				WrapperUInt32Value: wrapperspb.UInt32(4),
   199  				WrapperBoolValue:   wrapperspb.Bool(true),
   200  				WrapperStringValue: wrapperspb.String("str"),
   201  				WrapperBytesValue:  wrapperspb.Bytes([]byte("abc123!?$*&()'-=@~")),
   202  				MapValue: map[string]string{
   203  					"key":         "value",
   204  					"second":      "bar",
   205  					"third":       "zzz",
   206  					"fourth":      "",
   207  					`~!@#$%^&*()`: "value",
   208  				},
   209  				MapValue2:        map[string]int32{"key": -2},
   210  				MapValue3:        map[int32]string{-2: "value"},
   211  				MapValue4:        map[string]int64{"key": -1},
   212  				MapValue5:        map[int64]string{-1: "value"},
   213  				MapValue6:        map[string]uint32{"key": 3},
   214  				MapValue7:        map[uint32]string{3: "value"},
   215  				MapValue8:        map[string]uint64{"key": 4},
   216  				MapValue9:        map[uint64]string{4: "value"},
   217  				MapValue10:       map[string]float32{"key": 1.5},
   218  				MapValue12:       map[string]float64{"key": 2.5},
   219  				MapValue14:       map[string]bool{"key": true},
   220  				MapValue15:       map[bool]string{true: "value"},
   221  				MapValue16:       map[string]*wrapperspb.UInt64Value{"key": {Value: 2}},
   222  				StructValueValue: structValueValues[0],
   223  				StructValue:      structValues[0],
   224  			},
   225  		},
   226  		{
   227  			values: url.Values{
   228  				"floatValue":         {"1.5"},
   229  				"doubleValue":        {"2.5"},
   230  				"int64Value":         {"-1"},
   231  				"int32Value":         {"-2"},
   232  				"uint64Value":        {"3"},
   233  				"uint32Value":        {"4"},
   234  				"boolValue":          {"true"},
   235  				"stringValue":        {"str"},
   236  				"bytesValue":         {"Ynl0ZXM="},
   237  				"repeatedValue":      {"a", "b", "c"},
   238  				"enumValue":          {"1"},
   239  				"repeatedEnum":       {"1", "2", "0"},
   240  				"timestampValue":     {timeStr},
   241  				"durationValue":      {durationStr},
   242  				"fieldmaskValue":     {fieldmaskStr},
   243  				"wrapperFloatValue":  {"1.5"},
   244  				"wrapperDoubleValue": {"2.5"},
   245  				"wrapperInt64Value":  {"-1"},
   246  				"wrapperInt32Value":  {"-2"},
   247  				"wrapperUInt64Value": {"3"},
   248  				"wrapperUInt32Value": {"4"},
   249  				"wrapperBoolValue":   {"true"},
   250  				"wrapperStringValue": {"str"},
   251  				"wrapperBytesValue":  {"Ynl0ZXM="},
   252  				"struct_value_value": {structValueJsonStrings[1]},
   253  				"struct_value":       {structJsonStrings[1]},
   254  			},
   255  			filter: utilities.NewDoubleArray(nil),
   256  			want: &examplepb.Proto3Message{
   257  				FloatValue:         1.5,
   258  				DoubleValue:        2.5,
   259  				Int64Value:         -1,
   260  				Int32Value:         -2,
   261  				Uint64Value:        3,
   262  				Uint32Value:        4,
   263  				BoolValue:          true,
   264  				StringValue:        "str",
   265  				BytesValue:         []byte("bytes"),
   266  				RepeatedValue:      []string{"a", "b", "c"},
   267  				EnumValue:          examplepb.EnumValue_Y,
   268  				RepeatedEnum:       []examplepb.EnumValue{examplepb.EnumValue_Y, examplepb.EnumValue_Z, examplepb.EnumValue_X},
   269  				TimestampValue:     timePb,
   270  				DurationValue:      durationPb,
   271  				FieldmaskValue:     fieldmaskPb,
   272  				WrapperFloatValue:  wrapperspb.Float(1.5),
   273  				WrapperDoubleValue: wrapperspb.Double(2.5),
   274  				WrapperInt64Value:  wrapperspb.Int64(-1),
   275  				WrapperInt32Value:  wrapperspb.Int32(-2),
   276  				WrapperUInt64Value: wrapperspb.UInt64(3),
   277  				WrapperUInt32Value: wrapperspb.UInt32(4),
   278  				WrapperBoolValue:   wrapperspb.Bool(true),
   279  				WrapperStringValue: wrapperspb.String("str"),
   280  				WrapperBytesValue:  wrapperspb.Bytes([]byte("bytes")),
   281  				StructValueValue:   structValueValues[1],
   282  				StructValue:        structValues[1],
   283  			},
   284  		},
   285  		{
   286  			values: url.Values{
   287  				"enum_value":         {"Z"},
   288  				"repeated_enum":      {"X", "2", "0"},
   289  				"struct_value_value": {structValueJsonStrings[2]},
   290  				"struct_value":       {structJsonStrings[2]},
   291  			},
   292  			filter: utilities.NewDoubleArray(nil),
   293  			want: &examplepb.Proto3Message{
   294  				EnumValue:        examplepb.EnumValue_Z,
   295  				RepeatedEnum:     []examplepb.EnumValue{examplepb.EnumValue_X, examplepb.EnumValue_Z, examplepb.EnumValue_X},
   296  				StructValueValue: structValueValues[2],
   297  				StructValue:      structValues[2],
   298  			},
   299  		},
   300  		{
   301  			values: url.Values{
   302  				"struct_value_value": {structValueJsonStrings[3]},
   303  			},
   304  			filter: utilities.NewDoubleArray(nil),
   305  			want: &examplepb.Proto3Message{
   306  				StructValueValue: structValueValues[3],
   307  			},
   308  		},
   309  		{
   310  			values: url.Values{
   311  				"struct_value_value": {structValueJsonStrings[4]},
   312  			},
   313  			filter: utilities.NewDoubleArray(nil),
   314  			want: &examplepb.Proto3Message{
   315  				StructValueValue: structValueValues[4],
   316  			},
   317  		},
   318  		{
   319  			values: url.Values{
   320  				"struct_value_value": {structValueJsonStrings[5]},
   321  			},
   322  			filter: utilities.NewDoubleArray(nil),
   323  			want: &examplepb.Proto3Message{
   324  				StructValueValue: structValueValues[5],
   325  			},
   326  		},
   327  		{
   328  			values: url.Values{
   329  				"float_value":    {"1.5"},
   330  				"double_value":   {"2.5"},
   331  				"int64_value":    {"-1"},
   332  				"int32_value":    {"-2"},
   333  				"uint64_value":   {"3"},
   334  				"uint32_value":   {"4"},
   335  				"bool_value":     {"true"},
   336  				"string_value":   {"str"},
   337  				"repeated_value": {"a", "b", "c"},
   338  			},
   339  			filter: utilities.NewDoubleArray(nil),
   340  			want: &examplepb.Proto2Message{
   341  				FloatValue:    proto.Float32(1.5),
   342  				DoubleValue:   proto.Float64(2.5),
   343  				Int64Value:    proto.Int64(-1),
   344  				Int32Value:    proto.Int32(-2),
   345  				Uint64Value:   proto.Uint64(3),
   346  				Uint32Value:   proto.Uint32(4),
   347  				BoolValue:     proto.Bool(true),
   348  				StringValue:   proto.String("str"),
   349  				RepeatedValue: []string{"a", "b", "c"},
   350  			},
   351  		},
   352  		{
   353  			values: url.Values{
   354  				"floatValue":    {"1.5"},
   355  				"doubleValue":   {"2.5"},
   356  				"int64Value":    {"-1"},
   357  				"int32Value":    {"-2"},
   358  				"uint64Value":   {"3"},
   359  				"uint32Value":   {"4"},
   360  				"boolValue":     {"true"},
   361  				"stringValue":   {"str"},
   362  				"repeatedValue": {"a", "b", "c"},
   363  			},
   364  			filter: utilities.NewDoubleArray(nil),
   365  			want: &examplepb.Proto2Message{
   366  				FloatValue:    proto.Float32(1.5),
   367  				DoubleValue:   proto.Float64(2.5),
   368  				Int64Value:    proto.Int64(-1),
   369  				Int32Value:    proto.Int32(-2),
   370  				Uint64Value:   proto.Uint64(3),
   371  				Uint32Value:   proto.Uint32(4),
   372  				BoolValue:     proto.Bool(true),
   373  				StringValue:   proto.String("str"),
   374  				RepeatedValue: []string{"a", "b", "c"},
   375  			},
   376  		},
   377  		{
   378  			values: url.Values{
   379  				"nested.nested.nested.repeated_value": {"a", "b", "c"},
   380  				"nested.nested.nested.string_value":   {"s"},
   381  				"nested.nested.string_value":          {"t"},
   382  				"nested.string_value":                 {"u"},
   383  				"nested.nested.map_value[first]":      {"foo"},
   384  				"nested.nested.map_value[second]":     {"bar"},
   385  			},
   386  			filter: utilities.NewDoubleArray(nil),
   387  			want: &examplepb.Proto3Message{
   388  				Nested: &examplepb.Proto3Message{
   389  					Nested: &examplepb.Proto3Message{
   390  						MapValue: map[string]string{
   391  							"first":  "foo",
   392  							"second": "bar",
   393  						},
   394  						Nested: &examplepb.Proto3Message{
   395  							RepeatedValue: []string{"a", "b", "c"},
   396  							StringValue:   "s",
   397  						},
   398  						StringValue: "t",
   399  					},
   400  					StringValue: "u",
   401  				},
   402  			},
   403  		},
   404  		{
   405  			values: url.Values{
   406  				"oneof_string_value": {"foobar"},
   407  			},
   408  			filter: utilities.NewDoubleArray(nil),
   409  			want: &examplepb.Proto3Message{
   410  				OneofValue: &examplepb.Proto3Message_OneofStringValue{
   411  					OneofStringValue: "foobar",
   412  				},
   413  			},
   414  		},
   415  		{
   416  			values: url.Values{
   417  				"oneofStringValue": {"foobar"},
   418  			},
   419  			filter: utilities.NewDoubleArray(nil),
   420  			want: &examplepb.Proto3Message{
   421  				OneofValue: &examplepb.Proto3Message_OneofStringValue{
   422  					OneofStringValue: "foobar",
   423  				},
   424  			},
   425  		},
   426  		{
   427  			values: url.Values{
   428  				"oneof_bool_value": {"true"},
   429  			},
   430  			filter: utilities.NewDoubleArray(nil),
   431  			want: &examplepb.Proto3Message{
   432  				OneofValue: &examplepb.Proto3Message_OneofBoolValue{
   433  					OneofBoolValue: true,
   434  				},
   435  			},
   436  		},
   437  		{
   438  			values: url.Values{
   439  				"nested_oneof_value_one.int64Value":   {"-1"},
   440  				"nested_oneof_value_one.string_value": {"foo"},
   441  			},
   442  			filter: utilities.NewDoubleArray(nil),
   443  			want: &examplepb.Proto3Message{
   444  				NestedOneofValue: &examplepb.Proto3Message_NestedOneofValueOne{
   445  					NestedOneofValueOne: &examplepb.Proto3Message{
   446  						Int64Value:  -1,
   447  						StringValue: "foo",
   448  					},
   449  				},
   450  			},
   451  		},
   452  		{
   453  			// Error on "null"
   454  			values: url.Values{
   455  				"timestampValue": {"null"},
   456  			},
   457  			filter:  utilities.NewDoubleArray(nil),
   458  			want:    &examplepb.Proto3Message{},
   459  			wanterr: errors.New(`parsing field "timestamp_value": parsing time "null" as "2006-01-02T15:04:05.999999999Z07:00": cannot parse "null" as "2006"`),
   460  		},
   461  		{
   462  			// Error on "null"
   463  			values: url.Values{
   464  				"durationValue": {"null"},
   465  			},
   466  			filter:  utilities.NewDoubleArray(nil),
   467  			want:    &examplepb.Proto3Message{},
   468  			wanterr: errors.New(`parsing field "duration_value": time: invalid duration "null"`),
   469  		},
   470  		{
   471  			// Don't allow setting a oneof more than once
   472  			values: url.Values{
   473  				"oneof_bool_value":   {"true"},
   474  				"oneof_string_value": {"foobar"},
   475  			},
   476  			filter:  utilities.NewDoubleArray(nil),
   477  			want:    &examplepb.Proto3Message{},
   478  			wanterr: errors.New("field already set for oneof \"oneof_value\""),
   479  		},
   480  		{
   481  			// Error when there are too many values
   482  			values: url.Values{
   483  				"uint64_value": {"1", "2"},
   484  			},
   485  			filter:  utilities.NewDoubleArray(nil),
   486  			want:    &examplepb.Proto3Message{},
   487  			wanterr: errors.New("too many values for field \"uint64_value\": 1, 2"),
   488  		},
   489  		{
   490  			// Error when dereferencing a list of messages
   491  			values: url.Values{
   492  				"repeated_message.value": {"1"},
   493  			},
   494  			filter:  utilities.NewDoubleArray(nil),
   495  			want:    &examplepb.Proto3Message{},
   496  			wanterr: errors.New("invalid path: \"repeated_message\" is not a message"),
   497  		},
   498  	} {
   499  		t.Run(strconv.Itoa(i), func(t *testing.T) {
   500  			msg := spec.want.ProtoReflect().New().Interface()
   501  			err := runtime.PopulateQueryParameters(msg, spec.values, spec.filter)
   502  			if spec.wanterr != nil {
   503  				if err == nil || err.Error() != spec.wanterr.Error() {
   504  					t.Errorf("runtime.PopulateQueryParameters(msg, %v, %v) failed with %q; want error %q", spec.values, spec.filter, err, spec.wanterr)
   505  				}
   506  				return
   507  			}
   508  
   509  			if err != nil {
   510  				t.Errorf("runtime.PopulateQueryParameters(msg, %v, %v) failed with %v; want success", spec.values, spec.filter, err)
   511  				return
   512  			}
   513  			if diff := cmp.Diff(spec.want, msg, protocmp.Transform()); diff != "" {
   514  				t.Errorf("runtime.PopulateQueryParameters(msg, %v, %v): %s", spec.values, spec.filter, diff)
   515  			}
   516  		})
   517  	}
   518  }
   519  
   520  func TestPopulateParametersWithFilters(t *testing.T) {
   521  	for _, spec := range []struct {
   522  		values url.Values
   523  		filter *utilities.DoubleArray
   524  		want   proto.Message
   525  	}{
   526  		{
   527  			values: url.Values{
   528  				"bool_value":     {"true"},
   529  				"string_value":   {"str"},
   530  				"repeated_value": {"a", "b", "c"},
   531  			},
   532  			filter: utilities.NewDoubleArray([][]string{
   533  				{"bool_value"}, {"repeated_value"},
   534  			}),
   535  			want: &examplepb.Proto3Message{
   536  				StringValue: "str",
   537  			},
   538  		},
   539  		{
   540  			values: url.Values{
   541  				"nested.nested.bool_value":   {"true"},
   542  				"nested.nested.string_value": {"str"},
   543  				"nested.string_value":        {"str"},
   544  				"string_value":               {"str"},
   545  			},
   546  			filter: utilities.NewDoubleArray([][]string{
   547  				{"nested"},
   548  			}),
   549  			want: &examplepb.Proto3Message{
   550  				StringValue: "str",
   551  			},
   552  		},
   553  		{
   554  			values: url.Values{
   555  				"nested.nested.bool_value":   {"true"},
   556  				"nested.nested.string_value": {"str"},
   557  				"nested.string_value":        {"str"},
   558  				"string_value":               {"str"},
   559  			},
   560  			filter: utilities.NewDoubleArray([][]string{
   561  				{"nested", "nested"},
   562  			}),
   563  			want: &examplepb.Proto3Message{
   564  				Nested: &examplepb.Proto3Message{
   565  					StringValue: "str",
   566  				},
   567  				StringValue: "str",
   568  			},
   569  		},
   570  		{
   571  			values: url.Values{
   572  				"nested.nested.bool_value":   {"true"},
   573  				"nested.nested.string_value": {"str"},
   574  				"nested.string_value":        {"str"},
   575  				"string_value":               {"str"},
   576  			},
   577  			filter: utilities.NewDoubleArray([][]string{
   578  				{"nested", "nested", "string_value"},
   579  			}),
   580  			want: &examplepb.Proto3Message{
   581  				Nested: &examplepb.Proto3Message{
   582  					StringValue: "str",
   583  					Nested: &examplepb.Proto3Message{
   584  						BoolValue: true,
   585  					},
   586  				},
   587  				StringValue: "str",
   588  			},
   589  		},
   590  	} {
   591  		msg := spec.want.ProtoReflect().New().Interface()
   592  		err := runtime.PopulateQueryParameters(msg, spec.values, spec.filter)
   593  		if err != nil {
   594  			t.Errorf("runtime.PoplateQueryParameters(msg, %v, %v) failed with %v; want success", spec.values, spec.filter, err)
   595  			continue
   596  		}
   597  		if got, want := msg, spec.want; !proto.Equal(got, want) {
   598  			t.Errorf("runtime.PopulateQueryParameters(msg, %v, %v = %v; want %v", spec.values, spec.filter, got, want)
   599  		}
   600  	}
   601  }
   602  
   603  func TestPopulateQueryParametersWithInvalidNestedParameters(t *testing.T) {
   604  	for _, spec := range []struct {
   605  		msg    proto.Message
   606  		values url.Values
   607  		filter *utilities.DoubleArray
   608  	}{
   609  		{
   610  			msg: &examplepb.Proto3Message{},
   611  			values: url.Values{
   612  				"float_value.nested": {"test"},
   613  			},
   614  			filter: utilities.NewDoubleArray(nil),
   615  		},
   616  		{
   617  			msg: &examplepb.Proto3Message{},
   618  			values: url.Values{
   619  				"double_value.nested": {"test"},
   620  			},
   621  			filter: utilities.NewDoubleArray(nil),
   622  		},
   623  		{
   624  			msg: &examplepb.Proto3Message{},
   625  			values: url.Values{
   626  				"int64_value.nested": {"test"},
   627  			},
   628  			filter: utilities.NewDoubleArray(nil),
   629  		},
   630  		{
   631  			msg: &examplepb.Proto3Message{},
   632  			values: url.Values{
   633  				"int32_value.nested": {"test"},
   634  			},
   635  			filter: utilities.NewDoubleArray(nil),
   636  		},
   637  		{
   638  			msg: &examplepb.Proto3Message{},
   639  			values: url.Values{
   640  				"uint64_value.nested": {"test"},
   641  			},
   642  			filter: utilities.NewDoubleArray(nil),
   643  		},
   644  		{
   645  			msg: &examplepb.Proto3Message{},
   646  			values: url.Values{
   647  				"uint32_value.nested": {"test"},
   648  			},
   649  			filter: utilities.NewDoubleArray(nil),
   650  		},
   651  		{
   652  			msg: &examplepb.Proto3Message{},
   653  			values: url.Values{
   654  				"bool_value.nested": {"test"},
   655  			},
   656  			filter: utilities.NewDoubleArray(nil),
   657  		},
   658  		{
   659  			msg: &examplepb.Proto3Message{},
   660  			values: url.Values{
   661  				"string_value.nested": {"test"},
   662  			},
   663  			filter: utilities.NewDoubleArray(nil),
   664  		},
   665  		{
   666  			msg: &examplepb.Proto3Message{},
   667  			values: url.Values{
   668  				"repeated_value.nested": {"test"},
   669  			},
   670  			filter: utilities.NewDoubleArray(nil),
   671  		},
   672  		{
   673  			msg: &examplepb.Proto3Message{},
   674  			values: url.Values{
   675  				"enum_value.nested": {"test"},
   676  			},
   677  			filter: utilities.NewDoubleArray(nil),
   678  		},
   679  		{
   680  			msg: &examplepb.Proto3Message{},
   681  			values: url.Values{
   682  				"enum_value.nested": {"test"},
   683  			},
   684  			filter: utilities.NewDoubleArray(nil),
   685  		},
   686  		{
   687  			msg: &examplepb.Proto3Message{},
   688  			values: url.Values{
   689  				"repeated_enum.nested": {"test"},
   690  			},
   691  			filter: utilities.NewDoubleArray(nil),
   692  		},
   693  	} {
   694  		spec.msg = spec.msg.ProtoReflect().New().Interface()
   695  		err := runtime.PopulateQueryParameters(spec.msg, spec.values, spec.filter)
   696  		if err == nil {
   697  			t.Errorf("runtime.PopulateQueryParameters(msg, %v, %v) did not fail; want error", spec.values, spec.filter)
   698  		}
   699  	}
   700  }