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