github.com/jhump/protoreflect@v1.16.0/dynamic/msgregistry/message_registry_test.go (about) 1 package msgregistry 2 3 import ( 4 "fmt" 5 "io" 6 "io/ioutil" 7 "os" 8 "reflect" 9 "strings" 10 "testing" 11 12 "github.com/golang/protobuf/proto" 13 "github.com/golang/protobuf/ptypes" 14 "google.golang.org/protobuf/types/descriptorpb" 15 "google.golang.org/protobuf/types/known/anypb" 16 "google.golang.org/protobuf/types/known/apipb" 17 "google.golang.org/protobuf/types/known/durationpb" 18 "google.golang.org/protobuf/types/known/sourcecontextpb" 19 "google.golang.org/protobuf/types/known/typepb" 20 "google.golang.org/protobuf/types/known/wrapperspb" 21 22 "github.com/jhump/protoreflect/desc" 23 "github.com/jhump/protoreflect/desc/protoparse" 24 "github.com/jhump/protoreflect/dynamic" 25 "github.com/jhump/protoreflect/internal/testprotos" 26 "github.com/jhump/protoreflect/internal/testutil" 27 ) 28 29 func TestMessageRegistry_LookupTypes(t *testing.T) { 30 mr := &MessageRegistry{} 31 32 // register some types 33 md, err := desc.LoadMessageDescriptor("google.protobuf.DescriptorProto") 34 testutil.Ok(t, err) 35 err = mr.AddMessage("foo.bar/google.protobuf.DescriptorProto", md) 36 testutil.Ok(t, err) 37 ed := md.GetFile().FindEnum("google.protobuf.FieldDescriptorProto.Type") 38 testutil.Require(t, ed != nil) 39 err = mr.AddEnum("foo.bar/google.protobuf.FieldDescriptorProto.Type", ed) 40 testutil.Ok(t, err) 41 42 // lookups succeed 43 msg, err := mr.FindMessageTypeByUrl("foo.bar/google.protobuf.DescriptorProto") 44 testutil.Ok(t, err) 45 testutil.Eq(t, md, msg) 46 testutil.Eq(t, "https://foo.bar/google.protobuf.DescriptorProto", mr.ComputeURL(md)) 47 en, err := mr.FindEnumTypeByUrl("foo.bar/google.protobuf.FieldDescriptorProto.Type") 48 testutil.Ok(t, err) 49 testutil.Eq(t, ed, en) 50 testutil.Eq(t, "https://foo.bar/google.protobuf.FieldDescriptorProto.Type", mr.ComputeURL(ed)) 51 52 // right name but wrong domain? not found 53 msg, err = mr.FindMessageTypeByUrl("type.googleapis.com/google.protobuf.DescriptorProto") 54 testutil.Ok(t, err) 55 testutil.Require(t, msg == nil) 56 en, err = mr.FindEnumTypeByUrl("type.googleapis.com/google.protobuf.FieldDescriptorProto.Type") 57 testutil.Ok(t, err) 58 testutil.Require(t, en == nil) 59 60 // wrong type 61 _, err = mr.FindMessageTypeByUrl("foo.bar/google.protobuf.FieldDescriptorProto.Type") 62 _, ok := err.(*ErrUnexpectedType) 63 testutil.Require(t, ok) 64 _, err = mr.FindEnumTypeByUrl("foo.bar/google.protobuf.DescriptorProto") 65 _, ok = err.(*ErrUnexpectedType) 66 testutil.Require(t, ok) 67 68 // unmarshal any successfully finds the registered type 69 b, err := proto.Marshal(md.AsProto()) 70 testutil.Ok(t, err) 71 a := &anypb.Any{TypeUrl: "foo.bar/google.protobuf.DescriptorProto", Value: b} 72 pm, err := mr.UnmarshalAny(a) 73 testutil.Ok(t, err) 74 testutil.Ceq(t, md.AsProto(), pm, eqm) 75 // we didn't configure the registry with a message factory, so it would have 76 // produced a dynamic message instead of a generated message 77 testutil.Eq(t, reflect.TypeOf((*dynamic.Message)(nil)), reflect.TypeOf(pm)) 78 79 // by default, message registry knows about well-known types 80 dur := &durationpb.Duration{Nanos: 100, Seconds: 1000} 81 b, err = proto.Marshal(dur) 82 testutil.Ok(t, err) 83 a = &anypb.Any{TypeUrl: "foo.bar/google.protobuf.Duration", Value: b} 84 pm, err = mr.UnmarshalAny(a) 85 testutil.Ok(t, err) 86 testutil.Ceq(t, dur, pm, eqm) 87 testutil.Eq(t, reflect.TypeOf((*durationpb.Duration)(nil)), reflect.TypeOf(pm)) 88 89 fd, err := desc.LoadFileDescriptor("desc_test1.proto") 90 testutil.Ok(t, err) 91 mr.AddFile("frob.nitz/foo.bar", fd) 92 msgCount, enumCount := 0, 0 93 mds := fd.GetMessageTypes() 94 for i := 0; i < len(mds); i++ { 95 md := mds[i] 96 msgCount++ 97 mds = append(mds, md.GetNestedMessageTypes()...) 98 exp := fmt.Sprintf("https://frob.nitz/foo.bar/%s", md.GetFullyQualifiedName()) 99 testutil.Eq(t, exp, mr.ComputeURL(md)) 100 for _, ed := range md.GetNestedEnumTypes() { 101 enumCount++ 102 exp := fmt.Sprintf("https://frob.nitz/foo.bar/%s", ed.GetFullyQualifiedName()) 103 testutil.Eq(t, exp, mr.ComputeURL(ed)) 104 } 105 } 106 for _, ed := range fd.GetEnumTypes() { 107 enumCount++ 108 exp := fmt.Sprintf("https://frob.nitz/foo.bar/%s", ed.GetFullyQualifiedName()) 109 testutil.Eq(t, exp, mr.ComputeURL(ed)) 110 } 111 // sanity check 112 testutil.Eq(t, 11, msgCount) 113 testutil.Eq(t, 2, enumCount) 114 } 115 116 func TestMessageRegistry_LookupTypes_WithDefaults(t *testing.T) { 117 mr := NewMessageRegistryWithDefaults() 118 119 md, err := desc.LoadMessageDescriptor("google.protobuf.DescriptorProto") 120 testutil.Ok(t, err) 121 ed := md.GetFile().FindEnum("google.protobuf.FieldDescriptorProto.Type") 122 testutil.Require(t, ed != nil) 123 124 // lookups succeed 125 msg, err := mr.FindMessageTypeByUrl("type.googleapis.com/google.protobuf.DescriptorProto") 126 testutil.Ok(t, err) 127 testutil.Eq(t, md, msg) 128 // default types don't know their base URL, so will resolve even w/ wrong name 129 // (just have to get fully-qualified message name right) 130 msg, err = mr.FindMessageTypeByUrl("foo.bar/google.protobuf.DescriptorProto") 131 testutil.Ok(t, err) 132 testutil.Eq(t, md, msg) 133 134 // sad trombone: no way to lookup "default" enum types, so enums don't resolve 135 // without being explicitly registered :( 136 en, err := mr.FindEnumTypeByUrl("type.googleapis.com/google.protobuf.FieldDescriptorProto.Type") 137 testutil.Ok(t, err) 138 testutil.Require(t, en == nil) 139 en, err = mr.FindEnumTypeByUrl("foo.bar/google.protobuf.FieldDescriptorProto.Type") 140 testutil.Ok(t, err) 141 testutil.Require(t, en == nil) 142 143 // unmarshal any successfully finds the registered type 144 b, err := proto.Marshal(md.AsProto()) 145 testutil.Ok(t, err) 146 a := &anypb.Any{TypeUrl: "foo.bar/google.protobuf.DescriptorProto", Value: b} 147 pm, err := mr.UnmarshalAny(a) 148 testutil.Ok(t, err) 149 testutil.Ceq(t, md.AsProto(), pm, eqm) 150 // message registry with defaults implies known-type registry with defaults, so 151 // it should have marshalled the message into a generated message 152 testutil.Eq(t, reflect.TypeOf((*descriptorpb.DescriptorProto)(nil)), reflect.TypeOf(pm)) 153 } 154 155 func TestMessageRegistry_FindMessage_WithFetcher(t *testing.T) { 156 tf := createFetcher(t) 157 // we want "defaults" for the message factory so that we can properly process 158 // known extensions (which the type fetcher puts into the descriptor options) 159 mr := (&MessageRegistry{}).WithFetcher(tf).WithMessageFactory(dynamic.NewMessageFactoryWithDefaults()) 160 161 md, err := mr.FindMessageTypeByUrl("foo.bar/some.Type") 162 testutil.Ok(t, err) 163 164 // Fairly in-depth check of the returned message descriptor: 165 166 testutil.Eq(t, "Type", md.GetName()) 167 testutil.Eq(t, "some.Type", md.GetFullyQualifiedName()) 168 testutil.Eq(t, "some", md.GetFile().GetPackage()) 169 testutil.Eq(t, true, md.GetFile().IsProto3()) 170 testutil.Eq(t, true, md.IsProto3()) 171 172 mo := &descriptorpb.MessageOptions{ 173 Deprecated: proto.Bool(true), 174 } 175 err = proto.SetExtension(mo, testprotos.E_Mfubar, proto.Bool(true)) 176 testutil.Ok(t, err) 177 testutil.Ceq(t, mo, md.GetMessageOptions(), eqpm) 178 179 flds := md.GetFields() 180 testutil.Eq(t, 4, len(flds)) 181 testutil.Eq(t, "a", flds[0].GetName()) 182 testutil.Eq(t, int32(1), flds[0].GetNumber()) 183 testutil.Eq(t, (*desc.OneOfDescriptor)(nil), flds[0].GetOneOf()) 184 testutil.Eq(t, descriptorpb.FieldDescriptorProto_LABEL_OPTIONAL, flds[0].GetLabel()) 185 testutil.Eq(t, descriptorpb.FieldDescriptorProto_TYPE_MESSAGE, flds[0].GetType()) 186 187 fo := &descriptorpb.FieldOptions{ 188 Deprecated: proto.Bool(true), 189 } 190 err = proto.SetExtension(fo, testprotos.E_Ffubar, []string{"foo", "bar", "baz"}) 191 testutil.Ok(t, err) 192 err = proto.SetExtension(fo, testprotos.E_Ffubarb, []byte{1, 2, 3, 4, 5, 6, 7, 8}) 193 testutil.Ok(t, err) 194 testutil.Ceq(t, fo, flds[0].GetFieldOptions(), eqpm) 195 196 testutil.Eq(t, "b", flds[1].GetName()) 197 testutil.Eq(t, int32(2), flds[1].GetNumber()) 198 testutil.Eq(t, (*desc.OneOfDescriptor)(nil), flds[1].GetOneOf()) 199 testutil.Eq(t, descriptorpb.FieldDescriptorProto_LABEL_REPEATED, flds[1].GetLabel()) 200 testutil.Eq(t, descriptorpb.FieldDescriptorProto_TYPE_STRING, flds[1].GetType()) 201 202 testutil.Eq(t, "c", flds[2].GetName()) 203 testutil.Eq(t, int32(3), flds[2].GetNumber()) 204 testutil.Eq(t, "un", flds[2].GetOneOf().GetName()) 205 testutil.Eq(t, descriptorpb.FieldDescriptorProto_LABEL_OPTIONAL, flds[2].GetLabel()) 206 testutil.Eq(t, descriptorpb.FieldDescriptorProto_TYPE_ENUM, flds[2].GetType()) 207 208 testutil.Eq(t, "d", flds[3].GetName()) 209 testutil.Eq(t, int32(4), flds[3].GetNumber()) 210 testutil.Eq(t, "un", flds[3].GetOneOf().GetName()) 211 testutil.Eq(t, descriptorpb.FieldDescriptorProto_LABEL_OPTIONAL, flds[3].GetLabel()) 212 testutil.Eq(t, descriptorpb.FieldDescriptorProto_TYPE_INT32, flds[3].GetType()) 213 214 oos := md.GetOneOfs() 215 testutil.Eq(t, 1, len(oos)) 216 testutil.Eq(t, "un", oos[0].GetName()) 217 ooflds := oos[0].GetChoices() 218 testutil.Eq(t, 2, len(ooflds)) 219 testutil.Eq(t, flds[2], ooflds[0]) 220 testutil.Eq(t, flds[3], ooflds[1]) 221 222 // Quick, shallow check of the linked descriptors: 223 224 md2 := md.FindFieldByName("a").GetMessageType() 225 testutil.Eq(t, "OtherType", md2.GetName()) 226 testutil.Eq(t, "some.OtherType", md2.GetFullyQualifiedName()) 227 testutil.Eq(t, "some", md2.GetFile().GetPackage()) 228 testutil.Eq(t, false, md2.GetFile().IsProto3()) 229 testutil.Eq(t, false, md2.IsProto3()) 230 231 nmd := md2.GetNestedMessageTypes()[0] 232 testutil.Ceq(t, nmd.AsProto(), md2.FindFieldByName("a").GetMessageType().AsProto(), eqpm) 233 testutil.Eq(t, "AnotherType", nmd.GetName()) 234 testutil.Eq(t, "some.OtherType.AnotherType", nmd.GetFullyQualifiedName()) 235 testutil.Eq(t, "some", nmd.GetFile().GetPackage()) 236 testutil.Eq(t, false, nmd.GetFile().IsProto3()) 237 testutil.Eq(t, false, nmd.IsProto3()) 238 239 en := md.FindFieldByName("c").GetEnumType() 240 testutil.Eq(t, "Enum", en.GetName()) 241 testutil.Eq(t, "some.Enum", en.GetFullyQualifiedName()) 242 testutil.Eq(t, "some", en.GetFile().GetPackage()) 243 testutil.Eq(t, true, en.GetFile().IsProto3()) 244 245 // Ask for another one. This one has a name that looks like "some.YetAnother" 246 // package in this context. 247 md3, err := mr.FindMessageTypeByUrl("foo.bar/some.YetAnother.MessageType") 248 testutil.Ok(t, err) 249 testutil.Eq(t, "MessageType", md3.GetName()) 250 testutil.Eq(t, "some.YetAnother.MessageType", md3.GetFullyQualifiedName()) 251 testutil.Eq(t, "some.YetAnother", md3.GetFile().GetPackage()) 252 testutil.Eq(t, false, md3.GetFile().IsProto3()) 253 testutil.Eq(t, false, md3.IsProto3()) 254 } 255 256 func TestMessageRegistry_FindMessage_Mixed(t *testing.T) { 257 msgType := &typepb.Type{ 258 Name: "foo.Bar", 259 Oneofs: []string{"baz"}, 260 Fields: []*typepb.Field{ 261 { 262 Name: "id", 263 Number: 1, 264 Kind: typepb.Field_TYPE_UINT64, 265 Cardinality: typepb.Field_CARDINALITY_OPTIONAL, 266 JsonName: "id", 267 }, 268 { 269 Name: "name", 270 Number: 2, 271 Kind: typepb.Field_TYPE_STRING, 272 Cardinality: typepb.Field_CARDINALITY_OPTIONAL, 273 JsonName: "name", 274 }, 275 { 276 Name: "count", 277 Number: 3, 278 OneofIndex: 1, 279 Kind: typepb.Field_TYPE_INT32, 280 Cardinality: typepb.Field_CARDINALITY_OPTIONAL, 281 JsonName: "count", 282 }, 283 { 284 Name: "data", 285 Number: 4, 286 OneofIndex: 1, 287 Kind: typepb.Field_TYPE_BYTES, 288 Cardinality: typepb.Field_CARDINALITY_OPTIONAL, 289 JsonName: "data", 290 }, 291 { 292 Name: "other", 293 Number: 5, 294 OneofIndex: 1, 295 Kind: typepb.Field_TYPE_MESSAGE, 296 Cardinality: typepb.Field_CARDINALITY_OPTIONAL, 297 JsonName: "other", 298 TypeUrl: "type.googleapis.com/google.protobuf.Empty", 299 }, 300 { 301 Name: "created", 302 Number: 6, 303 Kind: typepb.Field_TYPE_MESSAGE, 304 Cardinality: typepb.Field_CARDINALITY_OPTIONAL, 305 JsonName: "created", 306 TypeUrl: "type.googleapis.com/google.protobuf.Timestamp", 307 }, 308 { 309 Name: "updated", 310 Number: 7, 311 Kind: typepb.Field_TYPE_MESSAGE, 312 Cardinality: typepb.Field_CARDINALITY_OPTIONAL, 313 JsonName: "updated", 314 TypeUrl: "type.googleapis.com/google.protobuf.Timestamp", 315 }, 316 { 317 Name: "tombstone", 318 Number: 8, 319 Kind: typepb.Field_TYPE_BOOL, 320 Cardinality: typepb.Field_CARDINALITY_OPTIONAL, 321 JsonName: "tombstone", 322 }, 323 }, 324 SourceContext: &sourcecontextpb.SourceContext{ 325 FileName: "test/foo.proto", 326 }, 327 Syntax: typepb.Syntax_SYNTAX_PROTO3, 328 } 329 330 var mr MessageRegistry 331 mr.WithFetcher(func(url string, enum bool) (proto.Message, error) { 332 if url == "https://foo.test.com/foo.Bar" && !enum { 333 return msgType, nil 334 } 335 return nil, fmt.Errorf("unknown type: %s", url) 336 }) 337 338 // Make sure we successfully get back a descriptor 339 md, err := mr.FindMessageTypeByUrl("foo.test.com/foo.Bar") 340 testutil.Ok(t, err) 341 342 // Check its properties. It should have the fields from the type 343 // description above, but also correctly refer to google/protobuf 344 // dependencies (which came from resolver, not the fetcher). 345 346 testutil.Eq(t, "foo.Bar", md.GetFullyQualifiedName()) 347 testutil.Eq(t, "Bar", md.GetName()) 348 testutil.Eq(t, "test/foo.proto", md.GetFile().GetName()) 349 testutil.Eq(t, "foo", md.GetFile().GetPackage()) 350 351 fd := md.FindFieldByName("created") 352 testutil.Eq(t, "google.protobuf.Timestamp", fd.GetMessageType().GetFullyQualifiedName()) 353 testutil.Eq(t, "google/protobuf/timestamp.proto", fd.GetMessageType().GetFile().GetName()) 354 355 ood := md.GetOneOfs()[0] 356 testutil.Eq(t, 3, len(ood.GetChoices())) 357 fd = ood.GetChoices()[2] 358 testutil.Eq(t, "google.protobuf.Empty", fd.GetMessageType().GetFullyQualifiedName()) 359 testutil.Eq(t, "google/protobuf/empty.proto", fd.GetMessageType().GetFile().GetName()) 360 } 361 362 func TestMessageRegistry_FindEnum_WithFetcher(t *testing.T) { 363 tf := createFetcher(t) 364 // we want "defaults" for the message factory so that we can properly process 365 // known extensions (which the type fetcher puts into the descriptor options) 366 mr := (&MessageRegistry{}).WithFetcher(tf).WithMessageFactory(dynamic.NewMessageFactoryWithDefaults()) 367 368 ed, err := mr.FindEnumTypeByUrl("foo.bar/some.Enum") 369 testutil.Ok(t, err) 370 371 testutil.Eq(t, "Enum", ed.GetName()) 372 testutil.Eq(t, "some.Enum", ed.GetFullyQualifiedName()) 373 testutil.Eq(t, "some", ed.GetFile().GetPackage()) 374 testutil.Eq(t, true, ed.GetFile().IsProto3()) 375 376 eo := &descriptorpb.EnumOptions{ 377 Deprecated: proto.Bool(true), 378 AllowAlias: proto.Bool(true), 379 } 380 err = proto.SetExtension(eo, testprotos.E_Efubar, proto.Int32(-42)) 381 testutil.Ok(t, err) 382 err = proto.SetExtension(eo, testprotos.E_Efubars, proto.Int32(-42)) 383 testutil.Ok(t, err) 384 err = proto.SetExtension(eo, testprotos.E_Efubarsf, proto.Int32(-42)) 385 testutil.Ok(t, err) 386 err = proto.SetExtension(eo, testprotos.E_Efubaru, proto.Uint32(42)) 387 testutil.Ok(t, err) 388 err = proto.SetExtension(eo, testprotos.E_Efubaruf, proto.Uint32(42)) 389 testutil.Ok(t, err) 390 testutil.Ceq(t, eo, ed.GetEnumOptions(), eqpm) 391 392 vals := ed.GetValues() 393 testutil.Eq(t, 3, len(vals)) 394 testutil.Eq(t, "ABC", vals[0].GetName()) 395 testutil.Eq(t, int32(0), vals[0].GetNumber()) 396 397 evo := &descriptorpb.EnumValueOptions{ 398 Deprecated: proto.Bool(true), 399 } 400 err = proto.SetExtension(evo, testprotos.E_Evfubar, proto.Int64(-420420420420)) 401 testutil.Ok(t, err) 402 err = proto.SetExtension(evo, testprotos.E_Evfubars, proto.Int64(-420420420420)) 403 testutil.Ok(t, err) 404 err = proto.SetExtension(evo, testprotos.E_Evfubarsf, proto.Int64(-420420420420)) 405 testutil.Ok(t, err) 406 err = proto.SetExtension(evo, testprotos.E_Evfubaru, proto.Uint64(420420420420)) 407 testutil.Ok(t, err) 408 err = proto.SetExtension(evo, testprotos.E_Evfubaruf, proto.Uint64(420420420420)) 409 testutil.Ok(t, err) 410 testutil.Ceq(t, evo, vals[0].GetEnumValueOptions(), eqpm) 411 412 testutil.Eq(t, "XYZ", vals[1].GetName()) 413 testutil.Eq(t, int32(1), vals[1].GetNumber()) 414 415 testutil.Eq(t, "WXY", vals[2].GetName()) 416 testutil.Eq(t, int32(1), vals[2].GetNumber()) 417 } 418 419 func createFetcher(t *testing.T) TypeFetcher { 420 bol, err := ptypes.MarshalAny(&wrapperspb.BoolValue{Value: true}) 421 testutil.Ok(t, err) 422 in32, err := ptypes.MarshalAny(&wrapperspb.Int32Value{Value: -42}) 423 testutil.Ok(t, err) 424 uin32, err := ptypes.MarshalAny(&wrapperspb.UInt32Value{Value: 42}) 425 testutil.Ok(t, err) 426 in64, err := ptypes.MarshalAny(&wrapperspb.Int64Value{Value: -420420420420}) 427 testutil.Ok(t, err) 428 uin64, err := ptypes.MarshalAny(&wrapperspb.UInt64Value{Value: 420420420420}) 429 testutil.Ok(t, err) 430 byt, err := ptypes.MarshalAny(&wrapperspb.BytesValue{Value: []byte{1, 2, 3, 4, 5, 6, 7, 8}}) 431 testutil.Ok(t, err) 432 str1, err := ptypes.MarshalAny(&wrapperspb.StringValue{Value: "foo"}) 433 testutil.Ok(t, err) 434 str2, err := ptypes.MarshalAny(&wrapperspb.StringValue{Value: "bar"}) 435 testutil.Ok(t, err) 436 str3, err := ptypes.MarshalAny(&wrapperspb.StringValue{Value: "baz"}) 437 testutil.Ok(t, err) 438 439 types := map[string]proto.Message{ 440 "https://foo.bar/some.Type": &typepb.Type{ 441 Name: "some.Type", 442 Oneofs: []string{"un"}, 443 Fields: []*typepb.Field{ 444 { 445 Name: "a", 446 JsonName: "a", 447 Number: 1, 448 Cardinality: typepb.Field_CARDINALITY_OPTIONAL, 449 Kind: typepb.Field_TYPE_MESSAGE, 450 TypeUrl: "foo.bar/some.OtherType", 451 Options: []*typepb.Option{ 452 { 453 Name: "deprecated", 454 Value: bol, 455 }, 456 { 457 Name: "testprotos.ffubar", 458 Value: str1, 459 }, 460 { 461 Name: "testprotos.ffubar", 462 Value: str2, 463 }, 464 { 465 Name: "testprotos.ffubar", 466 Value: str3, 467 }, 468 { 469 Name: "testprotos.ffubarb", 470 Value: byt, 471 }, 472 }, 473 }, 474 { 475 Name: "b", 476 JsonName: "b", 477 Number: 2, 478 Cardinality: typepb.Field_CARDINALITY_REPEATED, 479 Kind: typepb.Field_TYPE_STRING, 480 }, 481 { 482 Name: "c", 483 JsonName: "c", 484 Number: 3, 485 Cardinality: typepb.Field_CARDINALITY_OPTIONAL, 486 Kind: typepb.Field_TYPE_ENUM, 487 TypeUrl: "foo.bar/some.Enum", 488 OneofIndex: 1, 489 }, 490 { 491 Name: "d", 492 JsonName: "d", 493 Number: 4, 494 Cardinality: typepb.Field_CARDINALITY_OPTIONAL, 495 Kind: typepb.Field_TYPE_INT32, 496 OneofIndex: 1, 497 }, 498 }, 499 Options: []*typepb.Option{ 500 { 501 Name: "deprecated", 502 Value: bol, 503 }, 504 { 505 Name: "testprotos.mfubar", 506 Value: bol, 507 }, 508 }, 509 SourceContext: &sourcecontextpb.SourceContext{FileName: "foo.proto"}, 510 Syntax: typepb.Syntax_SYNTAX_PROTO3, 511 }, 512 "https://foo.bar/some.OtherType": &typepb.Type{ 513 Name: "some.OtherType", 514 Fields: []*typepb.Field{ 515 { 516 Name: "a", 517 JsonName: "a", 518 Number: 1, 519 Cardinality: typepb.Field_CARDINALITY_OPTIONAL, 520 Kind: typepb.Field_TYPE_MESSAGE, 521 TypeUrl: "foo.bar/some.OtherType.AnotherType", 522 }, 523 }, 524 SourceContext: &sourcecontextpb.SourceContext{FileName: "bar.proto"}, 525 Syntax: typepb.Syntax_SYNTAX_PROTO2, 526 }, 527 "https://foo.bar/some.OtherType.AnotherType": &typepb.Type{ 528 Name: "some.OtherType.AnotherType", 529 Fields: []*typepb.Field{ 530 { 531 Name: "a", 532 JsonName: "a", 533 Number: 1, 534 Cardinality: typepb.Field_CARDINALITY_OPTIONAL, 535 Kind: typepb.Field_TYPE_BYTES, 536 }, 537 }, 538 SourceContext: &sourcecontextpb.SourceContext{FileName: "bar.proto"}, 539 Syntax: typepb.Syntax_SYNTAX_PROTO2, 540 }, 541 "https://foo.bar/some.Enum": &typepb.Enum{ 542 Name: "some.Enum", 543 Enumvalue: []*typepb.EnumValue{ 544 { 545 Name: "ABC", 546 Number: 0, 547 Options: []*typepb.Option{ 548 { 549 Name: "deprecated", 550 Value: bol, 551 }, 552 { 553 Name: "testprotos.evfubar", 554 Value: in64, 555 }, 556 { 557 Name: "testprotos.evfubars", 558 Value: in64, 559 }, 560 { 561 Name: "testprotos.evfubarsf", 562 Value: in64, 563 }, 564 { 565 Name: "testprotos.evfubaru", 566 Value: uin64, 567 }, 568 { 569 Name: "testprotos.evfubaruf", 570 Value: uin64, 571 }, 572 }, 573 }, 574 { 575 Name: "XYZ", 576 Number: 1, 577 }, 578 { 579 Name: "WXY", 580 Number: 1, 581 }, 582 }, 583 Options: []*typepb.Option{ 584 { 585 Name: "deprecated", 586 Value: bol, 587 }, 588 { 589 Name: "allow_alias", 590 Value: bol, 591 }, 592 { 593 Name: "testprotos.efubar", 594 Value: in32, 595 }, 596 { 597 Name: "testprotos.efubars", 598 Value: in32, 599 }, 600 { 601 Name: "testprotos.efubarsf", 602 Value: in32, 603 }, 604 { 605 Name: "testprotos.efubaru", 606 Value: uin32, 607 }, 608 { 609 Name: "testprotos.efubaruf", 610 Value: uin32, 611 }, 612 }, 613 SourceContext: &sourcecontextpb.SourceContext{FileName: "foo.proto"}, 614 Syntax: typepb.Syntax_SYNTAX_PROTO3, 615 }, 616 "https://foo.bar/some.YetAnother.MessageType": &typepb.Type{ 617 // in a separate file, so it will look like package some.YetAnother 618 Name: "some.YetAnother.MessageType", 619 Fields: []*typepb.Field{ 620 { 621 Name: "a", 622 JsonName: "a", 623 Number: 1, 624 Cardinality: typepb.Field_CARDINALITY_OPTIONAL, 625 Kind: typepb.Field_TYPE_STRING, 626 }, 627 }, 628 SourceContext: &sourcecontextpb.SourceContext{FileName: "baz.proto"}, 629 Syntax: typepb.Syntax_SYNTAX_PROTO2, 630 }, 631 } 632 return func(url string, enum bool) (proto.Message, error) { 633 t := types[url] 634 if t == nil { 635 return nil, nil 636 } 637 if _, ok := t.(*typepb.Enum); ok == enum { 638 return t, nil 639 } else { 640 return nil, fmt.Errorf("bad type for %s", url) 641 } 642 } 643 } 644 645 func TestMessageRegistry_ResolveApiIntoServiceDescriptor(t *testing.T) { 646 tf := createFetcher(t) 647 // we want "defaults" for the message factory so that we can properly process 648 // known extensions (which the type fetcher puts into the descriptor options) 649 mr := (&MessageRegistry{}).WithFetcher(tf).WithMessageFactory(dynamic.NewMessageFactoryWithDefaults()) 650 651 sd, err := mr.ResolveApiIntoServiceDescriptor(getApi(t)) 652 testutil.Ok(t, err) 653 654 testutil.Eq(t, "Service", sd.GetName()) 655 testutil.Eq(t, "some.Service", sd.GetFullyQualifiedName()) 656 testutil.Eq(t, "some", sd.GetFile().GetPackage()) 657 testutil.Eq(t, true, sd.GetFile().IsProto3()) 658 659 so := &descriptorpb.ServiceOptions{ 660 Deprecated: proto.Bool(true), 661 } 662 err = proto.SetExtension(so, testprotos.E_Sfubar, &testprotos.ReallySimpleMessage{Id: proto.Uint64(100), Name: proto.String("deuce")}) 663 testutil.Ok(t, err) 664 err = proto.SetExtension(so, testprotos.E_Sfubare, testprotos.ReallySimpleEnum_VALUE.Enum()) 665 testutil.Ok(t, err) 666 testutil.Ceq(t, so, sd.GetServiceOptions(), eqpm) 667 668 methods := sd.GetMethods() 669 testutil.Eq(t, 4, len(methods)) 670 testutil.Eq(t, "UnaryMethod", methods[0].GetName()) 671 testutil.Eq(t, "some.Type", methods[0].GetInputType().GetFullyQualifiedName()) 672 testutil.Eq(t, "some.OtherType", methods[0].GetOutputType().GetFullyQualifiedName()) 673 674 mto := &descriptorpb.MethodOptions{ 675 Deprecated: proto.Bool(true), 676 } 677 err = proto.SetExtension(mto, testprotos.E_Mtfubar, []float32{3.14159, 2.71828}) 678 testutil.Ok(t, err) 679 err = proto.SetExtension(mto, testprotos.E_Mtfubard, proto.Float64(10203040.506070809)) 680 testutil.Ok(t, err) 681 testutil.Ceq(t, mto, methods[0].GetMethodOptions(), eqpm) 682 683 testutil.Eq(t, "ClientStreamMethod", methods[1].GetName()) 684 testutil.Eq(t, "some.OtherType", methods[1].GetInputType().GetFullyQualifiedName()) 685 testutil.Eq(t, "some.Type", methods[1].GetOutputType().GetFullyQualifiedName()) 686 687 testutil.Eq(t, "ServerStreamMethod", methods[2].GetName()) 688 testutil.Eq(t, "some.OtherType.AnotherType", methods[2].GetInputType().GetFullyQualifiedName()) 689 testutil.Eq(t, "some.YetAnother.MessageType", methods[2].GetOutputType().GetFullyQualifiedName()) 690 691 testutil.Eq(t, "BidiStreamMethod", methods[3].GetName()) 692 testutil.Eq(t, "some.YetAnother.MessageType", methods[3].GetInputType().GetFullyQualifiedName()) 693 testutil.Eq(t, "some.OtherType.AnotherType", methods[3].GetOutputType().GetFullyQualifiedName()) 694 695 // check linked message types 696 697 testutil.Eq(t, methods[0].GetInputType(), methods[1].GetOutputType()) 698 testutil.Eq(t, methods[0].GetOutputType(), methods[1].GetInputType()) 699 testutil.Eq(t, methods[2].GetInputType(), methods[3].GetOutputType()) 700 testutil.Eq(t, methods[2].GetOutputType(), methods[3].GetInputType()) 701 702 md1 := methods[0].GetInputType() 703 md2 := methods[0].GetOutputType() 704 md3 := methods[2].GetInputType() 705 md4 := methods[2].GetOutputType() 706 707 testutil.Eq(t, "Type", md1.GetName()) 708 testutil.Eq(t, "some.Type", md1.GetFullyQualifiedName()) 709 testutil.Eq(t, "some", md1.GetFile().GetPackage()) 710 testutil.Eq(t, true, md1.GetFile().IsProto3()) 711 testutil.Eq(t, true, md1.IsProto3()) 712 713 testutil.Eq(t, "OtherType", md2.GetName()) 714 testutil.Eq(t, "some.OtherType", md2.GetFullyQualifiedName()) 715 testutil.Eq(t, "some", md2.GetFile().GetPackage()) 716 testutil.Eq(t, false, md2.GetFile().IsProto3()) 717 testutil.Eq(t, false, md2.IsProto3()) 718 719 testutil.Eq(t, md3, md2.GetNestedMessageTypes()[0]) 720 testutil.Eq(t, "AnotherType", md3.GetName()) 721 testutil.Eq(t, "some.OtherType.AnotherType", md3.GetFullyQualifiedName()) 722 testutil.Eq(t, "some", md3.GetFile().GetPackage()) 723 testutil.Eq(t, false, md3.GetFile().IsProto3()) 724 testutil.Eq(t, false, md3.IsProto3()) 725 726 testutil.Eq(t, "MessageType", md4.GetName()) 727 testutil.Eq(t, "some.YetAnother.MessageType", md4.GetFullyQualifiedName()) 728 testutil.Eq(t, "some", md4.GetFile().GetPackage()) 729 testutil.Eq(t, true, md4.GetFile().IsProto3()) 730 testutil.Eq(t, true, md4.IsProto3()) 731 } 732 733 func getApi(t *testing.T) *apipb.Api { 734 bol, err := ptypes.MarshalAny(&wrapperspb.BoolValue{Value: true}) 735 testutil.Ok(t, err) 736 dbl, err := ptypes.MarshalAny(&wrapperspb.DoubleValue{Value: 10203040.506070809}) 737 testutil.Ok(t, err) 738 flt1, err := ptypes.MarshalAny(&wrapperspb.FloatValue{Value: 3.14159}) 739 testutil.Ok(t, err) 740 flt2, err := ptypes.MarshalAny(&wrapperspb.FloatValue{Value: 2.71828}) 741 testutil.Ok(t, err) 742 enu, err := ptypes.MarshalAny(&wrapperspb.Int32Value{Value: int32(testprotos.ReallySimpleEnum_VALUE)}) 743 testutil.Ok(t, err) 744 msg, err := ptypes.MarshalAny(&testprotos.ReallySimpleMessage{Id: proto.Uint64(100), Name: proto.String("deuce")}) 745 testutil.Ok(t, err) 746 return &apipb.Api{ 747 Name: "some.Service", 748 Methods: []*apipb.Method{ 749 { 750 Name: "UnaryMethod", 751 RequestTypeUrl: "foo.bar/some.Type", 752 ResponseTypeUrl: "foo.bar/some.OtherType", 753 Options: []*typepb.Option{ 754 { 755 Name: "deprecated", 756 Value: bol, 757 }, 758 { 759 Name: "testprotos.mtfubar", 760 Value: flt1, 761 }, 762 { 763 Name: "testprotos.mtfubar", 764 Value: flt2, 765 }, 766 { 767 Name: "testprotos.mtfubard", 768 Value: dbl, 769 }, 770 }, 771 Syntax: typepb.Syntax_SYNTAX_PROTO3, 772 }, 773 { 774 Name: "ClientStreamMethod", 775 RequestStreaming: true, 776 RequestTypeUrl: "foo.bar/some.OtherType", 777 ResponseTypeUrl: "foo.bar/some.Type", 778 Syntax: typepb.Syntax_SYNTAX_PROTO3, 779 }, 780 { 781 Name: "ServerStreamMethod", 782 ResponseStreaming: true, 783 RequestTypeUrl: "foo.bar/some.OtherType.AnotherType", 784 ResponseTypeUrl: "foo.bar/some.YetAnother.MessageType", 785 Syntax: typepb.Syntax_SYNTAX_PROTO3, 786 }, 787 { 788 Name: "BidiStreamMethod", 789 RequestStreaming: true, 790 ResponseStreaming: true, 791 RequestTypeUrl: "foo.bar/some.YetAnother.MessageType", 792 ResponseTypeUrl: "foo.bar/some.OtherType.AnotherType", 793 Syntax: typepb.Syntax_SYNTAX_PROTO3, 794 }, 795 }, 796 Options: []*typepb.Option{ 797 { 798 Name: "deprecated", 799 Value: bol, 800 }, 801 { 802 Name: "testprotos.sfubar", 803 Value: msg, 804 }, 805 { 806 Name: "testprotos.sfubare", 807 Value: enu, 808 }, 809 }, 810 SourceContext: &sourcecontextpb.SourceContext{FileName: "baz.proto"}, 811 Syntax: typepb.Syntax_SYNTAX_PROTO3, 812 } 813 } 814 815 func TestMessageRegistry_MarshalAndUnmarshalAny(t *testing.T) { 816 mr := NewMessageRegistryWithDefaults() 817 818 md, err := desc.LoadMessageDescriptor("google.protobuf.DescriptorProto") 819 testutil.Ok(t, err) 820 821 // marshal with default base URL 822 a, err := mr.MarshalAny(md.AsProto()) 823 testutil.Ok(t, err) 824 testutil.Eq(t, "type.googleapis.com/google.protobuf.DescriptorProto", a.TypeUrl) 825 826 // check that we can unmarshal it with normal ptypes library 827 var umd descriptorpb.DescriptorProto 828 err = ptypes.UnmarshalAny(a, &umd) 829 testutil.Ok(t, err) 830 testutil.Ceq(t, md.AsProto(), &umd, eqpm) 831 832 // and that we can unmarshal it with a message registry 833 pm, err := mr.UnmarshalAny(a) 834 testutil.Ok(t, err) 835 _, ok := pm.(*descriptorpb.DescriptorProto) 836 testutil.Require(t, ok) 837 testutil.Ceq(t, md.AsProto(), pm, eqpm) 838 839 // and that we can unmarshal it as a dynamic message, using a 840 // message registry that doesn't know about the generated type 841 mrWithoutDefaults := &MessageRegistry{} 842 err = mrWithoutDefaults.AddMessage("type.googleapis.com/google.protobuf.DescriptorProto", md) 843 testutil.Ok(t, err) 844 pm, err = mrWithoutDefaults.UnmarshalAny(a) 845 testutil.Ok(t, err) 846 dm, ok := pm.(*dynamic.Message) 847 testutil.Require(t, ok) 848 testutil.Ceq(t, md.AsProto(), dm, eqm) 849 850 // now test generation of type URLs with other settings 851 852 // - different default 853 mr.WithDefaultBaseUrl("foo.com/some/path/") 854 a, err = mr.MarshalAny(md.AsProto()) 855 testutil.Ok(t, err) 856 testutil.Eq(t, "foo.com/some/path/google.protobuf.DescriptorProto", a.TypeUrl) 857 858 // - custom base URL for package 859 mr.AddBaseUrlForElement("bar.com/other/", "google.protobuf") 860 a, err = mr.MarshalAny(md.AsProto()) 861 testutil.Ok(t, err) 862 testutil.Eq(t, "bar.com/other/google.protobuf.DescriptorProto", a.TypeUrl) 863 864 // - custom base URL for type 865 mr.AddBaseUrlForElement("http://baz.com/another/", "google.protobuf.DescriptorProto") 866 a, err = mr.MarshalAny(md.AsProto()) 867 testutil.Ok(t, err) 868 testutil.Eq(t, "http://baz.com/another/google.protobuf.DescriptorProto", a.TypeUrl) 869 } 870 871 func TestMessageRegistry_MessageDescriptorToPType(t *testing.T) { 872 protoSource := ` 873 syntax = "proto2"; 874 package foo; 875 message Bar { 876 optional string abc = 1 [deprecated = true]; 877 repeated int32 def = 2 [packed = true]; 878 optional string ghi = 3 [default = "foobar"]; 879 oneof oo { 880 uint64 nid = 4; 881 string sid = 5; 882 } 883 }` 884 p := protoparse.Parser{ 885 Accessor: func(filename string) (io.ReadCloser, error) { 886 if filename == "test.proto" { 887 return ioutil.NopCloser(strings.NewReader(protoSource)), nil 888 } 889 return nil, os.ErrNotExist 890 }, 891 } 892 fds, err := p.ParseFiles("test.proto") 893 testutil.Ok(t, err) 894 fd := fds[0] 895 896 msg := NewMessageRegistryWithDefaults().MessageAsPType(fd.GetMessageTypes()[0]) 897 898 // quick check of the resulting message's properties 899 testutil.Eq(t, "foo.Bar", msg.Name) 900 testutil.Eq(t, []string{"oo"}, msg.Oneofs) 901 testutil.Eq(t, typepb.Syntax_SYNTAX_PROTO2, msg.Syntax) 902 testutil.Eq(t, "test.proto", msg.SourceContext.GetFileName()) 903 testutil.Eq(t, 0, len(msg.Options)) 904 testutil.Eq(t, 5, len(msg.Fields)) 905 906 testutil.Eq(t, "abc", msg.Fields[0].Name) 907 testutil.Eq(t, typepb.Field_CARDINALITY_OPTIONAL, msg.Fields[0].Cardinality) 908 testutil.Eq(t, typepb.Field_TYPE_STRING, msg.Fields[0].Kind) 909 testutil.Eq(t, "", msg.Fields[0].DefaultValue) 910 testutil.Eq(t, int32(1), msg.Fields[0].Number) 911 testutil.Eq(t, int32(0), msg.Fields[0].OneofIndex) 912 testutil.Eq(t, 1, len(msg.Fields[0].Options)) 913 testutil.Eq(t, "deprecated", msg.Fields[0].Options[0].Name) 914 // make sure the value is a wrapped bool 915 var v ptypes.DynamicAny 916 err = ptypes.UnmarshalAny(msg.Fields[0].Options[0].Value, &v) 917 testutil.Ok(t, err) 918 testutil.Ceq(t, &wrapperspb.BoolValue{Value: true}, v.Message, eqpm) 919 920 testutil.Eq(t, "def", msg.Fields[1].Name) 921 testutil.Eq(t, typepb.Field_CARDINALITY_REPEATED, msg.Fields[1].Cardinality) 922 testutil.Eq(t, typepb.Field_TYPE_INT32, msg.Fields[1].Kind) 923 testutil.Eq(t, "", msg.Fields[1].DefaultValue) 924 testutil.Eq(t, int32(2), msg.Fields[1].Number) 925 testutil.Eq(t, int32(0), msg.Fields[1].OneofIndex) 926 testutil.Eq(t, true, msg.Fields[1].Packed) 927 testutil.Eq(t, 0, len(msg.Fields[1].Options)) 928 929 testutil.Eq(t, "ghi", msg.Fields[2].Name) 930 testutil.Eq(t, typepb.Field_CARDINALITY_OPTIONAL, msg.Fields[2].Cardinality) 931 testutil.Eq(t, typepb.Field_TYPE_STRING, msg.Fields[2].Kind) 932 testutil.Eq(t, "foobar", msg.Fields[2].DefaultValue) 933 testutil.Eq(t, int32(3), msg.Fields[2].Number) 934 testutil.Eq(t, int32(0), msg.Fields[2].OneofIndex) 935 testutil.Eq(t, 0, len(msg.Fields[2].Options)) 936 937 testutil.Eq(t, "nid", msg.Fields[3].Name) 938 testutil.Eq(t, typepb.Field_CARDINALITY_OPTIONAL, msg.Fields[3].Cardinality) 939 testutil.Eq(t, typepb.Field_TYPE_UINT64, msg.Fields[3].Kind) 940 testutil.Eq(t, "", msg.Fields[3].DefaultValue) 941 testutil.Eq(t, int32(4), msg.Fields[3].Number) 942 testutil.Eq(t, int32(1), msg.Fields[3].OneofIndex) 943 testutil.Eq(t, 0, len(msg.Fields[3].Options)) 944 945 testutil.Eq(t, "sid", msg.Fields[4].Name) 946 testutil.Eq(t, typepb.Field_CARDINALITY_OPTIONAL, msg.Fields[4].Cardinality) 947 testutil.Eq(t, typepb.Field_TYPE_STRING, msg.Fields[4].Kind) 948 testutil.Eq(t, "", msg.Fields[4].DefaultValue) 949 testutil.Eq(t, int32(5), msg.Fields[4].Number) 950 testutil.Eq(t, int32(1), msg.Fields[4].OneofIndex) 951 testutil.Eq(t, 0, len(msg.Fields[4].Options)) 952 } 953 954 func TestMessageRegistry_EnumDescriptorToPType(t *testing.T) { 955 protoSource := ` 956 syntax = "proto2"; 957 package foo; 958 enum Bar { 959 option allow_alias = true; 960 ZERO = 0; 961 __UNSET__ = 0 [deprecated = true]; 962 ONE = 1; 963 TWO = 2; 964 THREE = 3; 965 }` 966 p := protoparse.Parser{ 967 Accessor: func(filename string) (io.ReadCloser, error) { 968 if filename == "test.proto" { 969 return ioutil.NopCloser(strings.NewReader(protoSource)), nil 970 } 971 return nil, os.ErrNotExist 972 }, 973 } 974 fds, err := p.ParseFiles("test.proto") 975 testutil.Ok(t, err) 976 fd := fds[0] 977 978 enum := NewMessageRegistryWithDefaults().EnumAsPType(fd.GetEnumTypes()[0]) 979 980 // quick check of the resulting message's properties 981 testutil.Eq(t, "foo.Bar", enum.Name) 982 testutil.Eq(t, typepb.Syntax_SYNTAX_PROTO2, enum.Syntax) 983 testutil.Eq(t, "test.proto", enum.SourceContext.GetFileName()) 984 testutil.Eq(t, 5, len(enum.Enumvalue)) 985 testutil.Eq(t, 1, len(enum.Options)) 986 testutil.Eq(t, "allow_alias", enum.Options[0].Name) 987 // make sure the value is a wrapped bool 988 var v ptypes.DynamicAny 989 err = ptypes.UnmarshalAny(enum.Options[0].Value, &v) 990 testutil.Ok(t, err) 991 testutil.Ceq(t, &wrapperspb.BoolValue{Value: true}, v.Message, eqpm) 992 993 testutil.Eq(t, "ZERO", enum.Enumvalue[0].Name) 994 testutil.Eq(t, int32(0), enum.Enumvalue[0].Number) 995 testutil.Eq(t, 0, len(enum.Enumvalue[0].Options)) 996 997 testutil.Eq(t, "__UNSET__", enum.Enumvalue[1].Name) 998 testutil.Eq(t, int32(0), enum.Enumvalue[1].Number) 999 testutil.Eq(t, 1, len(enum.Enumvalue[1].Options)) 1000 testutil.Eq(t, "deprecated", enum.Enumvalue[1].Options[0].Name) 1001 // make sure the value is a wrapped bool 1002 err = ptypes.UnmarshalAny(enum.Enumvalue[1].Options[0].Value, &v) 1003 testutil.Ok(t, err) 1004 testutil.Ceq(t, &wrapperspb.BoolValue{Value: true}, v.Message, eqpm) 1005 1006 testutil.Eq(t, "ONE", enum.Enumvalue[2].Name) 1007 testutil.Eq(t, int32(1), enum.Enumvalue[2].Number) 1008 testutil.Eq(t, 0, len(enum.Enumvalue[2].Options)) 1009 1010 testutil.Eq(t, "TWO", enum.Enumvalue[3].Name) 1011 testutil.Eq(t, int32(2), enum.Enumvalue[3].Number) 1012 testutil.Eq(t, 0, len(enum.Enumvalue[3].Options)) 1013 1014 testutil.Eq(t, "THREE", enum.Enumvalue[4].Name) 1015 testutil.Eq(t, int32(3), enum.Enumvalue[4].Number) 1016 testutil.Eq(t, 0, len(enum.Enumvalue[4].Options)) 1017 } 1018 1019 func TestMessageRegistry_ServiceDescriptorToApi(t *testing.T) { 1020 // TODO 1021 } 1022 1023 func eqm(a, b interface{}) bool { 1024 return dynamic.MessagesEqual(a.(proto.Message), b.(proto.Message)) 1025 } 1026 1027 func eqpm(a, b interface{}) bool { 1028 return proto.Equal(a.(proto.Message), b.(proto.Message)) 1029 }