...

Source file src/github.com/grpc-ecosystem/grpc-gateway/v2/internal/descriptor/registry_test.go

Documentation: github.com/grpc-ecosystem/grpc-gateway/v2/internal/descriptor

     1  package descriptor
     2  
     3  import (
     4  	"testing"
     5  
     6  	"github.com/grpc-ecosystem/grpc-gateway/v2/internal/descriptor/openapiconfig"
     7  	"google.golang.org/protobuf/compiler/protogen"
     8  	"google.golang.org/protobuf/encoding/prototext"
     9  	"google.golang.org/protobuf/proto"
    10  	"google.golang.org/protobuf/types/descriptorpb"
    11  	"google.golang.org/protobuf/types/pluginpb"
    12  )
    13  
    14  func newGeneratorFromSources(req *pluginpb.CodeGeneratorRequest, sources ...string) (*protogen.Plugin, error) {
    15  	for _, src := range sources {
    16  		var fd descriptorpb.FileDescriptorProto
    17  		if err := prototext.Unmarshal([]byte(src), &fd); err != nil {
    18  			return nil, err
    19  		}
    20  		req.FileToGenerate = append(req.FileToGenerate, fd.GetName())
    21  		req.ProtoFile = append(req.ProtoFile, &fd)
    22  	}
    23  	return protogen.Options{}.New(req)
    24  }
    25  
    26  func loadFileWithCodeGeneratorRequest(t *testing.T, reg *Registry, req *pluginpb.CodeGeneratorRequest, sources ...string) []*descriptorpb.FileDescriptorProto {
    27  	t.Helper()
    28  	plugin, err := newGeneratorFromSources(req, sources...)
    29  	if err != nil {
    30  		t.Fatalf("failed to create a generator: %v", err)
    31  	}
    32  	err = reg.LoadFromPlugin(plugin)
    33  	if err != nil {
    34  		t.Fatalf("failed to Registry.LoadFromPlugin(): %v", err)
    35  	}
    36  	return plugin.Request.ProtoFile
    37  }
    38  
    39  func loadFile(t *testing.T, reg *Registry, src string) *descriptorpb.FileDescriptorProto {
    40  	t.Helper()
    41  	fds := loadFileWithCodeGeneratorRequest(t, reg, &pluginpb.CodeGeneratorRequest{}, src)
    42  	return fds[0]
    43  }
    44  
    45  func TestLoadFile(t *testing.T) {
    46  	reg := NewRegistry()
    47  	fd := loadFile(t, reg, `
    48  		name: 'example.proto'
    49  		package: 'example'
    50  		options < go_package: 'github.com/grpc-ecosystem/grpc-gateway/runtime/internal/example' >
    51  		message_type <
    52  			name: 'ExampleMessage'
    53  			field <
    54  				name: 'str'
    55  				label: LABEL_OPTIONAL
    56  				type: TYPE_STRING
    57  				number: 1
    58  			>
    59  		>
    60  	`)
    61  
    62  	file := reg.files["example.proto"]
    63  	if file == nil {
    64  		t.Errorf("reg.files[%q] = nil; want non-nil", "example.proto")
    65  		return
    66  	}
    67  	wantPkg := GoPackage{Path: "github.com/grpc-ecosystem/grpc-gateway/runtime/internal/example", Name: "example"}
    68  	if got, want := file.GoPkg, wantPkg; got != want {
    69  		t.Errorf("file.GoPkg = %#v; want %#v", got, want)
    70  	}
    71  
    72  	msg, err := reg.LookupMsg("", ".example.ExampleMessage")
    73  	if err != nil {
    74  		t.Errorf("reg.LookupMsg(%q, %q)) failed with %v; want success", "", ".example.ExampleMessage", err)
    75  		return
    76  	}
    77  	if got, want := msg.DescriptorProto, fd.MessageType[0]; got != want {
    78  		t.Errorf("reg.lookupMsg(%q, %q).DescriptorProto = %#v; want %#v", "", ".example.ExampleMessage", got, want)
    79  	}
    80  	if got, want := msg.File, file; got != want {
    81  		t.Errorf("msg.File = %v; want %v", got, want)
    82  	}
    83  	if got := msg.Outers; got != nil {
    84  		t.Errorf("msg.Outers = %v; want %v", got, nil)
    85  	}
    86  	if got, want := len(msg.Fields), 1; got != want {
    87  		t.Errorf("len(msg.Fields) = %d; want %d", got, want)
    88  	} else if got, want := msg.Fields[0].FieldDescriptorProto, fd.MessageType[0].Field[0]; got != want {
    89  		t.Errorf("msg.Fields[0].FieldDescriptorProto = %v; want %v", got, want)
    90  	} else if got, want := msg.Fields[0].Message, msg; got != want {
    91  		t.Errorf("msg.Fields[0].Message = %v; want %v", got, want)
    92  	}
    93  
    94  	if got, want := len(file.Messages), 1; got != want {
    95  		t.Errorf("file.Meeesages = %#v; want %#v", file.Messages, []*Message{msg})
    96  	}
    97  	if got, want := file.Messages[0], msg; got != want {
    98  		t.Errorf("file.Meeesages[0] = %v; want %v", got, want)
    99  	}
   100  }
   101  
   102  func TestLoadFileNestedPackage(t *testing.T) {
   103  	reg := NewRegistry()
   104  	loadFile(t, reg, `
   105  		name: 'example.proto'
   106  		package: 'example.nested.nested2'
   107  		options < go_package: 'github.com/grpc-ecosystem/grpc-gateway/runtime/internal/example.nested.nested2' >
   108  	`)
   109  
   110  	file := reg.files["example.proto"]
   111  	if file == nil {
   112  		t.Errorf("reg.files[%q] = nil; want non-nil", "example.proto")
   113  		return
   114  	}
   115  	wantPkg := GoPackage{Path: "github.com/grpc-ecosystem/grpc-gateway/runtime/internal/example.nested.nested2", Name: "example_nested_nested2"}
   116  	if got, want := file.GoPkg, wantPkg; got != want {
   117  		t.Errorf("file.GoPkg = %#v; want %#v", got, want)
   118  	}
   119  }
   120  
   121  func TestLoadFileWithDir(t *testing.T) {
   122  	reg := NewRegistry()
   123  	loadFile(t, reg, `
   124  		name: 'path/to/example.proto'
   125  		package: 'example'
   126  		options < go_package: 'github.com/grpc-ecosystem/grpc-gateway/runtime/internal/example' >
   127  	`)
   128  
   129  	file := reg.files["path/to/example.proto"]
   130  	if file == nil {
   131  		t.Errorf("reg.files[%q] = nil; want non-nil", "example.proto")
   132  		return
   133  	}
   134  	wantPkg := GoPackage{Path: "github.com/grpc-ecosystem/grpc-gateway/runtime/internal/example", Name: "example"}
   135  	if got, want := file.GoPkg, wantPkg; got != want {
   136  		t.Errorf("file.GoPkg = %#v; want %#v", got, want)
   137  	}
   138  }
   139  
   140  func TestLoadFileWithoutPackage(t *testing.T) {
   141  	reg := NewRegistry()
   142  	loadFile(t, reg, `
   143  		name: 'path/to/example_file.proto'
   144  		options < go_package: 'github.com/grpc-ecosystem/grpc-gateway/runtime/internal/example_file' >
   145  	`)
   146  
   147  	file := reg.files["path/to/example_file.proto"]
   148  	if file == nil {
   149  		t.Errorf("reg.files[%q] = nil; want non-nil", "example.proto")
   150  		return
   151  	}
   152  	wantPkg := GoPackage{Path: "github.com/grpc-ecosystem/grpc-gateway/runtime/internal/example_file", Name: "example_file"}
   153  	if got, want := file.GoPkg, wantPkg; got != want {
   154  		t.Errorf("file.GoPkg = %#v; want %#v", got, want)
   155  	}
   156  }
   157  
   158  func TestLoadFileWithMapping(t *testing.T) {
   159  	reg := NewRegistry()
   160  	loadFileWithCodeGeneratorRequest(t, reg, &pluginpb.CodeGeneratorRequest{
   161  		Parameter: proto.String("Mpath/to/example.proto=example.com/proj/example/proto"),
   162  	}, `
   163  		name: 'path/to/example.proto'
   164  		package: 'example'
   165  		options < go_package: 'github.com/grpc-ecosystem/grpc-gateway/runtime/internal/example' >
   166  	`)
   167  
   168  	file := reg.files["path/to/example.proto"]
   169  	if file == nil {
   170  		t.Errorf("reg.files[%q] = nil; want non-nil", "example.proto")
   171  		return
   172  	}
   173  	wantPkg := GoPackage{Path: "example.com/proj/example/proto", Name: "example"}
   174  	if got, want := file.GoPkg, wantPkg; got != want {
   175  		t.Errorf("file.GoPkg = %#v; want %#v", got, want)
   176  	}
   177  }
   178  
   179  func TestLoadFileWithPackageNameCollision(t *testing.T) {
   180  	reg := NewRegistry()
   181  	loadFile(t, reg, `
   182  		name: 'path/to/another.proto'
   183  		package: 'example'
   184  		options < go_package: 'github.com/grpc-ecosystem/grpc-gateway/runtime/internal/example' >
   185  	`)
   186  	loadFile(t, reg, `
   187  		name: 'path/to/example.proto'
   188  		package: 'example'
   189  		options < go_package: 'github.com/grpc-ecosystem/grpc-gateway/runtime/internal/example' >
   190  	`)
   191  	if err := reg.ReserveGoPackageAlias("ioutil", "io/ioutil"); err != nil {
   192  		t.Fatalf("reg.ReserveGoPackageAlias(%q) failed with %v; want success", "ioutil", err)
   193  	}
   194  	loadFile(t, reg, `
   195  		name: 'path/to/ioutil.proto'
   196  		package: 'ioutil'
   197  		options < go_package: 'github.com/grpc-ecosystem/grpc-gateway/runtime/internal/ioutil' >
   198  	`)
   199  
   200  	file := reg.files["path/to/another.proto"]
   201  	if file == nil {
   202  		t.Errorf("reg.files[%q] = nil; want non-nil", "path/to/another.proto")
   203  		return
   204  	}
   205  	wantPkg := GoPackage{Path: "github.com/grpc-ecosystem/grpc-gateway/runtime/internal/example", Name: "example"}
   206  	if got, want := file.GoPkg, wantPkg; got != want {
   207  		t.Errorf("file.GoPkg = %#v; want %#v", got, want)
   208  	}
   209  
   210  	file = reg.files["path/to/example.proto"]
   211  	if file == nil {
   212  		t.Errorf("reg.files[%q] = nil; want non-nil", "path/to/example.proto")
   213  		return
   214  	}
   215  	wantPkg = GoPackage{Path: "github.com/grpc-ecosystem/grpc-gateway/runtime/internal/example", Name: "example", Alias: ""}
   216  	if got, want := file.GoPkg, wantPkg; got != want {
   217  		t.Errorf("file.GoPkg = %#v; want %#v", got, want)
   218  	}
   219  
   220  	file = reg.files["path/to/ioutil.proto"]
   221  	if file == nil {
   222  		t.Errorf("reg.files[%q] = nil; want non-nil", "path/to/ioutil.proto")
   223  		return
   224  	}
   225  	wantPkg = GoPackage{Path: "github.com/grpc-ecosystem/grpc-gateway/runtime/internal/ioutil", Name: "ioutil", Alias: "ioutil_0"}
   226  	if got, want := file.GoPkg, wantPkg; got != want {
   227  		t.Errorf("file.GoPkg = %#v; want %#v", got, want)
   228  	}
   229  }
   230  
   231  func TestLoadFileWithIdenticalGoPkg(t *testing.T) {
   232  	reg := NewRegistry()
   233  	loadFileWithCodeGeneratorRequest(t, reg, &pluginpb.CodeGeneratorRequest{
   234  		Parameter: proto.String("Mpath/to/another.proto=example.com/example,Mpath/to/example.proto=example.com/example"),
   235  	}, `
   236  		name: 'path/to/another.proto'
   237  		package: 'example'
   238  		options < go_package: 'github.com/grpc-ecosystem/grpc-gateway/runtime/internal/example' >
   239  	`, `
   240  		name: 'path/to/example.proto'
   241  		package: 'example'
   242  		options < go_package: 'github.com/grpc-ecosystem/grpc-gateway/runtime/internal/example' >
   243  	`)
   244  
   245  	file := reg.files["path/to/example.proto"]
   246  	if file == nil {
   247  		t.Errorf("reg.files[%q] = nil; want non-nil", "example.proto")
   248  		return
   249  	}
   250  	wantPkg := GoPackage{Path: "example.com/example", Name: "example"}
   251  	if got, want := file.GoPkg, wantPkg; got != want {
   252  		t.Errorf("file.GoPkg = %#v; want %#v", got, want)
   253  	}
   254  
   255  	file = reg.files["path/to/another.proto"]
   256  	if file == nil {
   257  		t.Errorf("reg.files[%q] = nil; want non-nil", "example.proto")
   258  		return
   259  	}
   260  	wantPkg = GoPackage{Path: "example.com/example", Name: "example"}
   261  	if got, want := file.GoPkg, wantPkg; got != want {
   262  		t.Errorf("file.GoPkg = %#v; want %#v", got, want)
   263  	}
   264  }
   265  
   266  // TestLookupMsgWithoutPackage tests a case when there is no "package" directive.
   267  // In Go, it is required to have a generated package so we rely on
   268  // google.golang.org/protobuf/compiler/protogen to provide it.
   269  func TestLookupMsgWithoutPackage(t *testing.T) {
   270  	reg := NewRegistry()
   271  	fd := loadFile(t, reg, `
   272  		name: 'example.proto'
   273  		options < go_package: 'github.com/grpc-ecosystem/grpc-gateway/runtime/internal/example' >
   274  		message_type <
   275  			name: 'ExampleMessage'
   276  			field <
   277  				name: 'str'
   278  				label: LABEL_OPTIONAL
   279  				type: TYPE_STRING
   280  				number: 1
   281  			>
   282  		>
   283  	`)
   284  
   285  	msg, err := reg.LookupMsg("", ".ExampleMessage")
   286  	if err != nil {
   287  		t.Errorf("reg.LookupMsg(%q, %q)) failed with %v; want success", "", ".ExampleMessage", err)
   288  		return
   289  	}
   290  	if got, want := msg.DescriptorProto, fd.MessageType[0]; got != want {
   291  		t.Errorf("reg.lookupMsg(%q, %q).DescriptorProto = %#v; want %#v", "", ".ExampleMessage", got, want)
   292  	}
   293  }
   294  
   295  func TestLookupMsgWithNestedPackage(t *testing.T) {
   296  	reg := NewRegistry()
   297  	fd := loadFile(t, reg, `
   298  		name: 'example.proto'
   299  		package: 'nested.nested2.mypackage'
   300  		options < go_package: 'github.com/grpc-ecosystem/grpc-gateway/runtime/internal/example' >
   301  		message_type <
   302  			name: 'ExampleMessage'
   303  			field <
   304  				name: 'str'
   305  				label: LABEL_OPTIONAL
   306  				type: TYPE_STRING
   307  				number: 1
   308  			>
   309  		>
   310  	`)
   311  
   312  	for _, name := range []string{
   313  		"nested.nested2.mypackage.ExampleMessage",
   314  		"nested2.mypackage.ExampleMessage",
   315  		"mypackage.ExampleMessage",
   316  		"ExampleMessage",
   317  	} {
   318  		msg, err := reg.LookupMsg("nested.nested2.mypackage", name)
   319  		if err != nil {
   320  			t.Errorf("reg.LookupMsg(%q, %q)) failed with %v; want success", ".nested.nested2.mypackage", name, err)
   321  			return
   322  		}
   323  		if got, want := msg.DescriptorProto, fd.MessageType[0]; got != want {
   324  			t.Errorf("reg.lookupMsg(%q, %q).DescriptorProto = %#v; want %#v", ".nested.nested2.mypackage", name, got, want)
   325  		}
   326  	}
   327  
   328  	for _, loc := range []string{
   329  		".nested.nested2.mypackage",
   330  		"nested.nested2.mypackage",
   331  		".nested.nested2",
   332  		"nested.nested2",
   333  		".nested",
   334  		"nested",
   335  		".",
   336  		"",
   337  		"somewhere.else",
   338  	} {
   339  		name := "nested.nested2.mypackage.ExampleMessage"
   340  		msg, err := reg.LookupMsg(loc, name)
   341  		if err != nil {
   342  			t.Errorf("reg.LookupMsg(%q, %q)) failed with %v; want success", loc, name, err)
   343  			return
   344  		}
   345  		if got, want := msg.DescriptorProto, fd.MessageType[0]; got != want {
   346  			t.Errorf("reg.lookupMsg(%q, %q).DescriptorProto = %#v; want %#v", loc, name, got, want)
   347  		}
   348  	}
   349  
   350  	for _, loc := range []string{
   351  		".nested.nested2.mypackage",
   352  		"nested.nested2.mypackage",
   353  		".nested.nested2",
   354  		"nested.nested2",
   355  		".nested",
   356  		"nested",
   357  	} {
   358  		name := "nested2.mypackage.ExampleMessage"
   359  		msg, err := reg.LookupMsg(loc, name)
   360  		if err != nil {
   361  			t.Errorf("reg.LookupMsg(%q, %q)) failed with %v; want success", loc, name, err)
   362  			return
   363  		}
   364  		if got, want := msg.DescriptorProto, fd.MessageType[0]; got != want {
   365  			t.Errorf("reg.lookupMsg(%q, %q).DescriptorProto = %#v; want %#v", loc, name, got, want)
   366  		}
   367  	}
   368  }
   369  
   370  func TestLoadWithInconsistentTargetPackage(t *testing.T) {
   371  	for _, spec := range []struct {
   372  		req        string
   373  		consistent bool
   374  	}{
   375  		// root package, explicit go package
   376  		{
   377  			req: `
   378  				file_to_generate: 'a.proto'
   379  				file_to_generate: 'b.proto'
   380  				proto_file <
   381  					name: 'a.proto'
   382  					options < go_package: 'github.com/grpc-ecosystem/grpc-gateway/runtime/internal/example.foo' >
   383  					message_type < name: 'A' >
   384  					service <
   385  						name: "AService"
   386  						method <
   387  							name: "Meth"
   388  							input_type: "A"
   389  							output_type: "A"
   390  							options <
   391  								[google.api.http] < post: "/v1/a" body: "*" >
   392  							>
   393  						>
   394  					>
   395  				>
   396  				proto_file <
   397  					name: 'b.proto'
   398  					options < go_package: 'github.com/grpc-ecosystem/grpc-gateway/runtime/internal/example.foo' >
   399  					message_type < name: 'B' >
   400  					service <
   401  						name: "BService"
   402  						method <
   403  							name: "Meth"
   404  							input_type: "B"
   405  							output_type: "B"
   406  							options <
   407  								[google.api.http] < post: "/v1/b" body: "*" >
   408  							>
   409  						>
   410  					>
   411  				>
   412  			`,
   413  			consistent: true,
   414  		},
   415  		// named package, explicit go package
   416  		{
   417  			req: `
   418  				file_to_generate: 'a.proto'
   419  				file_to_generate: 'b.proto'
   420  				proto_file <
   421  					name: 'a.proto'
   422  					package: 'example.foo'
   423  					options < go_package: 'github.com/grpc-ecosystem/grpc-gateway/runtime/internal/example.foo' >
   424  					message_type < name: 'A' >
   425  					service <
   426  						name: "AService"
   427  						method <
   428  							name: "Meth"
   429  							input_type: "A"
   430  							output_type: "A"
   431  							options <
   432  								[google.api.http] < post: "/v1/a" body: "*" >
   433  							>
   434  						>
   435  					>
   436  				>
   437  				proto_file <
   438  					name: 'b.proto'
   439  					package: 'example.foo'
   440  					options < go_package: 'github.com/grpc-ecosystem/grpc-gateway/runtime/internal/example.foo' >
   441  					message_type < name: 'B' >
   442  					service <
   443  						name: "BService"
   444  						method <
   445  							name: "Meth"
   446  							input_type: "B"
   447  							output_type: "B"
   448  							options <
   449  								[google.api.http] < post: "/v1/b" body: "*" >
   450  							>
   451  						>
   452  					>
   453  				>
   454  			`,
   455  			consistent: true,
   456  		},
   457  	} {
   458  		var req pluginpb.CodeGeneratorRequest
   459  		if err := prototext.Unmarshal([]byte(spec.req), &req); err != nil {
   460  			t.Fatalf("proto.UnmarshalText(%s, &file) failed with %v; want success", spec.req, err)
   461  		}
   462  		_, err := newGeneratorFromSources(&req)
   463  		if got, want := err == nil, spec.consistent; got != want {
   464  			if want {
   465  				t.Errorf("reg.Load(%s) failed with %v; want success", spec.req, err)
   466  				continue
   467  			}
   468  			t.Errorf("reg.Load(%s) succeeded; want an package inconsistency error", spec.req)
   469  		}
   470  	}
   471  }
   472  
   473  func TestLoadOverriddenPackageName(t *testing.T) {
   474  	reg := NewRegistry()
   475  	loadFile(t, reg, `
   476  		name: 'example.proto'
   477  		package: 'example'
   478  		options < go_package: 'example.com/xyz;pb' >
   479  	`)
   480  	file := reg.files["example.proto"]
   481  	if file == nil {
   482  		t.Errorf("reg.files[%q] = nil; want non-nil", "example.proto")
   483  		return
   484  	}
   485  	wantPkg := GoPackage{Path: "example.com/xyz", Name: "pb"}
   486  	if got, want := file.GoPkg, wantPkg; got != want {
   487  		t.Errorf("file.GoPkg = %#v; want %#v", got, want)
   488  	}
   489  }
   490  
   491  func TestLoadWithStandalone(t *testing.T) {
   492  	reg := NewRegistry()
   493  	reg.SetStandalone(true)
   494  	loadFile(t, reg, `
   495  		name: 'example.proto'
   496  		package: 'example'
   497  		options < go_package: 'example.com/xyz;pb' >
   498  	`)
   499  	file := reg.files["example.proto"]
   500  	if file == nil {
   501  		t.Errorf("reg.files[%q] = nil; want non-nil", "example.proto")
   502  		return
   503  	}
   504  	wantPkg := GoPackage{Path: "example.com/xyz", Name: "pb", Alias: "extPb"}
   505  	if got, want := file.GoPkg, wantPkg; got != want {
   506  		t.Errorf("file.GoPkg = %#v; want %#v", got, want)
   507  	}
   508  }
   509  
   510  func TestUnboundExternalHTTPRules(t *testing.T) {
   511  	reg := NewRegistry()
   512  	methodName := ".example.ExampleService.Echo"
   513  	reg.AddExternalHTTPRule(methodName, nil)
   514  	assertStringSlice(t, "unbound external HTTP rules", reg.UnboundExternalHTTPRules(), []string{methodName})
   515  	loadFile(t, reg, `
   516  		name: "path/to/example.proto",
   517  		package: "example"
   518  		options < go_package: 'github.com/grpc-ecosystem/grpc-gateway/runtime/internal/example' >
   519  		message_type <
   520  			name: "StringMessage"
   521  			field <
   522  				name: "string"
   523  				number: 1
   524  				label: LABEL_OPTIONAL
   525  				type: TYPE_STRING
   526  			>
   527  		>
   528  		service <
   529  			name: "ExampleService"
   530  			method <
   531  				name: "Echo"
   532  				input_type: "StringMessage"
   533  				output_type: "StringMessage"
   534  			>
   535  		>
   536  	`)
   537  	assertStringSlice(t, "unbound external HTTP rules", reg.UnboundExternalHTTPRules(), []string{})
   538  }
   539  
   540  func TestRegisterOpenAPIOptions(t *testing.T) {
   541  	codeReqText := `file_to_generate: 'a.proto'
   542  	proto_file <
   543  		name: 'a.proto'
   544  		package: 'example.foo'
   545  		options < go_package: 'github.com/grpc-ecosystem/grpc-gateway/runtime/internal/example' >
   546  		message_type <
   547  			name: 'ExampleMessage'
   548  			field <
   549  				name: 'str'
   550  				label: LABEL_OPTIONAL
   551  				type: TYPE_STRING
   552  				number: 1
   553  			>
   554  		>
   555  		service <
   556  			name: "AService"
   557  			method <
   558  				name: "Meth"
   559  				input_type: "ExampleMessage"
   560  				output_type: "ExampleMessage"
   561  				options <
   562  					[google.api.http] < post: "/v1/a" body: "*" >
   563  				>
   564  			>
   565  		>
   566  	>
   567  	`
   568  	var codeReq pluginpb.CodeGeneratorRequest
   569  	if err := prototext.Unmarshal([]byte(codeReqText), &codeReq); err != nil {
   570  		t.Fatalf("proto.UnmarshalText(%s, &file) failed with %v; want success", codeReqText, err)
   571  	}
   572  
   573  	for _, tcase := range []struct {
   574  		options   *openapiconfig.OpenAPIOptions
   575  		shouldErr bool
   576  		desc      string
   577  	}{
   578  		{
   579  			desc: "handle nil options",
   580  		},
   581  		{
   582  			desc: "successfully add options if referenced entity exists",
   583  			options: &openapiconfig.OpenAPIOptions{
   584  				File: []*openapiconfig.OpenAPIFileOption{
   585  					{
   586  						File: "a.proto",
   587  					},
   588  				},
   589  				Method: []*openapiconfig.OpenAPIMethodOption{
   590  					{
   591  						Method: "example.foo.AService.Meth",
   592  					},
   593  				},
   594  				Message: []*openapiconfig.OpenAPIMessageOption{
   595  					{
   596  						Message: "example.foo.ExampleMessage",
   597  					},
   598  				},
   599  				Service: []*openapiconfig.OpenAPIServiceOption{
   600  					{
   601  						Service: "example.foo.AService",
   602  					},
   603  				},
   604  				Field: []*openapiconfig.OpenAPIFieldOption{
   605  					{
   606  						Field: "example.foo.ExampleMessage.str",
   607  					},
   608  				},
   609  			},
   610  		},
   611  		{
   612  			desc: "reject fully qualified names with leading \".\"",
   613  			options: &openapiconfig.OpenAPIOptions{
   614  				File: []*openapiconfig.OpenAPIFileOption{
   615  					{
   616  						File: "a.proto",
   617  					},
   618  				},
   619  				Method: []*openapiconfig.OpenAPIMethodOption{
   620  					{
   621  						Method: ".example.foo.AService.Meth",
   622  					},
   623  				},
   624  				Message: []*openapiconfig.OpenAPIMessageOption{
   625  					{
   626  						Message: ".example.foo.ExampleMessage",
   627  					},
   628  				},
   629  				Service: []*openapiconfig.OpenAPIServiceOption{
   630  					{
   631  						Service: ".example.foo.AService",
   632  					},
   633  				},
   634  				Field: []*openapiconfig.OpenAPIFieldOption{
   635  					{
   636  						Field: ".example.foo.ExampleMessage.str",
   637  					},
   638  				},
   639  			},
   640  			shouldErr: true,
   641  		},
   642  		{
   643  			desc: "error if file does not exist",
   644  			options: &openapiconfig.OpenAPIOptions{
   645  				File: []*openapiconfig.OpenAPIFileOption{
   646  					{
   647  						File: "b.proto",
   648  					},
   649  				},
   650  			},
   651  			shouldErr: true,
   652  		},
   653  		{
   654  			desc: "error if method does not exist",
   655  			options: &openapiconfig.OpenAPIOptions{
   656  				Method: []*openapiconfig.OpenAPIMethodOption{
   657  					{
   658  						Method: "example.foo.AService.Meth2",
   659  					},
   660  				},
   661  			},
   662  			shouldErr: true,
   663  		},
   664  		{
   665  			desc: "error if message does not exist",
   666  			options: &openapiconfig.OpenAPIOptions{
   667  				Message: []*openapiconfig.OpenAPIMessageOption{
   668  					{
   669  						Message: "example.foo.NonexistentMessage",
   670  					},
   671  				},
   672  			},
   673  			shouldErr: true,
   674  		},
   675  		{
   676  			desc: "error if service does not exist",
   677  			options: &openapiconfig.OpenAPIOptions{
   678  				Service: []*openapiconfig.OpenAPIServiceOption{
   679  					{
   680  						Service: "example.foo.AService1",
   681  					},
   682  				},
   683  			},
   684  			shouldErr: true,
   685  		},
   686  		{
   687  			desc: "error if field does not exist",
   688  			options: &openapiconfig.OpenAPIOptions{
   689  				Field: []*openapiconfig.OpenAPIFieldOption{
   690  					{
   691  						Field: "example.foo.ExampleMessage.str1",
   692  					},
   693  				},
   694  			},
   695  			shouldErr: true,
   696  		},
   697  	} {
   698  		t.Run(tcase.desc, func(t *testing.T) {
   699  			reg := NewRegistry()
   700  			loadFileWithCodeGeneratorRequest(t, reg, &codeReq)
   701  			err := reg.RegisterOpenAPIOptions(tcase.options)
   702  			if (err != nil) != tcase.shouldErr {
   703  				t.Fatalf("got unexpected error: %s", err)
   704  			}
   705  		})
   706  	}
   707  }
   708  
   709  func assertStringSlice(t *testing.T, message string, got, want []string) {
   710  	if len(got) != len(want) {
   711  		t.Errorf("%s = %#v len(%d); want %#v len(%d)", message, got, len(got), want, len(want))
   712  	}
   713  	for i := range want {
   714  		if got[i] != want[i] {
   715  			t.Errorf("%s[%d] = %#v; want %#v", message, i, got[i], want[i])
   716  		}
   717  	}
   718  }
   719  

View as plain text