1// Copyright 2021 Google LLC 2// 3// Licensed under the Apache License, Version 2.0 (the "License"); 4// you may not use this file except in compliance with the License. 5// You may obtain a copy of the License at 6// 7// https://www.apache.org/licenses/LICENSE-2.0 8// 9// Unless required by applicable law or agreed to in writing, software 10// distributed under the License is distributed on an "AS IS" BASIS, 11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12// See the License for the specific language governing permissions and 13// limitations under the License. 14 15package adapt 16 17import ( 18 "encoding/json" 19 "reflect" 20 "testing" 21 22 "cloud.google.com/go/bigquery/storage/managedwriter/testdata" 23 "github.com/google/go-cmp/cmp" 24 storagepb "google.golang.org/genproto/googleapis/cloud/bigquery/storage/v1" 25 "google.golang.org/protobuf/encoding/protojson" 26 "google.golang.org/protobuf/proto" 27 "google.golang.org/protobuf/reflect/protodesc" 28 "google.golang.org/protobuf/reflect/protoreflect" 29 "google.golang.org/protobuf/testing/protocmp" 30 "google.golang.org/protobuf/types/descriptorpb" 31 "google.golang.org/protobuf/types/dynamicpb" 32) 33 34func TestSchemaToProtoConversion(t *testing.T) { 35 testCases := []struct { 36 description string 37 bq *storagepb.TableSchema 38 wantProto2 *descriptorpb.DescriptorProto 39 wantProto3 *descriptorpb.DescriptorProto 40 }{ 41 { 42 description: "basic", 43 bq: &storagepb.TableSchema{ 44 Fields: []*storagepb.TableFieldSchema{ 45 {Name: "foo", Type: storagepb.TableFieldSchema_STRING, Mode: storagepb.TableFieldSchema_NULLABLE}, 46 {Name: "bar", Type: storagepb.TableFieldSchema_INT64, Mode: storagepb.TableFieldSchema_REQUIRED}, 47 {Name: "baz", Type: storagepb.TableFieldSchema_BYTES, Mode: storagepb.TableFieldSchema_REPEATED}, 48 }}, 49 wantProto2: &descriptorpb.DescriptorProto{ 50 Name: proto.String("root"), 51 Field: []*descriptorpb.FieldDescriptorProto{ 52 { 53 Name: proto.String("foo"), 54 Number: proto.Int32(1), 55 Type: descriptorpb.FieldDescriptorProto_TYPE_STRING.Enum(), 56 Label: descriptorpb.FieldDescriptorProto_LABEL_OPTIONAL.Enum()}, 57 {Name: proto.String("bar"), Number: proto.Int32(2), Type: descriptorpb.FieldDescriptorProto_TYPE_INT64.Enum(), Label: descriptorpb.FieldDescriptorProto_LABEL_REQUIRED.Enum()}, 58 {Name: proto.String("baz"), Number: proto.Int32(3), Type: descriptorpb.FieldDescriptorProto_TYPE_BYTES.Enum(), Label: descriptorpb.FieldDescriptorProto_LABEL_REPEATED.Enum()}, 59 }, 60 }, 61 wantProto3: &descriptorpb.DescriptorProto{ 62 Name: proto.String("root"), 63 Field: []*descriptorpb.FieldDescriptorProto{ 64 { 65 Name: proto.String("foo"), 66 Number: proto.Int32(1), 67 Type: descriptorpb.FieldDescriptorProto_TYPE_MESSAGE.Enum(), 68 TypeName: proto.String(".google.protobuf.StringValue"), 69 Label: descriptorpb.FieldDescriptorProto_LABEL_OPTIONAL.Enum()}, 70 {Name: proto.String("bar"), Number: proto.Int32(2), Type: descriptorpb.FieldDescriptorProto_TYPE_INT64.Enum(), Label: descriptorpb.FieldDescriptorProto_LABEL_OPTIONAL.Enum()}, 71 {Name: proto.String("baz"), Number: proto.Int32(3), Type: descriptorpb.FieldDescriptorProto_TYPE_BYTES.Enum(), Label: descriptorpb.FieldDescriptorProto_LABEL_REPEATED.Enum()}, 72 }, 73 }, 74 }, 75 { 76 // exercise construct of a submessage 77 description: "nested", 78 bq: &storagepb.TableSchema{ 79 Fields: []*storagepb.TableFieldSchema{ 80 {Name: "curdate", Type: storagepb.TableFieldSchema_DATE, Mode: storagepb.TableFieldSchema_NULLABLE}, 81 { 82 Name: "rec", 83 Type: storagepb.TableFieldSchema_STRUCT, 84 Mode: storagepb.TableFieldSchema_NULLABLE, 85 Fields: []*storagepb.TableFieldSchema{ 86 {Name: "userid", Type: storagepb.TableFieldSchema_STRING, Mode: storagepb.TableFieldSchema_REQUIRED}, 87 {Name: "location", Type: storagepb.TableFieldSchema_GEOGRAPHY, Mode: storagepb.TableFieldSchema_NULLABLE}, 88 }, 89 }, 90 }, 91 }, 92 wantProto2: &descriptorpb.DescriptorProto{ 93 Name: proto.String("root"), 94 Field: []*descriptorpb.FieldDescriptorProto{ 95 { 96 Name: proto.String("curdate"), 97 Number: proto.Int32(1), 98 Type: descriptorpb.FieldDescriptorProto_TYPE_INT32.Enum(), 99 Label: descriptorpb.FieldDescriptorProto_LABEL_OPTIONAL.Enum(), 100 }, 101 { 102 Name: proto.String("rec"), 103 Number: proto.Int32(2), 104 Type: descriptorpb.FieldDescriptorProto_TYPE_MESSAGE.Enum(), 105 TypeName: proto.String(".root__rec"), 106 Label: descriptorpb.FieldDescriptorProto_LABEL_OPTIONAL.Enum(), 107 }, 108 }, 109 }, 110 wantProto3: &descriptorpb.DescriptorProto{ 111 Name: proto.String("root"), 112 Field: []*descriptorpb.FieldDescriptorProto{ 113 { 114 Name: proto.String("curdate"), 115 Number: proto.Int32(1), 116 Type: descriptorpb.FieldDescriptorProto_TYPE_MESSAGE.Enum(), 117 TypeName: proto.String(".google.protobuf.Int32Value"), 118 Label: descriptorpb.FieldDescriptorProto_LABEL_OPTIONAL.Enum(), 119 }, 120 { 121 Name: proto.String("rec"), 122 Number: proto.Int32(2), 123 Type: descriptorpb.FieldDescriptorProto_TYPE_MESSAGE.Enum(), 124 TypeName: proto.String(".root__rec"), 125 Label: descriptorpb.FieldDescriptorProto_LABEL_OPTIONAL.Enum(), 126 }, 127 }, 128 }, 129 }, 130 { 131 // We expect to re-use the submessage twice, as the schema contains two identical structs. 132 description: "nested w/duplicate submessage", 133 bq: &storagepb.TableSchema{ 134 Fields: []*storagepb.TableFieldSchema{ 135 {Name: "curdate", Type: storagepb.TableFieldSchema_DATE, Mode: storagepb.TableFieldSchema_NULLABLE}, 136 { 137 Name: "rec1", 138 Type: storagepb.TableFieldSchema_STRUCT, 139 Mode: storagepb.TableFieldSchema_NULLABLE, 140 Fields: []*storagepb.TableFieldSchema{ 141 {Name: "userid", Type: storagepb.TableFieldSchema_STRING, Mode: storagepb.TableFieldSchema_REQUIRED}, 142 {Name: "location", Type: storagepb.TableFieldSchema_GEOGRAPHY, Mode: storagepb.TableFieldSchema_NULLABLE}, 143 }, 144 }, 145 { 146 Name: "rec2", 147 Type: storagepb.TableFieldSchema_STRUCT, 148 Mode: storagepb.TableFieldSchema_NULLABLE, 149 Fields: []*storagepb.TableFieldSchema{ 150 {Name: "userid", Type: storagepb.TableFieldSchema_STRING, Mode: storagepb.TableFieldSchema_REQUIRED}, 151 {Name: "location", Type: storagepb.TableFieldSchema_GEOGRAPHY, Mode: storagepb.TableFieldSchema_NULLABLE}, 152 }, 153 }, 154 }, 155 }, 156 wantProto2: &descriptorpb.DescriptorProto{ 157 Name: proto.String("root"), 158 Field: []*descriptorpb.FieldDescriptorProto{ 159 { 160 Name: proto.String("curdate"), 161 Number: proto.Int32(1), 162 Type: descriptorpb.FieldDescriptorProto_TYPE_INT32.Enum(), 163 Label: descriptorpb.FieldDescriptorProto_LABEL_OPTIONAL.Enum(), 164 }, 165 { 166 Name: proto.String("rec1"), 167 Number: proto.Int32(2), 168 Type: descriptorpb.FieldDescriptorProto_TYPE_MESSAGE.Enum(), 169 TypeName: proto.String(".root__rec1"), 170 Label: descriptorpb.FieldDescriptorProto_LABEL_OPTIONAL.Enum(), 171 }, 172 { 173 Name: proto.String("rec2"), 174 Number: proto.Int32(3), 175 Type: descriptorpb.FieldDescriptorProto_TYPE_MESSAGE.Enum(), 176 TypeName: proto.String(".root__rec1"), 177 Label: descriptorpb.FieldDescriptorProto_LABEL_OPTIONAL.Enum(), 178 }, 179 }, 180 }, 181 wantProto3: &descriptorpb.DescriptorProto{ 182 Name: proto.String("root"), 183 Field: []*descriptorpb.FieldDescriptorProto{ 184 { 185 Name: proto.String("curdate"), 186 Number: proto.Int32(1), 187 Type: descriptorpb.FieldDescriptorProto_TYPE_MESSAGE.Enum(), 188 TypeName: proto.String(".google.protobuf.Int32Value"), 189 Label: descriptorpb.FieldDescriptorProto_LABEL_OPTIONAL.Enum(), 190 }, 191 { 192 Name: proto.String("rec1"), 193 Number: proto.Int32(2), 194 Type: descriptorpb.FieldDescriptorProto_TYPE_MESSAGE.Enum(), 195 TypeName: proto.String(".root__rec1"), 196 Label: descriptorpb.FieldDescriptorProto_LABEL_OPTIONAL.Enum(), 197 }, 198 { 199 Name: proto.String("rec2"), 200 Number: proto.Int32(3), 201 Type: descriptorpb.FieldDescriptorProto_TYPE_MESSAGE.Enum(), 202 TypeName: proto.String(".root__rec1"), 203 Label: descriptorpb.FieldDescriptorProto_LABEL_OPTIONAL.Enum(), 204 }, 205 }, 206 }, 207 }, 208 } 209 for _, tc := range testCases { 210 p2d, err := StorageSchemaToProto2Descriptor(tc.bq, "root") 211 if err != nil { 212 t.Errorf("case (%s) failed proto2 conversion: %v", tc.description, err) 213 } 214 215 // convert it to DP form 216 mDesc, ok := p2d.(protoreflect.MessageDescriptor) 217 if !ok { 218 t.Errorf("%s: couldn't convert proto2 to messagedescriptor", tc.description) 219 } 220 gotDP := protodesc.ToDescriptorProto(mDesc) 221 if diff := cmp.Diff(gotDP, tc.wantProto2, protocmp.Transform()); diff != "" { 222 t.Errorf("%s proto2: -got, +want:\n%s", tc.description, diff) 223 } 224 225 p3d, err := StorageSchemaToProto3Descriptor(tc.bq, "root") 226 if err != nil { 227 t.Fatalf("case (%s) failed proto3 conversion: %v", tc.description, err) 228 } 229 230 mDesc, ok = p3d.(protoreflect.MessageDescriptor) 231 if !ok { 232 t.Errorf("%s: couldn't convert proto3 to messagedescriptor", tc.description) 233 } 234 gotDP = protodesc.ToDescriptorProto(mDesc) 235 if diff := cmp.Diff(gotDP, tc.wantProto3, protocmp.Transform()); diff != "" { 236 t.Errorf("%s proto3: -got, +want:\n%s", tc.description, diff) 237 } 238 239 } 240} 241 242func TestProtoJSONSerialization(t *testing.T) { 243 244 sourceSchema := &storagepb.TableSchema{ 245 Fields: []*storagepb.TableFieldSchema{ 246 {Name: "record_id", Type: storagepb.TableFieldSchema_INT64, Mode: storagepb.TableFieldSchema_NULLABLE}, 247 { 248 Name: "details", 249 Type: storagepb.TableFieldSchema_STRUCT, 250 Mode: storagepb.TableFieldSchema_REPEATED, 251 Fields: []*storagepb.TableFieldSchema{ 252 {Name: "key", Type: storagepb.TableFieldSchema_STRING, Mode: storagepb.TableFieldSchema_REQUIRED}, 253 {Name: "value", Type: storagepb.TableFieldSchema_STRING, Mode: storagepb.TableFieldSchema_NULLABLE}, 254 }, 255 }, 256 }, 257 } 258 259 descriptor, err := StorageSchemaToProto2Descriptor(sourceSchema, "root") 260 if err != nil { 261 t.Fatalf("failed to construct descriptor") 262 } 263 264 sampleRecord := []byte(`{"record_id":"12345","details":[{"key":"name","value":"jimmy"},{"key":"title","value":"clown"}]}`) 265 266 messageDescriptor, ok := descriptor.(protoreflect.MessageDescriptor) 267 if !ok { 268 t.Fatalf("StorageSchemaToDescriptor didn't yield a valid message descriptor, got %T", descriptor) 269 } 270 271 // First, ensure we got the expected descriptors. Check both outer and inner messages. 272 gotOuterDP := protodesc.ToDescriptorProto(messageDescriptor) 273 274 innerField := messageDescriptor.Fields().ByName("details") 275 if innerField == nil { 276 t.Fatalf("couldn't get inner descriptor for details") 277 } 278 gotInnerDP := protodesc.ToDescriptorProto(innerField.Message()) 279 280 wantOuterDP := &descriptorpb.DescriptorProto{ 281 Name: proto.String("root"), 282 Field: []*descriptorpb.FieldDescriptorProto{ 283 { 284 Name: proto.String("record_id"), 285 Number: proto.Int32(1), 286 Type: descriptorpb.FieldDescriptorProto_TYPE_INT64.Enum(), 287 Label: descriptorpb.FieldDescriptorProto_LABEL_OPTIONAL.Enum(), 288 }, 289 { 290 Name: proto.String("details"), 291 Number: proto.Int32(2), 292 Type: descriptorpb.FieldDescriptorProto_TYPE_MESSAGE.Enum(), 293 TypeName: proto.String(".root__details"), 294 Label: descriptorpb.FieldDescriptorProto_LABEL_REPEATED.Enum(), 295 }, 296 }, 297 } 298 299 wantInnerDP := &descriptorpb.DescriptorProto{ 300 Name: proto.String("root__details"), 301 Field: []*descriptorpb.FieldDescriptorProto{ 302 { 303 Name: proto.String("key"), 304 Number: proto.Int32(1), 305 Type: descriptorpb.FieldDescriptorProto_TYPE_STRING.Enum(), 306 Label: descriptorpb.FieldDescriptorProto_LABEL_REQUIRED.Enum(), 307 }, 308 { 309 Name: proto.String("value"), 310 Number: proto.Int32(2), 311 Type: descriptorpb.FieldDescriptorProto_TYPE_STRING.Enum(), 312 Label: descriptorpb.FieldDescriptorProto_LABEL_OPTIONAL.Enum(), 313 }, 314 }, 315 } 316 317 if outerDiff := cmp.Diff(gotOuterDP, wantOuterDP, protocmp.Transform()); outerDiff != "" { 318 t.Fatalf("DescriptorProto for outer message differs.\n-got, +want:\n%s", outerDiff) 319 } 320 if innerDiff := cmp.Diff(gotInnerDP, wantInnerDP, protocmp.Transform()); innerDiff != "" { 321 t.Fatalf("DescriptorProto for inner message differs.\n-got, +want:\n%s", innerDiff) 322 } 323 324 message := dynamicpb.NewMessage(messageDescriptor) 325 326 // Attempt to serialize json record into proto message. 327 err = protojson.Unmarshal(sampleRecord, message) 328 if err != nil { 329 t.Fatalf("failed to Unmarshal json message: %v", err) 330 } 331 332 // Serialize message back to json bytes. We must use options for idempotency, otherwise 333 // we'll serialize using the Go name rather than the proto name (recordId vs record_id). 334 options := protojson.MarshalOptions{ 335 UseProtoNames: true, 336 } 337 gotBytes, err := options.Marshal(message) 338 if err != nil { 339 t.Fatalf("failed to marshal message: %v", err) 340 } 341 342 var got, want interface{} 343 if err := json.Unmarshal(gotBytes, &got); err != nil { 344 t.Fatalf("couldn't marshal gotBytes: %v", err) 345 } 346 if err := json.Unmarshal(sampleRecord, &want); err != nil { 347 t.Fatalf("couldn't marshal sampleRecord: %v", err) 348 } 349 if !reflect.DeepEqual(got, want) { 350 t.Fatalf("mismatched json: got\n%q\nwant\n%q", gotBytes, sampleRecord) 351 } 352 353} 354 355func TestNormalizeDescriptor(t *testing.T) { 356 testCases := []struct { 357 description string 358 in protoreflect.MessageDescriptor 359 wantErr bool 360 want *descriptorpb.DescriptorProto 361 }{ 362 { 363 description: "nil", 364 in: nil, 365 wantErr: true, 366 }, 367 { 368 description: "AllSupportedTypes", 369 in: (&testdata.AllSupportedTypes{}).ProtoReflect().Descriptor(), 370 want: &descriptorpb.DescriptorProto{ 371 Name: proto.String("testdata_AllSupportedTypes"), 372 Field: []*descriptorpb.FieldDescriptorProto{ 373 { 374 Name: proto.String("int32_value"), 375 JsonName: proto.String("int32Value"), 376 Number: proto.Int32(1), 377 Type: descriptorpb.FieldDescriptorProto_TYPE_INT32.Enum(), 378 Label: descriptorpb.FieldDescriptorProto_LABEL_OPTIONAL.Enum(), 379 }, 380 { 381 Name: proto.String("int64_value"), 382 JsonName: proto.String("int64Value"), 383 Number: proto.Int32(2), 384 Type: descriptorpb.FieldDescriptorProto_TYPE_INT64.Enum(), 385 Label: descriptorpb.FieldDescriptorProto_LABEL_OPTIONAL.Enum(), 386 }, 387 { 388 Name: proto.String("uint32_value"), 389 JsonName: proto.String("uint32Value"), 390 Number: proto.Int32(3), 391 Type: descriptorpb.FieldDescriptorProto_TYPE_UINT32.Enum(), 392 Label: descriptorpb.FieldDescriptorProto_LABEL_OPTIONAL.Enum(), 393 }, 394 { 395 Name: proto.String("uint64_value"), 396 JsonName: proto.String("uint64Value"), 397 Number: proto.Int32(4), 398 Type: descriptorpb.FieldDescriptorProto_TYPE_UINT64.Enum(), 399 Label: descriptorpb.FieldDescriptorProto_LABEL_OPTIONAL.Enum(), 400 }, 401 { 402 Name: proto.String("float_value"), 403 JsonName: proto.String("floatValue"), 404 Number: proto.Int32(5), 405 Type: descriptorpb.FieldDescriptorProto_TYPE_FLOAT.Enum(), 406 Label: descriptorpb.FieldDescriptorProto_LABEL_OPTIONAL.Enum(), 407 }, 408 { 409 Name: proto.String("double_value"), 410 JsonName: proto.String("doubleValue"), 411 Number: proto.Int32(6), 412 Type: descriptorpb.FieldDescriptorProto_TYPE_DOUBLE.Enum(), 413 Label: descriptorpb.FieldDescriptorProto_LABEL_OPTIONAL.Enum(), 414 }, 415 { 416 Name: proto.String("bool_value"), 417 JsonName: proto.String("boolValue"), 418 Number: proto.Int32(7), 419 Type: descriptorpb.FieldDescriptorProto_TYPE_BOOL.Enum(), 420 Label: descriptorpb.FieldDescriptorProto_LABEL_OPTIONAL.Enum(), 421 }, 422 { 423 Name: proto.String("enum_value"), 424 JsonName: proto.String("enumValue"), 425 TypeName: proto.String("testdata_TestEnum_E.TestEnum"), 426 Number: proto.Int32(8), 427 Type: descriptorpb.FieldDescriptorProto_TYPE_ENUM.Enum(), 428 Label: descriptorpb.FieldDescriptorProto_LABEL_OPTIONAL.Enum(), 429 }, 430 { 431 Name: proto.String("string_value"), 432 JsonName: proto.String("stringValue"), 433 Number: proto.Int32(9), 434 Type: descriptorpb.FieldDescriptorProto_TYPE_STRING.Enum(), 435 Label: descriptorpb.FieldDescriptorProto_LABEL_REQUIRED.Enum(), 436 }, 437 { 438 Name: proto.String("fixed64_value"), 439 JsonName: proto.String("fixed64Value"), 440 Number: proto.Int32(10), 441 Type: descriptorpb.FieldDescriptorProto_TYPE_FIXED64.Enum(), 442 Label: descriptorpb.FieldDescriptorProto_LABEL_OPTIONAL.Enum(), 443 }, 444 }, 445 NestedType: []*descriptorpb.DescriptorProto{ 446 { 447 Name: proto.String("testdata_TestEnum_E"), 448 EnumType: []*descriptorpb.EnumDescriptorProto{ 449 { 450 Name: proto.String("TestEnum"), 451 Value: []*descriptorpb.EnumValueDescriptorProto{ 452 { 453 Name: proto.String("TestEnum0"), 454 Number: proto.Int32(0), 455 }, 456 { 457 Name: proto.String("TestEnum1"), 458 Number: proto.Int32(1), 459 }, 460 }, 461 }, 462 }, 463 }, 464 }, 465 }, 466 }, 467 { 468 description: "ContainsRecursive", 469 in: (&testdata.ContainsRecursive{}).ProtoReflect().Descriptor(), 470 wantErr: true, 471 }, 472 { 473 description: "RecursiveTypeTopMessage", 474 in: (&testdata.RecursiveTypeTopMessage{}).ProtoReflect().Descriptor(), 475 wantErr: true, 476 }, 477 { 478 description: "ComplexType", 479 in: (&testdata.ComplexType{}).ProtoReflect().Descriptor(), 480 want: &descriptorpb.DescriptorProto{ 481 Name: proto.String("testdata_ComplexType"), 482 Field: []*descriptorpb.FieldDescriptorProto{ 483 { 484 Name: proto.String("nested_repeated_type"), 485 JsonName: proto.String("nestedRepeatedType"), 486 Number: proto.Int32(1), 487 TypeName: proto.String("testdata_NestedType"), 488 Type: descriptorpb.FieldDescriptorProto_TYPE_MESSAGE.Enum(), 489 Label: descriptorpb.FieldDescriptorProto_LABEL_REPEATED.Enum(), 490 }, 491 { 492 Name: proto.String("inner_type"), 493 JsonName: proto.String("innerType"), 494 Number: proto.Int32(2), 495 TypeName: proto.String("testdata_InnerType"), 496 Type: descriptorpb.FieldDescriptorProto_TYPE_MESSAGE.Enum(), 497 Label: descriptorpb.FieldDescriptorProto_LABEL_OPTIONAL.Enum(), 498 }, 499 }, 500 NestedType: []*descriptorpb.DescriptorProto{ 501 { 502 Name: proto.String("testdata_InnerType"), 503 Field: []*descriptorpb.FieldDescriptorProto{ 504 { 505 Name: proto.String("value"), 506 JsonName: proto.String("value"), 507 Number: proto.Int32(1), 508 Type: descriptorpb.FieldDescriptorProto_TYPE_STRING.Enum(), 509 Label: descriptorpb.FieldDescriptorProto_LABEL_REPEATED.Enum(), 510 }, 511 }, 512 }, 513 { 514 Name: proto.String("testdata_NestedType"), 515 Field: []*descriptorpb.FieldDescriptorProto{ 516 { 517 Name: proto.String("inner_type"), 518 JsonName: proto.String("innerType"), 519 Number: proto.Int32(1), 520 TypeName: proto.String("testdata_InnerType"), 521 Type: descriptorpb.FieldDescriptorProto_TYPE_MESSAGE.Enum(), 522 Label: descriptorpb.FieldDescriptorProto_LABEL_REPEATED.Enum(), 523 }, 524 }, 525 }, 526 }, 527 }, 528 }, 529 { 530 description: "WithWellKnownTypes", 531 in: (&testdata.WithWellKnownTypes{}).ProtoReflect().Descriptor(), 532 want: &descriptorpb.DescriptorProto{ 533 Name: proto.String("testdata_WithWellKnownTypes"), 534 Field: []*descriptorpb.FieldDescriptorProto{ 535 { 536 Name: proto.String("int64_value"), 537 JsonName: proto.String("int64Value"), 538 Number: proto.Int32(1), 539 Type: descriptorpb.FieldDescriptorProto_TYPE_INT64.Enum(), 540 Label: descriptorpb.FieldDescriptorProto_LABEL_OPTIONAL.Enum(), 541 }, 542 { 543 Name: proto.String("wrapped_int64"), 544 JsonName: proto.String("wrappedInt64"), 545 Number: proto.Int32(2), 546 Type: descriptorpb.FieldDescriptorProto_TYPE_MESSAGE.Enum(), 547 TypeName: proto.String("google_protobuf_Int64Value"), 548 Label: descriptorpb.FieldDescriptorProto_LABEL_OPTIONAL.Enum(), 549 }, 550 { 551 Name: proto.String("string_value"), 552 JsonName: proto.String("stringValue"), 553 Number: proto.Int32(3), 554 Type: descriptorpb.FieldDescriptorProto_TYPE_STRING.Enum(), 555 Label: descriptorpb.FieldDescriptorProto_LABEL_REPEATED.Enum(), 556 }, 557 { 558 Name: proto.String("wrapped_string"), 559 JsonName: proto.String("wrappedString"), 560 Number: proto.Int32(4), 561 Type: descriptorpb.FieldDescriptorProto_TYPE_MESSAGE.Enum(), 562 TypeName: proto.String("google_protobuf_StringValue"), 563 Label: descriptorpb.FieldDescriptorProto_LABEL_REPEATED.Enum(), 564 }, 565 }, 566 NestedType: []*descriptorpb.DescriptorProto{ 567 { 568 Name: proto.String("google_protobuf_Int64Value"), 569 Field: []*descriptorpb.FieldDescriptorProto{ 570 { 571 Name: proto.String("value"), 572 JsonName: proto.String("value"), 573 Number: proto.Int32(1), 574 Type: descriptorpb.FieldDescriptorProto_TYPE_INT64.Enum(), 575 Label: descriptorpb.FieldDescriptorProto_LABEL_OPTIONAL.Enum(), 576 }, 577 }, 578 }, 579 { 580 Name: proto.String("google_protobuf_StringValue"), 581 Field: []*descriptorpb.FieldDescriptorProto{ 582 { 583 Name: proto.String("value"), 584 JsonName: proto.String("value"), 585 Number: proto.Int32(1), 586 Type: descriptorpb.FieldDescriptorProto_TYPE_STRING.Enum(), 587 Label: descriptorpb.FieldDescriptorProto_LABEL_OPTIONAL.Enum(), 588 }, 589 }, 590 }, 591 }, 592 }, 593 }, 594 } 595 596 for _, tc := range testCases { 597 gotDP, err := NormalizeDescriptor(tc.in) 598 599 if tc.wantErr && err == nil { 600 t.Errorf("%s: wanted err but got success", tc.description) 601 continue 602 } 603 if !tc.wantErr && err != nil { 604 t.Errorf("%s: wanted success, got err: %v", tc.description, err) 605 continue 606 } 607 if diff := cmp.Diff(gotDP, tc.want, protocmp.Transform()); diff != "" { 608 t.Errorf("%s: -got, +want:\n%s", tc.description, diff) 609 } 610 } 611} 612