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  }