github.com/bakjos/protoreflect@v1.9.2/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/protoc-gen-go/descriptor" 14 "github.com/golang/protobuf/ptypes" 15 "github.com/golang/protobuf/ptypes/any" 16 "github.com/golang/protobuf/ptypes/duration" 17 "github.com/golang/protobuf/ptypes/wrappers" 18 "google.golang.org/genproto/protobuf/api" 19 "google.golang.org/genproto/protobuf/ptype" 20 "google.golang.org/genproto/protobuf/source_context" 21 22 "github.com/bakjos/protoreflect/desc" 23 "github.com/bakjos/protoreflect/desc/protoparse" 24 "github.com/bakjos/protoreflect/dynamic" 25 "github.com/bakjos/protoreflect/internal/testprotos" 26 "github.com/bakjos/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 := &any.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 := &duration.Duration{Nanos: 100, Seconds: 1000} 81 b, err = proto.Marshal(dur) 82 testutil.Ok(t, err) 83 a = &any.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((*duration.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 := &any.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((*descriptor.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 := &descriptor.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, descriptor.FieldDescriptorProto_LABEL_OPTIONAL, flds[0].GetLabel()) 185 testutil.Eq(t, descriptor.FieldDescriptorProto_TYPE_MESSAGE, flds[0].GetType()) 186 187 fo := &descriptor.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, descriptor.FieldDescriptorProto_LABEL_REPEATED, flds[1].GetLabel()) 200 testutil.Eq(t, descriptor.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, descriptor.FieldDescriptorProto_LABEL_OPTIONAL, flds[2].GetLabel()) 206 testutil.Eq(t, descriptor.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, descriptor.FieldDescriptorProto_LABEL_OPTIONAL, flds[3].GetLabel()) 212 testutil.Eq(t, descriptor.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 := &ptype.Type{ 258 Name: "foo.Bar", 259 Oneofs: []string{"baz"}, 260 Fields: []*ptype.Field{ 261 { 262 Name: "id", 263 Number: 1, 264 Kind: ptype.Field_TYPE_UINT64, 265 Cardinality: ptype.Field_CARDINALITY_OPTIONAL, 266 JsonName: "id", 267 }, 268 { 269 Name: "name", 270 Number: 2, 271 Kind: ptype.Field_TYPE_STRING, 272 Cardinality: ptype.Field_CARDINALITY_OPTIONAL, 273 JsonName: "name", 274 }, 275 { 276 Name: "count", 277 Number: 3, 278 OneofIndex: 1, 279 Kind: ptype.Field_TYPE_INT32, 280 Cardinality: ptype.Field_CARDINALITY_OPTIONAL, 281 JsonName: "count", 282 }, 283 { 284 Name: "data", 285 Number: 4, 286 OneofIndex: 1, 287 Kind: ptype.Field_TYPE_BYTES, 288 Cardinality: ptype.Field_CARDINALITY_OPTIONAL, 289 JsonName: "data", 290 }, 291 { 292 Name: "other", 293 Number: 5, 294 OneofIndex: 1, 295 Kind: ptype.Field_TYPE_MESSAGE, 296 Cardinality: ptype.Field_CARDINALITY_OPTIONAL, 297 JsonName: "other", 298 TypeUrl: "type.googleapis.com/google.protobuf.Empty", 299 }, 300 { 301 Name: "created", 302 Number: 6, 303 Kind: ptype.Field_TYPE_MESSAGE, 304 Cardinality: ptype.Field_CARDINALITY_OPTIONAL, 305 JsonName: "created", 306 TypeUrl: "type.googleapis.com/google.protobuf.Timestamp", 307 }, 308 { 309 Name: "updated", 310 Number: 7, 311 Kind: ptype.Field_TYPE_MESSAGE, 312 Cardinality: ptype.Field_CARDINALITY_OPTIONAL, 313 JsonName: "updated", 314 TypeUrl: "type.googleapis.com/google.protobuf.Timestamp", 315 }, 316 { 317 Name: "tombstone", 318 Number: 8, 319 Kind: ptype.Field_TYPE_BOOL, 320 Cardinality: ptype.Field_CARDINALITY_OPTIONAL, 321 JsonName: "tombstone", 322 }, 323 }, 324 SourceContext: &source_context.SourceContext{ 325 FileName: "test/foo.proto", 326 }, 327 Syntax: ptype.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 := &descriptor.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, 2, len(vals)) 394 testutil.Eq(t, "ABC", vals[0].GetName()) 395 testutil.Eq(t, int32(1), vals[0].GetNumber()) 396 397 evo := &descriptor.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(2), vals[1].GetNumber()) 414 } 415 416 func createFetcher(t *testing.T) TypeFetcher { 417 bol, err := ptypes.MarshalAny(&wrappers.BoolValue{Value: true}) 418 testutil.Ok(t, err) 419 in32, err := ptypes.MarshalAny(&wrappers.Int32Value{Value: -42}) 420 testutil.Ok(t, err) 421 uin32, err := ptypes.MarshalAny(&wrappers.UInt32Value{Value: 42}) 422 testutil.Ok(t, err) 423 in64, err := ptypes.MarshalAny(&wrappers.Int64Value{Value: -420420420420}) 424 testutil.Ok(t, err) 425 uin64, err := ptypes.MarshalAny(&wrappers.UInt64Value{Value: 420420420420}) 426 testutil.Ok(t, err) 427 byt, err := ptypes.MarshalAny(&wrappers.BytesValue{Value: []byte{1, 2, 3, 4, 5, 6, 7, 8}}) 428 testutil.Ok(t, err) 429 str1, err := ptypes.MarshalAny(&wrappers.StringValue{Value: "foo"}) 430 testutil.Ok(t, err) 431 str2, err := ptypes.MarshalAny(&wrappers.StringValue{Value: "bar"}) 432 testutil.Ok(t, err) 433 str3, err := ptypes.MarshalAny(&wrappers.StringValue{Value: "baz"}) 434 testutil.Ok(t, err) 435 436 types := map[string]proto.Message{ 437 "https://foo.bar/some.Type": &ptype.Type{ 438 Name: "some.Type", 439 Oneofs: []string{"un"}, 440 Fields: []*ptype.Field{ 441 { 442 Name: "a", 443 JsonName: "a", 444 Number: 1, 445 Cardinality: ptype.Field_CARDINALITY_OPTIONAL, 446 Kind: ptype.Field_TYPE_MESSAGE, 447 TypeUrl: "foo.bar/some.OtherType", 448 Options: []*ptype.Option{ 449 { 450 Name: "deprecated", 451 Value: bol, 452 }, 453 { 454 Name: "testprotos.ffubar", 455 Value: str1, 456 }, 457 { 458 Name: "testprotos.ffubar", 459 Value: str2, 460 }, 461 { 462 Name: "testprotos.ffubar", 463 Value: str3, 464 }, 465 { 466 Name: "testprotos.ffubarb", 467 Value: byt, 468 }, 469 }, 470 }, 471 { 472 Name: "b", 473 JsonName: "b", 474 Number: 2, 475 Cardinality: ptype.Field_CARDINALITY_REPEATED, 476 Kind: ptype.Field_TYPE_STRING, 477 }, 478 { 479 Name: "c", 480 JsonName: "c", 481 Number: 3, 482 Cardinality: ptype.Field_CARDINALITY_OPTIONAL, 483 Kind: ptype.Field_TYPE_ENUM, 484 TypeUrl: "foo.bar/some.Enum", 485 OneofIndex: 1, 486 }, 487 { 488 Name: "d", 489 JsonName: "d", 490 Number: 4, 491 Cardinality: ptype.Field_CARDINALITY_OPTIONAL, 492 Kind: ptype.Field_TYPE_INT32, 493 OneofIndex: 1, 494 }, 495 }, 496 Options: []*ptype.Option{ 497 { 498 Name: "deprecated", 499 Value: bol, 500 }, 501 { 502 Name: "testprotos.mfubar", 503 Value: bol, 504 }, 505 }, 506 SourceContext: &source_context.SourceContext{FileName: "foo.proto"}, 507 Syntax: ptype.Syntax_SYNTAX_PROTO3, 508 }, 509 "https://foo.bar/some.OtherType": &ptype.Type{ 510 Name: "some.OtherType", 511 Fields: []*ptype.Field{ 512 { 513 Name: "a", 514 JsonName: "a", 515 Number: 1, 516 Cardinality: ptype.Field_CARDINALITY_OPTIONAL, 517 Kind: ptype.Field_TYPE_MESSAGE, 518 TypeUrl: "foo.bar/some.OtherType.AnotherType", 519 }, 520 }, 521 SourceContext: &source_context.SourceContext{FileName: "bar.proto"}, 522 Syntax: ptype.Syntax_SYNTAX_PROTO2, 523 }, 524 "https://foo.bar/some.OtherType.AnotherType": &ptype.Type{ 525 Name: "some.OtherType.AnotherType", 526 Fields: []*ptype.Field{ 527 { 528 Name: "a", 529 JsonName: "a", 530 Number: 1, 531 Cardinality: ptype.Field_CARDINALITY_OPTIONAL, 532 Kind: ptype.Field_TYPE_BYTES, 533 }, 534 }, 535 SourceContext: &source_context.SourceContext{FileName: "bar.proto"}, 536 Syntax: ptype.Syntax_SYNTAX_PROTO2, 537 }, 538 "https://foo.bar/some.Enum": &ptype.Enum{ 539 Name: "some.Enum", 540 Enumvalue: []*ptype.EnumValue{ 541 { 542 Name: "ABC", 543 Number: 1, 544 Options: []*ptype.Option{ 545 { 546 Name: "deprecated", 547 Value: bol, 548 }, 549 { 550 Name: "testprotos.evfubar", 551 Value: in64, 552 }, 553 { 554 Name: "testprotos.evfubars", 555 Value: in64, 556 }, 557 { 558 Name: "testprotos.evfubarsf", 559 Value: in64, 560 }, 561 { 562 Name: "testprotos.evfubaru", 563 Value: uin64, 564 }, 565 { 566 Name: "testprotos.evfubaruf", 567 Value: uin64, 568 }, 569 }, 570 }, 571 { 572 Name: "XYZ", 573 Number: 2, 574 }, 575 }, 576 Options: []*ptype.Option{ 577 { 578 Name: "deprecated", 579 Value: bol, 580 }, 581 { 582 Name: "allow_alias", 583 Value: bol, 584 }, 585 { 586 Name: "testprotos.efubar", 587 Value: in32, 588 }, 589 { 590 Name: "testprotos.efubars", 591 Value: in32, 592 }, 593 { 594 Name: "testprotos.efubarsf", 595 Value: in32, 596 }, 597 { 598 Name: "testprotos.efubaru", 599 Value: uin32, 600 }, 601 { 602 Name: "testprotos.efubaruf", 603 Value: uin32, 604 }, 605 }, 606 SourceContext: &source_context.SourceContext{FileName: "foo.proto"}, 607 Syntax: ptype.Syntax_SYNTAX_PROTO3, 608 }, 609 "https://foo.bar/some.YetAnother.MessageType": &ptype.Type{ 610 // in a separate file, so it will look like package some.YetAnother 611 Name: "some.YetAnother.MessageType", 612 Fields: []*ptype.Field{ 613 { 614 Name: "a", 615 JsonName: "a", 616 Number: 1, 617 Cardinality: ptype.Field_CARDINALITY_OPTIONAL, 618 Kind: ptype.Field_TYPE_STRING, 619 }, 620 }, 621 SourceContext: &source_context.SourceContext{FileName: "baz.proto"}, 622 Syntax: ptype.Syntax_SYNTAX_PROTO2, 623 }, 624 } 625 return func(url string, enum bool) (proto.Message, error) { 626 t := types[url] 627 if t == nil { 628 return nil, nil 629 } 630 if _, ok := t.(*ptype.Enum); ok == enum { 631 return t, nil 632 } else { 633 return nil, fmt.Errorf("bad type for %s", url) 634 } 635 } 636 } 637 638 func TestMessageRegistry_ResolveApiIntoServiceDescriptor(t *testing.T) { 639 tf := createFetcher(t) 640 // we want "defaults" for the message factory so that we can properly process 641 // known extensions (which the type fetcher puts into the descriptor options) 642 mr := (&MessageRegistry{}).WithFetcher(tf).WithMessageFactory(dynamic.NewMessageFactoryWithDefaults()) 643 644 sd, err := mr.ResolveApiIntoServiceDescriptor(getApi(t)) 645 testutil.Ok(t, err) 646 647 testutil.Eq(t, "Service", sd.GetName()) 648 testutil.Eq(t, "some.Service", sd.GetFullyQualifiedName()) 649 testutil.Eq(t, "some", sd.GetFile().GetPackage()) 650 testutil.Eq(t, true, sd.GetFile().IsProto3()) 651 652 so := &descriptor.ServiceOptions{ 653 Deprecated: proto.Bool(true), 654 } 655 err = proto.SetExtension(so, testprotos.E_Sfubar, &testprotos.ReallySimpleMessage{Id: proto.Uint64(100), Name: proto.String("deuce")}) 656 testutil.Ok(t, err) 657 err = proto.SetExtension(so, testprotos.E_Sfubare, testprotos.ReallySimpleEnum_VALUE.Enum()) 658 testutil.Ok(t, err) 659 testutil.Ceq(t, so, sd.GetServiceOptions(), eqpm) 660 661 methods := sd.GetMethods() 662 testutil.Eq(t, 4, len(methods)) 663 testutil.Eq(t, "UnaryMethod", methods[0].GetName()) 664 testutil.Eq(t, "some.Type", methods[0].GetInputType().GetFullyQualifiedName()) 665 testutil.Eq(t, "some.OtherType", methods[0].GetOutputType().GetFullyQualifiedName()) 666 667 mto := &descriptor.MethodOptions{ 668 Deprecated: proto.Bool(true), 669 } 670 err = proto.SetExtension(mto, testprotos.E_Mtfubar, []float32{3.14159, 2.71828}) 671 testutil.Ok(t, err) 672 err = proto.SetExtension(mto, testprotos.E_Mtfubard, proto.Float64(10203040.506070809)) 673 testutil.Ok(t, err) 674 testutil.Ceq(t, mto, methods[0].GetMethodOptions(), eqpm) 675 676 testutil.Eq(t, "ClientStreamMethod", methods[1].GetName()) 677 testutil.Eq(t, "some.OtherType", methods[1].GetInputType().GetFullyQualifiedName()) 678 testutil.Eq(t, "some.Type", methods[1].GetOutputType().GetFullyQualifiedName()) 679 680 testutil.Eq(t, "ServerStreamMethod", methods[2].GetName()) 681 testutil.Eq(t, "some.OtherType.AnotherType", methods[2].GetInputType().GetFullyQualifiedName()) 682 testutil.Eq(t, "some.YetAnother.MessageType", methods[2].GetOutputType().GetFullyQualifiedName()) 683 684 testutil.Eq(t, "BidiStreamMethod", methods[3].GetName()) 685 testutil.Eq(t, "some.YetAnother.MessageType", methods[3].GetInputType().GetFullyQualifiedName()) 686 testutil.Eq(t, "some.OtherType.AnotherType", methods[3].GetOutputType().GetFullyQualifiedName()) 687 688 // check linked message types 689 690 testutil.Eq(t, methods[0].GetInputType(), methods[1].GetOutputType()) 691 testutil.Eq(t, methods[0].GetOutputType(), methods[1].GetInputType()) 692 testutil.Eq(t, methods[2].GetInputType(), methods[3].GetOutputType()) 693 testutil.Eq(t, methods[2].GetOutputType(), methods[3].GetInputType()) 694 695 md1 := methods[0].GetInputType() 696 md2 := methods[0].GetOutputType() 697 md3 := methods[2].GetInputType() 698 md4 := methods[2].GetOutputType() 699 700 testutil.Eq(t, "Type", md1.GetName()) 701 testutil.Eq(t, "some.Type", md1.GetFullyQualifiedName()) 702 testutil.Eq(t, "some", md1.GetFile().GetPackage()) 703 testutil.Eq(t, true, md1.GetFile().IsProto3()) 704 testutil.Eq(t, true, md1.IsProto3()) 705 706 testutil.Eq(t, "OtherType", md2.GetName()) 707 testutil.Eq(t, "some.OtherType", md2.GetFullyQualifiedName()) 708 testutil.Eq(t, "some", md2.GetFile().GetPackage()) 709 testutil.Eq(t, false, md2.GetFile().IsProto3()) 710 testutil.Eq(t, false, md2.IsProto3()) 711 712 testutil.Eq(t, md3, md2.GetNestedMessageTypes()[0]) 713 testutil.Eq(t, "AnotherType", md3.GetName()) 714 testutil.Eq(t, "some.OtherType.AnotherType", md3.GetFullyQualifiedName()) 715 testutil.Eq(t, "some", md3.GetFile().GetPackage()) 716 testutil.Eq(t, false, md3.GetFile().IsProto3()) 717 testutil.Eq(t, false, md3.IsProto3()) 718 719 testutil.Eq(t, "MessageType", md4.GetName()) 720 testutil.Eq(t, "some.YetAnother.MessageType", md4.GetFullyQualifiedName()) 721 testutil.Eq(t, "some", md4.GetFile().GetPackage()) 722 testutil.Eq(t, true, md4.GetFile().IsProto3()) 723 testutil.Eq(t, true, md4.IsProto3()) 724 } 725 726 func getApi(t *testing.T) *api.Api { 727 bol, err := ptypes.MarshalAny(&wrappers.BoolValue{Value: true}) 728 testutil.Ok(t, err) 729 dbl, err := ptypes.MarshalAny(&wrappers.DoubleValue{Value: 10203040.506070809}) 730 testutil.Ok(t, err) 731 flt1, err := ptypes.MarshalAny(&wrappers.FloatValue{Value: 3.14159}) 732 testutil.Ok(t, err) 733 flt2, err := ptypes.MarshalAny(&wrappers.FloatValue{Value: 2.71828}) 734 testutil.Ok(t, err) 735 enu, err := ptypes.MarshalAny(&wrappers.Int32Value{Value: int32(testprotos.ReallySimpleEnum_VALUE)}) 736 testutil.Ok(t, err) 737 msg, err := ptypes.MarshalAny(&testprotos.ReallySimpleMessage{Id: proto.Uint64(100), Name: proto.String("deuce")}) 738 testutil.Ok(t, err) 739 return &api.Api{ 740 Name: "some.Service", 741 Methods: []*api.Method{ 742 { 743 Name: "UnaryMethod", 744 RequestTypeUrl: "foo.bar/some.Type", 745 ResponseTypeUrl: "foo.bar/some.OtherType", 746 Options: []*ptype.Option{ 747 { 748 Name: "deprecated", 749 Value: bol, 750 }, 751 { 752 Name: "testprotos.mtfubar", 753 Value: flt1, 754 }, 755 { 756 Name: "testprotos.mtfubar", 757 Value: flt2, 758 }, 759 { 760 Name: "testprotos.mtfubard", 761 Value: dbl, 762 }, 763 }, 764 Syntax: ptype.Syntax_SYNTAX_PROTO3, 765 }, 766 { 767 Name: "ClientStreamMethod", 768 RequestStreaming: true, 769 RequestTypeUrl: "foo.bar/some.OtherType", 770 ResponseTypeUrl: "foo.bar/some.Type", 771 Syntax: ptype.Syntax_SYNTAX_PROTO3, 772 }, 773 { 774 Name: "ServerStreamMethod", 775 ResponseStreaming: true, 776 RequestTypeUrl: "foo.bar/some.OtherType.AnotherType", 777 ResponseTypeUrl: "foo.bar/some.YetAnother.MessageType", 778 Syntax: ptype.Syntax_SYNTAX_PROTO3, 779 }, 780 { 781 Name: "BidiStreamMethod", 782 RequestStreaming: true, 783 ResponseStreaming: true, 784 RequestTypeUrl: "foo.bar/some.YetAnother.MessageType", 785 ResponseTypeUrl: "foo.bar/some.OtherType.AnotherType", 786 Syntax: ptype.Syntax_SYNTAX_PROTO3, 787 }, 788 }, 789 Options: []*ptype.Option{ 790 { 791 Name: "deprecated", 792 Value: bol, 793 }, 794 { 795 Name: "testprotos.sfubar", 796 Value: msg, 797 }, 798 { 799 Name: "testprotos.sfubare", 800 Value: enu, 801 }, 802 }, 803 SourceContext: &source_context.SourceContext{FileName: "baz.proto"}, 804 Syntax: ptype.Syntax_SYNTAX_PROTO3, 805 } 806 } 807 808 func TestMessageRegistry_MarshalAndUnmarshalAny(t *testing.T) { 809 mr := NewMessageRegistryWithDefaults() 810 811 md, err := desc.LoadMessageDescriptor("google.protobuf.DescriptorProto") 812 testutil.Ok(t, err) 813 814 // marshal with default base URL 815 a, err := mr.MarshalAny(md.AsProto()) 816 testutil.Ok(t, err) 817 testutil.Eq(t, "type.googleapis.com/google.protobuf.DescriptorProto", a.TypeUrl) 818 819 // check that we can unmarshal it with normal ptypes library 820 var umd descriptor.DescriptorProto 821 err = ptypes.UnmarshalAny(a, &umd) 822 testutil.Ok(t, err) 823 testutil.Ceq(t, md.AsProto(), &umd, eqpm) 824 825 // and that we can unmarshal it with a message registry 826 pm, err := mr.UnmarshalAny(a) 827 testutil.Ok(t, err) 828 _, ok := pm.(*descriptor.DescriptorProto) 829 testutil.Require(t, ok) 830 testutil.Ceq(t, md.AsProto(), pm, eqpm) 831 832 // and that we can unmarshal it as a dynamic message, using a 833 // message registry that doesn't know about the generated type 834 mrWithoutDefaults := &MessageRegistry{} 835 err = mrWithoutDefaults.AddMessage("type.googleapis.com/google.protobuf.DescriptorProto", md) 836 testutil.Ok(t, err) 837 pm, err = mrWithoutDefaults.UnmarshalAny(a) 838 testutil.Ok(t, err) 839 dm, ok := pm.(*dynamic.Message) 840 testutil.Require(t, ok) 841 testutil.Ceq(t, md.AsProto(), dm, eqm) 842 843 // now test generation of type URLs with other settings 844 845 // - different default 846 mr.WithDefaultBaseUrl("foo.com/some/path/") 847 a, err = mr.MarshalAny(md.AsProto()) 848 testutil.Ok(t, err) 849 testutil.Eq(t, "foo.com/some/path/google.protobuf.DescriptorProto", a.TypeUrl) 850 851 // - custom base URL for package 852 mr.AddBaseUrlForElement("bar.com/other/", "google.protobuf") 853 a, err = mr.MarshalAny(md.AsProto()) 854 testutil.Ok(t, err) 855 testutil.Eq(t, "bar.com/other/google.protobuf.DescriptorProto", a.TypeUrl) 856 857 // - custom base URL for type 858 mr.AddBaseUrlForElement("http://baz.com/another/", "google.protobuf.DescriptorProto") 859 a, err = mr.MarshalAny(md.AsProto()) 860 testutil.Ok(t, err) 861 testutil.Eq(t, "http://baz.com/another/google.protobuf.DescriptorProto", a.TypeUrl) 862 } 863 864 func TestMessageRegistry_MessageDescriptorToPType(t *testing.T) { 865 protoSource := ` 866 syntax = "proto2"; 867 package foo; 868 message Bar { 869 optional string abc = 1 [deprecated = true]; 870 repeated int32 def = 2 [packed = true]; 871 optional string ghi = 3 [default = "foobar"]; 872 oneof oo { 873 uint64 nid = 4; 874 string sid = 5; 875 } 876 }` 877 p := protoparse.Parser{ 878 Accessor: func(filename string) (io.ReadCloser, error) { 879 if filename == "test.proto" { 880 return ioutil.NopCloser(strings.NewReader(protoSource)), nil 881 } 882 return nil, os.ErrNotExist 883 }, 884 } 885 fds, err := p.ParseFiles("test.proto") 886 testutil.Ok(t, err) 887 fd := fds[0] 888 889 msg := NewMessageRegistryWithDefaults().MessageAsPType(fd.GetMessageTypes()[0]) 890 891 // quick check of the resulting message's properties 892 testutil.Eq(t, "foo.Bar", msg.Name) 893 testutil.Eq(t, []string{"oo"}, msg.Oneofs) 894 testutil.Eq(t, ptype.Syntax_SYNTAX_PROTO2, msg.Syntax) 895 testutil.Eq(t, "test.proto", msg.SourceContext.GetFileName()) 896 testutil.Eq(t, 0, len(msg.Options)) 897 testutil.Eq(t, 5, len(msg.Fields)) 898 899 testutil.Eq(t, "abc", msg.Fields[0].Name) 900 testutil.Eq(t, ptype.Field_CARDINALITY_OPTIONAL, msg.Fields[0].Cardinality) 901 testutil.Eq(t, ptype.Field_TYPE_STRING, msg.Fields[0].Kind) 902 testutil.Eq(t, "", msg.Fields[0].DefaultValue) 903 testutil.Eq(t, int32(1), msg.Fields[0].Number) 904 testutil.Eq(t, int32(0), msg.Fields[0].OneofIndex) 905 testutil.Eq(t, 1, len(msg.Fields[0].Options)) 906 testutil.Eq(t, "deprecated", msg.Fields[0].Options[0].Name) 907 // make sure the value is a wrapped bool 908 var v ptypes.DynamicAny 909 err = ptypes.UnmarshalAny(msg.Fields[0].Options[0].Value, &v) 910 testutil.Ok(t, err) 911 testutil.Ceq(t, &wrappers.BoolValue{Value: true}, v.Message, eqpm) 912 913 testutil.Eq(t, "def", msg.Fields[1].Name) 914 testutil.Eq(t, ptype.Field_CARDINALITY_REPEATED, msg.Fields[1].Cardinality) 915 testutil.Eq(t, ptype.Field_TYPE_INT32, msg.Fields[1].Kind) 916 testutil.Eq(t, "", msg.Fields[1].DefaultValue) 917 testutil.Eq(t, int32(2), msg.Fields[1].Number) 918 testutil.Eq(t, int32(0), msg.Fields[1].OneofIndex) 919 testutil.Eq(t, true, msg.Fields[1].Packed) 920 testutil.Eq(t, 0, len(msg.Fields[1].Options)) 921 922 testutil.Eq(t, "ghi", msg.Fields[2].Name) 923 testutil.Eq(t, ptype.Field_CARDINALITY_OPTIONAL, msg.Fields[2].Cardinality) 924 testutil.Eq(t, ptype.Field_TYPE_STRING, msg.Fields[2].Kind) 925 testutil.Eq(t, "foobar", msg.Fields[2].DefaultValue) 926 testutil.Eq(t, int32(3), msg.Fields[2].Number) 927 testutil.Eq(t, int32(0), msg.Fields[2].OneofIndex) 928 testutil.Eq(t, 0, len(msg.Fields[2].Options)) 929 930 testutil.Eq(t, "nid", msg.Fields[3].Name) 931 testutil.Eq(t, ptype.Field_CARDINALITY_OPTIONAL, msg.Fields[3].Cardinality) 932 testutil.Eq(t, ptype.Field_TYPE_UINT64, msg.Fields[3].Kind) 933 testutil.Eq(t, "", msg.Fields[3].DefaultValue) 934 testutil.Eq(t, int32(4), msg.Fields[3].Number) 935 testutil.Eq(t, int32(1), msg.Fields[3].OneofIndex) 936 testutil.Eq(t, 0, len(msg.Fields[3].Options)) 937 938 testutil.Eq(t, "sid", msg.Fields[4].Name) 939 testutil.Eq(t, ptype.Field_CARDINALITY_OPTIONAL, msg.Fields[4].Cardinality) 940 testutil.Eq(t, ptype.Field_TYPE_STRING, msg.Fields[4].Kind) 941 testutil.Eq(t, "", msg.Fields[4].DefaultValue) 942 testutil.Eq(t, int32(5), msg.Fields[4].Number) 943 testutil.Eq(t, int32(1), msg.Fields[4].OneofIndex) 944 testutil.Eq(t, 0, len(msg.Fields[4].Options)) 945 } 946 947 func TestMessageRegistry_EnumDescriptorToPType(t *testing.T) { 948 protoSource := ` 949 syntax = "proto2"; 950 package foo; 951 enum Bar { 952 option allow_alias = true; 953 ZERO = 0; 954 __UNSET__ = 0 [deprecated = true]; 955 ONE = 1; 956 TWO = 2; 957 THREE = 3; 958 }` 959 p := protoparse.Parser{ 960 Accessor: func(filename string) (io.ReadCloser, error) { 961 if filename == "test.proto" { 962 return ioutil.NopCloser(strings.NewReader(protoSource)), nil 963 } 964 return nil, os.ErrNotExist 965 }, 966 } 967 fds, err := p.ParseFiles("test.proto") 968 testutil.Ok(t, err) 969 fd := fds[0] 970 971 enum := NewMessageRegistryWithDefaults().EnumAsPType(fd.GetEnumTypes()[0]) 972 973 // quick check of the resulting message's properties 974 testutil.Eq(t, "foo.Bar", enum.Name) 975 testutil.Eq(t, ptype.Syntax_SYNTAX_PROTO2, enum.Syntax) 976 testutil.Eq(t, "test.proto", enum.SourceContext.GetFileName()) 977 testutil.Eq(t, 5, len(enum.Enumvalue)) 978 testutil.Eq(t, 1, len(enum.Options)) 979 testutil.Eq(t, "allow_alias", enum.Options[0].Name) 980 // make sure the value is a wrapped bool 981 var v ptypes.DynamicAny 982 err = ptypes.UnmarshalAny(enum.Options[0].Value, &v) 983 testutil.Ok(t, err) 984 testutil.Ceq(t, &wrappers.BoolValue{Value: true}, v.Message, eqpm) 985 986 testutil.Eq(t, "ZERO", enum.Enumvalue[0].Name) 987 testutil.Eq(t, int32(0), enum.Enumvalue[0].Number) 988 testutil.Eq(t, 0, len(enum.Enumvalue[0].Options)) 989 990 testutil.Eq(t, "__UNSET__", enum.Enumvalue[1].Name) 991 testutil.Eq(t, int32(0), enum.Enumvalue[1].Number) 992 testutil.Eq(t, 1, len(enum.Enumvalue[1].Options)) 993 testutil.Eq(t, "deprecated", enum.Enumvalue[1].Options[0].Name) 994 // make sure the value is a wrapped bool 995 err = ptypes.UnmarshalAny(enum.Enumvalue[1].Options[0].Value, &v) 996 testutil.Ok(t, err) 997 testutil.Ceq(t, &wrappers.BoolValue{Value: true}, v.Message, eqpm) 998 999 testutil.Eq(t, "ONE", enum.Enumvalue[2].Name) 1000 testutil.Eq(t, int32(1), enum.Enumvalue[2].Number) 1001 testutil.Eq(t, 0, len(enum.Enumvalue[2].Options)) 1002 1003 testutil.Eq(t, "TWO", enum.Enumvalue[3].Name) 1004 testutil.Eq(t, int32(2), enum.Enumvalue[3].Number) 1005 testutil.Eq(t, 0, len(enum.Enumvalue[3].Options)) 1006 1007 testutil.Eq(t, "THREE", enum.Enumvalue[4].Name) 1008 testutil.Eq(t, int32(3), enum.Enumvalue[4].Number) 1009 testutil.Eq(t, 0, len(enum.Enumvalue[4].Options)) 1010 } 1011 1012 func TestMessageRegistry_ServiceDescriptorToApi(t *testing.T) { 1013 // TODO 1014 } 1015 1016 func eqm(a, b interface{}) bool { 1017 return dynamic.MessagesEqual(a.(proto.Message), b.(proto.Message)) 1018 } 1019 1020 func eqpm(a, b interface{}) bool { 1021 return proto.Equal(a.(proto.Message), b.(proto.Message)) 1022 }